TUNIC: Logic Rules Redux (#3544)
* Clean these functions up, get the hell out of here 5 parameter function * Clean up a bunch of rules that no longer need to be multi-lined since the functions are shorter * Clean up some range functions * Update to use world instead of player like Vi recommended * Fix merge conflict * Create new options * Slightly revise ls rule * Update options.py * Update options.py * Add tedious option for ls * Update laurels zips description * Create new options * Slightly revise ls rule * Update options.py * Update options.py * Add tedious option for ls * Update laurels zips description * Creating structures to redo ladder storage rules * Put together overworld ladder groups, remove tedious * Write up the rules for the regular rules * Update slot data and UT stuff * Put new ice grapple stuff in er rules * Ice grapple hard to get to fountain cross room * More ladder data * Wrote majority of overworld ladder rules * Finish the ladder storage rules * Update notes * Add note * Add well rail to the rules * More rules * Comment out logically irrelevant entrances * Update with laurels_zip helper * Add parameter to has_ice_grapple_logic for difficulty * Add new parameter to has_ice_grapple_logic * Move ice grapple chest to lower forest in ER/ladders * Fix rule * Finishing out hooking the new rules into the code * Fix bugs * Add more hard ice grapples * Fix more bugs * Shops my beloved * Change victory condition back * Remove debug stuff * Update plando connections description * Fix extremely rare bug * Add well front -> back hard ladder storages * Note in ls rules about knocking yourself down with bombs being out of logic * Add atoll fuse with wand + hard ls * Add some nonsense that boils down to activating the fuse in overworld * Further update LS description * Fix missing logic on bridge switch chest in upper zig * Revise upper zig rule change to account for ER * Fix merge conflict * Fix formatting, fix rule for heir access after merge * Add the shop sword logic stuff in * Remove todo that was already done * Fill out a to-do with some cursed nonsense * Fix event in wrong region * Fix missing cathedral -> elevator connection * Fix missing cathedral -> elevator connection * Add ER exception to cathedral -> elevator * Fix secret gathering place issue * Fix incorrect ls rule * Move 3 locations to Quarry Back since they're easily accessible from the back * Also update non-er region * Remove redundant parentheses * Add new test for a weird edge case in ER * Slight option description updates * Use has_ladder in spots where it wasn't used for some reason, add a comment * Fix unit test for ER * Update per exempt's suggestion * Add back LogicRules as an invisible option, to not break old yamls * Remove unused elevation from portal class * Update ladder storage without items description * Remove shop_scene stuff since it's no longer relevant in the mod by the time this version comes out * Remove shop scene stuff from game info since it's no longer relevant in the mod by the time this comes out * Update portal list to match main * god I love github merging things * Remove note * Add ice grapple hard path from upper overworld to temple rafters entrance * Actually that should be medium * Remove outdated note * Add ice grapple hard for swamp mid to the ledge * Add missing laurels zip in swamp * Some fixes to the ladder storage data while reviewing it * Add unit test for weird edge case * Backport outlet region system to fix ls bug * Fix incorrect ls, add todo * Add missing swamp ladder storage connections * Add swamp zip to er data * Add swamp zip to er rules * Add hard ice grapple for forest grave path main to upper * Add ice grapple logic for all bomb walls except the east quarry one * Add ice grapple logic for frog stairs eye to mouth without the ladder * Add hard ice grapple for overworld to the stairs to west garden * Add the ice grapple boss quick kills to medium ice grappling * Add the reverse connection for the ice grapple kill on Garden Knight * Add atoll house ice grapple push, and add west garden ice grapple entry to the regular rules
This commit is contained in:
parent
a652108472
commit
dad228cd4a
|
@ -7,8 +7,9 @@ from .rules import set_location_rules, set_region_rules, randomize_ability_unloc
|
||||||
from .er_rules import set_er_location_rules
|
from .er_rules import set_er_location_rules
|
||||||
from .regions import tunic_regions
|
from .regions import tunic_regions
|
||||||
from .er_scripts import create_er_regions
|
from .er_scripts import create_er_regions
|
||||||
from .er_data import portal_mapping
|
from .er_data import portal_mapping, RegionInfo, tunic_er_regions
|
||||||
from .options import TunicOptions, EntranceRando, tunic_option_groups, tunic_option_presets, TunicPlandoConnections
|
from .options import (TunicOptions, EntranceRando, tunic_option_groups, tunic_option_presets, TunicPlandoConnections,
|
||||||
|
LaurelsLocation, LogicRules, LaurelsZips, IceGrappling, LadderStorage)
|
||||||
from worlds.AutoWorld import WebWorld, World
|
from worlds.AutoWorld import WebWorld, World
|
||||||
from Options import PlandoConnection
|
from Options import PlandoConnection
|
||||||
from decimal import Decimal, ROUND_HALF_UP
|
from decimal import Decimal, ROUND_HALF_UP
|
||||||
|
@ -48,10 +49,12 @@ class TunicLocation(Location):
|
||||||
|
|
||||||
|
|
||||||
class SeedGroup(TypedDict):
|
class SeedGroup(TypedDict):
|
||||||
logic_rules: int # logic rules value
|
laurels_zips: bool # laurels_zips value
|
||||||
|
ice_grappling: int # ice_grappling value
|
||||||
|
ladder_storage: int # ls value
|
||||||
laurels_at_10_fairies: bool # laurels location value
|
laurels_at_10_fairies: bool # laurels location value
|
||||||
fixed_shop: bool # fixed shop value
|
fixed_shop: bool # fixed shop value
|
||||||
plando: TunicPlandoConnections # consolidated of plando connections for the seed group
|
plando: TunicPlandoConnections # consolidated plando connections for the seed group
|
||||||
|
|
||||||
|
|
||||||
class TunicWorld(World):
|
class TunicWorld(World):
|
||||||
|
@ -77,8 +80,17 @@ class TunicWorld(World):
|
||||||
tunic_portal_pairs: Dict[str, str]
|
tunic_portal_pairs: Dict[str, str]
|
||||||
er_portal_hints: Dict[int, str]
|
er_portal_hints: Dict[int, str]
|
||||||
seed_groups: Dict[str, SeedGroup] = {}
|
seed_groups: Dict[str, SeedGroup] = {}
|
||||||
|
shop_num: int = 1 # need to make it so that you can walk out of shops, but also that they aren't all connected
|
||||||
|
er_regions: Dict[str, RegionInfo] # absolutely needed so outlet regions work
|
||||||
|
|
||||||
def generate_early(self) -> None:
|
def generate_early(self) -> None:
|
||||||
|
if self.options.logic_rules >= LogicRules.option_no_major_glitches:
|
||||||
|
self.options.laurels_zips.value = LaurelsZips.option_true
|
||||||
|
self.options.ice_grappling.value = IceGrappling.option_medium
|
||||||
|
if self.options.logic_rules.value == LogicRules.option_unrestricted:
|
||||||
|
self.options.ladder_storage.value = LadderStorage.option_medium
|
||||||
|
|
||||||
|
self.er_regions = tunic_er_regions.copy()
|
||||||
if self.options.plando_connections:
|
if self.options.plando_connections:
|
||||||
for index, cxn in enumerate(self.options.plando_connections):
|
for index, cxn in enumerate(self.options.plando_connections):
|
||||||
# making shops second to simplify other things later
|
# making shops second to simplify other things later
|
||||||
|
@ -99,7 +111,10 @@ class TunicWorld(World):
|
||||||
self.options.keys_behind_bosses.value = passthrough["keys_behind_bosses"]
|
self.options.keys_behind_bosses.value = passthrough["keys_behind_bosses"]
|
||||||
self.options.sword_progression.value = passthrough["sword_progression"]
|
self.options.sword_progression.value = passthrough["sword_progression"]
|
||||||
self.options.ability_shuffling.value = passthrough["ability_shuffling"]
|
self.options.ability_shuffling.value = passthrough["ability_shuffling"]
|
||||||
self.options.logic_rules.value = passthrough["logic_rules"]
|
self.options.laurels_zips.value = passthrough["laurels_zips"]
|
||||||
|
self.options.ice_grappling.value = passthrough["ice_grappling"]
|
||||||
|
self.options.ladder_storage.value = passthrough["ladder_storage"]
|
||||||
|
self.options.ladder_storage_without_items = passthrough["ladder_storage_without_items"]
|
||||||
self.options.lanternless.value = passthrough["lanternless"]
|
self.options.lanternless.value = passthrough["lanternless"]
|
||||||
self.options.maskless.value = passthrough["maskless"]
|
self.options.maskless.value = passthrough["maskless"]
|
||||||
self.options.hexagon_quest.value = passthrough["hexagon_quest"]
|
self.options.hexagon_quest.value = passthrough["hexagon_quest"]
|
||||||
|
@ -118,19 +133,28 @@ class TunicWorld(World):
|
||||||
group = tunic.options.entrance_rando.value
|
group = tunic.options.entrance_rando.value
|
||||||
# if this is the first world in the group, set the rules equal to its rules
|
# if this is the first world in the group, set the rules equal to its rules
|
||||||
if group not in cls.seed_groups:
|
if group not in cls.seed_groups:
|
||||||
cls.seed_groups[group] = SeedGroup(logic_rules=tunic.options.logic_rules.value,
|
cls.seed_groups[group] = \
|
||||||
laurels_at_10_fairies=tunic.options.laurels_location == 3,
|
SeedGroup(laurels_zips=bool(tunic.options.laurels_zips),
|
||||||
fixed_shop=bool(tunic.options.fixed_shop),
|
ice_grappling=tunic.options.ice_grappling.value,
|
||||||
plando=tunic.options.plando_connections)
|
ladder_storage=tunic.options.ladder_storage.value,
|
||||||
|
laurels_at_10_fairies=tunic.options.laurels_location == LaurelsLocation.option_10_fairies,
|
||||||
|
fixed_shop=bool(tunic.options.fixed_shop),
|
||||||
|
plando=tunic.options.plando_connections)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# off is more restrictive
|
||||||
|
if not tunic.options.laurels_zips:
|
||||||
|
cls.seed_groups[group]["laurels_zips"] = False
|
||||||
# lower value is more restrictive
|
# lower value is more restrictive
|
||||||
if tunic.options.logic_rules.value < cls.seed_groups[group]["logic_rules"]:
|
if tunic.options.ice_grappling < cls.seed_groups[group]["ice_grappling"]:
|
||||||
cls.seed_groups[group]["logic_rules"] = tunic.options.logic_rules.value
|
cls.seed_groups[group]["ice_grappling"] = tunic.options.ice_grappling.value
|
||||||
|
# lower value is more restrictive
|
||||||
|
if tunic.options.ladder_storage.value < cls.seed_groups[group]["ladder_storage"]:
|
||||||
|
cls.seed_groups[group]["ladder_storage"] = tunic.options.ladder_storage.value
|
||||||
# laurels at 10 fairies changes logic for secret gathering place placement
|
# laurels at 10 fairies changes logic for secret gathering place placement
|
||||||
if tunic.options.laurels_location == 3:
|
if tunic.options.laurels_location == 3:
|
||||||
cls.seed_groups[group]["laurels_at_10_fairies"] = True
|
cls.seed_groups[group]["laurels_at_10_fairies"] = True
|
||||||
# fewer shops, one at windmill
|
# more restrictive, overrides the option for others in the same group, which is better than failing imo
|
||||||
if tunic.options.fixed_shop:
|
if tunic.options.fixed_shop:
|
||||||
cls.seed_groups[group]["fixed_shop"] = True
|
cls.seed_groups[group]["fixed_shop"] = True
|
||||||
|
|
||||||
|
@ -366,7 +390,10 @@ class TunicWorld(World):
|
||||||
"ability_shuffling": self.options.ability_shuffling.value,
|
"ability_shuffling": self.options.ability_shuffling.value,
|
||||||
"hexagon_quest": self.options.hexagon_quest.value,
|
"hexagon_quest": self.options.hexagon_quest.value,
|
||||||
"fool_traps": self.options.fool_traps.value,
|
"fool_traps": self.options.fool_traps.value,
|
||||||
"logic_rules": self.options.logic_rules.value,
|
"laurels_zips": self.options.laurels_zips.value,
|
||||||
|
"ice_grappling": self.options.ice_grappling.value,
|
||||||
|
"ladder_storage": self.options.ladder_storage.value,
|
||||||
|
"ladder_storage_without_items": self.options.ladder_storage_without_items.value,
|
||||||
"lanternless": self.options.lanternless.value,
|
"lanternless": self.options.lanternless.value,
|
||||||
"maskless": self.options.maskless.value,
|
"maskless": self.options.maskless.value,
|
||||||
"entrance_rando": int(bool(self.options.entrance_rando.value)),
|
"entrance_rando": int(bool(self.options.entrance_rando.value)),
|
||||||
|
|
|
@ -83,8 +83,6 @@ Notes:
|
||||||
- The `direction` field is not supported. Connections are always coupled.
|
- The `direction` field is not supported. Connections are always coupled.
|
||||||
- For a list of entrance names, check `er_data.py` in the TUNIC world folder or generate a game with the Entrance Randomizer option enabled and check the spoiler log.
|
- For a list of entrance names, check `er_data.py` in the TUNIC world folder or generate a game with the Entrance Randomizer option enabled and check the spoiler log.
|
||||||
- There is no limit to the number of Shops you can plando.
|
- There is no limit to the number of Shops you can plando.
|
||||||
- If you have more than one shop in a scene, you may be wrong warped when exiting a shop.
|
|
||||||
- If you have a shop in every scene, and you have an odd number of shops, it will error out.
|
|
||||||
|
|
||||||
See the [Archipelago Plando Guide](../../../tutorial/Archipelago/plando/en) for more information on Plando and Connection Plando.
|
See the [Archipelago Plando Guide](../../../tutorial/Archipelago/plando/en) for more information on Plando and Connection Plando.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
from typing import Dict, NamedTuple, List
|
from typing import Dict, NamedTuple, List, TYPE_CHECKING, Optional
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from . import TunicWorld
|
||||||
|
|
||||||
|
|
||||||
class Portal(NamedTuple):
|
class Portal(NamedTuple):
|
||||||
name: str # human-readable name
|
name: str # human-readable name
|
||||||
|
@ -9,6 +12,8 @@ class Portal(NamedTuple):
|
||||||
tag: str # vanilla tag
|
tag: str # vanilla tag
|
||||||
|
|
||||||
def scene(self) -> str: # the actual scene name in Tunic
|
def scene(self) -> str: # the actual scene name in Tunic
|
||||||
|
if self.region.startswith("Shop"):
|
||||||
|
return tunic_er_regions["Shop"].game_scene
|
||||||
return tunic_er_regions[self.region].game_scene
|
return tunic_er_regions[self.region].game_scene
|
||||||
|
|
||||||
def scene_destination(self) -> str: # full, nonchanging name to interpret by the mod
|
def scene_destination(self) -> str: # full, nonchanging name to interpret by the mod
|
||||||
|
@ -458,7 +463,7 @@ portal_mapping: List[Portal] = [
|
||||||
|
|
||||||
Portal(name="Cathedral Main Exit", region="Cathedral",
|
Portal(name="Cathedral Main Exit", region="Cathedral",
|
||||||
destination="Swamp Redux 2", tag="_main"),
|
destination="Swamp Redux 2", tag="_main"),
|
||||||
Portal(name="Cathedral Elevator", region="Cathedral",
|
Portal(name="Cathedral Elevator", region="Cathedral to Gauntlet",
|
||||||
destination="Cathedral Arena", tag="_"),
|
destination="Cathedral Arena", tag="_"),
|
||||||
Portal(name="Cathedral Secret Legend Room Exit", region="Cathedral Secret Legend Room",
|
Portal(name="Cathedral Secret Legend Room Exit", region="Cathedral Secret Legend Room",
|
||||||
destination="Swamp Redux 2", tag="_secret"),
|
destination="Swamp Redux 2", tag="_secret"),
|
||||||
|
@ -517,6 +522,13 @@ portal_mapping: List[Portal] = [
|
||||||
class RegionInfo(NamedTuple):
|
class RegionInfo(NamedTuple):
|
||||||
game_scene: str # the name of the scene in the actual game
|
game_scene: str # the name of the scene in the actual game
|
||||||
dead_end: int = 0 # if a region has only one exit
|
dead_end: int = 0 # if a region has only one exit
|
||||||
|
outlet_region: Optional[str] = None
|
||||||
|
is_fake_region: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
# gets the outlet region name if it exists, the region if it doesn't
|
||||||
|
def get_portal_outlet_region(portal: Portal, world: "TunicWorld") -> str:
|
||||||
|
return world.er_regions[portal.region].outlet_region or portal.region
|
||||||
|
|
||||||
|
|
||||||
class DeadEnd(IntEnum):
|
class DeadEnd(IntEnum):
|
||||||
|
@ -558,11 +570,11 @@ tunic_er_regions: Dict[str, RegionInfo] = {
|
||||||
"Overworld Ruined Passage Door": RegionInfo("Overworld Redux"), # the small space betweeen the door and the portal
|
"Overworld Ruined Passage Door": RegionInfo("Overworld Redux"), # the small space betweeen the door and the portal
|
||||||
"Overworld Old House Door": RegionInfo("Overworld Redux"), # the too-small space between the door and the portal
|
"Overworld Old House Door": RegionInfo("Overworld Redux"), # the too-small space between the door and the portal
|
||||||
"Overworld Southeast Cross Door": RegionInfo("Overworld Redux"), # the small space betweeen the door and the portal
|
"Overworld Southeast Cross Door": RegionInfo("Overworld Redux"), # the small space betweeen the door and the portal
|
||||||
"Overworld Fountain Cross Door": RegionInfo("Overworld Redux"), # the small space between the door and the portal
|
"Overworld Fountain Cross Door": RegionInfo("Overworld Redux", outlet_region="Overworld"),
|
||||||
"Overworld Temple Door": RegionInfo("Overworld Redux"), # the small space betweeen the door and the portal
|
"Overworld Temple Door": RegionInfo("Overworld Redux"), # the small space betweeen the door and the portal
|
||||||
"Overworld Town Portal": RegionInfo("Overworld Redux"), # being able to go to or come from the portal
|
"Overworld Town Portal": RegionInfo("Overworld Redux", outlet_region="Overworld"),
|
||||||
"Overworld Spawn Portal": RegionInfo("Overworld Redux"), # being able to go to or come from the portal
|
"Overworld Spawn Portal": RegionInfo("Overworld Redux", outlet_region="Overworld"),
|
||||||
"Cube Cave Entrance Region": RegionInfo("Overworld Redux"), # other side of the bomb wall
|
"Cube Cave Entrance Region": RegionInfo("Overworld Redux", outlet_region="Overworld"), # other side of the bomb wall
|
||||||
"Stick House": RegionInfo("Sword Cave", dead_end=DeadEnd.all_cats),
|
"Stick House": RegionInfo("Sword Cave", dead_end=DeadEnd.all_cats),
|
||||||
"Windmill": RegionInfo("Windmill"),
|
"Windmill": RegionInfo("Windmill"),
|
||||||
"Old House Back": RegionInfo("Overworld Interiors"), # part with the hc door
|
"Old House Back": RegionInfo("Overworld Interiors"), # part with the hc door
|
||||||
|
@ -591,7 +603,7 @@ tunic_er_regions: Dict[str, RegionInfo] = {
|
||||||
"Forest Belltower Lower": RegionInfo("Forest Belltower"),
|
"Forest Belltower Lower": RegionInfo("Forest Belltower"),
|
||||||
"East Forest": RegionInfo("East Forest Redux"),
|
"East Forest": RegionInfo("East Forest Redux"),
|
||||||
"East Forest Dance Fox Spot": RegionInfo("East Forest Redux"),
|
"East Forest Dance Fox Spot": RegionInfo("East Forest Redux"),
|
||||||
"East Forest Portal": RegionInfo("East Forest Redux"),
|
"East Forest Portal": RegionInfo("East Forest Redux", outlet_region="East Forest"),
|
||||||
"Lower Forest": RegionInfo("East Forest Redux"), # bottom of the forest
|
"Lower Forest": RegionInfo("East Forest Redux"), # bottom of the forest
|
||||||
"Guard House 1 East": RegionInfo("East Forest Redux Laddercave"),
|
"Guard House 1 East": RegionInfo("East Forest Redux Laddercave"),
|
||||||
"Guard House 1 West": RegionInfo("East Forest Redux Laddercave"),
|
"Guard House 1 West": RegionInfo("East Forest Redux Laddercave"),
|
||||||
|
@ -601,7 +613,7 @@ tunic_er_regions: Dict[str, RegionInfo] = {
|
||||||
"Forest Grave Path Main": RegionInfo("Sword Access"),
|
"Forest Grave Path Main": RegionInfo("Sword Access"),
|
||||||
"Forest Grave Path Upper": RegionInfo("Sword Access"),
|
"Forest Grave Path Upper": RegionInfo("Sword Access"),
|
||||||
"Forest Grave Path by Grave": RegionInfo("Sword Access"),
|
"Forest Grave Path by Grave": RegionInfo("Sword Access"),
|
||||||
"Forest Hero's Grave": RegionInfo("Sword Access"),
|
"Forest Hero's Grave": RegionInfo("Sword Access", outlet_region="Forest Grave Path by Grave"),
|
||||||
"Dark Tomb Entry Point": RegionInfo("Crypt Redux"), # both upper exits
|
"Dark Tomb Entry Point": RegionInfo("Crypt Redux"), # both upper exits
|
||||||
"Dark Tomb Upper": RegionInfo("Crypt Redux"), # the part with the casket and the top of the ladder
|
"Dark Tomb Upper": RegionInfo("Crypt Redux"), # the part with the casket and the top of the ladder
|
||||||
"Dark Tomb Main": RegionInfo("Crypt Redux"),
|
"Dark Tomb Main": RegionInfo("Crypt Redux"),
|
||||||
|
@ -614,18 +626,19 @@ tunic_er_regions: Dict[str, RegionInfo] = {
|
||||||
"Beneath the Well Back": RegionInfo("Sewer"), # the back two portals, and all 4 upper chests
|
"Beneath the Well Back": RegionInfo("Sewer"), # the back two portals, and all 4 upper chests
|
||||||
"West Garden": RegionInfo("Archipelagos Redux"),
|
"West Garden": RegionInfo("Archipelagos Redux"),
|
||||||
"Magic Dagger House": RegionInfo("archipelagos_house", dead_end=DeadEnd.all_cats),
|
"Magic Dagger House": RegionInfo("archipelagos_house", dead_end=DeadEnd.all_cats),
|
||||||
"West Garden Portal": RegionInfo("Archipelagos Redux", dead_end=DeadEnd.restricted),
|
"West Garden Portal": RegionInfo("Archipelagos Redux", dead_end=DeadEnd.restricted, outlet_region="West Garden by Portal"),
|
||||||
|
"West Garden by Portal": RegionInfo("Archipelagos Redux", dead_end=DeadEnd.restricted),
|
||||||
"West Garden Portal Item": RegionInfo("Archipelagos Redux", dead_end=DeadEnd.restricted),
|
"West Garden Portal Item": RegionInfo("Archipelagos Redux", dead_end=DeadEnd.restricted),
|
||||||
"West Garden Laurels Exit Region": RegionInfo("Archipelagos Redux"),
|
"West Garden Laurels Exit Region": RegionInfo("Archipelagos Redux"),
|
||||||
"West Garden after Boss": RegionInfo("Archipelagos Redux"),
|
"West Garden after Boss": RegionInfo("Archipelagos Redux"),
|
||||||
"West Garden Hero's Grave Region": RegionInfo("Archipelagos Redux"),
|
"West Garden Hero's Grave Region": RegionInfo("Archipelagos Redux", outlet_region="West Garden"),
|
||||||
"Ruined Atoll": RegionInfo("Atoll Redux"),
|
"Ruined Atoll": RegionInfo("Atoll Redux"),
|
||||||
"Ruined Atoll Lower Entry Area": RegionInfo("Atoll Redux"),
|
"Ruined Atoll Lower Entry Area": RegionInfo("Atoll Redux"),
|
||||||
"Ruined Atoll Ladder Tops": RegionInfo("Atoll Redux"), # at the top of the 5 ladders in south Atoll
|
"Ruined Atoll Ladder Tops": RegionInfo("Atoll Redux"), # at the top of the 5 ladders in south Atoll
|
||||||
"Ruined Atoll Frog Mouth": RegionInfo("Atoll Redux"),
|
"Ruined Atoll Frog Mouth": RegionInfo("Atoll Redux"),
|
||||||
"Ruined Atoll Frog Eye": RegionInfo("Atoll Redux"),
|
"Ruined Atoll Frog Eye": RegionInfo("Atoll Redux"),
|
||||||
"Ruined Atoll Portal": RegionInfo("Atoll Redux"),
|
"Ruined Atoll Portal": RegionInfo("Atoll Redux", outlet_region="Ruined Atoll"),
|
||||||
"Ruined Atoll Statue": RegionInfo("Atoll Redux"),
|
"Ruined Atoll Statue": RegionInfo("Atoll Redux", outlet_region="Ruined Atoll"),
|
||||||
"Frog Stairs Eye Exit": RegionInfo("Frog Stairs"),
|
"Frog Stairs Eye Exit": RegionInfo("Frog Stairs"),
|
||||||
"Frog Stairs Upper": RegionInfo("Frog Stairs"),
|
"Frog Stairs Upper": RegionInfo("Frog Stairs"),
|
||||||
"Frog Stairs Lower": RegionInfo("Frog Stairs"),
|
"Frog Stairs Lower": RegionInfo("Frog Stairs"),
|
||||||
|
@ -633,18 +646,20 @@ tunic_er_regions: Dict[str, RegionInfo] = {
|
||||||
"Frog's Domain Entry": RegionInfo("frog cave main"),
|
"Frog's Domain Entry": RegionInfo("frog cave main"),
|
||||||
"Frog's Domain": RegionInfo("frog cave main"),
|
"Frog's Domain": RegionInfo("frog cave main"),
|
||||||
"Frog's Domain Back": RegionInfo("frog cave main"),
|
"Frog's Domain Back": RegionInfo("frog cave main"),
|
||||||
"Library Exterior Tree Region": RegionInfo("Library Exterior"),
|
"Library Exterior Tree Region": RegionInfo("Library Exterior", outlet_region="Library Exterior by Tree"),
|
||||||
|
"Library Exterior by Tree": RegionInfo("Library Exterior"),
|
||||||
"Library Exterior Ladder Region": RegionInfo("Library Exterior"),
|
"Library Exterior Ladder Region": RegionInfo("Library Exterior"),
|
||||||
"Library Hall Bookshelf": RegionInfo("Library Hall"),
|
"Library Hall Bookshelf": RegionInfo("Library Hall"),
|
||||||
"Library Hall": RegionInfo("Library Hall"),
|
"Library Hall": RegionInfo("Library Hall"),
|
||||||
"Library Hero's Grave Region": RegionInfo("Library Hall"),
|
"Library Hero's Grave Region": RegionInfo("Library Hall", outlet_region="Library Hall"),
|
||||||
"Library Hall to Rotunda": RegionInfo("Library Hall"),
|
"Library Hall to Rotunda": RegionInfo("Library Hall"),
|
||||||
"Library Rotunda to Hall": RegionInfo("Library Rotunda"),
|
"Library Rotunda to Hall": RegionInfo("Library Rotunda"),
|
||||||
"Library Rotunda": RegionInfo("Library Rotunda"),
|
"Library Rotunda": RegionInfo("Library Rotunda"),
|
||||||
"Library Rotunda to Lab": RegionInfo("Library Rotunda"),
|
"Library Rotunda to Lab": RegionInfo("Library Rotunda"),
|
||||||
"Library Lab": RegionInfo("Library Lab"),
|
"Library Lab": RegionInfo("Library Lab"),
|
||||||
"Library Lab Lower": RegionInfo("Library Lab"),
|
"Library Lab Lower": RegionInfo("Library Lab"),
|
||||||
"Library Portal": RegionInfo("Library Lab"),
|
"Library Portal": RegionInfo("Library Lab", outlet_region="Library Lab on Portal Pad"),
|
||||||
|
"Library Lab on Portal Pad": RegionInfo("Library Lab"),
|
||||||
"Library Lab to Librarian": RegionInfo("Library Lab"),
|
"Library Lab to Librarian": RegionInfo("Library Lab"),
|
||||||
"Library Arena": RegionInfo("Library Arena", dead_end=DeadEnd.all_cats),
|
"Library Arena": RegionInfo("Library Arena", dead_end=DeadEnd.all_cats),
|
||||||
"Fortress Exterior from East Forest": RegionInfo("Fortress Courtyard"),
|
"Fortress Exterior from East Forest": RegionInfo("Fortress Courtyard"),
|
||||||
|
@ -663,22 +678,22 @@ tunic_er_regions: Dict[str, RegionInfo] = {
|
||||||
"Fortress Grave Path": RegionInfo("Fortress Reliquary"),
|
"Fortress Grave Path": RegionInfo("Fortress Reliquary"),
|
||||||
"Fortress Grave Path Upper": RegionInfo("Fortress Reliquary", dead_end=DeadEnd.restricted),
|
"Fortress Grave Path Upper": RegionInfo("Fortress Reliquary", dead_end=DeadEnd.restricted),
|
||||||
"Fortress Grave Path Dusty Entrance Region": RegionInfo("Fortress Reliquary"),
|
"Fortress Grave Path Dusty Entrance Region": RegionInfo("Fortress Reliquary"),
|
||||||
"Fortress Hero's Grave Region": RegionInfo("Fortress Reliquary"),
|
"Fortress Hero's Grave Region": RegionInfo("Fortress Reliquary", outlet_region="Fortress Grave Path"),
|
||||||
"Fortress Leaf Piles": RegionInfo("Dusty", dead_end=DeadEnd.all_cats),
|
"Fortress Leaf Piles": RegionInfo("Dusty", dead_end=DeadEnd.all_cats),
|
||||||
"Fortress Arena": RegionInfo("Fortress Arena"),
|
"Fortress Arena": RegionInfo("Fortress Arena"),
|
||||||
"Fortress Arena Portal": RegionInfo("Fortress Arena"),
|
"Fortress Arena Portal": RegionInfo("Fortress Arena", outlet_region="Fortress Arena"),
|
||||||
"Lower Mountain": RegionInfo("Mountain"),
|
"Lower Mountain": RegionInfo("Mountain"),
|
||||||
"Lower Mountain Stairs": RegionInfo("Mountain"),
|
"Lower Mountain Stairs": RegionInfo("Mountain"),
|
||||||
"Top of the Mountain": RegionInfo("Mountaintop", dead_end=DeadEnd.all_cats),
|
"Top of the Mountain": RegionInfo("Mountaintop", dead_end=DeadEnd.all_cats),
|
||||||
"Quarry Connector": RegionInfo("Darkwoods Tunnel"),
|
"Quarry Connector": RegionInfo("Darkwoods Tunnel"),
|
||||||
"Quarry Entry": RegionInfo("Quarry Redux"),
|
"Quarry Entry": RegionInfo("Quarry Redux"),
|
||||||
"Quarry": RegionInfo("Quarry Redux"),
|
"Quarry": RegionInfo("Quarry Redux"),
|
||||||
"Quarry Portal": RegionInfo("Quarry Redux"),
|
"Quarry Portal": RegionInfo("Quarry Redux", outlet_region="Quarry Entry"),
|
||||||
"Quarry Back": RegionInfo("Quarry Redux"),
|
"Quarry Back": RegionInfo("Quarry Redux"),
|
||||||
"Quarry Monastery Entry": RegionInfo("Quarry Redux"),
|
"Quarry Monastery Entry": RegionInfo("Quarry Redux"),
|
||||||
"Monastery Front": RegionInfo("Monastery"),
|
"Monastery Front": RegionInfo("Monastery"),
|
||||||
"Monastery Back": RegionInfo("Monastery"),
|
"Monastery Back": RegionInfo("Monastery"),
|
||||||
"Monastery Hero's Grave Region": RegionInfo("Monastery"),
|
"Monastery Hero's Grave Region": RegionInfo("Monastery", outlet_region="Monastery Back"),
|
||||||
"Monastery Rope": RegionInfo("Quarry Redux"),
|
"Monastery Rope": RegionInfo("Quarry Redux"),
|
||||||
"Lower Quarry": RegionInfo("Quarry Redux"),
|
"Lower Quarry": RegionInfo("Quarry Redux"),
|
||||||
"Even Lower Quarry": RegionInfo("Quarry Redux"),
|
"Even Lower Quarry": RegionInfo("Quarry Redux"),
|
||||||
|
@ -691,19 +706,21 @@ tunic_er_regions: Dict[str, RegionInfo] = {
|
||||||
"Rooted Ziggurat Middle Bottom": RegionInfo("ziggurat2020_2"),
|
"Rooted Ziggurat Middle Bottom": RegionInfo("ziggurat2020_2"),
|
||||||
"Rooted Ziggurat Lower Front": RegionInfo("ziggurat2020_3"), # the vanilla entry point side
|
"Rooted Ziggurat Lower Front": RegionInfo("ziggurat2020_3"), # the vanilla entry point side
|
||||||
"Rooted Ziggurat Lower Back": RegionInfo("ziggurat2020_3"), # the boss side
|
"Rooted Ziggurat Lower Back": RegionInfo("ziggurat2020_3"), # the boss side
|
||||||
"Zig Skip Exit": RegionInfo("ziggurat2020_3", dead_end=DeadEnd.special), # the exit from zig skip, for use with fixed shop on
|
"Zig Skip Exit": RegionInfo("ziggurat2020_3", dead_end=DeadEnd.special, outlet_region="Rooted Ziggurat Lower Front"), # the exit from zig skip, for use with fixed shop on
|
||||||
"Rooted Ziggurat Portal Room Entrance": RegionInfo("ziggurat2020_3"), # the door itself on the zig 3 side
|
"Rooted Ziggurat Portal Room Entrance": RegionInfo("ziggurat2020_3", outlet_region="Rooted Ziggurat Lower Back"), # the door itself on the zig 3 side
|
||||||
"Rooted Ziggurat Portal": RegionInfo("ziggurat2020_FTRoom"),
|
"Rooted Ziggurat Portal": RegionInfo("ziggurat2020_FTRoom", outlet_region="Rooted Ziggurat Portal Room"),
|
||||||
|
"Rooted Ziggurat Portal Room": RegionInfo("ziggurat2020_FTRoom"),
|
||||||
"Rooted Ziggurat Portal Room Exit": RegionInfo("ziggurat2020_FTRoom"),
|
"Rooted Ziggurat Portal Room Exit": RegionInfo("ziggurat2020_FTRoom"),
|
||||||
"Swamp Front": RegionInfo("Swamp Redux 2"), # from the main entry to the top of the ladder after south
|
"Swamp Front": RegionInfo("Swamp Redux 2"), # from the main entry to the top of the ladder after south
|
||||||
"Swamp Mid": RegionInfo("Swamp Redux 2"), # from the bottom of the ladder to the cathedral door
|
"Swamp Mid": RegionInfo("Swamp Redux 2"), # from the bottom of the ladder to the cathedral door
|
||||||
"Swamp Ledge under Cathedral Door": RegionInfo("Swamp Redux 2"), # the ledge with the chest and secret door
|
"Swamp Ledge under Cathedral Door": RegionInfo("Swamp Redux 2"), # the ledge with the chest and secret door
|
||||||
"Swamp to Cathedral Treasure Room": RegionInfo("Swamp Redux 2"), # just the door
|
"Swamp to Cathedral Treasure Room": RegionInfo("Swamp Redux 2", outlet_region="Swamp Ledge under Cathedral Door"), # just the door
|
||||||
"Swamp to Cathedral Main Entrance Region": RegionInfo("Swamp Redux 2"), # just the door
|
"Swamp to Cathedral Main Entrance Region": RegionInfo("Swamp Redux 2"), # just the door
|
||||||
"Back of Swamp": RegionInfo("Swamp Redux 2"), # the area with hero grave and gauntlet entrance
|
"Back of Swamp": RegionInfo("Swamp Redux 2"), # the area with hero grave and gauntlet entrance
|
||||||
"Swamp Hero's Grave Region": RegionInfo("Swamp Redux 2"),
|
"Swamp Hero's Grave Region": RegionInfo("Swamp Redux 2", outlet_region="Back of Swamp"),
|
||||||
"Back of Swamp Laurels Area": RegionInfo("Swamp Redux 2"), # the spots you need laurels to traverse
|
"Back of Swamp Laurels Area": RegionInfo("Swamp Redux 2"), # the spots you need laurels to traverse
|
||||||
"Cathedral": RegionInfo("Cathedral Redux"),
|
"Cathedral": RegionInfo("Cathedral Redux"),
|
||||||
|
"Cathedral to Gauntlet": RegionInfo("Cathedral Redux"), # the elevator
|
||||||
"Cathedral Secret Legend Room": RegionInfo("Cathedral Redux", dead_end=DeadEnd.all_cats),
|
"Cathedral Secret Legend Room": RegionInfo("Cathedral Redux", dead_end=DeadEnd.all_cats),
|
||||||
"Cathedral Gauntlet Checkpoint": RegionInfo("Cathedral Arena"),
|
"Cathedral Gauntlet Checkpoint": RegionInfo("Cathedral Arena"),
|
||||||
"Cathedral Gauntlet": RegionInfo("Cathedral Arena"),
|
"Cathedral Gauntlet": RegionInfo("Cathedral Arena"),
|
||||||
|
@ -711,10 +728,10 @@ tunic_er_regions: Dict[str, RegionInfo] = {
|
||||||
"Far Shore": RegionInfo("Transit"),
|
"Far Shore": RegionInfo("Transit"),
|
||||||
"Far Shore to Spawn Region": RegionInfo("Transit"),
|
"Far Shore to Spawn Region": RegionInfo("Transit"),
|
||||||
"Far Shore to East Forest Region": RegionInfo("Transit"),
|
"Far Shore to East Forest Region": RegionInfo("Transit"),
|
||||||
"Far Shore to Quarry Region": RegionInfo("Transit"),
|
"Far Shore to Quarry Region": RegionInfo("Transit", outlet_region="Far Shore"),
|
||||||
"Far Shore to Fortress Region": RegionInfo("Transit"),
|
"Far Shore to Fortress Region": RegionInfo("Transit", outlet_region="Far Shore"),
|
||||||
"Far Shore to Library Region": RegionInfo("Transit"),
|
"Far Shore to Library Region": RegionInfo("Transit", outlet_region="Far Shore"),
|
||||||
"Far Shore to West Garden Region": RegionInfo("Transit"),
|
"Far Shore to West Garden Region": RegionInfo("Transit", outlet_region="Far Shore"),
|
||||||
"Hero Relic - Fortress": RegionInfo("RelicVoid", dead_end=DeadEnd.all_cats),
|
"Hero Relic - Fortress": RegionInfo("RelicVoid", dead_end=DeadEnd.all_cats),
|
||||||
"Hero Relic - Quarry": RegionInfo("RelicVoid", dead_end=DeadEnd.all_cats),
|
"Hero Relic - Quarry": RegionInfo("RelicVoid", dead_end=DeadEnd.all_cats),
|
||||||
"Hero Relic - West Garden": RegionInfo("RelicVoid", dead_end=DeadEnd.all_cats),
|
"Hero Relic - West Garden": RegionInfo("RelicVoid", dead_end=DeadEnd.all_cats),
|
||||||
|
@ -728,6 +745,16 @@ tunic_er_regions: Dict[str, RegionInfo] = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# this is essentially a pared down version of the region connections in rules.py, with some minor differences
|
||||||
|
# the main purpose of this is to make it so that you can access every region
|
||||||
|
# most items are excluded from the rules here, since we can assume Archipelago will properly place them
|
||||||
|
# laurels (hyperdash) can be locked at 10 fairies, requiring access to secret gathering place
|
||||||
|
# so until secret gathering place has been paired, you do not have hyperdash, so you cannot use hyperdash entrances
|
||||||
|
# Zip means you need the laurels zips option enabled
|
||||||
|
# IG# refers to ice grappling difficulties
|
||||||
|
# LS# refers to ladder storage difficulties
|
||||||
|
# LS rules are used for region connections here regardless of whether you have being knocked out of the air in logic
|
||||||
|
# this is because it just means you can reach the entrances in that region via ladder storage
|
||||||
traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Overworld": {
|
"Overworld": {
|
||||||
"Overworld Beach":
|
"Overworld Beach":
|
||||||
|
@ -735,13 +762,13 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Overworld to Atoll Upper":
|
"Overworld to Atoll Upper":
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"]],
|
||||||
"Overworld Belltower":
|
"Overworld Belltower":
|
||||||
[["Hyperdash"], ["UR"]],
|
[["Hyperdash"], ["LS1"]],
|
||||||
"Overworld Swamp Upper Entry":
|
"Overworld Swamp Upper Entry":
|
||||||
[["Hyperdash"], ["UR"]],
|
[["Hyperdash"], ["LS1"]],
|
||||||
"Overworld Swamp Lower Entry":
|
"Overworld Swamp Lower Entry":
|
||||||
[],
|
[],
|
||||||
"Overworld Special Shop Entry":
|
"Overworld Special Shop Entry":
|
||||||
[["Hyperdash"], ["UR"]],
|
[["Hyperdash"], ["LS1"]],
|
||||||
"Overworld Well Ladder":
|
"Overworld Well Ladder":
|
||||||
[],
|
[],
|
||||||
"Overworld Ruined Passage Door":
|
"Overworld Ruined Passage Door":
|
||||||
|
@ -759,11 +786,11 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Overworld after Envoy":
|
"Overworld after Envoy":
|
||||||
[],
|
[],
|
||||||
"Overworld Quarry Entry":
|
"Overworld Quarry Entry":
|
||||||
[["NMG"]],
|
[["IG2"], ["LS1"]],
|
||||||
"Overworld Tunnel Turret":
|
"Overworld Tunnel Turret":
|
||||||
[["NMG"], ["Hyperdash"]],
|
[["IG1"], ["LS1"], ["Hyperdash"]],
|
||||||
"Overworld Temple Door":
|
"Overworld Temple Door":
|
||||||
[["NMG"], ["Forest Belltower Upper", "Overworld Belltower"]],
|
[["IG2"], ["LS3"], ["Forest Belltower Upper", "Overworld Belltower"]],
|
||||||
"Overworld Southeast Cross Door":
|
"Overworld Southeast Cross Door":
|
||||||
[],
|
[],
|
||||||
"Overworld Fountain Cross Door":
|
"Overworld Fountain Cross Door":
|
||||||
|
@ -773,25 +800,28 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Overworld Spawn Portal":
|
"Overworld Spawn Portal":
|
||||||
[],
|
[],
|
||||||
"Overworld Well to Furnace Rail":
|
"Overworld Well to Furnace Rail":
|
||||||
[["UR"]],
|
[["LS2"]],
|
||||||
"Overworld Old House Door":
|
"Overworld Old House Door":
|
||||||
[],
|
[],
|
||||||
"Cube Cave Entrance Region":
|
"Cube Cave Entrance Region":
|
||||||
[],
|
[],
|
||||||
|
# drop a rudeling, icebolt or ice bomb
|
||||||
|
"Overworld to West Garden from Furnace":
|
||||||
|
[["IG3"]],
|
||||||
},
|
},
|
||||||
"East Overworld": {
|
"East Overworld": {
|
||||||
"Above Ruined Passage":
|
"Above Ruined Passage":
|
||||||
[],
|
[],
|
||||||
"After Ruined Passage":
|
"After Ruined Passage":
|
||||||
[["NMG"]],
|
[["IG1"], ["LS1"]],
|
||||||
"Overworld":
|
# "Overworld":
|
||||||
[],
|
# [],
|
||||||
"Overworld at Patrol Cave":
|
"Overworld at Patrol Cave":
|
||||||
[],
|
[],
|
||||||
"Overworld above Patrol Cave":
|
"Overworld above Patrol Cave":
|
||||||
[],
|
[],
|
||||||
"Overworld Special Shop Entry":
|
"Overworld Special Shop Entry":
|
||||||
[["Hyperdash"], ["UR"]]
|
[["Hyperdash"], ["LS1"]]
|
||||||
},
|
},
|
||||||
"Overworld Special Shop Entry": {
|
"Overworld Special Shop Entry": {
|
||||||
"East Overworld":
|
"East Overworld":
|
||||||
|
@ -800,8 +830,8 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Overworld Belltower": {
|
"Overworld Belltower": {
|
||||||
"Overworld Belltower at Bell":
|
"Overworld Belltower at Bell":
|
||||||
[],
|
[],
|
||||||
"Overworld":
|
# "Overworld":
|
||||||
[],
|
# [],
|
||||||
"Overworld to West Garden Upper":
|
"Overworld to West Garden Upper":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
@ -809,19 +839,19 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Overworld Belltower":
|
"Overworld Belltower":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Overworld Swamp Upper Entry": {
|
# "Overworld Swamp Upper Entry": {
|
||||||
"Overworld":
|
# "Overworld":
|
||||||
[],
|
# [],
|
||||||
},
|
# },
|
||||||
"Overworld Swamp Lower Entry": {
|
# "Overworld Swamp Lower Entry": {
|
||||||
"Overworld":
|
# "Overworld":
|
||||||
[],
|
# [],
|
||||||
},
|
# },
|
||||||
"Overworld Beach": {
|
"Overworld Beach": {
|
||||||
"Overworld":
|
# "Overworld":
|
||||||
[],
|
# [],
|
||||||
"Overworld West Garden Laurels Entry":
|
"Overworld West Garden Laurels Entry":
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"], ["LS1"]],
|
||||||
"Overworld to Atoll Upper":
|
"Overworld to Atoll Upper":
|
||||||
[],
|
[],
|
||||||
"Overworld Tunnel Turret":
|
"Overworld Tunnel Turret":
|
||||||
|
@ -832,38 +862,37 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"]],
|
||||||
},
|
},
|
||||||
"Overworld to Atoll Upper": {
|
"Overworld to Atoll Upper": {
|
||||||
"Overworld":
|
# "Overworld":
|
||||||
[],
|
# [],
|
||||||
"Overworld Beach":
|
"Overworld Beach":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Overworld Tunnel Turret": {
|
"Overworld Tunnel Turret": {
|
||||||
"Overworld":
|
# "Overworld":
|
||||||
[],
|
# [],
|
||||||
"Overworld Beach":
|
"Overworld Beach":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Overworld Well Ladder": {
|
"Overworld Well Ladder": {
|
||||||
"Overworld":
|
# "Overworld":
|
||||||
[],
|
# [],
|
||||||
},
|
},
|
||||||
"Overworld at Patrol Cave": {
|
"Overworld at Patrol Cave": {
|
||||||
"East Overworld":
|
"East Overworld":
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"], ["LS1"], ["IG1"]],
|
||||||
"Overworld above Patrol Cave":
|
"Overworld above Patrol Cave":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Overworld above Patrol Cave": {
|
"Overworld above Patrol Cave": {
|
||||||
"Overworld":
|
# "Overworld":
|
||||||
[],
|
# [],
|
||||||
"East Overworld":
|
"East Overworld":
|
||||||
[],
|
[],
|
||||||
"Upper Overworld":
|
"Upper Overworld":
|
||||||
[],
|
[],
|
||||||
"Overworld at Patrol Cave":
|
"Overworld at Patrol Cave":
|
||||||
[],
|
[],
|
||||||
"Overworld Belltower at Bell":
|
# readd long dong if we ever do a misc tricks option
|
||||||
[["NMG"]],
|
|
||||||
},
|
},
|
||||||
"Upper Overworld": {
|
"Upper Overworld": {
|
||||||
"Overworld above Patrol Cave":
|
"Overworld above Patrol Cave":
|
||||||
|
@ -878,51 +907,49 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Overworld above Quarry Entrance": {
|
"Overworld above Quarry Entrance": {
|
||||||
"Overworld":
|
# "Overworld":
|
||||||
[],
|
# [],
|
||||||
"Upper Overworld":
|
"Upper Overworld":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Overworld Quarry Entry": {
|
"Overworld Quarry Entry": {
|
||||||
"Overworld after Envoy":
|
"Overworld after Envoy":
|
||||||
[],
|
[],
|
||||||
"Overworld":
|
# "Overworld":
|
||||||
[["NMG"]],
|
# [["IG1"]],
|
||||||
},
|
},
|
||||||
"Overworld after Envoy": {
|
"Overworld after Envoy": {
|
||||||
"Overworld":
|
# "Overworld":
|
||||||
[],
|
# [],
|
||||||
"Overworld Quarry Entry":
|
"Overworld Quarry Entry":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"After Ruined Passage": {
|
"After Ruined Passage": {
|
||||||
"Overworld":
|
# "Overworld":
|
||||||
[],
|
# [],
|
||||||
"Above Ruined Passage":
|
"Above Ruined Passage":
|
||||||
[],
|
[],
|
||||||
"East Overworld":
|
|
||||||
[["NMG"]],
|
|
||||||
},
|
},
|
||||||
"Above Ruined Passage": {
|
"Above Ruined Passage": {
|
||||||
"Overworld":
|
# "Overworld":
|
||||||
[],
|
# [],
|
||||||
"After Ruined Passage":
|
"After Ruined Passage":
|
||||||
[],
|
[],
|
||||||
"East Overworld":
|
"East Overworld":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Overworld Ruined Passage Door": {
|
# "Overworld Ruined Passage Door": {
|
||||||
"Overworld":
|
# "Overworld":
|
||||||
[["Hyperdash", "NMG"]],
|
# [["Hyperdash", "Zip"]],
|
||||||
},
|
# },
|
||||||
"Overworld Town Portal": {
|
# "Overworld Town Portal": {
|
||||||
"Overworld":
|
# "Overworld":
|
||||||
[],
|
# [],
|
||||||
},
|
# },
|
||||||
"Overworld Spawn Portal": {
|
# "Overworld Spawn Portal": {
|
||||||
"Overworld":
|
# "Overworld":
|
||||||
[],
|
# [],
|
||||||
},
|
# },
|
||||||
"Cube Cave Entrance Region": {
|
"Cube Cave Entrance Region": {
|
||||||
"Overworld":
|
"Overworld":
|
||||||
[],
|
[],
|
||||||
|
@ -933,7 +960,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
},
|
},
|
||||||
"Old House Back": {
|
"Old House Back": {
|
||||||
"Old House Front":
|
"Old House Front":
|
||||||
[["Hyperdash", "NMG"]],
|
[["Hyperdash", "Zip"]],
|
||||||
},
|
},
|
||||||
"Furnace Fuse": {
|
"Furnace Fuse": {
|
||||||
"Furnace Ladder Area":
|
"Furnace Ladder Area":
|
||||||
|
@ -941,9 +968,9 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
},
|
},
|
||||||
"Furnace Ladder Area": {
|
"Furnace Ladder Area": {
|
||||||
"Furnace Fuse":
|
"Furnace Fuse":
|
||||||
[["Hyperdash"], ["UR"]],
|
[["Hyperdash"], ["LS1"]],
|
||||||
"Furnace Walking Path":
|
"Furnace Walking Path":
|
||||||
[["Hyperdash"], ["UR"]],
|
[["Hyperdash"], ["LS1"]],
|
||||||
},
|
},
|
||||||
"Furnace Walking Path": {
|
"Furnace Walking Path": {
|
||||||
"Furnace Ladder Area":
|
"Furnace Ladder Area":
|
||||||
|
@ -971,7 +998,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
},
|
},
|
||||||
"East Forest": {
|
"East Forest": {
|
||||||
"East Forest Dance Fox Spot":
|
"East Forest Dance Fox Spot":
|
||||||
[["Hyperdash"], ["NMG"]],
|
[["Hyperdash"], ["IG1"], ["LS1"]],
|
||||||
"East Forest Portal":
|
"East Forest Portal":
|
||||||
[],
|
[],
|
||||||
"Lower Forest":
|
"Lower Forest":
|
||||||
|
@ -979,7 +1006,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
},
|
},
|
||||||
"East Forest Dance Fox Spot": {
|
"East Forest Dance Fox Spot": {
|
||||||
"East Forest":
|
"East Forest":
|
||||||
[["Hyperdash"], ["NMG"]],
|
[["Hyperdash"], ["IG1"]],
|
||||||
},
|
},
|
||||||
"East Forest Portal": {
|
"East Forest Portal": {
|
||||||
"East Forest":
|
"East Forest":
|
||||||
|
@ -995,7 +1022,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
},
|
},
|
||||||
"Guard House 1 West": {
|
"Guard House 1 West": {
|
||||||
"Guard House 1 East":
|
"Guard House 1 East":
|
||||||
[["Hyperdash"], ["UR"]],
|
[["Hyperdash"], ["LS1"]],
|
||||||
},
|
},
|
||||||
"Guard House 2 Upper": {
|
"Guard House 2 Upper": {
|
||||||
"Guard House 2 Lower":
|
"Guard House 2 Lower":
|
||||||
|
@ -1007,19 +1034,19 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
},
|
},
|
||||||
"Forest Grave Path Main": {
|
"Forest Grave Path Main": {
|
||||||
"Forest Grave Path Upper":
|
"Forest Grave Path Upper":
|
||||||
[["Hyperdash"], ["UR"]],
|
[["Hyperdash"], ["LS2"], ["IG3"]],
|
||||||
"Forest Grave Path by Grave":
|
"Forest Grave Path by Grave":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Forest Grave Path Upper": {
|
"Forest Grave Path Upper": {
|
||||||
"Forest Grave Path Main":
|
"Forest Grave Path Main":
|
||||||
[["Hyperdash"], ["NMG"]],
|
[["Hyperdash"], ["IG1"]],
|
||||||
},
|
},
|
||||||
"Forest Grave Path by Grave": {
|
"Forest Grave Path by Grave": {
|
||||||
"Forest Hero's Grave":
|
"Forest Hero's Grave":
|
||||||
[],
|
[],
|
||||||
"Forest Grave Path Main":
|
"Forest Grave Path Main":
|
||||||
[["NMG"]],
|
[["IG1"]],
|
||||||
},
|
},
|
||||||
"Forest Hero's Grave": {
|
"Forest Hero's Grave": {
|
||||||
"Forest Grave Path by Grave":
|
"Forest Grave Path by Grave":
|
||||||
|
@ -1051,7 +1078,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
},
|
},
|
||||||
"Dark Tomb Checkpoint": {
|
"Dark Tomb Checkpoint": {
|
||||||
"Well Boss":
|
"Well Boss":
|
||||||
[["Hyperdash", "NMG"]],
|
[["Hyperdash", "Zip"]],
|
||||||
},
|
},
|
||||||
"Dark Tomb Entry Point": {
|
"Dark Tomb Entry Point": {
|
||||||
"Dark Tomb Upper":
|
"Dark Tomb Upper":
|
||||||
|
@ -1075,13 +1102,13 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
},
|
},
|
||||||
"West Garden": {
|
"West Garden": {
|
||||||
"West Garden Laurels Exit Region":
|
"West Garden Laurels Exit Region":
|
||||||
[["Hyperdash"], ["UR"]],
|
[["Hyperdash"], ["LS1"]],
|
||||||
"West Garden after Boss":
|
"West Garden after Boss":
|
||||||
[],
|
[],
|
||||||
"West Garden Hero's Grave Region":
|
"West Garden Hero's Grave Region":
|
||||||
[],
|
[],
|
||||||
"West Garden Portal Item":
|
"West Garden Portal Item":
|
||||||
[["NMG"]],
|
[["IG2"]],
|
||||||
},
|
},
|
||||||
"West Garden Laurels Exit Region": {
|
"West Garden Laurels Exit Region": {
|
||||||
"West Garden":
|
"West Garden":
|
||||||
|
@ -1093,13 +1120,19 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
},
|
},
|
||||||
"West Garden Portal Item": {
|
"West Garden Portal Item": {
|
||||||
"West Garden":
|
"West Garden":
|
||||||
[["NMG"]],
|
[["IG1"]],
|
||||||
"West Garden Portal":
|
"West Garden by Portal":
|
||||||
[["Hyperdash", "West Garden"]],
|
[["Hyperdash"]],
|
||||||
},
|
},
|
||||||
"West Garden Portal": {
|
"West Garden by Portal": {
|
||||||
"West Garden Portal Item":
|
"West Garden Portal Item":
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"]],
|
||||||
|
"West Garden Portal":
|
||||||
|
[["West Garden"]],
|
||||||
|
},
|
||||||
|
"West Garden Portal": {
|
||||||
|
"West Garden by Portal":
|
||||||
|
[],
|
||||||
},
|
},
|
||||||
"West Garden Hero's Grave Region": {
|
"West Garden Hero's Grave Region": {
|
||||||
"West Garden":
|
"West Garden":
|
||||||
|
@ -1107,7 +1140,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
},
|
},
|
||||||
"Ruined Atoll": {
|
"Ruined Atoll": {
|
||||||
"Ruined Atoll Lower Entry Area":
|
"Ruined Atoll Lower Entry Area":
|
||||||
[["Hyperdash"], ["UR"]],
|
[["Hyperdash"], ["LS1"]],
|
||||||
"Ruined Atoll Ladder Tops":
|
"Ruined Atoll Ladder Tops":
|
||||||
[],
|
[],
|
||||||
"Ruined Atoll Frog Mouth":
|
"Ruined Atoll Frog Mouth":
|
||||||
|
@ -1174,11 +1207,17 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Library Exterior Ladder Region": {
|
"Library Exterior Ladder Region": {
|
||||||
|
"Library Exterior by Tree":
|
||||||
|
[],
|
||||||
|
},
|
||||||
|
"Library Exterior by Tree": {
|
||||||
"Library Exterior Tree Region":
|
"Library Exterior Tree Region":
|
||||||
[],
|
[],
|
||||||
|
"Library Exterior Ladder Region":
|
||||||
|
[],
|
||||||
},
|
},
|
||||||
"Library Exterior Tree Region": {
|
"Library Exterior Tree Region": {
|
||||||
"Library Exterior Ladder Region":
|
"Library Exterior by Tree":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Library Hall Bookshelf": {
|
"Library Hall Bookshelf": {
|
||||||
|
@ -1223,15 +1262,21 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Library Lab": {
|
"Library Lab": {
|
||||||
"Library Lab Lower":
|
"Library Lab Lower":
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"]],
|
||||||
"Library Portal":
|
"Library Lab on Portal Pad":
|
||||||
[],
|
[],
|
||||||
"Library Lab to Librarian":
|
"Library Lab to Librarian":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Library Portal": {
|
"Library Lab on Portal Pad": {
|
||||||
|
"Library Portal":
|
||||||
|
[],
|
||||||
"Library Lab":
|
"Library Lab":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
"Library Portal": {
|
||||||
|
"Library Lab on Portal Pad":
|
||||||
|
[],
|
||||||
|
},
|
||||||
"Library Lab to Librarian": {
|
"Library Lab to Librarian": {
|
||||||
"Library Lab":
|
"Library Lab":
|
||||||
[],
|
[],
|
||||||
|
@ -1240,11 +1285,9 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Fortress Exterior from Overworld":
|
"Fortress Exterior from Overworld":
|
||||||
[],
|
[],
|
||||||
"Fortress Courtyard Upper":
|
"Fortress Courtyard Upper":
|
||||||
[["UR"]],
|
[["LS2"]],
|
||||||
"Fortress Exterior near cave":
|
|
||||||
[["UR"]],
|
|
||||||
"Fortress Courtyard":
|
"Fortress Courtyard":
|
||||||
[["UR"]],
|
[["LS1"]],
|
||||||
},
|
},
|
||||||
"Fortress Exterior from Overworld": {
|
"Fortress Exterior from Overworld": {
|
||||||
"Fortress Exterior from East Forest":
|
"Fortress Exterior from East Forest":
|
||||||
|
@ -1252,15 +1295,15 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Fortress Exterior near cave":
|
"Fortress Exterior near cave":
|
||||||
[],
|
[],
|
||||||
"Fortress Courtyard":
|
"Fortress Courtyard":
|
||||||
[["Hyperdash"], ["NMG"]],
|
[["Hyperdash"], ["IG1"], ["LS1"]],
|
||||||
},
|
},
|
||||||
"Fortress Exterior near cave": {
|
"Fortress Exterior near cave": {
|
||||||
"Fortress Exterior from Overworld":
|
"Fortress Exterior from Overworld":
|
||||||
[["Hyperdash"], ["UR"]],
|
[["Hyperdash"], ["LS1"]],
|
||||||
"Fortress Courtyard":
|
"Fortress Courtyard": # ice grapple hard: shoot far fire pot, it aggros one of the enemies over to you
|
||||||
[["UR"]],
|
[["IG3"], ["LS1"]],
|
||||||
"Fortress Courtyard Upper":
|
"Fortress Courtyard Upper":
|
||||||
[["UR"]],
|
[["LS2"]],
|
||||||
"Beneath the Vault Entry":
|
"Beneath the Vault Entry":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
@ -1270,7 +1313,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
},
|
},
|
||||||
"Fortress Courtyard": {
|
"Fortress Courtyard": {
|
||||||
"Fortress Courtyard Upper":
|
"Fortress Courtyard Upper":
|
||||||
[["NMG"]],
|
[["IG1"]],
|
||||||
"Fortress Exterior from Overworld":
|
"Fortress Exterior from Overworld":
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"]],
|
||||||
},
|
},
|
||||||
|
@ -1296,7 +1339,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
},
|
},
|
||||||
"Fortress East Shortcut Lower": {
|
"Fortress East Shortcut Lower": {
|
||||||
"Fortress East Shortcut Upper":
|
"Fortress East Shortcut Upper":
|
||||||
[["NMG"]],
|
[["IG1"]],
|
||||||
},
|
},
|
||||||
"Fortress East Shortcut Upper": {
|
"Fortress East Shortcut Upper": {
|
||||||
"Fortress East Shortcut Lower":
|
"Fortress East Shortcut Lower":
|
||||||
|
@ -1304,11 +1347,11 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
},
|
},
|
||||||
"Eastern Vault Fortress": {
|
"Eastern Vault Fortress": {
|
||||||
"Eastern Vault Fortress Gold Door":
|
"Eastern Vault Fortress Gold Door":
|
||||||
[["NMG"], ["Fortress Exterior from Overworld", "Beneath the Vault Back", "Fortress Courtyard Upper"]],
|
[["IG2"], ["Fortress Exterior from Overworld", "Beneath the Vault Back", "Fortress Courtyard Upper"]],
|
||||||
},
|
},
|
||||||
"Eastern Vault Fortress Gold Door": {
|
"Eastern Vault Fortress Gold Door": {
|
||||||
"Eastern Vault Fortress":
|
"Eastern Vault Fortress":
|
||||||
[["NMG"]],
|
[["IG1"]],
|
||||||
},
|
},
|
||||||
"Fortress Grave Path": {
|
"Fortress Grave Path": {
|
||||||
"Fortress Hero's Grave Region":
|
"Fortress Hero's Grave Region":
|
||||||
|
@ -1318,7 +1361,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
},
|
},
|
||||||
"Fortress Grave Path Upper": {
|
"Fortress Grave Path Upper": {
|
||||||
"Fortress Grave Path":
|
"Fortress Grave Path":
|
||||||
[["NMG"]],
|
[["IG1"]],
|
||||||
},
|
},
|
||||||
"Fortress Grave Path Dusty Entrance Region": {
|
"Fortress Grave Path Dusty Entrance Region": {
|
||||||
"Fortress Grave Path":
|
"Fortress Grave Path":
|
||||||
|
@ -1346,7 +1389,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
},
|
},
|
||||||
"Monastery Back": {
|
"Monastery Back": {
|
||||||
"Monastery Front":
|
"Monastery Front":
|
||||||
[["Hyperdash", "NMG"]],
|
[["Hyperdash", "Zip"]],
|
||||||
"Monastery Hero's Grave Region":
|
"Monastery Hero's Grave Region":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
@ -1363,6 +1406,8 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
[["Quarry Connector"]],
|
[["Quarry Connector"]],
|
||||||
"Quarry":
|
"Quarry":
|
||||||
[],
|
[],
|
||||||
|
"Monastery Rope":
|
||||||
|
[["LS2"]],
|
||||||
},
|
},
|
||||||
"Quarry Portal": {
|
"Quarry Portal": {
|
||||||
"Quarry Entry":
|
"Quarry Entry":
|
||||||
|
@ -1374,7 +1419,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Quarry Back":
|
"Quarry Back":
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"]],
|
||||||
"Monastery Rope":
|
"Monastery Rope":
|
||||||
[["UR"]],
|
[["LS2"]],
|
||||||
},
|
},
|
||||||
"Quarry Back": {
|
"Quarry Back": {
|
||||||
"Quarry":
|
"Quarry":
|
||||||
|
@ -1392,7 +1437,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Quarry Monastery Entry":
|
"Quarry Monastery Entry":
|
||||||
[],
|
[],
|
||||||
"Lower Quarry Zig Door":
|
"Lower Quarry Zig Door":
|
||||||
[["NMG"]],
|
[["IG3"]],
|
||||||
},
|
},
|
||||||
"Lower Quarry": {
|
"Lower Quarry": {
|
||||||
"Even Lower Quarry":
|
"Even Lower Quarry":
|
||||||
|
@ -1402,7 +1447,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Lower Quarry":
|
"Lower Quarry":
|
||||||
[],
|
[],
|
||||||
"Lower Quarry Zig Door":
|
"Lower Quarry Zig Door":
|
||||||
[["Quarry", "Quarry Connector"], ["NMG"]],
|
[["Quarry", "Quarry Connector"], ["IG3"]],
|
||||||
},
|
},
|
||||||
"Monastery Rope": {
|
"Monastery Rope": {
|
||||||
"Quarry Back":
|
"Quarry Back":
|
||||||
|
@ -1430,7 +1475,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
},
|
},
|
||||||
"Rooted Ziggurat Lower Back": {
|
"Rooted Ziggurat Lower Back": {
|
||||||
"Rooted Ziggurat Lower Front":
|
"Rooted Ziggurat Lower Front":
|
||||||
[["Hyperdash"], ["UR"]],
|
[["Hyperdash"], ["LS2"], ["IG1"]],
|
||||||
"Rooted Ziggurat Portal Room Entrance":
|
"Rooted Ziggurat Portal Room Entrance":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
@ -1443,26 +1488,35 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Rooted Ziggurat Portal Room Exit": {
|
"Rooted Ziggurat Portal Room Exit": {
|
||||||
"Rooted Ziggurat Portal":
|
"Rooted Ziggurat Portal Room":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Rooted Ziggurat Portal": {
|
"Rooted Ziggurat Portal Room": {
|
||||||
|
"Rooted Ziggurat Portal":
|
||||||
|
[],
|
||||||
"Rooted Ziggurat Portal Room Exit":
|
"Rooted Ziggurat Portal Room Exit":
|
||||||
[["Rooted Ziggurat Lower Back"]],
|
[["Rooted Ziggurat Lower Back"]],
|
||||||
},
|
},
|
||||||
|
"Rooted Ziggurat Portal": {
|
||||||
|
"Rooted Ziggurat Portal Room":
|
||||||
|
[],
|
||||||
|
},
|
||||||
"Swamp Front": {
|
"Swamp Front": {
|
||||||
"Swamp Mid":
|
"Swamp Mid":
|
||||||
[],
|
[],
|
||||||
|
# get one pillar from the gate, then dash onto the gate, very tricky
|
||||||
|
"Back of Swamp Laurels Area":
|
||||||
|
[["Hyperdash", "Zip"]],
|
||||||
},
|
},
|
||||||
"Swamp Mid": {
|
"Swamp Mid": {
|
||||||
"Swamp Front":
|
"Swamp Front":
|
||||||
[],
|
[],
|
||||||
"Swamp to Cathedral Main Entrance Region":
|
"Swamp to Cathedral Main Entrance Region":
|
||||||
[["Hyperdash"], ["NMG"]],
|
[["Hyperdash"], ["IG2"], ["LS3"]],
|
||||||
"Swamp Ledge under Cathedral Door":
|
"Swamp Ledge under Cathedral Door":
|
||||||
[],
|
[],
|
||||||
"Back of Swamp":
|
"Back of Swamp":
|
||||||
[["UR"]],
|
[["LS1"]], # ig3 later?
|
||||||
},
|
},
|
||||||
"Swamp Ledge under Cathedral Door": {
|
"Swamp Ledge under Cathedral Door": {
|
||||||
"Swamp Mid":
|
"Swamp Mid":
|
||||||
|
@ -1476,24 +1530,41 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
},
|
},
|
||||||
"Swamp to Cathedral Main Entrance Region": {
|
"Swamp to Cathedral Main Entrance Region": {
|
||||||
"Swamp Mid":
|
"Swamp Mid":
|
||||||
[["NMG"]],
|
[["IG1"]],
|
||||||
},
|
},
|
||||||
"Back of Swamp": {
|
"Back of Swamp": {
|
||||||
"Back of Swamp Laurels Area":
|
"Back of Swamp Laurels Area":
|
||||||
[["Hyperdash"], ["UR"]],
|
[["Hyperdash"], ["LS2"]],
|
||||||
"Swamp Hero's Grave Region":
|
"Swamp Hero's Grave Region":
|
||||||
[],
|
[],
|
||||||
|
"Swamp Mid":
|
||||||
|
[["LS2"]],
|
||||||
|
"Swamp Front":
|
||||||
|
[["LS1"]],
|
||||||
|
"Swamp to Cathedral Main Entrance Region":
|
||||||
|
[["LS3"]],
|
||||||
|
"Swamp to Cathedral Treasure Room":
|
||||||
|
[["LS3"]]
|
||||||
},
|
},
|
||||||
"Back of Swamp Laurels Area": {
|
"Back of Swamp Laurels Area": {
|
||||||
"Back of Swamp":
|
"Back of Swamp":
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"]],
|
||||||
|
# get one pillar from the gate, then dash onto the gate, very tricky
|
||||||
"Swamp Mid":
|
"Swamp Mid":
|
||||||
[["NMG", "Hyperdash"]],
|
[["IG1", "Hyperdash"], ["Hyperdash", "Zip"]],
|
||||||
},
|
},
|
||||||
"Swamp Hero's Grave Region": {
|
"Swamp Hero's Grave Region": {
|
||||||
"Back of Swamp":
|
"Back of Swamp":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
"Cathedral": {
|
||||||
|
"Cathedral to Gauntlet":
|
||||||
|
[],
|
||||||
|
},
|
||||||
|
"Cathedral to Gauntlet": {
|
||||||
|
"Cathedral":
|
||||||
|
[],
|
||||||
|
},
|
||||||
"Cathedral Gauntlet Checkpoint": {
|
"Cathedral Gauntlet Checkpoint": {
|
||||||
"Cathedral Gauntlet":
|
"Cathedral Gauntlet":
|
||||||
[],
|
[],
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
||||||
from typing import Dict, List, Set, TYPE_CHECKING
|
from typing import Dict, List, Set, Tuple, TYPE_CHECKING
|
||||||
from BaseClasses import Region, ItemClassification, Item, Location
|
from BaseClasses import Region, ItemClassification, Item, Location
|
||||||
from .locations import location_table
|
from .locations import location_table
|
||||||
from .er_data import Portal, tunic_er_regions, portal_mapping, traversal_requirements, DeadEnd
|
from .er_data import Portal, portal_mapping, traversal_requirements, DeadEnd, RegionInfo
|
||||||
from .er_rules import set_er_region_rules
|
from .er_rules import set_er_region_rules
|
||||||
from Options import PlandoConnection
|
from Options import PlandoConnection
|
||||||
from .options import EntranceRando
|
from .options import EntranceRando
|
||||||
|
@ -22,17 +22,18 @@ class TunicERLocation(Location):
|
||||||
|
|
||||||
def create_er_regions(world: "TunicWorld") -> Dict[Portal, Portal]:
|
def create_er_regions(world: "TunicWorld") -> Dict[Portal, Portal]:
|
||||||
regions: Dict[str, Region] = {}
|
regions: Dict[str, Region] = {}
|
||||||
|
for region_name, region_data in world.er_regions.items():
|
||||||
|
regions[region_name] = Region(region_name, world.player, world.multiworld)
|
||||||
|
|
||||||
if world.options.entrance_rando:
|
if world.options.entrance_rando:
|
||||||
portal_pairs = pair_portals(world)
|
portal_pairs = pair_portals(world, regions)
|
||||||
|
|
||||||
# output the entrances to the spoiler log here for convenience
|
# output the entrances to the spoiler log here for convenience
|
||||||
sorted_portal_pairs = sort_portals(portal_pairs)
|
sorted_portal_pairs = sort_portals(portal_pairs)
|
||||||
for portal1, portal2 in sorted_portal_pairs.items():
|
for portal1, portal2 in sorted_portal_pairs.items():
|
||||||
world.multiworld.spoiler.set_entrance(portal1, portal2, "both", world.player)
|
world.multiworld.spoiler.set_entrance(portal1, portal2, "both", world.player)
|
||||||
else:
|
else:
|
||||||
portal_pairs = vanilla_portals()
|
portal_pairs = vanilla_portals(world, regions)
|
||||||
|
|
||||||
for region_name, region_data in tunic_er_regions.items():
|
|
||||||
regions[region_name] = Region(region_name, world.player, world.multiworld)
|
|
||||||
|
|
||||||
set_er_region_rules(world, regions, portal_pairs)
|
set_er_region_rules(world, regions, portal_pairs)
|
||||||
|
|
||||||
|
@ -93,7 +94,18 @@ def place_event_items(world: "TunicWorld", regions: Dict[str, Region]) -> None:
|
||||||
region.locations.append(location)
|
region.locations.append(location)
|
||||||
|
|
||||||
|
|
||||||
def vanilla_portals() -> Dict[Portal, Portal]:
|
# all shops are the same shop. however, you cannot get to all shops from the same shop entrance.
|
||||||
|
# so, we need a bunch of shop regions that connect to the actual shop, but the actual shop cannot connect back
|
||||||
|
def create_shop_region(world: "TunicWorld", regions: Dict[str, Region]) -> None:
|
||||||
|
new_shop_name = f"Shop {world.shop_num}"
|
||||||
|
world.er_regions[new_shop_name] = RegionInfo("Shop", dead_end=DeadEnd.all_cats)
|
||||||
|
new_shop_region = Region(new_shop_name, world.player, world.multiworld)
|
||||||
|
new_shop_region.connect(regions["Shop"])
|
||||||
|
regions[new_shop_name] = new_shop_region
|
||||||
|
world.shop_num += 1
|
||||||
|
|
||||||
|
|
||||||
|
def vanilla_portals(world: "TunicWorld", regions: Dict[str, Region]) -> Dict[Portal, Portal]:
|
||||||
portal_pairs: Dict[Portal, Portal] = {}
|
portal_pairs: Dict[Portal, Portal] = {}
|
||||||
# we don't want the zig skip exit for vanilla portals, since it shouldn't be considered for logic here
|
# we don't want the zig skip exit for vanilla portals, since it shouldn't be considered for logic here
|
||||||
portal_map = [portal for portal in portal_mapping if portal.name != "Ziggurat Lower Falling Entrance"]
|
portal_map = [portal for portal in portal_mapping if portal.name != "Ziggurat Lower Falling Entrance"]
|
||||||
|
@ -105,8 +117,9 @@ def vanilla_portals() -> Dict[Portal, Portal]:
|
||||||
portal2_sdt = portal1.destination_scene()
|
portal2_sdt = portal1.destination_scene()
|
||||||
|
|
||||||
if portal2_sdt.startswith("Shop,"):
|
if portal2_sdt.startswith("Shop,"):
|
||||||
portal2 = Portal(name="Shop", region="Shop",
|
portal2 = Portal(name=f"Shop Portal {world.shop_num}", region=f"Shop {world.shop_num}",
|
||||||
destination="Previous Region", tag="_")
|
destination="Previous Region", tag="_")
|
||||||
|
create_shop_region(world, regions)
|
||||||
|
|
||||||
elif portal2_sdt == "Purgatory, Purgatory_bottom":
|
elif portal2_sdt == "Purgatory, Purgatory_bottom":
|
||||||
portal2_sdt = "Purgatory, Purgatory_top"
|
portal2_sdt = "Purgatory, Purgatory_top"
|
||||||
|
@ -125,14 +138,15 @@ def vanilla_portals() -> Dict[Portal, Portal]:
|
||||||
|
|
||||||
|
|
||||||
# pairing off portals, starting with dead ends
|
# pairing off portals, starting with dead ends
|
||||||
def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
|
def pair_portals(world: "TunicWorld", regions: Dict[str, Region]) -> Dict[Portal, Portal]:
|
||||||
# separate the portals into dead ends and non-dead ends
|
|
||||||
portal_pairs: Dict[Portal, Portal] = {}
|
portal_pairs: Dict[Portal, Portal] = {}
|
||||||
dead_ends: List[Portal] = []
|
dead_ends: List[Portal] = []
|
||||||
two_plus: List[Portal] = []
|
two_plus: List[Portal] = []
|
||||||
player_name = world.player_name
|
player_name = world.player_name
|
||||||
portal_map = portal_mapping.copy()
|
portal_map = portal_mapping.copy()
|
||||||
logic_rules = world.options.logic_rules.value
|
laurels_zips = world.options.laurels_zips.value
|
||||||
|
ice_grappling = world.options.ice_grappling.value
|
||||||
|
ladder_storage = world.options.ladder_storage.value
|
||||||
fixed_shop = world.options.fixed_shop
|
fixed_shop = world.options.fixed_shop
|
||||||
laurels_location = world.options.laurels_location
|
laurels_location = world.options.laurels_location
|
||||||
traversal_reqs = deepcopy(traversal_requirements)
|
traversal_reqs = deepcopy(traversal_requirements)
|
||||||
|
@ -142,19 +156,21 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
|
||||||
# if it's not one of the EntranceRando options, it's a custom seed
|
# if it's not one of the EntranceRando options, it's a custom seed
|
||||||
if world.options.entrance_rando.value not in EntranceRando.options.values():
|
if world.options.entrance_rando.value not in EntranceRando.options.values():
|
||||||
seed_group = world.seed_groups[world.options.entrance_rando.value]
|
seed_group = world.seed_groups[world.options.entrance_rando.value]
|
||||||
logic_rules = seed_group["logic_rules"]
|
laurels_zips = seed_group["laurels_zips"]
|
||||||
|
ice_grappling = seed_group["ice_grappling"]
|
||||||
|
ladder_storage = seed_group["ladder_storage"]
|
||||||
fixed_shop = seed_group["fixed_shop"]
|
fixed_shop = seed_group["fixed_shop"]
|
||||||
laurels_location = "10_fairies" if seed_group["laurels_at_10_fairies"] is True else False
|
laurels_location = "10_fairies" if seed_group["laurels_at_10_fairies"] is True else False
|
||||||
|
|
||||||
|
logic_tricks: Tuple[bool, int, int] = (laurels_zips, ice_grappling, ladder_storage)
|
||||||
|
|
||||||
# marking that you don't immediately have laurels
|
# marking that you don't immediately have laurels
|
||||||
if laurels_location == "10_fairies" and not hasattr(world.multiworld, "re_gen_passthrough"):
|
if laurels_location == "10_fairies" and not hasattr(world.multiworld, "re_gen_passthrough"):
|
||||||
has_laurels = False
|
has_laurels = False
|
||||||
|
|
||||||
shop_scenes: Set[str] = set()
|
|
||||||
shop_count = 6
|
shop_count = 6
|
||||||
if fixed_shop:
|
if fixed_shop:
|
||||||
shop_count = 0
|
shop_count = 0
|
||||||
shop_scenes.add("Overworld Redux")
|
|
||||||
else:
|
else:
|
||||||
# if fixed shop is off, remove this portal
|
# if fixed shop is off, remove this portal
|
||||||
for portal in portal_map:
|
for portal in portal_map:
|
||||||
|
@ -169,13 +185,13 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
|
||||||
|
|
||||||
# create separate lists for dead ends and non-dead ends
|
# create separate lists for dead ends and non-dead ends
|
||||||
for portal in portal_map:
|
for portal in portal_map:
|
||||||
dead_end_status = tunic_er_regions[portal.region].dead_end
|
dead_end_status = world.er_regions[portal.region].dead_end
|
||||||
if dead_end_status == DeadEnd.free:
|
if dead_end_status == DeadEnd.free:
|
||||||
two_plus.append(portal)
|
two_plus.append(portal)
|
||||||
elif dead_end_status == DeadEnd.all_cats:
|
elif dead_end_status == DeadEnd.all_cats:
|
||||||
dead_ends.append(portal)
|
dead_ends.append(portal)
|
||||||
elif dead_end_status == DeadEnd.restricted:
|
elif dead_end_status == DeadEnd.restricted:
|
||||||
if logic_rules:
|
if ice_grappling:
|
||||||
two_plus.append(portal)
|
two_plus.append(portal)
|
||||||
else:
|
else:
|
||||||
dead_ends.append(portal)
|
dead_ends.append(portal)
|
||||||
|
@ -196,7 +212,7 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
|
||||||
# make better start region stuff when/if implementing random start
|
# make better start region stuff when/if implementing random start
|
||||||
start_region = "Overworld"
|
start_region = "Overworld"
|
||||||
connected_regions.add(start_region)
|
connected_regions.add(start_region)
|
||||||
connected_regions = update_reachable_regions(connected_regions, traversal_reqs, has_laurels, logic_rules)
|
connected_regions = update_reachable_regions(connected_regions, traversal_reqs, has_laurels, logic_tricks)
|
||||||
|
|
||||||
if world.options.entrance_rando.value in EntranceRando.options.values():
|
if world.options.entrance_rando.value in EntranceRando.options.values():
|
||||||
plando_connections = world.options.plando_connections.value
|
plando_connections = world.options.plando_connections.value
|
||||||
|
@ -225,12 +241,14 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
|
||||||
plando_connections.append(PlandoConnection(portal_name1, portal_name2, "both"))
|
plando_connections.append(PlandoConnection(portal_name1, portal_name2, "both"))
|
||||||
|
|
||||||
non_dead_end_regions = set()
|
non_dead_end_regions = set()
|
||||||
for region_name, region_info in tunic_er_regions.items():
|
for region_name, region_info in world.er_regions.items():
|
||||||
if not region_info.dead_end:
|
if not region_info.dead_end:
|
||||||
non_dead_end_regions.add(region_name)
|
non_dead_end_regions.add(region_name)
|
||||||
elif region_info.dead_end == 2 and logic_rules:
|
# if ice grappling to places is in logic, both places stop being dead ends
|
||||||
|
elif region_info.dead_end == DeadEnd.restricted and ice_grappling:
|
||||||
non_dead_end_regions.add(region_name)
|
non_dead_end_regions.add(region_name)
|
||||||
elif region_info.dead_end == 3:
|
# secret gathering place and zig skip get weird, special handling
|
||||||
|
elif region_info.dead_end == DeadEnd.special:
|
||||||
if (region_name == "Secret Gathering Place" and laurels_location == "10_fairies") \
|
if (region_name == "Secret Gathering Place" and laurels_location == "10_fairies") \
|
||||||
or (region_name == "Zig Skip Exit" and fixed_shop):
|
or (region_name == "Zig Skip Exit" and fixed_shop):
|
||||||
non_dead_end_regions.add(region_name)
|
non_dead_end_regions.add(region_name)
|
||||||
|
@ -239,6 +257,9 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
|
||||||
for connection in plando_connections:
|
for connection in plando_connections:
|
||||||
p_entrance = connection.entrance
|
p_entrance = connection.entrance
|
||||||
p_exit = connection.exit
|
p_exit = connection.exit
|
||||||
|
# if you plando secret gathering place, need to know that during portal pairing
|
||||||
|
if "Secret Gathering Place Exit" in [p_entrance, p_exit]:
|
||||||
|
waterfall_plando = True
|
||||||
portal1_dead_end = True
|
portal1_dead_end = True
|
||||||
portal2_dead_end = True
|
portal2_dead_end = True
|
||||||
|
|
||||||
|
@ -285,16 +306,13 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
|
||||||
break
|
break
|
||||||
# if it's not a dead end, it might be a shop
|
# if it's not a dead end, it might be a shop
|
||||||
if p_exit == "Shop Portal":
|
if p_exit == "Shop Portal":
|
||||||
portal2 = Portal(name="Shop Portal", region="Shop",
|
portal2 = Portal(name=f"Shop Portal {world.shop_num}", region=f"Shop {world.shop_num}",
|
||||||
destination="Previous Region", tag="_")
|
destination="Previous Region", tag="_")
|
||||||
|
create_shop_region(world, regions)
|
||||||
shop_count -= 1
|
shop_count -= 1
|
||||||
# need to maintain an even number of portals total
|
# need to maintain an even number of portals total
|
||||||
if shop_count < 0:
|
if shop_count < 0:
|
||||||
shop_count += 2
|
shop_count += 2
|
||||||
for p in portal_mapping:
|
|
||||||
if p.name == p_entrance:
|
|
||||||
shop_scenes.add(p.scene())
|
|
||||||
break
|
|
||||||
# and if it's neither shop nor dead end, it just isn't correct
|
# and if it's neither shop nor dead end, it just isn't correct
|
||||||
else:
|
else:
|
||||||
if not portal2:
|
if not portal2:
|
||||||
|
@ -327,11 +345,10 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
|
||||||
else:
|
else:
|
||||||
raise Exception(f"{player_name} paired a dead end to a dead end in their "
|
raise Exception(f"{player_name} paired a dead end to a dead end in their "
|
||||||
"plando connections.")
|
"plando connections.")
|
||||||
waterfall_plando = True
|
|
||||||
portal_pairs[portal1] = portal2
|
portal_pairs[portal1] = portal2
|
||||||
|
|
||||||
# if we have plando connections, our connected regions may change somewhat
|
# if we have plando connections, our connected regions may change somewhat
|
||||||
connected_regions = update_reachable_regions(connected_regions, traversal_reqs, has_laurels, logic_rules)
|
connected_regions = update_reachable_regions(connected_regions, traversal_reqs, has_laurels, logic_tricks)
|
||||||
|
|
||||||
if fixed_shop and not hasattr(world.multiworld, "re_gen_passthrough"):
|
if fixed_shop and not hasattr(world.multiworld, "re_gen_passthrough"):
|
||||||
portal1 = None
|
portal1 = None
|
||||||
|
@ -343,7 +360,9 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
|
||||||
raise Exception(f"Failed to do Fixed Shop option. "
|
raise Exception(f"Failed to do Fixed Shop option. "
|
||||||
f"Did {player_name} plando connection the Windmill Shop entrance?")
|
f"Did {player_name} plando connection the Windmill Shop entrance?")
|
||||||
|
|
||||||
portal2 = Portal(name="Shop Portal", region="Shop", destination="Previous Region", tag="_")
|
portal2 = Portal(name=f"Shop Portal {world.shop_num}", region=f"Shop {world.shop_num}",
|
||||||
|
destination="Previous Region", tag="_")
|
||||||
|
create_shop_region(world, regions)
|
||||||
|
|
||||||
portal_pairs[portal1] = portal2
|
portal_pairs[portal1] = portal2
|
||||||
two_plus.remove(portal1)
|
two_plus.remove(portal1)
|
||||||
|
@ -393,7 +412,7 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
|
||||||
if waterfall_plando:
|
if waterfall_plando:
|
||||||
cr = connected_regions.copy()
|
cr = connected_regions.copy()
|
||||||
cr.add(portal.region)
|
cr.add(portal.region)
|
||||||
if "Secret Gathering Place" not in update_reachable_regions(cr, traversal_reqs, has_laurels, logic_rules):
|
if "Secret Gathering Place" not in update_reachable_regions(cr, traversal_reqs, has_laurels, logic_tricks):
|
||||||
continue
|
continue
|
||||||
elif portal.region != "Secret Gathering Place":
|
elif portal.region != "Secret Gathering Place":
|
||||||
continue
|
continue
|
||||||
|
@ -405,9 +424,9 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
|
||||||
|
|
||||||
# once we have both portals, connect them and add the new region(s) to connected_regions
|
# once we have both portals, connect them and add the new region(s) to connected_regions
|
||||||
if check_success == 2:
|
if check_success == 2:
|
||||||
connected_regions = update_reachable_regions(connected_regions, traversal_reqs, has_laurels, logic_rules)
|
|
||||||
if "Secret Gathering Place" in connected_regions:
|
if "Secret Gathering Place" in connected_regions:
|
||||||
has_laurels = True
|
has_laurels = True
|
||||||
|
connected_regions = update_reachable_regions(connected_regions, traversal_reqs, has_laurels, logic_tricks)
|
||||||
portal_pairs[portal1] = portal2
|
portal_pairs[portal1] = portal2
|
||||||
check_success = 0
|
check_success = 0
|
||||||
random_object.shuffle(two_plus)
|
random_object.shuffle(two_plus)
|
||||||
|
@ -418,16 +437,12 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
|
||||||
shop_count = 0
|
shop_count = 0
|
||||||
|
|
||||||
for i in range(shop_count):
|
for i in range(shop_count):
|
||||||
portal1 = None
|
portal1 = two_plus.pop()
|
||||||
for portal in two_plus:
|
|
||||||
if portal.scene() not in shop_scenes:
|
|
||||||
shop_scenes.add(portal.scene())
|
|
||||||
portal1 = portal
|
|
||||||
two_plus.remove(portal)
|
|
||||||
break
|
|
||||||
if portal1 is None:
|
if portal1 is None:
|
||||||
raise Exception("Too many shops in the pool, or something else went wrong.")
|
raise Exception("TUNIC: Too many shops in the pool, or something else went wrong.")
|
||||||
portal2 = Portal(name="Shop Portal", region="Shop", destination="Previous Region", tag="_")
|
portal2 = Portal(name=f"Shop Portal {world.shop_num}", region=f"Shop {world.shop_num}",
|
||||||
|
destination="Previous Region", tag="_")
|
||||||
|
create_shop_region(world, regions)
|
||||||
|
|
||||||
portal_pairs[portal1] = portal2
|
portal_pairs[portal1] = portal2
|
||||||
|
|
||||||
|
@ -460,13 +475,12 @@ def create_randomized_entrances(portal_pairs: Dict[Portal, Portal], regions: Dic
|
||||||
region1 = regions[portal1.region]
|
region1 = regions[portal1.region]
|
||||||
region2 = regions[portal2.region]
|
region2 = regions[portal2.region]
|
||||||
region1.connect(connecting_region=region2, name=portal1.name)
|
region1.connect(connecting_region=region2, name=portal1.name)
|
||||||
# prevent the logic from thinking you can get to any shop-connected region from the shop
|
region2.connect(connecting_region=region1, name=portal2.name)
|
||||||
if portal2.name not in {"Shop", "Shop Portal"}:
|
|
||||||
region2.connect(connecting_region=region1, name=portal2.name)
|
|
||||||
|
|
||||||
|
|
||||||
def update_reachable_regions(connected_regions: Set[str], traversal_reqs: Dict[str, Dict[str, List[List[str]]]],
|
def update_reachable_regions(connected_regions: Set[str], traversal_reqs: Dict[str, Dict[str, List[List[str]]]],
|
||||||
has_laurels: bool, logic: int) -> Set[str]:
|
has_laurels: bool, logic: Tuple[bool, int, int]) -> Set[str]:
|
||||||
|
zips, ice_grapples, ls = logic
|
||||||
# starting count, so we can run it again if this changes
|
# starting count, so we can run it again if this changes
|
||||||
region_count = len(connected_regions)
|
region_count = len(connected_regions)
|
||||||
for origin, destinations in traversal_reqs.items():
|
for origin, destinations in traversal_reqs.items():
|
||||||
|
@ -485,11 +499,15 @@ def update_reachable_regions(connected_regions: Set[str], traversal_reqs: Dict[s
|
||||||
if req == "Hyperdash":
|
if req == "Hyperdash":
|
||||||
if not has_laurels:
|
if not has_laurels:
|
||||||
break
|
break
|
||||||
elif req == "NMG":
|
elif req == "Zip":
|
||||||
if not logic:
|
if not zips:
|
||||||
break
|
break
|
||||||
elif req == "UR":
|
# if req is higher than logic option, then it breaks since it's not a valid connection
|
||||||
if logic < 2:
|
elif req.startswith("IG"):
|
||||||
|
if int(req[-1]) > ice_grapples:
|
||||||
|
break
|
||||||
|
elif req.startswith("LS"):
|
||||||
|
if int(req[-1]) > ls:
|
||||||
break
|
break
|
||||||
elif req not in connected_regions:
|
elif req not in connected_regions:
|
||||||
break
|
break
|
||||||
|
|
|
@ -166,6 +166,7 @@ item_table: Dict[str, TunicItemData] = {
|
||||||
"Ladders in Swamp": TunicItemData(ItemClassification.progression, 0, 150, "Ladders"),
|
"Ladders in Swamp": TunicItemData(ItemClassification.progression, 0, 150, "Ladders"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# items to be replaced by fool traps
|
||||||
fool_tiers: List[List[str]] = [
|
fool_tiers: List[List[str]] = [
|
||||||
[],
|
[],
|
||||||
["Money x1", "Money x10", "Money x15", "Money x16"],
|
["Money x1", "Money x10", "Money x15", "Money x16"],
|
||||||
|
@ -173,6 +174,7 @@ fool_tiers: List[List[str]] = [
|
||||||
["Money x1", "Money x10", "Money x15", "Money x16", "Money x20", "Money x25", "Money x30"],
|
["Money x1", "Money x10", "Money x15", "Money x16", "Money x20", "Money x25", "Money x30"],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# items we'll want the location of in slot data, for generating in-game hints
|
||||||
slot_data_item_names = [
|
slot_data_item_names = [
|
||||||
"Stick",
|
"Stick",
|
||||||
"Sword",
|
"Sword",
|
||||||
|
|
|
@ -0,0 +1,186 @@
|
||||||
|
from typing import Dict, List, Set, NamedTuple, Optional
|
||||||
|
|
||||||
|
|
||||||
|
# ladders in overworld, since it is the most complex area for ladder storage
|
||||||
|
class OWLadderInfo(NamedTuple):
|
||||||
|
ladders: Set[str] # ladders where the top or bottom is at the same elevation
|
||||||
|
portals: List[str] # portals at the same elevation, only those without doors
|
||||||
|
regions: List[str] # regions where a melee enemy can hit you out of ladder storage
|
||||||
|
|
||||||
|
|
||||||
|
# groups for ladders at the same elevation, for use in determing whether you can ls to entrances in diff rulesets
|
||||||
|
ow_ladder_groups: Dict[str, OWLadderInfo] = {
|
||||||
|
# lowest elevation
|
||||||
|
"LS Elev 0": OWLadderInfo({"Ladders in Overworld Town", "Ladder to Ruined Atoll", "Ladder to Swamp"},
|
||||||
|
["Swamp Redux 2_conduit", "Overworld Cave_", "Atoll Redux_lower", "Maze Room_",
|
||||||
|
"Town Basement_beach", "Archipelagos Redux_lower", "Archipelagos Redux_lowest"],
|
||||||
|
["Overworld Beach"]),
|
||||||
|
# also the east filigree room
|
||||||
|
"LS Elev 1": OWLadderInfo({"Ladders near Weathervane", "Ladders in Overworld Town", "Ladder to Swamp"},
|
||||||
|
["Furnace_gyro_lower", "Swamp Redux 2_wall"],
|
||||||
|
["Overworld Tunnel Turret"]),
|
||||||
|
# also the fountain filigree room and ruined passage door
|
||||||
|
"LS Elev 2": OWLadderInfo({"Ladders near Weathervane", "Ladders to West Bell"},
|
||||||
|
["Archipelagos Redux_upper", "Ruins Passage_east"],
|
||||||
|
["After Ruined Passage"]),
|
||||||
|
# also old house door
|
||||||
|
"LS Elev 3": OWLadderInfo({"Ladders near Weathervane", "Ladder to Quarry", "Ladders to West Bell",
|
||||||
|
"Ladders in Overworld Town"},
|
||||||
|
[],
|
||||||
|
["Overworld after Envoy", "East Overworld"]),
|
||||||
|
# skip top of top ladder next to weathervane level, does not provide logical access to anything
|
||||||
|
"LS Elev 4": OWLadderInfo({"Ladders near Dark Tomb", "Ladder to Quarry", "Ladders to West Bell", "Ladders in Well",
|
||||||
|
"Ladders in Overworld Town"},
|
||||||
|
["Darkwoods Tunnel_"],
|
||||||
|
[]),
|
||||||
|
"LS Elev 5": OWLadderInfo({"Ladders near Overworld Checkpoint", "Ladders near Patrol Cave"},
|
||||||
|
["PatrolCave_", "Forest Belltower_", "Fortress Courtyard_", "ShopSpecial_"],
|
||||||
|
["East Overworld"]),
|
||||||
|
# skip top of belltower, middle of dark tomb ladders, and top of checkpoint, does not grant access to anything
|
||||||
|
"LS Elev 6": OWLadderInfo({"Ladders near Patrol Cave", "Ladder near Temple Rafters"},
|
||||||
|
["Temple_rafters"],
|
||||||
|
["Overworld above Patrol Cave"]),
|
||||||
|
# in-line with the chest above dark tomb, gets you up the mountain stairs
|
||||||
|
"LS Elev 7": OWLadderInfo({"Ladders near Patrol Cave", "Ladder near Temple Rafters", "Ladders near Dark Tomb"},
|
||||||
|
["Mountain_"],
|
||||||
|
["Upper Overworld"]),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ladders accessible within different regions of overworld, only those that are relevant
|
||||||
|
# other scenes will just have them hardcoded since this type of structure is not necessary there
|
||||||
|
region_ladders: Dict[str, Set[str]] = {
|
||||||
|
"Overworld": {"Ladders near Weathervane", "Ladders near Overworld Checkpoint", "Ladders near Dark Tomb",
|
||||||
|
"Ladders in Overworld Town", "Ladder to Swamp", "Ladders in Well"},
|
||||||
|
"Overworld Beach": {"Ladder to Ruined Atoll"},
|
||||||
|
"Overworld at Patrol Cave": {"Ladders near Patrol Cave"},
|
||||||
|
"Overworld Quarry Entry": {"Ladder to Quarry"},
|
||||||
|
"Overworld Belltower": {"Ladders to West Bell"},
|
||||||
|
"Overworld after Temple Rafters": {"Ladders near Temple Rafters"},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class LadderInfo(NamedTuple):
|
||||||
|
origin: str # origin region
|
||||||
|
destination: str # destination portal
|
||||||
|
ladders_req: Optional[str] = None # ladders required to do this
|
||||||
|
dest_is_region: bool = False # whether it is a region that you are going to
|
||||||
|
|
||||||
|
|
||||||
|
easy_ls: List[LadderInfo] = [
|
||||||
|
# In the furnace
|
||||||
|
# Furnace ladder to the fuse entrance
|
||||||
|
LadderInfo("Furnace Ladder Area", "Furnace, Overworld Redux_gyro_upper_north"),
|
||||||
|
# Furnace ladder to Dark Tomb
|
||||||
|
LadderInfo("Furnace Ladder Area", "Furnace, Crypt Redux_"),
|
||||||
|
# Furnace ladder to the West Garden connector
|
||||||
|
LadderInfo("Furnace Ladder Area", "Furnace, Overworld Redux_gyro_west"),
|
||||||
|
|
||||||
|
# West Garden
|
||||||
|
# exit after Garden Knight
|
||||||
|
LadderInfo("West Garden", "Archipelagos Redux, Overworld Redux_upper"),
|
||||||
|
# West Garden laurels exit
|
||||||
|
LadderInfo("West Garden", "Archipelagos Redux, Overworld Redux_lowest"),
|
||||||
|
|
||||||
|
# Atoll, use the little ladder you fix at the beginning
|
||||||
|
LadderInfo("Ruined Atoll", "Atoll Redux, Overworld Redux_lower"),
|
||||||
|
LadderInfo("Ruined Atoll", "Atoll Redux, Frog Stairs_mouth"), # special case
|
||||||
|
|
||||||
|
# East Forest
|
||||||
|
# Entrance by the dancing fox holy cross spot
|
||||||
|
LadderInfo("East Forest", "East Forest Redux, East Forest Redux Laddercave_upper"),
|
||||||
|
|
||||||
|
# From the west side of Guard House 1 to the east side
|
||||||
|
LadderInfo("Guard House 1 West", "East Forest Redux Laddercave, East Forest Redux_gate"),
|
||||||
|
LadderInfo("Guard House 1 West", "East Forest Redux Laddercave, Forest Boss Room_"),
|
||||||
|
|
||||||
|
# Fortress Exterior
|
||||||
|
# shop, ls at the ladder by the telescope
|
||||||
|
LadderInfo("Fortress Exterior from Overworld", "Fortress Courtyard, Shop_"),
|
||||||
|
# Fortress main entry and grave path lower entry, ls at the ladder by the telescope
|
||||||
|
LadderInfo("Fortress Exterior from Overworld", "Fortress Courtyard, Fortress Main_Big Door"),
|
||||||
|
LadderInfo("Fortress Exterior from Overworld", "Fortress Courtyard, Fortress Reliquary_Lower"),
|
||||||
|
# Use the top of the ladder by the telescope
|
||||||
|
LadderInfo("Fortress Exterior from Overworld", "Fortress Courtyard, Fortress Reliquary_Upper"),
|
||||||
|
LadderInfo("Fortress Exterior from Overworld", "Fortress Courtyard, Fortress East_"),
|
||||||
|
|
||||||
|
# same as above, except from the east side of the area
|
||||||
|
LadderInfo("Fortress Exterior from East Forest", "Fortress Courtyard, Overworld Redux_"),
|
||||||
|
LadderInfo("Fortress Exterior from East Forest", "Fortress Courtyard, Shop_"),
|
||||||
|
LadderInfo("Fortress Exterior from East Forest", "Fortress Courtyard, Fortress Main_Big Door"),
|
||||||
|
LadderInfo("Fortress Exterior from East Forest", "Fortress Courtyard, Fortress Reliquary_Lower"),
|
||||||
|
|
||||||
|
# same as above, except from the Beneath the Vault entrance ladder
|
||||||
|
LadderInfo("Fortress Exterior near cave", "Fortress Courtyard, Overworld Redux_", "Ladder to Beneath the Vault"),
|
||||||
|
LadderInfo("Fortress Exterior near cave", "Fortress Courtyard, Fortress Main_Big Door",
|
||||||
|
"Ladder to Beneath the Vault"),
|
||||||
|
LadderInfo("Fortress Exterior near cave", "Fortress Courtyard, Fortress Reliquary_Lower",
|
||||||
|
"Ladder to Beneath the Vault"),
|
||||||
|
|
||||||
|
# Swamp to Gauntlet
|
||||||
|
LadderInfo("Swamp Mid", "Swamp Redux 2, Cathedral Arena_", "Ladders in Swamp"),
|
||||||
|
|
||||||
|
# Ladder by the hero grave
|
||||||
|
LadderInfo("Back of Swamp", "Swamp Redux 2, Overworld Redux_conduit"),
|
||||||
|
LadderInfo("Back of Swamp", "Swamp Redux 2, Shop_"),
|
||||||
|
]
|
||||||
|
|
||||||
|
# if we can gain elevation or get knocked down, add the harder ones
|
||||||
|
medium_ls: List[LadderInfo] = [
|
||||||
|
# region-destination versions of easy ls spots
|
||||||
|
LadderInfo("East Forest", "East Forest Dance Fox Spot", dest_is_region=True),
|
||||||
|
# fortress courtyard knockdowns are never logically relevant, the fuse requires upper
|
||||||
|
LadderInfo("Back of Swamp", "Swamp Mid", dest_is_region=True),
|
||||||
|
LadderInfo("Back of Swamp", "Swamp Front", dest_is_region=True),
|
||||||
|
|
||||||
|
# gain height off the northeast fuse ramp
|
||||||
|
LadderInfo("Ruined Atoll", "Atoll Redux, Frog Stairs_eye"),
|
||||||
|
|
||||||
|
# Upper exit from the Forest Grave Path, use LS at the ladder by the gate switch
|
||||||
|
LadderInfo("Forest Grave Path Main", "Sword Access, East Forest Redux_upper"),
|
||||||
|
|
||||||
|
# Upper exits from the courtyard. Use the ramp in the courtyard, then the blocks north of the first fuse
|
||||||
|
LadderInfo("Fortress Exterior from Overworld", "Fortress Courtyard Upper", dest_is_region=True),
|
||||||
|
LadderInfo("Fortress Exterior from East Forest", "Fortress Courtyard, Fortress Reliquary_Upper"),
|
||||||
|
LadderInfo("Fortress Exterior from East Forest", "Fortress Courtyard, Fortress East_"),
|
||||||
|
LadderInfo("Fortress Exterior from East Forest", "Fortress Courtyard Upper", dest_is_region=True),
|
||||||
|
LadderInfo("Fortress Exterior near cave", "Fortress Courtyard, Fortress Reliquary_Upper",
|
||||||
|
"Ladder to Beneath the Vault"),
|
||||||
|
LadderInfo("Fortress Exterior near cave", "Fortress Courtyard, Fortress East_", "Ladder to Beneath the Vault"),
|
||||||
|
LadderInfo("Fortress Exterior near cave", "Fortress Courtyard Upper", "Ladder to Beneath the Vault",
|
||||||
|
dest_is_region=True),
|
||||||
|
|
||||||
|
# need to gain height to get up the stairs
|
||||||
|
LadderInfo("Lower Mountain", "Mountain, Mountaintop_"),
|
||||||
|
|
||||||
|
# Where the rope is behind Monastery
|
||||||
|
LadderInfo("Quarry Entry", "Quarry Redux, Monastery_back"),
|
||||||
|
LadderInfo("Quarry Monastery Entry", "Quarry Redux, Monastery_back"),
|
||||||
|
LadderInfo("Quarry Back", "Quarry Redux, Monastery_back"),
|
||||||
|
|
||||||
|
LadderInfo("Rooted Ziggurat Lower Back", "ziggurat2020_3, ziggurat2020_2_"),
|
||||||
|
LadderInfo("Rooted Ziggurat Lower Back", "Rooted Ziggurat Lower Front", dest_is_region=True),
|
||||||
|
|
||||||
|
# Swamp to Overworld upper
|
||||||
|
LadderInfo("Swamp Mid", "Swamp Redux 2, Overworld Redux_wall", "Ladders in Swamp"),
|
||||||
|
LadderInfo("Back of Swamp", "Swamp Redux 2, Overworld Redux_wall"),
|
||||||
|
]
|
||||||
|
|
||||||
|
hard_ls: List[LadderInfo] = [
|
||||||
|
# lower ladder, go into the waterfall then above the bonfire, up a ramp, then through the right wall
|
||||||
|
LadderInfo("Beneath the Well Front", "Sewer, Sewer_Boss_", "Ladders in Well"),
|
||||||
|
LadderInfo("Beneath the Well Front", "Sewer, Overworld Redux_west_aqueduct", "Ladders in Well"),
|
||||||
|
LadderInfo("Beneath the Well Front", "Beneath the Well Back", "Ladders in Well", dest_is_region=True),
|
||||||
|
# go through the hexagon engraving above the vault door
|
||||||
|
LadderInfo("Frog's Domain", "frog cave main, Frog Stairs_Exit", "Ladders to Frog's Domain"),
|
||||||
|
# the turret at the end here is not affected by enemy rando
|
||||||
|
LadderInfo("Frog's Domain", "Frog's Domain Back", "Ladders to Frog's Domain", dest_is_region=True),
|
||||||
|
# todo: see if we can use that new laurels strat here
|
||||||
|
# LadderInfo("Rooted Ziggurat Lower Back", "ziggurat2020_3, ziggurat2020_FTRoom_"),
|
||||||
|
# go behind the cathedral to reach the door, pretty easily doable
|
||||||
|
LadderInfo("Swamp Mid", "Swamp Redux 2, Cathedral Redux_main", "Ladders in Swamp"),
|
||||||
|
LadderInfo("Back of Swamp", "Swamp Redux 2, Cathedral Redux_main"),
|
||||||
|
# need to do hc midair, probably cannot get into this without hc
|
||||||
|
LadderInfo("Swamp Mid", "Swamp Redux 2, Cathedral Redux_secret", "Ladders in Swamp"),
|
||||||
|
LadderInfo("Back of Swamp", "Swamp Redux 2, Cathedral Redux_secret"),
|
||||||
|
]
|
|
@ -47,7 +47,7 @@ location_table: Dict[str, TunicLocationData] = {
|
||||||
"Guardhouse 1 - Upper Floor": TunicLocationData("East Forest", "Guard House 1 East"),
|
"Guardhouse 1 - Upper Floor": TunicLocationData("East Forest", "Guard House 1 East"),
|
||||||
"East Forest - Dancing Fox Spirit Holy Cross": TunicLocationData("East Forest", "East Forest Dance Fox Spot", location_group="Holy Cross"),
|
"East Forest - Dancing Fox Spirit Holy Cross": TunicLocationData("East Forest", "East Forest Dance Fox Spot", location_group="Holy Cross"),
|
||||||
"East Forest - Golden Obelisk Holy Cross": TunicLocationData("East Forest", "Lower Forest", location_group="Holy Cross"),
|
"East Forest - Golden Obelisk Holy Cross": TunicLocationData("East Forest", "Lower Forest", location_group="Holy Cross"),
|
||||||
"East Forest - Ice Rod Grapple Chest": TunicLocationData("East Forest", "East Forest"),
|
"East Forest - Ice Rod Grapple Chest": TunicLocationData("East Forest", "Lower Forest"),
|
||||||
"East Forest - Above Save Point": TunicLocationData("East Forest", "East Forest"),
|
"East Forest - Above Save Point": TunicLocationData("East Forest", "East Forest"),
|
||||||
"East Forest - Above Save Point Obscured": TunicLocationData("East Forest", "East Forest"),
|
"East Forest - Above Save Point Obscured": TunicLocationData("East Forest", "East Forest"),
|
||||||
"East Forest - From Guardhouse 1 Chest": TunicLocationData("East Forest", "East Forest Dance Fox Spot"),
|
"East Forest - From Guardhouse 1 Chest": TunicLocationData("East Forest", "East Forest Dance Fox Spot"),
|
||||||
|
@ -205,7 +205,7 @@ location_table: Dict[str, TunicLocationData] = {
|
||||||
"Fountain Cross Door - Page Pickup": TunicLocationData("Overworld Holy Cross", "Fountain Cross Room", location_group="Holy Cross"),
|
"Fountain Cross Door - Page Pickup": TunicLocationData("Overworld Holy Cross", "Fountain Cross Room", location_group="Holy Cross"),
|
||||||
"Secret Gathering Place - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Secret Gathering Place", location_group="Holy Cross"),
|
"Secret Gathering Place - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Secret Gathering Place", location_group="Holy Cross"),
|
||||||
"Top of the Mountain - Page At The Peak": TunicLocationData("Overworld Holy Cross", "Top of the Mountain", location_group="Holy Cross"),
|
"Top of the Mountain - Page At The Peak": TunicLocationData("Overworld Holy Cross", "Top of the Mountain", location_group="Holy Cross"),
|
||||||
"Monastery - Monastery Chest": TunicLocationData("Quarry", "Monastery Back"),
|
"Monastery - Monastery Chest": TunicLocationData("Monastery", "Monastery Back"),
|
||||||
"Quarry - [Back Entrance] Bushes Holy Cross": TunicLocationData("Quarry Back", "Quarry Back", location_group="Holy Cross"),
|
"Quarry - [Back Entrance] Bushes Holy Cross": TunicLocationData("Quarry Back", "Quarry Back", location_group="Holy Cross"),
|
||||||
"Quarry - [Back Entrance] Chest": TunicLocationData("Quarry Back", "Quarry Back"),
|
"Quarry - [Back Entrance] Chest": TunicLocationData("Quarry Back", "Quarry Back"),
|
||||||
"Quarry - [Central] Near Shortcut Ladder": TunicLocationData("Quarry Back", "Quarry Back"),
|
"Quarry - [Central] Near Shortcut Ladder": TunicLocationData("Quarry Back", "Quarry Back"),
|
||||||
|
@ -224,7 +224,7 @@ location_table: Dict[str, TunicLocationData] = {
|
||||||
"Quarry - [Central] Above Ladder Dash Chest": TunicLocationData("Quarry", "Quarry Monastery Entry"),
|
"Quarry - [Central] Above Ladder Dash Chest": TunicLocationData("Quarry", "Quarry Monastery Entry"),
|
||||||
"Quarry - [West] Upper Area Bombable Wall": TunicLocationData("Quarry Back", "Quarry Back"),
|
"Quarry - [West] Upper Area Bombable Wall": TunicLocationData("Quarry Back", "Quarry Back"),
|
||||||
"Quarry - [East] Bombable Wall": TunicLocationData("Quarry", "Quarry"),
|
"Quarry - [East] Bombable Wall": TunicLocationData("Quarry", "Quarry"),
|
||||||
"Hero's Grave - Ash Relic": TunicLocationData("Quarry", "Hero Relic - Quarry"),
|
"Hero's Grave - Ash Relic": TunicLocationData("Monastery", "Hero Relic - Quarry"),
|
||||||
"Quarry - [West] Shooting Range Secret Path": TunicLocationData("Lower Quarry", "Lower Quarry"),
|
"Quarry - [West] Shooting Range Secret Path": TunicLocationData("Lower Quarry", "Lower Quarry"),
|
||||||
"Quarry - [West] Near Shooting Range": TunicLocationData("Lower Quarry", "Lower Quarry"),
|
"Quarry - [West] Near Shooting Range": TunicLocationData("Lower Quarry", "Lower Quarry"),
|
||||||
"Quarry - [West] Below Shooting Range": TunicLocationData("Lower Quarry", "Lower Quarry"),
|
"Quarry - [West] Below Shooting Range": TunicLocationData("Lower Quarry", "Lower Quarry"),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
from Options import (DefaultOnToggle, Toggle, StartInventoryPool, Choice, Range, TextChoice, PlandoConnections,
|
from Options import (DefaultOnToggle, Toggle, StartInventoryPool, Choice, Range, TextChoice, PlandoConnections,
|
||||||
PerGameCommonOptions, OptionGroup)
|
PerGameCommonOptions, OptionGroup, Visibility)
|
||||||
from .er_data import portal_mapping
|
from .er_data import portal_mapping
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,27 +39,6 @@ class AbilityShuffling(Toggle):
|
||||||
display_name = "Shuffle Abilities"
|
display_name = "Shuffle Abilities"
|
||||||
|
|
||||||
|
|
||||||
class LogicRules(Choice):
|
|
||||||
"""
|
|
||||||
Set which logic rules to use for your world.
|
|
||||||
Restricted: Standard logic, no glitches.
|
|
||||||
No Major Glitches: Sneaky Laurels zips, ice grapples through doors, shooting the west bell, and boss quick kills are included in logic.
|
|
||||||
* Ice grappling through the Ziggurat door is not in logic since you will get stuck in there without Prayer.
|
|
||||||
Unrestricted: Logic in No Major Glitches, as well as ladder storage to get to certain places early.
|
|
||||||
* Torch is given to the player at the start of the game due to the high softlock potential with various tricks. Using the torch is not required in logic.
|
|
||||||
* Using Ladder Storage to get to individual chests is not in logic to avoid tedium.
|
|
||||||
* Getting knocked out of the air by enemies during Ladder Storage to reach places is not in logic, except for in Rooted Ziggurat Lower. This is so you're not punished for playing with enemy rando on.
|
|
||||||
"""
|
|
||||||
internal_name = "logic_rules"
|
|
||||||
display_name = "Logic Rules"
|
|
||||||
option_restricted = 0
|
|
||||||
option_no_major_glitches = 1
|
|
||||||
alias_nmg = 1
|
|
||||||
option_unrestricted = 2
|
|
||||||
alias_ur = 2
|
|
||||||
default = 0
|
|
||||||
|
|
||||||
|
|
||||||
class Lanternless(Toggle):
|
class Lanternless(Toggle):
|
||||||
"""
|
"""
|
||||||
Choose whether you require the Lantern for dark areas.
|
Choose whether you require the Lantern for dark areas.
|
||||||
|
@ -173,8 +152,8 @@ class ShuffleLadders(Toggle):
|
||||||
"""
|
"""
|
||||||
internal_name = "shuffle_ladders"
|
internal_name = "shuffle_ladders"
|
||||||
display_name = "Shuffle Ladders"
|
display_name = "Shuffle Ladders"
|
||||||
|
|
||||||
|
|
||||||
class TunicPlandoConnections(PlandoConnections):
|
class TunicPlandoConnections(PlandoConnections):
|
||||||
"""
|
"""
|
||||||
Generic connection plando. Format is:
|
Generic connection plando. Format is:
|
||||||
|
@ -189,6 +168,82 @@ class TunicPlandoConnections(PlandoConnections):
|
||||||
duplicate_exits = True
|
duplicate_exits = True
|
||||||
|
|
||||||
|
|
||||||
|
class LaurelsZips(Toggle):
|
||||||
|
"""
|
||||||
|
Choose whether to include using the Hero's Laurels to zip through gates, doors, and tricky spots.
|
||||||
|
Notable inclusions are the Monastery gate, Ruined Passage door, Old House gate, Forest Grave Path gate, and getting from the Back of Swamp to the Middle of Swamp.
|
||||||
|
"""
|
||||||
|
internal_name = "laurels_zips"
|
||||||
|
display_name = "Laurels Zips Logic"
|
||||||
|
|
||||||
|
|
||||||
|
class IceGrappling(Choice):
|
||||||
|
"""
|
||||||
|
Choose whether grappling frozen enemies is in logic.
|
||||||
|
Easy includes ice grappling enemies that are in range without luring them. May include clips through terrain.
|
||||||
|
Medium includes using ice grapples to push enemies through doors or off ledges without luring them. Also includes bringing an enemy over to the Temple Door to grapple through it.
|
||||||
|
Hard includes luring or grappling enemies to get to where you want to go.
|
||||||
|
The Medium and Hard options will give the player the Torch to return to the Overworld checkpoint to avoid softlocks. Using the Torch is considered in logic.
|
||||||
|
Note: You will still be expected to ice grapple to the slime in East Forest from below with this option off.
|
||||||
|
"""
|
||||||
|
internal_name = "ice_grappling"
|
||||||
|
display_name = "Ice Grapple Logic"
|
||||||
|
option_off = 0
|
||||||
|
option_easy = 1
|
||||||
|
option_medium = 2
|
||||||
|
option_hard = 3
|
||||||
|
default = 0
|
||||||
|
|
||||||
|
|
||||||
|
class LadderStorage(Choice):
|
||||||
|
"""
|
||||||
|
Choose whether Ladder Storage is in logic.
|
||||||
|
Easy includes uses of Ladder Storage to get to open doors over a long distance without too much difficulty. May include convenient elevation changes (going up Mountain stairs, stairs in front of Special Shop, etc.).
|
||||||
|
Medium includes the above as well as changing your elevation using the environment and getting knocked down by melee enemies mid-LS.
|
||||||
|
Hard includes the above as well as going behind the map to enter closed doors from behind, shooting a fuse with the magic wand to knock yourself down at close range, and getting into the Cathedral Secret Legend room mid-LS.
|
||||||
|
Enabling any of these difficulty options will give the player the Torch item to return to the Overworld checkpoint to avoid softlocks. Using the Torch is considered in logic.
|
||||||
|
Opening individual chests while doing ladder storage is excluded due to tedium.
|
||||||
|
Knocking yourself out of LS with a bomb is excluded due to the problematic nature of consumables in logic.
|
||||||
|
"""
|
||||||
|
internal_name = "ladder_storage"
|
||||||
|
display_name = "Ladder Storage Logic"
|
||||||
|
option_off = 0
|
||||||
|
option_easy = 1
|
||||||
|
option_medium = 2
|
||||||
|
option_hard = 3
|
||||||
|
default = 0
|
||||||
|
|
||||||
|
|
||||||
|
class LadderStorageWithoutItems(Toggle):
|
||||||
|
"""
|
||||||
|
If disabled, you logically require Stick, Sword, or Magic Orb to perform Ladder Storage.
|
||||||
|
If enabled, you will be expected to perform Ladder Storage without progression items.
|
||||||
|
This can be done with the plushie code, a Golden Coin, Prayer, and many other options.
|
||||||
|
|
||||||
|
This option has no effect if you do not have Ladder Storage Logic enabled.
|
||||||
|
"""
|
||||||
|
internal_name = "ladder_storage_without_items"
|
||||||
|
display_name = "Ladder Storage without Items"
|
||||||
|
|
||||||
|
|
||||||
|
class LogicRules(Choice):
|
||||||
|
"""
|
||||||
|
This option has been superseded by the individual trick options.
|
||||||
|
If set to nmg, it will set Ice Grappling to medium and Laurels Zips on.
|
||||||
|
If set to ur, it will do nmg as well as set Ladder Storage to medium.
|
||||||
|
It is here to avoid breaking old yamls, and will be removed at a later date.
|
||||||
|
"""
|
||||||
|
visibility = Visibility.none
|
||||||
|
internal_name = "logic_rules"
|
||||||
|
display_name = "Logic Rules"
|
||||||
|
option_restricted = 0
|
||||||
|
option_no_major_glitches = 1
|
||||||
|
alias_nmg = 1
|
||||||
|
option_unrestricted = 2
|
||||||
|
alias_ur = 2
|
||||||
|
default = 0
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TunicOptions(PerGameCommonOptions):
|
class TunicOptions(PerGameCommonOptions):
|
||||||
start_inventory_from_pool: StartInventoryPool
|
start_inventory_from_pool: StartInventoryPool
|
||||||
|
@ -199,22 +254,30 @@ class TunicOptions(PerGameCommonOptions):
|
||||||
shuffle_ladders: ShuffleLadders
|
shuffle_ladders: ShuffleLadders
|
||||||
entrance_rando: EntranceRando
|
entrance_rando: EntranceRando
|
||||||
fixed_shop: FixedShop
|
fixed_shop: FixedShop
|
||||||
logic_rules: LogicRules
|
|
||||||
fool_traps: FoolTraps
|
fool_traps: FoolTraps
|
||||||
hexagon_quest: HexagonQuest
|
hexagon_quest: HexagonQuest
|
||||||
hexagon_goal: HexagonGoal
|
hexagon_goal: HexagonGoal
|
||||||
extra_hexagon_percentage: ExtraHexagonPercentage
|
extra_hexagon_percentage: ExtraHexagonPercentage
|
||||||
|
laurels_location: LaurelsLocation
|
||||||
lanternless: Lanternless
|
lanternless: Lanternless
|
||||||
maskless: Maskless
|
maskless: Maskless
|
||||||
laurels_location: LaurelsLocation
|
laurels_zips: LaurelsZips
|
||||||
|
ice_grappling: IceGrappling
|
||||||
|
ladder_storage: LadderStorage
|
||||||
|
ladder_storage_without_items: LadderStorageWithoutItems
|
||||||
plando_connections: TunicPlandoConnections
|
plando_connections: TunicPlandoConnections
|
||||||
|
|
||||||
|
logic_rules: LogicRules
|
||||||
|
|
||||||
|
|
||||||
tunic_option_groups = [
|
tunic_option_groups = [
|
||||||
OptionGroup("Logic Options", [
|
OptionGroup("Logic Options", [
|
||||||
LogicRules,
|
|
||||||
Lanternless,
|
Lanternless,
|
||||||
Maskless,
|
Maskless,
|
||||||
|
LaurelsZips,
|
||||||
|
IceGrappling,
|
||||||
|
LadderStorage,
|
||||||
|
LadderStorageWithoutItems
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -231,9 +294,12 @@ tunic_option_presets: Dict[str, Dict[str, Any]] = {
|
||||||
"Glace Mode": {
|
"Glace Mode": {
|
||||||
"accessibility": "minimal",
|
"accessibility": "minimal",
|
||||||
"ability_shuffling": True,
|
"ability_shuffling": True,
|
||||||
"entrance_rando": "yes",
|
"entrance_rando": True,
|
||||||
"fool_traps": "onslaught",
|
"fool_traps": "onslaught",
|
||||||
"logic_rules": "unrestricted",
|
"laurels_zips": True,
|
||||||
|
"ice_grappling": "hard",
|
||||||
|
"ladder_storage": "hard",
|
||||||
|
"ladder_storage_without_items": True,
|
||||||
"maskless": True,
|
"maskless": True,
|
||||||
"lanternless": True,
|
"lanternless": True,
|
||||||
},
|
},
|
||||||
|
|
|
@ -16,7 +16,8 @@ tunic_regions: Dict[str, Set[str]] = {
|
||||||
"Eastern Vault Fortress": {"Beneath the Vault"},
|
"Eastern Vault Fortress": {"Beneath the Vault"},
|
||||||
"Beneath the Vault": {"Eastern Vault Fortress"},
|
"Beneath the Vault": {"Eastern Vault Fortress"},
|
||||||
"Quarry Back": {"Quarry"},
|
"Quarry Back": {"Quarry"},
|
||||||
"Quarry": {"Lower Quarry"},
|
"Quarry": {"Monastery", "Lower Quarry"},
|
||||||
|
"Monastery": set(),
|
||||||
"Lower Quarry": {"Rooted Ziggurat"},
|
"Lower Quarry": {"Rooted Ziggurat"},
|
||||||
"Rooted Ziggurat": set(),
|
"Rooted Ziggurat": set(),
|
||||||
"Swamp": {"Cathedral"},
|
"Swamp": {"Cathedral"},
|
||||||
|
|
|
@ -3,7 +3,7 @@ from typing import Dict, TYPE_CHECKING
|
||||||
|
|
||||||
from worlds.generic.Rules import set_rule, forbid_item, add_rule
|
from worlds.generic.Rules import set_rule, forbid_item, add_rule
|
||||||
from BaseClasses import CollectionState
|
from BaseClasses import CollectionState
|
||||||
from .options import TunicOptions
|
from .options import TunicOptions, LadderStorage, IceGrappling
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from . import TunicWorld
|
from . import TunicWorld
|
||||||
|
|
||||||
|
@ -27,10 +27,10 @@ green_hexagon = "Green Questagon"
|
||||||
blue_hexagon = "Blue Questagon"
|
blue_hexagon = "Blue Questagon"
|
||||||
gold_hexagon = "Gold Questagon"
|
gold_hexagon = "Gold Questagon"
|
||||||
|
|
||||||
|
# "Quarry - [East] Bombable Wall" is excluded from this list since it has slightly different rules
|
||||||
bomb_walls = ["East Forest - Bombable Wall", "Eastern Vault Fortress - [East Wing] Bombable Wall",
|
bomb_walls = ["East Forest - Bombable Wall", "Eastern Vault Fortress - [East Wing] Bombable Wall",
|
||||||
"Overworld - [Central] Bombable Wall", "Overworld - [Southwest] Bombable Wall Near Fountain",
|
"Overworld - [Central] Bombable Wall", "Overworld - [Southwest] Bombable Wall Near Fountain",
|
||||||
"Quarry - [West] Upper Area Bombable Wall", "Quarry - [East] Bombable Wall",
|
"Quarry - [West] Upper Area Bombable Wall", "Ruined Atoll - [Northwest] Bombable Wall"]
|
||||||
"Ruined Atoll - [Northwest] Bombable Wall"]
|
|
||||||
|
|
||||||
|
|
||||||
def randomize_ability_unlocks(random: Random, options: TunicOptions) -> Dict[str, int]:
|
def randomize_ability_unlocks(random: Random, options: TunicOptions) -> Dict[str, int]:
|
||||||
|
@ -64,32 +64,33 @@ def has_sword(state: CollectionState, player: int) -> bool:
|
||||||
return state.has("Sword", player) or state.has("Sword Upgrade", player, 2)
|
return state.has("Sword", player) or state.has("Sword Upgrade", player, 2)
|
||||||
|
|
||||||
|
|
||||||
def has_ice_grapple_logic(long_range: bool, state: CollectionState, world: "TunicWorld") -> bool:
|
def laurels_zip(state: CollectionState, world: "TunicWorld") -> bool:
|
||||||
player = world.player
|
return world.options.laurels_zips and state.has(laurels, world.player)
|
||||||
if not world.options.logic_rules:
|
|
||||||
|
|
||||||
|
def has_ice_grapple_logic(long_range: bool, difficulty: IceGrappling, state: CollectionState, world: "TunicWorld") -> bool:
|
||||||
|
if world.options.ice_grappling < difficulty:
|
||||||
return False
|
return False
|
||||||
if not long_range:
|
if not long_range:
|
||||||
return state.has_all({ice_dagger, grapple}, player)
|
return state.has_all({ice_dagger, grapple}, world.player)
|
||||||
else:
|
else:
|
||||||
return state.has_all({ice_dagger, fire_wand, grapple}, player) and has_ability(icebolt, state, world)
|
return state.has_all({ice_dagger, fire_wand, grapple}, world.player) and has_ability(icebolt, state, world)
|
||||||
|
|
||||||
|
|
||||||
def can_ladder_storage(state: CollectionState, world: "TunicWorld") -> bool:
|
def can_ladder_storage(state: CollectionState, world: "TunicWorld") -> bool:
|
||||||
return world.options.logic_rules == "unrestricted" and has_stick(state, world.player)
|
if not world.options.ladder_storage:
|
||||||
|
return False
|
||||||
|
if world.options.ladder_storage_without_items:
|
||||||
|
return True
|
||||||
|
return has_stick(state, world.player) or state.has(grapple, world.player)
|
||||||
|
|
||||||
|
|
||||||
def has_mask(state: CollectionState, world: "TunicWorld") -> bool:
|
def has_mask(state: CollectionState, world: "TunicWorld") -> bool:
|
||||||
if world.options.maskless:
|
return world.options.maskless or state.has(mask, world.player)
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return state.has(mask, world.player)
|
|
||||||
|
|
||||||
|
|
||||||
def has_lantern(state: CollectionState, world: "TunicWorld") -> bool:
|
def has_lantern(state: CollectionState, world: "TunicWorld") -> bool:
|
||||||
if world.options.lanternless:
|
return world.options.lanternless or state.has(lantern, world.player)
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return state.has(lantern, world.player)
|
|
||||||
|
|
||||||
|
|
||||||
def set_region_rules(world: "TunicWorld") -> None:
|
def set_region_rules(world: "TunicWorld") -> None:
|
||||||
|
@ -102,12 +103,14 @@ def set_region_rules(world: "TunicWorld") -> None:
|
||||||
lambda state: has_stick(state, player) or state.has(fire_wand, player)
|
lambda state: has_stick(state, player) or state.has(fire_wand, player)
|
||||||
world.get_entrance("Overworld -> Dark Tomb").access_rule = \
|
world.get_entrance("Overworld -> Dark Tomb").access_rule = \
|
||||||
lambda state: has_lantern(state, world)
|
lambda state: has_lantern(state, world)
|
||||||
|
# laurels in, ladder storage in through the furnace, or ice grapple down the belltower
|
||||||
world.get_entrance("Overworld -> West Garden").access_rule = \
|
world.get_entrance("Overworld -> West Garden").access_rule = \
|
||||||
lambda state: state.has(laurels, player) \
|
lambda state: (state.has(laurels, player)
|
||||||
or can_ladder_storage(state, world)
|
or can_ladder_storage(state, world)
|
||||||
|
or has_ice_grapple_logic(False, IceGrappling.option_hard, state, world))
|
||||||
world.get_entrance("Overworld -> Eastern Vault Fortress").access_rule = \
|
world.get_entrance("Overworld -> Eastern Vault Fortress").access_rule = \
|
||||||
lambda state: state.has(laurels, player) \
|
lambda state: state.has(laurels, player) \
|
||||||
or has_ice_grapple_logic(True, state, world) \
|
or has_ice_grapple_logic(True, IceGrappling.option_easy, state, world) \
|
||||||
or can_ladder_storage(state, world)
|
or can_ladder_storage(state, world)
|
||||||
# using laurels or ls to get in is covered by the -> Eastern Vault Fortress rules
|
# using laurels or ls to get in is covered by the -> Eastern Vault Fortress rules
|
||||||
world.get_entrance("Overworld -> Beneath the Vault").access_rule = \
|
world.get_entrance("Overworld -> Beneath the Vault").access_rule = \
|
||||||
|
@ -124,8 +127,8 @@ def set_region_rules(world: "TunicWorld") -> None:
|
||||||
world.get_entrance("Lower Quarry -> Rooted Ziggurat").access_rule = \
|
world.get_entrance("Lower Quarry -> Rooted Ziggurat").access_rule = \
|
||||||
lambda state: state.has(grapple, player) and has_ability(prayer, state, world)
|
lambda state: state.has(grapple, player) and has_ability(prayer, state, world)
|
||||||
world.get_entrance("Swamp -> Cathedral").access_rule = \
|
world.get_entrance("Swamp -> Cathedral").access_rule = \
|
||||||
lambda state: state.has(laurels, player) and has_ability(prayer, state, world) \
|
lambda state: (state.has(laurels, player) and has_ability(prayer, state, world)) \
|
||||||
or has_ice_grapple_logic(False, state, world)
|
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)
|
||||||
world.get_entrance("Overworld -> Spirit Arena").access_rule = \
|
world.get_entrance("Overworld -> Spirit Arena").access_rule = \
|
||||||
lambda state: ((state.has(gold_hexagon, player, options.hexagon_goal.value) if options.hexagon_quest.value
|
lambda state: ((state.has(gold_hexagon, player, options.hexagon_goal.value) if options.hexagon_quest.value
|
||||||
else state.has_all({red_hexagon, green_hexagon, blue_hexagon}, player)
|
else state.has_all({red_hexagon, green_hexagon, blue_hexagon}, player)
|
||||||
|
@ -133,10 +136,18 @@ def set_region_rules(world: "TunicWorld") -> None:
|
||||||
and has_ability(prayer, state, world) and has_sword(state, player)
|
and has_ability(prayer, state, world) and has_sword(state, player)
|
||||||
and state.has_any({lantern, laurels}, player))
|
and state.has_any({lantern, laurels}, player))
|
||||||
|
|
||||||
|
world.get_region("Quarry").connect(world.get_region("Rooted Ziggurat"),
|
||||||
|
rule=lambda state: has_ice_grapple_logic(True, IceGrappling.option_hard, state, world)
|
||||||
|
and has_ability(prayer, state, world))
|
||||||
|
|
||||||
|
if options.ladder_storage >= LadderStorage.option_medium:
|
||||||
|
# ls at any ladder in a safe spot in quarry to get to the monastery rope entrance
|
||||||
|
world.get_region("Quarry Back").connect(world.get_region("Monastery"),
|
||||||
|
rule=lambda state: can_ladder_storage(state, world))
|
||||||
|
|
||||||
|
|
||||||
def set_location_rules(world: "TunicWorld") -> None:
|
def set_location_rules(world: "TunicWorld") -> None:
|
||||||
player = world.player
|
player = world.player
|
||||||
options = world.options
|
|
||||||
|
|
||||||
forbid_item(world.get_location("Secret Gathering Place - 20 Fairy Reward"), fairies, player)
|
forbid_item(world.get_location("Secret Gathering Place - 20 Fairy Reward"), fairies, player)
|
||||||
|
|
||||||
|
@ -147,11 +158,13 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||||
lambda state: has_ability(prayer, state, world)
|
lambda state: has_ability(prayer, state, world)
|
||||||
or state.has(laurels, player)
|
or state.has(laurels, player)
|
||||||
or can_ladder_storage(state, world)
|
or can_ladder_storage(state, world)
|
||||||
or (has_ice_grapple_logic(True, state, world) and has_lantern(state, world)))
|
or (has_ice_grapple_logic(True, IceGrappling.option_easy, state, world)
|
||||||
|
and has_lantern(state, world)))
|
||||||
set_rule(world.get_location("Fortress Courtyard - Page Near Cave"),
|
set_rule(world.get_location("Fortress Courtyard - Page Near Cave"),
|
||||||
lambda state: has_ability(prayer, state, world) or state.has(laurels, player)
|
lambda state: has_ability(prayer, state, world) or state.has(laurels, player)
|
||||||
or can_ladder_storage(state, world)
|
or can_ladder_storage(state, world)
|
||||||
or (has_ice_grapple_logic(True, state, world) and has_lantern(state, world)))
|
or (has_ice_grapple_logic(True, IceGrappling.option_easy, state, world)
|
||||||
|
and has_lantern(state, world)))
|
||||||
set_rule(world.get_location("East Forest - Dancing Fox Spirit Holy Cross"),
|
set_rule(world.get_location("East Forest - Dancing Fox Spirit Holy Cross"),
|
||||||
lambda state: has_ability(holy_cross, state, world))
|
lambda state: has_ability(holy_cross, state, world))
|
||||||
set_rule(world.get_location("Forest Grave Path - Holy Cross Code by Grave"),
|
set_rule(world.get_location("Forest Grave Path - Holy Cross Code by Grave"),
|
||||||
|
@ -186,17 +199,17 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||||
lambda state: state.has(laurels, player))
|
lambda state: state.has(laurels, player))
|
||||||
set_rule(world.get_location("Old House - Normal Chest"),
|
set_rule(world.get_location("Old House - Normal Chest"),
|
||||||
lambda state: state.has(house_key, player)
|
lambda state: state.has(house_key, player)
|
||||||
or has_ice_grapple_logic(False, state, world)
|
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)
|
||||||
or (state.has(laurels, player) and options.logic_rules))
|
or laurels_zip(state, world))
|
||||||
set_rule(world.get_location("Old House - Holy Cross Chest"),
|
set_rule(world.get_location("Old House - Holy Cross Chest"),
|
||||||
lambda state: has_ability(holy_cross, state, world) and (
|
lambda state: has_ability(holy_cross, state, world) and (
|
||||||
state.has(house_key, player)
|
state.has(house_key, player)
|
||||||
or has_ice_grapple_logic(False, state, world)
|
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)
|
||||||
or (state.has(laurels, player) and options.logic_rules)))
|
or laurels_zip(state, world)))
|
||||||
set_rule(world.get_location("Old House - Shield Pickup"),
|
set_rule(world.get_location("Old House - Shield Pickup"),
|
||||||
lambda state: state.has(house_key, player)
|
lambda state: state.has(house_key, player)
|
||||||
or has_ice_grapple_logic(False, state, world)
|
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)
|
||||||
or (state.has(laurels, player) and options.logic_rules))
|
or laurels_zip(state, world))
|
||||||
set_rule(world.get_location("Overworld - [Northwest] Page on Pillar by Dark Tomb"),
|
set_rule(world.get_location("Overworld - [Northwest] Page on Pillar by Dark Tomb"),
|
||||||
lambda state: state.has(laurels, player))
|
lambda state: state.has(laurels, player))
|
||||||
set_rule(world.get_location("Overworld - [Southwest] From West Garden"),
|
set_rule(world.get_location("Overworld - [Southwest] From West Garden"),
|
||||||
|
@ -206,7 +219,7 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||||
or (has_lantern(state, world) and has_sword(state, player))
|
or (has_lantern(state, world) and has_sword(state, player))
|
||||||
or can_ladder_storage(state, world))
|
or can_ladder_storage(state, world))
|
||||||
set_rule(world.get_location("Overworld - [Northwest] Chest Beneath Quarry Gate"),
|
set_rule(world.get_location("Overworld - [Northwest] Chest Beneath Quarry Gate"),
|
||||||
lambda state: state.has_any({grapple, laurels}, player) or options.logic_rules)
|
lambda state: state.has_any({grapple, laurels}, player))
|
||||||
set_rule(world.get_location("Overworld - [East] Grapple Chest"),
|
set_rule(world.get_location("Overworld - [East] Grapple Chest"),
|
||||||
lambda state: state.has(grapple, player))
|
lambda state: state.has(grapple, player))
|
||||||
set_rule(world.get_location("Special Shop - Secret Page Pickup"),
|
set_rule(world.get_location("Special Shop - Secret Page Pickup"),
|
||||||
|
@ -215,11 +228,11 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||||
lambda state: has_ability(holy_cross, state, world)
|
lambda state: has_ability(holy_cross, state, world)
|
||||||
and (state.has(laurels, player) or (has_lantern(state, world) and (has_sword(state, player)
|
and (state.has(laurels, player) or (has_lantern(state, world) and (has_sword(state, player)
|
||||||
or state.has(fire_wand, player)))
|
or state.has(fire_wand, player)))
|
||||||
or has_ice_grapple_logic(False, state, world)))
|
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)))
|
||||||
set_rule(world.get_location("Sealed Temple - Page Pickup"),
|
set_rule(world.get_location("Sealed Temple - Page Pickup"),
|
||||||
lambda state: state.has(laurels, player)
|
lambda state: state.has(laurels, player)
|
||||||
or (has_lantern(state, world) and (has_sword(state, player) or state.has(fire_wand, player)))
|
or (has_lantern(state, world) and (has_sword(state, player) or state.has(fire_wand, player)))
|
||||||
or has_ice_grapple_logic(False, state, world))
|
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world))
|
||||||
set_rule(world.get_location("West Furnace - Lantern Pickup"),
|
set_rule(world.get_location("West Furnace - Lantern Pickup"),
|
||||||
lambda state: has_stick(state, player) or state.has_any({fire_wand, laurels}, player))
|
lambda state: has_stick(state, player) or state.has_any({fire_wand, laurels}, player))
|
||||||
|
|
||||||
|
@ -254,7 +267,7 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||||
lambda state: state.has(laurels, player) and has_ability(holy_cross, state, world))
|
lambda state: state.has(laurels, player) and has_ability(holy_cross, state, world))
|
||||||
set_rule(world.get_location("West Garden - [East Lowlands] Page Behind Ice Dagger House"),
|
set_rule(world.get_location("West Garden - [East Lowlands] Page Behind Ice Dagger House"),
|
||||||
lambda state: (state.has(laurels, player) and has_ability(prayer, state, world))
|
lambda state: (state.has(laurels, player) and has_ability(prayer, state, world))
|
||||||
or has_ice_grapple_logic(True, state, world))
|
or has_ice_grapple_logic(True, IceGrappling.option_easy, state, world))
|
||||||
set_rule(world.get_location("West Garden - [Central Lowlands] Below Left Walkway"),
|
set_rule(world.get_location("West Garden - [Central Lowlands] Below Left Walkway"),
|
||||||
lambda state: state.has(laurels, player))
|
lambda state: state.has(laurels, player))
|
||||||
set_rule(world.get_location("West Garden - [Central Highlands] After Garden Knight"),
|
set_rule(world.get_location("West Garden - [Central Highlands] After Garden Knight"),
|
||||||
|
@ -265,12 +278,15 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||||
# Ruined Atoll
|
# Ruined Atoll
|
||||||
set_rule(world.get_location("Ruined Atoll - [West] Near Kevin Block"),
|
set_rule(world.get_location("Ruined Atoll - [West] Near Kevin Block"),
|
||||||
lambda state: state.has(laurels, player))
|
lambda state: state.has(laurels, player))
|
||||||
|
# ice grapple push a crab through the door
|
||||||
set_rule(world.get_location("Ruined Atoll - [East] Locked Room Lower Chest"),
|
set_rule(world.get_location("Ruined Atoll - [East] Locked Room Lower Chest"),
|
||||||
lambda state: state.has(laurels, player) or state.has(key, player, 2))
|
lambda state: state.has(laurels, player) or state.has(key, player, 2)
|
||||||
|
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world))
|
||||||
set_rule(world.get_location("Ruined Atoll - [East] Locked Room Upper Chest"),
|
set_rule(world.get_location("Ruined Atoll - [East] Locked Room Upper Chest"),
|
||||||
lambda state: state.has(laurels, player) or state.has(key, player, 2))
|
lambda state: state.has(laurels, player) or state.has(key, player, 2)
|
||||||
|
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world))
|
||||||
set_rule(world.get_location("Librarian - Hexagon Green"),
|
set_rule(world.get_location("Librarian - Hexagon Green"),
|
||||||
lambda state: has_sword(state, player) or options.logic_rules)
|
lambda state: has_sword(state, player))
|
||||||
|
|
||||||
# Frog's Domain
|
# Frog's Domain
|
||||||
set_rule(world.get_location("Frog's Domain - Side Room Grapple Secret"),
|
set_rule(world.get_location("Frog's Domain - Side Room Grapple Secret"),
|
||||||
|
@ -285,10 +301,12 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||||
lambda state: state.has(laurels, player))
|
lambda state: state.has(laurels, player))
|
||||||
set_rule(world.get_location("Fortress Arena - Siege Engine/Vault Key Pickup"),
|
set_rule(world.get_location("Fortress Arena - Siege Engine/Vault Key Pickup"),
|
||||||
lambda state: has_sword(state, player)
|
lambda state: has_sword(state, player)
|
||||||
and (has_ability(prayer, state, world) or has_ice_grapple_logic(False, state, world)))
|
and (has_ability(prayer, state, world)
|
||||||
|
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)))
|
||||||
set_rule(world.get_location("Fortress Arena - Hexagon Red"),
|
set_rule(world.get_location("Fortress Arena - Hexagon Red"),
|
||||||
lambda state: state.has(vault_key, player)
|
lambda state: state.has(vault_key, player)
|
||||||
and (has_ability(prayer, state, world) or has_ice_grapple_logic(False, state, world)))
|
and (has_ability(prayer, state, world)
|
||||||
|
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)))
|
||||||
|
|
||||||
# Beneath the Vault
|
# Beneath the Vault
|
||||||
set_rule(world.get_location("Beneath the Fortress - Bridge"),
|
set_rule(world.get_location("Beneath the Fortress - Bridge"),
|
||||||
|
@ -301,14 +319,14 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||||
lambda state: state.has(laurels, player))
|
lambda state: state.has(laurels, player))
|
||||||
set_rule(world.get_location("Rooted Ziggurat Upper - Near Bridge Switch"),
|
set_rule(world.get_location("Rooted Ziggurat Upper - Near Bridge Switch"),
|
||||||
lambda state: has_sword(state, player) or state.has_all({fire_wand, laurels}, player))
|
lambda state: has_sword(state, player) or state.has_all({fire_wand, laurels}, player))
|
||||||
# nmg - kill boss scav with orb + firecracker, or similar
|
|
||||||
set_rule(world.get_location("Rooted Ziggurat Lower - Hexagon Blue"),
|
set_rule(world.get_location("Rooted Ziggurat Lower - Hexagon Blue"),
|
||||||
lambda state: has_sword(state, player) or (state.has(grapple, player) and options.logic_rules))
|
lambda state: has_sword(state, player))
|
||||||
|
|
||||||
# Swamp
|
# Swamp
|
||||||
set_rule(world.get_location("Cathedral Gauntlet - Gauntlet Reward"),
|
set_rule(world.get_location("Cathedral Gauntlet - Gauntlet Reward"),
|
||||||
lambda state: (state.has(fire_wand, player) and has_sword(state, player))
|
lambda state: (state.has(fire_wand, player) and has_sword(state, player))
|
||||||
and (state.has(laurels, player) or has_ice_grapple_logic(False, state, world)))
|
and (state.has(laurels, player)
|
||||||
|
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)))
|
||||||
set_rule(world.get_location("Swamp - [Entrance] Above Entryway"),
|
set_rule(world.get_location("Swamp - [Entrance] Above Entryway"),
|
||||||
lambda state: state.has(laurels, player))
|
lambda state: state.has(laurels, player))
|
||||||
set_rule(world.get_location("Swamp - [South Graveyard] Upper Walkway Dash Chest"),
|
set_rule(world.get_location("Swamp - [South Graveyard] Upper Walkway Dash Chest"),
|
||||||
|
@ -335,8 +353,16 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||||
# Bombable Walls
|
# Bombable Walls
|
||||||
for location_name in bomb_walls:
|
for location_name in bomb_walls:
|
||||||
# has_sword is there because you can buy bombs in the shop
|
# has_sword is there because you can buy bombs in the shop
|
||||||
set_rule(world.get_location(location_name), lambda state: state.has(gun, player) or has_sword(state, player))
|
set_rule(world.get_location(location_name),
|
||||||
|
lambda state: state.has(gun, player)
|
||||||
|
or has_sword(state, player)
|
||||||
|
or has_ice_grapple_logic(False, IceGrappling.option_hard, state, world))
|
||||||
add_rule(world.get_location("Cube Cave - Holy Cross Chest"),
|
add_rule(world.get_location("Cube Cave - Holy Cross Chest"),
|
||||||
|
lambda state: state.has(gun, player)
|
||||||
|
or has_sword(state, player)
|
||||||
|
or has_ice_grapple_logic(False, IceGrappling.option_hard, state, world))
|
||||||
|
# can't ice grapple to this one, not enough space
|
||||||
|
set_rule(world.get_location("Quarry - [East] Bombable Wall"),
|
||||||
lambda state: state.has(gun, player) or has_sword(state, player))
|
lambda state: state.has(gun, player) or has_sword(state, player))
|
||||||
|
|
||||||
# Shop
|
# Shop
|
||||||
|
|
|
@ -68,3 +68,57 @@ class TestER(TunicTestBase):
|
||||||
self.assertFalse(self.can_reach_location("Overworld - [Southwest] Flowers Holy Cross"))
|
self.assertFalse(self.can_reach_location("Overworld - [Southwest] Flowers Holy Cross"))
|
||||||
self.collect_by_name(["Pages 42-43 (Holy Cross)"])
|
self.collect_by_name(["Pages 42-43 (Holy Cross)"])
|
||||||
self.assertTrue(self.can_reach_location("Overworld - [Southwest] Flowers Holy Cross"))
|
self.assertTrue(self.can_reach_location("Overworld - [Southwest] Flowers Holy Cross"))
|
||||||
|
|
||||||
|
|
||||||
|
class TestERSpecial(TunicTestBase):
|
||||||
|
options = {options.EntranceRando.internal_name: options.EntranceRando.option_yes,
|
||||||
|
options.AbilityShuffling.internal_name: options.AbilityShuffling.option_true,
|
||||||
|
options.HexagonQuest.internal_name: options.HexagonQuest.option_false,
|
||||||
|
options.FixedShop.internal_name: options.FixedShop.option_false,
|
||||||
|
options.IceGrappling.internal_name: options.IceGrappling.option_easy,
|
||||||
|
"plando_connections": [
|
||||||
|
{
|
||||||
|
"entrance": "Stick House Entrance",
|
||||||
|
"exit": "Ziggurat Portal Room Entrance"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entrance": "Ziggurat Lower to Ziggurat Tower",
|
||||||
|
"exit": "Secret Gathering Place Exit"
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
# with these plando connections, you need to ice grapple from the back of lower zig to the front to get laurels
|
||||||
|
|
||||||
|
|
||||||
|
# ensure that ladder storage connections connect to the outlet region, not the portal's region
|
||||||
|
class TestLadderStorage(TunicTestBase):
|
||||||
|
options = {options.EntranceRando.internal_name: options.EntranceRando.option_yes,
|
||||||
|
options.AbilityShuffling.internal_name: options.AbilityShuffling.option_true,
|
||||||
|
options.HexagonQuest.internal_name: options.HexagonQuest.option_false,
|
||||||
|
options.FixedShop.internal_name: options.FixedShop.option_false,
|
||||||
|
options.LadderStorage.internal_name: options.LadderStorage.option_hard,
|
||||||
|
options.LadderStorageWithoutItems.internal_name: options.LadderStorageWithoutItems.option_false,
|
||||||
|
"plando_connections": [
|
||||||
|
{
|
||||||
|
"entrance": "Fortress Courtyard Shop",
|
||||||
|
# "exit": "Ziggurat Portal Room Exit"
|
||||||
|
"exit": "Spawn to Far Shore"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entrance": "Fortress Courtyard to Beneath the Vault",
|
||||||
|
"exit": "Stick House Exit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entrance": "Stick House Entrance",
|
||||||
|
"exit": "Fortress Courtyard to Overworld"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"entrance": "Old House Waterfall Entrance",
|
||||||
|
"exit": "Ziggurat Portal Room Entrance"
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
|
||||||
|
def test_ls_to_shop_entrance(self) -> None:
|
||||||
|
self.collect_by_name(["Magic Orb"])
|
||||||
|
self.assertFalse(self.can_reach_location("Fortress Courtyard - Page Near Cave"))
|
||||||
|
self.collect_by_name(["Pages 24-25 (Prayer)"])
|
||||||
|
self.assertTrue(self.can_reach_location("Fortress Courtyard - Page Near Cave"))
|
||||||
|
|
Loading…
Reference in New Issue