diff --git a/Callbacks.py b/Callbacks.py index 455853c..d469936 100644 --- a/Callbacks.py +++ b/Callbacks.py @@ -233,10 +233,13 @@ def handle_vendor(ctx: "Rac2Context"): # Use Down/Up to toggle between ammo/weapon mode holding_down: bool = interface.pcsx2_interface.read_int16(addresses.controller_input) == 0x4000 holding_up: bool = interface.pcsx2_interface.read_int16(addresses.controller_input) == 0x1000 - if holding_down and interface.vendor.mode is Vendor.Mode.MEGACORP: + if holding_down and interface.vendor.mode in [Vendor.Mode.MEGACORP, Vendor.Mode.GADGETRON]: interface.vendor.change_mode(ctx, Vendor.Mode.AMMO) if holding_up and interface.vendor.mode is Vendor.Mode.AMMO: - interface.vendor.change_mode(ctx, Vendor.Mode.MEGACORP) + if interface.vendor.is_megacorp(): + interface.vendor.change_mode(ctx, Vendor.Mode.MEGACORP) + else: + interface.vendor.change_mode(ctx, Vendor.Mode.GADGETRON) def process_vendor_text(manager: TextManager, ctx: "Rac2Context") -> None: diff --git a/Container.py b/Container.py index 83ac40a..40041dc 100644 --- a/Container.py +++ b/Container.py @@ -211,26 +211,6 @@ def generate_patch(world: "Rac2World", patch: Rac2ProcedurePatch, instruction=No # Put the base scaling from vanilla game for XP & Bolts even for revisits patch.write_token(APTokenTypes.WRITE, address, bytes([100, 50, 40, 30, 25, 20, 15, 10] * 4)) - """ Normally, when the game gives you equipment (Gadgets/Items/Weapons), it will set a Primary and Secondary byte. - The Primary byte is what the game uses to determine if you have the equipment. The Secondary byte doesn't seem to - be used for anything. For the randomizer, the Primary byte will continue to be used to indicate whether the - equipment is collected but the Secondary byte will be repurposed to keep track of whether the location has been - visited. Here, the give equipment function for each planet is modified to only set the Secondary byte to mark that - the locations has been visited and prevent giving normal equipment. """ - for address in addresses.GIVE_EQUIPMENT_FUNCS: - patch.write_token(APTokenTypes.WRITE, address + 0x14, bytes([0x01, 0x00, 0x03, 0x24])) # addiu v1,zero,0x1 - patch.write_token(APTokenTypes.WRITE, address + 0x18, bytes([0x21, 0x38, 0x82, 0x00])) # addu a3,a0,v0 - patch.write_token(APTokenTypes.WRITE, address + 0x1C, bytes([0x38, 0x00, 0xE3, 0xA0])) # sb v1,0x38(a3) - patch.write_token(APTokenTypes.WRITE, address + 0x20, NOP * 43) - - for address in addresses.VENDOR_CONFIRM_MENU_FUNCS: - # Prevent auto-equipping anything purchased at the vendor. - patch.write_token(APTokenTypes.WRITE, address + 0x740, NOP) - - # Prevent vendor from overwriting slots after purchases. - patch.write_token(APTokenTypes.WRITE, address + 0x60C, NOP) - patch.write_token(APTokenTypes.WRITE, address + 0x790, NOP) - """ Prevent any inference of an upgraded weapon type, always take the base Lv1 weapon so that we know which weapon to edit temporarily into a fake buyable item. """ for address in addresses.VENDOR_LOOP_FUNCS: @@ -333,6 +313,26 @@ def generate_patch(world: "Rac2World", patch: Rac2ProcedurePatch, instruction=No patch.write_token(APTokenTypes.WRITE, address + 0x4, NOP * 3) patch.write_token(APTokenTypes.WRITE, address + 0x14, NOP * 21) + """ Normally, when the game gives you equipment (Gadgets/Items/Weapons), it will set a Primary and Secondary byte. + The Primary byte is what the game uses to determine if you have the equipment. The Secondary byte doesn't seem to + be used for anything. For the randomizer, the Primary byte will continue to be used to indicate whether the + equipment is collected but the Secondary byte will be repurposed to keep track of whether the location has been + visited. Here, the give equipment function for each planet is modified to only set the Secondary byte to mark that + the locations has been visited and prevent giving normal equipment. """ + for address in addresses.GIVE_EQUIPMENT_FUNCS: + patch.write_token(APTokenTypes.WRITE, address + 0x14, bytes([0x01, 0x00, 0x03, 0x24])) # addiu v1,zero,0x1 + patch.write_token(APTokenTypes.WRITE, address + 0x18, bytes([0x21, 0x38, 0x82, 0x00])) # addu a3,a0,v0 + patch.write_token(APTokenTypes.WRITE, address + 0x1C, bytes([0x38, 0x00, 0xE3, 0xA0])) # sb v1,0x38(a3) + patch.write_token(APTokenTypes.WRITE, address + 0x20, NOP * 43) + + for address in addresses.VENDOR_CONFIRM_MENU_FUNCS: + # Prevent auto-equipping anything purchased at the vendor. + patch.write_token(APTokenTypes.WRITE, address + 0x740, NOP) + + # Prevent vendor from overwriting slots after purchases. + patch.write_token(APTokenTypes.WRITE, address + 0x60C, NOP) + patch.write_token(APTokenTypes.WRITE, address + 0x790, NOP) + """--------- Oozla ---------""" diff --git a/ItemPool.py b/ItemPool.py index 0d49e3e..a95a497 100644 --- a/ItemPool.py +++ b/ItemPool.py @@ -95,10 +95,20 @@ def create_equipment(world: "Rac2World") -> list["Item"]: # Gadgetron Vendor if world.options.randomize_gadgetron_vendor: equipment_to_add += [i for i in Items.GADGETRON_VENDOR_WEAPONS if i not in world.starting_weapons] + else: + mapping: list[tuple[Locations.LocationData, Items.WeaponData]] = zip(Locations.GADGETRON_VENDOR_LOCATIONS, Items.GADGETRON_VENDOR_WEAPONS) + for loc, item in mapping: + location = world.multiworld.get_location(loc.name, world.player) + location.place_locked_item(world.create_item(item.name)) # Megacorp Vendor if world.options.randomize_megacorp_vendor: equipment_to_add += [i for i in Items.MEGACORP_VENDOR_WEAPONS if i not in world.starting_weapons] + else: + mapping: list[tuple[Locations.LocationData, Items.WeaponData]] = zip(Locations.MEGACORP_VENDOR_LOCATIONS, Items.MEGACORP_VENDOR_WEAPONS) + for loc, item in mapping: + location = world.multiworld.get_location(loc.name, world.player) + location.place_locked_item(world.create_item(item.name)) # Misc Weapons equipment_to_add += [Items.SHEEPINATOR] diff --git a/Rac2Interface.py b/Rac2Interface.py index 5819806..aac47b7 100644 --- a/Rac2Interface.py +++ b/Rac2Interface.py @@ -245,7 +245,9 @@ def change_mode(self, ctx: "Rac2Context", new_mode: Mode): return if new_mode is Vendor.Mode.AMMO: - if not ctx.slot_data["randomize_megacorp_vendor"]: + if self.mode is self.Mode.MEGACORP and not ctx.slot_data["randomize_megacorp_vendor"]: + return + if self.mode is self.Mode.GADGETRON and not ctx.slot_data["randomize_gadgetron_vendor"]: return current_inventory: dict[str, int] = ctx.game_interface.get_current_inventory() @@ -337,6 +339,9 @@ def change_mode(self, ctx: "Rac2Context", new_mode: Mode): slots.append(Vendor.VendorSlot(weapons[i].offset, False, 0x47)) self.interface.pcsx2_interface.write_int16(equipment_table + weapons[i].offset * 0xE0 + 0x3C, Items.get_icon_id(item)) + if not slots: + self.change_mode(ctx, self.Mode.AMMO) + return self.populate_slots(slots) elif new_mode is Vendor.Mode.CLOSED: # reset weapon data back to default when not in vendor diff --git a/data/Locations.py b/data/Locations.py index 2d64628..ba21fe6 100644 --- a/data/Locations.py +++ b/data/Locations.py @@ -209,85 +209,71 @@ class LocationData(NamedTuple): OOZLA_VENDOR_WEAPON_1 = LocationData( 300, "Oozla: Megacorp Vendor - New Weapon 1", checked_flag_address=lambda ram: ram.secondary_inventory + Items.CHOPPER.offset, - enable_if=lambda options_dict: options_dict["randomize_megacorp_vendor"], is_vendor=True ) OOZLA_VENDOR_WEAPON_2 = LocationData( 301, "Oozla: Megacorp Vendor - New Weapon 2", checked_flag_address=lambda ram: ram.secondary_inventory + Items.BLITZ_GUN.offset, - enable_if=lambda options_dict: options_dict["randomize_megacorp_vendor"], is_vendor=True ) ENDAKO_VENDOR_WEAPON_1 = LocationData( 302, "Endako: Megacorp Vendor - New Weapon 1", checked_flag_address=lambda ram: ram.secondary_inventory + Items.PULSE_RIFLE.offset, - enable_if=lambda options_dict: options_dict["randomize_megacorp_vendor"], is_vendor=True ) ENDAKO_VENDOR_WEAPON_2 = LocationData( 303, "Endako: Megacorp Vendor - New Weapon 2", checked_flag_address=lambda ram: ram.secondary_inventory + Items.MINITURRET_GLOVE.offset, - enable_if=lambda options_dict: options_dict["randomize_megacorp_vendor"], is_vendor=True ) BARLOW_VENDOR_WEAPON = LocationData( 304, "Barlow: Megacorp Vendor - New Weapon", checked_flag_address=lambda ram: ram.secondary_inventory + Items.SEEKER_GUN.offset, - enable_if=lambda options_dict: options_dict["randomize_megacorp_vendor"], is_vendor=True ) NOTAK_VENDOR_WEAPON = LocationData( 305, "Notak: Megacorp Vendor - New Weapon", checked_flag_address=lambda ram: ram.secondary_inventory + Items.SYNTHENOID.offset, - enable_if=lambda options_dict: options_dict["randomize_megacorp_vendor"], is_vendor=True ) TABORA_VENDOR_WEAPON_1 = LocationData( 306, "Tabora: Megacorp Vendor - New Weapon 1", checked_flag_address=lambda ram: ram.secondary_inventory + Items.LAVA_GUN.offset, - enable_if=lambda options_dict: options_dict["randomize_megacorp_vendor"], is_vendor=True ) TABORA_VENDOR_WEAPON_2 = LocationData( 307, "Tabora: Megacorp Vendor - New Weapon 2", checked_flag_address=lambda ram: ram.secondary_inventory + Items.BOUNCER.offset, - enable_if=lambda options_dict: options_dict["randomize_megacorp_vendor"], is_vendor=True ) DOBBO_VENDOR_WEAPON = LocationData( 308, "Dobbo: Megacorp Vendor - New Weapon", checked_flag_address=lambda ram: ram.secondary_inventory + Items.MINIROCKET_TUBE.offset, - enable_if=lambda options_dict: options_dict["randomize_megacorp_vendor"], is_vendor=True ) JOBA_VENDOR_WEAPON_1 = LocationData( 309, "Joba: Megacorp Vendor - New Weapon 1", checked_flag_address=lambda ram: ram.secondary_inventory + Items.SPIDERBOT_GLOVE.offset, - enable_if=lambda options_dict: options_dict["randomize_megacorp_vendor"], is_vendor=True ) JOBA_VENDOR_WEAPON_2 = LocationData( 310, "Joba: Megacorp Vendor - New Weapon 2", checked_flag_address=lambda ram: ram.secondary_inventory + Items.PLASMA_COIL.offset, - enable_if=lambda options_dict: options_dict["randomize_megacorp_vendor"], is_vendor=True ) TODANO_VENDOR_WEAPON = LocationData( 311, "Todano: Megacorp Vendor - New Weapon", checked_flag_address=lambda ram: ram.secondary_inventory + Items.HOVERBOMB_GUN.offset, - enable_if=lambda options_dict: options_dict["randomize_megacorp_vendor"], is_vendor=True ) ARANOS_VENDOR_WEAPON_1 = LocationData( 312, "Aranos Prison: Megacorp Vendor - New Weapon 1", checked_flag_address=lambda ram: ram.secondary_inventory + Items.SHIELD_CHARGER.offset, - enable_if=lambda options_dict: options_dict["randomize_megacorp_vendor"], is_vendor=True ) ARANOS_VENDOR_WEAPON_2 = LocationData( 313, "Aranos Prison: Megacorp Vendor - New Weapon 2", checked_flag_address=lambda ram: ram.secondary_inventory + Items.ZODIAC.offset, - enable_if=lambda options_dict: options_dict["randomize_megacorp_vendor"], is_vendor=True ) @@ -295,37 +281,31 @@ class LocationData(NamedTuple): BARLOW_GADGETRON_1 = LocationData( 314, "Barlow: Gadgetron Vendor - Weapon 1", checked_flag_address=lambda ram: ram.secondary_inventory + Items.BOMB_GLOVE.offset, - enable_if=lambda options_dict: options_dict["randomize_gadgetron_vendor"], is_vendor=True ) BARLOW_GADGETRON_2 = LocationData( 315, "Barlow: Gadgetron Vendor - Weapon 2", checked_flag_address=lambda ram: ram.secondary_inventory + Items.VISIBOMB_GUN.offset, - enable_if=lambda options_dict: options_dict["randomize_gadgetron_vendor"], is_vendor=True ) BARLOW_GADGETRON_3 = LocationData( 316, "Barlow: Gadgetron Vendor - Weapon 3", checked_flag_address=lambda ram: ram.secondary_inventory + Items.TESLA_CLAW.offset, - enable_if=lambda options_dict: options_dict["randomize_gadgetron_vendor"], is_vendor=True ) BARLOW_GADGETRON_4 = LocationData( 317, "Barlow: Gadgetron Vendor - Weapon 4", checked_flag_address=lambda ram: ram.secondary_inventory + Items.DECOY_GLOVE.offset, - enable_if=lambda options_dict: options_dict["randomize_gadgetron_vendor"], is_vendor=True ) BARLOW_GADGETRON_5 = LocationData( 318, "Barlow: Gadgetron Vendor - Weapon 5", checked_flag_address=lambda ram: ram.secondary_inventory + Items.RYNO_II.offset, - enable_if=lambda options_dict: options_dict["randomize_gadgetron_vendor"], is_vendor=True ) BARLOW_GADGETRON_6 = LocationData( 319, "Barlow: Gadgetron Vendor - Weapon 6", checked_flag_address=lambda ram: ram.secondary_inventory + Items.WALLOPER.offset, - enable_if=lambda options_dict: options_dict["randomize_gadgetron_vendor"], is_vendor=True )