diff --git a/Container.py b/Container.py index f19bd86..8ecfe98 100644 --- a/Container.py +++ b/Container.py @@ -248,9 +248,13 @@ def generate_patch(world: "Rac2World", patch: Rac2ProcedurePatch, instruction=No current platinum bolt count. This changes it to read a single byte that we control to get that count instead. This is done to decouple the platinum bolt count from platinum bolt locations checked. This same concept is also applied to nanotech boosts below. """ + upper_half, lower_half = MIPS.get_address_halves(ram.platinum_bolt_count) for address in addresses.PLAT_BOLT_COUNT_FUNCS: - patch.write_token(APTokenTypes.WRITE, address + 0x4, bytes([0x13, 0x00, 0x00, 0x10])) - patch.write_token(APTokenTypes.WRITE, address + 0x8, bytes([0xE4, 0xB2, 0x46, 0x90])) + patch.write_token(APTokenTypes.WRITE, address, bytes([ + *upper_half, 0x02, 0x3C, # lui v0, + 0x13, 0x00, 0x00, 0x10, # b (+0x13) + *lower_half, 0x46, 0x90 # _lbu a2,(v0) + ])) # For some reason, the "Weapons" menu sets the secondary inventory flag for any weapon you hover with your cursor. # This is a problem for us since secondary inventory is tied to locations, so we just disable that behavior. @@ -258,6 +262,7 @@ def generate_patch(world: "Rac2World", patch: Rac2ProcedurePatch, instruction=No patch.write_token(APTokenTypes.WRITE, address + 0x408, MIPS.nop()) # Same for nanotech boosts + upper_half, lower_half = MIPS.get_address_halves(ram.nanotech_boost_count) for address, spaceish_wars_address in zip(addresses.NANOTECH_COUNT_FUNCS, addresses.SPACEISH_WARS_FUNCS): # Inject a custom procedure run on each tick of the main loop of each planet. # It will be called through the NANOTECH_COUNT_FUNC since we are removing a few instructions there, @@ -265,13 +270,16 @@ def generate_patch(world: "Rac2World", patch: Rac2ProcedurePatch, instruction=No planet = ram.planet[IsoAddresses.get_planet_id_from_iso_address(address)] patch.write_token(APTokenTypes.WRITE, spaceish_wars_address, custom_main_loop(ram, planet)) - patch.write_token(APTokenTypes.WRITE, address + 0x70, bytes([0xE5, 0xB2, 0xA5, 0x90])) # lbu a1,-0x4D1B(a1) - patch.write_token(APTokenTypes.WRITE, address + 0x74, bytes([0x00, 0x00, 0xA4, 0x8F])) # lw a0,0x0(sp) - patch.write_token(APTokenTypes.WRITE, address + 0x78, bytes([0x21, 0x10, 0x85, 0x00])) # addu v0,a0,a1 - patch.write_token(APTokenTypes.WRITE, address + 0x7C, MIPS.jal(planet.spaceish_wars_func)) - patch.write_token(APTokenTypes.WRITE, address + 0x80, bytes([0x00, 0x00, 0xA2, 0xAF])) # sw v0,0x0(sp) - patch.write_token(APTokenTypes.WRITE, address + 0x84, bytes([0x07, 0x00, 0x00, 0x10])) # beq zero,zero,0x7 - patch.write_token(APTokenTypes.WRITE, address + 0x88, MIPS.nop()) + patch.write_token(APTokenTypes.WRITE, address + 0x6C, bytes([ + *upper_half, 0x05, 0x3C, # lui a1, + *lower_half, 0xA5, 0x90, # lbu a1,(a1) + 0x00, 0x00, 0xA4, 0x8F, # lw a0,0x0(sp) + 0x21, 0x10, 0x85, 0x00, # addu v0,a0,a1 + *MIPS.jal(planet.spaceish_wars_func), + 0x00, 0x00, 0xA2, 0xAF, # _sw v0,0x0(sp) + 0x07, 0x00, 0x00, 0x10, # beq zero,zero,0x7 + *MIPS.nop() + ])) # Prevent Platinum Bolt received message popup at the end of ship races. for address in addresses.RACE_CONTROLLER_FUNCS: @@ -636,22 +644,23 @@ def generate_patch(world: "Rac2World", patch: Rac2ProcedurePatch, instruction=No patch.write_token(APTokenTypes.WRITE, address + 0x440, bytes([0x67, 0x7B, 0x63, 0x90])) # Make Hypnotist check AP controlled Hypnomatic Part Count # instead of normal address to determine total parts collected. - patch.write_token(APTokenTypes.WRITE, address + 0x19C, bytes([0x1A, 0x00, 0x03, 0x3C])) - patch.write_token(APTokenTypes.WRITE, address + 0x1A0, bytes([0xE6, 0xB2, 0x62, 0x90])) - patch.write_token(APTokenTypes.WRITE, address + 0x1A4, bytes([0x03, 0x00, 0x42, 0x28])) - patch.write_token(APTokenTypes.WRITE, address + 0x1A8, bytes([0x14, 0x00, 0x40, 0x14])) - patch.write_token(APTokenTypes.WRITE, address + 0x1AC, NOP) - patch.write_token(APTokenTypes.WRITE, address + 0x1B0, NOP) - patch.write_token(APTokenTypes.WRITE, address + 0x1B4, NOP) - patch.write_token(APTokenTypes.WRITE, address + 0x1B8, NOP) - patch.write_token(APTokenTypes.WRITE, address + 0x1BC, NOP) - patch.write_token(APTokenTypes.WRITE, address + 0x218, bytes([0x1A, 0x00, 0x03, 0x3C])) - patch.write_token(APTokenTypes.WRITE, address + 0x21C, bytes([0xE6, 0xB2, 0x62, 0x90])) - patch.write_token(APTokenTypes.WRITE, address + 0x220, bytes([0xD9, 0x27, 0x04, 0x24])) - patch.write_token(APTokenTypes.WRITE, address + 0x224, bytes([0xFF, 0xFF, 0x06, 0x24])) - patch.write_token(APTokenTypes.WRITE, address + 0x228, bytes([0x03, 0x00, 0x03, 0x24])) - patch.write_token(APTokenTypes.WRITE, address + 0x22C, bytes([0x22, 0x28, 0x62, 0x00])) - patch.write_token(APTokenTypes.WRITE, address + 0x230, bytes([0x24, 0x00, 0x00, 0x10])) + upper_half, lower_half = MIPS.get_address_halves(ram.hypnomatic_part_count) + patch.write_token(APTokenTypes.WRITE, address + 0x19C, bytes([ + *upper_half, 0x03, 0x3C, + *lower_half, 0x62, 0x90, + 0x03, 0x00, 0x42, 0x28, + 0x14, 0x00, 0x40, 0x14, + *(MIPS.nop() * 5) + ])) + patch.write_token(APTokenTypes.WRITE, address + 0x218, bytes([ + *upper_half, 0x03, 0x3C, + *lower_half, 0x62, 0x90, + 0xD9, 0x27, 0x04, 0x24, + 0xFF, 0xFF, 0x06, 0x24, + 0x03, 0x00, 0x03, 0x24, + 0x22, 0x28, 0x62, 0x00, + 0x24, 0x00, 0x00, 0x10 + ])) # Replace code that gives Hypnomatic with code that just sets Secondary Inventory flag. patch.write_token(APTokenTypes.WRITE, address + 0x4A0, bytes([0x01, 0x00, 0x04, 0x24])) patch.write_token(APTokenTypes.WRITE, address + 0x4A4, bytes([0x77, 0x8B, 0x84, 0xA3])) diff --git a/data/IsoAddresses.py b/data/IsoAddresses.py index 005f5d7..110cd59 100644 --- a/data/IsoAddresses.py +++ b/data/IsoAddresses.py @@ -1,4 +1,4 @@ -from worlds.rac2.data.Planets import * +from worlds.rac2.data import Planets def get_planet_id_from_iso_address(address): @@ -201,7 +201,15 @@ class AddressesSCUS97268: 0xB6BF1144, 0xB9803FBC, 0xBAFC7AF0, 0xBF9C6DF8, 0xC3315BFC, 0xC62D316C, 0xC8F428B0, 0xCA5AE1C0, 0xCD97DC9C, 0xD00C1DA8, 0xD240DDF0, 0xD6790700, 0xDAEE2B1C, 0xDCCAB6B8, 0xDE86B62C, 0xDFA214B8, 0xE0682840, 0xE151F054 ] + MEMCARD_GAME_NAMES: list[int] = [0x3010BD, 0x3010D5, 0x3010ED, 0x30110D, 0x30112D, 0x301141, 0x30115D] + # MEMCARD_SAVED_DATA_TABLES: list[int] = [ + # 0x003AF8B8, 0x949B5C84, + # 0x9D292724, 0x9EB1F958, 0xA193E324, 0xA5DDA684, 0xA8EF558C, 0xACA9CBC8, 0xAE398904, 0xB070F218, 0xB2F6AE6C, + # 0xB6B82B1C, 0xB978CFF8, 0xBAF57CD4, 0xBF94F7F4, 0xC32A12EC, 0xC625C84C, 0xC8EC991C, 0xCA5416AC, 0xCD908DAC, + # 0xD003BF84, 0xD239E950, 0xD6712A84, 0xDAE6DCAC, 0xDCC32E6C, 0xDE7F0B4C, 0xDF9AD70C, 0xE060BE78, 0xE14A9C2C + # ] + # DEFAULT_MEMCARD_SAVE_FILE: int = 0x948F31F4 ROLL_RANDOM_NUMBER_FUNCS: list[int] = [ 0x00400F60, 0x94A06324, @@ -211,33 +219,33 @@ class AddressesSCUS97268: ] PLANET_WADS = { - ARANOS_TUTORIAL.number: [0x9D04C000, 0x9DF0C27F], # LEVEL0.WAD - OOZLA.number: [0x9E909800, 0x9FDE9D53], # LEVEL1.WAD - MAKTAR_NEBULA.number: [0xA1753800, 0xA2B143F7], # LEVEL2.WAD - ENDAKO.number: [0xA5B5E000, 0xA6C3185F], # LEVEL3.WAD - BARLOW.number: [0xA8CD7000, 0xAA4689DB], # LEVEL4.WAD - FELTZIN_SYSTEM.number: [0xAC85B800, 0xAD8452CF], # LEVEL5.WAD - NOTAK.number: [0xAE11F800, 0xAF3A2BDF], # LEVEL6.WAD - SIBERIUS.number: [0xB04BB800, 0xB19FB52F], # LEVEL7.WAD - TABORA.number: [0xB2D04000, 0xB40AA02F], # LEVEL8.WAD - DOBBO.number: [0xB690A000, 0xB7AFD2DF], # LEVEL9.WAD - HRUGIS_CLOUD.number: [0xB955E000, 0xBA3188BF], # LEVEL10.WAD - JOBA.number: [0xBAD77000, 0xBC2CEA9F], # LEVEL11.WAD - TODANO.number: [0xBF6F8000, 0xC07B628F], # LEVEL12.WAD - BOLDAN.number: [0xC3067800, 0xC418DAAF], # LEVEL13.WAD - ARANOS_PRISON.number: [0xC5FE2800, 0xC706FF9F], # LEVEL14.WAD - GORN.number: [0xC8C75000, 0xC99E65BF], # LEVEL15.WAD - SNIVELAK.number: [0xCA2D4800, 0xCB4563DF], # LEVEL16.WAD - SMOLG.number: [0xCD6E7800, 0xCE6E081F], # LEVEL17.WAD - DAMOSEL.number: [0xCFDEE800, 0xD0ECDABF], # LEVEL18.WAD - GRELBIN.number: [0xD2144800, 0xD39392D7], # LEVEL19.WAD - YEEDIL.number: [0xD64EA000, 0xD794D25B], # LEVEL20.WAD - INSOMNIAC_MUSEUM.number: [0xDAC72800, 0xDB9300DF], # LEVEL21.WAD - DOBBO_ORBIT.number: [0xDCA0C800, 0xDD708D2F], # LEVEL22.WAD - DAMOSEL_ORBIT.number: [0xDE57C000, 0xDF1811CF], # LEVEL23.WAD - SHIP_SHACK.number: [0xDF7F1800, 0xDFFE82D6], # LEVEL24.WAD - WUPASH_NEBULA.number: [0xE03D5000, 0xE0C12E8C], # LEVEL25.WAD - JAMMING_ARRAY.number: [0xE12CC800, 0xE1D47A8F], # LEVEL26.WAD + Planets.ARANOS_TUTORIAL.number: [0x9D04C000, 0x9DF0C27F], # LEVEL0.WAD + Planets.OOZLA.number: [0x9E909800, 0x9FDE9D53], # LEVEL1.WAD + Planets.MAKTAR_NEBULA.number: [0xA1753800, 0xA2B143F7], # LEVEL2.WAD + Planets.ENDAKO.number: [0xA5B5E000, 0xA6C3185F], # LEVEL3.WAD + Planets.BARLOW.number: [0xA8CD7000, 0xAA4689DB], # LEVEL4.WAD + Planets.FELTZIN_SYSTEM.number: [0xAC85B800, 0xAD8452CF], # LEVEL5.WAD + Planets.NOTAK.number: [0xAE11F800, 0xAF3A2BDF], # LEVEL6.WAD + Planets.SIBERIUS.number: [0xB04BB800, 0xB19FB52F], # LEVEL7.WAD + Planets.TABORA.number: [0xB2D04000, 0xB40AA02F], # LEVEL8.WAD + Planets.DOBBO.number: [0xB690A000, 0xB7AFD2DF], # LEVEL9.WAD + Planets.HRUGIS_CLOUD.number: [0xB955E000, 0xBA3188BF], # LEVEL10.WAD + Planets.JOBA.number: [0xBAD77000, 0xBC2CEA9F], # LEVEL11.WAD + Planets.TODANO.number: [0xBF6F8000, 0xC07B628F], # LEVEL12.WAD + Planets.BOLDAN.number: [0xC3067800, 0xC418DAAF], # LEVEL13.WAD + Planets.ARANOS_PRISON.number: [0xC5FE2800, 0xC706FF9F], # LEVEL14.WAD + Planets.GORN.number: [0xC8C75000, 0xC99E65BF], # LEVEL15.WAD + Planets.SNIVELAK.number: [0xCA2D4800, 0xCB4563DF], # LEVEL16.WAD + Planets.SMOLG.number: [0xCD6E7800, 0xCE6E081F], # LEVEL17.WAD + Planets.DAMOSEL.number: [0xCFDEE800, 0xD0ECDABF], # LEVEL18.WAD + Planets.GRELBIN.number: [0xD2144800, 0xD39392D7], # LEVEL19.WAD + Planets.YEEDIL.number: [0xD64EA000, 0xD794D25B], # LEVEL20.WAD + Planets.INSOMNIAC_MUSEUM.number: [0xDAC72800, 0xDB9300DF], # LEVEL21.WAD + Planets.DOBBO_ORBIT.number: [0xDCA0C800, 0xDD708D2F], # LEVEL22.WAD + Planets.DAMOSEL_ORBIT.number: [0xDE57C000, 0xDF1811CF], # LEVEL23.WAD + Planets.SHIP_SHACK.number: [0xDF7F1800, 0xDFFE82D6], # LEVEL24.WAD + Planets.WUPASH_NEBULA.number: [0xE03D5000, 0xE0C12E8C], # LEVEL25.WAD + Planets.JAMMING_ARRAY.number: [0xE12CC800, 0xE1D47A8F], # LEVEL26.WAD } # Oozla diff --git a/data/Items.py b/data/Items.py index 3a19ff5..f35a510 100644 --- a/data/Items.py +++ b/data/Items.py @@ -32,26 +32,42 @@ class EquipmentData(ItemData): oclass_id: Optional[int] = 0x0047 # Wrench model icon_id: Optional[int] = 0xEA75 # Question Mark icon `?` +# Unreferenced equipment offsets: +# - 00: ??? +# - 01: ??? +# - 04: "Hydro-Pack" +# - 06: "Armor" +# - 0A: "Wrench" +# - 0B: ??? +# - 0F: Unused Weapon 1 +# - 21: ??? +# - 22: Unused Weapon 2 +# - 23: Unused Weapon 3 +# - 28: Unused Weapon 4 +# - 2F: "Megacorp Helmet +# - 30: "Biker Helmet" +# - 34: ??? + # Gadgets/Items -HELI_PACK = EquipmentData(1, "Heli-Pack", 2, oclass_id=0x025F, icon_id=0xEA8D) -THRUSTER_PACK = EquipmentData(2, "Thruster-Pack", 3, oclass_id=0x0260, icon_id=0xEA7E) -MAPPER = EquipmentData(3, "Mapper", 5, oclass_id=0x1235, icon_id=0xEA9F) -ARMOR_MAGNETIZER = EquipmentData(4, "Armor Magnetizer", 7, oclass_id=0x112F, icon_id=0xEA9A) -LEVITATOR = EquipmentData(5, "Levitator", 8, oclass_id=0x096C, icon_id=0xEA90) -SWINGSHOT = EquipmentData(6, "Swingshot", 13, oclass_id=0x00D0, icon_id=0xEA8B) -GRAVITY_BOOTS = EquipmentData(7, "Gravity Boots", 19, oclass_id=0x00AD, icon_id=0xEA88) -GRIND_BOOTS = EquipmentData(8, "Grindboots", 20, oclass_id=0x00C3, icon_id=0xEA8C) -GLIDER = EquipmentData(9, "Glider", 21, icon_id=0xEA91) -DYNAMO = EquipmentData(10, "Dynamo", 36, oclass_id=0x0825, icon_id=0xEA7F) -ELECTROLYZER = EquipmentData(11, "Electrolyzer", 38, oclass_id=0x0870, icon_id=0xEA81) -THERMANATOR = EquipmentData(12, "Thermanator", 39, oclass_id=0x0FB0, icon_id=0xEA82) -TRACTOR_BEAM = EquipmentData(13, "Tractor Beam", 46, oclass_id=0x00BC, icon_id=0xEA80) -QWARK_STATUETTE = EquipmentData(14, "Qwark Statuette", 49, icon_id=0xEA9C) -BOX_BREAKER = EquipmentData(15, "Box Breaker", 50, oclass_id=0x1238, icon_id=0xEAA1) -INFILTRATOR = EquipmentData(16, "Infiltrator", 51, oclass_id=0x0BD3, icon_id=0xEA83) -CHARGE_BOOTS = EquipmentData(17, "Charge Boots", 54, oclass_id=0x0E70, icon_id=0xEA89) -HYPNOMATIC = EquipmentData(18, "Hypnomatic", 55, oclass_id=0x0950, icon_id=0xEA84) +HELI_PACK = EquipmentData(1, "Heli-Pack", 0x2, oclass_id=0x025F, icon_id=0xEA8D) +THRUSTER_PACK = EquipmentData(2, "Thruster-Pack", 0x3, oclass_id=0x0260, icon_id=0xEA7E) +MAPPER = EquipmentData(3, "Mapper", 0x5, oclass_id=0x1235, icon_id=0xEA9F) +ARMOR_MAGNETIZER = EquipmentData(4, "Armor Magnetizer", 0x7, oclass_id=0x112F, icon_id=0xEA9A) +LEVITATOR = EquipmentData(5, "Levitator", 0x8, oclass_id=0x096C, icon_id=0xEA90) +SWINGSHOT = EquipmentData(6, "Swingshot", 0xD, oclass_id=0x00D0, icon_id=0xEA8B) +GRAVITY_BOOTS = EquipmentData(7, "Gravity Boots", 0x13, oclass_id=0x00AD, icon_id=0xEA88) +GRIND_BOOTS = EquipmentData(8, "Grindboots", 0x14, oclass_id=0x00C3, icon_id=0xEA8C) +GLIDER = EquipmentData(9, "Glider", 0x15, icon_id=0xEA91) +DYNAMO = EquipmentData(10, "Dynamo", 0x24, oclass_id=0x0825, icon_id=0xEA7F) +ELECTROLYZER = EquipmentData(11, "Electrolyzer", 0x26, oclass_id=0x0870, icon_id=0xEA81) +THERMANATOR = EquipmentData(12, "Thermanator", 0x27, oclass_id=0x0FB0, icon_id=0xEA82) +TRACTOR_BEAM = EquipmentData(13, "Tractor Beam", 0x2E, oclass_id=0x00BC, icon_id=0xEA80) +QWARK_STATUETTE = EquipmentData(14, "Qwark Statuette", 0x31, icon_id=0xEA9C) +BOX_BREAKER = EquipmentData(15, "Box Breaker", 0x32, oclass_id=0x1238, icon_id=0xEAA1) +INFILTRATOR = EquipmentData(16, "Infiltrator", 0x33, oclass_id=0x0BD3, icon_id=0xEA83) +CHARGE_BOOTS = EquipmentData(17, "Charge Boots", 0x36, oclass_id=0x0E70, icon_id=0xEA89) +HYPNOMATIC = EquipmentData(18, "Hypnomatic", 0x37, oclass_id=0x0950, icon_id=0xEA84) @dataclass diff --git a/data/RamAddresses.py b/data/RamAddresses.py index 35914d3..a7a7e01 100644 --- a/data/RamAddresses.py +++ b/data/RamAddresses.py @@ -70,6 +70,7 @@ def __init__(self, game_version: str): self.inventory: int = 0x1A7AF8 self.secondary_inventory: int = 0x1A7B30 self.vendor_list: int = 0x1A7B68 + # self.savefile_play_time: int = 0x1A7BC0 self.unlocked_planets: int = 0x1A7BC8 self.loaded_flag: int = 0x1A7BE5 self.highlighted_planets: int = 0x1A7BE8 @@ -344,21 +345,25 @@ def __init__(self, game_version: str): # Custom addresses ################################################ - # I use some unused addresses at the end of the platinum bolt table to store some extra data for AP. - self.platinum_bolt_count: int = self.platinum_bolt_table + 0x6C - self.nanotech_boost_count: int = self.platinum_bolt_table + 0x6D - self.hypnomatic_part_count: int = self.platinum_bolt_table + 0x6E + # We use secondary inventory for unused weapons to store "global" data that needs to be saved to memcard + self.platinum_bolt_count: int = self.secondary_inventory + 0x0F # Unused weapon 1 + self.nanotech_boost_count: int = self.secondary_inventory + 0x22 # Unused weapon 2 + self.hypnomatic_part_count: int = self.secondary_inventory + 0x23 # Unused weapon 3 + self.bolt_pack_count: int = self.secondary_inventory + 0x28 # Unused weapon 4 - # The "enemy kill count table" is actually one table per planet of 0x400 bytes each, holding the number - # of times two enemies were killed (one in the upper half, the other in the lower half of the byte). - # As one might expect, this is way too much and most of this data is empty, but still saved on the memcard. - # We use the table for Feltzin since spaceship systems don't use that mechanic at all, but still have that - # table. - self.feltzin_kill_count_table: int = self.enemy_kill_count_table + (FELTZIN_SYSTEM.number * 0x400) - self.tabora_wrench_cutscene_flag: int = self.feltzin_kill_count_table + 0x1 - self.aranos_wrench_cutscene_flag: int = self.feltzin_kill_count_table + 0x2 - self.custom_text_notification_trigger: int = self.feltzin_kill_count_table + 0x3 - self.bolt_pack_count: int = self.feltzin_kill_count_table + 0x4 + # Put the new Tabora wrench cutscene flag inside the platinum bolt table for Tabora so it gets saved along + # other planet specific data + self.tabora_wrench_cutscene_flag: int = self.platinum_bolt_table + (TABORA.number * 4) + 3 + # Put the new Aranos Prison wrench cutscene flag inside the platinum bolt table for Aranos so it gets + # saved along other planet specific data + self.aranos_wrench_cutscene_flag: int = self.platinum_bolt_table + (ARANOS_PRISON.number * 4) + 1 + + # The "enemy kill count table" is a 0x400 table for each, holding the number of times two enemies were + # killed (one in the upper half, the other in the lower half of the byte). + # We use Feltzin's table as a spot to put temporary data, since spaceship systems don't use that mechanic + # at all but still have that table. + self.temporary_data_table: int = self.enemy_kill_count_table + (FELTZIN_SYSTEM.number * 0x400) + self.custom_text_notification_trigger: int = self.temporary_data_table class PlanetAddresses(NamedTuple):