diff --git a/src/Options.py b/src/Options.py index 063a3d1..f33aec5 100644 --- a/src/Options.py +++ b/src/Options.py @@ -115,6 +115,59 @@ class Tricks(OptionList): "Temple Grounds - Temple Assembly Site | NSJ SA to Item Ledge", "Temple Grounds - Temple Assembly Site | Slope Jump to Item Ledge", "Temple Grounds - Trooper Security Station | SA to break the gate", + "Torvus Bog - Abandoned Worksite | BSJ to Pickup Ledge", + "Torvus Bog - Abandoned Worksite | NSJ BSJ to Pickup Ledge", + "Torvus Bog - Abandoned Worksite | NSJ SA to Pickup Ledge", + "Torvus Bog - Abandoned Worksite | Boost Jump to Pickup Ledge", + "Torvus Bog - Abandoned Worksite | Roll Jump to Pickup Ledge", + "Torvus Bog - Catacombs | Activate Bomb Slot without Bombs", + "Torvus Bog - Catacombs | Clip Through Gate", # bounce off of wall through cage bars using Screw Attack + "Torvus Bog - Catacombs | Exit Water NSJ", + "Torvus Bog - Catacombs | Underwater Dash to Bomb Slot", + "Torvus Bog - Controller Access | Activate Bomb Slot without Bombs", + "Torvus Bog - Forgotten Bridge | Scan Dash from Bridge", + "Torvus Bog - Forgotten Bridge | Roll Jump from Bridge", + "Torvus Bog - Forgotten Bridge | BSJ into Cage", + "Torvus Bog - Forgotten Bridge | Bomb Jump Between Platforms", + "Torvus Bog - Forgotten Bridge | Air Underwater", + "Torvus Bog - Gathering Hall | Activate Bomb Slot without Bombs", + "Torvus Bog - Gathering Hall | SA to Rotating Spider Track Segments", + "Torvus Bog - Great Bridge | Instant Unmorph to Cannon Ledge", + "Torvus Bog - Great Bridge | Instant Unmorph to Scan Ledge", + "Torvus Bog - Great Bridge | Scan Dash around Top", + "Torvus Bog - Great Bridge | Slope Jump SA to North Path", + "Torvus Bog - Great Bridge | Slope Jump over Translator Gate", + "Torvus Bog - Great Bridge | Wall Boost to North Path", + "Torvus Bog - Hydrodynamo Shaft | Carry Air Underwater from Hydrodynamo Station", + "Torvus Bog - Hydrodynamo Station | Air Underwater", + "Torvus Bog - Hydrodynamo Station | Boost Jump", + "Torvus Bog - Hydrodynamo Station | Seeker Skip", + "Torvus Bog - Hydrodynamo Station | Underwater Dash", + "Torvus Bog - Main Hydrochamber | Alpha Blogg Skip", + "Torvus Bog - Main Hydrochamber | BSJ to skip Spider Track", + "Torvus Bog - Main Hydrochamber | Climb to Top Post-Alpha Blogg (NSJ)", + "Torvus Bog - Plaza Access | Out of Bounds", + "Torvus Bog - Portal Chamber | Wall Boost", + "Torvus Bog - Temple Access | Wall Boost", + "Torvus Bog - Torvus Grove | Climb Roots to reach Connected Ledge", + "Torvus Bog - Torvus Grove | STE to Reach Curved Ledge", + "Torvus Bog - Torvus Grove | Scan Dash to reach Curved Ledge", + "Torvus Bog - Torvus Grove | Instant Unmorph to reach Isolated Ledge" + "Torvus Bog - Torvus Lagoon | Air Underwater", + "Torvus Bog - Torvus Lagoon | STE to Bridge", + "Torvus Bog - Torvus Lagoon | STE to Save Room Ledge", + "Torvus Bog - Torvus Plaza | STE SA to Item", + "Torvus Bog - Torvus Plaza | Boost-only/Cannonball", + "Torvus Bog - Torvus Plaza | Instant Unmorph BSJ to Entrance", + "Torvus Bog - Torvus Temple | Out of Bounds", + "Torvus Bog - Torvus Temple | Seeker Skip", + "Torvus Bog - Training Chamber | Activate Bomb Slot without Bombs", + "Torvus Bog - Training Chamber | Bypass Statue with SJ", + "Torvus Bog - Training Chamber | Bypass Statue with Boost", + "Torvus Bog - Training Chamber | Extended Dash to Bomb Slot", + "Torvus Bog - Training Chamber | Reverse Air Underwater to Bomb Slot", + "Torvus Bog - Underground Tunnel | Instant Morph to enter Tunnel", + "Torvus Bog - Underground Tunnel | Wall Boost to enter Tunnel", ] diff --git a/src/logic/metroidprime2/__init__.py b/src/logic/metroidprime2/__init__.py index 265fb2a..5e16fd2 100644 --- a/src/logic/metroidprime2/__init__.py +++ b/src/logic/metroidprime2/__init__.py @@ -1,6 +1,8 @@ +import math from typing import cast from BaseClasses import CollectionState + from ...Enums import DoorCover from ...Options import MetroidPrime2Options, FinalBoss from ...Utils import condition_and, condition_or @@ -403,6 +405,95 @@ def can_activate_safe_zone(state: CollectionState, player: int) -> bool: ]) +def can_activate_bomb_slot(state: CollectionState, player: int, trick: str) -> bool: + """Trick is not required if the player can lay bombs""" + return condition_and([ + state.has('Morph Ball', player), + condition_or([ + can_lay_bomb(state, player), + condition_and([ + has_trick_enabled(state, player, trick), + condition_or([ + can_use_darkburst(state, player), + can_use_sonic_boom(state, player) + ]) + ]) + ]) + ]) + + +def catacombs_can_clip_through_gate(state, player, inside: bool = False) -> bool: + return condition_and([ + condition_or([ + condition_and([ + can_use_screw_attack(state, player), + not inside + ]), + inside + ]), + has_trick_enabled(state, player, "Torvus Bog - Catacombs | Clip Through Gate") + ]) + + +def hydrodynamo_station_has_scanned_panels(state: CollectionState, player: int) -> bool: + return condition_and([ + state.has("Torvus Bog - Hydrodynamo Station | Scanned North Panel", player), + state.has("Torvus Bog - Hydrodynamo Station | Scanned West Panel", player), + state.has("Torvus Bog - Hydrodynamo Station | Scanned East Panel", player), + ]) + + +def transit_tunnel_south_can_progress_room(state, player) -> bool: + return condition_and([ + state.has('Gravity Boost', player), + can_lay_bomb(state, player) + ]) + + + +def can_reach_underwater_bomb_slot(state: CollectionState, player: int, trick: str) -> bool: + """Trick is not required if the player has Gravity Boost""" + return condition_or([ + condition_and([ + state.has('Space Jump Boots', player), + has_trick_enabled(state, player, trick) + ]), + state.has('Gravity Boost', player) + ]) + + +def can_boost_jump(state: CollectionState, player: int, trick: str) -> bool: + return condition_and([ + has_trick_enabled(state, player, trick), + can_use_boost_ball(state, player) + ]) + + +def can_underwater_dash(state: CollectionState, player: int, trick: str) -> bool: + """This trick depends on the underwater physics, so it is no longer usable if the player has Grav Boost.""" + return condition_and([ + has_trick_enabled(state, player, trick), + state.has("Space Jump Boots", player) + ]) + + +def can_underwater_boost_jump(state: CollectionState, player: int, + boost_jump_trick: str, underwater_dash_trick: str) -> bool: + # https://youtu.be/7I2Jl824CMI + return condition_and([ + can_boost_jump(state, player, boost_jump_trick), + can_underwater_dash(state, player, underwater_dash_trick) + ]) + + +def underwater_movement(state: CollectionState, player: int, underwater_dash_trick: str) -> bool: + """Represents being able to get greater-than-usual horizontal movement while underwater.""" + return condition_or([ + state.has("Gravity Boost", player), + can_underwater_dash(state, player, underwater_dash_trick) + ]) + + def has_enough_sky_temple_keys(state: CollectionState, player: int) -> bool: options = cast(MetroidPrime2Options, state.multiworld.worlds[player].options) needed_sky_temple_keys_count: int = options.sky_temple_keys_count.value @@ -434,3 +525,51 @@ def has_oob_kit(state: CollectionState, player: int) -> bool: can_lay_bomb(state, player), state.has("Space Jump Boots", player), ]) + + +def can_defeat_alpha_blogg(state: CollectionState, player: int) -> bool: + """Alpha Blogg is a tough boss for a first-timer. It can also be very tough if your movement is restricted, + you have low (max) health, or you have low (max) ammo. It can be made easier by knowing its pattern, + having free-er movement, or having more damage potential.""" + dark_ammo = sum([1 if has_dark_ammo(state, player, i) else 0 for i in [100, 150, 200, 250]]) + charge_dark = can_use_charged_dark_beam(state, player) + has_power = can_use_power_beam(state, player) + charge_power = can_use_charged_power_beam(state, player) + has_supers = can_use_super_missile(state, player, 1) + missile_count = get_missile_count(state, player) + missile_score = math.floor(missile_count / 10) + double_jump = state.has("Space Jump Boots", player) + grav_boost = state.has("Gravity Boost", player) + ball_boost = can_use_boost_ball(state, player) + tanks = state.count('Energy Tank', player) + has_knowledge = state.has('Scan Visor', player) + + survive_count = tanks + # suits don't provide damage reduction in this game, and there is no environmental damage, + # so they aren't a factor for this boss + + move_count = 0 + if grav_boost: move_count += 3 + move_count += int(double_jump) + move_count += int(ball_boost) + + damage_count = 0 + damage_count += int(has_power) + if charge_power: + damage_count += 1 + if has_supers: + damage_count += 1 + damage_count += missile_score + if charge_dark: + damage_count += 2 + damage_count += dark_ammo + damage_count += dark_ammo + damage_count += missile_score + + threshold = 100 + score = 0 + if has_knowledge: score += 5 + score += survive_count * 5 + score += move_count * 10 + score += damage_count * 3 + return score >= threshold diff --git a/src/logic/metroidprime2/light_world/torvus_bog/abandoned_worksite.py b/src/logic/metroidprime2/light_world/torvus_bog/abandoned_worksite.py new file mode 100644 index 0000000..37c2fa7 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/abandoned_worksite.py @@ -0,0 +1,150 @@ +"""A room characterized by its split nature. Morph Ball tunnels provide connections between all but the pickup ledge subregion.""" + +from BaseClasses import MultiWorld, ItemClassification +from ... import ( + can_boost_jump, + can_lay_bomb, + can_use_screw_attack, + can_use_grapple_beam, + has_trick_enabled +) +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region +from .....Utils import condition_or, condition_and + + +class AbandonedWorksite_ForgottenBridgeEntrance(MetroidPrime2Region): + """The part of the room leading to/from Forgotten Bridge. Has a Super Missile door and a Morph Ball tunnel protected by a Sporb.""" + name = "Abandoned Worksite" + desc="Forgotten Bridge Entrance" + exits=[ + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Pickup Ledge)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Abandoned Worksite (Ledge Forgotten Bridge Side)", + rule=lambda state, player: condition_or([ + # TODO: replace with can_ball_jump + can_lay_bomb(state, player), + can_use_screw_attack(state, player) + ]) + ) + ] + + +class AbandonedWorksite_GreatBridgeEntrance(MetroidPrime2Region): + """The part of the room leading to/from Great Bridge. Has a piston to lead Samus to a Morph Ball tunnel. + The grapple point above can be accessed from down here.""" + name = "Abandoned Worksite" + desc="Great Bridge Entrance" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Scan Panel Ledge)", + door=DoorCover.Light, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Abandoned Worksite (Ledge Great Bridge Side)", + rule=lambda state, player: condition_or([ + # TODO: replace with can_ball_jump + can_lay_bomb(state, player), # either navigate the morph puzzle + can_use_grapple_beam(state, player) # or grapple from the floor to the ledge next to the morph tunnel + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Abandoned Worksite (Pickup Ledge)", + rule=lambda state, player: condition_or([ + state.has("Grapple Beam", player), # you can just grapple up there from the floor + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Abandoned Worksite | BSJ to Pickup Ledge"), + can_lay_bomb(state, player), + state.has("Space Jump Boots", player) + ]), + ]) + ) + ] + + +class AbandonedWorksite_LedgeForgottenBridgeSide(MetroidPrime2Region): + """The ledge on the Forgotten Bridge side of the room, joined to the Great Bridge side of the room by a Morph Ball tunnel.""" + name = "Abandoned Worksite" + desc="Ledge Forgotten Bridge Side" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Abandoned Worksite (Forgotten Bridge Entrance)", + rule=lambda state, player: condition_or([ + can_lay_bomb(state, player), + state.has("Space Jump Boots", player), + can_use_screw_attack(state, player, is_nsj=True) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Abandoned Worksite (Ledge Great Bridge Side)", + rule=lambda state, player: state.has("Morph Ball", player) + ) + ] + + +class AbandonedWorksite_LedgeGreatBridgeSide(MetroidPrime2Region): + """The ledge on the Great Bridge side of the room, joined to the Forgotten Bridge side of the room by a Morph Ball tunnel.""" + name = "Abandoned Worksite" + desc="Ledge Great Bridge Side" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Abandoned Worksite (Pickup Ledge)", + rule=lambda state, player: condition_or([ + can_use_grapple_beam(state, player), + can_use_screw_attack(state, player), + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Abandoned Worksite | NSJ BSJ to Pickup Ledge"), + can_lay_bomb(state, player) # todo: swap to can_ball_jump + ]), + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Abandoned Worksite | NSJ SA to Pickup Ledge"), + can_use_screw_attack(state, player, is_nsj=True) + ]), + can_boost_jump(state, player, "Torvus Bog - Abandoned Worksite | Boost Jump to Pickup Ledge"), + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Abandoned Worksite | Roll Jump to Pickup Ledge"), + state.has_all(["Morph Ball", "Space Jump Boots"], player) + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Abandoned Worksite (Ledge Forgotten Bridge Side)", + rule=lambda state, player: state.has("Morph Ball", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Abandoned Worksite (Great Bridge Entrance)", + rule=lambda state, player: True + ) + ] + + +class AbandonedWorksite_PickupLedge(MetroidPrime2Region): + """A ledge containing a pickup. Sits above the Great Bridge entrance and across from the ledge on the Great Bridge side.""" + name = "Abandoned Worksite" + desc="Pickup Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Abandoned Worksite (Great Bridge Entrance)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Abandoned Worksite (Ledge Great Bridge Side)", + rule=lambda state, player: condition_or([ + can_use_grapple_beam(state, player), + can_use_screw_attack(state, player) + ]) + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Pickup (Missile Expansion)", + can_access=lambda state, player: True + ) diff --git a/src/logic/metroidprime2/light_world/torvus_bog/catacombs.py b/src/logic/metroidprime2/light_world/torvus_bog/catacombs.py new file mode 100644 index 0000000..0e635f3 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/catacombs.py @@ -0,0 +1,186 @@ +"""A room characterized by its underwater bomb slot and dark portal wrapped by a gate.""" + +from BaseClasses import MultiWorld, ItemClassification +from ... import ( + has_trick_enabled, + can_lay_bomb, + can_use_screw_attack, + can_activate_bomb_slot, + can_reach_underwater_bomb_slot, + catacombs_can_clip_through_gate +) +from .....Enums import DoorCover +from .....Items import MetroidPrime2Item +from .....Utils import condition_or, condition_and +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region + + +# tricks: +# "Torvus Bog - Catacombs | Activate Bomb Slot without Bombs", +# "Torvus Bog - Catacombs | Clip Through Gate", +# "Torvus Bog - Catacombs | Exit Water NSJ", +# "Torvus Bog - Catacombs | Underwater Dash to Bomb Slot", + + +class Catacombs_TransitTunnelEastEntrance(MetroidPrime2Region): + """An isolated ledge with a blue door that connects to Transit Tunnel East.""" + name="Catacombs" + desc="Transit Tunnel East Entrance" + exits_=[ + MetroidPrime2Exit( + destination="Torvus Bog - Transit Tunnel East (Catacombs Side)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs (Portal Ledge)", + rule = lambda state, player: can_use_screw_attack(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs (Keybearer Ledge)", + rule= lambda state, player: can_use_screw_attack(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs (Under Water)", + rule= lambda state, player: True + ) + ] + + +class Catacombs_TransitTunnelSouthEntrance(MetroidPrime2Region): + """A connected ledge with a zebra-stripe door that connects to Transit Tunnel South.""" + name="Catacombs" + desc="Transit Tunnel South Entrance" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Transit Tunnel South (Catacombs Side)", + door=DoorCover.Annihilator, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs (Transit Tunnel East Ledge)", + rule=lambda state, player: can_use_screw_attack(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs (Keybearer Ledge)", + rule=lambda state, player: True + ) + ] + + +class Catacombs_KeybearerLedge(MetroidPrime2Region): + """A ledge sandwiched between two door alcoves and the large pool in the center of the room. + The Keybearer Luminoth body is its most notable feature. Entry: G-Sch's Testament + Contains a black door leading to Catacombs Access. + Contains Grenchler enemies (later visits).""" + name="Catacombs" + desc="Keybearer Ledge" + exits_=[ + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs (Portal Ledge)", + rule=lambda state, player: condition_or([ + condition_and([ + state.has('Torvus Bog - Catacombs | Bomb Slot Activated', player), + state.has('Space Jump Boots', player), + ]), + catacombs_can_clip_through_gate(state, player) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs Access (Catacombs Side)", + door=DoorCover.Dark, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs (Under Water)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs (Transit Tunnel East Entrance)", + rule=lambda state, player: can_use_screw_attack(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs (Transit Tunnel South Entrance)", + rule=lambda state, player: condition_or([ + can_lay_bomb(state, player), + state.has('Space Jump Boots', player) + ]) + ) + ] + + +class Catacombs_UnderWater(MetroidPrime2Region): + """The pool in the center of the room. + Contains Blogg enemies (first visit) and a Bomb Slot.""" + name="Catacombs" + desc="Under Water" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs (Keybearer Ledge)", + rule=lambda state, player: condition_or([ + state.has("Space Jump Boots", player), + has_trick_enabled(state, player, "Torvus Bog - Catacombs | Exit Water NSJ"), + can_lay_bomb(state, player) # requires DBJ, but this seems reasonable to expect someone to do + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs (Transit Tunnel East Entrance)", + rule=lambda state, player: condition_or([ + state.has("Space Jump Boots", player), + has_trick_enabled(state, player, "Torvus Bog - Catacombs | Exit Water NSJ"), + can_lay_bomb(state, player) # requires DBJ, but this seems reasonable to expect someone to do + ]) + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Bomb Slot Activated", + locked_item=MetroidPrime2Item( + name="Torvus Bog - Catacombs | Bomb Slot Activated", + classification=ItemClassification.progression, + code=None, + player=player + ), + can_access=lambda state, player: condition_and([ + can_reach_underwater_bomb_slot(state, player, "Torvus Bog - Catacombs | Underwater Dash to Bomb Slot"), + can_activate_bomb_slot(state, player, "Torvus Bog - Catacombs | Activate Bomb Slot without Bombs") + ]) + ) + +class Catacombs_PortalLedge(MetroidPrime2Region): + """An isolated ledge suspended above the central pool. + Until the Bomb Slot is used, it is barred from entry by a wrap-around gate. + Contains a Lore Projector. Entry: The New Terror (GC)/Recovering Energy (Wii)""" + name="Catacombs" + desc="Portal Ledge" + exits_=[ + MetroidPrime2Exit( + destination="P|Dark Torvus Bog - Dungeon (Portal Ledge)", + door=DoorCover.Dark, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs (Under Water)", + rule=lambda state, player: condition_or([ + state.has('Torvus Bog - Catacombs | Bomb Slot Activated', player), + catacombs_can_clip_through_gate(state, player, True) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs (Transit Tunnel East Entrance", + rule=lambda state, player: condition_and([ + can_use_screw_attack(state, player), + state.has('Torvus Bog - Catacombs | Bomb Slot Activated', player) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs (Transit Tunnel South Entrance", + rule=lambda state, player: condition_and([ + can_use_screw_attack(state, player), + state.has('Torvus Bog - Catacombs | Bomb Slot Activated', player) + ]) + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/catacombs_access.py b/src/logic/metroidprime2/light_world/torvus_bog/catacombs_access.py new file mode 100644 index 0000000..7a60f6f --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/catacombs_access.py @@ -0,0 +1,38 @@ +from BaseClasses import MultiWorld, ItemClassification +from ... import has_trick_enabled, can_lay_bomb +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region +from .....Utils import condition_and, condition_or + + +class CatacombsAccess_CatacombsSide(MetroidPrime2Region): + name="Catacombs Side" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs (Keybearer Ledge)", + door=DoorCover.Dark, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs Access (Hydrodynamo Station Side)", + rule=lambda state, player: condition_or([ + state.has("Space Jump Boots", player), + can_lay_bomb(state, player) + ]) + ) + ] + + +class CatacombsAccess_HydrodynamoStationSide(MetroidPrime2Region): + name="Hydrodynamo Station Side" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (East Door Ledge)", + door=DoorCover.Dark, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs Access (Catacombs Side)", + rule=lambda state, player: True + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/controller_access.py b/src/logic/metroidprime2/light_world/torvus_bog/controller_access.py new file mode 100644 index 0000000..d13ff10 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/controller_access.py @@ -0,0 +1,23 @@ +"""Room connecting Torvus Temple and Torvus Energy Controller. It's the same as the rooms in Agon and Sanctuary. + +Rotates by activating a bomb slot in the middle of the room. The bomb slot is not permanently activated.""" + +from ... import can_activate_bomb_slot +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region + + +class ControllerAccess(MetroidPrime2Region): + name="Controller Access" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Temple (Upper)", + door=DoorCover.Any, + rule=lambda state, player: can_activate_bomb_slot(state, player, "Torvus Bog - Controller Access | Activate Bomb Slot without Bombs") + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Energy Controller", + door=DoorCover.Any, + rule=lambda state, player: can_activate_bomb_slot(state, player, "Torvus Bog - Controller Access | Activate Bomb Slot without Bombs") + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/forgotten_bridge.py b/src/logic/metroidprime2/light_world/torvus_bog/forgotten_bridge.py new file mode 100644 index 0000000..8a4ad83 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/forgotten_bridge.py @@ -0,0 +1,223 @@ +""" +A room characterized by a large spinning bridge in the center of the room. It contains: +- One phased Bomb Slot +- One Spinner +- One caged area +- One Dark Portal +- One Super Missile Cover door +- One Missile Cover door +- One white door +- Grenchler or Dark Pirate Commando enemies + +Logic assumes that the bomb slot in Dark Forgotten Bridge has already been activated. +""" + +from BaseClasses import MultiWorld, ItemClassification +from ... import ( + can_activate_dark_portal, + can_lay_bomb, + can_use_boost_ball, + can_use_screw_attack, + has_trick_enabled, +) +from .....Enums import DoorCover +from .....Items import MetroidPrime2Item +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region +from .....Utils import condition_and, condition_or + + +class ForgottenBridge_Bridge(MetroidPrime2Region): + """The bridge. It rotates.""" + name="Forgotten Bridge" + desc="Bridge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Pickup Ledge)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Dark Portal Ledge)", + rule=lambda state, player: can_use_screw_attack(state, player, is_nsj=False) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Shallows)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Cliffs)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Cage)", + rule=lambda state, player: condition_and([ + state.has("Torvus Bog - Forgotten Bridge | Spinner Activated)", player), + state.has("Space Jump Boots", player) + ]) + ), + ] + + +class ForgottenBridge_Cage(MetroidPrime2Region): + """A path between two side rooms that is not open to the rest of the room until a Spinner is used in this subregion. + Also includes the platforms that lower after the Spinner has been operated.""" + name="Forgotten Bridge" + desc="Cage" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Cliffs)", + rule=lambda state, player: condition_or([ + state.has("Torvus Bog - Forgotten Bridge | Spinner Activated", player), + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Forgotten Bridge | BSJ into Cage"), + can_lay_bomb(state, player), + state.has("Space Jump Boots", player) + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Bridge)", + rule=lambda state, player: condition_and([ + state.has("Torvus Bog - Forgotten Bridge | Spinner Activated", player), + state.has_all(["Space Jump Boots", "Screw Attack"], player) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Shallows)", + rule=lambda state, player: state.has("Torvus Bog - Forgotten Bridge | Spinner Activated") + ), + MetroidPrime2Exit( + destination="Torvus Bog - Grove Access", + door=DoorCover.Dark, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Plaza Access (Entrance)", + door=DoorCover.Missile, + rule=lambda state, player: True + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Spinner Activated", + locked_item=MetroidPrime2Item( + name="Torvus Bog - Forgotten Bridge | Spinner Activated", + classification=ItemClassification.progression, + code=None, + player=player, + ), + can_access=lambda state, player: can_use_boost_ball(state, player) + ) + + +class ForgottenBridge_Cliffs(MetroidPrime2Region): + """Includes all the ledges leading up from the water to the phased bomb slot and bridge.""" + name="Forgotten Bridge" + desc="Cliffs" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Shallows)", + rule = lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Bridge)", + rule=lambda state, player: state.has("Space Jump Boots", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Cage)", + rule=lambda state, player: condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Forgotten Bridge | BSJ into Cage"), + can_lay_bomb(state, player), + state.has("Space Jump Boots", player) + ]) + ) + ] + + +class ForgottenBridge_DarkPortalLedge(MetroidPrime2Region): + """Contains a portal to Dark Aether.""" + name="Forgotten Bridge" + desc="Dark Portal Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="P|Dark Torvus Bog - Dark Forgotten Bridge (Light Portal Ledge)", + rule=lambda state, player: can_activate_dark_portal(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Cage)", + rule=lambda state, player: condition_and([ + state.has("Torvus Bog - Forgotten Bridge | Spinner Activated", player), + state.has("Space Jump Boots", player) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Cliffs)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Shallows)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Bridge)", + rule=lambda state, player: state.has("Space Jump Boots", player) + ) + ] + + +class ForgottenBridge_PickupLedge(MetroidPrime2Region): + "Contains a pickup and a Super Missile Cover door. Connects to the bridge once the bridge has been rotated." + name="Forgotten Bridge" + desc="Pickup Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Abandoned Worksite (Forgotten Bridge Entrance)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Shallows)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Bridge)", + rule=lambda state, player: True + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Pickup (Missile Expansion)", + can_access=lambda state, player: True, + ) + + +class ForgottenBridge_Shallows(MetroidPrime2Region): + """The water.""" + name="Forgotten Bridge" + desc="Shallows" + exits=[ + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Cliffs)", + rule=lambda state, player: condition_or([ + state.has("Space Jump Boots"), + condition_and([ + can_lay_bomb(state, player), + has_trick_enabled(state, player, "Torvus Bog - Forgotten Bridge | Bomb Jump Between Platforms") + ]), + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Forgotten Bridge | Air Underwater"), + state.has("Gravity Boost", player) + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Ruined Alcove", + door=DoorCover.Light, + rule=lambda state, player: True + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/fortress_transport_access.py b/src/logic/metroidprime2/light_world/torvus_bog/fortress_transport_access.py new file mode 100644 index 0000000..e0cd60c --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/fortress_transport_access.py @@ -0,0 +1,69 @@ +"""A room characterized by revolving platforms located above a small pool containing a Blogg enemy. + The room's two doors normally lead to Transport to Sanctuary on the north side and Training Hall on the south side. + Caution: A player may get stuck here if they fall into the water and lack any movement upgrades.""" + +from BaseClasses import MultiWorld, ItemClassification +from ... import can_lay_bomb, can_use_screw_attack +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region +from .....Utils import condition_or + + +class FortressTransportAccess_UpperLedge(MetroidPrime2Region): + """The taller ledge containing the door that leads to the Torvus-Sanctuary elevator.""" + name="Fortress Transport Access" + desc = "Upper Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Transport to Sanctuary Fortress", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Fortress Transport Access (Under Water)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Fortress Transport Access (Lower Ledge)", + rule=lambda state, player: True # it's easier to cross the platforms with space jump or screw + ) + ] + + +class FortressTransportAccess_LowerLedge(MetroidPrime2Region): + """The shorter ledge in the room. Has a yellow door that leads back to Training Chamber.""" + name="Fortress Transport Access" + desc="Lower Ledge" + exits_=[ + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (North Door Ledge)", + door=DoorCover.PowerBomb, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Fortress Transport Access (Under Water)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Fortress Transport Access (Upper Ledge)", + rule=lambda state, player: True # it's easier to cross the platforms with space jump or screw + ), + ] + + +class FortressTransportAccess_UnderWater(MetroidPrime2Region): + """The central pool of water, including the steps that lead to the Lower Ledge. + Contains a Blogg or Dark Blogg enemy. + Can be difficult to leave without upgrades.""" + name="Fortress Transport Access" + desc = "Under Water" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Fortress Transport Access (Lower Ledge)", + rule=lambda state, player: condition_or([ + can_lay_bomb(state, player), + state.has('Space Jump Boots', player), + state.has('Gravity Boost', player), + can_use_screw_attack(state, player) # this method requires some finicky movement in NSJ, but you can do it + ]) + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/gathering_access.py b/src/logic/metroidprime2/light_world/torvus_bog/gathering_access.py new file mode 100644 index 0000000..9ec1ebc --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/gathering_access.py @@ -0,0 +1,21 @@ +"""Room connecting Hydrodynamo Station and Gathering Hall. Has white doors on both sides.""" + +from BaseClasses import MultiWorld, ItemClassification +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region + + +class GatheringAccess(MetroidPrime2Region): + name="Gathering Access" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Upper Door Ledge)", + door=DoorCover.Light, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (West Door Ledge)", + door=DoorCover.Light, + rule=lambda state, player: True + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/gathering_hall.py b/src/logic/metroidprime2/light_world/torvus_bog/gathering_hall.py new file mode 100644 index 0000000..5fbc5a3 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/gathering_hall.py @@ -0,0 +1,377 @@ +"""A difficult room to traverse that is characterized by having many mechanics and a large central pool that can be drained. +Has: +- 3 bomb slots +- 2 grapple points +- A force-field protecting an item on a ledge +- A laser operated via Spinner +- One phased Spinner +- One blue door +- One zebra-stripe door +- A Dark Portal +- Metal bars removable via bomb slot +- A kinetic orb cannon +- A drain plug that can be destroyed to drain the central pool +- A half pipe revealed once the water is drained +- Spider tracks accessible via half-pipe +- Flippable spiny platforms +""" + +from BaseClasses import MultiWorld, ItemClassification +from ... import ( + can_activate_bomb_slot, + can_activate_dark_portal, + can_lay_bomb, + can_lay_pb, + can_use_dark_beam, + can_use_grapple_beam, + can_use_screw_attack, + has_trick_enabled, can_use_boost_ball, can_use_spider_ball +) +from .....Enums import DoorCover +from .....Items import MetroidPrime2Item +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region +from .....Utils import condition_and, condition_or + + +# solo bomb slot: lowers the gate locking off the portal +# paired bomb slots: removes barrier around item; extends platform from wall below item ledge +# spiny platforms don't stay flipped on room reload, so they aren't room state entities +# the spiny platforms can no longer be interacted with once the water is drained + + +class GatheringHall_UpperDoorLedge(MetroidPrime2Region): + """Ledge with a white door leading to Gathering Access. + If you know some tricks, you can jump or screw attack over to the rotating spider track elements to bypass boost ball.""" + name="Gathering Hall" + desc="Upper Door Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Access", + door=DoorCover.Light, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Laser Platform)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Item Ledge)", + rule=lambda state, player: condition_and([ + state.has("Torvus Bog - Gathering Hall | Paired Bomb Slots Activated", player) + can_use_screw_attack(state, player), + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Cannon Ledge)", + rule=lambda state, player: can_use_screw_attack(state, player), + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (North Door Ledge)", + rule=lambda state, player: can_use_screw_attack(state, player), + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (South Door Ledge)", + rule=lambda state, player: can_use_screw_attack(state, player), + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Bottom)", + rule=lambda state, player: True, + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Spider Tracks)", + rule=lambda state, player: condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Gathering Hall | SA to Rotating Spider Track Segments"), + can_use_screw_attack(state, player) + ]) + ) + ] + + +class GatheringHall_CannonLedge(MetroidPrime2Region): + """A ledge in the center of the room. Is below the item ledge. Its most notable feature is the Kinetic Orb Cannon + that launches Samus to the upper ledge.""" + name="Gathering Hall" + desc="Cannon Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (North Door Ledge)", + rule=lambda state, player: condition_or([ + can_use_grapple_beam(state, player), + can_use_screw_attack(state, player) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (South Door Ledge)", + rule=lambda state, player: condition_or([ + can_use_grapple_beam(state, player), + can_use_screw_attack(state, player) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Laser Ledge)", + rule=lambda state, player: can_use_screw_attack(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Upper Door Ledge)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Bottom)", + rule=lambda state, player: state.has("Morph Ball", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Item Ledge)", + rule=lambda state, player: condition_and([ + state.has("Torvus Bog - Gathering Hall | Paired Bomb Slots Activated", player), + state.has("Space Jump Boots", player) + ]) + ) + ] + + +class GatheringHall_ItemLedge(MetroidPrime2Region): + """Has the item. Is protected by a barrier until the paired bomb slots are activated.""" + name="Gathering Hall" + desc="Item Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (North Door Ledge)", + rule=lambda state, player: condition_or([ + can_use_grapple_beam(state, player), + can_use_screw_attack(state, player) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (South Door Ledge)", + rule=lambda state, player: condition_or([ + can_use_grapple_beam(state, player), + can_use_screw_attack(state, player) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Laser Ledge)", + rule=lambda state, player: can_use_screw_attack(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Upper Door Ledge)", + rule=lambda state, player: can_use_screw_attack(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Cannon Ledge)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Bottom)", + rule=lambda state, player: True + ), + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Pickup (Missile Expansion)", + can_access=lambda state, player: True + ) + + +class GatheringHall_LaserLedge(MetroidPrime2Region): + """Can be accessed via the spiny platforms. The platform here is used to """ + name="Gathering Hall" + desc="Laser Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Cannon Ledge)", + rule=lambda state, player: can_use_screw_attack(state, player), + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (North Door Ledge)", + rule=lambda state, player: can_use_screw_attack(state, player), + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (South Door Ledge)", + rule=lambda state, player: can_use_screw_attack(state, player), + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Bottom)", + rule=lambda state, player: True, + ), + ] + + +class GatheringHall_NorthDoorLedge(MetroidPrime2Region): + name="Gathering Hall" + desc="North Door Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Transit Tunnel West (South Entrance)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Cannon Ledge)", + rule=lambda state, player: condition_or([ + can_use_grapple_beam(state, player), + can_use_screw_attack(state, player) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Laser Ledge)", + rule=lambda state, player: condition_or([ + can_use_dark_beam(state, player), + can_use_screw_attack(state, player) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Bottom)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (South Door Ledge)", + rule=lambda state, player: can_use_screw_attack(state, player), + ) + ] + + +class GatheringHall_SouthDoorLedge(MetroidPrime2Region): + """The ledge with the solo bomb slot and zebra-stripe door.""" + name="Gathering Hall" + desc="South Door Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Transit Tunnel South (West Entrance)", + door=DoorCover.Annihilator, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Cannon Ledge)", + rule=lambda state, player: condition_or([ + can_use_grapple_beam(state, player), + can_use_screw_attack(state, player) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Laser Ledge)", + rule=lambda state, player: can_use_screw_attack(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Bottom)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (North Door Ledge)", + rule=lambda state, player: can_use_screw_attack(state, player), + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Solo Bomb Slot Activated", + locked_item=MetroidPrime2Item( + name="Torvus Bog - Gathering Hall | Solo Bomb Slot Activated", + classification=ItemClassification.progression, + code=None, + player=player + ), + can_access=lambda state, player: can_activate_bomb_slot(state, player, "Torvus Bog - Gathering Hall | Activate Bomb Slot without Bombs") + ) + + +class GatheringHall_Bottom(MetroidPrime2Region): + """The flooded section of the room. Can be drained. The spiky platforms end up here once the water is drained. + There is a Blogg enemy in the water until it is drained.""" + name="Gathering Hall" + desc="Bottom" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (North Door Ledge)", + rule=lambda state, player: condition_or([ + state.has("Space Jump Boots", player), + state.has("Gravity Boost", player) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Spider Tracks)", + rule=lambda state, player: condition_and([ + state.has("Torvus Bog - Gathering Hall | Water Drained", player), + can_lay_bomb(state, player), + can_use_boost_ball(state, player), + can_use_spider_ball(state, player), + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Portal Alcove)", + rule=lambda state, player: state.has("Torvus Bog - Gathering Hall | Solo Bomb Slot Activated", player) + ), + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Water Drained", + locked_item=MetroidPrime2Item( + name="Torvus Bog - Gathering Hall | Water Drained", + classification=ItemClassification.progression, + code=None, + player=player, + ), + can_access=lambda state, player: can_lay_pb(state, player, 2) + ) + + +class GatheringHall_PortalAlcove(MetroidPrime2Region): + """Holds the Dark Portal. You can be blocked from entering the room proper if the solo bomb slot has not been activated.""" + name="Gathering Hall" + desc="Portal Alcove" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Bottom)", + rule=lambda state, player: state.has("Torvus Bog - Gathering Hall | Solo Bomb Slot Activated", player) + ), + MetroidPrime2Exit( + destination="P|Dark Torvus Bog - Crypt (Portal Alcove)", + rule=lambda state, player: can_activate_dark_portal(state, player) + ) + ] + + +class GatheringHall_SpiderTracks(MetroidPrime2Region): + """Spider tracks suspended above the water/half-pipe. They bring the player to the paired bomb slots when used, and + can be stood on using tricks for the same purpose.""" + name="Gathering Hall" + desc="Spider Tracks" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (Bottom)", + rule=lambda state, player: True + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Paired Bomb Slots Activated", + locked_item=MetroidPrime2Item( + name="Torvus Bog - Gathering Hall | Paired Bomb Slots Activated", + classification=ItemClassification.progression, + code=None, + player=player, + ), + can_access=lambda state, player: condition_or([ + condition_and([ + can_lay_bomb(state, player), + can_lay_pb(state, player, 2), + can_use_boost_ball(state, player), + can_use_spider_ball(state, player) + ]), + condition_and([ + can_use_screw_attack(state, player), + has_trick_enabled(state, player, "Torvus Bog - Gathering Hall | SA to Rotating Spider Track Segments"), + can_activate_bomb_slot(state, player, "Torvus Bog - Gathering Hall | Activate Bomb Slot without Bombs") + ]) + ]) + ) diff --git a/src/logic/metroidprime2/light_world/torvus_bog/great_bridge.py b/src/logic/metroidprime2/light_world/torvus_bog/great_bridge.py new file mode 100644 index 0000000..f6a0c60 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/great_bridge.py @@ -0,0 +1,236 @@ +"""A large room with several doors, characterized by a large bridge that leads to the upper section of Temple Access.""" + +from BaseClasses import MultiWorld, ItemClassification +from ... import has_trick_enabled, can_lay_pb, can_lay_bomb +from .....Enums import DoorCover +from .....Items import MetroidPrime2Item +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region +from .....Utils import condition_or, condition_and + + +class GreatBridge_BehindTranslatorGate(MetroidPrime2Region): + """Requires the Translator Gate to be lowered, or """ + name = "Great Bridge" + desc = "Behind Translator Gate" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Map Station", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Cannon Ledge)", + door=DoorCover.EmeraldTranslator, + rule=lambda state, player: state.has("Space Jump Boots", player) + ) + ] + + +class GreatBridge_Beach(MetroidPrime2Region): + """Contains the water in the lower part of the room as well as the ledge that can be reached without jumping. + Contains Grenchler and Shrieker enemies on different room layers.""" + name = "Great Bridge" + desc = "Beach" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (North Path)", + rule=lambda state, player: condition_or([ + state.has("Space Jump Boots", player), + state.has("Screw Attack", player), + condition_and([ + state.has("Morph Ball", player), + state.has("Boost Ball", player), + has_trick_enabled(state, player, "Torvus Bog - Great Bridge | Wall Boost to North Path") + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Cannon Ledge)", + rule=lambda state, player: condition_and([ + state.has("Space Jump Boots", player), + state.has("Screw Attack", player) + ]) + ) + ] + + +class GreatBridge_Bridge(MetroidPrime2Region): + """The top of the bridge. Some Shriekbats hang from the ceiling near the upper door here.""" + name = "Great Bridge" + desc="Bridge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Temple Access (Upper Great Bridge Side)", + door=DoorCover.Dark, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Cannon Ledge)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (North Path)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Beach)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Morph Ball Tunnel)", + rule=lambda state, player: can_lay_pb(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Scan Panel Ledge)", + rule=lambda state, player: condition_or([ + state.has("Space Jump", player), + state.has("Screw Attack", player) + ]) + ) + ] + + +class GreatBridge_CannonLedge(MetroidPrime2Region): + """Contains the cannon that shoots Samus over to the bridge. Also has a Translator Gate which separates it from the + Behind Translator Gate subregion.""" + name = "Great Bridge" + desc="Cannon Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Beach)", + rule = lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Behind Translator Gate)", + door=DoorCover.EmeraldTranslator, + rule=lambda state, player: condition_or([ + condition_and([ + state.has("Space Jump Boots", player), + has_trick_enabled(state, player, "Torvus Bog - Great Bridge | Slope Jump over Translator Gate") + ]), + condition_and([ + state.has("Scan Visor", player), + state.has("Emerald Translator", player) #check translator + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Bridge)", + rule=lambda state, player: condition_or([ + condition_and([ + state.has("Torvus Bog - Great Bridge | Cannon Activated", player), + state.has("Morph Ball", player) + ]), + condition_and([ + state.has_all(["Space Jump Boots", "Scan Visor"], player), + has_trick_enabled(state, player, "Torvus Bog - Great Bridge | Slope Jump over Translator Gate"), + has_trick_enabled(state, player, "Torvus Bog - Great Bridge | Scan Dash across Top") + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Scan Panel Ledge)", + rule=lambda state, player: state.has("Screw Attack", player) + ) + ] + +class GreatBridge_MorphBallTunnel(MetroidPrime2Region): + """Blocked by Bendezium-containing rocks on both ends. Contains a pickup. Links the Scan Panel Ledge and Bridge subregions.""" + name = "Great Bridge" + desc="Morph Ball Tunnel" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Scan Panel Ledge)", + rule=lambda state, player: can_lay_pb(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Bridge)", + rule=lambda state, player: can_lay_pb(state, player) + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Pickup (Power Bomb Expansion)", + can_access=lambda state, player: True + ) + + +class GreatBridge_NorthPath(MetroidPrime2Region): + """The walkway connecting the lower Temple Access door and the Missile Cover door.""" + name = "Great Bridge" + desc="North Path" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Portal Chamber (Great Bridge Side)", + door=DoorCover.Missile, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Temple Access (Lower Great Bridge Side)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Beach)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Cannon Ledge)", + rule=lambda state, player: condition_or([ + state.has('Space Jump Boots', player), + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Great Bridge | Instant Unmorph to Cannon Ledge"), + can_lay_bomb(state, player) + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Scan Panel Ledge)", + rule=lambda state, player: condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Great Bridge | Instant Unmorph to Scan Panel Ledge"), + can_lay_bomb(state, player) + ]) + ) + ] + + +class GreatBridge_ScanPanelLedge(MetroidPrime2Region): + """Contains a scan panel that enables the Kinetic Orb Cannon. Also connects to the Morph Ball tunnel through a Bendezium blockage.""" + name = "Great Bridge" + desc="Scan Panel Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Abandoned Worksite (Great Bridge Entrance)", + door=DoorCover.Light, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (North Path)", + rule=lambda state, player: True, + ), + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Bridge)", + rule=lambda state, player: state.has_all(["Space Jump Boots", "Screw Attack"], player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Morph Ball Tunnel)", + rule=lambda state, player: can_lay_pb(state, player) + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Cannon Activated", + locked_item=MetroidPrime2Item( + name="Torvus Bog - Great Bridge | Cannon Activated", + classification=ItemClassification.progression, + code=None, + player=player, + ), + can_access=lambda state, player: state.has("Scan Visor", player) + ) diff --git a/src/logic/metroidprime2/light_world/torvus_bog/grove_access.py b/src/logic/metroidprime2/light_world/torvus_bog/grove_access.py new file mode 100644 index 0000000..2fef3ba --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/grove_access.py @@ -0,0 +1,21 @@ +"""Connects Torvus Grove and the caged area of Forgotten Bridge.""" + +from BaseClasses import MultiWorld, ItemClassification +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region + + +class GroveAccess(MetroidPrime2Region): + name="Grove Access" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Cage)", + door=DoorCover.Dark, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Grove (Upper Door Ledge)", + door=DoorCover.Dark, + rule=lambda state, player: True + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/hydrochamber_storage.py b/src/logic/metroidprime2/light_world/torvus_bog/hydrochamber_storage.py new file mode 100644 index 0000000..06ab10f --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/hydrochamber_storage.py @@ -0,0 +1,25 @@ +"""Contains the Gravity Boost in vanilla. Picking up the item would be the trigger for the Alpha Blogg boss, +but it has been moved in the Archipelago randomizer.""" + +from BaseClasses import MultiWorld, ItemClassification +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region + + +class HydrochamberStorage(MetroidPrime2Region): + name="Hydrochamber Storage" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Main Hydrochamber (Lower Door)", + door=DoorCover.Any, + rule=lambda state, player: True + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Pickup (Gravity Boost)", + can_access=lambda state, player: True + ) diff --git a/src/logic/metroidprime2/light_world/torvus_bog/hydrodynamo_shaft.py b/src/logic/metroidprime2/light_world/torvus_bog/hydrodynamo_shaft.py new file mode 100644 index 0000000..05d71b5 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/hydrodynamo_shaft.py @@ -0,0 +1,109 @@ +"""""" + +from BaseClasses import MultiWorld, ItemClassification +from src.Utils import condition_or, condition_and +from ... import has_trick_enabled, can_lay_bomb, can_activate_dark_portal, can_use_screw_attack +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region + + +class HydrodynamoShaft_Top(MetroidPrime2Region): + """The top section of the room, accessible through the blue door at the very bottom of Hydrodymano Station.""" + name="Hydrodynamo Shaft" + desc= "Top" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Under Movable Base)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Shaft (Stairs)", + rule=lambda state, player: True + ) + ] + + +class HydrodynamoShaft_Stairs(MetroidPrime2Region): + """The stairs. Can be difficult to reach from below.""" + name="Hydrodynamo Shaft" + desc= "Stairs" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Shaft (Top)", + rule=lambda state, player: state.has("Gravity Boost", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Shaft (Main)", + rule=lambda state, player: state.has("Gravity Boost", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Shaft (Portal Alcove)", + rule=lambda state, player: condition_and([ + can_use_screw_attack(state, player), + has_trick_enabled(state, player, + "Torvus Bog - Hydrodynamo Shaft | Carry Air Underwater from Hydrodynamo Station") + ]) + ) + ] + + +class HydrodynamoShaft_Main(MetroidPrime2Region): + """The subregion that links the Bottom, Stairs, and Portal Ledge room subregions.""" + name="Hydrodynamo Shaft" + desc= "Main" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Main Hydrochamber (Door Ledge)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Shaft (Stairs)", + rule=lambda state, player: state.has("Gravity Boost", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Shaft (Portal Alcove)", + rule=lambda state, player: state.has("Gravity Boost", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Shaft (Bottom)", + rule=lambda state, player: True + ) + ] + + +class HydrodynamoShaft_PortalAlcove(MetroidPrime2Region): + """Contains a Dark Portal.""" + name="Hydrodynamo Shaft" + desc= "Portal Alcove" + exits_ = [ + MetroidPrime2Exit( + destination="P|Dark Torvus Bog - Undertemple Access (Portal Alcove)", + rule=lambda state, player: can_activate_dark_portal(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Shaft (Main)", + rule=lambda state, player: True + ) + ] + +class HydrodynamoShaft_Bottom(MetroidPrime2Region): + """Contains a blue door leading to Main Hydrochamber.""" + name="Hydrodynamo Shaft" + desc= "Bottom" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Main Hydrochamber (Door Ledge)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Shaft (Main)", + rule=lambda state, player: condition_or([ + state.has("Gravity Boost", player), + can_lay_bomb(state, player), + state.has("Space Jump Boots", player) + ]) + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/hydrodynamo_station.py b/src/logic/metroidprime2/light_world/torvus_bog/hydrodynamo_station.py new file mode 100644 index 0000000..9446e0d --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/hydrodynamo_station.py @@ -0,0 +1,373 @@ +"""A the hub room of the lower level of Torvus. A large portion of this room is underwater.Has many doors to enter and things to scan. +The bottom raises up once all three panels are scanned, revealing a door.""" + +from BaseClasses import MultiWorld, ItemClassification +from ... import ( + has_trick_enabled, + can_lay_bomb, + has_missile_count, + hydrodynamo_station_has_scanned_panels, + underwater_movement) +from .....Enums import DoorCover +from .....Items import MetroidPrime2Item +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region +from .....Utils import condition_and, condition_or + +# tricks in this room: +# "Torvus Bog - Hydrodynamo Station | Boost Jump" +# - Cross gaps using the speed and momentum of a boost ball while underwater. Requires good unmorph timing to get an instant unmorph. +# - https://youtu.be/7I2Jl824CMI + +# "Torvus Bog - Hydrodynamo Station | Underwater Dash" +# - Dash while entering the water, preserving your momentum by taking advantage of the underwater physics. +# - https://youtu.be/IUcPDkHKd0I +# - https://youtu.be/nY4U_0Uyn0M + +# "Torvus Bog - Hydrodynamo Station | Air Underwater" +# - Trick the game into thinking you are not in the water when you are. This allows for standard movement. +# - You can activate Air Underwater by causing a camera transition while entering the water. This is almost always done by morphing. +# - https://youtu.be/g7DeBeDMpeU + +# "Torvus Bog - Hydrodynamo Station | Seeker Skip" +# - Break the Seeker locks using Missiles and Screw Attack. +# - Requires Air Underwater since you can't Screw Attack underwater normally. +# - https://youtu.be/ALjnm411Ldk + +class HydrodynamoStation_AboveWater(MetroidPrime2Region): + """Contains the entrance to Underground Transport, plus some rotating platforms that can be jumped + on to reach the top. Puffer enemies in the air can menace Samus while she navigates the platforms.""" + name="Hydrodynamo Station" + desc="Above Water" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Underground Transport (Lower)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Top)", + rule=lambda state, player: state.has("Space Jump Boots", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Three Doors)", + rule=lambda state, player: True + ), + ] + + +class HydrodynamoStation_Top(MetroidPrime2Region): + """The Kinetic Orb launcher in the center of the room launches you to the top of the tower. Samus can break a Missile + door cover to reach Save Station B and Samus can jump down to reach the rest of the room.""" + name="Hydrodynamo Station" + desc = "Top" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Top Door Ledge)", + rule=lambda state, player: state.has("Space Jump Boots", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Above Water)", + rule=lambda state, player: True + ), + ] + + +class HydrodynamoStation_TopDoorLedge(MetroidPrime2Region): + """The ledge containing the door to Save Station B. The door has a Missile cover.""" + name="Hydrodynamo Station" + desc = "Top Door Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Save Station B", + door=DoorCover.Missile, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Above Water)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Top)", + rule=lambda state, player: state.has("Space Jump Boots", player) + ), + ] + + +class HydrodynamoStation_ThreeDoors(MetroidPrime2Region): + """The first underwater section of the room, contains ledges with doors leading to Gathering Hall, Training Chamber, and + Catacombs. The ledges in front of each door are their own subregions""" + name="Hydrodynamo Station" + desc = "Three Doors" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (North Door Ledge)", + rule=lambda state, player: condition_or([ + state.has("Gravity Boost", player), + condition_and([ + state.has("Torvus Bog - Hydrodynamo Station | Scanned North Panel", player), + condition_or([ + can_lay_bomb(state, player), + state.has("Space Jump Boots", player) + ]), + ]) + ]) + ), # summary: if you have gravity boost, you're fine. If you have scanned the panel and can either DBJ or + # have SJB, you're fine + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (West Door Ledge)", + rule=lambda state, player: condition_or([ + state.has("Gravity Boost", player), + condition_and([ + state.has("Torvus Bog - Hydrodynamo Station | Scanned West Panel", player), + condition_or([ + can_lay_bomb(state, player), + state.has("Space Jump Boots", player) + ]), + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (East Door Ledge)", + rule=lambda state, player: condition_or([ + state.has("Gravity Boost", player), + condition_and([ + state.has("Torvus Bog - Hydrodynamo Station | Scanned East Panel", player), + condition_or([ + can_lay_bomb(state, player), + state.has("Space Jump Boots", player) + ]), + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Above Water)", + rule=lambda state, player: state.has("Gravity Boost", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Above Movable Base)", + rule=lambda state, player: True # just fall down + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (North Scan Ledge)", + rule=lambda state, player: True + ) + ] + + +class HydrodynamoStation_NorthDoorLedge(MetroidPrime2Region): + """Contains a door leading to Training Access, which is blocked by a Seeker Cover.""" + name="Hydrodynamo Station" + desc = "North Door Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Above Water)", + rule=lambda state, player: state.has("Gravity Boost", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Three Doors)", + rule=lambda state, player: condition_or([ + state.has("Gravity Boost", player), + state.has("Space Jump Boots", player), + state.has("Torvus Bog - Hydrodynamo Station | Scanned North Panel", player) + ]) # this jump is very tight; it's doable with nothing, but I think that's unlikely for most players + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Above Movable Base)", + rule=lambda state, player: True # just fall down + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (North Scan Ledge)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Access (South)", + door=DoorCover.Seeker, + rule=lambda state, player: condition_or([ + condition_and([ + state.has("Seeker Missile Launcher", player), + has_missile_count(state, player, 6) + ]), + condition_and([ + has_missile_count(state, player, 5), + state.has("Screw Attack", player), + has_trick_enabled(state, player, "Torvus Bog - Hydrodynamo Station | Air Underwater"), + # air underwater is needed for seeker skip here because that's the only way to use Screw Attack underwater + has_trick_enabled(state, player, "Torvus Bog - Hydrodynamo Station | Seeker Skip") + ]) + ]) + ), + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Pickup (Missile Expansion)", + can_access=lambda state, player: True + ) + + +class HydrodynamoStation_WestDoorLedge(MetroidPrime2Region): + """Has a scan panel and a white door leading to Gathering Access.""" + name="Hydrodynamo Station" + desc = "West Door Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Above Water)", + rule=lambda state, player: state.has("Gravity Boost", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Three Doors)", + rule=lambda state, player: condition_or([ + state.has("Gravity Boost", player), + state.has("Space Jump Boots", player), + state.has("Torvus Bog - Hydrodynamo Station | Scanned West Panel", player) + ]) #this jump is very tight; it's doable with nothing, but I think that's unlikely for most players + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (North Door Ledge)", + rule=lambda state, player: underwater_movement(state, player, "Torvus Bog - Hydrodynamo Station | Underwater Dash") + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (East Door Ledge)", + rule=lambda state, player: underwater_movement(state, player, "Torvus Bog - Hydrodynamo Station | Underwater Dash") + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Above Movable Base)", + rule=lambda state, player: True # just fall down + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Access (East)", + door=DoorCover.Light, + rule=lambda state, player: True + ), + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Scanned West Panel", + locked_item=MetroidPrime2Item( + name="Torvus Bog - Hydrodynamo Station | Scanned West Panel", + classification=ItemClassification.progression, + code=None, + player=player, + ), + can_access=lambda state, player: state.has("Scan Visor", player) + ) + + +class HydrodynamoStation_EastDoorLedge(MetroidPrime2Region): + """Ledge with a scan panel and a dark door leading to Catacombs Access.""" + name="Hydrodynamo Station" + desc = "East Door Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Above Water)", + rule=lambda state, player: state.has("Gravity Boost", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Three Doors)", + rule=lambda state, player: condition_or([ + state.has("Gravity Boost", player), + state.has("Space Jump Boots", player), + state.has("Torvus Bog - Hydrodynamo Station | Scanned East Panel", player) + ]) # this jump is very tight; it's doable with nothing, but I think that's unlikely for most players + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (West Door Ledge)", + rule=lambda state, player: underwater_movement(state, player, "Torvus Bog - Hydrodynamo Station | Underwater Dash") + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (North Door Ledge)", + rule=lambda state, player: underwater_movement(state, player, "Torvus Bog - Hydrodynamo Station | Underwater Dash") + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Above Movable Base)", + rule=lambda state, player: True # just fall down + ), + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs Access (West)", + door=DoorCover.Dark, + rule=lambda state, player: True + ), + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Scanned East Panel", + locked_item=MetroidPrime2Item( + name="Torvus Bog - Hydrodynamo Station | Scanned East Panel", + classification=ItemClassification.progression, + code=None, + player=player, + ), + can_access=lambda state, player: state.has("Scan Visor", player) + ) + + +class HydrodynamoStation_NorthScanLedge(MetroidPrime2Region): + """Contains the northern scan panel. You would need to fall down from Three Doors to reach this.""" + name="Hydrodynamo Station" + desc="North Scan Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Above Movable Base)", + rule=lambda state, player: True # just fall down + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Three Doors)", + rule=lambda state, player: state.has("Gravity Boost", player) + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Scanned North Panel", + locked_item=MetroidPrime2Item( + name="Torvus Bog - Hydrodynamo Station | Scanned North Panel", + classification=ItemClassification.progression, + code=None, + player=player, + ), + can_access=lambda state, player: state.has("Scan Visor", player) + ) + + +class HydrodynamoStation_AboveMovableBase(MetroidPrime2Region): + """Made smaller when the base below moves up. Also contains the Kinetic Orb Cannon, but the + cannon can't be logically relevant because it becomes inoperable after raising the movable base.""" + name="Hydrodynamo Station" + desc = "Above Movable Base" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Under Movable Base)", + rule=lambda state, player: hydrodynamo_station_has_scanned_panels(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Three Doors)", + rule=lambda state, player: state.has("Gravity Boost", player) + ) + ] + + +class HydrodynamoStation_UnderMovableBase(MetroidPrime2Region): + """Opened by scanning all three panels in the underwater section of the room.""" + name="Hydrodynamo Station" + desc = "Under Movable Base" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Shaft (Top)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Above Movable Base)", + rule=lambda state, player: hydrodynamo_station_has_scanned_panels(state, player) + ), + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/main_hydrochamber.py b/src/logic/metroidprime2/light_world/torvus_bog/main_hydrochamber.py new file mode 100644 index 0000000..54c41a6 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/main_hydrochamber.py @@ -0,0 +1,152 @@ +"""This room is most notable for being where the Alpha Blogg fight takes place. It has a large fan contraption that gets +destroyed during the boss cutscene.""" + +from BaseClasses import MultiWorld, ItemClassification +from ... import has_trick_enabled, can_lay_bomb, can_use_spider_ball, can_defeat_alpha_blogg, can_activate_dark_portal +from .....Enums import DoorCover +from .....Items import MetroidPrime2Item +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region +from .....Utils import condition_or, condition_and + + +# tricks: +# Alpha Blogg Skip: +# - Get onto the first platform from the bottom with a Bomb Space Jump, then fly over to the upper door using Grav Boost. +# - https://youtu.be/OSnzyN6ZFR0 + +# BSJ to skip Spider Track: +# - The track is standable, but you must Bomb Space Jump to get onto it. +# - https://youtu.be/KPs8MIRVY-I + +# Climb to Top Post-Alpha Blogg (NSJ): +# - The last jump requires an instant unmorph after rolling off the second platform from the bottom. +# - https://www.youtube.com/watch?v=tDa-PxtRTzo + + +class MainHydrochamber_Top(MetroidPrime2Region): + """The ledge containing the exit door.""" + name = "Main Hydrochamber" + desc="Top" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Hydrochamber Shaft (Bottom)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Main Hydrochamber (Main)", + rule=lambda state, player: True + ), + ] + + +class MainHydrochamber_Main(MetroidPrime2Region): + """Most of the room. Contains the fan platforms that are accessible after the boss battle.""" + name = "Main Hydrochamber" + desc="Main" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Main Hydrochamber (Spider Track)", + rule=lambda state, player: state.has("Torvus Bog - Main HydroChamber | Alpha Blogg Dead", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Main Hydrochamber (Top)", + # options: main (has grav boost or has beaten boss) + rule=lambda state, player: condition_or([ + # boss is dead + condition_and([ + state.has("Gravity Boost", player), + condition_or([ + condition_and([ + state.has('Space Jump Boots', player), # vanilla strats + state.has("Torvus Bog - Main HydroChamber | Alpha Blogg Dead", player) + ]), + condition_and([ # instant unmorph strats + has_trick_enabled(state, player, + "Torvus Bog - Main Hydrochamber | Climb to Top Post-Alpha Blogg (NSJ)"), + state.has('Morph Ball', player), + ]) + ]) + ]), + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Main Hydrochamber | Alpha Blogg Skip"), + state.has('Gravity Boost', player), + can_lay_bomb(state, player), + state.has('Space Jump Boots', player) + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Main Hydrochamber (Lower Door)", + rule=lambda state, player: True + ), + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Alpha Blogg Dead", + locked_item=MetroidPrime2Item( + name="Torvus Bog - Main Hydrochamber | Alpha Blogg Dead", + classification=ItemClassification.progression, + code=None, + player=player, + ), + can_access=lambda state, player: can_defeat_alpha_blogg(state, player) + ) + + +class MainHydrochamber_LowerDoor(MetroidPrime2Region): + """Leads to Hydrochamber Storage. Gets blocked off by the rotating room during the boss fight.""" + name = "Main Hydrochamber" + desc="Lower Door" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Main Hydrochamber (Main)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrochamber Storage", + rule=lambda state, player: state.has("Torvus Bog - Main HydroChamber | Alpha Blogg Dead", player) + ), + ] + + +class MainHydrochamber_SpiderTrack(MetroidPrime2Region): + """Leads to a Dark Portal. Gets blocked off by the rotating room during the boss fight.""" + name = "Main Hydrochamber" + desc="Spider Track" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Main Hydrochamber (Portal Ledge)", + rule=lambda state, player: condition_or([ + can_use_spider_ball(state, player), + condition_and([ + can_lay_bomb(state, player), + state.has('Space Jump Boots', player), + has_trick_enabled(state, player, "Torvus Bog - Main Hydrochamber | BSJ to skip Spider Track") + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Main Hydrochamber (Main)", + rule=lambda state, player: True + ), + ] + + +class MainHydrochamber_PortalLedge(MetroidPrime2Region): + """Accessible via the Spider track. Connects to Dark Aether.""" + name = "Main Hydrochamber" + desc="Portal Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="P|Dark Torvus Bog - Undertemple (Portal Ledge)", + rule=lambda state, player: can_activate_dark_portal(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Main Hydrochamber (Main)", + rule=lambda state, player: True + ), + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/meditation_vista.py b/src/logic/metroidprime2/light_world/torvus_bog/meditation_vista.py new file mode 100644 index 0000000..e79f528 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/meditation_vista.py @@ -0,0 +1,50 @@ +"""A seemingly simple room containing a portal device to enter Dark Aether. With Screw Attack, the player can reach a +floating platform that can carry them to a pickup.""" + +from BaseClasses import MultiWorld, ItemClassification +from ... import can_use_screw_attack +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region + + +class MeditationVista_Entrance(MetroidPrime2Region): + """Contains a portal device.""" + name = "Meditation Vista" + desc = "Entrance" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Grove (Center)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="P|Dark Torvus Bog - Gloom Vista", + rule=lambda state, player: state.has("Scan Visor", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Meditation Vista (Floating Platform)", + door=DoorCover.Any, + rule=lambda state, player: can_use_screw_attack(state, player) + ), + ] + + +class MeditationVista_FloatingPlatform(MetroidPrime2Region): + """Ride this to reach an item.""" + name="Meditation Vista" + desc="Floating Platform" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Meditation Vista (Entrance)", + rule=lambda state, player: True + ) + ] + + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Pickup (Energy Tank)", + can_access=lambda state, player: state.has("Morph Ball", player) + ) diff --git a/src/logic/metroidprime2/light_world/torvus_bog/path_of_roots.py b/src/logic/metroidprime2/light_world/torvus_bog/path_of_roots.py new file mode 100644 index 0000000..4be99bc --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/path_of_roots.py @@ -0,0 +1,96 @@ +"""Has a cage with an item on top. You're expected to come back here once you can use Grapple Beam in vanilla.""" + +from BaseClasses import MultiWorld, ItemClassification +from ... import can_use_grapple_beam, can_use_screw_attack, can_use_dark_beam +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region +from .....Utils import condition_or + + +class PathOfRoots_AboveCage(MetroidPrime2Region): + """The small area above the cage. You can grab a pickup here.""" + name="Path of Roots" + desc="Above Cage" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Path of Roots (Middle)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Path of Roots (Lagoon Side)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Path of Roots (Great Bridge Ledge)", + rule=lambda state, player: condition_or([ + can_use_grapple_beam(state, player), + can_use_screw_attack(state, player) + ]) + ), + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Pickup (Missile Expansion)", + can_access=lambda state, player: True + ) + + +class PathOfRoots_GreatBridgeLedge(MetroidPrime2Region): + """A ledge containing a blue door that leads to Great Bridge. Also has a lore projector. + Entry: The Ing Attack (GC) / Dark Aether (Trilogy)""" + name="Path of Roots" + desc="Great Bridge Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Beach)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Path of Roots (Above Cage)", + rule=lambda state, player: condition_or([ + can_use_grapple_beam(state, player), + can_use_screw_attack(state, player) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Path of Roots (Middle)", + rule=lambda state, player: True + ) + ] + + +class PathOfRoots_LagoonSide(MetroidPrime2Region): + """Contains a dark door that connects to Torvus Lagoon.""" + name="Path of Roots" + desc="Lagoon Side" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Lagoon (Beach)", + door=DoorCover.Dark, + rule=lambda state, player: can_use_dark_beam(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Path of Roots (Middle)", + rule=lambda state, player: state.has("Morph Ball", player) + ) + ] + + +class PathOfRoots_Middle(MetroidPrime2Region): + """Contains the watery area below the Great Bridge Ledge. Also contains the under-side of the cage.""" + name="Path of Roots" + desc="Middle" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Path of Roots (Lagoon Side)", + rule=lambda state, player: state.has("Morph Ball", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Path of Roots (Great Bridge Ledge)", + rule=lambda state, player: state.has("Space Jump Boots", player) + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/plaza_access.py b/src/logic/metroidprime2/light_world/torvus_bog/plaza_access.py new file mode 100644 index 0000000..bf85693 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/plaza_access.py @@ -0,0 +1,80 @@ +"""A short Morph Ball puzzle room that requires Bombs for traversal and Boost to get the item.""" + +from BaseClasses import MultiWorld, ItemClassification +from ... import can_lay_bomb, can_use_boost_ball +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region + + +class PlazaAccess_ForgottenBridgeEntrance(MetroidPrime2Region): + """The entrance on the Forgotten Bridge side. You are most likely starting here.""" + name = "Plaza Access" + desc = "Forgotten Bridge Entrance" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Cage)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Plaza Access (Morph Ball Tunnel Forgotten Bridge Side)", + rule=lambda state, player: can_lay_bomb(state, player) + ) + ] + + +class PlazaAccess_HalfPipe(MetroidPrime2Region): + """Entered via tunnel. Ride this half-pipe to the top to get a pickup.""" + name = "Plaza Access" + desc="Half-Pipe" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Plaza Access (Maze)", + rule=lambda state, player: state.has("Morph Ball", player) + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Pickup (Missile Expansion)", + can_access=lambda state, player: can_use_boost_ball(state, player) + ) + + +class PlazaAccess_Maze(MetroidPrime2Region): + """Has a few contraptions that need flipping via bomb slot in order to traverse the room.""" + name = "Plaza Access" + desc="Maze" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Plaza Access (Half-Pipe)", + rule=lambda state, player: can_lay_bomb(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Plaza Access (Torvus Plaza Entrance)", + rule=lambda state, player: can_lay_bomb(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Plaza Access (Forgotten Bridge Entrance)", + rule=lambda state, player: can_lay_bomb(state, player) + ), + ] + + +class PlazaAccess_TorvusPlazaEntrance(MetroidPrime2Region): + """Contains a blue door leading to Torvus Plaza.""" + name = "Plaza Access" + desc="Torvus Plaza Entrance" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Plaza (Entrance)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Plaza Access (Maze)", + rule=lambda state, player: can_lay_bomb(state, player) + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/portal_chamber.py b/src/logic/metroidprime2/light_world/torvus_bog/portal_chamber.py new file mode 100644 index 0000000..cc98988 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/portal_chamber.py @@ -0,0 +1,103 @@ +from BaseClasses import MultiWorld, ItemClassification +from ... import can_activate_dark_portal, can_lay_bomb, can_use_boost_ball, has_trick_enabled +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region +from .....Utils import condition_or, condition_and + + +class PortalChamber_Center(MetroidPrime2Region): + name="Portal Chamber" + desc="Center" + exits_ = [ + MetroidPrime2Exit( + destination="P|Dark Torvus Bog - Portal Chamber | Center", + door=DoorCover.Dark, + rule=lambda state, player: can_activate_dark_portal(state, player) + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Pickup (Missile Expansion)", + can_access=lambda state, player: True + ) + + +class PortalChamber_GreatBridgeSide(MetroidPrime2Region): + """Contains a Missile Cover door that leads to Great Bridge (North Path)""" + name = "Portal Chamber" + desc = "Great Bridge Side" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (North Path)", + door=DoorCover.Missile, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Portal Chamber (Morph Ball Tunnel)", + rule=lambda state, player: condition_or([ + #todo: change to can_ball_jump + can_lay_bomb(state, player), + condition_and([ + can_use_boost_ball(state, player), + has_trick_enabled(state, player, "Torvus Bog - Portal Chamber | Wall Boost") + ]) + ]) + ) + ] + + +class PortalChamber_MorphBallTunnel(MetroidPrime2Region): + """Connects the two Entrance subregions.""" + name = "Portal Chamber" + desc = "Morph Ball Tunnel" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Portal Chamber (Great Bridge Side)", + rule=lambda state, player: condition_or([ + # todo: change to can_ball_jump + can_lay_bomb(state, player), + condition_and([ + can_use_boost_ball(state, player), + has_trick_enabled(state, player, "Torvus Bog - Portal Chamber | Wall Boost") + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Portal Chamber (Torvus Lagoon Side)", + rule=lambda state, player: condition_or([ + # todo: change to can_ball_jump + can_lay_bomb(state, player), + condition_and([ + can_use_boost_ball(state, player), + has_trick_enabled(state, player, "Torvus Bog - Portal Chamber | Wall Boost") + ]) + ]) + ) + ] + + +class PortalChamber_TorvusLagoonSide(MetroidPrime2Region): + """Has a blue door that leads to the bridge section of Torvus Lagoon.""" + name="Portal Chamber" + desc="Torvus Lagoon Side" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Portal Chamber (Morph Ball Tunnel)", + rule=lambda state, player: condition_or([ + # todo: change to can_ball_jump + can_lay_bomb(state, player), + condition_and([ + can_use_boost_ball(state, player), + has_trick_enabled(state, player, "Torvus Bog - Portal Chamber | Wall Boost") + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Lagoon (Portal Chamber Ledge)", + door=DoorCover.Any, + rule=lambda state, player: True + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/ruined_alcove.py b/src/logic/metroidprime2/light_world/torvus_bog/ruined_alcove.py new file mode 100644 index 0000000..9167602 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/ruined_alcove.py @@ -0,0 +1,18 @@ +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Region, MetroidPrime2Exit + + +class RuinedAlcove(MetroidPrime2Region): + name = "Ruined Alcove" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Lagoon (Ruined Alcove Ledge)", + door=DoorCover.Light, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Forgotten Bridge (Shallows)", + door=DoorCover.Light, + rule=lambda state, player: True + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/save_station_a.py b/src/logic/metroidprime2/light_world/torvus_bog/save_station_a.py new file mode 100644 index 0000000..76241e0 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/save_station_a.py @@ -0,0 +1,13 @@ +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region + + +class SaveStationA(MetroidPrime2Region): + name="Save Station A" + exits_ = ([ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Lagoon (Save Room Ledge)", + door=DoorCover.Missile, + rule=lambda state, player: True + ) + ]) diff --git a/src/logic/metroidprime2/light_world/torvus_bog/save_station_b.py b/src/logic/metroidprime2/light_world/torvus_bog/save_station_b.py new file mode 100644 index 0000000..7f159d0 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/save_station_b.py @@ -0,0 +1,13 @@ +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region + + +class SaveStationB(MetroidPrime2Region): + name="Save Station B" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Top Door Ledge)", + door=DoorCover.Missile, + rule=lambda state, player: True + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/temple_access.py b/src/logic/metroidprime2/light_world/torvus_bog/temple_access.py new file mode 100644 index 0000000..47dbfb6 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/temple_access.py @@ -0,0 +1,115 @@ +"""Connects the upper parts of Great Bridge and Torvus Temple and the lower parts of both rooms. There is a one-way tunnel +in the center of the room that contains a pickup.""" + +from BaseClasses import MultiWorld, ItemClassification +from ... import has_trick_enabled, can_lay_bomb, can_lay_bomb_or_pb, can_use_screw_attack, can_use_boost_ball +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region +from .....Utils import condition_or, condition_and + + +class TempleAccess_Upper(MetroidPrime2Region): + """The upper section. Bomb a tunnel cover to fall down to the bottom section.""" + name = "Temple Access" + desc="Upper" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Temple (Arena)", + door=DoorCover.Dark, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Temple Access (Pickup Tube)", + rule=lambda state, player: condition_or([ + can_lay_bomb_or_pb(state, player), + can_use_screw_attack(state, player) + ]) # this check can be a point of no return if you're missing bombs. + ), + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Bridge)", + door=DoorCover.Dark, + rule=lambda state, player: True + ), + ] + + +class TempleAccess_PickupTube(MetroidPrime2Region): + """Contains a pickup. This is a one-way trip downward to the main morph ball tunnel.""" + name = "Temple Access" + desc="Pickup Tube" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Plaza Access (Morph Ball Tunnel)", + rule= lambda state, player: True + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Pickup (Energy Tank)", + can_access=lambda state, player: True + ) + + +class TempleAccess_LowerGreatBridgeEntrance(MetroidPrime2Region): + """A small section of the room that contains a door to Great Bridge.""" + name = "Temple Access" + desc="Lower Great Bridge Entrance" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (North Path)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Plaza Access (Morph Ball Tunnel)", + rule=lambda state, player: True + ) + ] + + +class TempleAccess_LowerTorvusTempleEntrance(MetroidPrime2Region): + """Contains a door that connects to the underground area of Torvus Temple.""" + name = "Temple Access" + desc="Lower Torvus Temple Entrance" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Temple (Underground)", + door=DoorCover.SuperMissile, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Plaza Access (Morph Ball Tunnel)", + rule=lambda state, player: True + ) + ] + + +class TempleAccess_MorphBallTunnel(MetroidPrime2Region): + """The morph ball tunnel in the center of the bottom section of the room.""" + name = "Temple Access" + desc="Morph Ball Tunnel" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Plaza Access (Lower Great Bridge Entrance)", + rule=lambda state, player: condition_or([ + can_lay_bomb(state, player), + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Temple Access | Wall Boost"), + can_use_boost_ball(state, player) + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Plaza Access (Lower Torvus Temple Entrance)", + rule=lambda state, player: condition_or([ + can_lay_bomb(state, player), + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Temple Access | Wall Boost"), + can_use_boost_ball(state, player) + ]) + ]) + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/temple_transport_access.py b/src/logic/metroidprime2/light_world/torvus_bog/temple_transport_access.py new file mode 100644 index 0000000..29e8b9a --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/temple_transport_access.py @@ -0,0 +1,18 @@ +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region + + +class TempleTransportAccess(MetroidPrime2Region): + name = "Temple Transport Access" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Transport to Temple Grounds", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Lagoon (Beach)", + door=DoorCover.Any, + rule=lambda state, player: True + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/torvus_energy_controller.py b/src/logic/metroidprime2/light_world/torvus_bog/torvus_energy_controller.py new file mode 100644 index 0000000..c84f974 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/torvus_energy_controller.py @@ -0,0 +1,33 @@ +from BaseClasses import MultiWorld, ItemClassification +from .....Enums import DoorCover +from .....Items import MetroidPrime2Item +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region + + +class TorvusEnergyController(MetroidPrime2Region): + name="Torvus Energy Controller" + exits_ = [ + MetroidPrime2Exit( + destination="Controller Access", + door=DoorCover.Any, + rule=lambda state, player: True + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Torvus Energy Returned", + locked_item=MetroidPrime2Item( + name="Torvus Bog - Torvus Energy Controller | Energy Returned", + classification=ItemClassification.progression, + code=None, + player=player, + ), + can_access=lambda state, player: state.has("Dark Torvus Bog - Dark Torvus Energy Controller | Energy Recovered", player) + ) + self.add_location( + name="Pickup (Emerald Translator)", + can_access=lambda state, player: True + ) diff --git a/src/logic/metroidprime2/light_world/torvus_bog/torvus_grove.py b/src/logic/metroidprime2/light_world/torvus_bog/torvus_grove.py new file mode 100644 index 0000000..75f0bc8 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/torvus_grove.py @@ -0,0 +1,155 @@ +"""A combat room leading to Meditation Vista. Contains a pickup that can be gotten after power-bombing the roots of the +large central tree.""" + +from BaseClasses import MultiWorld, ItemClassification +from ... import has_trick_enabled, can_lay_pb, can_lay_bomb, can_use_screw_attack, can_use_boost_ball +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region +from .....Utils import condition_or, condition_and + + +class TorvusGrove_Center(MetroidPrime2Region): + """Contains the large tree in the center, a half-pipe, two doors, and some Pirate Commando enemies triggered via cutscene.""" + name = "Torvus Grove" + desc="Center" # pretty much the entire bottom + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Grove (Connector Ledge)", + rule=lambda state, player: condition_or([ + can_use_boost_ball(state, player), + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Torvus Grove | Climb Roots to reach Connected Ledge"), + state.has("Space Jump Boots", player) + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Grove (Behind Breakable Wall)", + rule=lambda state, player: can_lay_pb(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Meditation Vista (Entrance)", + rule=lambda state, player: state.has("Torvus Bog - Torvus Grove | Pirates Dead", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Underground Tunnel (After Falls)", + door=DoorCover.Light, + rule=lambda state, player: state.has("Torvus Bog - Torvus Grove | Pirates Dead", player) + ) + ] + + +class TorvusGrove_BehindBreakableWall(MetroidPrime2Region): + """Contains a pickup.""" + name = "Torvus Grove" + desc="Behind Breakable Wall" # behind the wall broken by the felled tree + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Grove (Center)", + rule=lambda state, player: True, + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Pickup (Missile Expansion)", + can_access=lambda state, player: True + ) + + +class TorvusGrove_ConnectorLedge(MetroidPrime2Region): + """Contains the first walkway above the half-pipe.""" + name = "Torvus Grove" + desc="Connector Ledge" # at the top of the half-pipe + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Grove (Upper Door Ledge)", + rule=lambda state, player: can_use_screw_attack(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Grove (Curved Ledge)", + rule=lambda state, player: condition_or([ + state.has("Space Jump Boots", player), + state.has("Screw Attack", player), + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Torvus Grove | Scan Dash to reach Curved Ledge"), + state.has("Scan Visor", player), + ]), + has_trick_enabled(state, player, "Torvus Bog - Torvus Grove | STE to reach Curved Ledge") + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Grove (Center)", + rule=lambda state, player: True # You can always fall down + ) + ] + + +class TorvusGrove_CurvedLedge(MetroidPrime2Region): + """The second high ledge.""" + name = "Torvus Grove" + desc = "Curved Ledge" # past a small hole after the connector ledge + exits_=[ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Grove (Isolated Ledge)", + rule=lambda state, player: condition_or([ + state.has("Space Jump Boots", player), + condition_and([ + can_lay_bomb(state, player), + has_trick_enabled(state, player, "Torvus Bog - Torvus Grove | Instant Unmorph to reach Isolated Ledge") + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Grove (Center)", + rule=lambda state, player: True # You can always fall down + ) + ] + +class TorvusGrove_IsolatedLedge(MetroidPrime2Region): + """The third high ledge.""" + name = "Torvus Grove" + desc="Isolated Ledge" # the tall one + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Grove (Center)", + rule=lambda state, player: True # You can always fall down + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Grove (Curved Ledge)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Grove (Upper Door Ledge)", + rule=lambda state, player: True + ) + ] + +class TorvusGrove_UpperDoorLedge(MetroidPrime2Region): + """The fourth high ledge. Contains a dark door that leads to Grove Access.""" + name = "Torvus Grove" + desc="Upper Door Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Grove (Center)", + rule=lambda state, player: True # You can always fall down + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Grove (Isolated Ledge)", + rule=lambda state, player: condition_or([ + state.has("Space Jump Boots", player), + condition_and([ + can_lay_bomb(state, player), + has_trick_enabled(state, player, "Torvus Bog - Torvus Grove | Instant Unmorph to reach Isolated Ledge") + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Grove Access", # curiously, this door is not locked while the pirates are around, + # so you don't need to defeat them to get up here at all + door=DoorCover.Dark, + rule = lambda state, player: True + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/torvus_lagoon.py b/src/logic/metroidprime2/light_world/torvus_bog/torvus_lagoon.py new file mode 100644 index 0000000..9f5657c --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/torvus_lagoon.py @@ -0,0 +1,171 @@ +"""The room containing the entry cutscene for Torvus. This room is largely underwater, and contains many doors. The above-water +doors (that don't lead to Save Station A) are unreachable until a panel is scanned.""" + +from BaseClasses import MultiWorld, ItemClassification +from ... import has_trick_enabled, can_use_screw_attack +from .....Enums import DoorCover +from .....Items import MetroidPrime2Item +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region +from .....Utils import condition_and, condition_or + + +class TorvusLagoon_Beach(MetroidPrime2Region): + """The strip of land leading into the water.""" + name = "Torvus Lagoon" + desc = "Beach" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Temple Transport Access", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Lagoon (Save Room Ledge)", + rule=lambda state, player: condition_or([ + state.has("Space Jump Boots", player), + state.has("Screw Attack", player), + has_trick_enabled(state, player, "Torvus Bog - Torvus Lagoon | NSJ to Save Room Ledge") + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Lagoon (Bridge)", + rule=lambda state, player: condition_and([ + state.has("Torvus Bog - Torvus Lagoon | Gates Lowered", player), + condition_or([ + state.has("Space Jump Boots", player), + state.has("Screw Attack", player), + has_trick_enabled(state, player, "Torvus Bog - Torvus Lagoon | NSJ to Bridge") + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Path of Roots (Underwater)", + door=DoorCover.Dark, + rule=lambda state, player: state.has("Dark Beam", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Lagoon (Underwater Ledge)", + rule=lambda state, player: condition_or([ + state.has('Gravity Boost', player), + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Torvus Lagoon | Air Underwater"), + can_use_screw_attack(state, player) + ]) + ]) + ) + ] + + +class TorvusLagoon_SaveRoomLedge(MetroidPrime2Region): + """The ledge leading to Save Station A. Contains a door with a Missile Cover.""" + name = "Torvus Lagoon" + desc = "Save Room Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Save Station A", + door=DoorCover.Missile, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Lagoon (Beach)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Lagoon - Bridge", + rule=lambda state, player: condition_and([ + state.has("Torvus Bog - Torvus Lagoon | Gates Lowered", player), + condition_or([ + state.has("Space Jump Boots", player), + can_use_screw_attack(state, player, is_nsj=True) + ]) + ]) + ) + ] + + +class TorvusLagoon_UnderwaterLedge(MetroidPrime2Region): + """The pickup ledge underwater. Requires Gravity Boost or tricks to reach.""" + name= "Torvus Lagoon" + desc= "Underwater Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Beach", + rule = lambda state, player: True + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Pickup (Missile Expansion)", + can_access=lambda state, player: True + ) + + +class TorvusLagoon_Bridge(MetroidPrime2Region): + """Doesn't exist until the gates are lowered. This section is the walkway connecting the Portal Chamber and Ruined Alcove entrances.""" + name = "Torvus Lagoon" + desc = "Bridge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Lagoon (Portal Chamber Ledge)", + rule=lambda state, player: state.has("Torvus Bog - Torvus Lagoon | Bridge Lowered", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Lagoon (Ruined Alcove Ledge)", + rule=lambda state, player: state.has("Torvus Bog - Torvus Lagoon | Bridge Lowered", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Lagoon (Beach)", + rule=lambda state, player: True + ) + ] + + +class TorvusLagoon_PortalChamberLedge(MetroidPrime2Region): + """Ledge leading to Portal Chamber. Has the scan panel that opens up the walkway to the Ruined Alcove entrance.""" + name = "Torvus Lagoon" + desc = "Portal Chamber Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Portal Chamber (Torvus Lagoon Side)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Lagoon (Bridge)", + rule=lambda state, player: state.has("Scan Visor", player) + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Torvus Bog - Torvus Lagoon | Gates Lowered", + can_access=lambda state, player: state.has("Scan Visor", player), + locked_item= MetroidPrime2Item( + name="Torvus Bog - Torvus Lagoon | Gates Lowered", + classification=ItemClassification.progression, + code=None, + player=player + ) + ) + + +class TorvusLagoon_RuinedAlcoveLedge(MetroidPrime2Region): + """Has a door leading to Ruined Alcove.""" + name = "Torvus Lagoon" + desc = "Ruined Alcove Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Lagoon (Bridge)", + rule=lambda state, player: state.has("Torvus Bog - Torvus Lagoon | Gates Lowered", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Ruined Alcove", + door=DoorCover.Dark, + rule=lambda state, player: True + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/torvus_map_station.py b/src/logic/metroidprime2/light_world/torvus_bog/torvus_map_station.py new file mode 100644 index 0000000..bc148d4 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/torvus_map_station.py @@ -0,0 +1,13 @@ +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region + + +class TorvusMapStation(MetroidPrime2Region): + name="Torvus Map Station" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Great Bridge (Behind Translator Gate)", + door=DoorCover.Any, + rule=lambda state, player: True + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/torvus_plaza.py b/src/logic/metroidprime2/light_world/torvus_bog/torvus_plaza.py new file mode 100644 index 0000000..f765191 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/torvus_plaza.py @@ -0,0 +1,184 @@ +from BaseClasses import MultiWorld, ItemClassification +from ... import has_trick_enabled, can_lay_bomb, can_use_boost_ball, can_use_spider_ball, can_use_screw_attack +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region +from .....Utils import condition_or, condition_and + +# tricks: +# "Torvus Bog - Torvus Plaza | STE SA to Item", +# "Torvus Bog - Torvus Plaza | Boost-only/Cannonball", +# "Torvus Bog - Torvus Plaza | BSJ to Entrance", + + +class TorvusPlaza_Entrance(MetroidPrime2Region): + """The ledge that contains the door. Overlooks the half-pipe.""" + name = "Torvus Plaza" + desc="Entrance" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Plaza (Half-Pipe)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Plaza (Spider Track)", + rule=lambda state, player: condition_or([ + # normal conditions + condition_and([ + can_use_boost_ball(state, player), + can_use_spider_ball(state, player) + ]), + # using SA + condition_and([ + can_use_screw_attack(state, player), + has_trick_enabled(state, player, "Torvus Bog - Torvus Plaza | STE SA to Item") + ]), + # using Boost + condition_and([ + can_use_boost_ball(state, player), + has_trick_enabled(state, player, "Torvus Bog - Torvus Plaza | Boost-only/Cannonball") + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Plaza Access (Torvus Plaza Entrance)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + ] + + +class TorvusPlaza_HalfPipe(MetroidPrime2Region): + """The half-pipe. Use the boost to get through! Also contains the raised area leading up to a sealed hole where an + arena is in Dark Aether.""" + name = "Torvus Plaza" + desc = "Half-Pipe" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Plaza (Entrance)", + rule=lambda state, player: condition_or([ + can_use_boost_ball(state, player), + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Torvus Plaza | Instant Unmorph BSJ to Entrance"), + can_lay_bomb(state, player) + ]), + can_use_screw_attack(state, player) # you can screw from the back of the room to the middle of the entrance ledge easily + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Plaza (Spider Track)", + rule = lambda state, player: condition_and([ + can_use_boost_ball(state, player), + condition_or([ + can_use_spider_ball(state, player), + has_trick_enabled(state, player, "Torvus Bog - Torvus Plaza | Boost-only/Cannonball") + ]) + ]) + ) + ] + + +class TorvusPlaza_SpiderTrack(MetroidPrime2Region): + """The section of spider track ending before the Sporb. Also includes the area in the back of the room with a standable tree.""" + name = "Torvus Plaza" + desc="Spider Track" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Arena (Half-Pipe)", + rule=lambda state, player: True #you can simply just fall down + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Arena (Spider Challenge)", + rule=lambda state, player: condition_or([ + condition_and([ + can_lay_bomb(state, player), + can_use_spider_ball(state, player) + # boost makes it easier but isn't required for the expected path + ]), + condition_and([ + can_use_screw_attack(state, player), + has_trick_enabled(state, player, "Torvus Bog - Torvus Plaza | STE SA to Item") + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Arena (Item Ledge)", + rule=lambda state, player: condition_and([ + #boost-only requires BSJ + has_trick_enabled(state, player, "Torvus Bog - Torvus Plaza | Boost-only/Cannonball"), + can_lay_bomb(state, player), + state.has("Space Jump Boots", player) + ]) + ) + ] + + +class TorvusPlaza_SpiderChallenge(MetroidPrime2Region): + """The more difficult sections of the track, including the area covered by the Sporb as well as the parts with rotating + and moving elements. Leads all the way to the ledge containing the Kinetic Orb Cannon.""" + name = "Torvus Plaza" + desc="Spider Challenge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Arena (Entrance)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Arena (Half-Pipe)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Arena (Spider Track)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Arena (Cannon Ledge)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Arena (Item Ledge)", + rule=lambda state, player: condition_or([ + can_use_screw_attack(state, player), + can_use_spider_ball(state, player) + ]) + ), + ] + + +class TorvusPlaza_CannonLedge(MetroidPrime2Region): + """Contains a Kinetic Orb Cannon leading to the item ledge.""" + name = "Torvus Plaza" + desc="Cannon Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Arena (Entrance)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Arena (Half-Pipe)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Arena (Item Ledge)", + rule=lambda state, player: True # the cannon is already activated + ), + ] + + +class TorvusPlaza_ItemLedge(MetroidPrime2Region): + """Contains a pickup. Is the highest point in the room.""" + name = "Torvus Plaza" + desc="Item Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Arena (Half-Pipe)", + rule=lambda state, player: True + ), + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Pickup (Energy Tank)", + can_access=lambda state, player: True + ) diff --git a/src/logic/metroidprime2/light_world/torvus_bog/torvus_temple.py b/src/logic/metroidprime2/light_world/torvus_bog/torvus_temple.py new file mode 100644 index 0000000..024c304 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/torvus_temple.py @@ -0,0 +1,155 @@ +"""Best known for having a protracted Pirate battle. This is where Samus obtains Super Missile in vanilla. Has an elevator +that connects its above-ground and underground sections that requires the Emerald Translator.""" + +from BaseClasses import MultiWorld, ItemClassification +from ... import can_use_screw_attack, can_use_seeker_launcher, has_missile_count, has_trick_enabled +from .....Enums import DoorCover +from .....Items import MetroidPrime2Item +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region +from .....Utils import condition_or, condition_and + + +class TorvusTemple_Arena(MetroidPrime2Region): + """Where the Pirate battle takes place. The elevators become accessible after the pirates have been killed.""" + name = "Torvus Temple" + desc="Arena" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bug - Plaza Access (Upper)", + door=DoorCover.Dark, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Temple (Underground)", + door=DoorCover.EmeraldTranslator, + rule=lambda state, player: state.has("Torvus Bog - Torvus Temple | Item Collected", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Temple (Upper)", + rule=lambda state, player: state.has("Torvus Bog - Torvus Temple | Pirates Dead", player) + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Pirates Dead", + locked_item=MetroidPrime2Item( + name="Torvus Bog - Torvus Temple | Pirates Dead", + classification=ItemClassification.progression, + code=None, + player=player, + ), + can_access=lambda state, player: True + ) + self.add_location( + name="Item Collected", + locked_item=MetroidPrime2Item( + name="Torvus Bog - Torvus Temple | Item Collected", + classification=ItemClassification.progression, + code=None, + player=player, + ), + can_access=lambda state, player: True + ) + self.add_location( + name="Pickup (Super Missile)", + can_access=lambda state, player: True + ) + + +class TorvusTemple_Underground(MetroidPrime2Region): + """The area that the central elevator brings Samus to. Contains paths back into upper Torvus, a Seeker path to Agon, + and an elevator path to lower Torvus.""" + name = "Torvus Temple" + desc="Underground" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Temple Access (Lower Torvus Temple Entrance)", + door=DoorCover.SuperMissile, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Transport to Agon Wastes", + door=DoorCover.Seeker, + rule=lambda state, player: condition_or([ + can_use_seeker_launcher(state, player), + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Torvus Temple | Seeker Skip"), + has_missile_count(state, player, 2), + can_use_screw_attack(state, player) + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Underground Tunnel (Tunnel)", + door=DoorCover.SuperMissile, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Temple (Underground Transport Entrance)", + rule=lambda state, player: state.has("Morph Ball", player) + ), + # MetroidPrime2Exit( + # destination="Torvus Bog - Torvus Temple (Out of Bounds)", + # rule=lambda state, player: condition_and([ + # has_oob_kit(state, player), + # has_trick_enabled(state, player, "Torvus Bog - Torvus Temple | Out of Bounds") + # ]) + # ) + ] + + +class TorvusTemple_UndergroundTransportEntrance(MetroidPrime2Region): + """Leads to/from lower Torvus. The barrier that covers the elevator to the uppermost ledge extends down to cover the door here too.""" + name = "Torvus Temple" + desc="Underground Transport Entrance" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Temple (Underground)", + rule=lambda state, player: state.has("Morph Ball", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Underground Transport (Upper)", + door=DoorCover.SuperMissile, + rule=lambda state, player: state.has("Torvus Bog - Torvus Temple | Pirates Dead", player) + # the laser barrier preventing access to the Super Missiles also blocks the lower tunnel + ) + ] + + +class TorvusTemple_Upper(MetroidPrime2Region): + """The uppermost ledge, reached via the blocked elevator. Has a door that leads to Controller Access.""" + name = "Torvus Temple" + desc="Upper" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Temple (Arena)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Controller Access", + door=DoorCover.SuperMissile, + rule=lambda state, player: True + ) + ] + + +# class TorvusTemple_OutOfBounds(MetroidPrime2Region): +# name = "Torvus Temple" +# desc = "Out of Bounds" +# exits_ = [ +# MetroidPrime2Exit( +# destination="Torvus Bog - Torvus Temple (Arena)", +# rule=lambda state, player: True +# ), +# MetroidPrime2Exit( +# destination="Torvus Bog - Transport to Agon Wastes", +# rule=lambda state, player: True +# ), +# MetroidPrime2Exit( +# destination="Torvus Bog - Torvus Temple (Underground)", +# rule=lambda state, player: True +# ), +# ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/training_access.py b/src/logic/metroidprime2/light_world/torvus_bog/training_access.py new file mode 100644 index 0000000..2ed867c --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/training_access.py @@ -0,0 +1,18 @@ +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region + + +class TrainingAccess(MetroidPrime2Region): + name="Training Access" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (North Door Ledge)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (South Door Ledge)", + door=DoorCover.Any, + rule=lambda state, player: True + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/training_chamber.py b/src/logic/metroidprime2/light_world/torvus_bog/training_chamber.py new file mode 100644 index 0000000..3aa6683 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/training_chamber.py @@ -0,0 +1,272 @@ + + +from BaseClasses import MultiWorld, ItemClassification +from ... import ( + can_lay_bomb, + can_use_boost_ball, + can_use_darkburst, + can_use_screw_attack, + can_use_sonic_boom, + can_use_spider_ball, + has_trick_enabled +) +from .....Enums import DoorCover +from .....Items import MetroidPrime2Item +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region +from .....Utils import condition_or, condition_and + + +class TrainingChamber_WestCagedArea(MetroidPrime2Region): + name="Training Chamber" + desc="West Caged Area" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Transit Tunnel West (North Side)", + door=DoorCover.Light, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (Center)", + rule=lambda state, player: state.has('Gravity Boost', player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (Statue Platform)", + rule = lambda state, player: condition_and([ # you can stand on the open section of gate and triple jump to the statue platform + state.has('Gravity Boost', player), + can_use_screw_attack(state, player) + ]) + ) + ] + + +class TrainingChamber_EastCagedArea(MetroidPrime2Region): + name="Training Chamber" + desc="East Caged Area" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Transit Tunnel East (North Side)", + door=DoorCover.Dark, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (Center)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (Statue Platform)", + rule = lambda state, player: condition_and([ # you can stand on the open section of gate and triple jump to the statue platform + state.has('Gravity Boost', player), + can_use_screw_attack(state, player) + ]) + ) + ] + + +class TrainingChamber_Center(MetroidPrime2Region): + # - East and West Caged Areas can only connect to the center via Gravity Boost + # - The Center can connect outward using the spinner mechanism, but only after the Bloggs are dead + name="Training Chamber" + desc="Center" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (West Gated Area)", + rule=lambda state, player: condition_and([ + state.has('Gravity Boost', player), + state.has('Torvus Bog - Training Chamber | Bloggs Dead') + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (East Gated Area)", + rule=lambda state, player: condition_and([ + state.has('Gravity Boost', player), + state.has('Torvus Bog - Training Chamber | Bloggs Dead', player) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (Ledge Below South Door)", + rule=lambda state, player: condition_or([ + state.has('Gravity Boost', player), + state.has('Morph Ball', player) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (Behind Statue)", + rule=lambda state, player: state.has("Torvus Bog - Training Chamber | Statue Moved", player) + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Bloggs Dead", + locked_item=MetroidPrime2Item( + name="Torvus Bog - Training Chamber | Bloggs Dead", + classification=ItemClassification.progression, + code=None, + player=player, + ), + can_access=lambda state, player: True + ) + + +class TrainingChamber_StatuePlatform(MetroidPrime2Region): + name="Training Chamber" + desc="Statue Platform" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (Center)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (East Gated Area)", + rule=lambda state, player: state.has_any(['Space Jump', 'Gravity Boost'], player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (West Gated Area)", + rule=lambda state, player: state.has_any(['Space Jump', 'Gravity Boost'], player) + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Statue Moved", + locked_item=MetroidPrime2Item( + name="Torvus Bog - Training Chamber | Statue Moved", + classification=ItemClassification.progression, + code=None, + player=player, + ), + can_access=lambda state, player: condition_or([ + can_lay_bomb(state, player), + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Training Chamber | Activate Bomb Slot without Bombs"), + condition_or([ + can_use_darkburst(state, player), + can_use_sonic_boom(state, player), + ]), + state.has("Morph Ball", player) + ]) + ]) + ) + + +class TrainingChamber_SouthDoorLedge(MetroidPrime2Region): + name="Training Chamber" + desc="South Door Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (Ledge Below South Door)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Access", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Access (Statue Platform)", + rule=lambda state, player: condition_and([ + state.has_all(['Space Jump Boots', 'Scan Visor'], player), + has_trick_enabled(state, player, "Torvus Bog - Training Chamber | Extended Dash to Bomb Slot") + ]) + ) + ] + + +class TrainingChamber_LedgeBelowSouthDoor(MetroidPrime2Region): + name="Training Chamber" + desc="Ledge Below South Door" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (Center)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (Statue Platform)", + rule=lambda state, player: condition_or([ + can_use_screw_attack(state, player), + condition_and([ + + ]) + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (South Door Ledge)", + rule=lambda state, player: condition_or([ + can_lay_bomb(state, player), + state.has('Space Jump Boots', player), + state.has('Gravity Boost', player) + ]) + ) + ] + + +class TrainingChamber_BehindStatue(MetroidPrime2Region): + name="Training Chamber" + desc="Behind Statue" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (North Door Ledge)", + rule=lambda state, player: can_use_spider_ball(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (Center)", + rule=lambda state, player: state.has('Torvus Bog - Training Chamber | Statue Moved') + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Pickup (Missile Expansion)", + can_access=lambda state, player: state.has("Torvus Bog - Training Chamber | Statue Moved") + ) + + +class TrainingChamber_NorthDoorLedge(MetroidPrime2Region): + name="Training Chamber" + desc="North Door Ledge" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Fortress Transport Access (South Ledge)", + door=DoorCover.PowerBomb, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (Behind Statue)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (Center)", + rule=lambda state, player: condition_or([ + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Training Chamber | Bypass Statue with SJ"), + state.has('Space Jump Boots', player) + ]), + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Training Chamber | Bypass Statue with Boost"), + can_use_boost_ball(state, player), + can_use_spider_ball(state, player) + ]) + ]) + ) + ] + + +class TrainingChamber_SpiderTracks(MetroidPrime2Region): + name="Training Chamber" + desc="Spider Tracks" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (Statue Platform)", + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (Center)", + rule=lambda state, player: True + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/transit_tunnel_east.py b/src/logic/metroidprime2/light_world/torvus_bog/transit_tunnel_east.py new file mode 100644 index 0000000..5fd50f2 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/transit_tunnel_east.py @@ -0,0 +1,53 @@ +"""Morph Ball puzzle room connecting Training Chamber and Catacombs. +Air jets in this room can lift the Morph Ball if the player has neither Bombs nor Gravity Boost.""" + +from BaseClasses import MultiWorld, ItemClassification +from ... import can_lay_bomb +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region +from .....Utils import condition_and + + +class TransitTunnelEast_CatacombsSide(MetroidPrime2Region): + name="Transit Tunnel East" + desc="Catacombs Side" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs (Transit Tunnel East Entrance)", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Transit Tunnel East (Training Chamber Side)", + rule=lambda state, player: can_lay_bomb(state, player) + #todo: replace with can_ball_jump once that's a thing + ) + ] + +class TransitTunnelEast_TrainingChamberSide(MetroidPrime2Region): + """Has the Bomb Slot as well as the pickup.""" + name="Transit Tunnel East" + desc="Training Chamber Side" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (East Caged Area)", + door=DoorCover.Dark, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Transit Tunnel East (Catacombs Side)", + rule=lambda state, player: can_lay_bomb(state, player) + #todo: replace with can_ball_jump once that's a thing + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Pickup (Energy Tank)", + can_access=lambda state, player: condition_and([ + can_lay_bomb(state, player), + state.has('Gravity Boost', player) + ]) + ) diff --git a/src/logic/metroidprime2/light_world/torvus_bog/transit_tunnel_south.py b/src/logic/metroidprime2/light_world/torvus_bog/transit_tunnel_south.py new file mode 100644 index 0000000..ee7e5ed --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/transit_tunnel_south.py @@ -0,0 +1,65 @@ +"""Morph Ball puzzle room connecting Gathering Hall and Catacombs.""" + +from BaseClasses import MultiWorld, ItemClassification +from ... import can_lay_bomb, transit_tunnel_south_can_progress_room +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region +from .....Utils import condition_and + + +class TransitTunnelSouth_CatacombsSide(MetroidPrime2Region): + name = "Transit Tunnel South" + desc="Catacombs Side" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Catacombs (Transit Tunnel South Entrance)", + door=DoorCover.Annihilator, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Transit Tunnel South (Morph Ball Puzzle)", + rule=lambda state, player: transit_tunnel_south_can_progress_room(state, player) + ) + ] + + +class TransitTunnelSouth_GatheringHallSide(MetroidPrime2Region): + name = "Transit Tunnel South" + desc="Gathering Hall Side" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (South Door Ledge)", + door=DoorCover.Annihilator, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="Torvus Bog - Transit Tunnel South (Morph Ball Puzzle)", + rule=lambda state, player: transit_tunnel_south_can_progress_room(state, player) + ) + ] + + +class TransitTunnelSouth_MorphBallPuzzle(MetroidPrime2Region): + name = "Transit Tunnel South" + desc = "Morph Ball Puzzle" + exits_= [ + MetroidPrime2Exit( + destination="Torvus Bog - Transit Tunnel South (Catacombs Side)", + rule=lambda state, player: transit_tunnel_south_can_progress_room(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Transit Tunnel South (Gathering Hall Side)", + rule=lambda state, player: transit_tunnel_south_can_progress_room(state, player) + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Pickup (Missile Expansion)", + can_access=lambda state, player: condition_and([ + can_lay_bomb(state, player), + state.has('Gravity Boost', player) + ]) + ) diff --git a/src/logic/metroidprime2/light_world/torvus_bog/transit_tunnel_west.py b/src/logic/metroidprime2/light_world/torvus_bog/transit_tunnel_west.py new file mode 100644 index 0000000..299a752 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/transit_tunnel_west.py @@ -0,0 +1,38 @@ +"""Morph Ball puzzle room connecting Training Chamber and Gathering Hall. +Air jets in this room can lift the Morph Ball if the player does not have Gravity Boost.""" + +from ... import can_lay_bomb +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region + + +class TransitTunnelWest_SouthSide(MetroidPrime2Region): + name="Transit Tunnel West" + desc="South Side" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Transit Tunnel West (North Side)", + rule=lambda state, player: can_lay_bomb(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Gathering Hall (North Door Ledge)", + door=DoorCover.Any, + rule=lambda state, player: True + ) + ] + + +class TransitTunnelWest_NorthSide(MetroidPrime2Region): + name="Transit Tunnel West" + desc="North Side" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Transit Tunnel West (South Side)", + rule=lambda state, player: can_lay_bomb(state, player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Training Chamber (West Caged Area)", + door=DoorCover.Light, + rule=lambda state, player: True + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/transport_to_agon_wastes.py b/src/logic/metroidprime2/light_world/torvus_bog/transport_to_agon_wastes.py new file mode 100644 index 0000000..efaae32 --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/transport_to_agon_wastes.py @@ -0,0 +1,26 @@ +from ... import has_missile_count, has_trick_enabled, can_use_seeker_launcher, can_use_screw_attack +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region +from .....Utils import condition_or, condition_and + + +class TransportToAgonWastes(MetroidPrime2Region): + name="Transport to Agon Wastes" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Temple (Underground)", + door=DoorCover.Seeker, + rule=lambda state, player: condition_or([ + can_use_seeker_launcher(state, player), + condition_and([ + has_missile_count(state, player, 5), + can_use_screw_attack(state, player), + has_trick_enabled(state, player, "Torvus Bog - Torvus Temple | Seeker Skip") + ]) + ]) + ), + MetroidPrime2Exit( + destination="E|Agon Wastes - Transport to Torvus Bog", + rule=lambda state, player: state.has("Scan Visor", player) + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/transport_to_sanctuary_fortress.py b/src/logic/metroidprime2/light_world/torvus_bog/transport_to_sanctuary_fortress.py new file mode 100644 index 0000000..4a44d2d --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/transport_to_sanctuary_fortress.py @@ -0,0 +1,17 @@ +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region + + +class TransportToSanctuaryFortress(MetroidPrime2Region): + name="Transport to Sanctuary Fortress" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Fortress Transport Access (Upper Ledge)", + door = DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="E|Sanctuary Fortress - Transport to Torvus Bog", + rule=lambda state, player: state.has('Scan Visor', player) + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/transport_to_temple_grounds.py b/src/logic/metroidprime2/light_world/torvus_bog/transport_to_temple_grounds.py new file mode 100644 index 0000000..8604ddf --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/transport_to_temple_grounds.py @@ -0,0 +1,17 @@ +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region + + +class TransportToTempleGrounds(MetroidPrime2Region): + name = "Transport to Temple Grounds" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Temple Transport Access", + door=DoorCover.Any, + rule=lambda state, player: True + ), + MetroidPrime2Exit( + destination="E|Temple Grounds - Transport to Torvus Bog", + rule=lambda state, player: state.has("Scan Visor", player) + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/underground_transport.py b/src/logic/metroidprime2/light_world/torvus_bog/underground_transport.py new file mode 100644 index 0000000..f0be58c --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/underground_transport.py @@ -0,0 +1,39 @@ +"""An elevator room. Unlike most elevators, this room does not have an elevator cutscene. Connects upper and lower Torvus.""" + +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region + + +class UndergroundTransport_Upper(MetroidPrime2Region): + """Connects with Torvus Temple.""" + name="Underground Transport" + desc="Upper" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Underground Transport (Lower)", + rule=lambda state, player: state.has("Scan Visor", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Temple (Underground)", + door=DoorCover.Any, + rule=lambda state, player: state.has("Torvus Bog - Torvus Temple (Pirates Dead)", player) + # you MUST have this, or you get stuck in the doorway after the door is opened + ) + ] + + +class UndergroundTransport_Lower(MetroidPrime2Region): + """Connects with Hydrodynamo Station.""" + name="Underground Transport" + desc="Lower" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Underground Transport (Upper)", + rule=lambda state, player: state.has("Scan Visor", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Hydrodynamo Station (Above Water)", + door=DoorCover.Any, + rule=lambda state, player: True + ) + ] diff --git a/src/logic/metroidprime2/light_world/torvus_bog/underground_tunnel.py b/src/logic/metroidprime2/light_world/torvus_bog/underground_tunnel.py new file mode 100644 index 0000000..674b69d --- /dev/null +++ b/src/logic/metroidprime2/light_world/torvus_bog/underground_tunnel.py @@ -0,0 +1,68 @@ +""" +Connects Torvus Temple and Torvus Grove. There is an item in the floor on the Torvus Temple side. +The room is notable for having a short morph ball tunnel "hidden" by a small waterfall. +Has: +- Grenchler (1st pass) / Seedburster swarm (later passes) enemies +- 2 Sporb enemies +- An Emerald Lore Projector. Entry: Our War Begins (GC) / The Ing Attack (Wii) +- A pickup (Missile Expansion) +""" + +from BaseClasses import MultiWorld, ItemClassification +from src.Utils import condition_or, condition_and +from ... import can_lay_bomb, can_use_boost_ball, has_trick_enabled +from .....Enums import DoorCover +from .....Regions import MetroidPrime2Exit, MetroidPrime2Region + + +class UndergroundTunnel_Tunnel(MetroidPrime2Region): + """Be sure to check under the grating! The wooden tunnel makes for a striking environment.""" + name="Underground Tunnel" + desc="Tunnel" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Underground Tunnel (After Falls)", + rule=lambda state, player: state.has("Morph Ball", player) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Temple (Underground)", + door=DoorCover.SuperMissile, + rule=lambda state, player: True + ) + ] + + def __init__(self, region_name: str, player: int, multiworld: MultiWorld): + super().__init__(region_name, player, multiworld) + + self.add_location( + name="Pickup (Missile Expansion)", + can_access=lambda state, player: state.has("Morph Ball", player) + ) + + +class UndergroundTunnel_AfterFalls(MetroidPrime2Region): + """Two Sporbs guard the tunnel here.""" + name="Underground Tunnel" + desc="After Falls" + exits_ = [ + MetroidPrime2Exit( + destination="Torvus Bog - Underground Tunnel (Tunnel)", + rule=lambda state, player: condition_or([ + # TODO: change to can_ball_jump later + can_lay_bomb(state, player), + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Underground Tunnel | Instant Morph to enter Tunnel"), + state.has("Morph Ball", player) + ]), + condition_and([ + has_trick_enabled(state, player, "Torvus Bog - Underground Tunnel | Wall Boost to enter Tunnel"), + can_use_boost_ball(state, player) + ]), + ]) + ), + MetroidPrime2Exit( + destination="Torvus Bog - Torvus Grove (Center)", + door=DoorCover.Light, + rule=lambda state, player: True + ) + ]