def init_camera(self, feature_dimensions, map_size, camera_width_world_units): """Initialize the camera (especially for feature_units). This is called in the constructor and may be called repeatedly after `Features` is constructed, since it deals with rescaling coordinates and not changing environment/action specs. Args: feature_dimensions: See the documentation in `AgentInterfaceFormat`. map_size: The size of the map in world units. camera_width_world_units: See the documentation in `AgentInterfaceFormat`. Raises: ValueError: If map_size or camera_width_world_units are falsey (which should mainly happen if called by the constructor). """ if not map_size or not camera_width_world_units: raise ValueError( "Either pass the game_info with raw enabled, or map_size and " "camera_width_world_units in order to use feature_units or camera" "position.") map_size = point.Point.build(map_size) self._world_to_world_tl = transform.Linear(point.Point(1, -1), point.Point(0, map_size.y)) self._world_tl_to_world_camera_rel = transform.Linear( offset=-map_size / 4) world_camera_rel_to_feature_screen = transform.Linear( feature_dimensions.screen / camera_width_world_units, feature_dimensions.screen / 2) self._world_to_feature_screen_px = transform.Chain( self._world_to_world_tl, self._world_tl_to_world_camera_rel, world_camera_rel_to_feature_screen, transform.PixelToCoord())
def world_to_screen_pos(game_info, pos, obs): """ :param game_info: env.game_info :param pos: target_world_space_pos :param obs: obs.raw_observation.observation.raw_data.player.camera :return: screen_pos """ # init parameter and define map_size = point.Point.build(game_info.start_raw.map_size) fl_opts = game_info.options.feature_layer feature_layer_screen_size = point.Point.build(fl_opts.resolution) camera_width_world_units = fl_opts.width world_to_screen = transform.Linear(point.Point(1, -1), point.Point(0, map_size.y)) screen_to_fl_screen = transform.Linear(feature_layer_screen_size / camera_width_world_units) world_to_fl_screen = transform.Chain(world_to_screen, screen_to_fl_screen, transform.Floor()) # Update the camera transform based on the new camera center. camera_center = obs.raw_observation.observation.raw_data.player.camera camera_radius = (feature_layer_screen_size / feature_layer_screen_size.x * camera_width_world_units / 2) camera_center = point.Point.build(camera_center) center = camera_center.bound(camera_radius, map_size - camera_radius) camera = point.Rect((center - camera_radius).bound(map_size), (center + camera_radius).bound(map_size)) world_to_screen.offset = (-camera.bl * world_to_screen.scale) trans_pos = world_to_fl_screen.fwd_pt(point.Point.build(pos)) return np.clip(np.array(trans_pos), 0, 63).tolist()
def update_transformations(self): # Create transformations self._world_to_minimap = transform.Linear(point.Point(1, -1), point.Point(0, self._map_size.y)) max_map_dim = self._map_size.max_dim() self._minimap_to_fl_minimap = transform.Linear( self._minimap_size / max_map_dim) self._world_to_fl_minimap = transform.Chain( self._world_to_minimap, self._minimap_to_fl_minimap, transform.Floor()) # Flip and zoom to the camera area. Update the offset as the camera moves. # Get camera corner cam_x = -1 * (self._camera_pos['x'] - (self._camera_width / 2)) cam_y = 1 * (self._camera_pos['y'] + (self._camera_width / 2)) cam_pos = point.Point(x = cam_x, y = cam_y) self._reorient_world = transform.Linear(point.Point(1, -1), offset=cam_pos) self._world_to_screen = transform.Linear(point.Point(1, 1), point.Point(0, 0)) #self._world_to_screen = transform.Linear(point.Point(1, -1), # point.Point(0, self._map_size.y)) self._screen_to_fl_screen = transform.Linear( self._screen_size / self._camera_width) self._world_to_fl_screen = transform.Chain( self._reorient_world, self._world_to_screen, self._screen_to_fl_screen, transform.PixelToCoord())
def __init__(self, env: StarCraft2Env, mode: str): os.environ["PYGAME_HIDE_SUPPORT_PROMPT"] = "hide" self.env = env self.mode = mode self.obs = None self._window_scale = 0.75 self.game_info = game_info = self.env._controller.game_info() self.static_data = self.env._controller.data() self._obs_queue: queue.Queue = queue.Queue() self._game_times: collections.deque = collections.deque(maxlen=100) # Avg FPS over 100 frames. # pytype: disable=wrong-keyword-args self._render_times: collections.deque = collections.deque(maxlen=100) # pytype: disable=wrong-keyword-args self._last_time = time.time() self._last_game_loop = 0 self._name_lengths: dict = {} self._map_size = point.Point.build(game_info.start_raw.map_size) self._playable = point.Rect( point.Point.build(game_info.start_raw.playable_area.p0), point.Point.build(game_info.start_raw.playable_area.p1), ) window_size_px = point.Point(self.env.window_size[0], self.env.window_size[1]) window_size_px = self._map_size.scale_max_size( window_size_px * self._window_scale).ceil() self._scale = window_size_px.y // 32 self.display = pygame.Surface(window_size_px) if mode == "human": self.display = pygame.display.set_mode( window_size_px, 0, 32) # type: ignore # noqa: E501 pygame.display.init() pygame.display.set_caption("Starcraft Viewer") pygame.font.init() self._world_to_world_tl = transform.Linear( point.Point(1, -1), point.Point(0, self._map_size.y)) self._world_tl_to_screen = transform.Linear(scale=window_size_px / 32) self.screen_transform = transform.Chain(self._world_to_world_tl, self._world_tl_to_screen) surf_loc = point.Rect(point.origin, window_size_px) sub_surf = self.display.subsurface( pygame.Rect(surf_loc.tl, surf_loc.size)) self._surf = _Surface(sub_surf, None, surf_loc, self.screen_transform, None, self.draw_screen) self._font_small = pygame.font.Font(None, int(self._scale * 0.5)) self._font_large = pygame.font.Font(None, self._scale) self.upgrade_colors = [ colors.black, # unused... colors.white * 0.6, colors.white * 0.8, colors.white, ]
def _init_camera(self): self._world_to_world_tl = transform.Linear( point.Point(1, -1), point.Point(0, self._map_size.y)) self._world_tl_to_world_camera_rel = transform.Linear( offset=-self._map_size / 4) self._world_to_feature_screen_px = transform.Chain( self._world_to_world_tl, self._world_tl_to_world_camera_rel, transform.Linear( (self._screen_size_px / self._camera_width_world_units), self._screen_size_px / 2), transform.PixelToCoord())
def __init__(self): self._feature_layer_screen_size = point.Point(84.0, 84.0) self._camera_width_world_units = 24.0 self._map_size = point.Point(64, 64) self._camera_center = point.Point(33.0, 25.0) self._world_to_screen = transform.Linear( point.Point(1, -1), point.Point(0, self._map_size.y)) self._screen_to_fl_screen = transform.Linear( self._feature_layer_screen_size / self._camera_width_world_units) self._world_to_fl_screen = transform.Chain(self._world_to_screen, self._screen_to_fl_screen, transform.Floor()) self._update_camera(self._camera_center)
def world_to_minimap_pos(game_info, pos): map_size = point.Point.build(game_info.start_raw.map_size) fl_opts = game_info.options.feature_layer feature_layer_minimap_size = point.Point.build(fl_opts.minimap_resolution) max_map_dim = map_size.max_dim() world_to_minimap = transform.Linear(point.Point(1, -1), point.Point(0, map_size.y)) minimap_to_fl_minimap = transform.Linear(feature_layer_minimap_size / max_map_dim) world_to_fl_minimap = transform.Chain(world_to_minimap, minimap_to_fl_minimap, transform.Floor()) trans_pos = world_to_fl_minimap.fwd_pt(point.Point.build(pos)) return np.clip(np.array(trans_pos), 0, 63).tolist()
def _init_camera(self, feature_dimensions, map_size, camera_width_world_units): """Initialize the feature_units camera.""" if not map_size or not camera_width_world_units: raise ValueError( "Either pass the game_info with raw enabled, or map_size and " "camera_width_world_units in order to use feature_units.") map_size = point.Point.build(map_size) self._world_to_world_tl = transform.Linear(point.Point(1, -1), point.Point(0, map_size.y)) self._world_tl_to_world_camera_rel = transform.Linear( offset=-map_size / 4) world_camera_rel_to_feature_screen = transform.Linear( feature_dimensions.screen / camera_width_world_units, feature_dimensions.screen / 2) self._world_to_feature_screen_px = transform.Chain( self._world_to_world_tl, self._world_tl_to_world_camera_rel, world_camera_rel_to_feature_screen, transform.PixelToCoord())
def _world_tl_to_minimap_px(raw_unit): # TODO configurable resolution minimap_px = point.Point(64.0, 64.0) map_size = point.Point(88.0, 96.0) pos_transform = transform.Chain( transform.Linear(minimap_px / map_size.max_dim()), transform.PixelToCoord()) screen_pos = pos_transform.fwd_pt(point.Point(raw_unit.x, raw_unit.y)) return screen_pos.x, screen_pos.y
def screen_to_minimap_pos(game_info, screen_pos, obs): screen_pos = Pos(screen_pos[0], screen_pos[1]) # init parameter and define map_size = point.Point.build(game_info.start_raw.map_size) fl_opts = game_info.options.feature_layer feature_layer_screen_size = point.Point.build(fl_opts.resolution) feature_layer_minimap_size = point.Point.build(fl_opts.minimap_resolution) # screen to world camera_width_world_units = fl_opts.width world_to_screen = transform.Linear(point.Point(1, -1), point.Point(0, map_size.y)) screen_to_fl_screen = transform.Linear(feature_layer_screen_size / camera_width_world_units) world_to_fl_screen = transform.Chain(world_to_screen, screen_to_fl_screen, transform.Floor()) # Update the camera transform based on the new camera center. camera_center = obs.raw_observation.observation.raw_data.player.camera camera_radius = (feature_layer_screen_size / feature_layer_screen_size.x * camera_width_world_units / 2) camera_center = point.Point.build(camera_center) center = camera_center.bound(camera_radius, map_size - camera_radius) camera = point.Rect((center - camera_radius).bound(map_size), (center + camera_radius).bound(map_size)) world_to_screen.offset = (-camera.bl * world_to_screen.scale) world_pos = world_to_fl_screen.back_pt(point.Point.build(screen_pos)) # world to minimap max_map_dim = map_size.max_dim() world_to_minimap = transform.Linear(point.Point(1, -1), point.Point(0, map_size.y)) minimap_to_fl_minimap = transform.Linear(feature_layer_minimap_size / max_map_dim) world_to_fl_minimap = transform.Chain(world_to_minimap, minimap_to_fl_minimap, transform.Floor()) minimap_pos = world_to_fl_minimap.fwd_pt(point.Point.build(world_pos)) return np.clip(np.array(minimap_pos), 0, 63).tolist()
def transform_pos(pos): class map_size(object): x = 88 y = 96 class minimap_resolution(object): x = 64 y = 64 map_size_point = point.Point.build(map_size) feature_layer_minimap_point = point.Point.build(minimap_resolution) world_to_minimap = transform.Linear(point.Point(1, -1), point.Point(0, map_size_point.y)) minimap_to_fl_minimap = transform.Linear(feature_layer_minimap_point / map_size_point) world_to_fl_minimap = transform.Chain( world_to_minimap, minimap_to_fl_minimap, transform.Floor() ) class temp(object): x = 0 y = 0 pos_new = np.zeros((pos.shape[0], 2)) for i in range(pos.shape[0]): temp.x = pos[i, 0] temp.y = pos[i, 1] new = world_to_fl_minimap.fwd_pt(point.Point.build(temp)) pos_new[i, 0] = new.x pos_new[i, 1] = new.y return pos_new
def init_window(self): """Initialize the pygame window and lay out the surfaces.""" pygame.init() # Want a roughly square grid of feature layers, each being roughly square. num_feature_layers = (len(features.SCREEN_FEATURES) + len(features.MINIMAP_FEATURES)) cols = math.ceil(math.sqrt(num_feature_layers)) rows = math.ceil(num_feature_layers / cols) features_layout = point.Point(cols, rows * 1.05) # make room for titles # Scale such that features_layout and screen_aspect ratio have the same # height so that we can figure out the max window size and ratio of widths. screen_aspect_ratio = (self._feature_layer_screen_size * (rows / self._feature_layer_screen_size.y)) total = features_layout + point.Point(screen_aspect_ratio.x, 0) window_size_px = total.scale_max_size(_get_max_window_size()).ceil() # Create the actual window surface. This should only be blitted to from one # of the sub-surfaces defined below. self._window = pygame.display.set_mode(window_size_px, 0, 32) pygame.display.set_caption("Starcraft Viewer") # The sub-surfaces that the various draw functions will draw to. self.surfaces = [] def add_surface(surf_type, surf_loc, world_to_surf, draw_fn): """Add a surface. Drawn in order and intersect in reverse order.""" sub_surf = self._window.subsurface( pygame.Rect(surf_loc.tl, surf_loc.size)) self.surfaces.append(_Surface( sub_surf, surf_type, surf_loc, world_to_surf, draw_fn)) self.scale = window_size_px.y // 30 self.font_small = pygame.font.Font(None, int(self.scale * 0.5)) self.font_large = pygame.font.Font(None, self.scale) # Just flip so the base minimap is TL origin self._world_to_minimap = transform.Linear(point.Point(1, -1), point.Point(0, self._map_size.y)) max_map_dim = self._map_size.max_dim() self._minimap_to_fl_minimap = transform.Linear( self._feature_layer_minimap_size / max_map_dim) self._world_to_fl_minimap = transform.Chain( self._world_to_minimap, self._minimap_to_fl_minimap, transform.Floor()) # Flip and zoom to the camera area. Update the offset as the camera moves. self._world_to_screen = transform.Linear(point.Point(1, -1), point.Point(0, self._map_size.y)) self._screen_to_fl_screen = transform.Linear( self._feature_layer_screen_size / self._camera_width_world_units) self._world_to_fl_screen = transform.Chain( self._world_to_screen, self._screen_to_fl_screen, transform.Floor()) # Renderable space for the screen. self.screen_size_px = self._feature_layer_screen_size.scale_max_size( window_size_px) screen_to_visual_screen = transform.Linear( self.screen_size_px.x / self._camera_width_world_units) add_surface(SurfType.SCREEN, point.Rect(point.origin, self.screen_size_px), transform.Chain( self._world_to_screen, screen_to_visual_screen), self.draw_screen) # Renderable space for the minimap. self.minimap_size_px = self._map_size.scale_max_size( self.screen_size_px / 4) minimap_to_visual_minimap = transform.Linear( self.minimap_size_px.max_dim() / max_map_dim) minimap_offset = point.Point(0, (self.screen_size_px.y - self.minimap_size_px.y)) add_surface(SurfType.MINIMAP, point.Rect(minimap_offset, minimap_offset + self.minimap_size_px), transform.Chain( self._world_to_minimap, minimap_to_visual_minimap), self.draw_mini_map) # Add the feature layers features_loc = point.Point(self.screen_size_px.x, 0) feature_pane = self._window.subsurface( pygame.Rect(features_loc, window_size_px - features_loc)) feature_pane.fill(colors.white / 2) feature_pane_size = point.Point(*feature_pane.get_size()) feature_grid_size = feature_pane_size / point.Point(cols, rows) feature_layer_area = self._feature_layer_screen_size.scale_max_size( feature_grid_size) feature_layer_size = feature_layer_area * 0.9 feature_layer_padding = (feature_layer_area - feature_layer_size) / 2 feature_font_size = int(feature_grid_size.y * 0.09) feature_font = pygame.font.Font(None, feature_font_size) feature_counter = itertools.count() def add_feature_layer(feature, surf_type, world_to_surf): """Add a feature layer surface.""" i = next(feature_counter) grid_offset = point.Point(i % cols, i // cols) * feature_grid_size text = feature_font.render(feature.full_name, True, colors.white) rect = text.get_rect() rect.center = grid_offset + point.Point(feature_grid_size.x / 2, feature_font_size) feature_pane.blit(text, rect) surf_loc = (features_loc + grid_offset + feature_layer_padding + point.Point(0, feature_font_size)) add_surface(surf_type, point.Rect(surf_loc, surf_loc + feature_layer_size), world_to_surf, lambda surf: self.draw_feature_layer(surf, feature)) # Add the minimap feature layers fl_minimap_to_fl_surf = transform.Linear( feature_layer_size / self._feature_layer_minimap_size) world_to_fl_minimap_surf = transform.Chain( self._world_to_minimap, self._minimap_to_fl_minimap, transform.Center(), fl_minimap_to_fl_surf) for feature in features.MINIMAP_FEATURES: add_feature_layer(feature, SurfType.MINIMAP, world_to_fl_minimap_surf) # Add the screen feature layers fl_screen_to_fl_surf = transform.Linear( feature_layer_size / self._feature_layer_screen_size) world_to_fl_screen_surf = transform.Chain( self._world_to_screen, self._screen_to_fl_screen, transform.Center(), fl_screen_to_fl_surf) for feature in features.SCREEN_FEATURES: add_feature_layer(feature, SurfType.SCREEN, world_to_fl_screen_surf) # Add the help screen add_surface(SurfType.CHROME, point.Rect(window_size_px / 4, window_size_px * 3 / 4), None, self.draw_help) # Arbitrarily set the initial camera to the center of the map. self._update_camera(self._map_size / 2)
def init_window(self): """Initialize the pygame window and lay out the surfaces.""" if os.name == "nt": # Enable DPI awareness on Windows to give the correct window size. ctypes.windll.user32.SetProcessDPIAware() # pytype: disable=module-attr pygame.init() if self._render_rgb and self._rgb_screen_px: main_screen_px = self._rgb_screen_px else: main_screen_px = self._feature_screen_px window_size_ratio = main_screen_px if self._feature_screen_px and self._render_feature_grid: # Want a roughly square grid of feature layers, each being roughly square. num_feature_layers = (len(features.SCREEN_FEATURES) + len(features.MINIMAP_FEATURES)) feature_cols = math.ceil(math.sqrt(num_feature_layers)) feature_rows = math.ceil(num_feature_layers / feature_cols) features_layout = point.Point(feature_cols, feature_rows * 1.05) # make room for titles # Scale features_layout to main_screen_px height so we know its width. features_aspect_ratio = (features_layout * main_screen_px.y / features_layout.y) window_size_ratio += point.Point(features_aspect_ratio.x, 0) window_size_px = window_size_ratio.scale_max_size( _get_max_window_size()).ceil() # Create the actual window surface. This should only be blitted to from one # of the sub-surfaces defined below. self._window = pygame.display.set_mode(window_size_px, 0, 32) pygame.display.set_caption("Starcraft Viewer") # The sub-surfaces that the various draw functions will draw to. self._surfaces = [] def add_surface(surf_type, surf_loc, world_to_surf, world_to_obs, draw_fn): """Add a surface. Drawn in order and intersect in reverse order.""" sub_surf = self._window.subsurface( pygame.Rect(surf_loc.tl, surf_loc.size)) self._surfaces.append(_Surface( sub_surf, surf_type, surf_loc, world_to_surf, world_to_obs, draw_fn)) self._scale = window_size_px.y // 30 self._font_small = pygame.font.Font(None, int(self._scale * 0.5)) self._font_large = pygame.font.Font(None, self._scale) def check_eq(a, b): """Used to run unit tests on the transforms.""" assert (a - b).len() < 0.0001, "%s != %s" % (a, b) # World has origin at bl, world_tl has origin at tl. self._world_to_world_tl = transform.Linear( point.Point(1, -1), point.Point(0, self._map_size.y)) check_eq(self._world_to_world_tl.fwd_pt(point.Point(0, 0)), point.Point(0, self._map_size.y)) check_eq(self._world_to_world_tl.fwd_pt(point.Point(5, 10)), point.Point(5, self._map_size.y - 10)) # Move the point to be relative to the camera. This gets updated per frame. self._world_tl_to_world_camera_rel = transform.Linear( offset=-self._map_size / 4) check_eq(self._world_tl_to_world_camera_rel.fwd_pt(self._map_size / 4), point.Point(0, 0)) check_eq( self._world_tl_to_world_camera_rel.fwd_pt( (self._map_size / 4) + point.Point(5, 10)), point.Point(5, 10)) if self._feature_screen_px: # Feature layer locations in continuous space. feature_world_per_pixel = (self._feature_screen_px / self._feature_camera_width_world_units) world_camera_rel_to_feature_screen = transform.Linear( feature_world_per_pixel, self._feature_screen_px / 2) check_eq(world_camera_rel_to_feature_screen.fwd_pt(point.Point(0, 0)), self._feature_screen_px / 2) check_eq( world_camera_rel_to_feature_screen.fwd_pt( point.Point(-0.5, -0.5) * self._feature_camera_width_world_units), point.Point(0, 0)) self._world_to_feature_screen = transform.Chain( self._world_to_world_tl, self._world_tl_to_world_camera_rel, world_camera_rel_to_feature_screen) self._world_to_feature_screen_px = transform.Chain( self._world_to_feature_screen, transform.PixelToCoord()) world_tl_to_feature_minimap = transform.Linear( self._feature_minimap_px / self._map_size.max_dim()) check_eq(world_tl_to_feature_minimap.fwd_pt(point.Point(0, 0)), point.Point(0, 0)) check_eq(world_tl_to_feature_minimap.fwd_pt(self._map_size), self._map_size.scale_max_size(self._feature_minimap_px)) self._world_to_feature_minimap = transform.Chain( self._world_to_world_tl, world_tl_to_feature_minimap) self._world_to_feature_minimap_px = transform.Chain( self._world_to_feature_minimap, transform.PixelToCoord()) if self._rgb_screen_px: # RGB pixel locations in continuous space. # TODO(tewalds): Use a real 3d projection instead of orthogonal. rgb_world_per_pixel = (self._rgb_screen_px / 24) world_camera_rel_to_rgb_screen = transform.Linear( rgb_world_per_pixel, self._rgb_screen_px / 2) check_eq(world_camera_rel_to_rgb_screen.fwd_pt(point.Point(0, 0)), self._rgb_screen_px / 2) check_eq( world_camera_rel_to_rgb_screen.fwd_pt( point.Point(-0.5, -0.5) * 24), point.Point(0, 0)) self._world_to_rgb_screen = transform.Chain( self._world_to_world_tl, self._world_tl_to_world_camera_rel, world_camera_rel_to_rgb_screen) self._world_to_rgb_screen_px = transform.Chain( self._world_to_rgb_screen, transform.PixelToCoord()) world_tl_to_rgb_minimap = transform.Linear( self._rgb_minimap_px / self._map_size.max_dim()) check_eq(world_tl_to_rgb_minimap.fwd_pt(point.Point(0, 0)), point.Point(0, 0)) check_eq(world_tl_to_rgb_minimap.fwd_pt(self._map_size), self._map_size.scale_max_size(self._rgb_minimap_px)) self._world_to_rgb_minimap = transform.Chain( self._world_to_world_tl, world_tl_to_rgb_minimap) self._world_to_rgb_minimap_px = transform.Chain( self._world_to_rgb_minimap, transform.PixelToCoord()) # Renderable space for the screen. screen_size_px = main_screen_px.scale_max_size(window_size_px) minimap_size_px = self._map_size.scale_max_size(screen_size_px / 4) minimap_offset = point.Point(0, (screen_size_px.y - minimap_size_px.y)) if self._render_rgb: rgb_screen_to_main_screen = transform.Linear( screen_size_px / self._rgb_screen_px) add_surface(SurfType.RGB | SurfType.SCREEN, point.Rect(point.origin, screen_size_px), transform.Chain( # surf self._world_to_rgb_screen, rgb_screen_to_main_screen), self._world_to_rgb_screen_px, self.draw_screen) rgb_minimap_to_main_minimap = transform.Linear( minimap_size_px / self._rgb_minimap_px) add_surface(SurfType.RGB | SurfType.MINIMAP, point.Rect(minimap_offset, minimap_offset + minimap_size_px), transform.Chain( # surf self._world_to_rgb_minimap, rgb_minimap_to_main_minimap), self._world_to_rgb_minimap_px, self.draw_mini_map) else: feature_screen_to_main_screen = transform.Linear( screen_size_px / self._feature_screen_px) add_surface(SurfType.FEATURE | SurfType.SCREEN, point.Rect(point.origin, screen_size_px), transform.Chain( # surf self._world_to_feature_screen, feature_screen_to_main_screen), self._world_to_feature_screen_px, self.draw_screen) feature_minimap_to_main_minimap = transform.Linear( minimap_size_px / self._feature_minimap_px) add_surface(SurfType.FEATURE | SurfType.MINIMAP, point.Rect(minimap_offset, minimap_offset + minimap_size_px), transform.Chain( # surf self._world_to_feature_minimap, feature_minimap_to_main_minimap), self._world_to_feature_minimap_px, self.draw_mini_map) if self._feature_screen_px and self._render_feature_grid: # Add the feature layers features_loc = point.Point(screen_size_px.x, 0) feature_pane = self._window.subsurface( pygame.Rect(features_loc, window_size_px - features_loc)) feature_pane.fill(colors.white / 2) feature_pane_size = point.Point(*feature_pane.get_size()) feature_grid_size = feature_pane_size / point.Point(feature_cols, feature_rows) feature_layer_area = self._feature_screen_px.scale_max_size( feature_grid_size) feature_layer_padding = feature_layer_area // 20 feature_layer_size = feature_layer_area - feature_layer_padding * 2 feature_font_size = int(feature_grid_size.y * 0.09) feature_font = pygame.font.Font(None, feature_font_size) feature_counter = itertools.count() def add_feature_layer(feature, surf_type, world_to_surf, world_to_obs): """Add a feature layer surface.""" i = next(feature_counter) grid_offset = point.Point(i % feature_cols, i // feature_cols) * feature_grid_size text = feature_font.render(feature.full_name, True, colors.white) rect = text.get_rect() rect.center = grid_offset + point.Point(feature_grid_size.x / 2, feature_font_size) feature_pane.blit(text, rect) surf_loc = (features_loc + grid_offset + feature_layer_padding + point.Point(0, feature_font_size)) add_surface(surf_type, point.Rect(surf_loc, surf_loc + feature_layer_size), world_to_surf, world_to_obs, lambda surf: self.draw_feature_layer(surf, feature)) # Add the minimap feature layers feature_minimap_to_feature_minimap_surf = transform.Linear( feature_layer_size / self._feature_minimap_px) world_to_feature_minimap_surf = transform.Chain( self._world_to_feature_minimap, feature_minimap_to_feature_minimap_surf) for feature in features.MINIMAP_FEATURES: add_feature_layer(feature, SurfType.FEATURE | SurfType.MINIMAP, world_to_feature_minimap_surf, self._world_to_feature_minimap_px) # Add the screen feature layers feature_screen_to_feature_screen_surf = transform.Linear( feature_layer_size / self._feature_screen_px) world_to_feature_screen_surf = transform.Chain( self._world_to_feature_screen, feature_screen_to_feature_screen_surf) for feature in features.SCREEN_FEATURES: add_feature_layer(feature, SurfType.FEATURE | SurfType.SCREEN, world_to_feature_screen_surf, self._world_to_feature_screen_px) # Add the help screen help_size = point.Point( (max(len(s) for s, _ in self.shortcuts) + max(len(s) for _, s in self.shortcuts)) * 0.4 + 4, len(self.shortcuts) + 3) * self._scale help_rect = point.Rect(window_size_px / 2 - help_size / 2, window_size_px / 2 + help_size / 2) add_surface(SurfType.CHROME, help_rect, None, None, self.draw_help) # Arbitrarily set the initial camera to the center of the map. self._update_camera(self._map_size / 2)