def __init__(self, dma: Dma, dpci: Dpci, dpc: Dpc, dpl: Dpl): self._cached_rules = None self._cached_dungeon_surface = None self.dma = dma self.dpci = dpci self.dpc = dpc self.dpl = dpl self.dma_drawer = DmaDrawer(self.dma) chunks = self.dpc.chunks_to_pil(self.dpci, self.dpl.palettes, 1) self.single_tiles = { DmaType.FLOOR: self._single_tile(chunks, DmaType.FLOOR), DmaType.WALL: self._single_tile(chunks, DmaType.WALL), DmaType.WATER: self._single_tile(chunks, DmaType.WATER), }
def __init__(self, dbg: Dbg, dbg_dpci: Dpci, dbg_dpc: Dpc, dbg_dpl: Dpl, dma: Dma, chunks: Image.Image): self.dbg = dbg self.dbg_dpci = dbg_dpci self.dbg_dpc = dbg_dpc self.dbg_dpl = dbg_dpl self.dma = dma self.chunks = chunks self.dma_drawer = DmaDrawer(self.dma) self._cached_bg = None self._cached_rules = None self._cached_dungeon_surface = None self.single_tiles = { DmaType.FLOOR: self._single_tile(DmaType.FLOOR), DmaType.WALL: self._single_tile(DmaType.WALL), DmaType.WATER: self._single_tile(DmaType.WATER), }
class FixedFloorDrawerTileset(AbstractTilesetRenderer): def __init__(self, dma: Dma, dpci: Dpci, dpc: Dpc, dpl: Dpl): self._cached_rules = None self._cached_dungeon_surface = None self.dma = dma self.dpci = dpci self.dpc = dpc self.dpl = dpl self.dma_drawer = DmaDrawer(self.dma) chunks = self.dpc.chunks_to_pil(self.dpci, self.dpl.palettes, 1) self.single_tiles = { DmaType.FLOOR: self._single_tile(chunks, DmaType.FLOOR), DmaType.WALL: self._single_tile(chunks, DmaType.WALL), DmaType.WATER: self._single_tile(chunks, DmaType.WATER), } def get_background(self) -> Optional[cairo.Surface]: return None def get_dungeon(self, rules: List[List[DmaType]]) -> cairo.Surface: # TODO: If rules change only update the parts that need to be updated if rules != self._cached_rules: mappings = self.dma_drawer.get_mappings_for_rules(rules, treat_outside_as_wall=True, variation_index=0) self._cached_dungeon_surface = pil_to_cairo_surface( self.dma_drawer.draw(mappings, self.dpci, self.dpc, self.dpl, None)[0].convert('RGBA') ) self._cached_rules = rules return self._cached_dungeon_surface def get_single_tile(self, tile: DmaType) -> cairo.Surface: return self.single_tiles[tile] def _single_tile(self, chunks, type): index = self.dma.get(type, False)[0] chunk_dim = DPC_TILING_DIM * DPCI_TILE_DIM return pil_to_cairo_surface( chunks.crop((0, index * chunk_dim, chunk_dim, index * chunk_dim + chunk_dim)).convert('RGBA') )
def draw_full(self, ctx: cairo.Context, bma_chunks: List[int], bma_chunk_width: int, bma_chunk_height: int): if bma_chunk_width != self._cached__bma_chunk_width or self._cached__bma_chunks != bma_chunks: self._cached__bma_chunk_width = bma_chunk_width self._cached__bma_chunks = list(bma_chunks) self._cached = None if self._cached is None: drawer = DmaDrawer(self.dma) if self.fixed_room: rules = drawer.rules_from_fixed_room(self.fixed_room) else: rules = drawer.rules_from_bma(bma_chunks, bma_chunk_width) mappings = drawer.get_mappings_for_rules( rules, treat_outside_as_wall=True, variation_index=0) frame = pil_to_cairo_surface( drawer.draw(mappings, self.dpci, self.dpc, self.dpl, None)[0].convert('RGBA')) self._cached = frame ctx.set_source_surface(self._cached) ctx.get_source().set_filter(cairo.Filter.NEAREST) ctx.paint()
class FixedFloorDrawerBackground(AbstractTilesetRenderer): def __init__(self, dbg: Dbg, dbg_dpci: Dpci, dbg_dpc: Dpc, dbg_dpl: Dpl, dma: Dma, chunks: Image.Image): self.dbg = dbg self.dbg_dpci = dbg_dpci self.dbg_dpc = dbg_dpc self.dbg_dpl = dbg_dpl self.dma = dma self.chunks = chunks self.dma_drawer = DmaDrawer(self.dma) self._cached_bg = None self._cached_rules = None self._cached_dungeon_surface = None self.single_tiles = { DmaType.FLOOR: self._single_tile(DmaType.FLOOR), DmaType.WALL: self._single_tile(DmaType.WALL), DmaType.WATER: self._single_tile(DmaType.WATER), } def get_background(self) -> Optional[cairo.Surface]: if not self._cached_bg: self._cached_bg = pil_to_cairo_surface( self.dbg.to_pil(self.dbg_dpc, self.dbg_dpci, self.dbg_dpl.palettes).convert('RGBA')) return self._cached_bg def get_dungeon(self, rules: List[List[DmaType]]) -> cairo.Surface: # TODO: If rules change only update the parts that need to be updated if rules != self._cached_rules: mappings = self.dma_drawer.get_mappings_for_rules( rules, treat_outside_as_wall=True, variation_index=0) self._cached_dungeon_surface = pil_to_cairo_surface( self._draw_dungeon(mappings)) self._cached_rules = rules return self._cached_dungeon_surface def get_single_tile(self, tile: DmaType) -> cairo.Surface: return self.single_tiles[tile] def _single_tile(self, type): index = self.dma.get(type, False)[0] chunk_dim = DPC_TILING_DIM * DPCI_TILE_DIM chunk_width = int(self.chunks.width / chunk_dim) cy = int(index / chunk_width) * chunk_dim cx = index % chunk_width * chunk_dim return pil_to_cairo_surface( self.chunks.crop((cx, cy, cx + chunk_dim, cy + chunk_dim))) def _draw_dungeon(self, mappings: List[List[int]]) -> Image.Image: chunk_dim = DPCI_TILE_DIM * DPC_TILING_DIM chunk_width = int(self.chunks.width / chunk_dim) fimg = Image.new( 'RGBA', (len(mappings[0]) * chunk_dim, len(mappings) * chunk_dim)) def paste(chunk_index, x, y): cy = int(chunk_index / chunk_width) * chunk_dim cx = chunk_index % chunk_width * chunk_dim fimg.paste( self.chunks.crop((cx, cy, cx + chunk_dim, cy + chunk_dim)), (x * chunk_dim, y * chunk_dim)) for y, row in enumerate(mappings): for x, cell in enumerate(row): paste(cell, x, y) return fimg
row.append(DmaType.WALL) elif action.tr_type.floor_type == FloorType.SECONDARY: row.append(DmaType.WATER) elif action.tr_type.floor_type == FloorType.FLOOR_OR_WALL: row.append(DmaType.WALL) else: # TODO? Could be something else row.append(DmaType.FLOOR) ridx += 1 row += [outside, outside, outside, outside] rules.append([outside] * (ffloor.width + 8)) rules.append([outside] * (ffloor.width + 8)) rules.append([outside] * (ffloor.width + 8)) rules.append([outside] * (ffloor.width + 8)) drawer = DmaDrawer(dma) mappings = drawer.get_mappings_for_rules(rules, None, True) dungeon_floor = drawer.draw(mappings, dpci, dpc, dpl, dpla)[0].convert('RGBA') ridx = 0 # Draw items and Pokémon for y in range(4, ffloor.height + 4): for x in range(4, ffloor.width + 4): action = ffloor.actions[ridx] if isinstance(action, TileRule): # Leader spawn tile if action.tr_type == TileRuleType.LEADER_SPAWN: draw_monster_sprite(dungeon_floor, x, y, 1, action.direction) # Key walls if action.tr_type == TileRuleType.FL_WA_ROOM_FLAG_0C or action.tr_type == TileRuleType.FL_WA_ROOM_FLAG_0D: draw_text(dungeon_floor, x, y, (0, 255, 0), f'KEY\nDOOR')
def draw_dungeon_map_bgs(rom, dungeon_map_bg_dir, config): os.makedirs(dungeon_map_bg_dir, exist_ok=True) dungeon_bin = FileType.DUNGEON_BIN.deserialize(rom.getFileByName('DUNGEON/dungeon.bin'), config) ground_dungeon_tilesets = HardcodedGroundDungeonTilesets.get_ground_dungeon_tilesets( get_binary_from_rom_ppmdu(rom, config.binaries['overlay/overlay_0011.bin']), config ) dungeons = HardcodedDungeons.get_dungeon_list( get_binary_from_rom_ppmdu(rom, config.binaries['arm9.bin']), config ) mappa = FileType.MAPPA_BIN.deserialize(rom.getFileByName('BALANCE/mappa_s.bin')) levels_by_id = config.script_data.level_list__by_id bg_list_bin = rom.getFileByName('MAP_BG/bg_list.dat') bg_list = FileType.BG_LIST_DAT.deserialize(bg_list_bin) for i, entry in enumerate(ground_dungeon_tilesets): if entry.ground_level >= 0xFFFF: continue level = levels_by_id[entry.ground_level] print(f"{i + 1}/{len(ground_dungeon_tilesets)-1} - {level.name}") print(entry) mappa_idx = dungeons[entry.dungeon_id].mappa_index start_offset = dungeons[entry.dungeon_id].start_after length = dungeons[entry.dungeon_id].number_floors if entry.dungeon_id == 71: print("DEEP CONCEALED RUINS SKIPPED") continue if entry.unk2 == 1: tileset_id = mappa.floor_lists[mappa_idx][start_offset].layout.tileset_id elif entry.unk2 == 100: tileset_id = mappa.floor_lists[mappa_idx][start_offset + length - 1].layout.tileset_id else: raise ValueError("Unknown unk2") if tileset_id == 170: tileset_id = 1 dma: Dma = dungeon_bin.get(f'dungeon{tileset_id}.dma') dpl: Dpl = dungeon_bin.get(f'dungeon{tileset_id}.dpl') dpla: Dpla = dungeon_bin.get(f'dungeon{tileset_id}.dpla') dpci: Dpci = dungeon_bin.get(f'dungeon{tileset_id}.dpci') dpc: Dpc = dungeon_bin.get(f'dungeon{tileset_id}.dpc') bma: Bma = bg_list.level[level.mapid].get_bma(rom) duration = round(1000 / 60 * max(16, min(dpla.durations_per_frame_for_colors))) drawer = DmaDrawer(dma) rules = drawer.rules_from_bma(bma) mappings = drawer.get_mappings_for_rules(rules, treat_outside_as_wall=True, variation_index=0) frames = drawer.draw(mappings, dpci, dpc, dpl, dpla) frames[0].save( os.path.join(dungeon_map_bg_dir, level.name + '.gif'), save_all=True, append_images=frames[1:], duration=duration, loop=0, optimize=False ) frames[0].save( os.path.join(dungeon_map_bg_dir, level.name + '.png') )