Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
6689dde
add the following rooms:
hopesgit Aug 13, 2025
ae85011
Add Torvus Plaza and tricks used there
hopesgit Aug 13, 2025
a7631a9
Add plaza access oob rule and trick in options
hopesgit Aug 13, 2025
b4085c7
Add Forgotten Bridge tricks logic
hopesgit Aug 14, 2025
d043bfb
Added logic for:
hopesgit Aug 14, 2025
65788c0
Added logic for:
hopesgit Aug 15, 2025
169174f
New rooms:
hopesgit Aug 18, 2025
818b79c
New rooms:
hopesgit Aug 18, 2025
5d4c1ba
Added Torvus rooms to torvus_bog_rooms list in one of the __init__.py…
hopesgit Dec 19, 2025
339604f
Add Catacombs room
hopesgit Dec 19, 2025
0463d45
Add Transit Tunnel East and Transit Tunnel South
hopesgit Dec 19, 2025
c97fd1a
add alpha blogg damage logic
hopesgit Dec 20, 2025
7874bc1
Merge branch 'main' of github.com:/UltiNaruto/MetroidEchoesAP
hopesgit Feb 19, 2026
dd80259
Tightened up imports on Torvus rooms. Also moved the imports to the n…
hopesgit Feb 19, 2026
69d30e2
Removed base classes for some rooms, optimized some imports, removed …
hopesgit Feb 21, 2026
2776dfa
Moved accessory functions to init; added another trick to Options.py
hopesgit Feb 21, 2026
08486a5
add seeker skip trick to hydrodynamo station
hopesgit Feb 21, 2026
2f21544
Remove TorvusBog_ from Torvus class names
hopesgit Feb 21, 2026
feb5226
Match various base class files with source
hopesgit Feb 21, 2026
76b44d4
add typing to helper methods in one of the inits; remove main_hydroch…
hopesgit Feb 22, 2026
633eadc
update location setup in most torvus rooms to use self.add_location i…
hopesgit Feb 22, 2026
a80a633
remove trailing line in region file
hopesgit Feb 22, 2026
9a8259b
further attempt to undo committed changes in unrelated files
hopesgit Feb 22, 2026
1d1a7ae
more fixing
hopesgit Feb 22, 2026
3a9e91e
Revert problem files to an earlier version
hopesgit Feb 22, 2026
d86cbdc
Make requested updates to Abandoned Worksite
hopesgit Feb 23, 2026
faf89ad
Rename subregions in fortress transport access
hopesgit Feb 23, 2026
821b8a5
Making some of the quicker requested changes
hopesgit Mar 9, 2026
4b4c477
Fixing all but the Catacombs rooms
hopesgit Mar 13, 2026
4d207b6
Add some catacombs fixes, also a new trick related to catacombs
hopesgit Mar 13, 2026
01aa23e
- Add newlines at end of every file
hopesgit Mar 14, 2026
2a5bf03
- Removed trick in catacombs access
hopesgit Apr 3, 2026
534bab0
Add documentation for abandoned worksite and forgotten bridge. Also r…
hopesgit Apr 28, 2026
cfc4267
Add a couple missing player references in forgotten bridge
hopesgit Apr 28, 2026
1ce0e9b
Add Subregion and room documentation, as well as add missing player r…
hopesgit May 3, 2026
407eead
Add Subregion and room documentation, as well as add missing player r…
hopesgit May 6, 2026
fb900bb
Make some updates requested by UN
hopesgit May 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions src/Options.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]


Expand Down
139 changes: 139 additions & 0 deletions src/logic/metroidprime2/__init__.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
150 changes: 150 additions & 0 deletions src/logic/metroidprime2/light_world/torvus_bog/abandoned_worksite.py
Original file line number Diff line number Diff line change
@@ -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),
Comment thread
hopesgit marked this conversation as resolved.
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
Comment thread
hopesgit marked this conversation as resolved.
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
)
Loading