示例#1
0
 def _init_position_markers(self, data):
     lst = []
     if self.header.position_marker_pointer is not None:
         for entry in iter_bytes(data, POS_MARKER_ENTRY_LEN, self.header.position_marker_pointer, self.header.position_marker_end_pointer):
             lst.append(SsaPositionMarker(
                 pos=SsaPosition(
                     scriptdata=self._scriptdata,
                     direction=None,
                     x_pos=read_uintle(entry, 0x0, 2),
                     y_pos=read_uintle(entry, 0x2, 2),
                     x_offset=read_uintle(entry, 0x4, 2),
                     y_offset=read_uintle(entry, 0x6, 2),
                 ),
                 unk8=read_sintle(entry, 0x8, 2),
                 unkA=read_sintle(entry, 0xA, 2),
                 unkC=read_sintle(entry, 0xC, 2),
                 unkE=read_sintle(entry, 0xE, 2)
             ))
     return lst
示例#2
0
 def _init_events(self, data):
     lst = []
     if self.header.events_pointer is not None:
         for entry in iter_bytes(data, EVENTS_ENTRY_LEN, self.header.events_pointer, self.header.events_end_pointer):
             lst.append(SsaEvent(
                 trigger_width=read_uintle(entry, 0x0, 2),
                 trigger_height=read_uintle(entry, 0x2, 2),
                 trigger_pointer=read_uintle(entry, 0xC, 2) * 2,
                 trigger_table_start=self.header.trigger_pointer,
                 pos=SsaPosition(
                     scriptdata=self._scriptdata,
                     x_pos=read_uintle(entry, 0x4, 2),
                     y_pos=read_uintle(entry, 0x6, 2),
                     x_offset=read_uintle(entry, 0x8, 2),
                     y_offset=read_uintle(entry, 0xA, 2),
                 ),
                 unkE=read_uintle(entry, 0xE, 2),
             ))
     return lst
示例#3
0
 def _init_actors(self, data):
     lst = []
     if self.header.actor_pointer is not None:
         for entry in iter_bytes(data, ACTOR_ENTRY_LEN, self.header.actor_pointer, self.header.actor_end_pointer):
             lst.append(SsaActor(
                 scriptdata=self._scriptdata,
                 actor_id=read_uintle(entry, 0x0, 2),
                 pos=SsaPosition(
                     scriptdata=self._scriptdata,
                     direction=read_uintle(entry, 0x2, 2),
                     x_pos=read_uintle(entry, 0x4, 2),
                     y_pos=read_uintle(entry, 0x6, 2),
                     x_offset=read_uintle(entry, 0x8, 2),
                     y_offset=read_uintle(entry, 0xA, 2),
                 ),
                 script_id=read_sintle(entry, 0xC, 2),
                 unkE=read_sintle(entry, 0xE, 2),
             ))
     return lst
示例#4
0
 def _init_performers(self, data):
     lst = []
     if self.header.performer_pointer is not None:
         for entry in iter_bytes(data, PERFORMERS_ENTRY_LEN, self.header.performer_pointer, self.header.performer_end_pointer):
             lst.append(SsaPerformer(
                 type=read_uintle(entry, 0x0, 2),
                 hitbox_w=read_sintle(entry, 0x4, 2),
                 hitbox_h=read_sintle(entry, 0x6, 2),
                 pos=SsaPosition(
                     scriptdata=self._scriptdata,
                     direction=read_uintle(entry, 0x2, 2),
                     x_pos=read_uintle(entry, 0x8, 2),
                     y_pos=read_uintle(entry, 0xA, 2),
                     x_offset=read_uintle(entry, 0xC, 2),
                     y_offset=read_uintle(entry, 0xE, 2),
                 ),
                 unk10=read_sintle(entry, 0x10, 2),
                 unk12=read_sintle(entry, 0x12, 2),
             ))
     return lst
示例#5
0
 def _init_objects(self, data):
     lst = []
     if self.header.object_pointer is not None:
         for entry in iter_bytes(data, OBJECT_ENTRY_LEN, self.header.object_pointer, self.header.object_end_pointer):
             lst.append(SsaObject(
                 scriptdata=self._scriptdata,
                 object_id=read_uintle(entry, 0x0, 2),
                 htibox_w=read_sintle(entry, 0x4, 2),
                 hitbox_h=read_sintle(entry, 0x6, 2),
                 pos=SsaPosition(
                     scriptdata=self._scriptdata,
                     direction=read_uintle(entry, 0x2, 2),
                     x_pos=read_uintle(entry, 0x8, 2),
                     y_pos=read_uintle(entry, 0xA, 2),
                     x_offset=read_uintle(entry, 0xC, 2),
                     y_offset=read_uintle(entry, 0xE, 2),
                 ),
                 script_id=read_sintle(entry, 0x10, 2),
                 unk12=read_sintle(entry, 0x12, 2),
             ))
     return lst
示例#6
0
    def run(self, status: Status):
        status.step("Loading Seed Info...")

        langs = list(get_all_string_files(self.rom, self.static_data))
        str_offset = STR_EU
        if self.static_data.game_region == GAME_REGION_US:
            str_offset = STR_US

        for lang, string_file in langs:
            string_file.strings[str_offset] = f"""Randomized with SkyTemple Randomizer.
Version:[CS:Z]{version()}[CR]
Seed: [CS:C]{self.seed}[CR]

[CS:H]PLEASE NOTE:[CR]
This seed will only produce the same output
when used with the exact same version
and configuration of the randomizer that
was used.
You can see the configuration of the last
randomization applied by talking to the NPC
on Crossroads."""

        for lang, string_file in langs:
            self.rom.setFileByName(f'MESSAGE/{lang.filename}', FileType.STR.serialize(string_file))

        status.step("Placing Info NPC...")
        # Place NPC in scene
        scene: Ssa = FileType.SSA.deserialize(self.rom.getFileByName(f'SCRIPT/{MAP}/{SCENE}'))
        layer = scene.layer_list[0]
        already_exists = any(a.script_id == TALK_SCRIPT for a in layer.actors)
        if not already_exists:
            layer.actors.append(SsaActor(
                scriptdata=self.static_data.script_data,
                actor_id=ACTOR_TO_USE,
                pos=SsaPosition(
                    scriptdata=self.static_data.script_data,
                    direction=self.static_data.script_data.directions__by_name['Down'].ssa_id,
                    x_pos=NPC_X,
                    y_pos=NPC_Y,
                    x_offset=0, y_offset=0
                ),
                script_id=TALK_SCRIPT,
                unkE=-1,
            ))
        already_exists = any(a.script_id == TWO_TALK_SCRIPT for a in layer.actors)
        if not already_exists:
            layer.actors.append(SsaActor(
                scriptdata=self.static_data.script_data,
                actor_id=TWO_ACTOR_TO_USE,
                pos=SsaPosition(
                    scriptdata=self.static_data.script_data,
                    direction=self.static_data.script_data.directions__by_name['Down'].ssa_id,
                    x_pos=TWO_NPC_X,
                    y_pos=TWO_NPC_Y,
                    x_offset=0, y_offset=0
                ),
                script_id=TWO_TALK_SCRIPT,
                unkE=-1,
            ))
        self.rom.setFileByName(f'SCRIPT/{MAP}/{SCENE}', FileType.SSA.serialize(scene))
        # Fill talk script 1
        exps = f"""
def 0 {{
    with (actor ACTOR_TALK_MAIN) {{
        ExecuteCommon(CORO_LIVES_REPLY_NORMAL, 0);
    }}
    with (actor ACTOR_TALK_SUB) {{
        ExecuteCommon(CORO_LIVES_REPLY_NORMAL, 0);
    }}
    with (actor ACTOR_ATTENDANT1) {{
        SetAnimation(2);
    }}
    
    message_SetFace(ACTOR_NPC_TEST010, FACE_HAPPY, FACE_POS_TOP_L_FACEINW);
    message_Talk(" This ROM has been randomized\\nwith the SkyTemple Randomizer!");
    message_ResetActor();
    message_Notice("SkyTemple Randomizer by [CS:A]Parakoopa[CR].\\nVersion:[CS:Z]{escape(version())}[CR]\\nSeed: [CS:C]{escape(str(self.seed))}[CR]");
    
    §l_menu;
    switch ( message_SwitchMenu(0, 1) ) {{
        case menu("Show Settings"):
            ~settings();
            jump @l_menu;
        case menu("Patch Credits"):
            ~patches();
            jump @l_menu;
        case menu("Goodbye!"):
        default:
            break;
    }}
    
    JumpCommon(CORO_END_TALK);
}}

macro settings() {{
    §l_settings;
    switch ( message_SwitchMenu(0, 1) ) {{
        case menu("Starters & More"):
            message_Mail("Randomize Starters?: {self._bool(self.config['starters_npcs']['starters'])}\\nRandomize NPCs and Bosses?: {self._bool(self.config['starters_npcs']['npcs'])}\\nRandomize Shops?: {self._bool(self.config['starters_npcs']['global_items'])}\\nRandomize OW Music?: {self._bool(self.config['starters_npcs']['overworld_music'])}");
            jump @l_settings;
        case menu("Dungeons: General"):
            message_Mail("Mode: {self._dungeon_mode(self.config['dungeons']['mode'])}\\nLayouts and Tilesets?: {self._bool(self.config['dungeons']['layouts'])}\\nRandomize Weather?: {self._weather(self.config['dungeons']['weather'])}\\nRandomize Items?: {self._bool(self.config['dungeons']['items'])}\\nRandomize Pokémon?: {self._bool(self.config['dungeons']['pokemon'])}\\nRandomize Traps?: {self._bool(self.config['dungeons']['traps'])}\\nRandomize Boss Rooms?: {self._bool(self.config['dungeons']['fixed_rooms'])}");
            jump @l_settings;
        case menu("Improvements"):
            message_Mail("Download portraits?: {self._bool(self.config['improvements']['download_portraits'])}\\nApply 'MoveShortcuts'?: {self._bool(self.config['improvements']['patch_moveshortcuts'])}\\nApply 'UnusedDungeonChance'?: {self._bool(self.config['improvements']['patch_unuseddungeonchance'])}\\nApply 'CTC'?: {self._bool(self.config['improvements']['patch_totalteamcontrol'])}");
            jump @l_settings;
        case menu("Pokémon: General"):
            message_Mail("Randomize IQ Groups?: {self._bool(self.config['pokemon']['iq_groups'])}\\nRandomize Abilities?: {self._bool(self.config['pokemon']['abilities'])}\\nRandomize Typings?: {self._bool(self.config['pokemon']['typings'])}\\nRandomize Movesets?: {self._movesets(self.config['pokemon']['movesets'])}\\nBan Unowns?: {self._bool(self.config['pokemon']['ban_unowns'])}");
            jump @l_settings;
        case menu("Pokémon: Abilities"):
            {self._abilities(self.config['pokemon']['abilities_enabled'])}
            jump @l_settings;
        case menu("Locations (First)"):
            message_Mail("Randomize?: {self._bool(self.config['locations']['randomize'])}");
            {self._locs_chaps(self.config['locations']['first'])}
            jump @l_settings;
        case menu("Locations (Second)"):
            message_Mail("Randomize?: {self._bool(self.config['locations']['randomize'])}");
            {self._locs_chaps(self.config['locations']['second'])}
            jump @l_settings;
        case menu("Chapters"):
            message_Mail("Randomize?: {self._bool(self.config['chapters']['randomize'])}");
            {self._locs_chaps(self.config['chapters']['text'])}
            jump @l_settings;
        case menu("Text"):
            message_Mail("Randomize Main Texts?: {self._bool(self.config['text']['main'])}\\nRandomize Story Dialogue: {self._bool(self.config['text']['story'])}");
            jump @l_settings;
        {self._dungeon_cases()}
        case menu("Goodbye!"):
        default:
            break;
    }}
}}

macro patches() {{
    §l_patches;
    switch ( message_SwitchMenu(0, 1) ) {{
        {self._patch_credits()}
        case menu("Goodbye!"):
        default:
            break;
    }}
}}  
"""
        script, _ = ScriptCompiler(self.static_data).compile_explorerscript(
            exps, 'script.exps', lookup_paths=[]
        )

        script_fn = f'SCRIPT/{MAP}/{TALK_SCRIPT_NAME}'
        script_sera = FileType.SSB.serialize(script, static_data=self.static_data)
        try:
            create_file_in_rom(self.rom, script_fn, script_sera)
        except FileExistsError:
            self.rom.setFileByName(script_fn, script_sera)

        exps = f"""
        def 0 {{
            with (actor ACTOR_TALK_MAIN) {{
                ExecuteCommon(CORO_LIVES_REPLY_NORMAL, 0);
            }}
            with (actor ACTOR_TALK_SUB) {{
                ExecuteCommon(CORO_LIVES_REPLY_NORMAL, 0);
            }}
            with (actor ACTOR_ATTENDANT1) {{
                SetAnimation(2);
            }}

            message_SetFace(ACTOR_NPC_TEST009, FACE_HAPPY, FACE_POS_TOP_L_FACEINW);
            message_Talk(" This ROM has been randomized\\nwith the SkyTemple Randomizer!");
            message_ResetActor();
            message_Notice("SkyTemple Randomizer by [CS:A]Parakoopa[CR].\\nVersion:[CS:Z]{escape(version())}[CR]\\nSeed: [CS:C]{escape(str(self.seed))}[CR]");

            §l_menu;
            switch ( message_SwitchMenu(0, 1) ) {{
                case menu("Artist Credits"):
                    ~artists();
                    jump @l_menu;
                case menu("Goodbye!"):
                default:
                    break;
            }}

            JumpCommon(CORO_END_TALK);
        }}

        macro artists() {{
            §l_artists;
            switch ( message_SwitchMenu(0, 1) ) {{
                {self._artist_credits()}
                case menu("Goodbye!"):
                default:
                    break;
            }}
            message_ResetActor();
        }}
"""
        script, _ = ScriptCompiler(self.static_data).compile_explorerscript(
            exps, 'script.exps', lookup_paths=[]
        )

        script_fn = f'SCRIPT/{MAP}/{TWO_TALK_SCRIPT_NAME}'
        script_sera = FileType.SSB.serialize(script, static_data=self.static_data)
        try:
            create_file_in_rom(self.rom, script_fn, script_sera)
        except FileExistsError:
            self.rom.setFileByName(script_fn, script_sera)

        status.done()
示例#7
0
def main():
    os.makedirs(output_dir, exist_ok=True)

    rom = NintendoDSRom.fromFile(os.path.join(base_dir,
                                              'skyworkcopy_edit.nds'))

    bin_before = rom.getFileByName('SCRIPT/G01P01A/enter.sse')
    ssa_before = SsaHandler.deserialize(bin_before)
    data = Pmd2XmlReader.load_default()
    scriptdata = data.script_data

    ssa_before.layer_list[0].objects = [
        # TODO: 5=Width, 1=Height!
        SsaObject(
            scriptdata, 6, 5, 1,
            SsaPosition(scriptdata, 44, 24, 0, 0,
                        scriptdata.directions__by_name['Down'].id), 10, -1)
    ]

    # Write NPC types
    npc_table_start = data.binaries['arm9.bin'].blocks['Entities'].begin
    NPC_TABLE_ENTRY_LEN = 0x0c
    # uint16: type, uint16: entid, uint32: pointer to name, unk3, unk4

    # Shaymin NPC_SHEIMI 534 / V02P06A
    ent_id__shaymin = scriptdata.level_entities__by_name['NPC_SHEIMI'].id
    print(
        read_uintle(
            rom.arm9,
            npc_table_start + ent_id__shaymin * NPC_TABLE_ENTRY_LEN + 0x02, 2))
    write_uintle(
        rom.arm9, 534,
        npc_table_start + ent_id__shaymin * NPC_TABLE_ENTRY_LEN + 0x02, 2)
    # Elekid NPC_SHEIMI1 266 / V02P07A
    ent_id__elekid = scriptdata.level_entities__by_name['NPC_SHEIMI1'].id
    print(
        read_uintle(
            rom.arm9,
            npc_table_start + ent_id__elekid * NPC_TABLE_ENTRY_LEN + 0x02, 2))
    write_uintle(rom.arm9, 266,
                 npc_table_start + ent_id__elekid * NPC_TABLE_ENTRY_LEN + 0x02,
                 2)
    # Piplup NPC_SHEIMI2 428 / V03P01A
    ent_id__piplup = scriptdata.level_entities__by_name['NPC_SHEIMI2'].id
    print(
        read_uintle(
            rom.arm9,
            npc_table_start + ent_id__piplup * NPC_TABLE_ENTRY_LEN + 0x02, 2))
    write_uintle(rom.arm9, 428,
                 npc_table_start + ent_id__piplup * NPC_TABLE_ENTRY_LEN + 0x02,
                 2)
    # Meowth NPC_SHEIMI3 52 / V03P02A
    ent_id__meowth = scriptdata.level_entities__by_name['NPC_SHEIMI3'].id
    print(
        read_uintle(
            rom.arm9,
            npc_table_start + ent_id__meowth * NPC_TABLE_ENTRY_LEN + 0x02, 2))
    write_uintle(rom.arm9, 52,
                 npc_table_start + ent_id__meowth * NPC_TABLE_ENTRY_LEN + 0x02,
                 2)
    # Buneary NPC_SHEIMI4 469 / V03P03A
    ent_id__buneary = scriptdata.level_entities__by_name['NPC_SHEIMI4'].id
    print(
        read_uintle(
            rom.arm9,
            npc_table_start + ent_id__buneary * NPC_TABLE_ENTRY_LEN + 0x02, 2))
    write_uintle(
        rom.arm9, 469,
        npc_table_start + ent_id__buneary * NPC_TABLE_ENTRY_LEN + 0x02, 2)

    ssa_before.layer_list[0].actors = [
        SsaActor(
            scriptdata, ent_id__shaymin,
            SsaPosition(scriptdata, 14, 24, 2, 0,
                        scriptdata.directions__by_name['Down'].id), 5, -1),
        SsaActor(
            scriptdata, ent_id__elekid,
            SsaPosition(scriptdata, 20, 24, 2, 0,
                        scriptdata.directions__by_name['Down'].id), 6, -1),
        SsaActor(
            scriptdata, ent_id__piplup,
            SsaPosition(scriptdata, 26, 24, 2, 0,
                        scriptdata.directions__by_name['Down'].id), 7, -1),
        SsaActor(
            scriptdata, ent_id__meowth,
            SsaPosition(scriptdata, 32, 24, 2, 0,
                        scriptdata.directions__by_name['Down'].id), 8, -1),
        SsaActor(
            scriptdata, ent_id__buneary,
            SsaPosition(scriptdata, 38, 24, 2, 0,
                        scriptdata.directions__by_name['Down'].id), 9, -1),
        # Mimikyu NPC_PUKURIN 40 / V03P04A
        # Litten NPC_ZUBATTO 41 / V04P02A
        # Zorua NPC_DIGUDA 50  / V03P13A
    ]
    ssa_before.layer_list[0].events = [
        SsaEvent(6, 2, 1, 0, SsaPosition(scriptdata, 27, 0, 0, 0, None),
                 65535),
        SsaEvent(6, 2, 2, 0, SsaPosition(scriptdata, 27, 49, 0, 0, None),
                 65535),
    ]

    # Exit Guild
    ssa_before.layer_list[1].actors = [
        SsaActor(
            scriptdata, 0,
            SsaPosition(scriptdata, 29, 7, 2, 0,
                        scriptdata.directions__by_name['Down'].id), -1, -1),
        SsaActor(
            scriptdata, 10,
            SsaPosition(scriptdata, 29, 4, 2, 0,
                        scriptdata.directions__by_name['Down'].id), -1, -1)
    ]

    # Exit Town
    ssa_before.layer_list[2].actors = [
        SsaActor(
            scriptdata, 0,
            SsaPosition(scriptdata, 29, 44, 2, 0,
                        scriptdata.directions__by_name['Up'].id), -1, -1),
        SsaActor(
            scriptdata, 10,
            SsaPosition(scriptdata, 29, 47, 2, 0,
                        scriptdata.directions__by_name['Up'].id), -1, -1)
    ]

    # Create scripts, if don't exist
    tpl_ssb = rom.getFileByName('SCRIPT/G01P01A/enter01.ssb')
    try:
        rom.getFileByName('SCRIPT/G01P01A/enter05.ssb')
    except ValueError:
        create_file_in_rom(rom, 'SCRIPT/G01P01A/enter05.ssb', tpl_ssb)
    try:
        rom.getFileByName('SCRIPT/G01P01A/enter06.ssb')
    except ValueError:
        create_file_in_rom(rom, 'SCRIPT/G01P01A/enter06.ssb', tpl_ssb)
    try:
        rom.getFileByName('SCRIPT/G01P01A/enter07.ssb')
    except ValueError:
        create_file_in_rom(rom, 'SCRIPT/G01P01A/enter07.ssb', tpl_ssb)
    try:
        rom.getFileByName('SCRIPT/G01P01A/enter08.ssb')
    except ValueError:
        create_file_in_rom(rom, 'SCRIPT/G01P01A/enter08.ssb', tpl_ssb)
    try:
        rom.getFileByName('SCRIPT/G01P01A/enter09.ssb')
    except ValueError:
        create_file_in_rom(rom, 'SCRIPT/G01P01A/enter09.ssb', tpl_ssb)
    try:
        rom.getFileByName('SCRIPT/G01P01A/enter10.ssb')
    except ValueError:
        create_file_in_rom(rom, 'SCRIPT/G01P01A/enter10.ssb', tpl_ssb)

    bin_after = SsaHandler.serialize(ssa_before)
    rom.setFileByName('SCRIPT/G01P01A/enter.sse', bin_after)
    rom.saveToFile(os.path.join(base_dir, 'skyworkcopy_edit.nds'))

    draw_maps_main()