def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: START_ACCURACY = self.get_parameter("StartGraphicPos") START_POWER = START_ACCURACY + 12 MAX_POWER = f"[M:B{START_POWER}]" + (f"[M:B{START_POWER + 10}]") * 9 + f"[M:B{START_POWER + 9}]" MAX_ACCU = f"[M:B{START_POWER}]" + (f"[M:B{START_ACCURACY + 11}]") * 10 + f"[M:B{START_POWER}]" DESC_CHANGES = {8: MAX_POWER, 60: MAX_POWER, 75: MAX_POWER, 100: MAX_POWER, 153: MAX_POWER, 156: MAX_POWER, 205: MAX_POWER, 206: MAX_POWER, 348: MAX_POWER, 477: MAX_POWER, 61: MAX_ACCU, 340: MAX_ACCU, 535: MAX_ACCU} bin_before = rom.getFileByName("FONT/markfont.dat") model = GraphicFontHandler.deserialize(bin_before) entries = [] for x in range(model.get_nb_entries()): entries.append(model.get_entry(x)) while len(entries) < max(START_ACCURACY + 12, START_POWER + 11): entries.append(None) for x in range(START_ACCURACY, START_ACCURACY + 12): img = Image.open(os.path.join(SRC_DIR, "accu_%02d.png" % (x - START_ACCURACY)), 'r') entries[x] = img for x in range(START_POWER, START_POWER + 11): img = Image.open(os.path.join(SRC_DIR, "pow_%02d.png" % (x - START_POWER)), 'r') entries[x] = img model.set_entries(entries) bin_after = GraphicFontHandler.serialize(model) rom.setFileByName("FONT/markfont.dat", bin_after) # Change some move descriptions for filename in get_files_from_rom_with_extension(rom, 'str'): bin_before = rom.getFileByName(filename) strings = StrHandler.deserialize(bin_before) block = config.string_index_data.string_blocks['Move Descriptions'] for k, v in DESC_CHANGES.items(): strings.strings[block.begin + k] = strings.strings[block.begin + k].replace(OLD_STAT, v) bin_after = StrHandler.serialize(strings) rom.setFileByName(filename, bin_after) try: apply() except RuntimeError as ex: raise ex
def draw_object(img: Image.Image, draw, obj: SsaObject, rom: NintendoDSRom): """Draws the sprite for an object""" if obj.object.name == 'NULL': if draw_invisible_actors_objects: # Draw invisible object hitboxes w = obj.hitbox_w * BPC_TILE_DIM h = obj.hitbox_h * BPC_TILE_DIM tlx = obj.pos.x_absolute - int(w / 2) tly = obj.pos.y_absolute - int(h / 2) draw.rectangle(( tlx, tly, tlx + w, tly + h, ), COLOR_OBJECTS, (0, 0, 0)) return try: sprite = FileType.WAN.deserialize( rom.getFileByName(f'GROUND/{obj.object.name}.wan') ) ani_group = sprite.get_animations_for_group(sprite.anim_groups[0]) except (ValueError, TypeError) as e: warnings.warn(f"Failed to render a sprite, replaced with placeholder ({obj}): {e}") if not draw_invisible_actors_objects: return return triangle(draw, obj.pos.x_absolute, obj.pos.y_absolute, COLOR_OBJECTS, obj.pos.direction.id) frame_id = obj.pos.direction.id - 1 if obj.pos.direction.id > 0 else 0 if frame_id > len(ani_group) - 1: frame_id = 0 mfg_id = ani_group[frame_id].frames[0].frame_id sprite_img, (cx, cy) = sprite.render_frame_group(sprite.frame_groups[mfg_id]) render_x = obj.pos.x_absolute - cx render_y = obj.pos.y_absolute - cy img.paste(sprite_img, (render_x, render_y), sprite_img)
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: string_id = STRING_ID_US overlay_size = OVERLAY13_INITAL_SIZE_US if config.game_region == GAME_REGION_EU: string_id = STRING_ID_EU overlay_size = OVERLAY13_INITAL_SIZE_EU if config.game_region == GAME_REGION_JP: string_id = STRING_ID_JP overlay_size = OVERLAY13_INITAL_SIZE_JP # Change dialogue for lang in config.string_index_data.languages: filename = 'MESSAGE/' + lang.filename bin_before = rom.getFileByName(filename) strings = StrHandler.deserialize(bin_before) strings.strings[string_id - 1] = get_locales().translate( MESSAGE, lang.locale.replace('-', '_')) bin_after = StrHandler.serialize(strings) rom.setFileByName(filename, bin_after) table = loadOverlayTable(rom.arm9OverlayTable, lambda x, y: bytes()) ov = table[13] ov.ramSize = overlay_size + OVERLAY13_ADD_SIZE rom.arm9OverlayTable = saveOverlayTable(table) ov13 = rom.files[ov.fileID] rom.files[ov.fileID] = ov13[:overlay_size] try: apply() except RuntimeError as ex: raise ex
def draw_object(img: Image.Image, draw, obj: SsaObject, rom: NintendoDSRom): """Draws the sprite for an object""" if obj.object.name == 'NULL': return triangle(draw, obj.pos.x_absolute, obj.pos.y_absolute, COLOR_ACTOR, obj.pos.direction.id) try: sprite = FileType.WAN.deserialize( rom.getFileByName(f'GROUND/{obj.object.name}.wan')) except ValueError as e: warnings.warn( f"Failed to render a sprite, replaced with placeholder ({obj}): {e}" ) return triangle(draw, obj.pos.x_absolute, obj.pos.y_absolute, COLOR_ACTOR, obj.pos.direction.id) ani_group = sprite.get_animations_for_group(sprite.anim_groups[0]) frame_id = obj.pos.direction.id - 1 if obj.pos.direction.id > 0 else 0 if frame_id > len(ani_group) - 1: frame_id = 0 mfg_id = ani_group[frame_id].frames[0].frame_id sprite_img, (cx, cy) = sprite.render_frame_group(sprite.frame_groups[mfg_id]) render_x = obj.pos.x_absolute - cx render_y = obj.pos.y_absolute - cy img.paste(sprite_img, (render_x, render_y), sprite_img)
def draw_map_bgs(rom: NintendoDSRom, map_bg_dir): global map_bgs, map_bg_durations os.makedirs(map_bg_dir, exist_ok=True) bin = rom.getFileByName('MAP_BG/bg_list.dat') bg_list = FileType.BG_LIST_DAT.deserialize(bin) count = len(bg_list.level) for i, l in enumerate(bg_list.level): try: bma = l.get_bma(rom) print(f"{i + 1}/{count} - {l.bpl_name}") bpas = l.get_bpas(rom) non_none_bpas = [b for b in bpas if b is not None] bpc = l.get_bpc(rom) bpl = l.get_bpl(rom) # Saving animated map! bpa_duration = -1 pal_ani_duration = -1 if len(non_none_bpas) > 0: bpa_duration = round( 1000 / 60 * non_none_bpas[0].frame_info[0].duration_per_frame) if bpl.has_palette_animation: pal_ani_duration = round(1000 / 60 * max(spec.duration_per_frame for spec in bpl.animation_specs)) duration = max(bpa_duration, pal_ani_duration) if duration == -1: # Default for only one frame, doesn't really matter duration = 1000 frames = bma.to_pil(bpc, bpl, bpas, include_collision=False, include_unknown_data_block=False) frames[0].save(os.path.join(map_bg_dir, l.bpl_name + '.gif'), save_all=True, append_images=frames[1:], duration=duration, loop=0, optimize=False) frames[0].save(os.path.join(map_bg_dir, l.bpl_name + '.png')) map_bgs[l.bpl_name] = frames map_bg_durations[l.bpl_name] = duration except (NotImplementedError, SystemError) as ex: print(f"error for {l.bma_name}: {repr(ex)}", file=sys.stderr) print(''.join( traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__)), file=sys.stderr)
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: type_table = TYPE_TABLE_US gummi_iq_table = GUMMI_IQ_TABLE_US gummi_belly_table = GUMMI_BELLY_TABLE_US if config.game_region == GAME_REGION_EU: type_table = TYPE_TABLE_EU gummi_iq_table = GUMMI_IQ_TABLE_EU gummi_belly_table = GUMMI_BELLY_TABLE_EU if config.game_region == GAME_REGION_JP: type_table = TYPE_TABLE_JP gummi_iq_table = GUMMI_IQ_TABLE_JP gummi_belly_table = GUMMI_BELLY_TABLE_JP bincfg = config.binaries['overlay/overlay_0010.bin'] data = bytearray(get_binary_from_rom_ppmdu(rom, bincfg)) data[type_table:type_table + TABLE_LEN] = bytearray(NEW_TYPES) set_binary_in_rom_ppmdu(rom, bincfg, bytes(data)) # Change Fairy's type name for filename in get_files_from_rom_with_extension(rom, 'str'): bin_before = rom.getFileByName(filename) strings = StrHandler.deserialize(bin_before) block = config.string_index_data.string_blocks['Type Names'] strings.strings[block.begin + 18] = TYPE_LIST[filename] bin_after = StrHandler.serialize(strings) rom.setFileByName(filename, bin_after) bincfg = config.binaries['arm9.bin'] data = bytearray(get_binary_from_rom_ppmdu(rom, bincfg)) data[gummi_iq_table:gummi_iq_table + TABLE_LEN] = bytearray(NEW_IQ_GUMMI) data[gummi_belly_table:gummi_belly_table + TABLE_LEN] = bytearray(NEW_BELLY_GUMMI) set_binary_in_rom_ppmdu(rom, bincfg, bytes(data)) try: apply() except RuntimeError as ex: raise ex
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: string_id = STRING_ID_US if config.game_region == GAME_REGION_EU: string_id = STRING_ID_EU if config.game_region == GAME_REGION_JP: string_id = STRING_ID_JP # Change dialogue for lang in config.string_index_data.languages: filename = 'MESSAGE/' + lang.filename bin_before = rom.getFileByName(filename) strings = StrHandler.deserialize(bin_before) strings.strings[string_id - 1] = get_locales().translate(MESSAGE, lang.locale.replace('-', '_')) bin_after = StrHandler.serialize(strings) rom.setFileByName(filename, bin_after) try: apply() except RuntimeError as ex: raise ex
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: param = self.get_parameters() if self.is_applied_ms(rom, config): param["MoveShortcuts"] = 1 else: param["MoveShortcuts"] = 0 if self.is_applied_dv(rom, config): param["DisplayVal"] = 1 else: param["DisplayVal"] = 0 START_EXT = param["StartGraphicPos"] START_LVL = START_EXT + 11 START_SUB = START_LVL + 9 bin_before = rom.getFileByName("FONT/markfont.dat") model = GraphicFontHandler.deserialize(bin_before) entries = [] for x in range(model.get_nb_entries()): entries.append(model.get_entry(x)) while len(entries) < max(START_LVL + 9, START_SUB + 4, START_EXT + 11): entries.append(None) for x in range(START_EXT, START_EXT + 11): img = Image.open( os.path.join(SRC_DIR, "ext%d.png" % (x - START_EXT)), 'r') entries[x] = img for x in range(START_LVL, START_LVL + 9): img = Image.open( os.path.join(SRC_DIR, "lvl%d.png" % (x - START_LVL)), 'r') entries[x] = img for x in range(START_SUB, START_SUB + 4): img = Image.open( os.path.join(SRC_DIR, "sub%d.png" % (x - START_SUB)), 'r') entries[x] = img model.set_entries(entries) bin_after = GraphicFontHandler.serialize(model) rom.setFileByName("FONT/markfont.dat", bin_after) if MGROW_PATH not in rom.filenames: mgrow_data_stat = bytearray(0x96) mgrow_data_dama = bytearray(0x96) for x in range(25): exp_req = MGROW_TABLE[x * 4] pwr = MGROW_TABLE[x * 4 + 1] pp = MGROW_TABLE[x * 4 + 2] acc = MGROW_TABLE[x * 4 + 3] write_u16(mgrow_data_dama, u16(exp_req), x * 6) write_u16(mgrow_data_dama, u16(pwr), x * 6 + 2) write_u8(mgrow_data_dama, u8(pp), x * 6 + 4) write_u8(mgrow_data_dama, u8(acc), x * 6 + 5) bin_before = rom.getFileByName("BALANCE/waza_p.bin") model = WazaPHandler.deserialize(bin_before) total = bytearray(0) for m in model.moves: if m.category != WazaMoveCategory.STATUS and m.max_upgrade_level == 99: total += mgrow_data_dama else: total += mgrow_data_stat create_file_in_rom(rom, MGROW_PATH, bytes(total)) try: apply() except RuntimeError as ex: raise ex
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: item_effect = ITEM_EFFECT_US if config.game_region == GAME_REGION_EU: item_effect = ITEM_EFFECT_EU if config.game_region == GAME_REGION_JP: item_effect = ITEM_EFFECT_JP # Apply Gummi item properties item_p_bin = rom.getFileByName('BALANCE/item_p.bin') item_p_model = ItemPHandler.deserialize(item_p_bin) gummi = item_p_model.item_list[GUMMI_ITEM_ID] gummi.buy_price = 800 gummi.sell_price = 50 gummi.category = 3 gummi.sprite = 17 gummi.item_id = GUMMI_ITEM_ID gummi.move_id = 0 gummi.range_min = 0 gummi.range_max = 0 gummi.palette = 14 gummi.action_name = 4 gummi.is_valid = True gummi.is_in_td = False gummi.ai_flag_1 = False gummi.ai_flag_2 = True gummi.ai_flag_3 = False rom.setFileByName('BALANCE/item_p.bin', ItemPHandler.serialize(item_p_model)) # Apply Gummi item dungeon effect item_cd_bin = rom.getFileByName('BALANCE/item_cd.bin') item_cd_model = DataCDHandler.deserialize(item_cd_bin) effect_id = item_cd_model.nb_effects() item_cd_model.add_effect_code(item_effect) item_cd_model.set_item_effect_id(GUMMI_ITEM_ID, effect_id) rom.setFileByName('BALANCE/item_cd.bin', DataCDHandler.serialize(item_cd_model)) # Change item's text attributes for filename in get_files_from_rom_with_extension(rom, 'str'): bin_before = rom.getFileByName(filename) strings = StrHandler.deserialize(bin_before) block = config.string_index_data.string_blocks['Item Names'] strings.strings[block.begin + GUMMI_ITEM_ID] = NAME_LIST[filename] block = config.string_index_data.string_blocks[ 'Item Short Descriptions'] strings.strings[block.begin + GUMMI_ITEM_ID] = SDES_LIST[filename] block = config.string_index_data.string_blocks[ 'Item Long Descriptions'] strings.strings[block.begin + GUMMI_ITEM_ID] = LDES_LIST[filename] bin_after = StrHandler.serialize(strings) rom.setFileByName(filename, bin_after) bar_bin = rom.getFileByName('BALANCE/itembar.bin') bar_model = DataSTHandler.deserialize(bar_bin) gummi_id = bar_model.get_item_struct_id(OTHER_GUMMI_ID) bar_model.set_item_struct_id(GUMMI_ITEM_ID, gummi_id) rom.setFileByName('BALANCE/itembar.bin', DataSTHandler.serialize(bar_model)) try: apply() except RuntimeError as ex: raise ex
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if not self.is_applied(rom, config): if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: evo_hp_bonus = EVO_HP_BONUS_US evo_ph_bonus = EVO_PHYS_BONUS_US evo_sp_bonus = EVO_SPEC_BONUS_US if config.game_region == GAME_REGION_EU: evo_hp_bonus = EVO_HP_BONUS_EU evo_ph_bonus = EVO_PHYS_BONUS_EU evo_sp_bonus = EVO_SPEC_BONUS_EU if config.game_region == GAME_REGION_JP: evo_hp_bonus = EVO_HP_BONUS_JP evo_ph_bonus = EVO_PHYS_BONUS_JP evo_sp_bonus = EVO_SPEC_BONUS_JP # Create Evo Table md_bin = rom.getFileByName('BALANCE/monster.md') md_model = MdHandler.deserialize(md_bin) mevo_data = bytearray(len(md_model) * MEVO_ENTRY_LENGTH + 4) write_u32(mevo_data, u32_checked(len(mevo_data)), 0) for i in range(len(md_model)): if i in SPECIAL_EVOS: next_stage = SPECIAL_EVOS[i] else: next_stage = [] for j, x in enumerate(md_model): if (i < 600 and 1 <= j < 555) or (i >= 600 and 601 <= j < 1155): if x.pre_evo_index == i and x.evo_method != EvolutionMethod.NONE and ( x.evo_param2 != AdditionalRequirement.MALE or i < 600 ) and (x.evo_param2 != AdditionalRequirement.FEMALE or i >= 600): next_stage.append(j) write_u16(mevo_data, u16_checked(len(next_stage)), i * MEVO_ENTRY_LENGTH + 4) for j, x in enumerate(next_stage): write_u16(mevo_data, x, i * MEVO_ENTRY_LENGTH + j * 2 + 6) if i in SPECIAL_EGGS: next_stage = SPECIAL_EGGS[i] else: next_stage = [] pre_evo = i tries = 0 while md_model[pre_evo].pre_evo_index != 0: current = md_model[pre_evo] pre_evo = current.pre_evo_index if current.evo_param2 == AdditionalRequirement.MALE and md_model[ pre_evo % 600].gender == Gender.MALE and md_model[ pre_evo % 600 + 600].gender == Gender.FEMALE: pre_evo = pre_evo % 600 elif current.evo_param2 == AdditionalRequirement.FEMALE and md_model[ pre_evo % 600].gender == Gender.MALE and md_model[ pre_evo % 600 + 600].gender == Gender.FEMALE: pre_evo = pre_evo % 600 + 600 tries += 1 if tries >= MAX_TRY: raise Exception( _("Infinite recursion detected in pre evolutions for md entry {i}. " )) next_stage.append(pre_evo) if next_stage != [i]: write_u16(mevo_data, u16_checked(len(next_stage)), i * MEVO_ENTRY_LENGTH + 0x16) for j, x in enumerate(next_stage): write_u16(mevo_data, x, i * MEVO_ENTRY_LENGTH + j * 2 + 0x18) hp_bonus = read_u32(rom.arm9, evo_hp_bonus) atk_bonus = read_u16(rom.arm9, evo_ph_bonus) def_bonus = read_u16(rom.arm9, evo_ph_bonus + 2) spatk_bonus = read_u16(rom.arm9, evo_sp_bonus) spdef_bonus = read_u16(rom.arm9, evo_sp_bonus + 2) evo_add_stats = bytearray(MEVO_STATS_LENGTH) write_u16(evo_add_stats, u16_checked(hp_bonus), 0) write_u16(evo_add_stats, atk_bonus, 2) write_u16(evo_add_stats, spatk_bonus, 4) write_u16(evo_add_stats, def_bonus, 6) write_u16(evo_add_stats, spdef_bonus, 8) mevo_data += evo_add_stats * len(md_model) if MEVO_PATH not in rom.filenames: create_file_in_rom(rom, MEVO_PATH, mevo_data) else: rom.setFileByName(MEVO_PATH, mevo_data) try: apply() except RuntimeError as ex: raise ex
def apply(self, apply: Callable[[], None], rom: NintendoDSRom, config: Pmd2Data) -> None: if config.game_version == GAME_VERSION_EOS: if config.game_region == GAME_REGION_US: new_pkmn_str_region = US_NEW_PKMN_STR_REGION new_cat_str_region = US_NEW_CAT_STR_REGION file_assoc = US_FILE_ASSOC table_sf = US_TABLE_SF table_mf = US_TABLE_MF table_sp = US_TABLE_SP if config.game_region == GAME_REGION_EU: new_pkmn_str_region = EU_NEW_PKMN_STR_REGION new_cat_str_region = EU_NEW_CAT_STR_REGION file_assoc = EU_FILE_ASSOC table_sf = EU_TABLE_SF table_mf = EU_TABLE_MF table_sp = EU_TABLE_SP if not self.is_applied(rom, config): bincfg = config.binaries['arm9.bin'] binary = bytearray(get_binary_from_rom_ppmdu(rom, bincfg)) # Apply the patch for filename in get_files_from_rom_with_extension(rom, 'str'): bin_before = rom.getFileByName(filename) strings = StrHandler.deserialize(bin_before) block = config.string_index_data.string_blocks['Pokemon Names'] monsters = strings.strings[block.begin:block.end] strings.strings[block.begin:block.end] = [""] * (block.end - block.begin) block = config.string_index_data.string_blocks[ 'Pokemon Categories'] cats = strings.strings[block.begin:block.end] strings.strings[block.begin:block.end] = [""] * (block.end - block.begin) for x in range(NUM_NEW_ENTRIES): if x < NUM_PREVIOUS_ENTRIES * 2: str_pkmn = monsters[x % NUM_PREVIOUS_ENTRIES] else: str_pkmn = "DmyPk%04d" % x if len(strings.strings) <= new_pkmn_str_region + x - 1: strings.strings.append(str_pkmn) else: strings.strings[new_pkmn_str_region + x - 1] = str_pkmn for x in range(NUM_NEW_ENTRIES): if x < NUM_PREVIOUS_ENTRIES * 2: str_cat = cats[x % NUM_PREVIOUS_ENTRIES] else: str_cat = "DmyCa%04d" % x if len(strings.strings) <= new_cat_str_region + x - 1: strings.strings.append(str_cat) else: strings.strings[new_cat_str_region + x - 1] = str_cat bin_after = StrHandler.serialize(strings) rom.setFileByName(filename, bin_after) sorted_list = list( enumerate(strings.strings[new_pkmn_str_region - 1:new_pkmn_str_region - 1 + NUM_NEW_ENTRIES])) sorted_list.sort(key=lambda x: normalize_string(x[1])) sorted_list = [x[0] for x in sorted_list] inv_sorted_list = [ sorted_list.index(i) for i in range(NUM_NEW_ENTRIES) ] m2n_model = ValListHandler.deserialize( rom.getFileByName(file_assoc[filename][0])) m2n_model.set_list(inv_sorted_list) rom.setFileByName(file_assoc[filename][0], ValListHandler.serialize(m2n_model)) n2m_model = ValListHandler.deserialize( rom.getFileByName(file_assoc[filename][1])) n2m_model.set_list(sorted_list) rom.setFileByName(file_assoc[filename][1], ValListHandler.serialize(n2m_model)) # Expand kao file kao_bin = rom.getFileByName('FONT/kaomado.kao') kao_model = KaoHandler.deserialize(kao_bin) kao_model.expand(NUM_NEW_ENTRIES - 1) for i in range(NUM_PREVIOUS_ENTRIES - 1): for j in range(SUBENTRIES): a = kao_model.get(i, j) b = kao_model.get(i + NUM_PREVIOUS_ENTRIES, j) if b == None and a != None: kao_model.set(i + NUM_PREVIOUS_ENTRIES, j, a) rom.setFileByName('FONT/kaomado.kao', KaoHandler.serialize(kao_model)) # Expand tbl_talk tlk_bin = rom.getFileByName('MESSAGE/tbl_talk.tlk') tlk_model = TblTalkHandler.deserialize(tlk_bin) while tlk_model.get_nb_monsters() < NUM_NEW_ENTRIES: tlk_model.add_monster_personality(DUMMY_PERSONALITY) rom.setFileByName('MESSAGE/tbl_talk.tlk', TblTalkHandler.serialize(tlk_model)) # Add monsters md_bin = rom.getFileByName('BALANCE/monster.md') md_model = MdHandler.deserialize(md_bin) while len(md_model.entries) < NUM_NEW_ENTRIES: md_model.entries.append( MdEntry.new_empty(u16_checked(len(md_model.entries)))) for i in range(NUM_PREVIOUS_ENTRIES): md_model.entries[i].entid = i if md_model.entries[NUM_PREVIOUS_ENTRIES + i].gender == Gender.INVALID: md_model.entries[NUM_PREVIOUS_ENTRIES + i].entid = NUM_PREVIOUS_ENTRIES + i else: md_model.entries[NUM_PREVIOUS_ENTRIES + i].entid = i block = bincfg.symbols['MonsterSpriteData'] data = binary[block.begin:block.end] + binary[block.begin:block. end] data += b'\x00\x00' * (NUM_NEW_ENTRIES - (len(data) // 2)) for i in range(0, len(data), 2): md_model.entries[i // 2].unk17 = data[i] md_model.entries[i // 2].unk18 = data[i + 1] md_model.entries[i // 2].bitfield1_0 = False md_model.entries[i // 2].bitfield1_1 = False md_model.entries[i // 2].bitfield1_2 = False md_model.entries[i // 2].bitfield1_3 = False x = table_sf while read_u16(rom.arm9, x) != 0: pkmn_id = read_u16(rom.arm9, x) md_model.entries[pkmn_id].bitfield1_3 = True # pylint: disable=invalid-sequence-index if md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].gender != Gender.INVALID: md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].bitfield1_3 = True x += 2 x = table_mf while read_u16(rom.arm9, x) != 0: pkmn_id = read_u16(rom.arm9, x) md_model.entries[pkmn_id].bitfield1_2 = True # pylint: disable=invalid-sequence-index if md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].gender != Gender.INVALID: md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].bitfield1_2 = True x += 2 ov19 = rom.loadArm9Overlays([19])[19].data for x in range(table_sp, table_sp + TABLE_SP_SIZE, 2): pkmn_id = read_u16(ov19, x) md_model.entries[pkmn_id].bitfield1_1 = True # pylint: disable=invalid-sequence-index md_model.entries[pkmn_id].bitfield1_0 = True # pylint: disable=invalid-sequence-index if md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].gender != Gender.INVALID: md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].bitfield1_1 = True md_model.entries[NUM_PREVIOUS_ENTRIES + pkmn_id].bitfield1_0 = True rom.setFileByName('BALANCE/monster.md', MdHandler.serialize(md_model)) # Edit Mappa bin mappa_bin = rom.getFileByName('BALANCE/mappa_s.bin') mappa_model = MappaBinHandler.deserialize(mappa_bin) dl = HardcodedDungeons.get_dungeon_list(bytes(rom.arm9), config) # Handle Dojos start_floor = 0 for x in range(DOJO_DUNGEONS_FIRST, DOJO_DUNGEONS_LAST - 2): dl.append( DungeonDefinition(u8(5), DOJO_MAPPA_ENTRY, u8(start_floor), u8(0))) start_floor += 5 dl.append( DungeonDefinition(u8(1), DOJO_MAPPA_ENTRY, u8(start_floor), u8(0))) start_floor += 1 dl.append( DungeonDefinition(u8(0x30), DOJO_MAPPA_ENTRY, u8(start_floor), u8(0))) start_floor += 0x30 for dungeon in dl: for f in range(dungeon.start_after + 1, dungeon.start_after + dungeon.number_floors, 2): try: for entry in mappa_model.floor_lists[ dungeon.mappa_index][f].monsters: if entry.md_index != DUMMY_PKMN and entry.md_index < NUM_PREVIOUS_ENTRIES and \ entry.md_index + NUM_PREVIOUS_ENTRIES < len(md_model.entries) and \ md_model.entries[entry.md_index + NUM_PREVIOUS_ENTRIES].gender != Gender.INVALID: entry.md_index += NUM_PREVIOUS_ENTRIES except: print(f"{dungeon.mappa_index}, {f} is not valid.") rom.setFileByName('BALANCE/mappa_s.bin', MappaBinHandler.serialize(mappa_model)) # Add moves waza_p_bin = rom.getFileByName('BALANCE/waza_p.bin') waza_p_model = WazaPHandler.deserialize(waza_p_bin) while len(waza_p_model.learnsets) < NUM_PREVIOUS_ENTRIES: waza_p_model.learnsets.append( waza_p_model.learnsets[DUMMY_LS]) # Max Moveset waza_p_model.learnsets = waza_p_model.learnsets + waza_p_model.learnsets while len(waza_p_model.learnsets) < NUM_NEW_ENTRIES: waza_p_model.learnsets.append( waza_p_model.learnsets[DUMMY_LS]) # Max Moveset rom.setFileByName('BALANCE/waza_p.bin', WazaPHandler.serialize(waza_p_model)) # Add moves 2 waza_p_bin = rom.getFileByName('BALANCE/waza_p2.bin') waza_p_model = WazaPHandler.deserialize(waza_p_bin) while len(waza_p_model.learnsets) < NUM_PREVIOUS_ENTRIES: waza_p_model.learnsets.append( waza_p_model.learnsets[DUMMY_LS]) # Max Moveset waza_p_model.learnsets = waza_p_model.learnsets + waza_p_model.learnsets while len(waza_p_model.learnsets) < NUM_NEW_ENTRIES: waza_p_model.learnsets.append( waza_p_model.learnsets[DUMMY_LS]) # Max Moveset rom.setFileByName('BALANCE/waza_p2.bin', WazaPHandler.serialize(waza_p_model)) # Add levels level_bin = rom.getFileByName('BALANCE/m_level.bin') level_model = BinPackHandler.deserialize(level_bin) while len(level_model.get_files_bytes()) < NUM_PREVIOUS_ENTRIES: new_bytes_unpacked = bytes(LEVEL_BIN_ENTRY_LEVEL_LEN * 100) new_bytes_pkdpx = PkdpxHandler.serialize( PkdpxHandler.compress(new_bytes_unpacked)) new_bytes = Sir0Handler.serialize( Sir0Handler.wrap(new_bytes_pkdpx, [])) level_model.append(new_bytes) # Empty Levelup data for i in range(NUM_PREVIOUS_ENTRIES): level_model.append(level_model[i]) while len(level_model.get_files_bytes()) < NUM_NEW_ENTRIES: new_bytes_unpacked = bytes(LEVEL_BIN_ENTRY_LEVEL_LEN * 100) new_bytes_pkdpx = PkdpxHandler.serialize( PkdpxHandler.compress(new_bytes_unpacked)) new_bytes = Sir0Handler.serialize( Sir0Handler.wrap(new_bytes_pkdpx, [])) level_model.append(new_bytes) # Empty Levelup data rom.setFileByName('BALANCE/m_level.bin', BinPackHandler.serialize(level_model)) # Add evolutions evo_bin = rom.getFileByName('BALANCE/md_evo.bin') evo_model = MdEvoHandler.deserialize(evo_bin) while len(evo_model.evo_entries) < NUM_NEW_ENTRIES: evo_model.evo_entries.append( MdEvoEntry(bytearray(MEVO_ENTRY_LENGTH))) while len(evo_model.evo_stats) < NUM_NEW_ENTRIES: evo_model.evo_stats.append( MdEvoStats(bytearray(MEVO_STATS_LENGTH))) rom.setFileByName('BALANCE/md_evo.bin', MdEvoHandler.serialize(evo_model)) # Fixed floors ov29 = config.binaries['overlay/overlay_0029.bin'] ov29bin = bytearray(get_binary_from_rom_ppmdu(rom, ov29)) monster_list = HardcodedFixedFloorTables.get_monster_spawn_list( ov29bin, config) for m in monster_list: if m.md_idx >= NUM_PREVIOUS_MD_MAX: m.md_idx += NUM_NEW_ENTRIES - NUM_PREVIOUS_MD_MAX HardcodedFixedFloorTables.set_monster_spawn_list( ov29bin, monster_list, config) set_binary_in_rom_ppmdu(rom, ov29, bytes(ov29bin)) try: apply() except RuntimeError as ex: raise ex