mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
204 lines
12 KiB
Python
204 lines
12 KiB
Python
|
|
from BaseClasses import CollectionState
|
||
|
|
from .region_base import JakAndDaxterRegion
|
||
|
|
from ..options import EnableOrbsanity
|
||
|
|
from typing import TYPE_CHECKING
|
||
|
|
if TYPE_CHECKING:
|
||
|
|
from .. import JakAndDaxterWorld
|
||
|
|
from ..rules import can_free_scout_flies, can_fight, can_reach_orbs_level
|
||
|
|
|
||
|
|
|
||
|
|
# God help me... here we go.
|
||
|
|
def build_regions(level_name: str, world: "JakAndDaxterWorld") -> JakAndDaxterRegion:
|
||
|
|
multiworld = world.multiworld
|
||
|
|
options = world.options
|
||
|
|
player = world.player
|
||
|
|
|
||
|
|
# We need a few helper functions.
|
||
|
|
def can_cross_long_gap(state: CollectionState, p: int) -> bool:
|
||
|
|
return (state.has_all(("Roll", "Roll Jump"), p)
|
||
|
|
or state.has_all(("Double Jump", "Jump Kick"), p))
|
||
|
|
|
||
|
|
def can_jump_blockers(state: CollectionState, p: int) -> bool:
|
||
|
|
return (state.has_any(("Double Jump", "Jump Kick"), p)
|
||
|
|
or state.has_all(("Crouch", "Crouch Jump"), p)
|
||
|
|
or state.has_all(("Punch", "Punch Uppercut"), p))
|
||
|
|
|
||
|
|
main_area = JakAndDaxterRegion("Main Area", player, multiworld, level_name, 0)
|
||
|
|
main_area.add_fly_locations([65], access_rule=lambda state: can_free_scout_flies(state, player))
|
||
|
|
|
||
|
|
# We need a few virtual regions like we had for Dark Crystals in Spider Cave.
|
||
|
|
# First, a virtual region for the glacier lurkers.
|
||
|
|
glacier_lurkers = JakAndDaxterRegion("Glacier Lurkers", player, multiworld, level_name, 0)
|
||
|
|
|
||
|
|
# Need to fight all the troops.
|
||
|
|
# Troop in snowball_canyon: cross main_area.
|
||
|
|
# Troop in ice_skating_rink: cross main_area and fort_exterior.
|
||
|
|
# Troop in fort_exterior: cross main_area and fort_exterior.
|
||
|
|
glacier_lurkers.add_cell_locations([61], access_rule=lambda state:
|
||
|
|
can_fight(state, player)
|
||
|
|
and can_cross_long_gap(state, player))
|
||
|
|
|
||
|
|
# Second, a virtual region for the precursor blockers. Unlike the others, this contains orbs:
|
||
|
|
# the total number of orbs that sit on top of the blockers. Yes, there are only 8.
|
||
|
|
blockers = JakAndDaxterRegion("Precursor Blockers", player, multiworld, level_name, 8)
|
||
|
|
|
||
|
|
# 1 in main_area
|
||
|
|
# 2 in snowball_canyon
|
||
|
|
# 4 in ice_skating_rink
|
||
|
|
# 3 in fort_exterior
|
||
|
|
# 3 in bunny_cave_start
|
||
|
|
blockers.add_cell_locations([66], access_rule=lambda state:
|
||
|
|
can_fight(state, player)
|
||
|
|
and can_cross_long_gap(state, player))
|
||
|
|
|
||
|
|
snowball_canyon = JakAndDaxterRegion("Snowball Canyon", player, multiworld, level_name, 28)
|
||
|
|
|
||
|
|
# The scout fly box *can* be broken without YES, so leave it in this region.
|
||
|
|
frozen_box_cave = JakAndDaxterRegion("Frozen Box Cave", player, multiworld, level_name, 12)
|
||
|
|
frozen_box_cave.add_fly_locations([327745], access_rule=lambda state:
|
||
|
|
state.has("Yellow Eco Switch", player)
|
||
|
|
or can_free_scout_flies(state, player))
|
||
|
|
|
||
|
|
# This region has crates that can *only* be broken with YES.
|
||
|
|
frozen_box_cave_crates = JakAndDaxterRegion("Frozen Box Cave Orb Crates", player, multiworld, level_name, 8)
|
||
|
|
frozen_box_cave_crates.add_cell_locations([67], access_rule=lambda state:
|
||
|
|
state.has("Yellow Eco Switch", player))
|
||
|
|
|
||
|
|
# Include 6 orbs on the twin elevator ice ramp.
|
||
|
|
ice_skating_rink = JakAndDaxterRegion("Ice Skating Rink", player, multiworld, level_name, 20)
|
||
|
|
ice_skating_rink.add_fly_locations([131137], access_rule=lambda state: can_free_scout_flies(state, player))
|
||
|
|
|
||
|
|
flut_flut_course = JakAndDaxterRegion("Flut Flut Course", player, multiworld, level_name, 15)
|
||
|
|
flut_flut_course.add_cell_locations([63], access_rule=lambda state: state.has("Flut Flut", player))
|
||
|
|
flut_flut_course.add_special_locations([63], access_rule=lambda state: state.has("Flut Flut", player))
|
||
|
|
|
||
|
|
# Includes the bridge from snowball_canyon, the area beneath that bridge, and the areas around the fort.
|
||
|
|
fort_exterior = JakAndDaxterRegion("Fort Exterior", player, multiworld, level_name, 20)
|
||
|
|
fort_exterior.add_fly_locations([65601, 393281], access_rule=lambda state:
|
||
|
|
can_free_scout_flies(state, player))
|
||
|
|
|
||
|
|
# Includes the icy island and bridge outside the cave entrance.
|
||
|
|
bunny_cave_start = JakAndDaxterRegion("Bunny Cave (Start)", player, multiworld, level_name, 10)
|
||
|
|
|
||
|
|
# Includes the cell and 3 orbs at the exit.
|
||
|
|
bunny_cave_end = JakAndDaxterRegion("Bunny Cave (End)", player, multiworld, level_name, 3)
|
||
|
|
bunny_cave_end.add_cell_locations([64])
|
||
|
|
|
||
|
|
switch_cave = JakAndDaxterRegion("Yellow Eco Switch Cave", player, multiworld, level_name, 4)
|
||
|
|
switch_cave.add_cell_locations([60])
|
||
|
|
switch_cave.add_special_locations([60])
|
||
|
|
|
||
|
|
# Only what can be covered by single jump.
|
||
|
|
fort_interior = JakAndDaxterRegion("Fort Interior (Main)", player, multiworld, level_name, 19)
|
||
|
|
|
||
|
|
# Reaching the top of the watch tower, getting the fly with the blue eco, and falling down to get the caches.
|
||
|
|
fort_interior_caches = JakAndDaxterRegion("Fort Interior (Caches)", player, multiworld, level_name, 51)
|
||
|
|
fort_interior_caches.add_fly_locations([196673])
|
||
|
|
fort_interior_caches.add_cache_locations([23348, 23349, 23350])
|
||
|
|
|
||
|
|
# Need higher jump.
|
||
|
|
fort_interior_base = JakAndDaxterRegion("Fort Interior (Base)", player, multiworld, level_name, 0)
|
||
|
|
fort_interior_base.add_fly_locations([262209], access_rule=lambda state:
|
||
|
|
can_free_scout_flies(state, player))
|
||
|
|
|
||
|
|
# Need farther jump.
|
||
|
|
fort_interior_course_end = JakAndDaxterRegion("Fort Interior (Course End)", player, multiworld, level_name, 2)
|
||
|
|
fort_interior_course_end.add_cell_locations([62])
|
||
|
|
|
||
|
|
# Wire up the virtual regions first.
|
||
|
|
main_area.connect(blockers, rule=lambda state: can_jump_blockers(state, player))
|
||
|
|
main_area.connect(glacier_lurkers, rule=lambda state: can_fight(state, player))
|
||
|
|
|
||
|
|
# Yes, the only way into the rest of the level requires advanced movement.
|
||
|
|
main_area.connect(snowball_canyon, rule=lambda state: can_cross_long_gap(state, player))
|
||
|
|
|
||
|
|
snowball_canyon.connect(main_area) # But you can just jump down and run up the ramp.
|
||
|
|
snowball_canyon.connect(bunny_cave_start) # Jump down from the glacier troop cliff.
|
||
|
|
snowball_canyon.connect(fort_exterior) # Jump down, to the left of frozen box cave.
|
||
|
|
snowball_canyon.connect(frozen_box_cave, rule=lambda state: # More advanced movement.
|
||
|
|
can_cross_long_gap(state, player))
|
||
|
|
|
||
|
|
frozen_box_cave.connect(snowball_canyon, rule=lambda state: # Same movement to go back.
|
||
|
|
can_cross_long_gap(state, player))
|
||
|
|
frozen_box_cave.connect(frozen_box_cave_crates, rule=lambda state: # YES to get these crates.
|
||
|
|
state.has("Yellow Eco Switch", player))
|
||
|
|
frozen_box_cave.connect(ice_skating_rink, rule=lambda state: # Same movement to go forward.
|
||
|
|
can_cross_long_gap(state, player))
|
||
|
|
|
||
|
|
frozen_box_cave_crates.connect(frozen_box_cave) # Semi-virtual region, no moves req'd.
|
||
|
|
|
||
|
|
ice_skating_rink.connect(frozen_box_cave, rule=lambda state: # Same movement to go back.
|
||
|
|
can_cross_long_gap(state, player))
|
||
|
|
ice_skating_rink.connect(flut_flut_course, rule=lambda state: # Duh.
|
||
|
|
state.has("Flut Flut", player))
|
||
|
|
ice_skating_rink.connect(fort_exterior) # Just slide down the elevator ramp.
|
||
|
|
|
||
|
|
fort_exterior.connect(ice_skating_rink, rule=lambda state: # Twin elevators OR scout fly ledge.
|
||
|
|
can_cross_long_gap(state, player)) # Both doable with main_gap logic.
|
||
|
|
fort_exterior.connect(snowball_canyon) # Run across bridge.
|
||
|
|
fort_exterior.connect(fort_interior, rule=lambda state: # Duh.
|
||
|
|
state.has("Snowy Fort Gate", player))
|
||
|
|
fort_exterior.connect(bunny_cave_start) # Run across bridge.
|
||
|
|
fort_exterior.connect(switch_cave, rule=lambda state: # Yes, blocker jumps work here.
|
||
|
|
can_jump_blockers(state, player))
|
||
|
|
|
||
|
|
fort_interior.connect(fort_interior_caches, rule=lambda state: # Just need a little height.
|
||
|
|
state.has("Double Jump", player)
|
||
|
|
or state.has_all(("Crouch", "Crouch Jump"), player))
|
||
|
|
fort_interior.connect(fort_interior_base, rule=lambda state: # Just need a little height.
|
||
|
|
state.has("Double Jump", player)
|
||
|
|
or state.has_all(("Crouch", "Crouch Jump"), player))
|
||
|
|
fort_interior.connect(fort_interior_course_end, rule=lambda state: # Just need a little distance.
|
||
|
|
state.has_any(("Double Jump", "Jump Kick"), player)
|
||
|
|
or state.has_all(("Punch", "Punch Uppercut"), player))
|
||
|
|
|
||
|
|
flut_flut_course.connect(fort_exterior) # Ride the elevator.
|
||
|
|
|
||
|
|
# Must fight way through cave, but there is also a grab-less ledge we must jump over.
|
||
|
|
bunny_cave_start.connect(bunny_cave_end, rule=lambda state:
|
||
|
|
can_fight(state, player)
|
||
|
|
and (state.has("Double Jump", player)
|
||
|
|
or state.has_all(("Crouch", "Crouch Jump"), player)))
|
||
|
|
|
||
|
|
# All jump down.
|
||
|
|
fort_interior_caches.connect(fort_interior)
|
||
|
|
fort_interior_base.connect(fort_interior)
|
||
|
|
fort_interior_course_end.connect(fort_interior)
|
||
|
|
switch_cave.connect(fort_exterior)
|
||
|
|
bunny_cave_end.connect(fort_exterior)
|
||
|
|
|
||
|
|
# I really hope that is everything.
|
||
|
|
world.level_to_regions[level_name].append(main_area)
|
||
|
|
world.level_to_regions[level_name].append(glacier_lurkers)
|
||
|
|
world.level_to_regions[level_name].append(blockers)
|
||
|
|
world.level_to_regions[level_name].append(snowball_canyon)
|
||
|
|
world.level_to_regions[level_name].append(frozen_box_cave)
|
||
|
|
world.level_to_regions[level_name].append(frozen_box_cave_crates)
|
||
|
|
world.level_to_regions[level_name].append(ice_skating_rink)
|
||
|
|
world.level_to_regions[level_name].append(flut_flut_course)
|
||
|
|
world.level_to_regions[level_name].append(fort_exterior)
|
||
|
|
world.level_to_regions[level_name].append(bunny_cave_start)
|
||
|
|
world.level_to_regions[level_name].append(bunny_cave_end)
|
||
|
|
world.level_to_regions[level_name].append(switch_cave)
|
||
|
|
world.level_to_regions[level_name].append(fort_interior)
|
||
|
|
world.level_to_regions[level_name].append(fort_interior_caches)
|
||
|
|
world.level_to_regions[level_name].append(fort_interior_base)
|
||
|
|
world.level_to_regions[level_name].append(fort_interior_course_end)
|
||
|
|
|
||
|
|
# If Per-Level Orbsanity is enabled, build the special Orbsanity Region. This is a virtual region always
|
||
|
|
# accessible to Main Area. The Locations within are automatically checked when you collect enough orbs.
|
||
|
|
if options.enable_orbsanity == EnableOrbsanity.option_per_level:
|
||
|
|
orbs = JakAndDaxterRegion("Orbsanity", player, multiworld, level_name)
|
||
|
|
|
||
|
|
bundle_count = 200 // world.orb_bundle_size
|
||
|
|
for bundle_index in range(bundle_count):
|
||
|
|
amount = world.orb_bundle_size * (bundle_index + 1)
|
||
|
|
orbs.add_orb_locations(12,
|
||
|
|
bundle_index,
|
||
|
|
access_rule=lambda state, level=level_name, orb_amount=amount:
|
||
|
|
can_reach_orbs_level(state, player, world, level, orb_amount))
|
||
|
|
multiworld.regions.append(orbs)
|
||
|
|
main_area.connect(orbs)
|
||
|
|
|
||
|
|
return main_area
|