def _screen_from_json(json_data: Any, sprites: Dict[str, Sprite]) -> Screen: background_data = json_data.get("background") background_color = None background_tilemap = None if background_data: background_color = background_data.get("background_color") tilemap_data = background_data.get("background_tilemap") if tilemap_data: background_tilemap = Tilemap(tilemap_id=tilemap_data["tilemap_id"], rect_uv=Rect.from_list( tilemap_data["tilemap_uv"])) screen_elements = [] if json_data.get("elements"): screen_elements = [ ScreenElement(position=Point.from_list(data["position"]), sprite=sprites.get(data.get("sprite_ref")), text=data.get("text")) for data in json_data["elements"] ] menu_position = None menu_scrollbar_rect = None if json_data.get("menu"): menu_data = json_data["menu"] menu_position = \ Point.from_list(menu_data["position"]) if menu_data.get("position") else None menu_scrollbar_rect = \ Rect.from_list(menu_data["scrollbar_rect"]) if menu_data.get("scrollbar_rect") else None return Screen(background_color=background_color, background_tilemap=background_tilemap, elements=tuple(screen_elements), menu_position=menu_position, menu_scrollbar_rect=menu_scrollbar_rect)
def process_sprites(input_data: Dict[str, Any], base_dir: Path) -> Dict[str, Any]: """Process and pack sprites from input resource file into sprite sheet. :param input_data: input data from JSON file (root -> sprites) :param base_dir: :return: processed sprites (ready to be serialized to JSON) """ sprite_packers = [ SpriteSheetPacker( 0, Rect.from_coords(0, 128, IMAGE_BANK_WIDTH, IMAGE_BANK_HEIGHT - 128)), SpriteSheetPacker( 1, Rect.from_coords(0, 0, IMAGE_BANK_WIDTH, IMAGE_BANK_HEIGHT)) ] sprites = {} sprites_ids: List[Dict[str, int]] = [{}, {}] for sprite_name, sprite_data in input_data.items(): image_bank = sprite_data["image_bank"] sprite_path = Path(base_dir).joinpath(sprite_data["image"]) sprites_ids[image_bank][sprite_name] = sprite_packers[ image_bank].add_sprite(sprite_path) num_layers = LEVEL_NUM_LAYERS if sprite_data.get("multilayer", False) else 1 transparency_color = -1 if sprite_data.get("transparency_color"): transparency_color = int(sprite_data["transparency_color"], 16) sprites[sprite_name] = { "image_bank": image_bank, "directional": sprite_data.get("directional", False), "transparency_color": transparency_color, "num_frames": sprite_data.get("num_frames", 1), "num_layers": num_layers } sprite_uv_rects = [packer.pack() for packer in sprite_packers] for sprite_name, sprite in sprites.items(): image_bank = sprite["image_bank"] sprite_id = sprites_ids[image_bank][sprite_name] uv_rect = sprite_uv_rects[image_bank][sprite_id] sprite["uv_rect"] = uv_rect.as_list logging.info("Sprite '%s' (%dx%d) added to image bank %d", sprite_name, uv_rect.w, uv_rect.h, image_bank) logging.info("Total sprites: %d", len(sprites)) return sprites
def from_level_num(cls, level_num: int, tileset_index: int, draw_offset: Point, sprite_packs: LevelSpritePacks) -> "LevelTemplate": """Create a new level template for given level number. :param level_num: level number to create level template for :param tileset_index: index of first tile (starting tile) used in the template :param draw_offset: the initial offset of the level (u3sed when level is drawn) :param sprite_packs: sprite packs used in the level :return: newly created level template """ tilemap_u = LEVEL_WIDTH * (level_num % TILE_SIZE) tilemap_v = LEVEL_HEIGHT * (level_num // TILE_SIZE) tilemap_uv_rect = Rect.from_coords(tilemap_u, tilemap_v, LEVEL_WIDTH, LEVEL_HEIGHT) tilemap = Tilemap(LEVEL_BASE_TILEMAP, tilemap_uv_rect, LEVEL_NUM_LAYERS) tileset = Tileset(tileset_index) layers = tuple([ Layer(i, opaque=(i == 0), global_offset=draw_offset) for i in range(LEVEL_NUM_LAYERS) ]) return cls(level_num=level_num, tilemap=tilemap, tileset=tileset, layers=layers, sprite_packs=sprite_packs)
def pack(self, rect: Rect) -> List[Rect]: """Pack all boxes that were added to box packer. :param rect: destination rect to pack boxes in :return: collection of positions for all packed boxes (coords for given box can be found by using box's id assigned during add_box call) """ if not self.boxes: return [] uv_rects: List[Rect] = [Rect.from_coords(0, 0, 0, 0)] * len(self.boxes) sorted_boxes = sorted(self.boxes, key=lambda b: b.size.max_dimension, reverse=True) root_node = _Node(rect) for box in sorted_boxes: node = _Node.find_node_for_box(root_node, box.size) if node: node.split(box.size.width, box.size.height) node.box_id = box.box_id uv_rects[box.box_id] = node.rect else: raise ResourceError( f"Unable to fit box with size ({box.size.width}x{box.size.height})" ) return uv_rects
def split(self, right: int, bottom: int) -> None: """Split node by creating bottom and right child. :param right: the offset for right node :param bottom: the offset for bottom node """ if self.is_split: return self.bottom_child = _Node( Rect.from_coords(self.rect.x, self.rect.y + bottom, self.rect.w, self.rect.h - bottom)) self.right_child = _Node( Rect.from_coords(self.rect.x + right, self.rect.y, self.rect.w - right, bottom)) self.rect = Rect(self.rect.position, Size(right, bottom))
def _process_frames(tilemap_num: int, frame_tileset_data: Any, frame_tilesets: Dict[str, NineSlicingFrame]) -> None: tilemap_rect = tilemap_rect_nth(tilemap_num) for frame_data in frame_tileset_data: frame_tileset = frame_tilesets[frame_data["tileset_ref"]] frame_rect = Rect.from_list(frame_data["rect"]).offset( tilemap_rect.position) frame_tileset.draw_frame(BACKGROUND_TILEMAP_ID, frame_rect)
def tilemap_rect_nth(index: int) -> Rect: """Create a rectangle representing position and size of n-th tilemap in Pyxel's mega-tilemap. :param index: index of tilemap to create rectangle for :return: tilemap's rect (representing position and size of a tilemap expressed in tiles) """ levels_horizontally = TILEMAP_WIDTH // LEVEL_WIDTH return Rect.from_coords((index % levels_horizontally) * LEVEL_WIDTH, (index // levels_horizontally) * LEVEL_HEIGHT, LEVEL_WIDTH, LEVEL_HEIGHT)
def generate_tilemap(self, tilemap_id: int, tilemap_rect: Rect, seed: int) -> None: """Generate a tilemap using random tiles at given position in Pyxel's mega-tilemap. Tiles are randomized using specified seed and tiles weights. :param tilemap_id: Pyxel's mega-tilemap id :param tilemap_rect: tilemap rect where generated tiles will be put into :param seed: seed to be used during tiles generation """ state = random.getstate() random.seed(seed) tilemap_points = tilemap_rect.inside_points() for point in tilemap_points: pyxel.tilemap(tilemap_id).set(point.x, point.y, self._next_tile()) random.setstate(state)
def create_sprites(json_data: Any) -> Dict[str, Sprite]: """Create sprites from metadata. :param json_data: input JSON containing sprites metadata :return: collection of sprites """ return { name: Sprite(image_bank=data["image_bank"], uv_rect=Rect.from_list(data["uv_rect"]), directional=data["directional"], transparency_color=data["transparency_color"], num_layers=data["num_layers"], num_frames=data["num_frames"]) for name, data in json_data.items() }