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 |