def run(self, status: Status):
        if not self.config['starters_npcs']['starters']:
            return status.done()
        status.step("Randomizing Partner Starters...")
        overlay13 = get_binary_from_rom_ppmdu(
            self.rom, self.static_data.binaries['overlay/overlay_0013.bin'])
        pokemon_string_data = self.static_data.string_index_data.string_blocks[
            "Pokemon Names"]
        langs = list(get_all_string_files(self.rom, self.static_data))

        orig_partner_ids = HardcodedPersonalityTestStarters.get_partner_md_ids(
            overlay13, self.static_data)
        new_partner_ids = [
            self._random_gender(choice(get_allowed_md_ids(self.config)))
            for _ in range(0, len(orig_partner_ids))
        ]
        HardcodedPersonalityTestStarters.set_partner_md_ids(
            new_partner_ids, overlay13, self.static_data)

        status.step("Randomizing Player Starters...")
        # The player options are put into two-pairs for each nature, first male then female.
        orig_player_ids = HardcodedPersonalityTestStarters.get_player_md_ids(
            overlay13, self.static_data)
        new_player_ids = []
        k = 0  # Index of text for "Will be..."
        for i in range(0, len(orig_player_ids)):
            new_id = choice(get_allowed_md_ids(self.config))
            if k % 3 == 0:
                k += 1
            # todo: refactor, this isn't really efficient.
            for lang, string_file in langs:
                string_file.strings[0x67C + k] = replace_strings(
                    string_file.strings[0x67C + k], {
                        self._get_name(string_file, orig_player_ids[i], pokemon_string_data):
                        self._get_name(string_file, new_id,
                                       pokemon_string_data)
                    })
            if i % 2 == 1 and new_id + NUM_ENTITIES <= 1154:
                new_id += NUM_ENTITIES
            new_player_ids.append(new_id)
            k += 1
        HardcodedPersonalityTestStarters.set_player_md_ids(
            new_player_ids, overlay13, self.static_data)

        status.step("Cloning missing starter portraits...")
        kao = FileType.KAO.deserialize(
            self.rom.getFileByName('FONT/kaomado.kao'))
        for new in new_player_ids + new_partner_ids:
            new_base = new % 600
            clone_missing_portraits(kao, new_base - 1)

        set_binary_in_rom_ppmdu(
            self.rom, self.static_data.binaries['overlay/overlay_0013.bin'],
            overlay13)
        for lang, string_file in langs:
            self.rom.setFileByName(f'MESSAGE/{lang.filename}',
                                   FileType.STR.serialize(string_file))
        self.rom.setFileByName('FONT/kaomado.kao', FileType.KAO.serialize(kao))

        status.done()
Exemple #2
0
    def _randomize_actors(self, string_file,
                          pokemon_string_data) -> Dict[int, int]:
        """Returns a dict that maps old entids -> new entids"""
        actor_list: ActorListBin = FileType.SIR0.unwrap_obj(
            FileType.SIR0.deserialize(
                self.rom.getFileByName('BALANCE/actor_list.bin')),
            ActorListBin)
        md = FileType.MD.deserialize(
            self.rom.getFileByName('BALANCE/monster.md'))

        mapped = {}
        # We want to map actors with the same name to the same ID
        mapped_for_names = {}
        old_entid_bases = [
            actor.entid % NUM_ENTITIES for actor in actor_list.list
        ]
        for actor in actor_list.list:
            if actor.entid > 0:
                old_name = self._get_name(string_file,
                                          actor.entid % NUM_ENTITIES,
                                          pokemon_string_data)
                if old_name in mapped_for_names.keys():
                    new_entid = mapped_for_names[old_name]
                    if new_entid >= 1154:
                        new_entid -= NUM_ENTITIES
                else:
                    new_entid = choice(
                        get_allowed_md_ids(self.config,
                                           True,
                                           roster=Roster.NPCS))
                    # Make it less likely to get duplicates
                    while new_entid in mapped.values() and randrange(0,
                                                                     4) != 0:
                        new_entid = choice(
                            get_allowed_md_ids(self.config,
                                               True,
                                               roster=Roster.NPCS))
                    # Due to the way the string replacing works we don't want anything that previously existed.
                    while md.get_by_index(
                            new_entid
                    ).gender == Gender.INVALID or new_entid % NUM_ENTITIES in old_entid_bases:
                        new_entid = choice(
                            get_allowed_md_ids(self.config,
                                               True,
                                               roster=Roster.NPCS))
                mapped[actor.entid] = new_entid
                mapped_for_names[old_name] = new_entid
                actor.entid = new_entid

        self.rom.setFileByName(
            'BALANCE/actor_list.bin',
            FileType.SIR0.serialize(FileType.SIR0.wrap_obj(actor_list)))
        return mapped
    def _randomize_monsters(self, min_level, max_level, allow_shaymin=True):
        monsters = []
        allowed = get_allowed_md_ids(self.config)
        if not allow_shaymin:
            for idx in SHAYMIN_IDS:
                if idx in allowed:
                    allowed.remove(idx)
        md_ids = sorted(
            set(
                choice(allowed) for _ in range(
                    0,
                    randrange(MIN_MONSTERS_PER_LIST, MAX_MONSTERS_PER_LIST +
                              1))))
        weights = sorted(self._random_weights(len(md_ids)))
        for md_id, weight in zip(md_ids, weights):
            level = min(
                100,
                max(
                    1,
                    randrange(min_level - MONSTER_LEVEL_VARIANCE,
                              max_level + MONSTER_LEVEL_VARIANCE + 1)))
            monsters.append(MappaMonster(level, weight, weight, md_id))

        # Add Kecleon and Dummy
        monsters.append(MappaMonster(42, 0, 0, KECLEON_MD_INDEX))
        monsters.append(MappaMonster(1, 0, 0, DUMMY_MD_INDEX))

        return sorted(monsters, key=lambda m: m.md_index)
    def run(self, status: Status):
        if not self.config['starters_npcs']['npcs']:
            return status.done()

        status.step("Apply 'ActorAndLevelLoader' patch...")
        patcher = Patcher(self.rom, self.static_data)
        if not patcher.is_applied('ActorAndLevelLoader'):
            patcher.apply('ActorAndLevelLoader')

        status.step("Updating bosses...")

        actor_list: ActorListBin = FileType.SIR0.unwrap_obj(
            FileType.SIR0.deserialize(
                self.rom.getFileByName('BALANCE/actor_list.bin')),
            ActorListBin)

        binary = get_binary_from_rom_ppmdu(
            self.rom, self.static_data.binaries['overlay/overlay_0029.bin'])
        boss_list = HardcodedFixedFloorTables.get_monster_spawn_list(
            binary, self.static_data)

        for i, actor in enumerate(actor_list.list):
            if i in ACTOR_TO_BOSS_MAPPING:
                for bi in ACTOR_TO_BOSS_MAPPING[i]:
                    boss_list[bi].md_idx = actor.entid

        for extra_id in EXTRA_FF_MONSTER_RANDOMIZE:
            boss_list[extra_id].md_idx = choice(
                get_allowed_md_ids(self.config, False))

        HardcodedFixedFloorTables.set_monster_spawn_list(
            binary, boss_list, self.static_data)
        set_binary_in_rom_ppmdu(
            self.rom, self.static_data.binaries['overlay/overlay_0029.bin'],
            binary)

        status.done()