From 0f25f9cafa898cc1e4f0eea5b51c73b3447364a1 Mon Sep 17 00:00:00 2001 From: Taoshi Date: Wed, 22 Jul 2026 01:32:31 +0200 Subject: [PATCH] RAC3: Add the ability to use charge boots and gravity boots at once in the sewers --- worlds/rac3/client/rac3_interface.py | 44 ++++++++++++++++++-- worlds/rac3/constants/options.py | 1 + worlds/rac3/constants/status.py | 1 + worlds/rac3/options/sewer_gravity_options.py | 17 ++++++++ worlds/rac3/rac3options.py | 4 ++ worlds/rac3/universal_tracker.py | 1 + worlds/rac3/world.py | 1 + 7 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 worlds/rac3/options/sewer_gravity_options.py diff --git a/worlds/rac3/client/rac3_interface.py b/worlds/rac3/client/rac3_interface.py index b08d180958b6..24f8387e78d9 100644 --- a/worlds/rac3/client/rac3_interface.py +++ b/worlds/rac3/client/rac3_interface.py @@ -132,6 +132,7 @@ class Options: helpdesk: int weapon_level_locations: int vendor_access: int + sewer_charge_gravity: bool UnlockItem: dict[str, UnlockData] = None options = Options @@ -359,6 +360,7 @@ def proc_option(self, slot_data: dict[str, Any]): self.options.helpdesk = slot_data[RAC3OPTION.HELP_DESK] self.options.weapon_level_locations = slot_data[RAC3OPTION.WEAPON_LEVEL_LOCATIONS] self.options.vendor_access = slot_data[RAC3OPTION.VENDOR_ACCESS] + self.options.sewer_charge_gravity = slot_data[RAC3OPTION.SEWER_CHARGE_GRAVITY] ######################################## # Called on Game and Server Connection # @@ -1792,6 +1794,7 @@ def late_update(self): self.patch_cycler() self.overflow_fix() self.process_delayed_things_cycler() + self.sewer_enhanced_gravity_cycler() self.health_cycler() self.shortcut_cycler() self.speedup_cycler() @@ -1805,6 +1808,8 @@ def gadget_cycler(self): return for name in gadget_data.keys(): + if name == RAC3ITEM.GRAV_BOOTS and self.should_apply_sewer_enhanced_gravity(): + continue addr = gadget_data[name].UNLOCK_ADDRESS if self.UnlockItem[name].status: if self.UnlockItem[name].unlock_delay: @@ -1826,6 +1831,29 @@ def should_cycle_gadgets(self) -> bool: return False return True + def should_apply_sewer_enhanced_gravity(self) -> bool: + """Check if we should apply the ability to use charge boots and run up walls in the sewer""" + sewer_shuttle_pos = RAC3POSITIONDATA(218, 393, 939) + aquatos_base_entrance_pos = RAC3POSITIONDATA(474.5, 446, 970) + if (self.planet == RAC3REGION.AQUATOS_SEWERS + and self.UnlockItem[RAC3ITEM.CHARGE_BOOTS].status + and self.UnlockItem[RAC3ITEM.GRAV_BOOTS].status + and self.options.sewer_charge_gravity + and not self.pause_state_value == RAC3PAUSESTATE.PAUSED # Make gravity boots show up in gadget menu + and self.distance(self.player_pos, sewer_shuttle_pos) > 22 # prevent player from getting stuck in the plumber area + and self.distance_2d(self.player_pos, aquatos_base_entrance_pos) > 3 # prevent weird behavior from first time entering the sewer + and self.player_pos.Z > 940): # disable when swimming in the sewer water + return True + return False + + def sewer_enhanced_gravity_cycler(self): + """Apply the ability to use charge boots and run up walls in the sewer""" + if self.should_apply_sewer_enhanced_gravity(): + self._write8(gadget_data[RAC3ITEM.GRAV_BOOTS].UNLOCK_ADDRESS, 0) + self._write32(RAC3STATUS.ENHANCED_GRAVITY, 1) + else: + self._write32(RAC3STATUS.ENHANCED_GRAVITY, 0) + def near_pda_vendor(self) -> bool: """Check if we are near the PDA Vendor""" if self.planet == RAC3REGION.QWARKS_HIDEOUT and self.distance_to_moby(self.pda_vendor) < 15.0: @@ -1852,10 +1880,18 @@ def distance_to_moby(self, moby: int) -> float: self._read_float(moby + 0x10), self._read_float(moby + 0x14), self._read_float(moby + 0x18)) - distance = ((player_pos.X - moby_pos.X) ** 2 + - (player_pos.Y - moby_pos.Y) ** 2 + - (player_pos.Z - moby_pos.Z) ** 2) ** 0.5 - return distance + return self.distance(player_pos, moby_pos) + + def distance(self, pos1: RAC3POSITIONDATA, pos2: RAC3POSITIONDATA) -> float: + """Calculate the distance between two positions""" + return ((pos1.X - pos2.X) ** 2 + + (pos1.Y - pos2.Y) ** 2 + + (pos1.Z - pos2.Z) ** 2) ** 0.5 + + def distance_2d(self, pos1: RAC3POSITIONDATA, pos2: RAC3POSITIONDATA) -> float: + """Calculate the 2D distance between two positions""" + return ((pos1.X - pos2.X) ** 2 + + (pos1.Y - pos2.Y) ** 2) ** 0.5 @deprecated("Unused") def get_checked_locations_by_tag(self, tag: str) -> list[str]: diff --git a/worlds/rac3/constants/options.py b/worlds/rac3/constants/options.py index ab344f26387d..c06bd0fe1c1f 100644 --- a/worlds/rac3/constants/options.py +++ b/worlds/rac3/constants/options.py @@ -49,3 +49,4 @@ class RAC3OPTION: HELP_DESK = "Help Desk" WEAPON_LEVEL_LOCATIONS = "Weapon Level Locations" VENDOR_ACCESS = "Vendor Access" + SEWER_CHARGE_GRAVITY = "Sewer Charge Gravity" diff --git a/worlds/rac3/constants/status.py b/worlds/rac3/constants/status.py index 9bb23af6999e..c76d739128bc 100644 --- a/worlds/rac3/constants/status.py +++ b/worlds/rac3/constants/status.py @@ -104,6 +104,7 @@ class RAC3STATUS: HELP_DESK_VOICE = 0x00143954 HELP_DESK_TEXT = 0x00143955 SEWERS_VISITED = 0x001D558C + ENHANCED_GRAVITY = 0x001D6740 BOLT_PACKS = 0x00142DE4 NANOTECH_EXP_PACKS = 0x00142DE8 WEAPON_LEVEL_PACKS = 0x00142DEC diff --git a/worlds/rac3/options/sewer_gravity_options.py b/worlds/rac3/options/sewer_gravity_options.py new file mode 100644 index 000000000000..c22231b0dc92 --- /dev/null +++ b/worlds/rac3/options/sewer_gravity_options.py @@ -0,0 +1,17 @@ +"""This module contains options for enabling enhanced gravity in the sewer""" + +from Options import Toggle +from worlds.rac3 import RAC3OPTION + + +class SewerChargeGravity(Toggle): + """ + Determines if it is possible to use charge boots and run up walls in the sewer. + ------------------------------------------------------------------------------ + Yes: If the player has both the charge boots and gravity boots, they will be able to use both at once in the sewer. + No: Vanilla behavior, the player will not be able to use charge boots and run up walls in the sewer at once. + ------------------------------------------------------------------------------ + Warning: The game was not designed to allow the player to use charge boots and run up walls in the sewer at once. + This makes EVERYTHING a "gravity ramp" so expect to see some weird behavior. + """ + display_name = RAC3OPTION.SEWER_CHARGE_GRAVITY diff --git a/worlds/rac3/rac3options.py b/worlds/rac3/rac3options.py index a84ad8c78bb7..9d416eec85c6 100644 --- a/worlds/rac3/rac3options.py +++ b/worlds/rac3/rac3options.py @@ -22,6 +22,7 @@ from worlds.rac3.options.rangers_options import Rangers from worlds.rac3.options.ratchet_skins_options import RatchetSkin from worlds.rac3.options.scout_vendors_options import ScoutVendors +from worlds.rac3.options.sewer_gravity_options import SewerChargeGravity from worlds.rac3.options.sewer_limitation_options import SewerLimitation from worlds.rac3.options.sewer_options import SewerCrystals from worlds.rac3.options.ship_nose_options import ShipNose @@ -94,6 +95,7 @@ class RaC3Options(PerGameCommonOptions): helpdesk: HelpDesk vendor_access: VendorAccess weapon_level_locations: WeaponLevels + sewer_charge_gravity: SewerChargeGravity rac3_option_groups = [ @@ -147,6 +149,7 @@ class RaC3Options(PerGameCommonOptions): OptionGroup("RAC3 QoL Options", [ HelpDesk, VendorAccess, + SewerChargeGravity, ]), OptionGroup("Item & Location Options", [ RAC3ExcludeLocations, @@ -193,4 +196,5 @@ class RaC3Options(PerGameCommonOptions): RAC3OPTION.HELP_DESK, RAC3OPTION.WEAPON_LEVEL_LOCATIONS, RAC3OPTION.VENDOR_ACCESS, + RAC3OPTION.SEWER_CHARGE_GRAVITY, ] diff --git a/worlds/rac3/universal_tracker.py b/worlds/rac3/universal_tracker.py index 03d197fb2adb..e94382c539fd 100644 --- a/worlds/rac3/universal_tracker.py +++ b/worlds/rac3/universal_tracker.py @@ -56,6 +56,7 @@ def setup_options_from_slot_data(world: "RaC3World") -> None: world.options.helpdesk.value = world.passthrough[RAC3OPTION.HELP_DESK] world.options.weapon_level_locations.value = world.passthrough[RAC3OPTION.WEAPON_LEVEL_LOCATIONS] world.options.vendor_access.value = world.passthrough[RAC3OPTION.VENDOR_ACCESS] + world.options.sewer_charge_gravity.value = world.passthrough[RAC3OPTION.SEWER_CHARGE_GRAVITY] else: world.using_ut = False else: diff --git a/worlds/rac3/world.py b/worlds/rac3/world.py index 0722d73599b2..1d8f6b26cf80 100644 --- a/worlds/rac3/world.py +++ b/worlds/rac3/world.py @@ -284,6 +284,7 @@ def fill_slot_data(self) -> dict[str, Any]: RAC3OPTION.HELP_DESK: self.options.helpdesk.value, RAC3OPTION.WEAPON_LEVEL_LOCATIONS: self.options.weapon_level_locations.value, RAC3OPTION.VENDOR_ACCESS: self.options.vendor_access.value, + RAC3OPTION.SEWER_CHARGE_GRAVITY: self.options.sewer_charge_gravity.value, RAC3OPTION.TOTAL_LOCATIONS: get_total_locations(self), }