TUNIC: Shuffle Ladders option (#2919)

This commit is contained in:
Scipio Wright 2024-03-21 11:50:07 -04:00 committed by GitHub
parent 30a0aa2c85
commit 14816743fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 1659 additions and 1056 deletions

View File

@ -177,7 +177,7 @@
/worlds/tloz/ @Rosalie-A @t3hf1gm3nt /worlds/tloz/ @Rosalie-A @t3hf1gm3nt
# TUNIC # TUNIC
/worlds/tunic/ @silent-destroyer /worlds/tunic/ @silent-destroyer @ScipioWright
# Undertale # Undertale
/worlds/undertale/ @jonloveslegos /worlds/undertale/ @jonloveslegos

View File

@ -7,6 +7,7 @@ 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 .options import TunicOptions from .options import TunicOptions
from worlds.AutoWorld import WebWorld, World from worlds.AutoWorld import WebWorld, World
from decimal import Decimal, ROUND_HALF_UP from decimal import Decimal, ROUND_HALF_UP
@ -44,7 +45,6 @@ class TunicWorld(World):
game = "TUNIC" game = "TUNIC"
web = TunicWeb() web = TunicWeb()
data_version = 2
options: TunicOptions options: TunicOptions
options_dataclass = TunicOptions options_dataclass = TunicOptions
item_name_groups = item_name_groups item_name_groups = item_name_groups
@ -72,6 +72,7 @@ class TunicWorld(World):
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"]
self.options.entrance_rando.value = passthrough["entrance_rando"] self.options.entrance_rando.value = passthrough["entrance_rando"]
self.options.shuffle_ladders.value = passthrough["shuffle_ladders"]
def create_item(self, name: str) -> TunicItem: def create_item(self, name: str) -> TunicItem:
item_data = item_table[name] item_data = item_table[name]
@ -119,27 +120,46 @@ class TunicWorld(World):
items_to_create[rgb_hexagon] = 0 items_to_create[rgb_hexagon] = 0
items_to_create[gold_hexagon] -= 3 items_to_create[gold_hexagon] -= 3
# Filler items in the item pool
available_filler: List[str] = [filler for filler in items_to_create if items_to_create[filler] > 0 and
item_table[filler].classification == ItemClassification.filler]
# Remove filler to make room for other items
def remove_filler(amount: int):
for _ in range(0, amount):
if not available_filler:
fill = "Fool Trap"
else:
fill = self.random.choice(available_filler)
if items_to_create[fill] == 0:
raise Exception("No filler items left to accommodate options selected. Turn down fool trap amount.")
items_to_create[fill] -= 1
if items_to_create[fill] == 0:
available_filler.remove(fill)
if self.options.shuffle_ladders:
ladder_count = 0
for item_name, item_data in item_table.items():
if item_data.item_group == "ladders":
items_to_create[item_name] = 1
ladder_count += 1
remove_filler(ladder_count)
if hexagon_quest: if hexagon_quest:
# Calculate number of hexagons in item pool # Calculate number of hexagons in item pool
hexagon_goal = self.options.hexagon_goal hexagon_goal = self.options.hexagon_goal
extra_hexagons = self.options.extra_hexagon_percentage extra_hexagons = self.options.extra_hexagon_percentage
items_to_create[gold_hexagon] += int((Decimal(100 + extra_hexagons) / 100 * hexagon_goal).to_integral_value(rounding=ROUND_HALF_UP)) items_to_create[gold_hexagon] += int((Decimal(100 + extra_hexagons) / 100 * hexagon_goal).to_integral_value(rounding=ROUND_HALF_UP))
# Replace pages and normal hexagons with filler # Replace pages and normal hexagons with filler
for replaced_item in list(filter(lambda item: "Pages" in item or item in hexagon_locations, items_to_create)): for replaced_item in list(filter(lambda item: "Pages" in item or item in hexagon_locations, items_to_create)):
items_to_create[self.get_filler_item_name()] += items_to_create[replaced_item] filler_name = self.get_filler_item_name()
items_to_create[filler_name] += items_to_create[replaced_item]
if items_to_create[filler_name] >= 1 and filler_name not in available_filler:
available_filler.append(filler_name)
items_to_create[replaced_item] = 0 items_to_create[replaced_item] = 0
# Filler items that are still in the item pool to swap out remove_filler(items_to_create[gold_hexagon])
available_filler: List[str] = [filler for filler in items_to_create if items_to_create[filler] > 0 and
item_table[filler].classification == ItemClassification.filler]
# Remove filler to make room for extra hexagons
for i in range(0, items_to_create[gold_hexagon]):
fill = self.random.choice(available_filler)
items_to_create[fill] -= 1
if items_to_create[fill] == 0:
available_filler.remove(fill)
if self.options.maskless: if self.options.maskless:
mask_item = TunicItem("Scavenger Mask", ItemClassification.useful, self.item_name_to_id["Scavenger Mask"], self.player) mask_item = TunicItem("Scavenger Mask", ItemClassification.useful, self.item_name_to_id["Scavenger Mask"], self.player)
@ -147,8 +167,8 @@ class TunicWorld(World):
items_to_create["Scavenger Mask"] = 0 items_to_create["Scavenger Mask"] = 0
if self.options.lanternless: if self.options.lanternless:
mask_item = TunicItem("Lantern", ItemClassification.useful, self.item_name_to_id["Lantern"], self.player) lantern_item = TunicItem("Lantern", ItemClassification.useful, self.item_name_to_id["Lantern"], self.player)
tunic_items.append(mask_item) tunic_items.append(lantern_item)
items_to_create["Lantern"] = 0 items_to_create["Lantern"] = 0
for item, quantity in items_to_create.items(): for item, quantity in items_to_create.items():
@ -172,15 +192,16 @@ class TunicWorld(World):
self.ability_unlocks["Pages 24-25 (Prayer)"] = passthrough["Hexagon Quest Prayer"] self.ability_unlocks["Pages 24-25 (Prayer)"] = passthrough["Hexagon Quest Prayer"]
self.ability_unlocks["Pages 42-43 (Holy Cross)"] = passthrough["Hexagon Quest Holy Cross"] self.ability_unlocks["Pages 42-43 (Holy Cross)"] = passthrough["Hexagon Quest Holy Cross"]
self.ability_unlocks["Pages 52-53 (Icebolt)"] = passthrough["Hexagon Quest Icebolt"] self.ability_unlocks["Pages 52-53 (Icebolt)"] = passthrough["Hexagon Quest Icebolt"]
if self.options.entrance_rando:
portal_pairs, portal_hints = create_er_regions(self)
for portal1, portal2 in portal_pairs.items():
self.tunic_portal_pairs[portal1.scene_destination()] = portal2.scene_destination()
self.er_portal_hints = portal_hints
# ladder rando uses ER with vanilla connections, so that we're not managing more rules files
if self.options.entrance_rando or self.options.shuffle_ladders:
portal_pairs = create_er_regions(self)
if self.options.entrance_rando:
# these get interpreted by the game to tell it which entrances to connect
for portal1, portal2 in portal_pairs.items():
self.tunic_portal_pairs[portal1.scene_destination()] = portal2.scene_destination()
else: else:
# for non-ER, non-ladders
for region_name in tunic_regions: for region_name in tunic_regions:
region = Region(region_name, self.player, self.multiworld) region = Region(region_name, self.player, self.multiworld)
self.multiworld.regions.append(region) self.multiworld.regions.append(region)
@ -201,7 +222,7 @@ class TunicWorld(World):
victory_region.locations.append(victory_location) victory_region.locations.append(victory_location)
def set_rules(self) -> None: def set_rules(self) -> None:
if self.options.entrance_rando: if self.options.entrance_rando or self.options.shuffle_ladders:
set_er_location_rules(self, self.ability_unlocks) set_er_location_rules(self, self.ability_unlocks)
else: else:
set_region_rules(self, self.ability_unlocks) set_region_rules(self, self.ability_unlocks)
@ -212,7 +233,31 @@ class TunicWorld(World):
def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]): def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]):
if self.options.entrance_rando: if self.options.entrance_rando:
hint_data[self.player] = self.er_portal_hints hint_data.update({self.player: {}})
# all state seems to have efficient paths
all_state = self.multiworld.get_all_state(True)
all_state.update_reachable_regions(self.player)
paths = all_state.path
portal_names = [portal.name for portal in portal_mapping]
for location in self.multiworld.get_locations(self.player):
# skipping event locations
if not location.address:
continue
path_to_loc = []
previous_name = "placeholder"
name, connection = paths[location.parent_region]
while connection != ("Menu", None):
name, connection = connection
# for LS entrances, we just want to give the portal name
if "(LS)" in name:
name, _ = name.split(" (LS) ")
# was getting some cases like Library Grave -> Library Grave -> other place
if name in portal_names and name != previous_name:
previous_name = name
path_to_loc.append(name)
hint_text = " -> ".join(reversed(path_to_loc))
if hint_text:
hint_data[self.player][location.address] = hint_text
def fill_slot_data(self) -> Dict[str, Any]: def fill_slot_data(self) -> Dict[str, Any]:
slot_data: Dict[str, Any] = { slot_data: Dict[str, Any] = {
@ -226,7 +271,8 @@ class TunicWorld(World):
"logic_rules": self.options.logic_rules.value, "logic_rules": self.options.logic_rules.value,
"lanternless": self.options.lanternless.value, "lanternless": self.options.lanternless.value,
"maskless": self.options.maskless.value, "maskless": self.options.maskless.value,
"entrance_rando": bool(self.options.entrance_rando.value), "entrance_rando": int(bool(self.options.entrance_rando.value)),
"shuffle_ladders": self.options.shuffle_ladders.value,
"Hexagon Quest Prayer": self.ability_unlocks["Pages 24-25 (Prayer)"], "Hexagon Quest Prayer": self.ability_unlocks["Pages 24-25 (Prayer)"],
"Hexagon Quest Holy Cross": self.ability_unlocks["Pages 42-43 (Holy Cross)"], "Hexagon Quest Holy Cross": self.ability_unlocks["Pages 42-43 (Holy Cross)"],
"Hexagon Quest Icebolt": self.ability_unlocks["Pages 52-53 (Icebolt)"], "Hexagon Quest Icebolt": self.ability_unlocks["Pages 52-53 (Icebolt)"],

View File

@ -67,7 +67,7 @@ For the Entrance Randomizer:
Bombs, consumables (non-bomb ones), weapons, melee weapons (stick and sword), keys, hexagons, offerings, hero relics, cards, golden treasures, money, pages, and abilities (the three ability pages). There are also a few groups being used for singular items: laurels, orb, dagger, magic rod, holy cross, prayer, icebolt, and progressive sword. Bombs, consumables (non-bomb ones), weapons, melee weapons (stick and sword), keys, hexagons, offerings, hero relics, cards, golden treasures, money, pages, and abilities (the three ability pages). There are also a few groups being used for singular items: laurels, orb, dagger, magic rod, holy cross, prayer, icebolt, and progressive sword.
## What location groups are there? ## What location groups are there?
Holy cross (for all holy cross checks), fairies (for the two fairy checks), well (for the coin well checks), and shop. Additionally, for checks that do not fall into the above categories, the name of the region is the name of the location group. Holy cross (for all holy cross checks), fairies (for the two fairy checks), well (for the coin well checks), shop, bosses (for the bosses with checks associated with them), hero relic (for the 6 hero grave checks), and ladders (for the ladder items when you have shuffle ladders enabled).
## Is Connection Plando supported? ## Is Connection Plando supported?
Yes. The host needs to enable it in their `host.yaml`, and the player's yaml needs to contain a plando_connections block. Yes. The host needs to enable it in their `host.yaml`, and the player's yaml needs to contain a plando_connections block.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
from typing import Dict, List, Set, Tuple, 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, hallway_helper, hallway_helper_ur, \ from .er_data import Portal, tunic_er_regions, portal_mapping, \
dependent_regions_restricted, dependent_regions_nmg, dependent_regions_ur dependent_regions_restricted, dependent_regions_nmg, dependent_regions_ur
from .er_rules import set_er_region_rules from .er_rules import set_er_region_rules
from worlds.generic import PlandoConnection from worlds.generic import PlandoConnection
@ -19,118 +19,26 @@ class TunicERLocation(Location):
game: str = "TUNIC" game: str = "TUNIC"
def create_er_regions(world: "TunicWorld") -> Tuple[Dict[Portal, Portal], Dict[int, str]]: def create_er_regions(world: "TunicWorld") -> Dict[Portal, Portal]:
regions: Dict[str, Region] = {} regions: Dict[str, Region] = {}
portal_pairs: Dict[Portal, Portal] = pair_portals(world) if world.options.entrance_rando:
logic_rules = world.options.logic_rules portal_pairs: Dict[Portal, Portal] = pair_portals(world)
# output the entrances to the spoiler log here for convenience # output the entrances to the spoiler log here for convenience
for portal1, portal2 in portal_pairs.items(): for portal1, portal2 in portal_pairs.items():
world.multiworld.spoiler.set_entrance(portal1.name, portal2.name, "both", world.player) world.multiworld.spoiler.set_entrance(portal1.name, portal2.name, "both", world.player)
else:
portal_pairs: Dict[Portal, Portal] = vanilla_portals()
# check if a portal leads to a hallway. if it does, update the hint text accordingly
def hint_helper(portal: Portal, hint_string: str = "") -> str:
# start by setting it as the name of the portal, for the case we're not using the hallway helper
if hint_string == "":
hint_string = portal.name
# unrestricted has fewer hallways, like the well rail
if logic_rules == "unrestricted":
hallways = hallway_helper_ur
else:
hallways = hallway_helper
if portal.scene_destination() in hallways:
# if we have a hallway, we want the region rather than the portal name
if hint_string == portal.name:
hint_string = portal.region
# library exterior is two regions, we just want to fix up the name
if hint_string in {"Library Exterior Tree", "Library Exterior Ladder"}:
hint_string = "Library Exterior"
# search through the list for the other end of the hallway
for portala, portalb in portal_pairs.items():
if portala.scene_destination() == hallways[portal.scene_destination()]:
# if we find that we have a chain of hallways, do recursion
if portalb.scene_destination() in hallways:
hint_region = portalb.region
if hint_region in {"Library Exterior Tree", "Library Exterior Ladder"}:
hint_region = "Library Exterior"
hint_string = hint_region + " then " + hint_string
hint_string = hint_helper(portalb, hint_string)
else:
# if we didn't find a chain, get the portal name for the end of the chain
hint_string = portalb.name + " then " + hint_string
return hint_string
# and then the same thing for the other portal, since we have to check each separately
if portalb.scene_destination() == hallways[portal.scene_destination()]:
if portala.scene_destination() in hallways:
hint_region = portala.region
if hint_region in {"Library Exterior Tree", "Library Exterior Ladder"}:
hint_region = "Library Exterior"
hint_string = hint_region + " then " + hint_string
hint_string = hint_helper(portala, hint_string)
else:
hint_string = portala.name + " then " + hint_string
return hint_string
return hint_string
# create our regions, give them hint text if they're in a spot where it makes sense to
# we're limiting which ones get hints so that it still gets that ER feel with a little less BS
for region_name, region_data in tunic_er_regions.items(): for region_name, region_data in tunic_er_regions.items():
hint_text = "error" regions[region_name] = Region(region_name, world.player, world.multiworld)
if region_data.hint == 1:
for portal1, portal2 in portal_pairs.items():
if portal1.region == region_name:
hint_text = hint_helper(portal2)
break
if portal2.region == region_name:
hint_text = hint_helper(portal1)
break
regions[region_name] = Region(region_name, world.player, world.multiworld, hint_text)
elif region_data.hint == 2:
for portal1, portal2 in portal_pairs.items():
if portal1.scene() == tunic_er_regions[region_name].game_scene:
hint_text = hint_helper(portal2)
break
if portal2.scene() == tunic_er_regions[region_name].game_scene:
hint_text = hint_helper(portal1)
break
regions[region_name] = Region(region_name, world.player, world.multiworld, hint_text)
elif region_data.hint == 3:
# west garden portal item is at a dead end in restricted, otherwise just in west garden
if region_name == "West Garden Portal Item":
if world.options.logic_rules:
for portal1, portal2 in portal_pairs.items():
if portal1.scene() == "Archipelagos Redux":
hint_text = hint_helper(portal2)
break
if portal2.scene() == "Archipelagos Redux":
hint_text = hint_helper(portal1)
break
regions[region_name] = Region(region_name, world.player, world.multiworld, hint_text)
else:
for portal1, portal2 in portal_pairs.items():
if portal1.region == "West Garden Portal":
hint_text = hint_helper(portal2)
break
if portal2.region == "West Garden Portal":
hint_text = hint_helper(portal1)
break
regions[region_name] = Region(region_name, world.player, world.multiworld, hint_text)
else:
regions[region_name] = Region(region_name, world.player, world.multiworld)
set_er_region_rules(world, world.ability_unlocks, regions, portal_pairs) set_er_region_rules(world, world.ability_unlocks, regions, portal_pairs)
er_hint_data: Dict[int, str] = {}
for location_name, location_id in world.location_name_to_id.items(): for location_name, location_id in world.location_name_to_id.items():
region = regions[location_table[location_name].er_region] region = regions[location_table[location_name].er_region]
location = TunicERLocation(world.player, location_name, location_id, region) location = TunicERLocation(world.player, location_name, location_id, region)
region.locations.append(location) region.locations.append(location)
if region.name == region.hint_text:
continue
er_hint_data[location.address] = region.hint_text
create_randomized_entrances(portal_pairs, regions) create_randomized_entrances(portal_pairs, regions)
@ -145,14 +53,12 @@ def create_er_regions(world: "TunicWorld") -> Tuple[Dict[Portal, Portal], Dict[i
world.multiworld.completion_condition[world.player] = lambda state: state.has("Victory", world.player) world.multiworld.completion_condition[world.player] = lambda state: state.has("Victory", world.player)
victory_region.locations.append(victory_location) victory_region.locations.append(victory_location)
portals_and_hints = (portal_pairs, er_hint_data) return portal_pairs
return portals_and_hints
tunic_events: Dict[str, str] = { tunic_events: Dict[str, str] = {
"Eastern Bell": "Forest Belltower Upper", "Eastern Bell": "Forest Belltower Upper",
"Western Bell": "Overworld Belltower", "Western Bell": "Overworld Belltower at Bell",
"Furnace Fuse": "Furnace Fuse", "Furnace Fuse": "Furnace Fuse",
"South and West Fortress Exterior Fuses": "Fortress Exterior from Overworld", "South and West Fortress Exterior Fuses": "Fortress Exterior from Overworld",
"Upper and Central Fortress Exterior Fuses": "Fortress Courtyard Upper", "Upper and Central Fortress Exterior Fuses": "Fortress Courtyard Upper",
@ -163,7 +69,7 @@ tunic_events: Dict[str, str] = {
"Quarry Fuse": "Quarry", "Quarry Fuse": "Quarry",
"Ziggurat Fuse": "Rooted Ziggurat Lower Back", "Ziggurat Fuse": "Rooted Ziggurat Lower Back",
"West Garden Fuse": "West Garden", "West Garden Fuse": "West Garden",
"Library Fuse": "Library Lab", "Library Fuse": "Library Lab"
} }
@ -180,6 +86,38 @@ 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]:
portal_pairs: Dict[Portal, Portal] = {}
portal_map = portal_mapping.copy()
shop_num = 1
while portal_map:
portal1 = portal_map[0]
portal2 = None
# portal2 scene destination tag is portal1's destination scene tag
portal2_sdt = portal1.destination_scene()
if portal2_sdt.startswith("Shop,"):
portal2 = Portal(name=f"Shop", region="Shop",
destination="Previous Region", tag="_")
shop_num += 1
if portal2_sdt == "Purgatory, Purgatory_bottom":
portal2_sdt = "Purgatory, Purgatory_top"
for portal in portal_map:
if portal.scene_destination() == portal2_sdt:
portal2 = portal
break
portal_pairs[portal1] = portal2
portal_map.remove(portal1)
if not portal2_sdt.startswith("Shop,"):
portal_map.remove(portal2)
return portal_pairs
# 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") -> Dict[Portal, Portal]:
# separate the portals into dead ends and non-dead ends # separate the portals into dead ends and non-dead ends
@ -290,7 +228,7 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
break break
if p_exit in ["Shop Portal", "Shop"]: if p_exit in ["Shop Portal", "Shop"]:
portal2 = Portal(name="Shop Portal", region=f"Shop", portal2 = Portal(name="Shop Portal", region=f"Shop",
destination="Previous Region_") destination="Previous Region", tag="_")
shop_count -= 1 shop_count -= 1
if shop_count < 0: if shop_count < 0:
shop_count += 2 shop_count += 2
@ -355,10 +293,12 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
if portal.scene_destination() == "Overworld Redux, Windmill_": if portal.scene_destination() == "Overworld Redux, Windmill_":
portal1 = portal portal1 = portal
break break
portal2 = Portal(name="Shop Portal", region="Shop", destination="Previous Region_")
if not portal1: if not portal1:
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="_")
portal_pairs[portal1] = portal2 portal_pairs[portal1] = portal2
two_plus.remove(portal1) two_plus.remove(portal1)
@ -433,7 +373,8 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
break break
if portal1 is None: if portal1 is None:
raise Exception("Too many shops in the pool, or something else went wrong.") raise Exception("Too many shops in the pool, or something else went wrong.")
portal2 = Portal(name="Shop Portal", region="Shop", destination="Previous Region_") portal2 = Portal(name="Shop Portal", region="Shop", destination="Previous Region", tag="_")
portal_pairs[portal1] = portal2 portal_pairs[portal1] = portal2
# connect dead ends to random non-dead ends # connect dead ends to random non-dead ends
@ -465,10 +406,10 @@ def create_randomized_entrances(portal_pairs: Dict[Portal, Portal], regions: Dic
for portal1, portal2 in portal_pairs.items(): for portal1, portal2 in portal_pairs.items():
region1 = regions[portal1.region] region1 = regions[portal1.region]
region2 = regions[portal2.region] region2 = regions[portal2.region]
region1.connect(region2, f"{portal1.name} -> {portal2.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 # prevent the logic from thinking you can get to any shop-connected region from the shop
if not portal2.name.startswith("Shop"): if portal2.name not in {"Shop", "Shop Portal"}:
region2.connect(region1, f"{portal2.name} -> {portal1.name}") region2.connect(connecting_region=region1, name=portal2.name)
# loop through the static connections, return regions you can reach from this region # loop through the static connections, return regions you can reach from this region
@ -519,8 +460,8 @@ def gate_before_switch(check_portal: Portal, two_plus: List[Portal]) -> bool:
return True return True
# fortress teleporter needs only the left fuses # fortress teleporter needs only the left fuses
elif check_portal.scene_destination() in ["Fortress Arena, Transit_teleporter_spidertank", elif check_portal.scene_destination() in {"Fortress Arena, Transit_teleporter_spidertank",
"Transit, Fortress Arena_teleporter_spidertank"]: "Transit, Fortress Arena_teleporter_spidertank"}:
i = j = k = 0 i = j = k = 0
for portal in two_plus: for portal in two_plus:
if portal.scene() == "Fortress Courtyard": if portal.scene() == "Fortress Courtyard":
@ -537,7 +478,8 @@ def gate_before_switch(check_portal: Portal, two_plus: List[Portal]) -> bool:
elif check_portal.scene_destination() == "Swamp Redux 2, Cathedral Redux_main": elif check_portal.scene_destination() == "Swamp Redux 2, Cathedral Redux_main":
i = 0 i = 0
for portal in two_plus: for portal in two_plus:
if portal.region == "Swamp": if portal.region in {"Swamp Front", "Swamp to Cathedral Treasure Room",
"Swamp to Cathedral Main Entrance Region"}:
i += 1 i += 1
if i == 4: if i == 4:
return True return True
@ -553,8 +495,8 @@ def gate_before_switch(check_portal: Portal, two_plus: List[Portal]) -> bool:
# Quarry teleporter needs you to hit the Darkwoods fuse # Quarry teleporter needs you to hit the Darkwoods fuse
# Since it's physically in Quarry, we don't need to check for it # Since it's physically in Quarry, we don't need to check for it
elif check_portal.scene_destination() in ["Quarry Redux, Transit_teleporter_quarry teleporter", elif check_portal.scene_destination() in {"Quarry Redux, Transit_teleporter_quarry teleporter",
"Quarry Redux, ziggurat2020_0_"]: "Quarry Redux, ziggurat2020_0_"}:
i = 0 i = 0
for portal in two_plus: for portal in two_plus:
if portal.scene() == "Darkwoods Tunnel": if portal.scene() == "Darkwoods Tunnel":

View File

@ -143,6 +143,28 @@ item_table: Dict[str, TunicItemData] = {
"Pages 50-51": TunicItemData(ItemClassification.useful, 1, 127, "pages"), "Pages 50-51": TunicItemData(ItemClassification.useful, 1, 127, "pages"),
"Pages 52-53 (Icebolt)": TunicItemData(ItemClassification.progression, 1, 128, "pages"), "Pages 52-53 (Icebolt)": TunicItemData(ItemClassification.progression, 1, 128, "pages"),
"Pages 54-55": TunicItemData(ItemClassification.useful, 1, 129, "pages"), "Pages 54-55": TunicItemData(ItemClassification.useful, 1, 129, "pages"),
"Ladders near Weathervane": TunicItemData(ItemClassification.progression, 0, 130, "ladders"),
"Ladders near Overworld Checkpoint": TunicItemData(ItemClassification.progression, 0, 131, "ladders"),
"Ladders near Patrol Cave": TunicItemData(ItemClassification.progression, 0, 132, "ladders"),
"Ladder near Temple Rafters": TunicItemData(ItemClassification.progression, 0, 133, "ladders"),
"Ladders near Dark Tomb": TunicItemData(ItemClassification.progression, 0, 134, "ladders"),
"Ladder to Quarry": TunicItemData(ItemClassification.progression, 0, 135, "ladders"),
"Ladders to West Bell": TunicItemData(ItemClassification.progression, 0, 136, "ladders"),
"Ladders in Overworld Town": TunicItemData(ItemClassification.progression, 0, 137, "ladders"),
"Ladder to Ruined Atoll": TunicItemData(ItemClassification.progression, 0, 138, "ladders"),
"Ladder to Swamp": TunicItemData(ItemClassification.progression, 0, 139, "ladders"),
"Ladders in Well": TunicItemData(ItemClassification.progression, 0, 140, "ladders"),
"Ladder in Dark Tomb": TunicItemData(ItemClassification.progression, 0, 141, "ladders"),
"Ladder to East Forest": TunicItemData(ItemClassification.progression, 0, 142, "ladders"),
"Ladders to Lower Forest": TunicItemData(ItemClassification.progression, 0, 143, "ladders"),
"Ladder to Beneath the Vault": TunicItemData(ItemClassification.progression, 0, 144, "ladders"),
"Ladders in Hourglass Cave": TunicItemData(ItemClassification.progression, 0, 145, "ladders"),
"Ladders in South Atoll": TunicItemData(ItemClassification.progression, 0, 146, "ladders"),
"Ladders to Frog's Domain": TunicItemData(ItemClassification.progression, 0, 147, "ladders"),
"Ladders in Library": TunicItemData(ItemClassification.progression, 0, 148, "ladders"),
"Ladders in Lower Quarry": TunicItemData(ItemClassification.progression, 0, 149, "ladders"),
"Ladders in Swamp": TunicItemData(ItemClassification.progression, 0, 150, "ladders"),
} }
fool_tiers: List[List[str]] = [ fool_tiers: List[List[str]] = [
@ -209,7 +231,9 @@ extra_groups: Dict[str, Set[str]] = {
"melee weapons": {"Stick", "Sword", "Sword Upgrade"}, "melee weapons": {"Stick", "Sword", "Sword Upgrade"},
"progressive sword": {"Sword Upgrade"}, "progressive sword": {"Sword Upgrade"},
"abilities": {"Pages 24-25 (Prayer)", "Pages 42-43 (Holy Cross)", "Pages 52-53 (Icebolt)"}, "abilities": {"Pages 24-25 (Prayer)", "Pages 42-43 (Holy Cross)", "Pages 52-53 (Icebolt)"},
"questagons": {"Red Questagon", "Green Questagon", "Blue Questagon", "Gold Questagon"} "questagons": {"Red Questagon", "Green Questagon", "Blue Questagon", "Gold Questagon"},
"ladder to atoll": {"Ladder to Ruined Atoll"}, # fuzzy matching made it hint Ladders in Well, now it won't
"ladders to bell": {"Ladders to West Bell"},
} }
item_name_groups.update(extra_groups) item_name_groups.update(extra_groups)

View File

@ -1,11 +1,11 @@
from typing import Dict, NamedTuple, Set from typing import Dict, NamedTuple, Set, Optional, List
from itertools import groupby
class TunicLocationData(NamedTuple): class TunicLocationData(NamedTuple):
region: str region: str
er_region: str # entrance rando region er_region: str # entrance rando region
location_group: str = "region" location_group: Optional[str] = None
location_groups: Optional[List[str]] = None
location_base_id = 509342400 location_base_id = 509342400
@ -22,10 +22,10 @@ location_table: Dict[str, TunicLocationData] = {
"Beneath the Well - [Back Corridor] Right Secret": TunicLocationData("Beneath the Well", "Beneath the Well Main"), "Beneath the Well - [Back Corridor] Right Secret": TunicLocationData("Beneath the Well", "Beneath the Well Main"),
"Beneath the Well - [Back Corridor] Left Secret": TunicLocationData("Beneath the Well", "Beneath the Well Main"), "Beneath the Well - [Back Corridor] Left Secret": TunicLocationData("Beneath the Well", "Beneath the Well Main"),
"Beneath the Well - [Second Room] Obscured Behind Waterfall": TunicLocationData("Beneath the Well", "Beneath the Well Main"), "Beneath the Well - [Second Room] Obscured Behind Waterfall": TunicLocationData("Beneath the Well", "Beneath the Well Main"),
"Beneath the Well - [Side Room] Chest By Pots": TunicLocationData("Beneath the Well", "Beneath the Well Main"), "Beneath the Well - [Side Room] Chest By Pots": TunicLocationData("Beneath the Well", "Beneath the Well Back"),
"Beneath the Well - [Side Room] Chest By Phrends": TunicLocationData("Beneath the Well", "Beneath the Well Back"), "Beneath the Well - [Side Room] Chest By Phrends": TunicLocationData("Beneath the Well", "Beneath the Well Back"),
"Beneath the Well - [Second Room] Page": TunicLocationData("Beneath the Well", "Beneath the Well Main"), "Beneath the Well - [Second Room] Page": TunicLocationData("Beneath the Well", "Beneath the Well Main"),
"Dark Tomb Checkpoint - [Passage To Dark Tomb] Page Pickup": TunicLocationData("Beneath the Well", "Dark Tomb Checkpoint"), "Dark Tomb Checkpoint - [Passage To Dark Tomb] Page Pickup": TunicLocationData("Overworld", "Dark Tomb Checkpoint"),
"Cathedral - [1F] Guarded By Lasers": TunicLocationData("Cathedral", "Cathedral"), "Cathedral - [1F] Guarded By Lasers": TunicLocationData("Cathedral", "Cathedral"),
"Cathedral - [1F] Near Spikes": TunicLocationData("Cathedral", "Cathedral"), "Cathedral - [1F] Near Spikes": TunicLocationData("Cathedral", "Cathedral"),
"Cathedral - [2F] Bird Room": TunicLocationData("Cathedral", "Cathedral"), "Cathedral - [2F] Bird Room": TunicLocationData("Cathedral", "Cathedral"),
@ -39,25 +39,25 @@ location_table: Dict[str, TunicLocationData] = {
"Dark Tomb - 2nd Laser Room": TunicLocationData("Dark Tomb", "Dark Tomb Main"), "Dark Tomb - 2nd Laser Room": TunicLocationData("Dark Tomb", "Dark Tomb Main"),
"Dark Tomb - 1st Laser Room": TunicLocationData("Dark Tomb", "Dark Tomb Main"), "Dark Tomb - 1st Laser Room": TunicLocationData("Dark Tomb", "Dark Tomb Main"),
"Dark Tomb - Spike Maze Upper Walkway": TunicLocationData("Dark Tomb", "Dark Tomb Main"), "Dark Tomb - Spike Maze Upper Walkway": TunicLocationData("Dark Tomb", "Dark Tomb Main"),
"Dark Tomb - Skulls Chest": TunicLocationData("Dark Tomb", "Dark Tomb Main"), "Dark Tomb - Skulls Chest": TunicLocationData("Dark Tomb", "Dark Tomb Upper"),
"Dark Tomb - Spike Maze Near Stairs": TunicLocationData("Dark Tomb", "Dark Tomb Main"), "Dark Tomb - Spike Maze Near Stairs": TunicLocationData("Dark Tomb", "Dark Tomb Main"),
"Dark Tomb - 1st Laser Room Obscured": TunicLocationData("Dark Tomb", "Dark Tomb Main"), "Dark Tomb - 1st Laser Room Obscured": TunicLocationData("Dark Tomb", "Dark Tomb Main"),
"Guardhouse 2 - Upper Floor": TunicLocationData("East Forest", "Guard House 2"), "Guardhouse 2 - Upper Floor": TunicLocationData("East Forest", "Guard House 2 Upper"),
"Guardhouse 2 - Bottom Floor Secret": TunicLocationData("East Forest", "Guard House 2"), "Guardhouse 2 - Bottom Floor Secret": TunicLocationData("East Forest", "Guard House 2 Lower"),
"Guardhouse 1 - Upper Floor Obscured": TunicLocationData("East Forest", "Guard House 1 East"), "Guardhouse 1 - Upper Floor Obscured": TunicLocationData("East Forest", "Guard House 1 East"),
"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", "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", "East Forest", "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", "East 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"),
"East Forest - Near Save Point": TunicLocationData("East Forest", "East Forest"), "East Forest - Near Save Point": TunicLocationData("East Forest", "East Forest"),
"East Forest - Beneath Spider Chest": TunicLocationData("East Forest", "East Forest"), "East Forest - Beneath Spider Chest": TunicLocationData("East Forest", "Lower Forest"),
"East Forest - Near Telescope": TunicLocationData("East Forest", "East Forest"), "East Forest - Near Telescope": TunicLocationData("East Forest", "East Forest"),
"East Forest - Spider Chest": TunicLocationData("East Forest", "East Forest"), "East Forest - Spider Chest": TunicLocationData("East Forest", "Lower Forest"),
"East Forest - Lower Dash Chest": TunicLocationData("East Forest", "East Forest"), "East Forest - Lower Dash Chest": TunicLocationData("East Forest", "Lower Forest"),
"East Forest - Lower Grapple Chest": TunicLocationData("East Forest", "East Forest"), "East Forest - Lower Grapple Chest": TunicLocationData("East Forest", "Lower Forest"),
"East Forest - Bombable Wall": TunicLocationData("East Forest", "East Forest"), "East Forest - Bombable Wall": TunicLocationData("East Forest", "East Forest"),
"East Forest - Page On Teleporter": TunicLocationData("East Forest", "East Forest"), "East Forest - Page On Teleporter": TunicLocationData("East Forest", "East Forest"),
"Forest Belltower - Near Save Point": TunicLocationData("East Forest", "Forest Belltower Lower"), "Forest Belltower - Near Save Point": TunicLocationData("East Forest", "Forest Belltower Lower"),
@ -65,18 +65,18 @@ location_table: Dict[str, TunicLocationData] = {
"Forest Belltower - Obscured Near Bell Top Floor": TunicLocationData("East Forest", "Forest Belltower Upper"), "Forest Belltower - Obscured Near Bell Top Floor": TunicLocationData("East Forest", "Forest Belltower Upper"),
"Forest Belltower - Obscured Beneath Bell Bottom Floor": TunicLocationData("East Forest", "Forest Belltower Main"), "Forest Belltower - Obscured Beneath Bell Bottom Floor": TunicLocationData("East Forest", "Forest Belltower Main"),
"Forest Belltower - Page Pickup": TunicLocationData("East Forest", "Forest Belltower Main"), "Forest Belltower - Page Pickup": TunicLocationData("East Forest", "Forest Belltower Main"),
"Forest Grave Path - Holy Cross Code by Grave": TunicLocationData("East Forest", "Forest Grave Path by Grave", "holy cross"), "Forest Grave Path - Holy Cross Code by Grave": TunicLocationData("East Forest", "Forest Grave Path by Grave", location_group="holy cross"),
"Forest Grave Path - Above Gate": TunicLocationData("East Forest", "Forest Grave Path Main"), "Forest Grave Path - Above Gate": TunicLocationData("East Forest", "Forest Grave Path Main"),
"Forest Grave Path - Obscured Chest": TunicLocationData("East Forest", "Forest Grave Path Main"), "Forest Grave Path - Obscured Chest": TunicLocationData("East Forest", "Forest Grave Path Main"),
"Forest Grave Path - Upper Walkway": TunicLocationData("East Forest", "Forest Grave Path Upper"), "Forest Grave Path - Upper Walkway": TunicLocationData("East Forest", "Forest Grave Path Upper"),
"Forest Grave Path - Sword Pickup": TunicLocationData("East Forest", "Forest Grave Path by Grave"), "Forest Grave Path - Sword Pickup": TunicLocationData("East Forest", "Forest Grave Path by Grave"),
"Hero's Grave - Tooth Relic": TunicLocationData("East Forest", "Hero Relic - East Forest"), "Hero's Grave - Tooth Relic": TunicLocationData("East Forest", "Hero Relic - East Forest", location_group="hero relic"),
"Fortress Courtyard - From East Belltower": TunicLocationData("East Forest", "Fortress Exterior from East Forest"), "Fortress Courtyard - From East Belltower": TunicLocationData("East Forest", "Fortress Exterior from East Forest"),
"Fortress Leaf Piles - Secret Chest": TunicLocationData("Eastern Vault Fortress", "Fortress Leaf Piles"), "Fortress Leaf Piles - Secret Chest": TunicLocationData("Eastern Vault Fortress", "Fortress Leaf Piles"),
"Fortress Arena - Hexagon Red": TunicLocationData("Eastern Vault Fortress", "Fortress Arena"), "Fortress Arena - Hexagon Red": TunicLocationData("Eastern Vault Fortress", "Fortress Arena"),
"Fortress Arena - Siege Engine/Vault Key Pickup": TunicLocationData("Eastern Vault Fortress", "Fortress Arena"), "Fortress Arena - Siege Engine/Vault Key Pickup": TunicLocationData("Eastern Vault Fortress", "Fortress Arena", location_group="bosses"),
"Fortress East Shortcut - Chest Near Slimes": TunicLocationData("Eastern Vault Fortress", "Fortress East Shortcut Lower"), "Fortress East Shortcut - Chest Near Slimes": TunicLocationData("Eastern Vault Fortress", "Fortress East Shortcut Lower"),
"Eastern Vault Fortress - [West Wing] Candles Holy Cross": TunicLocationData("Eastern Vault Fortress", "Eastern Vault Fortress", "holy cross"), "Eastern Vault Fortress - [West Wing] Candles Holy Cross": TunicLocationData("Eastern Vault Fortress", "Eastern Vault Fortress", location_group="holy cross"),
"Eastern Vault Fortress - [West Wing] Dark Room Chest 1": TunicLocationData("Eastern Vault Fortress", "Eastern Vault Fortress"), "Eastern Vault Fortress - [West Wing] Dark Room Chest 1": TunicLocationData("Eastern Vault Fortress", "Eastern Vault Fortress"),
"Eastern Vault Fortress - [West Wing] Dark Room Chest 2": TunicLocationData("Eastern Vault Fortress", "Eastern Vault Fortress"), "Eastern Vault Fortress - [West Wing] Dark Room Chest 2": TunicLocationData("Eastern Vault Fortress", "Eastern Vault Fortress"),
"Eastern Vault Fortress - [East Wing] Bombable Wall": TunicLocationData("Eastern Vault Fortress", "Eastern Vault Fortress"), "Eastern Vault Fortress - [East Wing] Bombable Wall": TunicLocationData("Eastern Vault Fortress", "Eastern Vault Fortress"),
@ -84,7 +84,7 @@ location_table: Dict[str, TunicLocationData] = {
"Fortress Grave Path - Upper Walkway": TunicLocationData("Eastern Vault Fortress", "Fortress Grave Path Upper"), "Fortress Grave Path - Upper Walkway": TunicLocationData("Eastern Vault Fortress", "Fortress Grave Path Upper"),
"Fortress Grave Path - Chest Right of Grave": TunicLocationData("Eastern Vault Fortress", "Fortress Grave Path"), "Fortress Grave Path - Chest Right of Grave": TunicLocationData("Eastern Vault Fortress", "Fortress Grave Path"),
"Fortress Grave Path - Obscured Chest Left of Grave": TunicLocationData("Eastern Vault Fortress", "Fortress Grave Path"), "Fortress Grave Path - Obscured Chest Left of Grave": TunicLocationData("Eastern Vault Fortress", "Fortress Grave Path"),
"Hero's Grave - Flowers Relic": TunicLocationData("Eastern Vault Fortress", "Hero Relic - Fortress"), "Hero's Grave - Flowers Relic": TunicLocationData("Eastern Vault Fortress", "Hero Relic - Fortress", location_group="hero relic"),
"Beneath the Fortress - Bridge": TunicLocationData("Beneath the Vault", "Beneath the Vault Back"), "Beneath the Fortress - Bridge": TunicLocationData("Beneath the Vault", "Beneath the Vault Back"),
"Beneath the Fortress - Cell Chest 1": TunicLocationData("Beneath the Vault", "Beneath the Vault Back"), "Beneath the Fortress - Cell Chest 1": TunicLocationData("Beneath the Vault", "Beneath the Vault Back"),
"Beneath the Fortress - Obscured Behind Waterfall": TunicLocationData("Beneath the Vault", "Beneath the Vault Front"), "Beneath the Fortress - Obscured Behind Waterfall": TunicLocationData("Beneath the Vault", "Beneath the Vault Front"),
@ -101,8 +101,8 @@ location_table: Dict[str, TunicLocationData] = {
"Frog's Domain - Side Room Chest": TunicLocationData("Frog's Domain", "Frog's Domain"), "Frog's Domain - Side Room Chest": TunicLocationData("Frog's Domain", "Frog's Domain"),
"Frog's Domain - Side Room Grapple Secret": TunicLocationData("Frog's Domain", "Frog's Domain"), "Frog's Domain - Side Room Grapple Secret": TunicLocationData("Frog's Domain", "Frog's Domain"),
"Frog's Domain - Magic Orb Pickup": TunicLocationData("Frog's Domain", "Frog's Domain"), "Frog's Domain - Magic Orb Pickup": TunicLocationData("Frog's Domain", "Frog's Domain"),
"Librarian - Hexagon Green": TunicLocationData("Library", "Library Arena"), "Librarian - Hexagon Green": TunicLocationData("Library", "Library Arena", location_group="bosses"),
"Library Hall - Holy Cross Chest": TunicLocationData("Library", "Library Hall", "holy cross"), "Library Hall - Holy Cross Chest": TunicLocationData("Library", "Library Hall", location_group="holy cross"),
"Library Lab - Chest By Shrine 2": TunicLocationData("Library", "Library Lab"), "Library Lab - Chest By Shrine 2": TunicLocationData("Library", "Library Lab"),
"Library Lab - Chest By Shrine 1": TunicLocationData("Library", "Library Lab"), "Library Lab - Chest By Shrine 1": TunicLocationData("Library", "Library Lab"),
"Library Lab - Chest By Shrine 3": TunicLocationData("Library", "Library Lab"), "Library Lab - Chest By Shrine 3": TunicLocationData("Library", "Library Lab"),
@ -110,7 +110,7 @@ location_table: Dict[str, TunicLocationData] = {
"Library Lab - Page 3": TunicLocationData("Library", "Library Lab"), "Library Lab - Page 3": TunicLocationData("Library", "Library Lab"),
"Library Lab - Page 1": TunicLocationData("Library", "Library Lab"), "Library Lab - Page 1": TunicLocationData("Library", "Library Lab"),
"Library Lab - Page 2": TunicLocationData("Library", "Library Lab"), "Library Lab - Page 2": TunicLocationData("Library", "Library Lab"),
"Hero's Grave - Mushroom Relic": TunicLocationData("Library", "Hero Relic - Library"), "Hero's Grave - Mushroom Relic": TunicLocationData("Library", "Hero Relic - Library", location_group="hero relic"),
"Lower Mountain - Page Before Door": TunicLocationData("Overworld", "Lower Mountain"), "Lower Mountain - Page Before Door": TunicLocationData("Overworld", "Lower Mountain"),
"Changing Room - Normal Chest": TunicLocationData("Overworld", "Changing Room"), "Changing Room - Normal Chest": TunicLocationData("Overworld", "Changing Room"),
"Fortress Courtyard - Chest Near Cave": TunicLocationData("Overworld", "Fortress Exterior near cave"), "Fortress Courtyard - Chest Near Cave": TunicLocationData("Overworld", "Fortress Exterior near cave"),
@ -122,42 +122,42 @@ location_table: Dict[str, TunicLocationData] = {
"Old House - Normal Chest": TunicLocationData("Overworld", "Old House Front"), "Old House - Normal Chest": TunicLocationData("Overworld", "Old House Front"),
"Old House - Shield Pickup": TunicLocationData("Overworld", "Old House Front"), "Old House - Shield Pickup": TunicLocationData("Overworld", "Old House Front"),
"Overworld - [West] Obscured Behind Windmill": TunicLocationData("Overworld", "Overworld"), "Overworld - [West] Obscured Behind Windmill": TunicLocationData("Overworld", "Overworld"),
"Overworld - [South] Beach Chest": TunicLocationData("Overworld", "Overworld"), "Overworld - [South] Beach Chest": TunicLocationData("Overworld", "Overworld Beach"),
"Overworld - [West] Obscured Near Well": TunicLocationData("Overworld", "Overworld"), "Overworld - [West] Obscured Near Well": TunicLocationData("Overworld", "Overworld"),
"Overworld - [Central] Bombable Wall": TunicLocationData("Overworld", "Overworld"), "Overworld - [Central] Bombable Wall": TunicLocationData("Overworld", "Overworld"),
"Overworld - [Northwest] Chest Near Turret": TunicLocationData("Overworld", "Overworld"), "Overworld - [Northwest] Chest Near Turret": TunicLocationData("Overworld", "Overworld"),
"Overworld - [East] Chest Near Pots": TunicLocationData("Overworld", "Overworld"), "Overworld - [East] Chest Near Pots": TunicLocationData("Overworld", "East Overworld"),
"Overworld - [Northwest] Chest Near Golden Obelisk": TunicLocationData("Overworld", "Overworld"), "Overworld - [Northwest] Chest Near Golden Obelisk": TunicLocationData("Overworld", "Overworld above Quarry Entrance"),
"Overworld - [Southwest] South Chest Near Guard": TunicLocationData("Overworld", "Overworld"), "Overworld - [Southwest] South Chest Near Guard": TunicLocationData("Overworld", "Overworld"),
"Overworld - [Southwest] West Beach Guarded By Turret": TunicLocationData("Overworld", "Overworld"), "Overworld - [Southwest] West Beach Guarded By Turret": TunicLocationData("Overworld", "Overworld Beach"),
"Overworld - [Southwest] Chest Guarded By Turret": TunicLocationData("Overworld", "Overworld"), "Overworld - [Southwest] Chest Guarded By Turret": TunicLocationData("Overworld", "Overworld"),
"Overworld - [Northwest] Shadowy Corner Chest": TunicLocationData("Overworld", "Overworld"), "Overworld - [Northwest] Shadowy Corner Chest": TunicLocationData("Overworld", "Overworld"),
"Overworld - [Southwest] Obscured In Tunnel To Beach": TunicLocationData("Overworld", "Overworld"), "Overworld - [Southwest] Obscured In Tunnel To Beach": TunicLocationData("Overworld", "Overworld"),
"Overworld - [Southwest] Grapple Chest Over Walkway": TunicLocationData("Overworld", "Overworld"), "Overworld - [Southwest] Grapple Chest Over Walkway": TunicLocationData("Overworld", "Overworld"),
"Overworld - [Northwest] Chest Beneath Quarry Gate": TunicLocationData("Overworld", "Overworld"), "Overworld - [Northwest] Chest Beneath Quarry Gate": TunicLocationData("Overworld", "Overworld after Envoy"),
"Overworld - [Southeast] Chest Near Swamp": TunicLocationData("Overworld", "Overworld"), "Overworld - [Southeast] Chest Near Swamp": TunicLocationData("Overworld", "Overworld Swamp Lower Entry"),
"Overworld - [Southwest] From West Garden": TunicLocationData("Overworld", "Overworld"), "Overworld - [Southwest] From West Garden": TunicLocationData("Overworld", "Overworld Beach"),
"Overworld - [East] Grapple Chest": TunicLocationData("Overworld", "Overworld"), "Overworld - [East] Grapple Chest": TunicLocationData("Overworld", "Overworld above Patrol Cave"),
"Overworld - [Southwest] West Beach Guarded By Turret 2": TunicLocationData("Overworld", "Overworld"), "Overworld - [Southwest] West Beach Guarded By Turret 2": TunicLocationData("Overworld", "Overworld Beach"),
"Overworld - [Southwest] Beach Chest Near Flowers": TunicLocationData("Overworld", "Overworld"), "Overworld - [Southwest] Beach Chest Near Flowers": TunicLocationData("Overworld", "Overworld Beach"),
"Overworld - [Southwest] Bombable Wall Near Fountain": TunicLocationData("Overworld", "Overworld"), "Overworld - [Southwest] Bombable Wall Near Fountain": TunicLocationData("Overworld", "Overworld"),
"Overworld - [West] Chest After Bell": TunicLocationData("Overworld", "Overworld Belltower"), "Overworld - [West] Chest After Bell": TunicLocationData("Overworld", "Overworld Belltower"),
"Overworld - [Southwest] Tunnel Guarded By Turret": TunicLocationData("Overworld", "Overworld"), "Overworld - [Southwest] Tunnel Guarded By Turret": TunicLocationData("Overworld", "Overworld Tunnel Turret"),
"Overworld - [East] Between Ladders Near Ruined Passage": TunicLocationData("Overworld", "Overworld"), "Overworld - [East] Between Ladders Near Ruined Passage": TunicLocationData("Overworld", "Above Ruined Passage"),
"Overworld - [Northeast] Chest Above Patrol Cave": TunicLocationData("Overworld", "Overworld"), "Overworld - [Northeast] Chest Above Patrol Cave": TunicLocationData("Overworld", "Upper Overworld"),
"Overworld - [Southwest] Beach Chest Beneath Guard": TunicLocationData("Overworld", "Overworld"), "Overworld - [Southwest] Beach Chest Beneath Guard": TunicLocationData("Overworld", "Overworld Beach"),
"Overworld - [Central] Chest Across From Well": TunicLocationData("Overworld", "Overworld"), "Overworld - [Central] Chest Across From Well": TunicLocationData("Overworld", "Overworld"),
"Overworld - [Northwest] Chest Near Quarry Gate": TunicLocationData("Overworld", "Overworld"), "Overworld - [Northwest] Chest Near Quarry Gate": TunicLocationData("Overworld", "Overworld"),
"Overworld - [East] Chest In Trees": TunicLocationData("Overworld", "Overworld"), "Overworld - [East] Chest In Trees": TunicLocationData("Overworld", "Above Ruined Passage"),
"Overworld - [West] Chest Behind Moss Wall": TunicLocationData("Overworld", "Overworld"), "Overworld - [West] Chest Behind Moss Wall": TunicLocationData("Overworld", "Overworld"),
"Overworld - [South] Beach Page": TunicLocationData("Overworld", "Overworld"), "Overworld - [South] Beach Page": TunicLocationData("Overworld", "Overworld Beach"),
"Overworld - [Southeast] Page on Pillar by Swamp": TunicLocationData("Overworld", "Overworld"), "Overworld - [Southeast] Page on Pillar by Swamp": TunicLocationData("Overworld", "Overworld"),
"Overworld - [Southwest] Key Pickup": TunicLocationData("Overworld", "Overworld"), "Overworld - [Southwest] Key Pickup": TunicLocationData("Overworld", "Overworld"),
"Overworld - [West] Key Pickup": TunicLocationData("Overworld", "Overworld"), "Overworld - [West] Key Pickup": TunicLocationData("Overworld", "Overworld"),
"Overworld - [East] Page Near Secret Shop": TunicLocationData("Overworld", "Overworld"), "Overworld - [East] Page Near Secret Shop": TunicLocationData("Overworld", "East Overworld"),
"Overworld - [Southwest] Fountain Page": TunicLocationData("Overworld", "Overworld"), "Overworld - [Southwest] Fountain Page": TunicLocationData("Overworld", "Overworld"),
"Overworld - [Northwest] Page on Pillar by Dark Tomb": TunicLocationData("Overworld", "Overworld"), "Overworld - [Northwest] Page on Pillar by Dark Tomb": TunicLocationData("Overworld", "Overworld"),
"Overworld - [Northwest] Fire Wand Pickup": TunicLocationData("Overworld", "Overworld"), "Overworld - [Northwest] Fire Wand Pickup": TunicLocationData("Overworld", "Upper Overworld"),
"Overworld - [West] Page On Teleporter": TunicLocationData("Overworld", "Overworld"), "Overworld - [West] Page On Teleporter": TunicLocationData("Overworld", "Overworld"),
"Overworld - [Northwest] Page By Well": TunicLocationData("Overworld", "Overworld"), "Overworld - [Northwest] Page By Well": TunicLocationData("Overworld", "Overworld"),
"Patrol Cave - Normal Chest": TunicLocationData("Overworld", "Patrol Cave"), "Patrol Cave - Normal Chest": TunicLocationData("Overworld", "Patrol Cave"),
@ -165,49 +165,49 @@ location_table: Dict[str, TunicLocationData] = {
"Ruined Shop - Chest 2": TunicLocationData("Overworld", "Ruined Shop"), "Ruined Shop - Chest 2": TunicLocationData("Overworld", "Ruined Shop"),
"Ruined Shop - Chest 3": TunicLocationData("Overworld", "Ruined Shop"), "Ruined Shop - Chest 3": TunicLocationData("Overworld", "Ruined Shop"),
"Ruined Passage - Page Pickup": TunicLocationData("Overworld", "Ruined Passage"), "Ruined Passage - Page Pickup": TunicLocationData("Overworld", "Ruined Passage"),
"Shop - Potion 1": TunicLocationData("Overworld", "Shop", "shop"), "Shop - Potion 1": TunicLocationData("Overworld", "Shop", location_group="shop"),
"Shop - Potion 2": TunicLocationData("Overworld", "Shop", "shop"), "Shop - Potion 2": TunicLocationData("Overworld", "Shop", location_group="shop"),
"Shop - Coin 1": TunicLocationData("Overworld", "Shop", "shop"), "Shop - Coin 1": TunicLocationData("Overworld", "Shop", location_group="shop"),
"Shop - Coin 2": TunicLocationData("Overworld", "Shop", "shop"), "Shop - Coin 2": TunicLocationData("Overworld", "Shop", location_group="shop"),
"Special Shop - Secret Page Pickup": TunicLocationData("Overworld", "Special Shop"), "Special Shop - Secret Page Pickup": TunicLocationData("Overworld", "Special Shop"),
"Stick House - Stick Chest": TunicLocationData("Overworld", "Stick House"), "Stick House - Stick Chest": TunicLocationData("Overworld", "Stick House"),
"Sealed Temple - Page Pickup": TunicLocationData("Overworld", "Sealed Temple"), "Sealed Temple - Page Pickup": TunicLocationData("Overworld", "Sealed Temple"),
"Hourglass Cave - Hourglass Chest": TunicLocationData("Overworld", "Hourglass Cave"), "Hourglass Cave - Hourglass Chest": TunicLocationData("Overworld", "Hourglass Cave"),
"Far Shore - Secret Chest": TunicLocationData("Overworld", "Far Shore"), "Far Shore - Secret Chest": TunicLocationData("Overworld", "Far Shore"),
"Far Shore - Page Pickup": TunicLocationData("Overworld", "Far Shore to Spawn"), "Far Shore - Page Pickup": TunicLocationData("Overworld", "Far Shore to Spawn Region"),
"Coins in the Well - 10 Coins": TunicLocationData("Overworld", "Overworld", "well"), "Coins in the Well - 10 Coins": TunicLocationData("Overworld", "Overworld", location_group="well"),
"Coins in the Well - 15 Coins": TunicLocationData("Overworld", "Overworld", "well"), "Coins in the Well - 15 Coins": TunicLocationData("Overworld", "Overworld", location_group="well"),
"Coins in the Well - 3 Coins": TunicLocationData("Overworld", "Overworld", "well"), "Coins in the Well - 3 Coins": TunicLocationData("Overworld", "Overworld", location_group="well"),
"Coins in the Well - 6 Coins": TunicLocationData("Overworld", "Overworld", "well"), "Coins in the Well - 6 Coins": TunicLocationData("Overworld", "Overworld", location_group="well"),
"Secret Gathering Place - 20 Fairy Reward": TunicLocationData("Overworld", "Secret Gathering Place", "fairies"), "Secret Gathering Place - 20 Fairy Reward": TunicLocationData("Overworld", "Secret Gathering Place", location_group="fairies"),
"Secret Gathering Place - 10 Fairy Reward": TunicLocationData("Overworld", "Secret Gathering Place", "fairies"), "Secret Gathering Place - 10 Fairy Reward": TunicLocationData("Overworld", "Secret Gathering Place", location_group="fairies"),
"Overworld - [West] Moss Wall Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", "holy cross"), "Overworld - [West] Moss Wall Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", location_group="holy cross"),
"Overworld - [Southwest] Flowers Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", "holy cross"), "Overworld - [Southwest] Flowers Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Beach", location_group="holy cross"),
"Overworld - [Southwest] Fountain Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", "holy cross"), "Overworld - [Southwest] Fountain Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", location_group="holy cross"),
"Overworld - [Northeast] Flowers Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", "holy cross"), "Overworld - [Northeast] Flowers Holy Cross": TunicLocationData("Overworld Holy Cross", "East Overworld", location_group="holy cross"),
"Overworld - [East] Weathervane Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", "holy cross"), "Overworld - [East] Weathervane Holy Cross": TunicLocationData("Overworld Holy Cross", "East Overworld", location_group="holy cross"),
"Overworld - [West] Windmill Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", "holy cross"), "Overworld - [West] Windmill Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", location_group="holy cross"),
"Overworld - [Southwest] Haiku Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", "holy cross"), "Overworld - [Southwest] Haiku Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Beach", location_group="holy cross"),
"Overworld - [West] Windchimes Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", "holy cross"), "Overworld - [West] Windchimes Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", location_group="holy cross"),
"Overworld - [South] Starting Platform Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", "holy cross"), "Overworld - [South] Starting Platform Holy Cross": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", location_group="holy cross"),
"Overworld - [Northwest] Golden Obelisk Page": TunicLocationData("Overworld Holy Cross", "Overworld Holy Cross", "holy cross"), "Overworld - [Northwest] Golden Obelisk Page": TunicLocationData("Overworld Holy Cross", "Upper Overworld", location_group="holy cross"),
"Old House - Holy Cross Door Page": TunicLocationData("Overworld Holy Cross", "Old House Back", "holy cross"), "Old House - Holy Cross Door Page": TunicLocationData("Overworld Holy Cross", "Old House Back", location_group="holy cross"),
"Cube Cave - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Cube Cave", "holy cross"), "Cube Cave - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Cube Cave", location_group="holy cross"),
"Southeast Cross Door - Chest 3": TunicLocationData("Overworld Holy Cross", "Southeast Cross Room", "holy cross"), "Southeast Cross Door - Chest 3": TunicLocationData("Overworld Holy Cross", "Southeast Cross Room", location_group="holy cross"),
"Southeast Cross Door - Chest 2": TunicLocationData("Overworld Holy Cross", "Southeast Cross Room", "holy cross"), "Southeast Cross Door - Chest 2": TunicLocationData("Overworld Holy Cross", "Southeast Cross Room", location_group="holy cross"),
"Southeast Cross Door - Chest 1": TunicLocationData("Overworld Holy Cross", "Southeast Cross Room", "holy cross"), "Southeast Cross Door - Chest 1": TunicLocationData("Overworld Holy Cross", "Southeast Cross Room", location_group="holy cross"),
"Maze Cave - Maze Room Holy Cross": TunicLocationData("Overworld Holy Cross", "Maze Cave", "holy cross"), "Maze Cave - Maze Room Holy Cross": TunicLocationData("Overworld Holy Cross", "Maze Cave", location_group="holy cross"),
"Caustic Light Cave - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Caustic Light Cave", "holy cross"), "Caustic Light Cave - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Caustic Light Cave", location_group="holy cross"),
"Old House - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Old House Front", "holy cross"), "Old House - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Old House Front", location_group="holy cross"),
"Patrol Cave - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Patrol Cave", "holy cross"), "Patrol Cave - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Patrol Cave", location_group="holy cross"),
"Ruined Passage - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Ruined Passage", "holy cross"), "Ruined Passage - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Ruined Passage", location_group="holy cross"),
"Hourglass Cave - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Hourglass Cave", "holy cross"), "Hourglass Cave - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Hourglass Cave Tower", location_group="holy cross"),
"Sealed Temple - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Sealed Temple", "holy cross"), "Sealed Temple - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Sealed Temple", location_group="holy cross"),
"Fountain Cross Door - Page Pickup": TunicLocationData("Overworld Holy Cross", "Fountain Cross Room", "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", "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", "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("Quarry", "Monastery Back"),
"Quarry - [Back Entrance] Bushes Holy Cross": TunicLocationData("Quarry Back", "Quarry Back", "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", "Quarry"), "Quarry - [Central] Near Shortcut Ladder": TunicLocationData("Quarry", "Quarry"),
"Quarry - [East] Near Telescope": TunicLocationData("Quarry", "Quarry"), "Quarry - [East] Near Telescope": TunicLocationData("Quarry", "Quarry"),
@ -225,16 +225,16 @@ 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("Quarry", "Hero Relic - Quarry", location_group="hero relics"),
"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"),
"Quarry - [Lowlands] Below Broken Ladder": TunicLocationData("Lower Quarry", "Lower Quarry"), "Quarry - [Lowlands] Below Broken Ladder": TunicLocationData("Lower Quarry", "Even Lower Quarry"),
"Quarry - [West] Upper Area Near Waterfall": TunicLocationData("Lower Quarry", "Lower Quarry"), "Quarry - [West] Upper Area Near Waterfall": TunicLocationData("Lower Quarry", "Lower Quarry"),
"Quarry - [Lowlands] Upper Walkway": TunicLocationData("Lower Quarry", "Lower Quarry"), "Quarry - [Lowlands] Upper Walkway": TunicLocationData("Lower Quarry", "Even Lower Quarry"),
"Quarry - [West] Lower Area Below Bridge": TunicLocationData("Lower Quarry", "Lower Quarry"), "Quarry - [West] Lower Area Below Bridge": TunicLocationData("Lower Quarry", "Lower Quarry"),
"Quarry - [West] Lower Area Isolated Chest": TunicLocationData("Lower Quarry", "Lower Quarry"), "Quarry - [West] Lower Area Isolated Chest": TunicLocationData("Lower Quarry", "Lower Quarry"),
"Quarry - [Lowlands] Near Elevator": TunicLocationData("Lower Quarry", "Lower Quarry"), "Quarry - [Lowlands] Near Elevator": TunicLocationData("Lower Quarry", "Even Lower Quarry"),
"Quarry - [West] Lower Area After Bridge": TunicLocationData("Lower Quarry", "Lower Quarry"), "Quarry - [West] Lower Area After Bridge": TunicLocationData("Lower Quarry", "Lower Quarry"),
"Rooted Ziggurat Upper - Near Bridge Switch": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Upper Front"), "Rooted Ziggurat Upper - Near Bridge Switch": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Upper Front"),
"Rooted Ziggurat Upper - Beneath Bridge To Administrator": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Upper Back"), "Rooted Ziggurat Upper - Beneath Bridge To Administrator": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Upper Back"),
@ -246,15 +246,15 @@ location_table: Dict[str, TunicLocationData] = {
"Rooted Ziggurat Lower - Guarded By Double Turrets": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Front"), "Rooted Ziggurat Lower - Guarded By Double Turrets": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Front"),
"Rooted Ziggurat Lower - After 2nd Double Turret Chest": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Front"), "Rooted Ziggurat Lower - After 2nd Double Turret Chest": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Front"),
"Rooted Ziggurat Lower - Guarded By Double Turrets 2": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Front"), "Rooted Ziggurat Lower - Guarded By Double Turrets 2": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Front"),
"Rooted Ziggurat Lower - Hexagon Blue": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Back"), "Rooted Ziggurat Lower - Hexagon Blue": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Back", location_group="bosses"),
"Ruined Atoll - [West] Near Kevin Block": TunicLocationData("Ruined Atoll", "Ruined Atoll"), "Ruined Atoll - [West] Near Kevin Block": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
"Ruined Atoll - [South] Upper Floor On Power Line": TunicLocationData("Ruined Atoll", "Ruined Atoll"), "Ruined Atoll - [South] Upper Floor On Power Line": TunicLocationData("Ruined Atoll", "Ruined Atoll Ladder Tops"),
"Ruined Atoll - [South] Chest Near Big Crabs": TunicLocationData("Ruined Atoll", "Ruined Atoll"), "Ruined Atoll - [South] Chest Near Big Crabs": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
"Ruined Atoll - [North] Guarded By Bird": TunicLocationData("Ruined Atoll", "Ruined Atoll"), "Ruined Atoll - [North] Guarded By Bird": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
"Ruined Atoll - [Northeast] Chest Beneath Brick Walkway": TunicLocationData("Ruined Atoll", "Ruined Atoll"), "Ruined Atoll - [Northeast] Chest Beneath Brick Walkway": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
"Ruined Atoll - [Northwest] Bombable Wall": TunicLocationData("Ruined Atoll", "Ruined Atoll"), "Ruined Atoll - [Northwest] Bombable Wall": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
"Ruined Atoll - [North] Obscured Beneath Bridge": TunicLocationData("Ruined Atoll", "Ruined Atoll"), "Ruined Atoll - [North] Obscured Beneath Bridge": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
"Ruined Atoll - [South] Upper Floor On Bricks": TunicLocationData("Ruined Atoll", "Ruined Atoll"), "Ruined Atoll - [South] Upper Floor On Bricks": TunicLocationData("Ruined Atoll", "Ruined Atoll Ladder Tops"),
"Ruined Atoll - [South] Near Birds": TunicLocationData("Ruined Atoll", "Ruined Atoll"), "Ruined Atoll - [South] Near Birds": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
"Ruined Atoll - [Northwest] Behind Envoy": TunicLocationData("Ruined Atoll", "Ruined Atoll"), "Ruined Atoll - [Northwest] Behind Envoy": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
"Ruined Atoll - [Southwest] Obscured Behind Fuse": TunicLocationData("Ruined Atoll", "Ruined Atoll"), "Ruined Atoll - [Southwest] Obscured Behind Fuse": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
@ -262,40 +262,40 @@ location_table: Dict[str, TunicLocationData] = {
"Ruined Atoll - [North] From Lower Overworld Entrance": TunicLocationData("Ruined Atoll", "Ruined Atoll Lower Entry Area"), "Ruined Atoll - [North] From Lower Overworld Entrance": TunicLocationData("Ruined Atoll", "Ruined Atoll Lower Entry Area"),
"Ruined Atoll - [East] Locked Room Lower Chest": TunicLocationData("Ruined Atoll", "Ruined Atoll"), "Ruined Atoll - [East] Locked Room Lower Chest": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
"Ruined Atoll - [Northeast] Chest On Brick Walkway": TunicLocationData("Ruined Atoll", "Ruined Atoll"), "Ruined Atoll - [Northeast] Chest On Brick Walkway": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
"Ruined Atoll - [Southeast] Chest Near Fuse": TunicLocationData("Ruined Atoll", "Ruined Atoll"), "Ruined Atoll - [Southeast] Chest Near Fuse": TunicLocationData("Ruined Atoll", "Ruined Atoll Ladder Tops"),
"Ruined Atoll - [Northeast] Key Pickup": TunicLocationData("Ruined Atoll", "Ruined Atoll"), "Ruined Atoll - [Northeast] Key Pickup": TunicLocationData("Ruined Atoll", "Ruined Atoll"),
"Cathedral Gauntlet - Gauntlet Reward": TunicLocationData("Swamp", "Cathedral Gauntlet"), "Cathedral Gauntlet - Gauntlet Reward": TunicLocationData("Swamp", "Cathedral Gauntlet"),
"Cathedral - Secret Legend Trophy Chest": TunicLocationData("Swamp", "Cathedral Secret Legend Room"), "Cathedral - Secret Legend Trophy Chest": TunicLocationData("Swamp", "Cathedral Secret Legend Room"),
"Swamp - [Upper Graveyard] Obscured Behind Hill": TunicLocationData("Swamp", "Swamp"), "Swamp - [Upper Graveyard] Obscured Behind Hill": TunicLocationData("Swamp", "Swamp Mid"),
"Swamp - [South Graveyard] 4 Orange Skulls": TunicLocationData("Swamp", "Swamp"), "Swamp - [South Graveyard] 4 Orange Skulls": TunicLocationData("Swamp", "Swamp Front"),
"Swamp - [Central] Near Ramps Up": TunicLocationData("Swamp", "Swamp"), "Swamp - [Central] Near Ramps Up": TunicLocationData("Swamp", "Swamp Mid"),
"Swamp - [Upper Graveyard] Near Shield Fleemers": TunicLocationData("Swamp", "Swamp"), "Swamp - [Upper Graveyard] Near Shield Fleemers": TunicLocationData("Swamp", "Swamp Mid"),
"Swamp - [South Graveyard] Obscured Behind Ridge": TunicLocationData("Swamp", "Swamp"), "Swamp - [South Graveyard] Obscured Behind Ridge": TunicLocationData("Swamp", "Swamp Mid"),
"Swamp - [South Graveyard] Obscured Beneath Telescope": TunicLocationData("Swamp", "Swamp"), "Swamp - [South Graveyard] Obscured Beneath Telescope": TunicLocationData("Swamp", "Swamp Front"),
"Swamp - [Entrance] Above Entryway": TunicLocationData("Swamp", "Back of Swamp Laurels Area"), "Swamp - [Entrance] Above Entryway": TunicLocationData("Swamp", "Back of Swamp Laurels Area"),
"Swamp - [Central] South Secret Passage": TunicLocationData("Swamp", "Swamp"), "Swamp - [Central] South Secret Passage": TunicLocationData("Swamp", "Swamp Mid"),
"Swamp - [South Graveyard] Upper Walkway On Pedestal": TunicLocationData("Swamp", "Swamp"), "Swamp - [South Graveyard] Upper Walkway On Pedestal": TunicLocationData("Swamp", "Swamp Front"),
"Swamp - [South Graveyard] Guarded By Tentacles": TunicLocationData("Swamp", "Swamp"), "Swamp - [South Graveyard] Guarded By Tentacles": TunicLocationData("Swamp", "Swamp Front"),
"Swamp - [Upper Graveyard] Near Telescope": TunicLocationData("Swamp", "Swamp"), "Swamp - [Upper Graveyard] Near Telescope": TunicLocationData("Swamp", "Swamp Mid"),
"Swamp - [Outside Cathedral] Near Moonlight Bridge Door": TunicLocationData("Swamp", "Swamp"), "Swamp - [Outside Cathedral] Near Moonlight Bridge Door": TunicLocationData("Swamp", "Swamp Ledge under Cathedral Door"),
"Swamp - [Entrance] Obscured Inside Watchtower": TunicLocationData("Swamp", "Swamp"), "Swamp - [Entrance] Obscured Inside Watchtower": TunicLocationData("Swamp", "Swamp Front"),
"Swamp - [Entrance] South Near Fence": TunicLocationData("Swamp", "Swamp"), "Swamp - [Entrance] South Near Fence": TunicLocationData("Swamp", "Swamp Front"),
"Swamp - [South Graveyard] Guarded By Big Skeleton": TunicLocationData("Swamp", "Swamp"), "Swamp - [South Graveyard] Guarded By Big Skeleton": TunicLocationData("Swamp", "Swamp Front"),
"Swamp - [South Graveyard] Chest Near Graves": TunicLocationData("Swamp", "Swamp"), "Swamp - [South Graveyard] Chest Near Graves": TunicLocationData("Swamp", "Swamp Front"),
"Swamp - [Entrance] North Small Island": TunicLocationData("Swamp", "Swamp"), "Swamp - [Entrance] North Small Island": TunicLocationData("Swamp", "Swamp Front"),
"Swamp - [Outside Cathedral] Obscured Behind Memorial": TunicLocationData("Swamp", "Back of Swamp"), "Swamp - [Outside Cathedral] Obscured Behind Memorial": TunicLocationData("Swamp", "Back of Swamp"),
"Swamp - [Central] Obscured Behind Northern Mountain": TunicLocationData("Swamp", "Swamp"), "Swamp - [Central] Obscured Behind Northern Mountain": TunicLocationData("Swamp", "Swamp Mid"),
"Swamp - [South Graveyard] Upper Walkway Dash Chest": TunicLocationData("Swamp", "Swamp"), "Swamp - [South Graveyard] Upper Walkway Dash Chest": TunicLocationData("Swamp", "Swamp Mid"),
"Swamp - [South Graveyard] Above Big Skeleton": TunicLocationData("Swamp", "Swamp"), "Swamp - [South Graveyard] Above Big Skeleton": TunicLocationData("Swamp", "Swamp Front"),
"Swamp - [Central] Beneath Memorial": TunicLocationData("Swamp", "Swamp"), "Swamp - [Central] Beneath Memorial": TunicLocationData("Swamp", "Swamp Mid"),
"Hero's Grave - Feathers Relic": TunicLocationData("Swamp", "Hero Relic - Swamp"), "Hero's Grave - Feathers Relic": TunicLocationData("Swamp", "Hero Relic - Swamp", location_group="hero relic"),
"West Furnace - Chest": TunicLocationData("West Garden", "Furnace Walking Path"), "West Furnace - Chest": TunicLocationData("West Garden", "Furnace Walking Path"),
"Overworld - [West] Near West Garden Entrance": TunicLocationData("West Garden", "Overworld to West Garden from Furnace"), "Overworld - [West] Near West Garden Entrance": TunicLocationData("West Garden", "Overworld to West Garden from Furnace"),
"West Garden - [Central Highlands] Holy Cross (Blue Lines)": TunicLocationData("West Garden", "West Garden", "holy cross"), "West Garden - [Central Highlands] Holy Cross (Blue Lines)": TunicLocationData("West Garden", "West Garden", location_group="holy cross"),
"West Garden - [West Lowlands] Tree Holy Cross Chest": TunicLocationData("West Garden", "West Garden", "holy cross"), "West Garden - [West Lowlands] Tree Holy Cross Chest": TunicLocationData("West Garden", "West Garden", location_group="holy cross"),
"West Garden - [Southeast Lowlands] Outside Cave": TunicLocationData("West Garden", "West Garden"), "West Garden - [Southeast Lowlands] Outside Cave": TunicLocationData("West Garden", "West Garden"),
"West Garden - [Central Lowlands] Chest Beneath Faeries": TunicLocationData("West Garden", "West Garden"), "West Garden - [Central Lowlands] Chest Beneath Faeries": TunicLocationData("West Garden", "West Garden"),
"West Garden - [North] Behind Holy Cross Door": TunicLocationData("West Garden", "West Garden", "holy cross"), "West Garden - [North] Behind Holy Cross Door": TunicLocationData("West Garden", "West Garden", location_group="holy cross"),
"West Garden - [Central Highlands] Top of Ladder Before Boss": TunicLocationData("West Garden", "West Garden"), "West Garden - [Central Highlands] Top of Ladder Before Boss": TunicLocationData("West Garden", "West Garden"),
"West Garden - [Central Lowlands] Passage Beneath Bridge": TunicLocationData("West Garden", "West Garden"), "West Garden - [Central Lowlands] Passage Beneath Bridge": TunicLocationData("West Garden", "West Garden"),
"West Garden - [North] Across From Page Pickup": TunicLocationData("West Garden", "West Garden"), "West Garden - [North] Across From Page Pickup": TunicLocationData("West Garden", "West Garden"),
@ -307,12 +307,12 @@ location_table: Dict[str, TunicLocationData] = {
"West Garden - [West Highlands] Upper Left Walkway": TunicLocationData("West Garden", "West Garden"), "West Garden - [West Highlands] Upper Left Walkway": TunicLocationData("West Garden", "West Garden"),
"West Garden - [Central Lowlands] Chest Beneath Save Point": TunicLocationData("West Garden", "West Garden"), "West Garden - [Central Lowlands] Chest Beneath Save Point": TunicLocationData("West Garden", "West Garden"),
"West Garden - [Central Highlands] Behind Guard Captain": TunicLocationData("West Garden", "West Garden"), "West Garden - [Central Highlands] Behind Guard Captain": TunicLocationData("West Garden", "West Garden"),
"West Garden - [Central Highlands] After Garden Knight": TunicLocationData("West Garden", "West Garden after Boss"), "West Garden - [Central Highlands] After Garden Knight": TunicLocationData("Overworld", "West Garden after Boss", location_group="bosses"),
"West Garden - [South Highlands] Secret Chest Beneath Fuse": TunicLocationData("West Garden", "West Garden"), "West Garden - [South Highlands] Secret Chest Beneath Fuse": TunicLocationData("West Garden", "West Garden"),
"West Garden - [East Lowlands] Page Behind Ice Dagger House": TunicLocationData("West Garden", "West Garden Portal Item"), "West Garden - [East Lowlands] Page Behind Ice Dagger House": TunicLocationData("West Garden", "West Garden Portal Item"),
"West Garden - [North] Page Pickup": TunicLocationData("West Garden", "West Garden"), "West Garden - [North] Page Pickup": TunicLocationData("West Garden", "West Garden"),
"West Garden House - [Southeast Lowlands] Ice Dagger Pickup": TunicLocationData("West Garden", "Magic Dagger House"), "West Garden House - [Southeast Lowlands] Ice Dagger Pickup": TunicLocationData("West Garden", "Magic Dagger House"),
"Hero's Grave - Effigy Relic": TunicLocationData("West Garden", "Hero Relic - West Garden"), "Hero's Grave - Effigy Relic": TunicLocationData("West Garden", "Hero Relic - West Garden", location_group="hero relic"),
} }
hexagon_locations: Dict[str, str] = { hexagon_locations: Dict[str, str] = {
@ -323,15 +323,9 @@ hexagon_locations: Dict[str, str] = {
location_name_to_id: Dict[str, int] = {name: location_base_id + index for index, name in enumerate(location_table)} location_name_to_id: Dict[str, int] = {name: location_base_id + index for index, name in enumerate(location_table)}
location_name_groups: Dict[str, Set[str]] = {}
def get_loc_group(location_name: str) -> str: for loc_name, loc_data in location_table.items():
loc_group = location_table[location_name].location_group if loc_data.location_group:
if loc_group == "region": if loc_data.location_group not in location_name_groups.keys():
# set loc_group as the region name. Typically, location groups are lowercase location_name_groups[loc_data.location_group] = set()
loc_group = location_table[location_name].region.lower() location_name_groups[loc_data.location_group].add(loc_name)
return loc_group
location_name_groups: Dict[str, Set[str]] = {
group: set(item_names) for group, item_names in groupby(sorted(location_table, key=get_loc_group), get_loc_group)
}

View File

@ -4,8 +4,7 @@ from Options import DefaultOnToggle, Toggle, StartInventoryPool, Choice, Range,
class SwordProgression(DefaultOnToggle): class SwordProgression(DefaultOnToggle):
"""Adds four sword upgrades to the item pool that will progressively grant stronger melee weapons, including two new """Adds four sword upgrades to the item pool that will progressively grant stronger melee weapons, including two new swords with increased range and attack power."""
swords with increased range and attack power."""
internal_name = "sword_progression" internal_name = "sword_progression"
display_name = "Sword Progression" display_name = "Sword Progression"
@ -24,25 +23,24 @@ class KeysBehindBosses(Toggle):
class AbilityShuffling(Toggle): class AbilityShuffling(Toggle):
"""Locks the usage of Prayer, Holy Cross*, and the Icebolt combo until the relevant pages of the manual have been found. """Locks the usage of Prayer, Holy Cross*, and the Icebolt combo until the relevant pages of the manual have been found.
If playing Hexagon Quest, abilities are instead randomly unlocked after obtaining 25%, 50%, and 75% of the required If playing Hexagon Quest, abilities are instead randomly unlocked after obtaining 25%, 50%, and 75% of the required Hexagon goal amount.
Hexagon goal amount. *Certain Holy Cross usages are still allowed, such as the free bomb codes, the seeking spell, and other player-facing codes.
*Certain Holy Cross usages are still allowed, such as the free bomb codes, the seeking spell, and other
player-facing codes.
""" """
internal_name = "ability_shuffling" internal_name = "ability_shuffling"
display_name = "Shuffle Abilities" display_name = "Shuffle Abilities"
class LogicRules(Choice): class LogicRules(Choice):
"""Set which logic rules to use for your world. """
Set which logic rules to use for your world.
Restricted: Standard logic, no glitches. 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. 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. * 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. Unrestricted: Logic in No Major Glitches, as well as ladder storage to get to certain places early.
*Special Shop is not in logic without the Hero's Laurels due to soft lock potential. *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. *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 *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.
Rooted Ziggurat Lower. This is so you're not punished for playing with enemy rando on.""" """
internal_name = "logic_rules" internal_name = "logic_rules"
display_name = "Logic Rules" display_name = "Logic Rules"
option_restricted = 0 option_restricted = 0
@ -68,8 +66,7 @@ class Maskless(Toggle):
class FoolTraps(Choice): class FoolTraps(Choice):
"""Replaces low-to-medium value money rewards in the item pool with fool traps, which cause random negative """Replaces low-to-medium value money rewards in the item pool with fool traps, which cause random negative effects to the player."""
effects to the player."""
internal_name = "fool_traps" internal_name = "fool_traps"
display_name = "Fool Traps" display_name = "Fool Traps"
option_off = 0 option_off = 0
@ -80,8 +77,7 @@ class FoolTraps(Choice):
class HexagonQuest(Toggle): class HexagonQuest(Toggle):
"""An alternate goal that shuffles Gold "Questagon" items into the item pool and allows the game to be completed """An alternate goal that shuffles Gold "Questagon" items into the item pool and allows the game to be completed after collecting the required number of them."""
after collecting the required number of them."""
internal_name = "hexagon_quest" internal_name = "hexagon_quest"
display_name = "Hexagon Quest" display_name = "Hexagon Quest"
@ -105,9 +101,11 @@ class ExtraHexagonPercentage(Range):
class EntranceRando(TextChoice): class EntranceRando(TextChoice):
"""Randomize the connections between scenes. """
You can choose a custom seed by editing this option. Randomize the connections between scenes.
A small, very lost fox on a big adventure.""" If you set this to a value besides true or false, that value will be used as a custom seed.
A small, very lost fox on a big adventure.
"""
internal_name = "entrance_rando" internal_name = "entrance_rando"
display_name = "Entrance Rando" display_name = "Entrance Rando"
alias_false = 0 alias_false = 0
@ -137,15 +135,24 @@ class LaurelsLocation(Choice):
default = 0 default = 0
class ShuffleLadders(Toggle):
"""Turns several ladders in the game into items that must be found before they can be climbed on.
Adds more layers of progression to the game by blocking access to many areas early on.
"Ladders were a mistake." Andrew Shouldice"""
internal_name = "shuffle_ladders"
display_name = "Shuffle Ladders"
@dataclass @dataclass
class TunicOptions(PerGameCommonOptions): class TunicOptions(PerGameCommonOptions):
sword_progression: SwordProgression sword_progression: SwordProgression
start_with_sword: StartWithSword start_with_sword: StartWithSword
keys_behind_bosses: KeysBehindBosses keys_behind_bosses: KeysBehindBosses
ability_shuffling: AbilityShuffling ability_shuffling: AbilityShuffling
logic_rules: LogicRules 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

View File

@ -6,10 +6,10 @@ tunic_regions: Dict[str, Set[str]] = {
"Ruined Atoll", "Eastern Vault Fortress", "Beneath the Vault", "Quarry Back", "Quarry", "Swamp", "Ruined Atoll", "Eastern Vault Fortress", "Beneath the Vault", "Quarry Back", "Quarry", "Swamp",
"Spirit Arena"}, "Spirit Arena"},
"Overworld Holy Cross": set(), "Overworld Holy Cross": set(),
"East Forest": {"Eastern Vault Fortress"}, "East Forest": set(),
"Dark Tomb": {"West Garden"}, "Dark Tomb": {"West Garden"},
"Beneath the Well": {"Dark Tomb"}, "Beneath the Well": set(),
"West Garden": {"Overworld", "Dark Tomb"}, "West Garden": set(),
"Ruined Atoll": {"Frog's Domain", "Library"}, "Ruined Atoll": {"Frog's Domain", "Library"},
"Frog's Domain": set(), "Frog's Domain": set(),
"Library": set(), "Library": set(),

View File

@ -103,18 +103,10 @@ def set_region_rules(world: "TunicWorld", ability_unlocks: Dict[str, int]) -> No
multiworld.get_entrance("Overworld -> West Garden", player).access_rule = \ multiworld.get_entrance("Overworld -> West Garden", player).access_rule = \
lambda state: state.has(laurels, player) \ lambda state: state.has(laurels, player) \
or can_ladder_storage(state, player, options) or can_ladder_storage(state, player, options)
multiworld.get_entrance("Beneath the Well -> Dark Tomb", player).access_rule = \
lambda state: has_lantern(state, player, options)
multiworld.get_entrance("West Garden -> Dark Tomb", player).access_rule = \
lambda state: has_lantern(state, player, options)
multiworld.get_entrance("Overworld -> Eastern Vault Fortress", player).access_rule = \ multiworld.get_entrance("Overworld -> Eastern Vault Fortress", player).access_rule = \
lambda state: state.has(laurels, player) \ lambda state: state.has(laurels, player) \
or has_ice_grapple_logic(True, state, player, options, ability_unlocks) \ or has_ice_grapple_logic(True, state, player, options, ability_unlocks) \
or can_ladder_storage(state, player, options) or can_ladder_storage(state, player, options)
multiworld.get_entrance("East Forest -> Eastern Vault Fortress", player).access_rule = \
lambda state: state.has(laurels, player) \
or has_ice_grapple_logic(True, state, player, options, ability_unlocks) \
or can_ladder_storage(state, player, options)
# 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
multiworld.get_entrance("Overworld -> Beneath the Vault", player).access_rule = \ multiworld.get_entrance("Overworld -> Beneath the Vault", player).access_rule = \
lambda state: has_lantern(state, player, options) and \ lambda state: has_lantern(state, player, options) and \
@ -211,7 +203,8 @@ def set_location_rules(world: "TunicWorld", ability_unlocks: Dict[str, int]) ->
lambda state: state.has(laurels, player)) lambda state: state.has(laurels, player))
set_rule(multiworld.get_location("Overworld - [West] Chest After Bell", player), set_rule(multiworld.get_location("Overworld - [West] Chest After Bell", player),
lambda state: state.has(laurels, player) lambda state: state.has(laurels, player)
or (has_lantern(state, player, options) and has_sword(state, player))) or (has_lantern(state, player, options) and has_sword(state, player))
or can_ladder_storage(state, player, options))
set_rule(multiworld.get_location("Overworld - [Northwest] Chest Beneath Quarry Gate", player), set_rule(multiworld.get_location("Overworld - [Northwest] Chest Beneath Quarry Gate", player),
lambda state: state.has_any({grapple, laurels}, player) or options.logic_rules) lambda state: state.has_any({grapple, laurels}, player) or options.logic_rules)
set_rule(multiworld.get_location("Overworld - [East] Grapple Chest", player), set_rule(multiworld.get_location("Overworld - [East] Grapple Chest", player),
@ -228,6 +221,8 @@ def set_location_rules(world: "TunicWorld", ability_unlocks: Dict[str, int]) ->
lambda state: state.has(laurels, player) lambda state: state.has(laurels, player)
or (has_lantern(state, player, options) and (has_sword(state, player) or state.has(fire_wand, player))) or (has_lantern(state, player, options) and (has_sword(state, player) or state.has(fire_wand, player)))
or has_ice_grapple_logic(False, state, player, options, ability_unlocks)) or has_ice_grapple_logic(False, state, player, options, ability_unlocks))
set_rule(multiworld.get_location("West Furnace - Lantern Pickup", player),
lambda state: has_stick(state, player) or state.has_any({fire_wand, laurels}, player))
set_rule(multiworld.get_location("Secret Gathering Place - 10 Fairy Reward", player), set_rule(multiworld.get_location("Secret Gathering Place - 10 Fairy Reward", player),
lambda state: state.has(fairies, player, 10)) lambda state: state.has(fairies, player, 10))
@ -265,8 +260,8 @@ def set_location_rules(world: "TunicWorld", ability_unlocks: Dict[str, int]) ->
set_rule(multiworld.get_location("West Garden - [Central Lowlands] Below Left Walkway", player), set_rule(multiworld.get_location("West Garden - [Central Lowlands] Below Left Walkway", player),
lambda state: state.has(laurels, player)) lambda state: state.has(laurels, player))
set_rule(multiworld.get_location("West Garden - [Central Highlands] After Garden Knight", player), set_rule(multiworld.get_location("West Garden - [Central Highlands] After Garden Knight", player),
lambda state: has_sword(state, player) or state.has(laurels, player) lambda state: state.has(laurels, player)
or has_ice_grapple_logic(False, state, player, options, ability_unlocks) or (has_lantern(state, player, options) and has_sword(state, player))
or can_ladder_storage(state, player, options)) or can_ladder_storage(state, player, options))
# Ruined Atoll # Ruined Atoll
@ -325,8 +320,6 @@ def set_location_rules(world: "TunicWorld", ability_unlocks: Dict[str, int]) ->
lambda state: state.has(laurels, player)) lambda state: state.has(laurels, player))
set_rule(multiworld.get_location("Swamp - [South Graveyard] 4 Orange Skulls", player), set_rule(multiworld.get_location("Swamp - [South Graveyard] 4 Orange Skulls", player),
lambda state: has_sword(state, player)) lambda state: has_sword(state, player))
set_rule(multiworld.get_location("Swamp - [South Graveyard] Guarded By Tentacles", player),
lambda state: has_sword(state, player))
# Hero's Grave # Hero's Grave
set_rule(multiworld.get_location("Hero's Grave - Tooth Relic", player), set_rule(multiworld.get_location("Hero's Grave - Tooth Relic", player),