mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 12:11:33 -06:00

- Cleans up a few missed references in the setup guide. - Refactors Options class to use metaclass and decorators to enforce friendly limits on multiple levels. - Templates generated from the website, even ones with `random` should not fail generation because the website will only allow values inside the friendly limits. - _Uploaded_ yamls to the website with `random`, should also now respect friendly limits without the need for `random-range` shenanigans. - _Uploaded_ yamls to the website, or yamls that are used to generate locally, that have hard-defined values outside the friendly limits, will be clamped/dragged/massaged into those limits (with logged warnings). - Removed an early completion goal that was playing havoc with fill. Not enough people seem to use this goal, so its loss will not be mourned.
133 lines
7.0 KiB
Python
133 lines
7.0 KiB
Python
import typing
|
|
from Options import OptionError
|
|
from .items import item_table
|
|
from .options import EnableOrbsanity, CompletionCondition
|
|
from .rules import can_reach_orbs_global
|
|
from .locs import cell_locations as cells, scout_locations as scouts
|
|
from .regs import (geyser_rock_regions as geyser_rock,
|
|
sandover_village_regions as sandover_village,
|
|
forbidden_jungle_regions as forbidden_jungle,
|
|
sentinel_beach_regions as sentinel_beach,
|
|
misty_island_regions as misty_island,
|
|
fire_canyon_regions as fire_canyon,
|
|
rock_village_regions as rock_village,
|
|
precursor_basin_regions as precursor_basin,
|
|
lost_precursor_city_regions as lost_precursor_city,
|
|
boggy_swamp_regions as boggy_swamp,
|
|
mountain_pass_regions as mountain_pass,
|
|
volcanic_crater_regions as volcanic_crater,
|
|
spider_cave_regions as spider_cave,
|
|
snowy_mountain_regions as snowy_mountain,
|
|
lava_tube_regions as lava_tube,
|
|
gol_and_maias_citadel_regions as gol_and_maias_citadel)
|
|
from .regs.region_base import JakAndDaxterRegion
|
|
|
|
if typing.TYPE_CHECKING:
|
|
from . import JakAndDaxterWorld
|
|
|
|
|
|
def create_regions(world: "JakAndDaxterWorld"):
|
|
multiworld = world.multiworld
|
|
options = world.options
|
|
player = world.player
|
|
|
|
# Always start with Menu.
|
|
menu = JakAndDaxterRegion("Menu", player, multiworld)
|
|
multiworld.regions.append(menu)
|
|
|
|
# Build the special "Free 7 Scout Flies" Region. This is a virtual region always accessible to Menu.
|
|
# The Locations within are automatically checked when you receive the 7th scout fly for the corresponding cell.
|
|
free7 = JakAndDaxterRegion("'Free 7 Scout Flies' Power Cells", player, multiworld)
|
|
free7.add_cell_locations(cells.loc7SF_cellTable.keys())
|
|
for scout_fly_cell in free7.locations:
|
|
|
|
# Translate from Cell AP ID to Scout AP ID using game ID as an intermediary.
|
|
scout_fly_id = scouts.to_ap_id(cells.to_game_id(typing.cast(int, scout_fly_cell.address)))
|
|
scout_fly_cell.access_rule = lambda state, flies=scout_fly_id: state.has(item_table[flies], player, 7)
|
|
multiworld.regions.append(free7)
|
|
menu.connect(free7)
|
|
|
|
# If Global Orbsanity is enabled, build the special Orbsanity Region. This is a virtual region always
|
|
# accessible to Menu. The Locations within are automatically checked when you collect enough orbs.
|
|
if options.enable_orbsanity == EnableOrbsanity.option_global:
|
|
orbs = JakAndDaxterRegion("Orbsanity", player, multiworld)
|
|
|
|
bundle_count = 2000 // world.orb_bundle_size
|
|
for bundle_index in range(bundle_count):
|
|
|
|
# Unlike Per-Level Orbsanity, Global Orbsanity Locations always have a level_index of 16.
|
|
amount = world.orb_bundle_size * (bundle_index + 1)
|
|
orbs.add_orb_locations(16,
|
|
bundle_index,
|
|
access_rule=lambda state, orb_amount=amount:
|
|
can_reach_orbs_global(state, player, world, orb_amount))
|
|
multiworld.regions.append(orbs)
|
|
menu.connect(orbs)
|
|
|
|
# Build all regions. Include their intra-connecting Rules, their Locations, and their Location access rules.
|
|
gr = geyser_rock.build_regions("Geyser Rock", world)
|
|
sv = sandover_village.build_regions("Sandover Village", world)
|
|
fj, fjp = forbidden_jungle.build_regions("Forbidden Jungle", world)
|
|
sb = sentinel_beach.build_regions("Sentinel Beach", world)
|
|
mi = misty_island.build_regions("Misty Island", world)
|
|
fc = fire_canyon.build_regions("Fire Canyon", world)
|
|
rv, rvp, rvc = rock_village.build_regions("Rock Village", world)
|
|
pb = precursor_basin.build_regions("Precursor Basin", world)
|
|
lpc = lost_precursor_city.build_regions("Lost Precursor City", world)
|
|
bs = boggy_swamp.build_regions("Boggy Swamp", world)
|
|
mp, mpr = mountain_pass.build_regions("Mountain Pass", world)
|
|
vc = volcanic_crater.build_regions("Volcanic Crater", world)
|
|
sc = spider_cave.build_regions("Spider Cave", world)
|
|
sm = snowy_mountain.build_regions("Snowy Mountain", world)
|
|
lt = lava_tube.build_regions("Lava Tube", world)
|
|
gmc, fb, fd = gol_and_maias_citadel.build_regions("Gol and Maia's Citadel", world)
|
|
|
|
# Configurable counts of cells for connector levels.
|
|
fc_count = options.fire_canyon_cell_count.value
|
|
mp_count = options.mountain_pass_cell_count.value
|
|
lt_count = options.lava_tube_cell_count.value
|
|
|
|
# Define the interconnecting rules.
|
|
menu.connect(gr)
|
|
gr.connect(sv) # Geyser Rock modified to let you leave at any time.
|
|
sv.connect(fj)
|
|
sv.connect(sb)
|
|
sv.connect(mi, rule=lambda state: state.has("Fisherman's Boat", player))
|
|
sv.connect(fc, rule=lambda state: state.has("Power Cell", player, fc_count)) # Normally 20.
|
|
fc.connect(rv)
|
|
rv.connect(pb)
|
|
rv.connect(lpc)
|
|
rvp.connect(bs) # rv->rvp/rvc connections defined internally by RockVillageRegions.
|
|
rvc.connect(mp, rule=lambda state: state.has("Power Cell", player, mp_count)) # Normally 45.
|
|
mpr.connect(vc) # mp->mpr connection defined internally by MountainPassRegions.
|
|
vc.connect(sc)
|
|
vc.connect(sm, rule=lambda state: state.has("Snowy Mountain Gondola", player))
|
|
vc.connect(lt, rule=lambda state: state.has("Power Cell", player, lt_count)) # Normally 72.
|
|
lt.connect(gmc) # gmc->fb connection defined internally by GolAndMaiasCitadelRegions.
|
|
|
|
# Set the completion condition.
|
|
if options.jak_completion_condition == CompletionCondition.option_cross_fire_canyon:
|
|
multiworld.completion_condition[player] = lambda state: state.can_reach(rv, "Region", player)
|
|
|
|
elif options.jak_completion_condition == CompletionCondition.option_cross_mountain_pass:
|
|
multiworld.completion_condition[player] = lambda state: state.can_reach(vc, "Region", player)
|
|
|
|
elif options.jak_completion_condition == CompletionCondition.option_cross_lava_tube:
|
|
multiworld.completion_condition[player] = lambda state: state.can_reach(gmc, "Region", player)
|
|
|
|
# elif options.jak_completion_condition == CompletionCondition.option_defeat_dark_eco_plant:
|
|
# multiworld.completion_condition[player] = lambda state: state.can_reach(fjp, "Region", player)
|
|
|
|
elif options.jak_completion_condition == CompletionCondition.option_defeat_klaww:
|
|
multiworld.completion_condition[player] = lambda state: state.can_reach(mp, "Region", player)
|
|
|
|
elif options.jak_completion_condition == CompletionCondition.option_defeat_gol_and_maia:
|
|
multiworld.completion_condition[player] = lambda state: state.can_reach(fb, "Region", player)
|
|
|
|
elif options.jak_completion_condition == CompletionCondition.option_open_100_cell_door:
|
|
multiworld.completion_condition[player] = lambda state: state.can_reach(fd, "Region", player)
|
|
|
|
else:
|
|
raise OptionError(f"{world.player_name}: Unknown completion goal ID "
|
|
f"({options.jak_completion_condition.value}).")
|