TUNIC: Additional Combat Logic Option (#3658)
This commit is contained in:
parent
0fdc14bc42
commit
6282efb13c
|
@ -1,7 +1,8 @@
|
||||||
from typing import Dict, List, Any, Tuple, TypedDict, ClassVar, Union
|
from typing import Dict, List, Any, Tuple, TypedDict, ClassVar, Union
|
||||||
from logging import warning
|
from logging import warning
|
||||||
from BaseClasses import Region, Location, Item, Tutorial, ItemClassification, MultiWorld
|
from BaseClasses import Region, Location, Item, Tutorial, ItemClassification, MultiWorld, CollectionState
|
||||||
from .items import item_name_to_id, item_table, item_name_groups, fool_tiers, filler_items, slot_data_item_names
|
from .items import (item_name_to_id, item_table, item_name_groups, fool_tiers, filler_items, slot_data_item_names,
|
||||||
|
combat_items)
|
||||||
from .locations import location_table, location_name_groups, location_name_to_id, hexagon_locations
|
from .locations import location_table, location_name_groups, location_name_to_id, hexagon_locations
|
||||||
from .rules import set_location_rules, set_region_rules, randomize_ability_unlocks, gold_hexagon
|
from .rules import set_location_rules, set_region_rules, randomize_ability_unlocks, gold_hexagon
|
||||||
from .er_rules import set_er_location_rules
|
from .er_rules import set_er_location_rules
|
||||||
|
@ -10,6 +11,7 @@ from .er_scripts import create_er_regions
|
||||||
from .er_data import portal_mapping, RegionInfo, tunic_er_regions
|
from .er_data import portal_mapping, RegionInfo, tunic_er_regions
|
||||||
from .options import (TunicOptions, EntranceRando, tunic_option_groups, tunic_option_presets, TunicPlandoConnections,
|
from .options import (TunicOptions, EntranceRando, tunic_option_groups, tunic_option_presets, TunicPlandoConnections,
|
||||||
LaurelsLocation, LogicRules, LaurelsZips, IceGrappling, LadderStorage)
|
LaurelsLocation, LogicRules, LaurelsZips, IceGrappling, LadderStorage)
|
||||||
|
from .combat_logic import area_data, CombatState
|
||||||
from worlds.AutoWorld import WebWorld, World
|
from worlds.AutoWorld import WebWorld, World
|
||||||
from Options import PlandoConnection
|
from Options import PlandoConnection
|
||||||
from decimal import Decimal, ROUND_HALF_UP
|
from decimal import Decimal, ROUND_HALF_UP
|
||||||
|
@ -127,11 +129,21 @@ class TunicWorld(World):
|
||||||
self.options.shuffle_ladders.value = passthrough["shuffle_ladders"]
|
self.options.shuffle_ladders.value = passthrough["shuffle_ladders"]
|
||||||
self.options.fixed_shop.value = self.options.fixed_shop.option_false
|
self.options.fixed_shop.value = self.options.fixed_shop.option_false
|
||||||
self.options.laurels_location.value = self.options.laurels_location.option_anywhere
|
self.options.laurels_location.value = self.options.laurels_location.option_anywhere
|
||||||
|
self.options.combat_logic.value = passthrough["combat_logic"]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def stage_generate_early(cls, multiworld: MultiWorld) -> None:
|
def stage_generate_early(cls, multiworld: MultiWorld) -> None:
|
||||||
tunic_worlds: Tuple[TunicWorld] = multiworld.get_game_worlds("TUNIC")
|
tunic_worlds: Tuple[TunicWorld] = multiworld.get_game_worlds("TUNIC")
|
||||||
for tunic in tunic_worlds:
|
for tunic in tunic_worlds:
|
||||||
|
# setting up state combat logic stuff, see has_combat_reqs for its use
|
||||||
|
# and this is magic so pycharm doesn't like it, unfortunately
|
||||||
|
if tunic.options.combat_logic:
|
||||||
|
multiworld.state.tunic_need_to_reset_combat_from_collect[tunic.player] = False
|
||||||
|
multiworld.state.tunic_need_to_reset_combat_from_remove[tunic.player] = False
|
||||||
|
multiworld.state.tunic_area_combat_state[tunic.player] = {}
|
||||||
|
for area_name in area_data.keys():
|
||||||
|
multiworld.state.tunic_area_combat_state[tunic.player][area_name] = CombatState.unchecked
|
||||||
|
|
||||||
# if it's one of the options, then it isn't a custom seed group
|
# if it's one of the options, then it isn't a custom seed group
|
||||||
if tunic.options.entrance_rando.value in EntranceRando.options.values():
|
if tunic.options.entrance_rando.value in EntranceRando.options.values():
|
||||||
continue
|
continue
|
||||||
|
@ -190,10 +202,12 @@ class TunicWorld(World):
|
||||||
|
|
||||||
def create_item(self, name: str, classification: ItemClassification = None) -> TunicItem:
|
def create_item(self, name: str, classification: ItemClassification = None) -> TunicItem:
|
||||||
item_data = item_table[name]
|
item_data = item_table[name]
|
||||||
return TunicItem(name, classification or item_data.classification, self.item_name_to_id[name], self.player)
|
# if item_data.combat_ic is None, it'll take item_data.classification instead
|
||||||
|
itemclass: ItemClassification = ((item_data.combat_ic if self.options.combat_logic else None)
|
||||||
|
or item_data.classification)
|
||||||
|
return TunicItem(name, classification or itemclass, self.item_name_to_id[name], self.player)
|
||||||
|
|
||||||
def create_items(self) -> None:
|
def create_items(self) -> None:
|
||||||
|
|
||||||
tunic_items: List[TunicItem] = []
|
tunic_items: List[TunicItem] = []
|
||||||
self.slot_data_items = []
|
self.slot_data_items = []
|
||||||
|
|
||||||
|
@ -322,15 +336,15 @@ class TunicWorld(World):
|
||||||
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"]
|
||||||
|
|
||||||
# ladder rando uses ER with vanilla connections, so that we're not managing more rules files
|
# Ladders and Combat Logic uses ER rules with vanilla connections for easier maintenance
|
||||||
if self.options.entrance_rando or self.options.shuffle_ladders:
|
if self.options.entrance_rando or self.options.shuffle_ladders or self.options.combat_logic:
|
||||||
portal_pairs = create_er_regions(self)
|
portal_pairs = create_er_regions(self)
|
||||||
if self.options.entrance_rando:
|
if self.options.entrance_rando:
|
||||||
# these get interpreted by the game to tell it which entrances to connect
|
# these get interpreted by the game to tell it which entrances to connect
|
||||||
for portal1, portal2 in portal_pairs.items():
|
for portal1, portal2 in portal_pairs.items():
|
||||||
self.tunic_portal_pairs[portal1.scene_destination()] = portal2.scene_destination()
|
self.tunic_portal_pairs[portal1.scene_destination()] = portal2.scene_destination()
|
||||||
else:
|
else:
|
||||||
# for non-ER, non-ladders
|
# uses the original rules, easier to navigate and reference
|
||||||
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)
|
||||||
|
@ -351,7 +365,8 @@ 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 or self.options.shuffle_ladders:
|
# same reason as in create_regions, could probably be put into create_regions
|
||||||
|
if self.options.entrance_rando or self.options.shuffle_ladders or self.options.combat_logic:
|
||||||
set_er_location_rules(self)
|
set_er_location_rules(self)
|
||||||
else:
|
else:
|
||||||
set_region_rules(self)
|
set_region_rules(self)
|
||||||
|
@ -360,6 +375,19 @@ class TunicWorld(World):
|
||||||
def get_filler_item_name(self) -> str:
|
def get_filler_item_name(self) -> str:
|
||||||
return self.random.choice(filler_items)
|
return self.random.choice(filler_items)
|
||||||
|
|
||||||
|
# cache whether you can get through combat logic areas
|
||||||
|
def collect(self, state: CollectionState, item: Item) -> bool:
|
||||||
|
change = super().collect(state, item)
|
||||||
|
if change and self.options.combat_logic and item.name in combat_items:
|
||||||
|
state.tunic_need_to_reset_combat_from_collect[self.player] = True
|
||||||
|
return change
|
||||||
|
|
||||||
|
def remove(self, state: CollectionState, item: Item) -> bool:
|
||||||
|
change = super().remove(state, item)
|
||||||
|
if change and self.options.combat_logic and item.name in combat_items:
|
||||||
|
state.tunic_need_to_reset_combat_from_remove[self.player] = True
|
||||||
|
return change
|
||||||
|
|
||||||
def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]) -> None:
|
def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]) -> None:
|
||||||
if self.options.entrance_rando:
|
if self.options.entrance_rando:
|
||||||
hint_data.update({self.player: {}})
|
hint_data.update({self.player: {}})
|
||||||
|
@ -426,6 +454,7 @@ class TunicWorld(World):
|
||||||
"maskless": self.options.maskless.value,
|
"maskless": self.options.maskless.value,
|
||||||
"entrance_rando": int(bool(self.options.entrance_rando.value)),
|
"entrance_rando": int(bool(self.options.entrance_rando.value)),
|
||||||
"shuffle_ladders": self.options.shuffle_ladders.value,
|
"shuffle_ladders": self.options.shuffle_ladders.value,
|
||||||
|
"combat_logic": self.options.combat_logic.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)"],
|
||||||
|
|
|
@ -0,0 +1,422 @@
|
||||||
|
from typing import Dict, List, NamedTuple, Tuple, Optional
|
||||||
|
from enum import IntEnum
|
||||||
|
from collections import defaultdict
|
||||||
|
from BaseClasses import CollectionState
|
||||||
|
from .rules import has_sword, has_melee
|
||||||
|
from worlds.AutoWorld import LogicMixin
|
||||||
|
|
||||||
|
|
||||||
|
# the vanilla stats you are expected to have to get through an area, based on where they are in vanilla
|
||||||
|
class AreaStats(NamedTuple):
|
||||||
|
att_level: int
|
||||||
|
def_level: int
|
||||||
|
potion_level: int # all 3 are before your first bonfire after getting the upgrade page, third costs 1k
|
||||||
|
hp_level: int
|
||||||
|
sp_level: int
|
||||||
|
mp_level: int
|
||||||
|
potion_count: int
|
||||||
|
equipment: List[str] = []
|
||||||
|
is_boss: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
# the vanilla upgrades/equipment you would have
|
||||||
|
area_data: Dict[str, AreaStats] = {
|
||||||
|
"Overworld": AreaStats(1, 1, 1, 1, 1, 1, 0, ["Stick"]),
|
||||||
|
"East Forest": AreaStats(1, 1, 1, 1, 1, 1, 0, ["Sword"]),
|
||||||
|
"Before Well": AreaStats(1, 1, 1, 1, 1, 1, 3, ["Sword", "Shield"]),
|
||||||
|
# learn how to upgrade
|
||||||
|
"Beneath the Well": AreaStats(2, 1, 3, 3, 1, 1, 3, ["Sword", "Shield"]),
|
||||||
|
"Dark Tomb": AreaStats(2, 2, 3, 3, 1, 1, 3, ["Sword", "Shield"]),
|
||||||
|
"West Garden": AreaStats(2, 3, 3, 3, 1, 1, 4, ["Sword", "Shield"]),
|
||||||
|
"Garden Knight": AreaStats(3, 3, 3, 3, 2, 1, 4, ["Sword", "Shield"], is_boss=True),
|
||||||
|
# get the wand here
|
||||||
|
"Beneath the Vault": AreaStats(3, 3, 3, 3, 2, 1, 4, ["Sword", "Shield", "Magic"]),
|
||||||
|
"Eastern Vault Fortress": AreaStats(3, 3, 3, 4, 3, 2, 4, ["Sword", "Shield", "Magic"]),
|
||||||
|
"Siege Engine": AreaStats(3, 3, 3, 4, 3, 2, 4, ["Sword", "Shield", "Magic"], is_boss=True),
|
||||||
|
"Frog's Domain": AreaStats(3, 4, 3, 5, 3, 3, 4, ["Sword", "Shield", "Magic"]),
|
||||||
|
# the second half of Atoll is the part you need the stats for, so putting it after frogs
|
||||||
|
"Ruined Atoll": AreaStats(4, 4, 3, 5, 3, 3, 5, ["Sword", "Shield", "Magic"]),
|
||||||
|
"The Librarian": AreaStats(4, 4, 3, 5, 3, 3, 5, ["Sword", "Shield", "Magic"], is_boss=True),
|
||||||
|
"Quarry": AreaStats(5, 4, 3, 5, 3, 3, 5, ["Sword", "Shield", "Magic"]),
|
||||||
|
"Rooted Ziggurat": AreaStats(5, 5, 3, 5, 3, 3, 6, ["Sword", "Shield", "Magic"]),
|
||||||
|
"Boss Scavenger": AreaStats(5, 5, 3, 5, 3, 3, 6, ["Sword", "Shield", "Magic"], is_boss=True),
|
||||||
|
"Swamp": AreaStats(1, 1, 1, 1, 1, 1, 6, ["Sword", "Shield", "Magic"]),
|
||||||
|
"Cathedral": AreaStats(1, 1, 1, 1, 1, 1, 6, ["Sword", "Shield", "Magic"]),
|
||||||
|
# marked as boss because the garden knights can't get hurt by stick
|
||||||
|
"Gauntlet": AreaStats(1, 1, 1, 1, 1, 1, 6, ["Sword", "Shield", "Magic"], is_boss=True),
|
||||||
|
"The Heir": AreaStats(5, 5, 3, 5, 3, 3, 6, ["Sword", "Shield", "Magic", "Laurels"], is_boss=True),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# these are used for caching which areas can currently be reached in state
|
||||||
|
boss_areas: List[str] = [name for name, data in area_data.items() if data.is_boss and name != "Gauntlet"]
|
||||||
|
non_boss_areas: List[str] = [name for name, data in area_data.items() if not data.is_boss]
|
||||||
|
|
||||||
|
|
||||||
|
class CombatState(IntEnum):
|
||||||
|
unchecked = 0
|
||||||
|
failed = 1
|
||||||
|
succeeded = 2
|
||||||
|
|
||||||
|
|
||||||
|
def has_combat_reqs(area_name: str, state: CollectionState, player: int) -> bool:
|
||||||
|
# we're caching whether you've met the combat reqs before if the state didn't change first
|
||||||
|
# if the combat state is stale, mark each area's combat state as stale
|
||||||
|
if state.tunic_need_to_reset_combat_from_collect[player]:
|
||||||
|
state.tunic_need_to_reset_combat_from_collect[player] = False
|
||||||
|
for name in area_data.keys():
|
||||||
|
if state.tunic_area_combat_state[player][name] == CombatState.failed:
|
||||||
|
state.tunic_area_combat_state[player][name] = CombatState.unchecked
|
||||||
|
|
||||||
|
if state.tunic_need_to_reset_combat_from_remove[player]:
|
||||||
|
state.tunic_need_to_reset_combat_from_remove[player] = False
|
||||||
|
for name in area_data.keys():
|
||||||
|
if state.tunic_area_combat_state[player][name] == CombatState.succeeded:
|
||||||
|
state.tunic_area_combat_state[player][name] = CombatState.unchecked
|
||||||
|
|
||||||
|
if state.tunic_area_combat_state[player][area_name] > CombatState.unchecked:
|
||||||
|
return state.tunic_area_combat_state[player][area_name] == CombatState.succeeded
|
||||||
|
|
||||||
|
met_combat_reqs = check_combat_reqs(area_name, state, player)
|
||||||
|
|
||||||
|
# we want to skip the "none area" since we don't record its results
|
||||||
|
if area_name not in area_data.keys():
|
||||||
|
return met_combat_reqs
|
||||||
|
|
||||||
|
# loop through the lists and set the easier/harder area states accordingly
|
||||||
|
if area_name in boss_areas:
|
||||||
|
area_list = boss_areas
|
||||||
|
elif area_name in non_boss_areas:
|
||||||
|
area_list = non_boss_areas
|
||||||
|
else:
|
||||||
|
area_list = [area_name]
|
||||||
|
|
||||||
|
if met_combat_reqs:
|
||||||
|
# set the state as true for each area until you get to the area we're looking at
|
||||||
|
for name in area_list:
|
||||||
|
state.tunic_area_combat_state[player][name] = CombatState.succeeded
|
||||||
|
if name == area_name:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# set the state as false for the area we're looking at and each area after that
|
||||||
|
reached_name = False
|
||||||
|
for name in area_list:
|
||||||
|
if name == area_name:
|
||||||
|
reached_name = True
|
||||||
|
if reached_name:
|
||||||
|
state.tunic_area_combat_state[player][name] = CombatState.failed
|
||||||
|
|
||||||
|
return met_combat_reqs
|
||||||
|
|
||||||
|
|
||||||
|
def check_combat_reqs(area_name: str, state: CollectionState, player: int, alt_data: Optional[AreaStats] = None) -> bool:
|
||||||
|
data = alt_data or area_data[area_name]
|
||||||
|
extra_att_needed = 0
|
||||||
|
extra_def_needed = 0
|
||||||
|
extra_mp_needed = 0
|
||||||
|
has_magic = state.has_any({"Magic Wand", "Gun"}, player)
|
||||||
|
stick_bool = False
|
||||||
|
sword_bool = False
|
||||||
|
for item in data.equipment:
|
||||||
|
if item == "Stick":
|
||||||
|
if not has_melee(state, player):
|
||||||
|
if has_magic:
|
||||||
|
# magic can make up for the lack of stick
|
||||||
|
extra_mp_needed += 2
|
||||||
|
extra_att_needed -= 16
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
stick_bool = True
|
||||||
|
|
||||||
|
elif item == "Sword":
|
||||||
|
if not has_sword(state, player):
|
||||||
|
# need sword for bosses
|
||||||
|
if data.is_boss:
|
||||||
|
return False
|
||||||
|
if has_magic:
|
||||||
|
# +4 mp pretty much makes up for the lack of sword, at least in Quarry
|
||||||
|
extra_mp_needed += 4
|
||||||
|
# stick is a backup plan, and doesn't scale well, so let's require a little less
|
||||||
|
extra_att_needed -= 2
|
||||||
|
elif has_melee(state, player):
|
||||||
|
# may revise this later based on feedback
|
||||||
|
extra_att_needed += 3
|
||||||
|
extra_def_needed += 2
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
sword_bool = True
|
||||||
|
|
||||||
|
elif item == "Shield":
|
||||||
|
if not state.has("Shield", player):
|
||||||
|
extra_def_needed += 2
|
||||||
|
elif item == "Laurels":
|
||||||
|
if not state.has("Hero's Laurels", player):
|
||||||
|
# these are entirely based on vibes
|
||||||
|
extra_att_needed += 2
|
||||||
|
extra_def_needed += 3
|
||||||
|
elif item == "Magic":
|
||||||
|
if not has_magic:
|
||||||
|
extra_att_needed += 2
|
||||||
|
extra_def_needed += 2
|
||||||
|
extra_mp_needed -= 16
|
||||||
|
modified_stats = AreaStats(data.att_level + extra_att_needed, data.def_level + extra_def_needed, data.potion_level,
|
||||||
|
data.hp_level, data.sp_level, data.mp_level + extra_mp_needed, data.potion_count)
|
||||||
|
if not has_required_stats(modified_stats, state, player):
|
||||||
|
# we may need to check if you would have the required stats if you were missing a weapon
|
||||||
|
# it's kinda janky, but these only get hit in less than once per 100 generations, so whatever
|
||||||
|
if sword_bool and "Sword" in data.equipment and "Magic" in data.equipment:
|
||||||
|
# we need to check if you would have the required stats if you didn't have melee
|
||||||
|
equip_list = [item for item in data.equipment if item != "Sword"]
|
||||||
|
more_modified_stats = AreaStats(data.att_level - 16, data.def_level, data.potion_level,
|
||||||
|
data.hp_level, data.sp_level, data.mp_level + 4, data.potion_count,
|
||||||
|
equip_list)
|
||||||
|
if check_combat_reqs("none", state, player, more_modified_stats):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# and we need to check if you would have the required stats if you didn't have magic
|
||||||
|
equip_list = [item for item in data.equipment if item != "Magic"]
|
||||||
|
more_modified_stats = AreaStats(data.att_level + 2, data.def_level + 2, data.potion_level,
|
||||||
|
data.hp_level, data.sp_level, data.mp_level - 16, data.potion_count,
|
||||||
|
equip_list)
|
||||||
|
if check_combat_reqs("none", state, player, more_modified_stats):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif stick_bool and "Stick" in data.equipment and "Magic" in data.equipment:
|
||||||
|
# we need to check if you would have the required stats if you didn't have the stick
|
||||||
|
equip_list = [item for item in data.equipment if item != "Stick"]
|
||||||
|
more_modified_stats = AreaStats(data.att_level - 16, data.def_level, data.potion_level,
|
||||||
|
data.hp_level, data.sp_level, data.mp_level + 4, data.potion_count,
|
||||||
|
equip_list)
|
||||||
|
if check_combat_reqs("none", state, player, more_modified_stats):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# check if you have the required stats, and the money to afford them
|
||||||
|
# it may be innaccurate due to poor spending, and it may even require you to "spend poorly"
|
||||||
|
# but that's fine -- it's already pretty generous to begin with
|
||||||
|
def has_required_stats(data: AreaStats, state: CollectionState, player: int) -> bool:
|
||||||
|
money_required = 0
|
||||||
|
player_att = 0
|
||||||
|
|
||||||
|
# check if we actually need the stat before checking state
|
||||||
|
if data.att_level > 1:
|
||||||
|
player_att, att_offerings = get_att_level(state, player)
|
||||||
|
if player_att < data.att_level:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
extra_att = player_att - data.att_level
|
||||||
|
paid_att = max(0, att_offerings - extra_att)
|
||||||
|
# attack upgrades cost 100 for the first, +50 for each additional
|
||||||
|
money_per_att = 100
|
||||||
|
for _ in range(paid_att):
|
||||||
|
money_required += money_per_att
|
||||||
|
money_per_att += 50
|
||||||
|
|
||||||
|
# adding defense and sp together since they accomplish similar things: making you take less damage
|
||||||
|
if data.def_level + data.sp_level > 2:
|
||||||
|
player_def, def_offerings = get_def_level(state, player)
|
||||||
|
player_sp, sp_offerings = get_sp_level(state, player)
|
||||||
|
if player_def + player_sp < data.def_level + data.sp_level:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
free_def = player_def - def_offerings
|
||||||
|
free_sp = player_sp - sp_offerings
|
||||||
|
paid_stats = data.def_level + data.sp_level - free_def - free_sp
|
||||||
|
sp_to_buy = 0
|
||||||
|
|
||||||
|
if paid_stats <= 0:
|
||||||
|
# if you don't have to pay for any stats, you don't need money for these upgrades
|
||||||
|
def_to_buy = 0
|
||||||
|
elif paid_stats <= def_offerings:
|
||||||
|
# get the amount needed to buy these def offerings
|
||||||
|
def_to_buy = paid_stats
|
||||||
|
else:
|
||||||
|
def_to_buy = def_offerings
|
||||||
|
sp_to_buy = max(0, paid_stats - def_offerings)
|
||||||
|
|
||||||
|
# if you have to buy more than 3 def, it's cheaper to buy 1 extra sp
|
||||||
|
if def_to_buy > 3 and sp_offerings > 0:
|
||||||
|
def_to_buy -= 1
|
||||||
|
sp_to_buy += 1
|
||||||
|
# def costs 100 for the first, +50 for each additional
|
||||||
|
money_per_def = 100
|
||||||
|
for _ in range(def_to_buy):
|
||||||
|
money_required += money_per_def
|
||||||
|
money_per_def += 50
|
||||||
|
# sp costs 200 for the first, +200 for each additional
|
||||||
|
money_per_sp = 200
|
||||||
|
for _ in range(sp_to_buy):
|
||||||
|
money_required += money_per_sp
|
||||||
|
money_per_sp += 200
|
||||||
|
|
||||||
|
# if you have 2 more attack than needed, we can forego needing mp
|
||||||
|
if data.mp_level > 1 and player_att < data.att_level + 2:
|
||||||
|
player_mp, mp_offerings = get_mp_level(state, player)
|
||||||
|
if player_mp < data.mp_level:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
extra_mp = player_mp - data.mp_level
|
||||||
|
paid_mp = max(0, mp_offerings - extra_mp)
|
||||||
|
# mp costs 300 for the first, +50 for each additional
|
||||||
|
money_per_mp = 300
|
||||||
|
for _ in range(paid_mp):
|
||||||
|
money_required += money_per_mp
|
||||||
|
money_per_mp += 50
|
||||||
|
|
||||||
|
req_effective_hp = calc_effective_hp(data.hp_level, data.potion_level, data.potion_count)
|
||||||
|
player_potion, potion_offerings = get_potion_level(state, player)
|
||||||
|
player_hp, hp_offerings = get_hp_level(state, player)
|
||||||
|
player_potion_count = get_potion_count(state, player)
|
||||||
|
player_effective_hp = calc_effective_hp(player_hp, player_potion, player_potion_count)
|
||||||
|
if player_effective_hp < req_effective_hp:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
# need a way to determine which of potion offerings or hp offerings you can reduce
|
||||||
|
# your level if you didn't pay for offerings
|
||||||
|
free_potion = player_potion - potion_offerings
|
||||||
|
free_hp = player_hp - hp_offerings
|
||||||
|
paid_hp_count = 0
|
||||||
|
paid_potion_count = 0
|
||||||
|
if calc_effective_hp(free_hp, free_potion, player_potion_count) >= req_effective_hp:
|
||||||
|
# you don't need to buy upgrades
|
||||||
|
pass
|
||||||
|
# if you have no potions, or no potion upgrades, you only need to check your hp upgrades
|
||||||
|
elif player_potion_count == 0 or potion_offerings == 0:
|
||||||
|
# check if you have enough hp at each paid hp offering
|
||||||
|
for i in range(hp_offerings):
|
||||||
|
paid_hp_count = i + 1
|
||||||
|
if calc_effective_hp(paid_hp_count, 0, player_potion_count) > req_effective_hp:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
for i in range(potion_offerings):
|
||||||
|
paid_potion_count = i + 1
|
||||||
|
if calc_effective_hp(free_hp, free_potion + paid_potion_count, player_potion_count) > req_effective_hp:
|
||||||
|
break
|
||||||
|
for j in range(hp_offerings):
|
||||||
|
paid_hp_count = j + 1
|
||||||
|
if (calc_effective_hp(free_hp + paid_hp_count, free_potion + paid_potion_count, player_potion_count)
|
||||||
|
> req_effective_hp):
|
||||||
|
break
|
||||||
|
# hp costs 200 for the first, +50 for each additional
|
||||||
|
money_per_hp = 200
|
||||||
|
for _ in range(paid_hp_count):
|
||||||
|
money_required += money_per_hp
|
||||||
|
money_per_hp += 50
|
||||||
|
|
||||||
|
# potion costs 100 for the first, 300 for the second, 1,000 for the third, and +200 for each additional
|
||||||
|
# currently we assume you will not buy past the second potion upgrade, but we might change our minds later
|
||||||
|
money_per_potion = 100
|
||||||
|
for _ in range(paid_potion_count):
|
||||||
|
money_required += money_per_potion
|
||||||
|
if money_per_potion == 100:
|
||||||
|
money_per_potion = 300
|
||||||
|
elif money_per_potion == 300:
|
||||||
|
money_per_potion = 1000
|
||||||
|
else:
|
||||||
|
money_per_potion += 200
|
||||||
|
|
||||||
|
if money_required > get_money_count(state, player):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# returns a tuple of your max attack level, the number of attack offerings
|
||||||
|
def get_att_level(state: CollectionState, player: int) -> Tuple[int, int]:
|
||||||
|
att_offerings = state.count("ATT Offering", player)
|
||||||
|
att_upgrades = state.count("Hero Relic - ATT", player)
|
||||||
|
sword_level = state.count("Sword Upgrade", player)
|
||||||
|
if sword_level >= 3:
|
||||||
|
att_upgrades += min(2, sword_level - 2)
|
||||||
|
# attack falls off, can just cap it at 8 for simplicity
|
||||||
|
return min(8, 1 + att_offerings + att_upgrades), att_offerings
|
||||||
|
|
||||||
|
|
||||||
|
# returns a tuple of your max defense level, the number of defense offerings
|
||||||
|
def get_def_level(state: CollectionState, player: int) -> Tuple[int, int]:
|
||||||
|
def_offerings = state.count("DEF Offering", player)
|
||||||
|
# defense falls off, can just cap it at 8 for simplicity
|
||||||
|
return (min(8, 1 + def_offerings
|
||||||
|
+ state.count_from_list({"Hero Relic - DEF", "Secret Legend", "Phonomath"}, player)),
|
||||||
|
def_offerings)
|
||||||
|
|
||||||
|
|
||||||
|
# returns a tuple of your max potion level, the number of potion offerings
|
||||||
|
def get_potion_level(state: CollectionState, player: int) -> Tuple[int, int]:
|
||||||
|
potion_offerings = min(2, state.count("Potion Offering", player))
|
||||||
|
# your third potion upgrade (from offerings) costs 1,000 money, reasonable to assume you won't do that
|
||||||
|
return (1 + potion_offerings
|
||||||
|
+ state.count_from_list({"Hero Relic - POTION", "Just Some Pals", "Spring Falls", "Back To Work"}, player),
|
||||||
|
potion_offerings)
|
||||||
|
|
||||||
|
|
||||||
|
# returns a tuple of your max hp level, the number of hp offerings
|
||||||
|
def get_hp_level(state: CollectionState, player: int) -> Tuple[int, int]:
|
||||||
|
hp_offerings = state.count("HP Offering", player)
|
||||||
|
return 1 + hp_offerings + state.count("Hero Relic - HP", player), hp_offerings
|
||||||
|
|
||||||
|
|
||||||
|
# returns a tuple of your max sp level, the number of sp offerings
|
||||||
|
def get_sp_level(state: CollectionState, player: int) -> Tuple[int, int]:
|
||||||
|
sp_offerings = state.count("SP Offering", player)
|
||||||
|
return (1 + sp_offerings
|
||||||
|
+ state.count_from_list({"Hero Relic - SP", "Mr Mayor", "Power Up",
|
||||||
|
"Regal Weasel", "Forever Friend"}, player),
|
||||||
|
sp_offerings)
|
||||||
|
|
||||||
|
|
||||||
|
def get_mp_level(state: CollectionState, player: int) -> Tuple[int, int]:
|
||||||
|
mp_offerings = state.count("MP Offering", player)
|
||||||
|
return (1 + mp_offerings
|
||||||
|
+ state.count_from_list({"Hero Relic - MP", "Sacred Geometry", "Vintage", "Dusty"}, player),
|
||||||
|
mp_offerings)
|
||||||
|
|
||||||
|
|
||||||
|
def get_potion_count(state: CollectionState, player: int) -> int:
|
||||||
|
return state.count("Potion Flask", player) + state.count("Flask Shard", player) // 3
|
||||||
|
|
||||||
|
|
||||||
|
def calc_effective_hp(hp_level: int, potion_level: int, potion_count: int) -> int:
|
||||||
|
player_hp = 60 + hp_level * 20
|
||||||
|
# since you don't tend to use potions efficiently all the time, scale healing by .75
|
||||||
|
total_healing = int(.75 * potion_count * min(player_hp, 20 + 10 * potion_level))
|
||||||
|
return player_hp + total_healing
|
||||||
|
|
||||||
|
|
||||||
|
# returns the total amount of progression money the player has
|
||||||
|
def get_money_count(state: CollectionState, player: int) -> int:
|
||||||
|
money: int = 0
|
||||||
|
# this could be done with something to parse the money count at the end of the string, but I don't wanna
|
||||||
|
money += state.count("Money x255", player) * 255 # 1 in pool
|
||||||
|
money += state.count("Money x200", player) * 200 # 1 in pool
|
||||||
|
money += state.count("Money x128", player) * 128 # 3 in pool
|
||||||
|
# total from regular money: 839
|
||||||
|
# first effigy is 8, doubles until it reaches 512 at number 7, after effigy 28 they stop dropping money
|
||||||
|
# with the vanilla count of 12, you get 3,576 money from effigies
|
||||||
|
effigy_count = min(28, state.count("Effigy", player)) # 12 in pool
|
||||||
|
money_per_break = 8
|
||||||
|
for _ in range(effigy_count):
|
||||||
|
money += money_per_break
|
||||||
|
money_per_break = min(512, money_per_break * 2)
|
||||||
|
return money
|
||||||
|
|
||||||
|
|
||||||
|
class TunicState(LogicMixin):
|
||||||
|
tunic_need_to_reset_combat_from_collect: Dict[int, bool]
|
||||||
|
tunic_need_to_reset_combat_from_remove: Dict[int, bool]
|
||||||
|
tunic_area_combat_state: Dict[int, Dict[str, int]]
|
||||||
|
|
||||||
|
def init_mixin(self, _):
|
||||||
|
# the per-player need to reset the combat state when collecting a combat item
|
||||||
|
self.tunic_need_to_reset_combat_from_collect = defaultdict(lambda: False)
|
||||||
|
# the per-player need to reset the combat state when removing a combat item
|
||||||
|
self.tunic_need_to_reset_combat_from_remove = defaultdict(lambda: False)
|
||||||
|
# the per-player, per-area state of combat checking -- unchecked, failed, or succeeded
|
||||||
|
self.tunic_area_combat_state = defaultdict(lambda: defaultdict(lambda: CombatState.unchecked))
|
|
@ -248,13 +248,13 @@ portal_mapping: List[Portal] = [
|
||||||
Portal(name="Dark Tomb to Checkpoint", region="Dark Tomb Entry Point",
|
Portal(name="Dark Tomb to Checkpoint", region="Dark Tomb Entry Point",
|
||||||
destination="Sewer_Boss", tag="_"),
|
destination="Sewer_Boss", tag="_"),
|
||||||
|
|
||||||
Portal(name="West Garden Exit near Hero's Grave", region="West Garden",
|
Portal(name="West Garden Exit near Hero's Grave", region="West Garden before Terry",
|
||||||
destination="Overworld Redux", tag="_lower"),
|
destination="Overworld Redux", tag="_lower"),
|
||||||
Portal(name="West Garden to Magic Dagger House", region="West Garden",
|
Portal(name="West Garden to Magic Dagger House", region="West Garden at Dagger House",
|
||||||
destination="archipelagos_house", tag="_"),
|
destination="archipelagos_house", tag="_"),
|
||||||
Portal(name="West Garden Exit after Boss", region="West Garden after Boss",
|
Portal(name="West Garden Exit after Boss", region="West Garden after Boss",
|
||||||
destination="Overworld Redux", tag="_upper"),
|
destination="Overworld Redux", tag="_upper"),
|
||||||
Portal(name="West Garden Shop", region="West Garden",
|
Portal(name="West Garden Shop", region="West Garden before Terry",
|
||||||
destination="Shop", tag="_"),
|
destination="Shop", tag="_"),
|
||||||
Portal(name="West Garden Laurels Exit", region="West Garden Laurels Exit Region",
|
Portal(name="West Garden Laurels Exit", region="West Garden Laurels Exit Region",
|
||||||
destination="Overworld Redux", tag="_lowest"),
|
destination="Overworld Redux", tag="_lowest"),
|
||||||
|
@ -308,7 +308,7 @@ portal_mapping: List[Portal] = [
|
||||||
Portal(name="East Fortress to Interior Upper", region="Fortress East Shortcut Upper",
|
Portal(name="East Fortress to Interior Upper", region="Fortress East Shortcut Upper",
|
||||||
destination="Fortress Main", tag="_upper"),
|
destination="Fortress Main", tag="_upper"),
|
||||||
|
|
||||||
Portal(name="Fortress Grave Path Lower Exit", region="Fortress Grave Path",
|
Portal(name="Fortress Grave Path Lower Exit", region="Fortress Grave Path Entry",
|
||||||
destination="Fortress Courtyard", tag="_Lower"),
|
destination="Fortress Courtyard", tag="_Lower"),
|
||||||
Portal(name="Fortress Hero's Grave", region="Fortress Hero's Grave Region",
|
Portal(name="Fortress Hero's Grave", region="Fortress Hero's Grave Region",
|
||||||
destination="RelicVoid", tag="_teleporter_relic plinth"),
|
destination="RelicVoid", tag="_teleporter_relic plinth"),
|
||||||
|
@ -433,7 +433,7 @@ portal_mapping: List[Portal] = [
|
||||||
Portal(name="Ziggurat Tower to Ziggurat Lower", region="Rooted Ziggurat Middle Bottom",
|
Portal(name="Ziggurat Tower to Ziggurat Lower", region="Rooted Ziggurat Middle Bottom",
|
||||||
destination="ziggurat2020_3", tag="_"),
|
destination="ziggurat2020_3", tag="_"),
|
||||||
|
|
||||||
Portal(name="Ziggurat Lower to Ziggurat Tower", region="Rooted Ziggurat Lower Front",
|
Portal(name="Ziggurat Lower to Ziggurat Tower", region="Rooted Ziggurat Lower Entry",
|
||||||
destination="ziggurat2020_2", tag="_"),
|
destination="ziggurat2020_2", tag="_"),
|
||||||
Portal(name="Ziggurat Portal Room Entrance", region="Rooted Ziggurat Portal Room Entrance",
|
Portal(name="Ziggurat Portal Room Entrance", region="Rooted Ziggurat Portal Room Entrance",
|
||||||
destination="ziggurat2020_FTRoom", tag="_"),
|
destination="ziggurat2020_FTRoom", tag="_"),
|
||||||
|
@ -461,7 +461,7 @@ portal_mapping: List[Portal] = [
|
||||||
Portal(name="Swamp Hero's Grave", region="Swamp Hero's Grave Region",
|
Portal(name="Swamp Hero's Grave", region="Swamp Hero's Grave Region",
|
||||||
destination="RelicVoid", tag="_teleporter_relic plinth"),
|
destination="RelicVoid", tag="_teleporter_relic plinth"),
|
||||||
|
|
||||||
Portal(name="Cathedral Main Exit", region="Cathedral",
|
Portal(name="Cathedral Main Exit", region="Cathedral Entry",
|
||||||
destination="Swamp Redux 2", tag="_main"),
|
destination="Swamp Redux 2", tag="_main"),
|
||||||
Portal(name="Cathedral Elevator", region="Cathedral to Gauntlet",
|
Portal(name="Cathedral Elevator", region="Cathedral to Gauntlet",
|
||||||
destination="Cathedral Arena", tag="_"),
|
destination="Cathedral Arena", tag="_"),
|
||||||
|
@ -523,7 +523,6 @@ class RegionInfo(NamedTuple):
|
||||||
game_scene: str # the name of the scene in the actual game
|
game_scene: str # the name of the scene in the actual game
|
||||||
dead_end: int = 0 # if a region has only one exit
|
dead_end: int = 0 # if a region has only one exit
|
||||||
outlet_region: Optional[str] = None
|
outlet_region: Optional[str] = None
|
||||||
is_fake_region: bool = False
|
|
||||||
|
|
||||||
|
|
||||||
# gets the outlet region name if it exists, the region if it doesn't
|
# gets the outlet region name if it exists, the region if it doesn't
|
||||||
|
@ -563,6 +562,8 @@ tunic_er_regions: Dict[str, RegionInfo] = {
|
||||||
"Overworld to West Garden Upper": RegionInfo("Overworld Redux"), # usually leads to garden knight
|
"Overworld to West Garden Upper": RegionInfo("Overworld Redux"), # usually leads to garden knight
|
||||||
"Overworld to West Garden from Furnace": RegionInfo("Overworld Redux"), # isolated stairway with one chest
|
"Overworld to West Garden from Furnace": RegionInfo("Overworld Redux"), # isolated stairway with one chest
|
||||||
"Overworld Well Ladder": RegionInfo("Overworld Redux"), # just the ladder entrance itself as a region
|
"Overworld Well Ladder": RegionInfo("Overworld Redux"), # just the ladder entrance itself as a region
|
||||||
|
"Overworld Well Entry Area": RegionInfo("Overworld Redux"), # the page, the bridge, etc.
|
||||||
|
"Overworld Tunnel to Beach": RegionInfo("Overworld Redux"), # the tunnel with the chest
|
||||||
"Overworld Beach": RegionInfo("Overworld Redux"), # from the two turrets to invisble maze, and lower atoll entry
|
"Overworld Beach": RegionInfo("Overworld Redux"), # from the two turrets to invisble maze, and lower atoll entry
|
||||||
"Overworld Tunnel Turret": RegionInfo("Overworld Redux"), # the tunnel turret by the southwest beach ladder
|
"Overworld Tunnel Turret": RegionInfo("Overworld Redux"), # the tunnel turret by the southwest beach ladder
|
||||||
"Overworld to Atoll Upper": RegionInfo("Overworld Redux"), # the little ledge before the ladder
|
"Overworld to Atoll Upper": RegionInfo("Overworld Redux"), # the little ledge before the ladder
|
||||||
|
@ -624,14 +625,18 @@ tunic_er_regions: Dict[str, RegionInfo] = {
|
||||||
"Beneath the Well Front": RegionInfo("Sewer"), # the front, to separate it from the weapon requirement in the mid
|
"Beneath the Well Front": RegionInfo("Sewer"), # the front, to separate it from the weapon requirement in the mid
|
||||||
"Beneath the Well Main": RegionInfo("Sewer"), # the main section of it, requires a weapon
|
"Beneath the Well Main": RegionInfo("Sewer"), # the main section of it, requires a weapon
|
||||||
"Beneath the Well Back": RegionInfo("Sewer"), # the back two portals, and all 4 upper chests
|
"Beneath the Well Back": RegionInfo("Sewer"), # the back two portals, and all 4 upper chests
|
||||||
"West Garden": RegionInfo("Archipelagos Redux"),
|
"West Garden before Terry": RegionInfo("Archipelagos Redux"), # the lower entry point, near hero grave
|
||||||
|
"West Garden after Terry": RegionInfo("Archipelagos Redux"), # after Terry, up until next chompignons
|
||||||
|
"West Garden at Dagger House": RegionInfo("Archipelagos Redux"), # just outside magic dagger house
|
||||||
|
"West Garden South Checkpoint": RegionInfo("Archipelagos Redux"),
|
||||||
"Magic Dagger House": RegionInfo("archipelagos_house", dead_end=DeadEnd.all_cats),
|
"Magic Dagger House": RegionInfo("archipelagos_house", dead_end=DeadEnd.all_cats),
|
||||||
"West Garden Portal": RegionInfo("Archipelagos Redux", dead_end=DeadEnd.restricted, outlet_region="West Garden by Portal"),
|
"West Garden Portal": RegionInfo("Archipelagos Redux", dead_end=DeadEnd.restricted, outlet_region="West Garden by Portal"),
|
||||||
"West Garden by Portal": RegionInfo("Archipelagos Redux", dead_end=DeadEnd.restricted),
|
"West Garden by Portal": RegionInfo("Archipelagos Redux", dead_end=DeadEnd.restricted),
|
||||||
"West Garden Portal Item": RegionInfo("Archipelagos Redux", dead_end=DeadEnd.restricted),
|
"West Garden Portal Item": RegionInfo("Archipelagos Redux", dead_end=DeadEnd.restricted),
|
||||||
"West Garden Laurels Exit Region": RegionInfo("Archipelagos Redux"),
|
"West Garden Laurels Exit Region": RegionInfo("Archipelagos Redux"),
|
||||||
|
"West Garden before Boss": RegionInfo("Archipelagos Redux"), # main west garden
|
||||||
"West Garden after Boss": RegionInfo("Archipelagos Redux"),
|
"West Garden after Boss": RegionInfo("Archipelagos Redux"),
|
||||||
"West Garden Hero's Grave Region": RegionInfo("Archipelagos Redux", outlet_region="West Garden"),
|
"West Garden Hero's Grave Region": RegionInfo("Archipelagos Redux", outlet_region="West Garden before Terry"),
|
||||||
"Ruined Atoll": RegionInfo("Atoll Redux"),
|
"Ruined Atoll": RegionInfo("Atoll Redux"),
|
||||||
"Ruined Atoll Lower Entry Area": RegionInfo("Atoll Redux"),
|
"Ruined Atoll Lower Entry Area": RegionInfo("Atoll Redux"),
|
||||||
"Ruined Atoll Ladder Tops": RegionInfo("Atoll Redux"), # at the top of the 5 ladders in south Atoll
|
"Ruined Atoll Ladder Tops": RegionInfo("Atoll Redux"), # at the top of the 5 ladders in south Atoll
|
||||||
|
@ -643,8 +648,9 @@ tunic_er_regions: Dict[str, RegionInfo] = {
|
||||||
"Frog Stairs Upper": RegionInfo("Frog Stairs"),
|
"Frog Stairs Upper": RegionInfo("Frog Stairs"),
|
||||||
"Frog Stairs Lower": RegionInfo("Frog Stairs"),
|
"Frog Stairs Lower": RegionInfo("Frog Stairs"),
|
||||||
"Frog Stairs to Frog's Domain": RegionInfo("Frog Stairs"),
|
"Frog Stairs to Frog's Domain": RegionInfo("Frog Stairs"),
|
||||||
"Frog's Domain Entry": RegionInfo("frog cave main"),
|
"Frog's Domain Entry": RegionInfo("frog cave main"), # just the ladder
|
||||||
"Frog's Domain": RegionInfo("frog cave main"),
|
"Frog's Domain Front": RegionInfo("frog cave main"), # before combat
|
||||||
|
"Frog's Domain Main": RegionInfo("frog cave main"),
|
||||||
"Frog's Domain Back": RegionInfo("frog cave main"),
|
"Frog's Domain Back": RegionInfo("frog cave main"),
|
||||||
"Library Exterior Tree Region": RegionInfo("Library Exterior", outlet_region="Library Exterior by Tree"),
|
"Library Exterior Tree Region": RegionInfo("Library Exterior", outlet_region="Library Exterior by Tree"),
|
||||||
"Library Exterior by Tree": RegionInfo("Library Exterior"),
|
"Library Exterior by Tree": RegionInfo("Library Exterior"),
|
||||||
|
@ -658,8 +664,8 @@ tunic_er_regions: Dict[str, RegionInfo] = {
|
||||||
"Library Rotunda to Lab": RegionInfo("Library Rotunda"),
|
"Library Rotunda to Lab": RegionInfo("Library Rotunda"),
|
||||||
"Library Lab": RegionInfo("Library Lab"),
|
"Library Lab": RegionInfo("Library Lab"),
|
||||||
"Library Lab Lower": RegionInfo("Library Lab"),
|
"Library Lab Lower": RegionInfo("Library Lab"),
|
||||||
"Library Portal": RegionInfo("Library Lab", outlet_region="Library Lab on Portal Pad"),
|
|
||||||
"Library Lab on Portal Pad": RegionInfo("Library Lab"),
|
"Library Lab on Portal Pad": RegionInfo("Library Lab"),
|
||||||
|
"Library Portal": RegionInfo("Library Lab", outlet_region="Library Lab on Portal Pad"),
|
||||||
"Library Lab to Librarian": RegionInfo("Library Lab"),
|
"Library Lab to Librarian": RegionInfo("Library Lab"),
|
||||||
"Library Arena": RegionInfo("Library Arena", dead_end=DeadEnd.all_cats),
|
"Library Arena": RegionInfo("Library Arena", dead_end=DeadEnd.all_cats),
|
||||||
"Fortress Exterior from East Forest": RegionInfo("Fortress Courtyard"),
|
"Fortress Exterior from East Forest": RegionInfo("Fortress Courtyard"),
|
||||||
|
@ -675,10 +681,12 @@ tunic_er_regions: Dict[str, RegionInfo] = {
|
||||||
"Eastern Vault Fortress Gold Door": RegionInfo("Fortress Main"),
|
"Eastern Vault Fortress Gold Door": RegionInfo("Fortress Main"),
|
||||||
"Fortress East Shortcut Upper": RegionInfo("Fortress East"),
|
"Fortress East Shortcut Upper": RegionInfo("Fortress East"),
|
||||||
"Fortress East Shortcut Lower": RegionInfo("Fortress East"),
|
"Fortress East Shortcut Lower": RegionInfo("Fortress East"),
|
||||||
"Fortress Grave Path": RegionInfo("Fortress Reliquary"),
|
"Fortress Grave Path Entry": RegionInfo("Fortress Reliquary"),
|
||||||
|
"Fortress Grave Path Combat": RegionInfo("Fortress Reliquary"), # the combat is basically just a barrier here
|
||||||
|
"Fortress Grave Path by Grave": RegionInfo("Fortress Reliquary"),
|
||||||
"Fortress Grave Path Upper": RegionInfo("Fortress Reliquary", dead_end=DeadEnd.restricted),
|
"Fortress Grave Path Upper": RegionInfo("Fortress Reliquary", dead_end=DeadEnd.restricted),
|
||||||
"Fortress Grave Path Dusty Entrance Region": RegionInfo("Fortress Reliquary"),
|
"Fortress Grave Path Dusty Entrance Region": RegionInfo("Fortress Reliquary"),
|
||||||
"Fortress Hero's Grave Region": RegionInfo("Fortress Reliquary", outlet_region="Fortress Grave Path"),
|
"Fortress Hero's Grave Region": RegionInfo("Fortress Reliquary", outlet_region="Fortress Grave Path by Grave"),
|
||||||
"Fortress Leaf Piles": RegionInfo("Dusty", dead_end=DeadEnd.all_cats),
|
"Fortress Leaf Piles": RegionInfo("Dusty", dead_end=DeadEnd.all_cats),
|
||||||
"Fortress Arena": RegionInfo("Fortress Arena"),
|
"Fortress Arena": RegionInfo("Fortress Arena"),
|
||||||
"Fortress Arena Portal": RegionInfo("Fortress Arena", outlet_region="Fortress Arena"),
|
"Fortress Arena Portal": RegionInfo("Fortress Arena", outlet_region="Fortress Arena"),
|
||||||
|
@ -697,6 +705,7 @@ tunic_er_regions: Dict[str, RegionInfo] = {
|
||||||
"Monastery Rope": RegionInfo("Quarry Redux"),
|
"Monastery Rope": RegionInfo("Quarry Redux"),
|
||||||
"Lower Quarry": RegionInfo("Quarry Redux"),
|
"Lower Quarry": RegionInfo("Quarry Redux"),
|
||||||
"Even Lower Quarry": RegionInfo("Quarry Redux"),
|
"Even Lower Quarry": RegionInfo("Quarry Redux"),
|
||||||
|
"Even Lower Quarry Isolated Chest": RegionInfo("Quarry Redux"), # a region for that one chest
|
||||||
"Lower Quarry Zig Door": RegionInfo("Quarry Redux"),
|
"Lower Quarry Zig Door": RegionInfo("Quarry Redux"),
|
||||||
"Rooted Ziggurat Entry": RegionInfo("ziggurat2020_0"),
|
"Rooted Ziggurat Entry": RegionInfo("ziggurat2020_0"),
|
||||||
"Rooted Ziggurat Upper Entry": RegionInfo("ziggurat2020_1"),
|
"Rooted Ziggurat Upper Entry": RegionInfo("ziggurat2020_1"),
|
||||||
|
@ -704,13 +713,15 @@ tunic_er_regions: Dict[str, RegionInfo] = {
|
||||||
"Rooted Ziggurat Upper Back": RegionInfo("ziggurat2020_1"), # after the administrator
|
"Rooted Ziggurat Upper Back": RegionInfo("ziggurat2020_1"), # after the administrator
|
||||||
"Rooted Ziggurat Middle Top": RegionInfo("ziggurat2020_2"),
|
"Rooted Ziggurat Middle Top": RegionInfo("ziggurat2020_2"),
|
||||||
"Rooted Ziggurat Middle Bottom": RegionInfo("ziggurat2020_2"),
|
"Rooted Ziggurat Middle Bottom": RegionInfo("ziggurat2020_2"),
|
||||||
"Rooted Ziggurat Lower Front": RegionInfo("ziggurat2020_3"), # the vanilla entry point side
|
"Rooted Ziggurat Lower Entry": RegionInfo("ziggurat2020_3"), # the vanilla entry point side
|
||||||
|
"Rooted Ziggurat Lower Front": RegionInfo("ziggurat2020_3"), # the front for combat logic
|
||||||
|
"Rooted Ziggurat Lower Mid Checkpoint": RegionInfo("ziggurat2020_3"), # the mid-checkpoint before double admin
|
||||||
"Rooted Ziggurat Lower Back": RegionInfo("ziggurat2020_3"), # the boss side
|
"Rooted Ziggurat Lower Back": RegionInfo("ziggurat2020_3"), # the boss side
|
||||||
"Zig Skip Exit": RegionInfo("ziggurat2020_3", dead_end=DeadEnd.special, outlet_region="Rooted Ziggurat Lower Front"), # the exit from zig skip, for use with fixed shop on
|
"Zig Skip Exit": RegionInfo("ziggurat2020_3", dead_end=DeadEnd.special, outlet_region="Rooted Ziggurat Lower Entry"), # for use with fixed shop on
|
||||||
"Rooted Ziggurat Portal Room Entrance": RegionInfo("ziggurat2020_3", outlet_region="Rooted Ziggurat Lower Back"), # the door itself on the zig 3 side
|
"Rooted Ziggurat Portal Room Entrance": RegionInfo("ziggurat2020_3", outlet_region="Rooted Ziggurat Lower Back"), # the door itself on the zig 3 side
|
||||||
"Rooted Ziggurat Portal": RegionInfo("ziggurat2020_FTRoom", outlet_region="Rooted Ziggurat Portal Room"),
|
"Rooted Ziggurat Portal": RegionInfo("ziggurat2020_FTRoom", outlet_region="Rooted Ziggurat Portal Room"),
|
||||||
"Rooted Ziggurat Portal Room": RegionInfo("ziggurat2020_FTRoom"),
|
"Rooted Ziggurat Portal Room": RegionInfo("ziggurat2020_FTRoom"),
|
||||||
"Rooted Ziggurat Portal Room Exit": RegionInfo("ziggurat2020_FTRoom"),
|
"Rooted Ziggurat Portal Room Exit": RegionInfo("ziggurat2020_FTRoom", outlet_region="Rooted Ziggurat Portal Room"),
|
||||||
"Swamp Front": RegionInfo("Swamp Redux 2"), # from the main entry to the top of the ladder after south
|
"Swamp Front": RegionInfo("Swamp Redux 2"), # from the main entry to the top of the ladder after south
|
||||||
"Swamp Mid": RegionInfo("Swamp Redux 2"), # from the bottom of the ladder to the cathedral door
|
"Swamp Mid": RegionInfo("Swamp Redux 2"), # from the bottom of the ladder to the cathedral door
|
||||||
"Swamp Ledge under Cathedral Door": RegionInfo("Swamp Redux 2"), # the ledge with the chest and secret door
|
"Swamp Ledge under Cathedral Door": RegionInfo("Swamp Redux 2"), # the ledge with the chest and secret door
|
||||||
|
@ -719,7 +730,8 @@ tunic_er_regions: Dict[str, RegionInfo] = {
|
||||||
"Back of Swamp": RegionInfo("Swamp Redux 2"), # the area with hero grave and gauntlet entrance
|
"Back of Swamp": RegionInfo("Swamp Redux 2"), # the area with hero grave and gauntlet entrance
|
||||||
"Swamp Hero's Grave Region": RegionInfo("Swamp Redux 2", outlet_region="Back of Swamp"),
|
"Swamp Hero's Grave Region": RegionInfo("Swamp Redux 2", outlet_region="Back of Swamp"),
|
||||||
"Back of Swamp Laurels Area": RegionInfo("Swamp Redux 2"), # the spots you need laurels to traverse
|
"Back of Swamp Laurels Area": RegionInfo("Swamp Redux 2"), # the spots you need laurels to traverse
|
||||||
"Cathedral": RegionInfo("Cathedral Redux"),
|
"Cathedral Entry": RegionInfo("Cathedral Redux"), # the checkpoint and easily-accessible chests
|
||||||
|
"Cathedral Main": RegionInfo("Cathedral Redux"), # the majority of Cathedral
|
||||||
"Cathedral to Gauntlet": RegionInfo("Cathedral Redux"), # the elevator
|
"Cathedral to Gauntlet": RegionInfo("Cathedral Redux"), # the elevator
|
||||||
"Cathedral Secret Legend Room": RegionInfo("Cathedral Redux", dead_end=DeadEnd.all_cats),
|
"Cathedral Secret Legend Room": RegionInfo("Cathedral Redux", dead_end=DeadEnd.all_cats),
|
||||||
"Cathedral Gauntlet Checkpoint": RegionInfo("Cathedral Arena"),
|
"Cathedral Gauntlet Checkpoint": RegionInfo("Cathedral Arena"),
|
||||||
|
@ -741,7 +753,7 @@ tunic_er_regions: Dict[str, RegionInfo] = {
|
||||||
"Purgatory": RegionInfo("Purgatory"),
|
"Purgatory": RegionInfo("Purgatory"),
|
||||||
"Shop": RegionInfo("Shop", dead_end=DeadEnd.all_cats),
|
"Shop": RegionInfo("Shop", dead_end=DeadEnd.all_cats),
|
||||||
"Spirit Arena": RegionInfo("Spirit Arena", dead_end=DeadEnd.all_cats),
|
"Spirit Arena": RegionInfo("Spirit Arena", dead_end=DeadEnd.all_cats),
|
||||||
"Spirit Arena Victory": RegionInfo("Spirit Arena", dead_end=DeadEnd.all_cats)
|
"Spirit Arena Victory": RegionInfo("Spirit Arena", dead_end=DeadEnd.all_cats),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -759,6 +771,8 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Overworld": {
|
"Overworld": {
|
||||||
"Overworld Beach":
|
"Overworld Beach":
|
||||||
[],
|
[],
|
||||||
|
"Overworld Tunnel to Beach":
|
||||||
|
[],
|
||||||
"Overworld to Atoll Upper":
|
"Overworld to Atoll Upper":
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"]],
|
||||||
"Overworld Belltower":
|
"Overworld Belltower":
|
||||||
|
@ -769,7 +783,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
[],
|
[],
|
||||||
"Overworld Special Shop Entry":
|
"Overworld Special Shop Entry":
|
||||||
[["Hyperdash"], ["LS1"]],
|
[["Hyperdash"], ["LS1"]],
|
||||||
"Overworld Well Ladder":
|
"Overworld Well Entry Area":
|
||||||
[],
|
[],
|
||||||
"Overworld Ruined Passage Door":
|
"Overworld Ruined Passage Door":
|
||||||
[],
|
[],
|
||||||
|
@ -847,6 +861,12 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
# "Overworld":
|
# "Overworld":
|
||||||
# [],
|
# [],
|
||||||
# },
|
# },
|
||||||
|
"Overworld Tunnel to Beach": {
|
||||||
|
# "Overworld":
|
||||||
|
# [],
|
||||||
|
"Overworld Beach":
|
||||||
|
[],
|
||||||
|
},
|
||||||
"Overworld Beach": {
|
"Overworld Beach": {
|
||||||
# "Overworld":
|
# "Overworld":
|
||||||
# [],
|
# [],
|
||||||
|
@ -873,9 +893,15 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Overworld Beach":
|
"Overworld Beach":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Overworld Well Ladder": {
|
"Overworld Well Entry Area": {
|
||||||
# "Overworld":
|
# "Overworld":
|
||||||
# [],
|
# [],
|
||||||
|
"Overworld Well Ladder":
|
||||||
|
[],
|
||||||
|
},
|
||||||
|
"Overworld Well Ladder": {
|
||||||
|
"Overworld Well Entry Area":
|
||||||
|
[],
|
||||||
},
|
},
|
||||||
"Overworld at Patrol Cave": {
|
"Overworld at Patrol Cave": {
|
||||||
"East Overworld":
|
"East Overworld":
|
||||||
|
@ -954,6 +980,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Overworld":
|
"Overworld":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Old House Front": {
|
"Old House Front": {
|
||||||
"Old House Back":
|
"Old House Back":
|
||||||
[],
|
[],
|
||||||
|
@ -962,6 +989,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Old House Front":
|
"Old House Front":
|
||||||
[["Hyperdash", "Zip"]],
|
[["Hyperdash", "Zip"]],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Furnace Fuse": {
|
"Furnace Fuse": {
|
||||||
"Furnace Ladder Area":
|
"Furnace Ladder Area":
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"]],
|
||||||
|
@ -976,6 +1004,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Furnace Ladder Area":
|
"Furnace Ladder Area":
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"]],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Sealed Temple": {
|
"Sealed Temple": {
|
||||||
"Sealed Temple Rafters":
|
"Sealed Temple Rafters":
|
||||||
[],
|
[],
|
||||||
|
@ -984,10 +1013,12 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Sealed Temple":
|
"Sealed Temple":
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"]],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Hourglass Cave": {
|
"Hourglass Cave": {
|
||||||
"Hourglass Cave Tower":
|
"Hourglass Cave Tower":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Forest Belltower Upper": {
|
"Forest Belltower Upper": {
|
||||||
"Forest Belltower Main":
|
"Forest Belltower Main":
|
||||||
[],
|
[],
|
||||||
|
@ -996,6 +1027,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Forest Belltower Lower":
|
"Forest Belltower Lower":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"East Forest": {
|
"East Forest": {
|
||||||
"East Forest Dance Fox Spot":
|
"East Forest Dance Fox Spot":
|
||||||
[["Hyperdash"], ["IG1"], ["LS1"]],
|
[["Hyperdash"], ["IG1"], ["LS1"]],
|
||||||
|
@ -1016,6 +1048,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"East Forest":
|
"East Forest":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Guard House 1 East": {
|
"Guard House 1 East": {
|
||||||
"Guard House 1 West":
|
"Guard House 1 West":
|
||||||
[],
|
[],
|
||||||
|
@ -1024,6 +1057,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Guard House 1 East":
|
"Guard House 1 East":
|
||||||
[["Hyperdash"], ["LS1"]],
|
[["Hyperdash"], ["LS1"]],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Guard House 2 Upper": {
|
"Guard House 2 Upper": {
|
||||||
"Guard House 2 Lower":
|
"Guard House 2 Lower":
|
||||||
[],
|
[],
|
||||||
|
@ -1032,6 +1066,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Guard House 2 Upper":
|
"Guard House 2 Upper":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Forest Grave Path Main": {
|
"Forest Grave Path Main": {
|
||||||
"Forest Grave Path Upper":
|
"Forest Grave Path Upper":
|
||||||
[["Hyperdash"], ["LS2"], ["IG3"]],
|
[["Hyperdash"], ["LS2"], ["IG3"]],
|
||||||
|
@ -1052,6 +1087,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Forest Grave Path by Grave":
|
"Forest Grave Path by Grave":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Beneath the Well Ladder Exit": {
|
"Beneath the Well Ladder Exit": {
|
||||||
"Beneath the Well Front":
|
"Beneath the Well Front":
|
||||||
[],
|
[],
|
||||||
|
@ -1072,6 +1108,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Beneath the Well Main":
|
"Beneath the Well Main":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Well Boss": {
|
"Well Boss": {
|
||||||
"Dark Tomb Checkpoint":
|
"Dark Tomb Checkpoint":
|
||||||
[],
|
[],
|
||||||
|
@ -1080,6 +1117,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Well Boss":
|
"Well Boss":
|
||||||
[["Hyperdash", "Zip"]],
|
[["Hyperdash", "Zip"]],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Dark Tomb Entry Point": {
|
"Dark Tomb Entry Point": {
|
||||||
"Dark Tomb Upper":
|
"Dark Tomb Upper":
|
||||||
[],
|
[],
|
||||||
|
@ -1100,44 +1138,72 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Dark Tomb Main":
|
"Dark Tomb Main":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"West Garden": {
|
|
||||||
"West Garden Laurels Exit Region":
|
"West Garden before Terry": {
|
||||||
[["Hyperdash"], ["LS1"]],
|
"West Garden after Terry":
|
||||||
"West Garden after Boss":
|
|
||||||
[],
|
[],
|
||||||
"West Garden Hero's Grave Region":
|
"West Garden Hero's Grave Region":
|
||||||
[],
|
[],
|
||||||
|
},
|
||||||
|
"West Garden Hero's Grave Region": {
|
||||||
|
"West Garden before Terry":
|
||||||
|
[],
|
||||||
|
},
|
||||||
|
"West Garden after Terry": {
|
||||||
|
"West Garden before Terry":
|
||||||
|
[],
|
||||||
|
"West Garden South Checkpoint":
|
||||||
|
[],
|
||||||
|
"West Garden Laurels Exit Region":
|
||||||
|
[["LS1"]],
|
||||||
|
},
|
||||||
|
"West Garden South Checkpoint": {
|
||||||
|
"West Garden before Boss":
|
||||||
|
[],
|
||||||
|
"West Garden at Dagger House":
|
||||||
|
[],
|
||||||
|
"West Garden after Terry":
|
||||||
|
[],
|
||||||
|
},
|
||||||
|
"West Garden before Boss": {
|
||||||
|
"West Garden after Boss":
|
||||||
|
[],
|
||||||
|
"West Garden South Checkpoint":
|
||||||
|
[],
|
||||||
|
},
|
||||||
|
"West Garden after Boss": {
|
||||||
|
"West Garden before Boss":
|
||||||
|
[["Hyperdash"]],
|
||||||
|
},
|
||||||
|
"West Garden at Dagger House": {
|
||||||
|
"West Garden Laurels Exit Region":
|
||||||
|
[["Hyperdash"]],
|
||||||
|
"West Garden South Checkpoint":
|
||||||
|
[],
|
||||||
"West Garden Portal Item":
|
"West Garden Portal Item":
|
||||||
[["IG2"]],
|
[["IG2"]],
|
||||||
},
|
},
|
||||||
"West Garden Laurels Exit Region": {
|
"West Garden Laurels Exit Region": {
|
||||||
"West Garden":
|
"West Garden at Dagger House":
|
||||||
[["Hyperdash"]],
|
|
||||||
},
|
|
||||||
"West Garden after Boss": {
|
|
||||||
"West Garden":
|
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"]],
|
||||||
},
|
},
|
||||||
"West Garden Portal Item": {
|
"West Garden Portal Item": {
|
||||||
"West Garden":
|
"West Garden at Dagger House":
|
||||||
[["IG1"]],
|
[["IG1"]],
|
||||||
"West Garden by Portal":
|
"West Garden by Portal":
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"]],
|
||||||
},
|
},
|
||||||
"West Garden by Portal": {
|
"West Garden by Portal": {
|
||||||
|
"West Garden Portal":
|
||||||
|
[["West Garden South Checkpoint"]],
|
||||||
"West Garden Portal Item":
|
"West Garden Portal Item":
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"]],
|
||||||
"West Garden Portal":
|
|
||||||
[["West Garden"]],
|
|
||||||
},
|
},
|
||||||
"West Garden Portal": {
|
"West Garden Portal": {
|
||||||
"West Garden by Portal":
|
"West Garden by Portal":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"West Garden Hero's Grave Region": {
|
|
||||||
"West Garden":
|
|
||||||
[],
|
|
||||||
},
|
|
||||||
"Ruined Atoll": {
|
"Ruined Atoll": {
|
||||||
"Ruined Atoll Lower Entry Area":
|
"Ruined Atoll Lower Entry Area":
|
||||||
[["Hyperdash"], ["LS1"]],
|
[["Hyperdash"], ["LS1"]],
|
||||||
|
@ -1176,6 +1242,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Ruined Atoll":
|
"Ruined Atoll":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Frog Stairs Eye Exit": {
|
"Frog Stairs Eye Exit": {
|
||||||
"Frog Stairs Upper":
|
"Frog Stairs Upper":
|
||||||
[],
|
[],
|
||||||
|
@ -1196,16 +1263,25 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Frog Stairs Lower":
|
"Frog Stairs Lower":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Frog's Domain Entry": {
|
"Frog's Domain Entry": {
|
||||||
"Frog's Domain":
|
"Frog's Domain Front":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Frog's Domain": {
|
"Frog's Domain Front": {
|
||||||
"Frog's Domain Entry":
|
"Frog's Domain Entry":
|
||||||
[],
|
[],
|
||||||
|
"Frog's Domain Main":
|
||||||
|
[],
|
||||||
|
},
|
||||||
|
"Frog's Domain Main": {
|
||||||
|
"Frog's Domain Front":
|
||||||
|
[],
|
||||||
"Frog's Domain Back":
|
"Frog's Domain Back":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
# cannot get from frogs back to front
|
||||||
"Library Exterior Ladder Region": {
|
"Library Exterior Ladder Region": {
|
||||||
"Library Exterior by Tree":
|
"Library Exterior by Tree":
|
||||||
[],
|
[],
|
||||||
|
@ -1220,6 +1296,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Library Exterior by Tree":
|
"Library Exterior by Tree":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Library Hall Bookshelf": {
|
"Library Hall Bookshelf": {
|
||||||
"Library Hall":
|
"Library Hall":
|
||||||
[],
|
[],
|
||||||
|
@ -1240,6 +1317,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Library Hall":
|
"Library Hall":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Library Rotunda to Hall": {
|
"Library Rotunda to Hall": {
|
||||||
"Library Rotunda":
|
"Library Rotunda":
|
||||||
[],
|
[],
|
||||||
|
@ -1281,6 +1359,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Library Lab":
|
"Library Lab":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Fortress Exterior from East Forest": {
|
"Fortress Exterior from East Forest": {
|
||||||
"Fortress Exterior from Overworld":
|
"Fortress Exterior from Overworld":
|
||||||
[],
|
[],
|
||||||
|
@ -1321,6 +1400,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Fortress Courtyard":
|
"Fortress Courtyard":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Beneath the Vault Ladder Exit": {
|
"Beneath the Vault Ladder Exit": {
|
||||||
"Beneath the Vault Main":
|
"Beneath the Vault Main":
|
||||||
[],
|
[],
|
||||||
|
@ -1337,6 +1417,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Beneath the Vault Ladder Exit":
|
"Beneath the Vault Ladder Exit":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Fortress East Shortcut Lower": {
|
"Fortress East Shortcut Lower": {
|
||||||
"Fortress East Shortcut Upper":
|
"Fortress East Shortcut Upper":
|
||||||
[["IG1"]],
|
[["IG1"]],
|
||||||
|
@ -1345,6 +1426,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Fortress East Shortcut Lower":
|
"Fortress East Shortcut Lower":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Eastern Vault Fortress": {
|
"Eastern Vault Fortress": {
|
||||||
"Eastern Vault Fortress Gold Door":
|
"Eastern Vault Fortress Gold Door":
|
||||||
[["IG2"], ["Fortress Exterior from Overworld", "Beneath the Vault Back", "Fortress Courtyard Upper"]],
|
[["IG2"], ["Fortress Exterior from Overworld", "Beneath the Vault Back", "Fortress Courtyard Upper"]],
|
||||||
|
@ -1353,24 +1435,44 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Eastern Vault Fortress":
|
"Eastern Vault Fortress":
|
||||||
[["IG1"]],
|
[["IG1"]],
|
||||||
},
|
},
|
||||||
"Fortress Grave Path": {
|
|
||||||
|
"Fortress Grave Path Entry": {
|
||||||
|
"Fortress Grave Path Combat":
|
||||||
|
[],
|
||||||
|
# redundant here, keeping a comment to show it's intentional
|
||||||
|
# "Fortress Grave Path Dusty Entrance Region":
|
||||||
|
# [["Hyperdash"]],
|
||||||
|
},
|
||||||
|
"Fortress Grave Path Combat": {
|
||||||
|
"Fortress Grave Path Entry":
|
||||||
|
[],
|
||||||
|
"Fortress Grave Path by Grave":
|
||||||
|
[],
|
||||||
|
},
|
||||||
|
"Fortress Grave Path by Grave": {
|
||||||
|
"Fortress Grave Path Entry":
|
||||||
|
[],
|
||||||
|
# unnecessary, you can just skip it
|
||||||
|
# "Fortress Grave Path Combat":
|
||||||
|
# [],
|
||||||
"Fortress Hero's Grave Region":
|
"Fortress Hero's Grave Region":
|
||||||
[],
|
[],
|
||||||
"Fortress Grave Path Dusty Entrance Region":
|
"Fortress Grave Path Dusty Entrance Region":
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"]],
|
||||||
},
|
},
|
||||||
"Fortress Grave Path Upper": {
|
"Fortress Grave Path Upper": {
|
||||||
"Fortress Grave Path":
|
"Fortress Grave Path Entry":
|
||||||
[["IG1"]],
|
[["IG1"]],
|
||||||
},
|
},
|
||||||
"Fortress Grave Path Dusty Entrance Region": {
|
"Fortress Grave Path Dusty Entrance Region": {
|
||||||
"Fortress Grave Path":
|
"Fortress Grave Path by Grave":
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"]],
|
||||||
},
|
},
|
||||||
"Fortress Hero's Grave Region": {
|
"Fortress Hero's Grave Region": {
|
||||||
"Fortress Grave Path":
|
"Fortress Grave Path by Grave":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Fortress Arena": {
|
"Fortress Arena": {
|
||||||
"Fortress Arena Portal":
|
"Fortress Arena Portal":
|
||||||
[["Fortress Exterior from Overworld", "Beneath the Vault Back", "Eastern Vault Fortress"]],
|
[["Fortress Exterior from Overworld", "Beneath the Vault Back", "Eastern Vault Fortress"]],
|
||||||
|
@ -1379,6 +1481,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Fortress Arena":
|
"Fortress Arena":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Lower Mountain": {
|
"Lower Mountain": {
|
||||||
"Lower Mountain Stairs":
|
"Lower Mountain Stairs":
|
||||||
[],
|
[],
|
||||||
|
@ -1387,6 +1490,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Lower Mountain":
|
"Lower Mountain":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Monastery Back": {
|
"Monastery Back": {
|
||||||
"Monastery Front":
|
"Monastery Front":
|
||||||
[["Hyperdash", "Zip"]],
|
[["Hyperdash", "Zip"]],
|
||||||
|
@ -1401,6 +1505,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Monastery Back":
|
"Monastery Back":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Quarry Entry": {
|
"Quarry Entry": {
|
||||||
"Quarry Portal":
|
"Quarry Portal":
|
||||||
[["Quarry Connector"]],
|
[["Quarry Connector"]],
|
||||||
|
@ -1436,15 +1541,17 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
[],
|
[],
|
||||||
"Quarry Monastery Entry":
|
"Quarry Monastery Entry":
|
||||||
[],
|
[],
|
||||||
"Lower Quarry Zig Door":
|
|
||||||
[["IG3"]],
|
|
||||||
},
|
},
|
||||||
"Lower Quarry": {
|
"Lower Quarry": {
|
||||||
"Even Lower Quarry":
|
"Even Lower Quarry":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Even Lower Quarry": {
|
"Even Lower Quarry": {
|
||||||
"Lower Quarry":
|
"Even Lower Quarry Isolated Chest":
|
||||||
|
[],
|
||||||
|
},
|
||||||
|
"Even Lower Quarry Isolated Chest": {
|
||||||
|
"Even Lower Quarry":
|
||||||
[],
|
[],
|
||||||
"Lower Quarry Zig Door":
|
"Lower Quarry Zig Door":
|
||||||
[["Quarry", "Quarry Connector"], ["IG3"]],
|
[["Quarry", "Quarry Connector"], ["IG3"]],
|
||||||
|
@ -1453,6 +1560,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Quarry Back":
|
"Quarry Back":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Rooted Ziggurat Upper Entry": {
|
"Rooted Ziggurat Upper Entry": {
|
||||||
"Rooted Ziggurat Upper Front":
|
"Rooted Ziggurat Upper Front":
|
||||||
[],
|
[],
|
||||||
|
@ -1465,17 +1573,38 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Rooted Ziggurat Upper Front":
|
"Rooted Ziggurat Upper Front":
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"]],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Rooted Ziggurat Middle Top": {
|
"Rooted Ziggurat Middle Top": {
|
||||||
"Rooted Ziggurat Middle Bottom":
|
"Rooted Ziggurat Middle Bottom":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"Rooted Ziggurat Lower Entry": {
|
||||||
|
"Rooted Ziggurat Lower Front":
|
||||||
|
[],
|
||||||
|
# can zip through to the checkpoint
|
||||||
|
"Rooted Ziggurat Lower Mid Checkpoint":
|
||||||
|
[["Hyperdash"]],
|
||||||
|
},
|
||||||
"Rooted Ziggurat Lower Front": {
|
"Rooted Ziggurat Lower Front": {
|
||||||
|
"Rooted Ziggurat Lower Entry":
|
||||||
|
[],
|
||||||
|
"Rooted Ziggurat Lower Mid Checkpoint":
|
||||||
|
[],
|
||||||
|
},
|
||||||
|
"Rooted Ziggurat Lower Mid Checkpoint": {
|
||||||
|
"Rooted Ziggurat Lower Entry":
|
||||||
|
[["Hyperdash"]],
|
||||||
|
"Rooted Ziggurat Lower Front":
|
||||||
|
[],
|
||||||
"Rooted Ziggurat Lower Back":
|
"Rooted Ziggurat Lower Back":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Rooted Ziggurat Lower Back": {
|
"Rooted Ziggurat Lower Back": {
|
||||||
"Rooted Ziggurat Lower Front":
|
"Rooted Ziggurat Lower Entry":
|
||||||
[["Hyperdash"], ["LS2"], ["IG1"]],
|
[["LS2"]],
|
||||||
|
"Rooted Ziggurat Lower Mid Checkpoint":
|
||||||
|
[["Hyperdash"], ["IG1"]],
|
||||||
"Rooted Ziggurat Portal Room Entrance":
|
"Rooted Ziggurat Portal Room Entrance":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
@ -1487,20 +1616,22 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Rooted Ziggurat Lower Back":
|
"Rooted Ziggurat Lower Back":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Rooted Ziggurat Portal Room Exit": {
|
"Rooted Ziggurat Portal Room Exit": {
|
||||||
"Rooted Ziggurat Portal Room":
|
"Rooted Ziggurat Portal Room":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Rooted Ziggurat Portal Room": {
|
"Rooted Ziggurat Portal Room": {
|
||||||
"Rooted Ziggurat Portal":
|
|
||||||
[],
|
|
||||||
"Rooted Ziggurat Portal Room Exit":
|
"Rooted Ziggurat Portal Room Exit":
|
||||||
[["Rooted Ziggurat Lower Back"]],
|
[["Rooted Ziggurat Lower Back"]],
|
||||||
|
"Rooted Ziggurat Portal":
|
||||||
|
[],
|
||||||
},
|
},
|
||||||
"Rooted Ziggurat Portal": {
|
"Rooted Ziggurat Portal": {
|
||||||
"Rooted Ziggurat Portal Room":
|
"Rooted Ziggurat Portal Room":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Swamp Front": {
|
"Swamp Front": {
|
||||||
"Swamp Mid":
|
"Swamp Mid":
|
||||||
[],
|
[],
|
||||||
|
@ -1557,14 +1688,26 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Back of Swamp":
|
"Back of Swamp":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Cathedral": {
|
|
||||||
|
"Cathedral Entry": {
|
||||||
|
"Cathedral to Gauntlet":
|
||||||
|
[],
|
||||||
|
"Cathedral Main":
|
||||||
|
[],
|
||||||
|
},
|
||||||
|
"Cathedral Main": {
|
||||||
|
"Cathedral Entry":
|
||||||
|
[],
|
||||||
"Cathedral to Gauntlet":
|
"Cathedral to Gauntlet":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
"Cathedral to Gauntlet": {
|
"Cathedral to Gauntlet": {
|
||||||
"Cathedral":
|
"Cathedral Entry":
|
||||||
|
[],
|
||||||
|
"Cathedral Main":
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Cathedral Gauntlet Checkpoint": {
|
"Cathedral Gauntlet Checkpoint": {
|
||||||
"Cathedral Gauntlet":
|
"Cathedral Gauntlet":
|
||||||
[],
|
[],
|
||||||
|
@ -1577,6 +1720,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Cathedral Gauntlet":
|
"Cathedral Gauntlet":
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"]],
|
||||||
},
|
},
|
||||||
|
|
||||||
"Far Shore": {
|
"Far Shore": {
|
||||||
"Far Shore to Spawn Region":
|
"Far Shore to Spawn Region":
|
||||||
[["Hyperdash"]],
|
[["Hyperdash"]],
|
||||||
|
@ -1587,7 +1731,7 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
|
||||||
"Far Shore to Library Region":
|
"Far Shore to Library Region":
|
||||||
[["Library Lab"]],
|
[["Library Lab"]],
|
||||||
"Far Shore to West Garden Region":
|
"Far Shore to West Garden Region":
|
||||||
[["West Garden"]],
|
[["West Garden South Checkpoint"]],
|
||||||
"Far Shore to Fortress Region":
|
"Far Shore to Fortress Region":
|
||||||
[["Fortress Exterior from Overworld", "Beneath the Vault Back", "Eastern Vault Fortress"]],
|
[["Fortress Exterior from Overworld", "Beneath the Vault Back", "Eastern Vault Fortress"]],
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
from typing import Dict, FrozenSet, Tuple, TYPE_CHECKING
|
from typing import Dict, FrozenSet, Tuple, TYPE_CHECKING
|
||||||
from worlds.generic.Rules import set_rule, forbid_item
|
from worlds.generic.Rules import set_rule, add_rule, forbid_item
|
||||||
from .options import IceGrappling, LadderStorage
|
from .options import IceGrappling, LadderStorage, CombatLogic
|
||||||
from .rules import (has_ability, has_sword, has_stick, has_ice_grapple_logic, has_lantern, has_mask, can_ladder_storage,
|
from .rules import (has_ability, has_sword, has_melee, has_ice_grapple_logic, has_lantern, has_mask, can_ladder_storage,
|
||||||
laurels_zip, bomb_walls)
|
laurels_zip, bomb_walls)
|
||||||
from .er_data import Portal, get_portal_outlet_region
|
from .er_data import Portal, get_portal_outlet_region
|
||||||
from .ladder_storage_data import ow_ladder_groups, region_ladders, easy_ls, medium_ls, hard_ls
|
from .ladder_storage_data import ow_ladder_groups, region_ladders, easy_ls, medium_ls, hard_ls
|
||||||
|
from .combat_logic import has_combat_reqs
|
||||||
from BaseClasses import Region, CollectionState
|
from BaseClasses import Region, CollectionState
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
@ -43,6 +44,24 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
player = world.player
|
player = world.player
|
||||||
options = world.options
|
options = world.options
|
||||||
|
|
||||||
|
# input scene destination tag, returns portal's name and paired portal's outlet region or region
|
||||||
|
def get_portal_info(portal_sd: str) -> Tuple[str, str]:
|
||||||
|
for portal1, portal2 in portal_pairs.items():
|
||||||
|
if portal1.scene_destination() == portal_sd:
|
||||||
|
return portal1.name, get_portal_outlet_region(portal2, world)
|
||||||
|
if portal2.scene_destination() == portal_sd:
|
||||||
|
return portal2.name, get_portal_outlet_region(portal1, world)
|
||||||
|
raise Exception("No matches found in get_portal_info")
|
||||||
|
|
||||||
|
# input scene destination tag, returns paired portal's name and region
|
||||||
|
def get_paired_portal(portal_sd: str) -> Tuple[str, str]:
|
||||||
|
for portal1, portal2 in portal_pairs.items():
|
||||||
|
if portal1.scene_destination() == portal_sd:
|
||||||
|
return portal2.name, portal2.region
|
||||||
|
if portal2.scene_destination() == portal_sd:
|
||||||
|
return portal1.name, portal1.region
|
||||||
|
raise Exception("no matches found in get_paired_portal")
|
||||||
|
|
||||||
regions["Menu"].connect(
|
regions["Menu"].connect(
|
||||||
connecting_region=regions["Overworld"])
|
connecting_region=regions["Overworld"])
|
||||||
|
|
||||||
|
@ -56,10 +75,18 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
connecting_region=regions["Overworld Beach"],
|
connecting_region=regions["Overworld Beach"],
|
||||||
rule=lambda state: has_ladder("Ladders in Overworld Town", state, world)
|
rule=lambda state: has_ladder("Ladders in Overworld Town", state, world)
|
||||||
or state.has_any({laurels, grapple}, player))
|
or state.has_any({laurels, grapple}, player))
|
||||||
|
# regions["Overworld Beach"].connect(
|
||||||
|
# connecting_region=regions["Overworld"],
|
||||||
|
# rule=lambda state: has_ladder("Ladders in Overworld Town", state, world)
|
||||||
|
# or state.has_any({laurels, grapple}, player))
|
||||||
|
|
||||||
|
# region for combat logic, no need to connect it to beach since it would be the same as the ow -> beach cxn
|
||||||
|
ow_tunnel_beach = regions["Overworld"].connect(
|
||||||
|
connecting_region=regions["Overworld Tunnel to Beach"])
|
||||||
|
|
||||||
regions["Overworld Beach"].connect(
|
regions["Overworld Beach"].connect(
|
||||||
connecting_region=regions["Overworld"],
|
connecting_region=regions["Overworld Tunnel to Beach"],
|
||||||
rule=lambda state: has_ladder("Ladders in Overworld Town", state, world)
|
rule=lambda state: state.has(laurels, player) or has_ladder("Ladders in Overworld Town", state, world))
|
||||||
or state.has_any({laurels, grapple}, player))
|
|
||||||
|
|
||||||
regions["Overworld Beach"].connect(
|
regions["Overworld Beach"].connect(
|
||||||
connecting_region=regions["Overworld West Garden Laurels Entry"],
|
connecting_region=regions["Overworld West Garden Laurels Entry"],
|
||||||
|
@ -277,11 +304,17 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
connecting_region=regions["East Overworld"],
|
connecting_region=regions["East Overworld"],
|
||||||
rule=lambda state: state.has(laurels, player))
|
rule=lambda state: state.has(laurels, player))
|
||||||
|
|
||||||
regions["Overworld"].connect(
|
# region made for combat logic
|
||||||
|
ow_to_well_entry = regions["Overworld"].connect(
|
||||||
|
connecting_region=regions["Overworld Well Entry Area"])
|
||||||
|
regions["Overworld Well Entry Area"].connect(
|
||||||
|
connecting_region=regions["Overworld"])
|
||||||
|
|
||||||
|
regions["Overworld Well Entry Area"].connect(
|
||||||
connecting_region=regions["Overworld Well Ladder"],
|
connecting_region=regions["Overworld Well Ladder"],
|
||||||
rule=lambda state: has_ladder("Ladders in Well", state, world))
|
rule=lambda state: has_ladder("Ladders in Well", state, world))
|
||||||
regions["Overworld Well Ladder"].connect(
|
regions["Overworld Well Ladder"].connect(
|
||||||
connecting_region=regions["Overworld"],
|
connecting_region=regions["Overworld Well Entry Area"],
|
||||||
rule=lambda state: has_ladder("Ladders in Well", state, world))
|
rule=lambda state: has_ladder("Ladders in Well", state, world))
|
||||||
|
|
||||||
# nmg: can ice grapple through the door
|
# nmg: can ice grapple through the door
|
||||||
|
@ -306,7 +339,7 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
regions["Overworld Fountain Cross Door"].connect(
|
regions["Overworld Fountain Cross Door"].connect(
|
||||||
connecting_region=regions["Overworld"])
|
connecting_region=regions["Overworld"])
|
||||||
|
|
||||||
regions["Overworld"].connect(
|
ow_to_town_portal = regions["Overworld"].connect(
|
||||||
connecting_region=regions["Overworld Town Portal"],
|
connecting_region=regions["Overworld Town Portal"],
|
||||||
rule=lambda state: has_ability(prayer, state, world))
|
rule=lambda state: has_ability(prayer, state, world))
|
||||||
regions["Overworld Town Portal"].connect(
|
regions["Overworld Town Portal"].connect(
|
||||||
|
@ -337,6 +370,7 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
rule=lambda state: has_ladder("Ladders in Overworld Town", state, world)
|
rule=lambda state: has_ladder("Ladders in Overworld Town", state, world)
|
||||||
or has_ice_grapple_logic(True, IceGrappling.option_easy, state, world))
|
or has_ice_grapple_logic(True, IceGrappling.option_easy, state, world))
|
||||||
|
|
||||||
|
# don't need the ice grapple rule since you can go from ow -> beach -> tunnel
|
||||||
regions["Overworld"].connect(
|
regions["Overworld"].connect(
|
||||||
connecting_region=regions["Overworld Tunnel Turret"],
|
connecting_region=regions["Overworld Tunnel Turret"],
|
||||||
rule=lambda state: state.has(laurels, player))
|
rule=lambda state: state.has(laurels, player))
|
||||||
|
@ -473,29 +507,28 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
connecting_region=regions["Beneath the Well Ladder Exit"],
|
connecting_region=regions["Beneath the Well Ladder Exit"],
|
||||||
rule=lambda state: has_ladder("Ladders in Well", state, world))
|
rule=lambda state: has_ladder("Ladders in Well", state, world))
|
||||||
|
|
||||||
regions["Beneath the Well Front"].connect(
|
btw_front_main = regions["Beneath the Well Front"].connect(
|
||||||
connecting_region=regions["Beneath the Well Main"],
|
connecting_region=regions["Beneath the Well Main"],
|
||||||
rule=lambda state: has_stick(state, player) or state.has(fire_wand, player))
|
rule=lambda state: has_melee(state, player) or state.has(fire_wand, player))
|
||||||
regions["Beneath the Well Main"].connect(
|
regions["Beneath the Well Main"].connect(
|
||||||
connecting_region=regions["Beneath the Well Front"],
|
connecting_region=regions["Beneath the Well Front"])
|
||||||
rule=lambda state: has_stick(state, player) or state.has(fire_wand, player))
|
|
||||||
|
|
||||||
regions["Beneath the Well Main"].connect(
|
regions["Beneath the Well Main"].connect(
|
||||||
connecting_region=regions["Beneath the Well Back"],
|
connecting_region=regions["Beneath the Well Back"],
|
||||||
rule=lambda state: has_ladder("Ladders in Well", state, world))
|
rule=lambda state: has_ladder("Ladders in Well", state, world))
|
||||||
regions["Beneath the Well Back"].connect(
|
btw_back_main = regions["Beneath the Well Back"].connect(
|
||||||
connecting_region=regions["Beneath the Well Main"],
|
connecting_region=regions["Beneath the Well Main"],
|
||||||
rule=lambda state: has_ladder("Ladders in Well", state, world)
|
rule=lambda state: has_ladder("Ladders in Well", state, world)
|
||||||
and (has_stick(state, player) or state.has(fire_wand, player)))
|
and (has_melee(state, player) or state.has(fire_wand, player)))
|
||||||
|
|
||||||
regions["Well Boss"].connect(
|
well_boss_to_dt = regions["Well Boss"].connect(
|
||||||
connecting_region=regions["Dark Tomb Checkpoint"])
|
connecting_region=regions["Dark Tomb Checkpoint"])
|
||||||
# can laurels through the gate, no setup needed
|
# can laurels through the gate, no setup needed
|
||||||
regions["Dark Tomb Checkpoint"].connect(
|
regions["Dark Tomb Checkpoint"].connect(
|
||||||
connecting_region=regions["Well Boss"],
|
connecting_region=regions["Well Boss"],
|
||||||
rule=lambda state: laurels_zip(state, world))
|
rule=lambda state: laurels_zip(state, world))
|
||||||
|
|
||||||
regions["Dark Tomb Entry Point"].connect(
|
dt_entry_to_upper = regions["Dark Tomb Entry Point"].connect(
|
||||||
connecting_region=regions["Dark Tomb Upper"],
|
connecting_region=regions["Dark Tomb Upper"],
|
||||||
rule=lambda state: has_lantern(state, world))
|
rule=lambda state: has_lantern(state, world))
|
||||||
regions["Dark Tomb Upper"].connect(
|
regions["Dark Tomb Upper"].connect(
|
||||||
|
@ -512,34 +545,57 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
|
|
||||||
regions["Dark Tomb Main"].connect(
|
regions["Dark Tomb Main"].connect(
|
||||||
connecting_region=regions["Dark Tomb Dark Exit"])
|
connecting_region=regions["Dark Tomb Dark Exit"])
|
||||||
regions["Dark Tomb Dark Exit"].connect(
|
dt_exit_to_main = regions["Dark Tomb Dark Exit"].connect(
|
||||||
connecting_region=regions["Dark Tomb Main"],
|
connecting_region=regions["Dark Tomb Main"],
|
||||||
rule=lambda state: has_lantern(state, world))
|
rule=lambda state: has_lantern(state, world))
|
||||||
|
|
||||||
# West Garden
|
# West Garden
|
||||||
|
# combat logic regions
|
||||||
|
wg_before_to_after_terry = regions["West Garden before Terry"].connect(
|
||||||
|
connecting_region=regions["West Garden after Terry"])
|
||||||
|
wg_after_to_before_terry = regions["West Garden after Terry"].connect(
|
||||||
|
connecting_region=regions["West Garden before Terry"])
|
||||||
|
|
||||||
|
regions["West Garden after Terry"].connect(
|
||||||
|
connecting_region=regions["West Garden South Checkpoint"])
|
||||||
|
wg_checkpoint_to_after_terry = regions["West Garden South Checkpoint"].connect(
|
||||||
|
connecting_region=regions["West Garden after Terry"])
|
||||||
|
|
||||||
|
wg_checkpoint_to_dagger = regions["West Garden South Checkpoint"].connect(
|
||||||
|
connecting_region=regions["West Garden at Dagger House"])
|
||||||
|
regions["West Garden at Dagger House"].connect(
|
||||||
|
connecting_region=regions["West Garden South Checkpoint"])
|
||||||
|
|
||||||
|
wg_checkpoint_to_before_boss = regions["West Garden South Checkpoint"].connect(
|
||||||
|
connecting_region=regions["West Garden before Boss"])
|
||||||
|
regions["West Garden before Boss"].connect(
|
||||||
|
connecting_region=regions["West Garden South Checkpoint"])
|
||||||
|
|
||||||
regions["West Garden Laurels Exit Region"].connect(
|
regions["West Garden Laurels Exit Region"].connect(
|
||||||
connecting_region=regions["West Garden"],
|
connecting_region=regions["West Garden at Dagger House"],
|
||||||
rule=lambda state: state.has(laurels, player))
|
rule=lambda state: state.has(laurels, player))
|
||||||
regions["West Garden"].connect(
|
regions["West Garden at Dagger House"].connect(
|
||||||
connecting_region=regions["West Garden Laurels Exit Region"],
|
connecting_region=regions["West Garden Laurels Exit Region"],
|
||||||
rule=lambda state: state.has(laurels, player))
|
rule=lambda state: state.has(laurels, player))
|
||||||
|
|
||||||
# you can grapple Garden Knight to aggro it, then ledge it
|
# laurels past, or ice grapple it off, or ice grapple to it then fight
|
||||||
regions["West Garden after Boss"].connect(
|
after_gk_to_wg = regions["West Garden after Boss"].connect(
|
||||||
connecting_region=regions["West Garden"],
|
connecting_region=regions["West Garden before Boss"],
|
||||||
rule=lambda state: state.has(laurels, player)
|
rule=lambda state: state.has(laurels, player)
|
||||||
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world))
|
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)
|
||||||
|
or (has_ice_grapple_logic(False, IceGrappling.option_easy, state, world)
|
||||||
|
and has_sword(state, player)))
|
||||||
# ice grapple push Garden Knight off the side
|
# ice grapple push Garden Knight off the side
|
||||||
regions["West Garden"].connect(
|
wg_to_after_gk = regions["West Garden before Boss"].connect(
|
||||||
connecting_region=regions["West Garden after Boss"],
|
connecting_region=regions["West Garden after Boss"],
|
||||||
rule=lambda state: state.has(laurels, player) or has_sword(state, player)
|
rule=lambda state: state.has(laurels, player) or has_sword(state, player)
|
||||||
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world))
|
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world))
|
||||||
|
|
||||||
regions["West Garden"].connect(
|
regions["West Garden before Terry"].connect(
|
||||||
connecting_region=regions["West Garden Hero's Grave Region"],
|
connecting_region=regions["West Garden Hero's Grave Region"],
|
||||||
rule=lambda state: has_ability(prayer, state, world))
|
rule=lambda state: has_ability(prayer, state, world))
|
||||||
regions["West Garden Hero's Grave Region"].connect(
|
regions["West Garden Hero's Grave Region"].connect(
|
||||||
connecting_region=regions["West Garden"])
|
connecting_region=regions["West Garden before Terry"])
|
||||||
|
|
||||||
regions["West Garden Portal"].connect(
|
regions["West Garden Portal"].connect(
|
||||||
connecting_region=regions["West Garden by Portal"])
|
connecting_region=regions["West Garden by Portal"])
|
||||||
|
@ -556,9 +612,9 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
|
|
||||||
# can ice grapple to and from the item behind the magic dagger house
|
# can ice grapple to and from the item behind the magic dagger house
|
||||||
regions["West Garden Portal Item"].connect(
|
regions["West Garden Portal Item"].connect(
|
||||||
connecting_region=regions["West Garden"],
|
connecting_region=regions["West Garden at Dagger House"],
|
||||||
rule=lambda state: has_ice_grapple_logic(True, IceGrappling.option_easy, state, world))
|
rule=lambda state: has_ice_grapple_logic(True, IceGrappling.option_easy, state, world))
|
||||||
regions["West Garden"].connect(
|
regions["West Garden at Dagger House"].connect(
|
||||||
connecting_region=regions["West Garden Portal Item"],
|
connecting_region=regions["West Garden Portal Item"],
|
||||||
rule=lambda state: has_ice_grapple_logic(True, IceGrappling.option_medium, state, world))
|
rule=lambda state: has_ice_grapple_logic(True, IceGrappling.option_medium, state, world))
|
||||||
|
|
||||||
|
@ -596,7 +652,7 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
regions["Ruined Atoll Portal"].connect(
|
regions["Ruined Atoll Portal"].connect(
|
||||||
connecting_region=regions["Ruined Atoll"])
|
connecting_region=regions["Ruined Atoll"])
|
||||||
|
|
||||||
regions["Ruined Atoll"].connect(
|
atoll_statue = regions["Ruined Atoll"].connect(
|
||||||
connecting_region=regions["Ruined Atoll Statue"],
|
connecting_region=regions["Ruined Atoll Statue"],
|
||||||
rule=lambda state: has_ability(prayer, state, world)
|
rule=lambda state: has_ability(prayer, state, world)
|
||||||
and (has_ladder("Ladders in South Atoll", state, world)
|
and (has_ladder("Ladders in South Atoll", state, world)
|
||||||
|
@ -629,10 +685,13 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
rule=lambda state: has_ladder("Ladders to Frog's Domain", state, world))
|
rule=lambda state: has_ladder("Ladders to Frog's Domain", state, world))
|
||||||
|
|
||||||
regions["Frog's Domain Entry"].connect(
|
regions["Frog's Domain Entry"].connect(
|
||||||
connecting_region=regions["Frog's Domain"],
|
connecting_region=regions["Frog's Domain Front"],
|
||||||
rule=lambda state: has_ladder("Ladders to Frog's Domain", state, world))
|
rule=lambda state: has_ladder("Ladders to Frog's Domain", state, world))
|
||||||
|
|
||||||
regions["Frog's Domain"].connect(
|
frogs_front_to_main = regions["Frog's Domain Front"].connect(
|
||||||
|
connecting_region=regions["Frog's Domain Main"])
|
||||||
|
|
||||||
|
regions["Frog's Domain Main"].connect(
|
||||||
connecting_region=regions["Frog's Domain Back"],
|
connecting_region=regions["Frog's Domain Back"],
|
||||||
rule=lambda state: state.has(grapple, player))
|
rule=lambda state: state.has(grapple, player))
|
||||||
|
|
||||||
|
@ -752,7 +811,7 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
rule=lambda state: state.has(laurels, player)
|
rule=lambda state: state.has(laurels, player)
|
||||||
or has_ice_grapple_logic(True, IceGrappling.option_easy, state, world))
|
or has_ice_grapple_logic(True, IceGrappling.option_easy, state, world))
|
||||||
|
|
||||||
regions["Fortress Courtyard Upper"].connect(
|
fort_upper_lower = regions["Fortress Courtyard Upper"].connect(
|
||||||
connecting_region=regions["Fortress Courtyard"])
|
connecting_region=regions["Fortress Courtyard"])
|
||||||
# nmg: can ice grapple to the upper ledge
|
# nmg: can ice grapple to the upper ledge
|
||||||
regions["Fortress Courtyard"].connect(
|
regions["Fortress Courtyard"].connect(
|
||||||
|
@ -762,12 +821,12 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
regions["Fortress Courtyard Upper"].connect(
|
regions["Fortress Courtyard Upper"].connect(
|
||||||
connecting_region=regions["Fortress Exterior from Overworld"])
|
connecting_region=regions["Fortress Exterior from Overworld"])
|
||||||
|
|
||||||
regions["Beneath the Vault Ladder Exit"].connect(
|
btv_front_to_main = regions["Beneath the Vault Ladder Exit"].connect(
|
||||||
connecting_region=regions["Beneath the Vault Main"],
|
connecting_region=regions["Beneath the Vault Main"],
|
||||||
rule=lambda state: has_ladder("Ladder to Beneath the Vault", state, world)
|
rule=lambda state: has_ladder("Ladder to Beneath the Vault", state, world)
|
||||||
and has_lantern(state, world)
|
and has_lantern(state, world)
|
||||||
# there's some boxes in the way
|
# there's some boxes in the way
|
||||||
and (has_stick(state, player) or state.has_any((gun, grapple, fire_wand, laurels), player)))
|
and (has_melee(state, player) or state.has_any((gun, grapple, fire_wand, laurels), player)))
|
||||||
# on the reverse trip, you can lure an enemy over to break the boxes if needed
|
# on the reverse trip, you can lure an enemy over to break the boxes if needed
|
||||||
regions["Beneath the Vault Main"].connect(
|
regions["Beneath the Vault Main"].connect(
|
||||||
connecting_region=regions["Beneath the Vault Ladder Exit"],
|
connecting_region=regions["Beneath the Vault Ladder Exit"],
|
||||||
|
@ -775,11 +834,11 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
|
|
||||||
regions["Beneath the Vault Main"].connect(
|
regions["Beneath the Vault Main"].connect(
|
||||||
connecting_region=regions["Beneath the Vault Back"])
|
connecting_region=regions["Beneath the Vault Back"])
|
||||||
regions["Beneath the Vault Back"].connect(
|
btv_back_to_main = regions["Beneath the Vault Back"].connect(
|
||||||
connecting_region=regions["Beneath the Vault Main"],
|
connecting_region=regions["Beneath the Vault Main"],
|
||||||
rule=lambda state: has_lantern(state, world))
|
rule=lambda state: has_lantern(state, world))
|
||||||
|
|
||||||
regions["Fortress East Shortcut Upper"].connect(
|
fort_east_upper_lower = regions["Fortress East Shortcut Upper"].connect(
|
||||||
connecting_region=regions["Fortress East Shortcut Lower"])
|
connecting_region=regions["Fortress East Shortcut Lower"])
|
||||||
regions["Fortress East Shortcut Lower"].connect(
|
regions["Fortress East Shortcut Lower"].connect(
|
||||||
connecting_region=regions["Fortress East Shortcut Upper"],
|
connecting_region=regions["Fortress East Shortcut Upper"],
|
||||||
|
@ -794,21 +853,31 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
connecting_region=regions["Eastern Vault Fortress"],
|
connecting_region=regions["Eastern Vault Fortress"],
|
||||||
rule=lambda state: has_ice_grapple_logic(False, IceGrappling.option_easy, state, world))
|
rule=lambda state: has_ice_grapple_logic(False, IceGrappling.option_easy, state, world))
|
||||||
|
|
||||||
regions["Fortress Grave Path"].connect(
|
fort_grave_entry_to_combat = regions["Fortress Grave Path Entry"].connect(
|
||||||
connecting_region=regions["Fortress Grave Path Dusty Entrance Region"],
|
connecting_region=regions["Fortress Grave Path Combat"])
|
||||||
rule=lambda state: state.has(laurels, player))
|
regions["Fortress Grave Path Combat"].connect(
|
||||||
regions["Fortress Grave Path Dusty Entrance Region"].connect(
|
connecting_region=regions["Fortress Grave Path Entry"])
|
||||||
connecting_region=regions["Fortress Grave Path"],
|
|
||||||
rule=lambda state: state.has(laurels, player))
|
|
||||||
|
|
||||||
regions["Fortress Grave Path"].connect(
|
regions["Fortress Grave Path Combat"].connect(
|
||||||
|
connecting_region=regions["Fortress Grave Path by Grave"])
|
||||||
|
|
||||||
|
# run past the enemies
|
||||||
|
regions["Fortress Grave Path by Grave"].connect(
|
||||||
|
connecting_region=regions["Fortress Grave Path Entry"])
|
||||||
|
|
||||||
|
regions["Fortress Grave Path by Grave"].connect(
|
||||||
connecting_region=regions["Fortress Hero's Grave Region"],
|
connecting_region=regions["Fortress Hero's Grave Region"],
|
||||||
rule=lambda state: has_ability(prayer, state, world))
|
rule=lambda state: has_ability(prayer, state, world))
|
||||||
regions["Fortress Hero's Grave Region"].connect(
|
regions["Fortress Hero's Grave Region"].connect(
|
||||||
connecting_region=regions["Fortress Grave Path"])
|
connecting_region=regions["Fortress Grave Path by Grave"])
|
||||||
|
|
||||||
|
regions["Fortress Grave Path by Grave"].connect(
|
||||||
|
connecting_region=regions["Fortress Grave Path Dusty Entrance Region"],
|
||||||
|
rule=lambda state: state.has(laurels, player))
|
||||||
|
# reverse connection is conditionally made later, depending on whether combat logic is on, and the details of ER
|
||||||
|
|
||||||
regions["Fortress Grave Path Upper"].connect(
|
regions["Fortress Grave Path Upper"].connect(
|
||||||
connecting_region=regions["Fortress Grave Path"],
|
connecting_region=regions["Fortress Grave Path Entry"],
|
||||||
rule=lambda state: has_ice_grapple_logic(True, IceGrappling.option_easy, state, world))
|
rule=lambda state: has_ice_grapple_logic(True, IceGrappling.option_easy, state, world))
|
||||||
|
|
||||||
regions["Fortress Arena"].connect(
|
regions["Fortress Arena"].connect(
|
||||||
|
@ -831,19 +900,19 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
regions["Quarry Portal"].connect(
|
regions["Quarry Portal"].connect(
|
||||||
connecting_region=regions["Quarry Entry"])
|
connecting_region=regions["Quarry Entry"])
|
||||||
|
|
||||||
regions["Quarry Entry"].connect(
|
quarry_entry_to_main = regions["Quarry Entry"].connect(
|
||||||
connecting_region=regions["Quarry"],
|
connecting_region=regions["Quarry"],
|
||||||
rule=lambda state: state.has(fire_wand, player) or has_sword(state, player))
|
rule=lambda state: state.has(fire_wand, player) or has_sword(state, player))
|
||||||
regions["Quarry"].connect(
|
regions["Quarry"].connect(
|
||||||
connecting_region=regions["Quarry Entry"])
|
connecting_region=regions["Quarry Entry"])
|
||||||
|
|
||||||
regions["Quarry Back"].connect(
|
quarry_back_to_main = regions["Quarry Back"].connect(
|
||||||
connecting_region=regions["Quarry"],
|
connecting_region=regions["Quarry"],
|
||||||
rule=lambda state: state.has(fire_wand, player) or has_sword(state, player))
|
rule=lambda state: state.has(fire_wand, player) or has_sword(state, player))
|
||||||
regions["Quarry"].connect(
|
regions["Quarry"].connect(
|
||||||
connecting_region=regions["Quarry Back"])
|
connecting_region=regions["Quarry Back"])
|
||||||
|
|
||||||
regions["Quarry Monastery Entry"].connect(
|
monastery_to_quarry_main = regions["Quarry Monastery Entry"].connect(
|
||||||
connecting_region=regions["Quarry"],
|
connecting_region=regions["Quarry"],
|
||||||
rule=lambda state: state.has(fire_wand, player) or has_sword(state, player))
|
rule=lambda state: state.has(fire_wand, player) or has_sword(state, player))
|
||||||
regions["Quarry"].connect(
|
regions["Quarry"].connect(
|
||||||
|
@ -869,18 +938,24 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
rule=lambda state: has_ladder("Ladders in Lower Quarry", state, world)
|
rule=lambda state: has_ladder("Ladders in Lower Quarry", state, world)
|
||||||
or has_ice_grapple_logic(True, IceGrappling.option_easy, state, world))
|
or has_ice_grapple_logic(True, IceGrappling.option_easy, state, world))
|
||||||
|
|
||||||
# nmg: bring a scav over, then ice grapple through the door, only with ER on to avoid soft lock
|
|
||||||
regions["Even Lower Quarry"].connect(
|
regions["Even Lower Quarry"].connect(
|
||||||
|
connecting_region=regions["Even Lower Quarry Isolated Chest"])
|
||||||
|
# you grappled down, might as well loot the rest too
|
||||||
|
lower_quarry_empty_to_combat = regions["Even Lower Quarry Isolated Chest"].connect(
|
||||||
|
connecting_region=regions["Even Lower Quarry"],
|
||||||
|
rule=lambda state: has_mask(state, world))
|
||||||
|
|
||||||
|
regions["Even Lower Quarry Isolated Chest"].connect(
|
||||||
connecting_region=regions["Lower Quarry Zig Door"],
|
connecting_region=regions["Lower Quarry Zig Door"],
|
||||||
rule=lambda state: state.has("Activate Quarry Fuse", player)
|
rule=lambda state: state.has("Activate Quarry Fuse", player)
|
||||||
or has_ice_grapple_logic(False, IceGrappling.option_hard, state, world))
|
or has_ice_grapple_logic(False, IceGrappling.option_hard, state, world))
|
||||||
|
|
||||||
# nmg: use ice grapple to get from the beginning of Quarry to the door without really needing mask only with ER on
|
# don't need the mask for this either, please don't complain about not needing a mask here, you know what you did
|
||||||
regions["Quarry"].connect(
|
regions["Quarry"].connect(
|
||||||
connecting_region=regions["Lower Quarry Zig Door"],
|
connecting_region=regions["Even Lower Quarry Isolated Chest"],
|
||||||
rule=lambda state: has_ice_grapple_logic(True, IceGrappling.option_hard, state, world))
|
rule=lambda state: has_ice_grapple_logic(True, IceGrappling.option_hard, state, world))
|
||||||
|
|
||||||
regions["Monastery Front"].connect(
|
monastery_front_to_back = regions["Monastery Front"].connect(
|
||||||
connecting_region=regions["Monastery Back"])
|
connecting_region=regions["Monastery Back"])
|
||||||
# laurels through the gate, no setup needed
|
# laurels through the gate, no setup needed
|
||||||
regions["Monastery Back"].connect(
|
regions["Monastery Back"].connect(
|
||||||
|
@ -897,7 +972,7 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
regions["Rooted Ziggurat Upper Entry"].connect(
|
regions["Rooted Ziggurat Upper Entry"].connect(
|
||||||
connecting_region=regions["Rooted Ziggurat Upper Front"])
|
connecting_region=regions["Rooted Ziggurat Upper Front"])
|
||||||
|
|
||||||
regions["Rooted Ziggurat Upper Front"].connect(
|
zig_upper_front_back = regions["Rooted Ziggurat Upper Front"].connect(
|
||||||
connecting_region=regions["Rooted Ziggurat Upper Back"],
|
connecting_region=regions["Rooted Ziggurat Upper Back"],
|
||||||
rule=lambda state: state.has(laurels, player) or has_sword(state, player))
|
rule=lambda state: state.has(laurels, player) or has_sword(state, player))
|
||||||
regions["Rooted Ziggurat Upper Back"].connect(
|
regions["Rooted Ziggurat Upper Back"].connect(
|
||||||
|
@ -907,13 +982,23 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
regions["Rooted Ziggurat Middle Top"].connect(
|
regions["Rooted Ziggurat Middle Top"].connect(
|
||||||
connecting_region=regions["Rooted Ziggurat Middle Bottom"])
|
connecting_region=regions["Rooted Ziggurat Middle Bottom"])
|
||||||
|
|
||||||
|
zig_low_entry_to_front = regions["Rooted Ziggurat Lower Entry"].connect(
|
||||||
|
connecting_region=regions["Rooted Ziggurat Lower Front"])
|
||||||
regions["Rooted Ziggurat Lower Front"].connect(
|
regions["Rooted Ziggurat Lower Front"].connect(
|
||||||
|
connecting_region=regions["Rooted Ziggurat Lower Entry"])
|
||||||
|
|
||||||
|
regions["Rooted Ziggurat Lower Front"].connect(
|
||||||
|
connecting_region=regions["Rooted Ziggurat Lower Mid Checkpoint"])
|
||||||
|
zig_low_mid_to_front = regions["Rooted Ziggurat Lower Mid Checkpoint"].connect(
|
||||||
|
connecting_region=regions["Rooted Ziggurat Lower Front"])
|
||||||
|
|
||||||
|
zig_low_mid_to_back = regions["Rooted Ziggurat Lower Mid Checkpoint"].connect(
|
||||||
connecting_region=regions["Rooted Ziggurat Lower Back"],
|
connecting_region=regions["Rooted Ziggurat Lower Back"],
|
||||||
rule=lambda state: state.has(laurels, player)
|
rule=lambda state: state.has(laurels, player)
|
||||||
or (has_sword(state, player) and has_ability(prayer, state, world)))
|
or (has_sword(state, player) and has_ability(prayer, state, world)))
|
||||||
# nmg: can ice grapple on the voidlings to the double admin fight, still need to pray at the fuse
|
# can ice grapple to the voidlings to get to the double admin fight, still need to pray at the fuse
|
||||||
regions["Rooted Ziggurat Lower Back"].connect(
|
zig_low_back_to_mid = regions["Rooted Ziggurat Lower Back"].connect(
|
||||||
connecting_region=regions["Rooted Ziggurat Lower Front"],
|
connecting_region=regions["Rooted Ziggurat Lower Mid Checkpoint"],
|
||||||
rule=lambda state: (state.has(laurels, player)
|
rule=lambda state: (state.has(laurels, player)
|
||||||
or has_ice_grapple_logic(True, IceGrappling.option_easy, state, world))
|
or has_ice_grapple_logic(True, IceGrappling.option_easy, state, world))
|
||||||
and has_ability(prayer, state, world)
|
and has_ability(prayer, state, world)
|
||||||
|
@ -925,8 +1010,10 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
regions["Rooted Ziggurat Portal Room Entrance"].connect(
|
regions["Rooted Ziggurat Portal Room Entrance"].connect(
|
||||||
connecting_region=regions["Rooted Ziggurat Lower Back"])
|
connecting_region=regions["Rooted Ziggurat Lower Back"])
|
||||||
|
|
||||||
regions["Zig Skip Exit"].connect(
|
# zig skip region only gets made if entrance rando and fewer shops are on
|
||||||
connecting_region=regions["Rooted Ziggurat Lower Front"])
|
if options.entrance_rando and options.fixed_shop:
|
||||||
|
regions["Zig Skip Exit"].connect(
|
||||||
|
connecting_region=regions["Rooted Ziggurat Lower Front"])
|
||||||
|
|
||||||
regions["Rooted Ziggurat Portal"].connect(
|
regions["Rooted Ziggurat Portal"].connect(
|
||||||
connecting_region=regions["Rooted Ziggurat Portal Room"])
|
connecting_region=regions["Rooted Ziggurat Portal Room"])
|
||||||
|
@ -952,7 +1039,6 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
or state.has(laurels, player)
|
or state.has(laurels, player)
|
||||||
or has_ice_grapple_logic(False, IceGrappling.option_hard, state, world))
|
or has_ice_grapple_logic(False, IceGrappling.option_hard, state, world))
|
||||||
|
|
||||||
# a whole lot of stuff to basically say "you need to pray at the overworld fuse"
|
|
||||||
swamp_mid_to_cath = regions["Swamp Mid"].connect(
|
swamp_mid_to_cath = regions["Swamp Mid"].connect(
|
||||||
connecting_region=regions["Swamp to Cathedral Main Entrance Region"],
|
connecting_region=regions["Swamp to Cathedral Main Entrance Region"],
|
||||||
rule=lambda state: (has_ability(prayer, state, world)
|
rule=lambda state: (has_ability(prayer, state, world)
|
||||||
|
@ -965,7 +1051,9 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
"Ladder to Swamp",
|
"Ladder to Swamp",
|
||||||
"Ladders near Weathervane"}, player)
|
"Ladders near Weathervane"}, player)
|
||||||
or (state.has("Ladder to Ruined Atoll", player)
|
or (state.has("Ladder to Ruined Atoll", player)
|
||||||
and state.can_reach_region("Overworld Beach", player))))))
|
and state.can_reach_region("Overworld Beach", player)))))
|
||||||
|
and (not options.combat_logic
|
||||||
|
or has_combat_reqs("Swamp", state, player)))
|
||||||
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world))
|
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world))
|
||||||
|
|
||||||
if options.ladder_storage >= LadderStorage.option_hard and options.shuffle_ladders:
|
if options.ladder_storage >= LadderStorage.option_hard and options.shuffle_ladders:
|
||||||
|
@ -1017,13 +1105,23 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
regions["Swamp Hero's Grave Region"].connect(
|
regions["Swamp Hero's Grave Region"].connect(
|
||||||
connecting_region=regions["Back of Swamp"])
|
connecting_region=regions["Back of Swamp"])
|
||||||
|
|
||||||
regions["Cathedral"].connect(
|
cath_entry_to_elev = regions["Cathedral Entry"].connect(
|
||||||
connecting_region=regions["Cathedral to Gauntlet"],
|
connecting_region=regions["Cathedral to Gauntlet"],
|
||||||
rule=lambda state: (has_ability(prayer, state, world)
|
rule=lambda state: (has_ability(prayer, state, world)
|
||||||
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world))
|
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world))
|
||||||
or options.entrance_rando) # elevator is always there in ER
|
or options.entrance_rando) # elevator is always there in ER
|
||||||
regions["Cathedral to Gauntlet"].connect(
|
regions["Cathedral to Gauntlet"].connect(
|
||||||
connecting_region=regions["Cathedral"])
|
connecting_region=regions["Cathedral Entry"])
|
||||||
|
|
||||||
|
cath_entry_to_main = regions["Cathedral Entry"].connect(
|
||||||
|
connecting_region=regions["Cathedral Main"])
|
||||||
|
regions["Cathedral Main"].connect(
|
||||||
|
connecting_region=regions["Cathedral Entry"])
|
||||||
|
|
||||||
|
cath_elev_to_main = regions["Cathedral to Gauntlet"].connect(
|
||||||
|
connecting_region=regions["Cathedral Main"])
|
||||||
|
regions["Cathedral Main"].connect(
|
||||||
|
connecting_region=regions["Cathedral to Gauntlet"])
|
||||||
|
|
||||||
regions["Cathedral Gauntlet Checkpoint"].connect(
|
regions["Cathedral Gauntlet Checkpoint"].connect(
|
||||||
connecting_region=regions["Cathedral Gauntlet"])
|
connecting_region=regions["Cathedral Gauntlet"])
|
||||||
|
@ -1075,7 +1173,7 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
connecting_region=regions["Far Shore"])
|
connecting_region=regions["Far Shore"])
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
regions["Spirit Arena"].connect(
|
heir_fight = regions["Spirit Arena"].connect(
|
||||||
connecting_region=regions["Spirit Arena Victory"],
|
connecting_region=regions["Spirit Arena Victory"],
|
||||||
rule=lambda state: (state.has(gold_hexagon, player, world.options.hexagon_goal.value) if
|
rule=lambda state: (state.has(gold_hexagon, player, world.options.hexagon_goal.value) if
|
||||||
world.options.hexagon_quest else
|
world.options.hexagon_quest else
|
||||||
|
@ -1219,6 +1317,192 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
|
||||||
for region in ladder_regions.values():
|
for region in ladder_regions.values():
|
||||||
world.multiworld.regions.append(region)
|
world.multiworld.regions.append(region)
|
||||||
|
|
||||||
|
# for combat logic, easiest to replace or add to existing rules
|
||||||
|
if world.options.combat_logic >= CombatLogic.option_bosses_only:
|
||||||
|
set_rule(wg_to_after_gk,
|
||||||
|
lambda state: state.has(laurels, player)
|
||||||
|
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)
|
||||||
|
or has_combat_reqs("Garden Knight", state, player))
|
||||||
|
# laurels past, or ice grapple it off, or ice grapple to it and fight
|
||||||
|
set_rule(after_gk_to_wg,
|
||||||
|
lambda state: state.has(laurels, player)
|
||||||
|
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)
|
||||||
|
or (has_ice_grapple_logic(False, IceGrappling.option_easy, state, world)
|
||||||
|
and has_combat_reqs("Garden Knight", state, player)))
|
||||||
|
|
||||||
|
if not world.options.hexagon_quest:
|
||||||
|
add_rule(heir_fight,
|
||||||
|
lambda state: has_combat_reqs("The Heir", state, player))
|
||||||
|
|
||||||
|
if world.options.combat_logic == CombatLogic.option_on:
|
||||||
|
# these are redundant with combat logic off
|
||||||
|
regions["Fortress Grave Path Entry"].connect(
|
||||||
|
connecting_region=regions["Fortress Grave Path Dusty Entrance Region"],
|
||||||
|
rule=lambda state: state.has(laurels, player))
|
||||||
|
|
||||||
|
regions["Rooted Ziggurat Lower Entry"].connect(
|
||||||
|
connecting_region=regions["Rooted Ziggurat Lower Mid Checkpoint"],
|
||||||
|
rule=lambda state: state.has(laurels, player))
|
||||||
|
regions["Rooted Ziggurat Lower Mid Checkpoint"].connect(
|
||||||
|
connecting_region=regions["Rooted Ziggurat Lower Entry"],
|
||||||
|
rule=lambda state: state.has(laurels, player))
|
||||||
|
|
||||||
|
add_rule(ow_to_town_portal,
|
||||||
|
lambda state: has_combat_reqs("Before Well", state, player))
|
||||||
|
# need to fight through the rudelings and turret, or just laurels from near the windmill
|
||||||
|
set_rule(ow_to_well_entry,
|
||||||
|
lambda state: state.has(laurels, player)
|
||||||
|
or has_combat_reqs("East Forest", state, player))
|
||||||
|
set_rule(ow_tunnel_beach,
|
||||||
|
lambda state: has_combat_reqs("East Forest", state, player))
|
||||||
|
|
||||||
|
add_rule(atoll_statue,
|
||||||
|
lambda state: has_combat_reqs("Ruined Atoll", state, player))
|
||||||
|
set_rule(frogs_front_to_main,
|
||||||
|
lambda state: has_combat_reqs("Frog's Domain", state, player))
|
||||||
|
|
||||||
|
set_rule(btw_front_main,
|
||||||
|
lambda state: state.has(laurels, player) or has_combat_reqs("Beneath the Well", state, player))
|
||||||
|
set_rule(btw_back_main,
|
||||||
|
lambda state: has_ladder("Ladders in Well", state, world)
|
||||||
|
and (state.has(laurels, player) or has_combat_reqs("Beneath the Well", state, player)))
|
||||||
|
set_rule(well_boss_to_dt,
|
||||||
|
lambda state: has_combat_reqs("Beneath the Well", state, player)
|
||||||
|
or laurels_zip(state, world))
|
||||||
|
|
||||||
|
add_rule(dt_entry_to_upper,
|
||||||
|
lambda state: has_combat_reqs("Dark Tomb", state, player))
|
||||||
|
add_rule(dt_exit_to_main,
|
||||||
|
lambda state: has_combat_reqs("Dark Tomb", state, player))
|
||||||
|
|
||||||
|
set_rule(wg_before_to_after_terry,
|
||||||
|
lambda state: state.has_any({laurels, ice_dagger}, player)
|
||||||
|
or has_combat_reqs("West Garden", state, player))
|
||||||
|
set_rule(wg_after_to_before_terry,
|
||||||
|
lambda state: state.has_any({laurels, ice_dagger}, player)
|
||||||
|
or has_combat_reqs("West Garden", state, player))
|
||||||
|
# laurels through, probably to the checkpoint, or just fight
|
||||||
|
set_rule(wg_checkpoint_to_after_terry,
|
||||||
|
lambda state: state.has(laurels, player) or has_combat_reqs("West Garden", state, player))
|
||||||
|
set_rule(wg_checkpoint_to_before_boss,
|
||||||
|
lambda state: has_combat_reqs("West Garden", state, player))
|
||||||
|
|
||||||
|
add_rule(btv_front_to_main,
|
||||||
|
lambda state: has_combat_reqs("Beneath the Vault", state, player))
|
||||||
|
add_rule(btv_back_to_main,
|
||||||
|
lambda state: has_combat_reqs("Beneath the Vault", state, player))
|
||||||
|
|
||||||
|
add_rule(fort_upper_lower,
|
||||||
|
lambda state: state.has(ice_dagger, player)
|
||||||
|
or has_combat_reqs("Eastern Vault Fortress", state, player))
|
||||||
|
set_rule(fort_grave_entry_to_combat,
|
||||||
|
lambda state: has_combat_reqs("Eastern Vault Fortress", state, player))
|
||||||
|
|
||||||
|
set_rule(quarry_entry_to_main,
|
||||||
|
lambda state: has_combat_reqs("Quarry", state, player))
|
||||||
|
set_rule(quarry_back_to_main,
|
||||||
|
lambda state: has_combat_reqs("Quarry", state, player))
|
||||||
|
set_rule(monastery_to_quarry_main,
|
||||||
|
lambda state: has_combat_reqs("Quarry", state, player))
|
||||||
|
set_rule(monastery_front_to_back,
|
||||||
|
lambda state: has_combat_reqs("Quarry", state, player))
|
||||||
|
set_rule(lower_quarry_empty_to_combat,
|
||||||
|
lambda state: has_combat_reqs("Quarry", state, player))
|
||||||
|
|
||||||
|
set_rule(zig_upper_front_back,
|
||||||
|
lambda state: state.has(laurels, player)
|
||||||
|
or has_combat_reqs("Rooted Ziggurat", state, player))
|
||||||
|
set_rule(zig_low_entry_to_front,
|
||||||
|
lambda state: has_combat_reqs("Rooted Ziggurat", state, player))
|
||||||
|
set_rule(zig_low_mid_to_front,
|
||||||
|
lambda state: has_combat_reqs("Rooted Ziggurat", state, player))
|
||||||
|
set_rule(zig_low_mid_to_back,
|
||||||
|
lambda state: state.has(laurels, player)
|
||||||
|
or (has_ability(prayer, state, world) and has_combat_reqs("Rooted Ziggurat", state, player)))
|
||||||
|
set_rule(zig_low_back_to_mid,
|
||||||
|
lambda state: (state.has(laurels, player)
|
||||||
|
or has_ice_grapple_logic(True, IceGrappling.option_easy, state, world))
|
||||||
|
and has_ability(prayer, state, world)
|
||||||
|
and has_combat_reqs("Rooted Ziggurat", state, player))
|
||||||
|
|
||||||
|
# only activating the fuse requires combat logic
|
||||||
|
set_rule(cath_entry_to_elev,
|
||||||
|
lambda state: options.entrance_rando
|
||||||
|
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)
|
||||||
|
or (has_ability(prayer, state, world) and has_combat_reqs("Cathedral", state, player)))
|
||||||
|
|
||||||
|
set_rule(cath_entry_to_main,
|
||||||
|
lambda state: has_combat_reqs("Cathedral", state, player))
|
||||||
|
set_rule(cath_elev_to_main,
|
||||||
|
lambda state: has_combat_reqs("Cathedral", state, player))
|
||||||
|
|
||||||
|
# for spots where you can go into and come out of an entrance to reset enemy aggro
|
||||||
|
if world.options.entrance_rando:
|
||||||
|
# for the chest outside of magic dagger house
|
||||||
|
dagger_entry_paired_name, dagger_entry_paired_region = (
|
||||||
|
get_paired_portal("Archipelagos Redux, archipelagos_house_"))
|
||||||
|
try:
|
||||||
|
dagger_entry_paired_entrance = world.get_entrance(dagger_entry_paired_name)
|
||||||
|
except KeyError:
|
||||||
|
# there is no paired entrance, so you must fight or dash past, which is done in the finally
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
set_rule(wg_checkpoint_to_dagger,
|
||||||
|
lambda state: dagger_entry_paired_entrance.can_reach(state))
|
||||||
|
world.multiworld.register_indirect_condition(region=regions["West Garden at Dagger House"],
|
||||||
|
entrance=dagger_entry_paired_entrance)
|
||||||
|
finally:
|
||||||
|
add_rule(wg_checkpoint_to_dagger,
|
||||||
|
lambda state: state.has(laurels, player) or has_combat_reqs("West Garden", state, player),
|
||||||
|
combine="or")
|
||||||
|
|
||||||
|
# zip past enemies in fortress grave path to enter the dusty entrance, then come back out
|
||||||
|
fort_dusty_paired_name, fort_dusty_paired_region = get_paired_portal("Fortress Reliquary, Dusty_")
|
||||||
|
try:
|
||||||
|
fort_dusty_paired_entrance = world.get_entrance(fort_dusty_paired_name)
|
||||||
|
except KeyError:
|
||||||
|
# there is no paired entrance, so you can't run past to deaggro
|
||||||
|
# the path to dusty can be done via combat, so no need to do anything here
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# there is a paired entrance, so you can use that to deaggro enemies
|
||||||
|
regions["Fortress Grave Path Dusty Entrance Region"].connect(
|
||||||
|
connecting_region=regions["Fortress Grave Path by Grave"],
|
||||||
|
rule=lambda state: state.has(laurels, player) and fort_dusty_paired_entrance.can_reach(state))
|
||||||
|
world.multiworld.register_indirect_condition(region=regions["Fortress Grave Path by Grave"],
|
||||||
|
entrance=fort_dusty_paired_entrance)
|
||||||
|
|
||||||
|
# for activating the ladder switch to get from fortress east upper to lower
|
||||||
|
fort_east_upper_right_paired_name, fort_east_upper_right_paired_region = (
|
||||||
|
get_paired_portal("Fortress East, Fortress Courtyard_"))
|
||||||
|
try:
|
||||||
|
fort_east_upper_right_paired_entrance = (
|
||||||
|
world.get_entrance(fort_east_upper_right_paired_name))
|
||||||
|
except KeyError:
|
||||||
|
# no paired entrance, so you must fight, which is done in the finally
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
set_rule(fort_east_upper_lower,
|
||||||
|
lambda state: fort_east_upper_right_paired_entrance.can_reach(state))
|
||||||
|
world.multiworld.register_indirect_condition(region=regions["Fortress East Shortcut Lower"],
|
||||||
|
entrance=fort_east_upper_right_paired_entrance)
|
||||||
|
finally:
|
||||||
|
add_rule(fort_east_upper_lower,
|
||||||
|
lambda state: has_combat_reqs("Eastern Vault Fortress", state, player)
|
||||||
|
or has_ice_grapple_logic(True, IceGrappling.option_easy, state, world),
|
||||||
|
combine="or")
|
||||||
|
|
||||||
|
else:
|
||||||
|
# if combat logic is on and ER is off, we can make this entrance freely
|
||||||
|
regions["Fortress Grave Path Dusty Entrance Region"].connect(
|
||||||
|
connecting_region=regions["Fortress Grave Path by Grave"],
|
||||||
|
rule=lambda state: state.has(laurels, player))
|
||||||
|
else:
|
||||||
|
# if combat logic is off, we can make this entrance freely
|
||||||
|
regions["Fortress Grave Path Dusty Entrance Region"].connect(
|
||||||
|
connecting_region=regions["Fortress Grave Path by Grave"],
|
||||||
|
rule=lambda state: state.has(laurels, player))
|
||||||
|
|
||||||
|
|
||||||
def set_er_location_rules(world: "TunicWorld") -> None:
|
def set_er_location_rules(world: "TunicWorld") -> None:
|
||||||
player = world.player
|
player = world.player
|
||||||
|
@ -1315,6 +1599,11 @@ def set_er_location_rules(world: "TunicWorld") -> None:
|
||||||
set_rule(world.get_location("East Forest - Ice Rod Grapple Chest"), lambda state: (
|
set_rule(world.get_location("East Forest - Ice Rod Grapple Chest"), lambda state: (
|
||||||
state.has_all({grapple, ice_dagger, fire_wand}, player) and has_ability(icebolt, state, world)))
|
state.has_all({grapple, ice_dagger, fire_wand}, player) and has_ability(icebolt, state, world)))
|
||||||
|
|
||||||
|
# Dark Tomb
|
||||||
|
# added to make combat logic smoother
|
||||||
|
set_rule(world.get_location("Dark Tomb - 2nd Laser Room"),
|
||||||
|
lambda state: has_lantern(state, world))
|
||||||
|
|
||||||
# West Garden
|
# West Garden
|
||||||
set_rule(world.get_location("West Garden - [North] Across From Page Pickup"),
|
set_rule(world.get_location("West Garden - [North] Across From Page Pickup"),
|
||||||
lambda state: state.has(laurels, player))
|
lambda state: state.has(laurels, player))
|
||||||
|
@ -1348,11 +1637,11 @@ def set_er_location_rules(world: "TunicWorld") -> None:
|
||||||
|
|
||||||
# Library Lab
|
# Library Lab
|
||||||
set_rule(world.get_location("Library Lab - Page 1"),
|
set_rule(world.get_location("Library Lab - Page 1"),
|
||||||
lambda state: has_stick(state, player) or state.has_any((fire_wand, gun), player))
|
lambda state: has_melee(state, player) or state.has_any((fire_wand, gun), player))
|
||||||
set_rule(world.get_location("Library Lab - Page 2"),
|
set_rule(world.get_location("Library Lab - Page 2"),
|
||||||
lambda state: has_stick(state, player) or state.has_any((fire_wand, gun), player))
|
lambda state: has_melee(state, player) or state.has_any((fire_wand, gun), player))
|
||||||
set_rule(world.get_location("Library Lab - Page 3"),
|
set_rule(world.get_location("Library Lab - Page 3"),
|
||||||
lambda state: has_stick(state, player) or state.has_any((fire_wand, gun), player))
|
lambda state: has_melee(state, player) or state.has_any((fire_wand, gun), player))
|
||||||
|
|
||||||
# Eastern Vault Fortress
|
# Eastern Vault Fortress
|
||||||
set_rule(world.get_location("Fortress Arena - Hexagon Red"),
|
set_rule(world.get_location("Fortress Arena - Hexagon Red"),
|
||||||
|
@ -1361,11 +1650,11 @@ def set_er_location_rules(world: "TunicWorld") -> None:
|
||||||
# gun isn't included since it can only break one leaf pile at a time, and we don't check how much mana you have
|
# gun isn't included since it can only break one leaf pile at a time, and we don't check how much mana you have
|
||||||
# but really, I expect the player to just throw a bomb at them if they don't have melee
|
# but really, I expect the player to just throw a bomb at them if they don't have melee
|
||||||
set_rule(world.get_location("Fortress Leaf Piles - Secret Chest"),
|
set_rule(world.get_location("Fortress Leaf Piles - Secret Chest"),
|
||||||
lambda state: has_stick(state, player) or state.has(ice_dagger, player))
|
lambda state: has_melee(state, player) or state.has(ice_dagger, player))
|
||||||
|
|
||||||
# Beneath the Vault
|
# Beneath the Vault
|
||||||
set_rule(world.get_location("Beneath the Fortress - Bridge"),
|
set_rule(world.get_location("Beneath the Fortress - Bridge"),
|
||||||
lambda state: has_stick(state, player) or state.has_any({laurels, fire_wand}, player))
|
lambda state: has_melee(state, player) or state.has_any({laurels, fire_wand}, player))
|
||||||
|
|
||||||
# Quarry
|
# Quarry
|
||||||
set_rule(world.get_location("Quarry - [Central] Above Ladder Dash Chest"),
|
set_rule(world.get_location("Quarry - [Central] Above Ladder Dash Chest"),
|
||||||
|
@ -1421,9 +1710,9 @@ def set_er_location_rules(world: "TunicWorld") -> None:
|
||||||
|
|
||||||
# Events
|
# Events
|
||||||
set_rule(world.get_location("Eastern Bell"),
|
set_rule(world.get_location("Eastern Bell"),
|
||||||
lambda state: (has_stick(state, player) or state.has(fire_wand, player)))
|
lambda state: (has_melee(state, player) or state.has(fire_wand, player)))
|
||||||
set_rule(world.get_location("Western Bell"),
|
set_rule(world.get_location("Western Bell"),
|
||||||
lambda state: (has_stick(state, player) or state.has(fire_wand, player)))
|
lambda state: (has_melee(state, player) or state.has(fire_wand, player)))
|
||||||
set_rule(world.get_location("Furnace Fuse"),
|
set_rule(world.get_location("Furnace Fuse"),
|
||||||
lambda state: has_ability(prayer, state, world))
|
lambda state: has_ability(prayer, state, world))
|
||||||
set_rule(world.get_location("South and West Fortress Exterior Fuses"),
|
set_rule(world.get_location("South and West Fortress Exterior Fuses"),
|
||||||
|
@ -1470,3 +1759,129 @@ def set_er_location_rules(world: "TunicWorld") -> None:
|
||||||
lambda state: has_sword(state, player))
|
lambda state: has_sword(state, player))
|
||||||
set_rule(world.get_location("Shop - Coin 2"),
|
set_rule(world.get_location("Shop - Coin 2"),
|
||||||
lambda state: has_sword(state, player))
|
lambda state: has_sword(state, player))
|
||||||
|
|
||||||
|
def combat_logic_to_loc(loc_name: str, combat_req_area: str, set_instead: bool = False,
|
||||||
|
dagger: bool = False, laurel: bool = False) -> None:
|
||||||
|
# dagger means you can use magic dagger instead of combat for that check
|
||||||
|
# laurel means you can dodge the enemies freely with the laurels
|
||||||
|
if set_instead:
|
||||||
|
set_rule(world.get_location(loc_name),
|
||||||
|
lambda state: has_combat_reqs(combat_req_area, state, player)
|
||||||
|
or (dagger and state.has(ice_dagger, player))
|
||||||
|
or (laurel and state.has(laurels, player)))
|
||||||
|
else:
|
||||||
|
add_rule(world.get_location(loc_name),
|
||||||
|
lambda state: has_combat_reqs(combat_req_area, state, player)
|
||||||
|
or (dagger and state.has(ice_dagger, player))
|
||||||
|
or (laurel and state.has(laurels, player)))
|
||||||
|
|
||||||
|
if world.options.combat_logic >= CombatLogic.option_bosses_only:
|
||||||
|
# garden knight is in the regions part above
|
||||||
|
combat_logic_to_loc("Fortress Arena - Siege Engine/Vault Key Pickup", "Siege Engine", set_instead=True)
|
||||||
|
combat_logic_to_loc("Librarian - Hexagon Green", "The Librarian", set_instead=True)
|
||||||
|
set_rule(world.get_location("Librarian - Hexagon Green"),
|
||||||
|
rule=lambda state: has_combat_reqs("The Librarian", state, player)
|
||||||
|
and has_ladder("Ladders in Library", state, world))
|
||||||
|
combat_logic_to_loc("Rooted Ziggurat Lower - Hexagon Blue", "Boss Scavenger", set_instead=True)
|
||||||
|
if world.options.ice_grappling >= IceGrappling.option_medium:
|
||||||
|
add_rule(world.get_location("Rooted Ziggurat Lower - Hexagon Blue"),
|
||||||
|
lambda state: has_ice_grapple_logic(False, IceGrappling.option_medium, state, world))
|
||||||
|
combat_logic_to_loc("Cathedral Gauntlet - Gauntlet Reward", "Gauntlet", set_instead=True)
|
||||||
|
|
||||||
|
if world.options.combat_logic == CombatLogic.option_on:
|
||||||
|
combat_logic_to_loc("Overworld - [Northeast] Flowers Holy Cross", "Garden Knight")
|
||||||
|
combat_logic_to_loc("Overworld - [Northwest] Chest Near Quarry Gate", "Before Well", dagger=True)
|
||||||
|
combat_logic_to_loc("Overworld - [Northeast] Chest Above Patrol Cave", "Garden Knight", dagger=True)
|
||||||
|
combat_logic_to_loc("Overworld - [Southwest] West Beach Guarded By Turret", "Overworld", dagger=True)
|
||||||
|
combat_logic_to_loc("Overworld - [Southwest] West Beach Guarded By Turret 2", "Overworld")
|
||||||
|
combat_logic_to_loc("Overworld - [Southwest] Bombable Wall Near Fountain", "East Forest", dagger=True)
|
||||||
|
combat_logic_to_loc("Overworld - [Southwest] Fountain Holy Cross", "East Forest", dagger=True)
|
||||||
|
combat_logic_to_loc("Overworld - [Southwest] South Chest Near Guard", "East Forest", dagger=True)
|
||||||
|
combat_logic_to_loc("Overworld - [Southwest] Tunnel Guarded By Turret", "East Forest", dagger=True)
|
||||||
|
combat_logic_to_loc("Overworld - [Northwest] Chest Near Turret", "Before Well")
|
||||||
|
|
||||||
|
add_rule(world.get_location("Hourglass Cave - Hourglass Chest"),
|
||||||
|
lambda state: has_sword(state, player) and (state.has("Shield", player)
|
||||||
|
# kill the turrets through the wall with a longer sword
|
||||||
|
or state.has("Sword Upgrade", player, 3)))
|
||||||
|
add_rule(world.get_location("Hourglass Cave - Holy Cross Chest"),
|
||||||
|
lambda state: has_sword(state, player) and (state.has("Shield", player)
|
||||||
|
or state.has("Sword Upgrade", player, 3)))
|
||||||
|
|
||||||
|
# the first spider chest they literally do not attack you until you open the chest
|
||||||
|
# the second one, you can still just walk past them, but I guess /something/ would be wanted
|
||||||
|
combat_logic_to_loc("East Forest - Beneath Spider Chest", "East Forest", dagger=True, laurel=True)
|
||||||
|
combat_logic_to_loc("East Forest - Golden Obelisk Holy Cross", "East Forest", dagger=True)
|
||||||
|
combat_logic_to_loc("East Forest - Dancing Fox Spirit Holy Cross", "East Forest", dagger=True, laurel=True)
|
||||||
|
combat_logic_to_loc("East Forest - From Guardhouse 1 Chest", "East Forest", dagger=True, laurel=True)
|
||||||
|
combat_logic_to_loc("East Forest - Above Save Point", "East Forest", dagger=True)
|
||||||
|
combat_logic_to_loc("East Forest - Above Save Point Obscured", "East Forest", dagger=True)
|
||||||
|
combat_logic_to_loc("Forest Grave Path - Above Gate", "East Forest", dagger=True, laurel=True)
|
||||||
|
combat_logic_to_loc("Forest Grave Path - Obscured Chest", "East Forest", dagger=True, laurel=True)
|
||||||
|
|
||||||
|
# most of beneath the well is covered by the region access rule
|
||||||
|
combat_logic_to_loc("Beneath the Well - [Entryway] Chest", "Beneath the Well")
|
||||||
|
combat_logic_to_loc("Beneath the Well - [Entryway] Obscured Behind Waterfall", "Beneath the Well")
|
||||||
|
combat_logic_to_loc("Beneath the Well - [Back Corridor] Left Secret", "Beneath the Well")
|
||||||
|
combat_logic_to_loc("Beneath the Well - [Side Room] Chest By Phrends", "Overworld")
|
||||||
|
|
||||||
|
# laurels past the enemies, then use the wand or gun to take care of the fairies that chased you
|
||||||
|
add_rule(world.get_location("West Garden - [West Lowlands] Tree Holy Cross Chest"),
|
||||||
|
lambda state: state.has_any({fire_wand, "Gun"}, player))
|
||||||
|
combat_logic_to_loc("West Garden - [Central Lowlands] Chest Beneath Faeries", "West Garden")
|
||||||
|
combat_logic_to_loc("West Garden - [Central Lowlands] Chest Beneath Save Point", "West Garden")
|
||||||
|
combat_logic_to_loc("West Garden - [West Highlands] Upper Left Walkway", "West Garden")
|
||||||
|
|
||||||
|
# with combat logic on, I presume the player will want to be able to see to avoid the spiders
|
||||||
|
set_rule(world.get_location("Beneath the Fortress - Bridge"),
|
||||||
|
lambda state: has_lantern(state, world)
|
||||||
|
and (state.has_any({laurels, fire_wand, "Gun"}, player) or has_melee(state, player)))
|
||||||
|
|
||||||
|
combat_logic_to_loc("Eastern Vault Fortress - [West Wing] Candles Holy Cross", "Eastern Vault Fortress",
|
||||||
|
dagger=True)
|
||||||
|
|
||||||
|
# could just do the last two, but this outputs better in the spoiler log
|
||||||
|
# dagger is maybe viable here, but it's sketchy -- activate ladder switch, save to reset enemies, climb up
|
||||||
|
combat_logic_to_loc("Upper and Central Fortress Exterior Fuses", "Eastern Vault Fortress")
|
||||||
|
combat_logic_to_loc("Beneath the Vault Fuse", "Beneath the Vault")
|
||||||
|
combat_logic_to_loc("Eastern Vault West Fuses", "Eastern Vault Fortress")
|
||||||
|
|
||||||
|
# if you come in from the left, you only need to fight small crabs
|
||||||
|
add_rule(world.get_location("Ruined Atoll - [South] Near Birds"),
|
||||||
|
lambda state: has_melee(state, player) or state.has_any({laurels, "Gun"}, player))
|
||||||
|
|
||||||
|
# can get this one without fighting if you have laurels
|
||||||
|
add_rule(world.get_location("Frog's Domain - Above Vault"),
|
||||||
|
lambda state: state.has(laurels, player) or has_combat_reqs("Frog's Domain", state, player))
|
||||||
|
|
||||||
|
# with wand, you can get this chest. Non-ER, you need laurels to continue down. ER, you can just torch
|
||||||
|
set_rule(world.get_location("Rooted Ziggurat Upper - Near Bridge Switch"),
|
||||||
|
lambda state: (state.has(fire_wand, player)
|
||||||
|
and (state.has(laurels, player) or world.options.entrance_rando))
|
||||||
|
or has_combat_reqs("Rooted Ziggurat", state, player))
|
||||||
|
set_rule(world.get_location("Rooted Ziggurat Lower - After Guarded Fuse"),
|
||||||
|
lambda state: has_ability(prayer, state, world)
|
||||||
|
and has_combat_reqs("Rooted Ziggurat", state, player))
|
||||||
|
|
||||||
|
# replace the sword rule with this one
|
||||||
|
combat_logic_to_loc("Swamp - [South Graveyard] 4 Orange Skulls", "Swamp", set_instead=True)
|
||||||
|
combat_logic_to_loc("Swamp - [South Graveyard] Guarded By Big Skeleton", "Swamp", dagger=True)
|
||||||
|
# don't really agree with this one but eh
|
||||||
|
combat_logic_to_loc("Swamp - [South Graveyard] Above Big Skeleton", "Swamp", dagger=True, laurel=True)
|
||||||
|
# the tentacles deal with everything else reasonably, and you can hide on the island, so no rule for it
|
||||||
|
add_rule(world.get_location("Swamp - [South Graveyard] Obscured Beneath Telescope"),
|
||||||
|
lambda state: state.has(laurels, player) # can dash from swamp mid to here and grab it
|
||||||
|
or has_combat_reqs("Swamp", state, player))
|
||||||
|
add_rule(world.get_location("Swamp - [Central] South Secret Passage"),
|
||||||
|
lambda state: state.has(laurels, player) # can dash from swamp front to here and grab it
|
||||||
|
or has_combat_reqs("Swamp", state, player))
|
||||||
|
combat_logic_to_loc("Swamp - [South Graveyard] Upper Walkway On Pedestal", "Swamp")
|
||||||
|
combat_logic_to_loc("Swamp - [Central] Beneath Memorial", "Swamp")
|
||||||
|
combat_logic_to_loc("Swamp - [Central] Near Ramps Up", "Swamp")
|
||||||
|
combat_logic_to_loc("Swamp - [Upper Graveyard] Near Telescope", "Swamp")
|
||||||
|
combat_logic_to_loc("Swamp - [Upper Graveyard] Near Shield Fleemers", "Swamp")
|
||||||
|
combat_logic_to_loc("Swamp - [Upper Graveyard] Obscured Behind Hill", "Swamp")
|
||||||
|
|
||||||
|
# zip through the rubble to sneakily grab this chest, or just fight to it
|
||||||
|
add_rule(world.get_location("Cathedral - [1F] Near Spikes"),
|
||||||
|
lambda state: laurels_zip(state, world) or has_combat_reqs("Cathedral", state, player))
|
||||||
|
|
|
@ -22,10 +22,19 @@ class TunicERLocation(Location):
|
||||||
|
|
||||||
def create_er_regions(world: "TunicWorld") -> Dict[Portal, Portal]:
|
def create_er_regions(world: "TunicWorld") -> Dict[Portal, Portal]:
|
||||||
regions: Dict[str, Region] = {}
|
regions: Dict[str, Region] = {}
|
||||||
for region_name, region_data in world.er_regions.items():
|
|
||||||
regions[region_name] = Region(region_name, world.player, world.multiworld)
|
|
||||||
|
|
||||||
if world.options.entrance_rando:
|
if world.options.entrance_rando:
|
||||||
|
for region_name, region_data in world.er_regions.items():
|
||||||
|
# if fewer shops is off, zig skip is not made
|
||||||
|
if region_name == "Zig Skip Exit":
|
||||||
|
# need to check if there's a seed group for this first
|
||||||
|
if world.options.entrance_rando.value not in EntranceRando.options.values():
|
||||||
|
if not world.seed_groups[world.options.entrance_rando.value]["fixed_shop"]:
|
||||||
|
continue
|
||||||
|
elif not world.options.fixed_shop:
|
||||||
|
continue
|
||||||
|
regions[region_name] = Region(region_name, world.player, world.multiworld)
|
||||||
|
|
||||||
portal_pairs = pair_portals(world, regions)
|
portal_pairs = pair_portals(world, regions)
|
||||||
|
|
||||||
# output the entrances to the spoiler log here for convenience
|
# output the entrances to the spoiler log here for convenience
|
||||||
|
@ -33,8 +42,15 @@ def create_er_regions(world: "TunicWorld") -> Dict[Portal, Portal]:
|
||||||
for portal1, portal2 in sorted_portal_pairs.items():
|
for portal1, portal2 in sorted_portal_pairs.items():
|
||||||
world.multiworld.spoiler.set_entrance(portal1, portal2, "both", world.player)
|
world.multiworld.spoiler.set_entrance(portal1, portal2, "both", world.player)
|
||||||
else:
|
else:
|
||||||
|
for region_name, region_data in world.er_regions.items():
|
||||||
|
# filter out regions that are inaccessible in non-er
|
||||||
|
if region_name not in ["Zig Skip Exit", "Purgatory"]:
|
||||||
|
regions[region_name] = Region(region_name, world.player, world.multiworld)
|
||||||
|
|
||||||
portal_pairs = vanilla_portals(world, regions)
|
portal_pairs = vanilla_portals(world, regions)
|
||||||
|
|
||||||
|
create_randomized_entrances(portal_pairs, regions)
|
||||||
|
|
||||||
set_er_region_rules(world, regions, portal_pairs)
|
set_er_region_rules(world, regions, portal_pairs)
|
||||||
|
|
||||||
for location_name, location_id in world.location_name_to_id.items():
|
for location_name, location_id in world.location_name_to_id.items():
|
||||||
|
@ -42,8 +58,6 @@ def create_er_regions(world: "TunicWorld") -> Dict[Portal, Portal]:
|
||||||
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)
|
||||||
|
|
||||||
create_randomized_entrances(portal_pairs, regions)
|
|
||||||
|
|
||||||
for region in regions.values():
|
for region in regions.values():
|
||||||
world.multiworld.regions.append(region)
|
world.multiworld.regions.append(region)
|
||||||
|
|
||||||
|
@ -70,7 +84,7 @@ tunic_events: Dict[str, str] = {
|
||||||
"Quarry Connector Fuse": "Quarry Connector",
|
"Quarry Connector Fuse": "Quarry Connector",
|
||||||
"Quarry Fuse": "Quarry Entry",
|
"Quarry Fuse": "Quarry Entry",
|
||||||
"Ziggurat Fuse": "Rooted Ziggurat Lower Back",
|
"Ziggurat Fuse": "Rooted Ziggurat Lower Back",
|
||||||
"West Garden Fuse": "West Garden",
|
"West Garden Fuse": "West Garden South Checkpoint",
|
||||||
"Library Fuse": "Library Lab",
|
"Library Fuse": "Library Lab",
|
||||||
"Place Questagons": "Sealed Temple",
|
"Place Questagons": "Sealed Temple",
|
||||||
}
|
}
|
||||||
|
@ -108,7 +122,8 @@ def create_shop_region(world: "TunicWorld", regions: Dict[str, Region]) -> None:
|
||||||
def vanilla_portals(world: "TunicWorld", regions: Dict[str, Region]) -> Dict[Portal, Portal]:
|
def vanilla_portals(world: "TunicWorld", regions: Dict[str, Region]) -> Dict[Portal, Portal]:
|
||||||
portal_pairs: Dict[Portal, Portal] = {}
|
portal_pairs: Dict[Portal, Portal] = {}
|
||||||
# we don't want the zig skip exit for vanilla portals, since it shouldn't be considered for logic here
|
# we don't want the zig skip exit for vanilla portals, since it shouldn't be considered for logic here
|
||||||
portal_map = [portal for portal in portal_mapping if portal.name != "Ziggurat Lower Falling Entrance"]
|
portal_map = [portal for portal in portal_mapping if portal.name not in
|
||||||
|
["Ziggurat Lower Falling Entrance", "Purgatory Bottom Exit", "Purgatory Top Exit"]]
|
||||||
|
|
||||||
while portal_map:
|
while portal_map:
|
||||||
portal1 = portal_map[0]
|
portal1 = portal_map[0]
|
||||||
|
@ -121,9 +136,6 @@ def vanilla_portals(world: "TunicWorld", regions: Dict[str, Region]) -> Dict[Por
|
||||||
destination="Previous Region", tag="_")
|
destination="Previous Region", tag="_")
|
||||||
create_shop_region(world, regions)
|
create_shop_region(world, regions)
|
||||||
|
|
||||||
elif portal2_sdt == "Purgatory, Purgatory_bottom":
|
|
||||||
portal2_sdt = "Purgatory, Purgatory_top"
|
|
||||||
|
|
||||||
for portal in portal_map:
|
for portal in portal_map:
|
||||||
if portal.scene_destination() == portal2_sdt:
|
if portal.scene_destination() == portal2_sdt:
|
||||||
portal2 = portal
|
portal2 = portal
|
||||||
|
@ -414,6 +426,7 @@ def pair_portals(world: "TunicWorld", regions: Dict[str, Region]) -> Dict[Portal
|
||||||
cr.add(portal.region)
|
cr.add(portal.region)
|
||||||
if "Secret Gathering Place" not in update_reachable_regions(cr, traversal_reqs, has_laurels, logic_tricks):
|
if "Secret Gathering Place" not in update_reachable_regions(cr, traversal_reqs, has_laurels, logic_tricks):
|
||||||
continue
|
continue
|
||||||
|
# if not waterfall_plando, then we just want to pair secret gathering place now
|
||||||
elif portal.region != "Secret Gathering Place":
|
elif portal.region != "Secret Gathering Place":
|
||||||
continue
|
continue
|
||||||
portal2 = portal
|
portal2 = portal
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from itertools import groupby
|
from itertools import groupby
|
||||||
from typing import Dict, List, Set, NamedTuple
|
from typing import Dict, List, Set, NamedTuple, Optional
|
||||||
from BaseClasses import ItemClassification as IC
|
from BaseClasses import ItemClassification as IC
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@ class TunicItemData(NamedTuple):
|
||||||
quantity_in_item_pool: int
|
quantity_in_item_pool: int
|
||||||
item_id_offset: int
|
item_id_offset: int
|
||||||
item_group: str = ""
|
item_group: str = ""
|
||||||
|
# classification if combat logic is on
|
||||||
|
combat_ic: Optional[IC] = None
|
||||||
|
|
||||||
|
|
||||||
item_base_id = 509342400
|
item_base_id = 509342400
|
||||||
|
@ -27,7 +29,7 @@ item_table: Dict[str, TunicItemData] = {
|
||||||
"Lure x2": TunicItemData(IC.filler, 1, 11, "Consumables"),
|
"Lure x2": TunicItemData(IC.filler, 1, 11, "Consumables"),
|
||||||
"Pepper x2": TunicItemData(IC.filler, 4, 12, "Consumables"),
|
"Pepper x2": TunicItemData(IC.filler, 4, 12, "Consumables"),
|
||||||
"Ivy x3": TunicItemData(IC.filler, 2, 13, "Consumables"),
|
"Ivy x3": TunicItemData(IC.filler, 2, 13, "Consumables"),
|
||||||
"Effigy": TunicItemData(IC.useful, 12, 14, "Money"),
|
"Effigy": TunicItemData(IC.useful, 12, 14, "Money", combat_ic=IC.progression),
|
||||||
"HP Berry": TunicItemData(IC.filler, 2, 15, "Consumables"),
|
"HP Berry": TunicItemData(IC.filler, 2, 15, "Consumables"),
|
||||||
"HP Berry x2": TunicItemData(IC.filler, 4, 16, "Consumables"),
|
"HP Berry x2": TunicItemData(IC.filler, 4, 16, "Consumables"),
|
||||||
"HP Berry x3": TunicItemData(IC.filler, 2, 17, "Consumables"),
|
"HP Berry x3": TunicItemData(IC.filler, 2, 17, "Consumables"),
|
||||||
|
@ -44,32 +46,32 @@ item_table: Dict[str, TunicItemData] = {
|
||||||
"Hero's Laurels": TunicItemData(IC.progression | IC.useful, 1, 28),
|
"Hero's Laurels": TunicItemData(IC.progression | IC.useful, 1, 28),
|
||||||
"Lantern": TunicItemData(IC.progression, 1, 29),
|
"Lantern": TunicItemData(IC.progression, 1, 29),
|
||||||
"Gun": TunicItemData(IC.progression | IC.useful, 1, 30, "Weapons"),
|
"Gun": TunicItemData(IC.progression | IC.useful, 1, 30, "Weapons"),
|
||||||
"Shield": TunicItemData(IC.useful, 1, 31),
|
"Shield": TunicItemData(IC.useful, 1, 31, combat_ic=IC.progression | IC.useful),
|
||||||
"Dath Stone": TunicItemData(IC.useful, 1, 32),
|
"Dath Stone": TunicItemData(IC.useful, 1, 32),
|
||||||
"Hourglass": TunicItemData(IC.useful, 1, 33),
|
"Hourglass": TunicItemData(IC.useful, 1, 33),
|
||||||
"Old House Key": TunicItemData(IC.progression, 1, 34, "Keys"),
|
"Old House Key": TunicItemData(IC.progression, 1, 34, "Keys"),
|
||||||
"Key": TunicItemData(IC.progression, 2, 35, "Keys"),
|
"Key": TunicItemData(IC.progression, 2, 35, "Keys"),
|
||||||
"Fortress Vault Key": TunicItemData(IC.progression, 1, 36, "Keys"),
|
"Fortress Vault Key": TunicItemData(IC.progression, 1, 36, "Keys"),
|
||||||
"Flask Shard": TunicItemData(IC.useful, 12, 37),
|
"Flask Shard": TunicItemData(IC.useful, 12, 37, combat_ic=IC.progression),
|
||||||
"Potion Flask": TunicItemData(IC.useful, 5, 38, "Flask"),
|
"Potion Flask": TunicItemData(IC.useful, 5, 38, "Flask", combat_ic=IC.progression),
|
||||||
"Golden Coin": TunicItemData(IC.progression, 17, 39),
|
"Golden Coin": TunicItemData(IC.progression, 17, 39),
|
||||||
"Card Slot": TunicItemData(IC.useful, 4, 40),
|
"Card Slot": TunicItemData(IC.useful, 4, 40),
|
||||||
"Red Questagon": TunicItemData(IC.progression_skip_balancing, 1, 41, "Hexagons"),
|
"Red Questagon": TunicItemData(IC.progression_skip_balancing, 1, 41, "Hexagons"),
|
||||||
"Green Questagon": TunicItemData(IC.progression_skip_balancing, 1, 42, "Hexagons"),
|
"Green Questagon": TunicItemData(IC.progression_skip_balancing, 1, 42, "Hexagons"),
|
||||||
"Blue Questagon": TunicItemData(IC.progression_skip_balancing, 1, 43, "Hexagons"),
|
"Blue Questagon": TunicItemData(IC.progression_skip_balancing, 1, 43, "Hexagons"),
|
||||||
"Gold Questagon": TunicItemData(IC.progression_skip_balancing, 0, 44, "Hexagons"),
|
"Gold Questagon": TunicItemData(IC.progression_skip_balancing, 0, 44, "Hexagons"),
|
||||||
"ATT Offering": TunicItemData(IC.useful, 4, 45, "Offerings"),
|
"ATT Offering": TunicItemData(IC.useful, 4, 45, "Offerings", combat_ic=IC.progression),
|
||||||
"DEF Offering": TunicItemData(IC.useful, 4, 46, "Offerings"),
|
"DEF Offering": TunicItemData(IC.useful, 4, 46, "Offerings", combat_ic=IC.progression),
|
||||||
"Potion Offering": TunicItemData(IC.useful, 3, 47, "Offerings"),
|
"Potion Offering": TunicItemData(IC.useful, 3, 47, "Offerings", combat_ic=IC.progression),
|
||||||
"HP Offering": TunicItemData(IC.useful, 6, 48, "Offerings"),
|
"HP Offering": TunicItemData(IC.useful, 6, 48, "Offerings", combat_ic=IC.progression),
|
||||||
"MP Offering": TunicItemData(IC.useful, 3, 49, "Offerings"),
|
"MP Offering": TunicItemData(IC.useful, 3, 49, "Offerings", combat_ic=IC.progression),
|
||||||
"SP Offering": TunicItemData(IC.useful, 2, 50, "Offerings"),
|
"SP Offering": TunicItemData(IC.useful, 2, 50, "Offerings", combat_ic=IC.progression),
|
||||||
"Hero Relic - ATT": TunicItemData(IC.progression_skip_balancing, 1, 51, "Hero Relics"),
|
"Hero Relic - ATT": TunicItemData(IC.progression_skip_balancing, 1, 51, "Hero Relics", combat_ic=IC.progression),
|
||||||
"Hero Relic - DEF": TunicItemData(IC.progression_skip_balancing, 1, 52, "Hero Relics"),
|
"Hero Relic - DEF": TunicItemData(IC.progression_skip_balancing, 1, 52, "Hero Relics", combat_ic=IC.progression),
|
||||||
"Hero Relic - HP": TunicItemData(IC.progression_skip_balancing, 1, 53, "Hero Relics"),
|
"Hero Relic - HP": TunicItemData(IC.progression_skip_balancing, 1, 53, "Hero Relics", combat_ic=IC.progression),
|
||||||
"Hero Relic - MP": TunicItemData(IC.progression_skip_balancing, 1, 54, "Hero Relics"),
|
"Hero Relic - MP": TunicItemData(IC.progression_skip_balancing, 1, 54, "Hero Relics", combat_ic=IC.progression),
|
||||||
"Hero Relic - POTION": TunicItemData(IC.progression_skip_balancing, 1, 55, "Hero Relics"),
|
"Hero Relic - POTION": TunicItemData(IC.progression_skip_balancing, 1, 55, "Hero Relics", combat_ic=IC.progression),
|
||||||
"Hero Relic - SP": TunicItemData(IC.progression_skip_balancing, 1, 56, "Hero Relics"),
|
"Hero Relic - SP": TunicItemData(IC.progression_skip_balancing, 1, 56, "Hero Relics", combat_ic=IC.progression),
|
||||||
"Orange Peril Ring": TunicItemData(IC.useful, 1, 57, "Cards"),
|
"Orange Peril Ring": TunicItemData(IC.useful, 1, 57, "Cards"),
|
||||||
"Tincture": TunicItemData(IC.useful, 1, 58, "Cards"),
|
"Tincture": TunicItemData(IC.useful, 1, 58, "Cards"),
|
||||||
"Scavenger Mask": TunicItemData(IC.progression, 1, 59, "Cards"),
|
"Scavenger Mask": TunicItemData(IC.progression, 1, 59, "Cards"),
|
||||||
|
@ -86,18 +88,18 @@ item_table: Dict[str, TunicItemData] = {
|
||||||
"Louder Echo": TunicItemData(IC.useful, 1, 70, "Cards"),
|
"Louder Echo": TunicItemData(IC.useful, 1, 70, "Cards"),
|
||||||
"Aura's Gem": TunicItemData(IC.useful, 1, 71, "Cards"),
|
"Aura's Gem": TunicItemData(IC.useful, 1, 71, "Cards"),
|
||||||
"Bone Card": TunicItemData(IC.useful, 1, 72, "Cards"),
|
"Bone Card": TunicItemData(IC.useful, 1, 72, "Cards"),
|
||||||
"Mr Mayor": TunicItemData(IC.useful, 1, 73, "Golden Treasures"),
|
"Mr Mayor": TunicItemData(IC.useful, 1, 73, "Golden Treasures", combat_ic=IC.progression),
|
||||||
"Secret Legend": TunicItemData(IC.useful, 1, 74, "Golden Treasures"),
|
"Secret Legend": TunicItemData(IC.useful, 1, 74, "Golden Treasures", combat_ic=IC.progression),
|
||||||
"Sacred Geometry": TunicItemData(IC.useful, 1, 75, "Golden Treasures"),
|
"Sacred Geometry": TunicItemData(IC.useful, 1, 75, "Golden Treasures", combat_ic=IC.progression),
|
||||||
"Vintage": TunicItemData(IC.useful, 1, 76, "Golden Treasures"),
|
"Vintage": TunicItemData(IC.useful, 1, 76, "Golden Treasures", combat_ic=IC.progression),
|
||||||
"Just Some Pals": TunicItemData(IC.useful, 1, 77, "Golden Treasures"),
|
"Just Some Pals": TunicItemData(IC.useful, 1, 77, "Golden Treasures", combat_ic=IC.progression),
|
||||||
"Regal Weasel": TunicItemData(IC.useful, 1, 78, "Golden Treasures"),
|
"Regal Weasel": TunicItemData(IC.useful, 1, 78, "Golden Treasures", combat_ic=IC.progression),
|
||||||
"Spring Falls": TunicItemData(IC.useful, 1, 79, "Golden Treasures"),
|
"Spring Falls": TunicItemData(IC.useful, 1, 79, "Golden Treasures", combat_ic=IC.progression),
|
||||||
"Power Up": TunicItemData(IC.useful, 1, 80, "Golden Treasures"),
|
"Power Up": TunicItemData(IC.useful, 1, 80, "Golden Treasures", combat_ic=IC.progression),
|
||||||
"Back To Work": TunicItemData(IC.useful, 1, 81, "Golden Treasures"),
|
"Back To Work": TunicItemData(IC.useful, 1, 81, "Golden Treasures", combat_ic=IC.progression),
|
||||||
"Phonomath": TunicItemData(IC.useful, 1, 82, "Golden Treasures"),
|
"Phonomath": TunicItemData(IC.useful, 1, 82, "Golden Treasures", combat_ic=IC.progression),
|
||||||
"Dusty": TunicItemData(IC.useful, 1, 83, "Golden Treasures"),
|
"Dusty": TunicItemData(IC.useful, 1, 83, "Golden Treasures", combat_ic=IC.progression),
|
||||||
"Forever Friend": TunicItemData(IC.useful, 1, 84, "Golden Treasures"),
|
"Forever Friend": TunicItemData(IC.useful, 1, 84, "Golden Treasures", combat_ic=IC.progression),
|
||||||
"Fool Trap": TunicItemData(IC.trap, 0, 85),
|
"Fool Trap": TunicItemData(IC.trap, 0, 85),
|
||||||
"Money x1": TunicItemData(IC.filler, 3, 86, "Money"),
|
"Money x1": TunicItemData(IC.filler, 3, 86, "Money"),
|
||||||
"Money x10": TunicItemData(IC.filler, 1, 87, "Money"),
|
"Money x10": TunicItemData(IC.filler, 1, 87, "Money"),
|
||||||
|
@ -112,9 +114,9 @@ item_table: Dict[str, TunicItemData] = {
|
||||||
"Money x50": TunicItemData(IC.filler, 7, 96, "Money"),
|
"Money x50": TunicItemData(IC.filler, 7, 96, "Money"),
|
||||||
"Money x64": TunicItemData(IC.filler, 1, 97, "Money"),
|
"Money x64": TunicItemData(IC.filler, 1, 97, "Money"),
|
||||||
"Money x100": TunicItemData(IC.filler, 5, 98, "Money"),
|
"Money x100": TunicItemData(IC.filler, 5, 98, "Money"),
|
||||||
"Money x128": TunicItemData(IC.useful, 3, 99, "Money"),
|
"Money x128": TunicItemData(IC.useful, 3, 99, "Money", combat_ic=IC.progression),
|
||||||
"Money x200": TunicItemData(IC.useful, 1, 100, "Money"),
|
"Money x200": TunicItemData(IC.useful, 1, 100, "Money", combat_ic=IC.progression),
|
||||||
"Money x255": TunicItemData(IC.useful, 1, 101, "Money"),
|
"Money x255": TunicItemData(IC.useful, 1, 101, "Money", combat_ic=IC.progression),
|
||||||
"Pages 0-1": TunicItemData(IC.useful, 1, 102, "Pages"),
|
"Pages 0-1": TunicItemData(IC.useful, 1, 102, "Pages"),
|
||||||
"Pages 2-3": TunicItemData(IC.useful, 1, 103, "Pages"),
|
"Pages 2-3": TunicItemData(IC.useful, 1, 103, "Pages"),
|
||||||
"Pages 4-5": TunicItemData(IC.useful, 1, 104, "Pages"),
|
"Pages 4-5": TunicItemData(IC.useful, 1, 104, "Pages"),
|
||||||
|
@ -206,6 +208,10 @@ slot_data_item_names = [
|
||||||
"Gold Questagon",
|
"Gold Questagon",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
combat_items: List[str] = [name for name, data in item_table.items()
|
||||||
|
if data.combat_ic and IC.progression in data.combat_ic]
|
||||||
|
combat_items.extend(["Stick", "Sword", "Sword Upgrade", "Magic Wand", "Hero's Laurels"])
|
||||||
|
|
||||||
item_name_to_id: Dict[str, int] = {name: item_base_id + data.item_id_offset for name, data in item_table.items()}
|
item_name_to_id: Dict[str, int] = {name: item_base_id + data.item_id_offset for name, data in item_table.items()}
|
||||||
|
|
||||||
filler_items: List[str] = [name for name, data in item_table.items() if data.classification == IC.filler]
|
filler_items: List[str] = [name for name, data in item_table.items() if data.classification == IC.filler]
|
||||||
|
|
|
@ -78,9 +78,11 @@ easy_ls: List[LadderInfo] = [
|
||||||
|
|
||||||
# West Garden
|
# West Garden
|
||||||
# exit after Garden Knight
|
# exit after Garden Knight
|
||||||
LadderInfo("West Garden", "Archipelagos Redux, Overworld Redux_upper"),
|
LadderInfo("West Garden before Boss", "Archipelagos Redux, Overworld Redux_upper"),
|
||||||
# West Garden laurels exit
|
# West Garden laurels exit
|
||||||
LadderInfo("West Garden", "Archipelagos Redux, Overworld Redux_lowest"),
|
LadderInfo("West Garden after Terry", "Archipelagos Redux, Overworld Redux_lowest"),
|
||||||
|
# Magic dagger house, only relevant with combat logic on
|
||||||
|
LadderInfo("West Garden after Terry", "Archipelagos Redux, archipelagos_house_"),
|
||||||
|
|
||||||
# Atoll, use the little ladder you fix at the beginning
|
# Atoll, use the little ladder you fix at the beginning
|
||||||
LadderInfo("Ruined Atoll", "Atoll Redux, Overworld Redux_lower"),
|
LadderInfo("Ruined Atoll", "Atoll Redux, Overworld Redux_lower"),
|
||||||
|
@ -159,7 +161,8 @@ medium_ls: List[LadderInfo] = [
|
||||||
LadderInfo("Quarry Back", "Quarry Redux, Monastery_back"),
|
LadderInfo("Quarry Back", "Quarry Redux, Monastery_back"),
|
||||||
|
|
||||||
LadderInfo("Rooted Ziggurat Lower Back", "ziggurat2020_3, ziggurat2020_2_"),
|
LadderInfo("Rooted Ziggurat Lower Back", "ziggurat2020_3, ziggurat2020_2_"),
|
||||||
LadderInfo("Rooted Ziggurat Lower Back", "Rooted Ziggurat Lower Front", dest_is_region=True),
|
LadderInfo("Rooted Ziggurat Lower Back", "Rooted Ziggurat Lower Entry", dest_is_region=True),
|
||||||
|
LadderInfo("Rooted Ziggurat Lower Back", "Rooted Ziggurat Lower Mid Checkpoint", dest_is_region=True),
|
||||||
|
|
||||||
# Swamp to Overworld upper
|
# Swamp to Overworld upper
|
||||||
LadderInfo("Swamp Mid", "Swamp Redux 2, Overworld Redux_wall", "Ladders in Swamp"),
|
LadderInfo("Swamp Mid", "Swamp Redux 2, Overworld Redux_wall", "Ladders in Swamp"),
|
||||||
|
@ -172,9 +175,9 @@ hard_ls: List[LadderInfo] = [
|
||||||
LadderInfo("Beneath the Well Front", "Sewer, Overworld Redux_west_aqueduct", "Ladders in Well"),
|
LadderInfo("Beneath the Well Front", "Sewer, Overworld Redux_west_aqueduct", "Ladders in Well"),
|
||||||
LadderInfo("Beneath the Well Front", "Beneath the Well Back", "Ladders in Well", dest_is_region=True),
|
LadderInfo("Beneath the Well Front", "Beneath the Well Back", "Ladders in Well", dest_is_region=True),
|
||||||
# go through the hexagon engraving above the vault door
|
# go through the hexagon engraving above the vault door
|
||||||
LadderInfo("Frog's Domain", "frog cave main, Frog Stairs_Exit", "Ladders to Frog's Domain"),
|
LadderInfo("Frog's Domain Front", "frog cave main, Frog Stairs_Exit", "Ladders to Frog's Domain"),
|
||||||
# the turret at the end here is not affected by enemy rando
|
# the turret at the end here is not affected by enemy rando
|
||||||
LadderInfo("Frog's Domain", "Frog's Domain Back", "Ladders to Frog's Domain", dest_is_region=True),
|
LadderInfo("Frog's Domain Front", "Frog's Domain Back", "Ladders to Frog's Domain", dest_is_region=True),
|
||||||
# todo: see if we can use that new laurels strat here
|
# todo: see if we can use that new laurels strat here
|
||||||
# LadderInfo("Rooted Ziggurat Lower Back", "ziggurat2020_3, ziggurat2020_FTRoom_"),
|
# LadderInfo("Rooted Ziggurat Lower Back", "ziggurat2020_3, ziggurat2020_FTRoom_"),
|
||||||
# go behind the cathedral to reach the door, pretty easily doable
|
# go behind the cathedral to reach the door, pretty easily doable
|
||||||
|
|
|
@ -25,17 +25,17 @@ location_table: Dict[str, TunicLocationData] = {
|
||||||
"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("Overworld", "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 Main"),
|
||||||
"Cathedral - [1F] Near Spikes": TunicLocationData("Cathedral", "Cathedral"),
|
"Cathedral - [1F] Near Spikes": TunicLocationData("Cathedral", "Cathedral Entry"), # entry because special rules
|
||||||
"Cathedral - [2F] Bird Room": TunicLocationData("Cathedral", "Cathedral"),
|
"Cathedral - [2F] Bird Room": TunicLocationData("Cathedral", "Cathedral Main"),
|
||||||
"Cathedral - [2F] Entryway Upper Walkway": TunicLocationData("Cathedral", "Cathedral"),
|
"Cathedral - [2F] Entryway Upper Walkway": TunicLocationData("Cathedral", "Cathedral Main"),
|
||||||
"Cathedral - [1F] Library": TunicLocationData("Cathedral", "Cathedral"),
|
"Cathedral - [1F] Library": TunicLocationData("Cathedral", "Cathedral Entry"),
|
||||||
"Cathedral - [2F] Library": TunicLocationData("Cathedral", "Cathedral"),
|
"Cathedral - [2F] Library": TunicLocationData("Cathedral", "Cathedral Main"),
|
||||||
"Cathedral - [2F] Guarded By Lasers": TunicLocationData("Cathedral", "Cathedral"),
|
"Cathedral - [2F] Guarded By Lasers": TunicLocationData("Cathedral", "Cathedral Main"),
|
||||||
"Cathedral - [2F] Bird Room Secret": TunicLocationData("Cathedral", "Cathedral"),
|
"Cathedral - [2F] Bird Room Secret": TunicLocationData("Cathedral", "Cathedral Main"),
|
||||||
"Cathedral - [1F] Library Secret": TunicLocationData("Cathedral", "Cathedral"),
|
"Cathedral - [1F] Library Secret": TunicLocationData("Cathedral", "Cathedral Entry"),
|
||||||
"Dark Tomb - Spike Maze Near Exit": TunicLocationData("Dark Tomb", "Dark Tomb Main"),
|
"Dark Tomb - Spike Maze Near Exit": TunicLocationData("Dark Tomb", "Dark Tomb Main"),
|
||||||
"Dark Tomb - 2nd Laser Room": TunicLocationData("Dark Tomb", "Dark Tomb Main"),
|
"Dark Tomb - 2nd Laser Room": TunicLocationData("Dark Tomb", "Dark Tomb Dark Exit"),
|
||||||
"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 Upper"),
|
"Dark Tomb - Skulls Chest": TunicLocationData("Dark Tomb", "Dark Tomb Upper"),
|
||||||
|
@ -81,25 +81,25 @@ location_table: Dict[str, TunicLocationData] = {
|
||||||
"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"),
|
||||||
"Eastern Vault Fortress - [West Wing] Page Pickup": TunicLocationData("Eastern Vault Fortress", "Eastern Vault Fortress"),
|
"Eastern Vault Fortress - [West Wing] Page Pickup": TunicLocationData("Eastern Vault Fortress", "Eastern Vault Fortress"),
|
||||||
"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 by Grave"),
|
||||||
"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 by Grave"),
|
||||||
"Hero's Grave - Flowers Relic": TunicLocationData("Eastern Vault Fortress", "Hero Relic - Fortress"),
|
"Hero's Grave - Flowers Relic": TunicLocationData("Eastern Vault Fortress", "Hero Relic - Fortress"),
|
||||||
"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 Main"),
|
"Beneath the Fortress - Obscured Behind Waterfall": TunicLocationData("Beneath the Vault", "Beneath the Vault Main"),
|
||||||
"Beneath the Fortress - Back Room Chest": TunicLocationData("Beneath the Vault", "Beneath the Vault Back"),
|
"Beneath the Fortress - Back Room Chest": TunicLocationData("Beneath the Vault", "Beneath the Vault Back"),
|
||||||
"Beneath the Fortress - Cell Chest 2": TunicLocationData("Beneath the Vault", "Beneath the Vault Back"),
|
"Beneath the Fortress - Cell Chest 2": TunicLocationData("Beneath the Vault", "Beneath the Vault Back"),
|
||||||
"Frog's Domain - Near Vault": TunicLocationData("Frog's Domain", "Frog's Domain"),
|
"Frog's Domain - Near Vault": TunicLocationData("Frog's Domain", "Frog's Domain Main"),
|
||||||
"Frog's Domain - Slorm Room": TunicLocationData("Frog's Domain", "Frog's Domain"),
|
"Frog's Domain - Slorm Room": TunicLocationData("Frog's Domain", "Frog's Domain Main"),
|
||||||
"Frog's Domain - Escape Chest": TunicLocationData("Frog's Domain", "Frog's Domain Back"),
|
"Frog's Domain - Escape Chest": TunicLocationData("Frog's Domain", "Frog's Domain Back"),
|
||||||
"Frog's Domain - Grapple Above Hot Tub": TunicLocationData("Frog's Domain", "Frog's Domain"),
|
"Frog's Domain - Grapple Above Hot Tub": TunicLocationData("Frog's Domain", "Frog's Domain Front"),
|
||||||
"Frog's Domain - Above Vault": TunicLocationData("Frog's Domain", "Frog's Domain"),
|
"Frog's Domain - Above Vault": TunicLocationData("Frog's Domain", "Frog's Domain Front"),
|
||||||
"Frog's Domain - Main Room Top Floor": TunicLocationData("Frog's Domain", "Frog's Domain"),
|
"Frog's Domain - Main Room Top Floor": TunicLocationData("Frog's Domain", "Frog's Domain Main"),
|
||||||
"Frog's Domain - Main Room Bottom Floor": TunicLocationData("Frog's Domain", "Frog's Domain"),
|
"Frog's Domain - Main Room Bottom Floor": TunicLocationData("Frog's Domain", "Frog's Domain Main"),
|
||||||
"Frog's Domain - Side Room Secret Passage": TunicLocationData("Frog's Domain", "Frog's Domain"),
|
"Frog's Domain - Side Room Secret Passage": TunicLocationData("Frog's Domain", "Frog's Domain Main"),
|
||||||
"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 Main"),
|
||||||
"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 Front"),
|
||||||
"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 Main"),
|
||||||
"Librarian - Hexagon Green": TunicLocationData("Library", "Library Arena", location_group="Bosses"),
|
"Librarian - Hexagon Green": TunicLocationData("Library", "Library Arena", location_group="Bosses"),
|
||||||
"Library Hall - Holy Cross Chest": TunicLocationData("Library", "Library Hall", location_group="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"),
|
||||||
|
@ -131,7 +131,7 @@ location_table: Dict[str, TunicLocationData] = {
|
||||||
"Overworld - [Southwest] West Beach Guarded By Turret": TunicLocationData("Overworld", "Overworld Beach"),
|
"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 Tunnel to Beach"),
|
||||||
"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 after Envoy"),
|
"Overworld - [Northwest] Chest Beneath Quarry Gate": TunicLocationData("Overworld", "Overworld after Envoy"),
|
||||||
"Overworld - [Southeast] Chest Near Swamp": TunicLocationData("Overworld", "Overworld Swamp Lower Entry"),
|
"Overworld - [Southeast] Chest Near Swamp": TunicLocationData("Overworld", "Overworld Swamp Lower Entry"),
|
||||||
|
@ -158,7 +158,7 @@ location_table: Dict[str, TunicLocationData] = {
|
||||||
"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", "Upper 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 Well Entry Area"),
|
||||||
"Patrol Cave - Normal Chest": TunicLocationData("Overworld", "Patrol Cave"),
|
"Patrol Cave - Normal Chest": TunicLocationData("Overworld", "Patrol Cave"),
|
||||||
"Ruined Shop - Chest 1": TunicLocationData("Overworld", "Ruined Shop"),
|
"Ruined Shop - Chest 1": TunicLocationData("Overworld", "Ruined Shop"),
|
||||||
"Ruined Shop - Chest 2": TunicLocationData("Overworld", "Ruined Shop"),
|
"Ruined Shop - Chest 2": TunicLocationData("Overworld", "Ruined Shop"),
|
||||||
|
@ -233,17 +233,17 @@ location_table: Dict[str, TunicLocationData] = {
|
||||||
"Quarry - [Lowlands] Upper Walkway": TunicLocationData("Lower Quarry", "Even 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", "Even Lower Quarry"),
|
"Quarry - [Lowlands] Near Elevator": TunicLocationData("Lower Quarry", "Even Lower Quarry Isolated Chest"),
|
||||||
"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"),
|
||||||
"Rooted Ziggurat Tower - Inside Tower": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Middle Top"),
|
"Rooted Ziggurat Tower - Inside Tower": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Middle Top"),
|
||||||
"Rooted Ziggurat Lower - Near Corpses": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Front"),
|
"Rooted Ziggurat Lower - Near Corpses": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Entry"),
|
||||||
"Rooted Ziggurat Lower - Spider Ambush": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Front"),
|
"Rooted Ziggurat Lower - Spider Ambush": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Entry"),
|
||||||
"Rooted Ziggurat Lower - Left Of Checkpoint Before Fuse": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Front"),
|
"Rooted Ziggurat Lower - Left Of Checkpoint Before Fuse": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Mid Checkpoint"),
|
||||||
"Rooted Ziggurat Lower - After Guarded Fuse": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Front"),
|
"Rooted Ziggurat Lower - After Guarded Fuse": TunicLocationData("Rooted Ziggurat", "Rooted Ziggurat Lower Mid Checkpoint"),
|
||||||
"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 Mid Checkpoint"),
|
||||||
"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", location_group="Bosses"),
|
"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"),
|
||||||
|
@ -290,26 +290,26 @@ location_table: Dict[str, TunicLocationData] = {
|
||||||
"Hero's Grave - Feathers Relic": TunicLocationData("Swamp", "Hero Relic - Swamp"),
|
"Hero's Grave - Feathers Relic": TunicLocationData("Swamp", "Hero Relic - Swamp"),
|
||||||
"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", location_group="Holy Cross"),
|
"West Garden - [Central Highlands] Holy Cross (Blue Lines)": TunicLocationData("West Garden", "West Garden before Boss", location_group="Holy Cross"),
|
||||||
"West Garden - [West Lowlands] Tree Holy Cross Chest": TunicLocationData("West Garden", "West Garden", location_group="Holy Cross"),
|
"West Garden - [West Lowlands] Tree Holy Cross Chest": TunicLocationData("West Garden", "West Garden after Terry", 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 at Dagger House"),
|
||||||
"West Garden - [Central Lowlands] Chest Beneath Faeries": TunicLocationData("West Garden", "West Garden"),
|
"West Garden - [Central Lowlands] Chest Beneath Faeries": TunicLocationData("West Garden", "West Garden South Checkpoint"),
|
||||||
"West Garden - [North] Behind Holy Cross Door": TunicLocationData("West Garden", "West Garden", location_group="Holy Cross"),
|
"West Garden - [North] Behind Holy Cross Door": TunicLocationData("West Garden", "West Garden before Terry", 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 before Boss"),
|
||||||
"West Garden - [Central Lowlands] Passage Beneath Bridge": TunicLocationData("West Garden", "West Garden"),
|
"West Garden - [Central Lowlands] Passage Beneath Bridge": TunicLocationData("West Garden", "West Garden after Terry"),
|
||||||
"West Garden - [North] Across From Page Pickup": TunicLocationData("West Garden", "West Garden"),
|
"West Garden - [North] Across From Page Pickup": TunicLocationData("West Garden", "West Garden before Terry"),
|
||||||
"West Garden - [Central Lowlands] Below Left Walkway": TunicLocationData("West Garden", "West Garden"),
|
"West Garden - [Central Lowlands] Below Left Walkway": TunicLocationData("West Garden", "West Garden after Terry"),
|
||||||
"West Garden - [West] In Flooded Walkway": TunicLocationData("West Garden", "West Garden"),
|
"West Garden - [West] In Flooded Walkway": TunicLocationData("West Garden", "West Garden after Terry"),
|
||||||
"West Garden - [West] Past Flooded Walkway": TunicLocationData("West Garden", "West Garden"),
|
"West Garden - [West] Past Flooded Walkway": TunicLocationData("West Garden", "West Garden after Terry"),
|
||||||
"West Garden - [North] Obscured Beneath Hero's Memorial": TunicLocationData("West Garden", "West Garden"),
|
"West Garden - [North] Obscured Beneath Hero's Memorial": TunicLocationData("West Garden", "West Garden before Terry"),
|
||||||
"West Garden - [Central Lowlands] Chest Near Shortcut Bridge": TunicLocationData("West Garden", "West Garden"),
|
"West Garden - [Central Lowlands] Chest Near Shortcut Bridge": TunicLocationData("West Garden", "West Garden after Terry"),
|
||||||
"West Garden - [West Highlands] Upper Left Walkway": TunicLocationData("West Garden", "West Garden"),
|
"West Garden - [West Highlands] Upper Left Walkway": TunicLocationData("West Garden", "West Garden South Checkpoint"),
|
||||||
"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 South Checkpoint"),
|
||||||
"West Garden - [Central Highlands] Behind Guard Captain": TunicLocationData("West Garden", "West Garden"),
|
"West Garden - [Central Highlands] Behind Guard Captain": TunicLocationData("West Garden", "West Garden before Boss"),
|
||||||
"West Garden - [Central Highlands] After Garden Knight": TunicLocationData("Overworld", "West Garden after Boss", location_group="Bosses"),
|
"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 South Checkpoint"),
|
||||||
"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 before Terry"),
|
||||||
"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"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,6 +168,22 @@ class TunicPlandoConnections(PlandoConnections):
|
||||||
duplicate_exits = True
|
duplicate_exits = True
|
||||||
|
|
||||||
|
|
||||||
|
class CombatLogic(Choice):
|
||||||
|
"""
|
||||||
|
If enabled, the player will logically require a combination of stat upgrade items and equipment to get some checks or navigate to some areas, with a goal of matching the vanilla combat difficulty.
|
||||||
|
The player may still be expected to run past enemies, reset aggro (by using a checkpoint or doing a scene transition), or find sneaky paths to checks.
|
||||||
|
This option marks many more items as progression and may force weapons much earlier than normal.
|
||||||
|
Bosses Only makes it so that additional combat logic is only added to the boss fights and the Gauntlet.
|
||||||
|
If disabled, the standard, looser logic is used. The standard logic does not include stat upgrades, just minimal weapon requirements, such as requiring a Sword or Magic Wand for Quarry, or not requiring a weapon for Swamp.
|
||||||
|
"""
|
||||||
|
internal_name = "combat_logic"
|
||||||
|
display_name = "More Combat Logic"
|
||||||
|
option_off = 0
|
||||||
|
option_bosses_only = 1
|
||||||
|
option_on = 2
|
||||||
|
default = 0
|
||||||
|
|
||||||
|
|
||||||
class LaurelsZips(Toggle):
|
class LaurelsZips(Toggle):
|
||||||
"""
|
"""
|
||||||
Choose whether to include using the Hero's Laurels to zip through gates, doors, and tricky spots.
|
Choose whether to include using the Hero's Laurels to zip through gates, doors, and tricky spots.
|
||||||
|
@ -259,6 +275,7 @@ class TunicOptions(PerGameCommonOptions):
|
||||||
hexagon_goal: HexagonGoal
|
hexagon_goal: HexagonGoal
|
||||||
extra_hexagon_percentage: ExtraHexagonPercentage
|
extra_hexagon_percentage: ExtraHexagonPercentage
|
||||||
laurels_location: LaurelsLocation
|
laurels_location: LaurelsLocation
|
||||||
|
combat_logic: CombatLogic
|
||||||
lanternless: Lanternless
|
lanternless: Lanternless
|
||||||
maskless: Maskless
|
maskless: Maskless
|
||||||
laurels_zips: LaurelsZips
|
laurels_zips: LaurelsZips
|
||||||
|
@ -272,6 +289,7 @@ class TunicOptions(PerGameCommonOptions):
|
||||||
|
|
||||||
tunic_option_groups = [
|
tunic_option_groups = [
|
||||||
OptionGroup("Logic Options", [
|
OptionGroup("Logic Options", [
|
||||||
|
CombatLogic,
|
||||||
Lanternless,
|
Lanternless,
|
||||||
Maskless,
|
Maskless,
|
||||||
LaurelsZips,
|
LaurelsZips,
|
||||||
|
|
|
@ -56,9 +56,8 @@ def has_ability(ability: str, state: CollectionState, world: "TunicWorld") -> bo
|
||||||
|
|
||||||
|
|
||||||
# a check to see if you can whack things in melee at all
|
# a check to see if you can whack things in melee at all
|
||||||
def has_stick(state: CollectionState, player: int) -> bool:
|
def has_melee(state: CollectionState, player: int) -> bool:
|
||||||
return (state.has("Stick", player) or state.has("Sword Upgrade", player, 1)
|
return state.has_any({"Stick", "Sword", "Sword Upgrade"}, player)
|
||||||
or state.has("Sword", player))
|
|
||||||
|
|
||||||
|
|
||||||
def has_sword(state: CollectionState, player: int) -> bool:
|
def has_sword(state: CollectionState, player: int) -> bool:
|
||||||
|
@ -83,7 +82,7 @@ def can_ladder_storage(state: CollectionState, world: "TunicWorld") -> bool:
|
||||||
return False
|
return False
|
||||||
if world.options.ladder_storage_without_items:
|
if world.options.ladder_storage_without_items:
|
||||||
return True
|
return True
|
||||||
return has_stick(state, world.player) or state.has_any((grapple, shield), world.player)
|
return has_melee(state, world.player) or state.has_any((grapple, shield), world.player)
|
||||||
|
|
||||||
|
|
||||||
def has_mask(state: CollectionState, world: "TunicWorld") -> bool:
|
def has_mask(state: CollectionState, world: "TunicWorld") -> bool:
|
||||||
|
@ -101,7 +100,7 @@ def set_region_rules(world: "TunicWorld") -> None:
|
||||||
world.get_entrance("Overworld -> Overworld Holy Cross").access_rule = \
|
world.get_entrance("Overworld -> Overworld Holy Cross").access_rule = \
|
||||||
lambda state: has_ability(holy_cross, state, world)
|
lambda state: has_ability(holy_cross, state, world)
|
||||||
world.get_entrance("Overworld -> Beneath the Well").access_rule = \
|
world.get_entrance("Overworld -> Beneath the Well").access_rule = \
|
||||||
lambda state: has_stick(state, player) or state.has(fire_wand, player)
|
lambda state: has_melee(state, player) or state.has(fire_wand, player)
|
||||||
world.get_entrance("Overworld -> Dark Tomb").access_rule = \
|
world.get_entrance("Overworld -> Dark Tomb").access_rule = \
|
||||||
lambda state: has_lantern(state, world)
|
lambda state: has_lantern(state, world)
|
||||||
# laurels in, ladder storage in through the furnace, or ice grapple down the belltower
|
# laurels in, ladder storage in through the furnace, or ice grapple down the belltower
|
||||||
|
@ -117,7 +116,7 @@ def set_region_rules(world: "TunicWorld") -> None:
|
||||||
world.get_entrance("Overworld -> Beneath the Vault").access_rule = \
|
world.get_entrance("Overworld -> Beneath the Vault").access_rule = \
|
||||||
lambda state: (has_lantern(state, world) and has_ability(prayer, state, world)
|
lambda state: (has_lantern(state, world) and has_ability(prayer, state, world)
|
||||||
# there's some boxes in the way
|
# there's some boxes in the way
|
||||||
and (has_stick(state, player) or state.has_any((gun, grapple, fire_wand), player)))
|
and (has_melee(state, player) or state.has_any((gun, grapple, fire_wand), player)))
|
||||||
world.get_entrance("Ruined Atoll -> Library").access_rule = \
|
world.get_entrance("Ruined Atoll -> Library").access_rule = \
|
||||||
lambda state: state.has_any({grapple, laurels}, player) and has_ability(prayer, state, world)
|
lambda state: state.has_any({grapple, laurels}, player) and has_ability(prayer, state, world)
|
||||||
world.get_entrance("Overworld -> Quarry").access_rule = \
|
world.get_entrance("Overworld -> Quarry").access_rule = \
|
||||||
|
@ -237,7 +236,7 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||||
or (has_lantern(state, world) and (has_sword(state, player) or state.has(fire_wand, player)))
|
or (has_lantern(state, world) and (has_sword(state, player) or state.has(fire_wand, player)))
|
||||||
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world))
|
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world))
|
||||||
set_rule(world.get_location("West Furnace - Lantern Pickup"),
|
set_rule(world.get_location("West Furnace - Lantern Pickup"),
|
||||||
lambda state: has_stick(state, player) or state.has_any({fire_wand, laurels}, player))
|
lambda state: has_melee(state, player) or state.has_any({fire_wand, laurels}, player))
|
||||||
|
|
||||||
set_rule(world.get_location("Secret Gathering Place - 10 Fairy Reward"),
|
set_rule(world.get_location("Secret Gathering Place - 10 Fairy Reward"),
|
||||||
lambda state: state.has(fairies, player, 10))
|
lambda state: state.has(fairies, player, 10))
|
||||||
|
@ -301,18 +300,18 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||||
|
|
||||||
# Library Lab
|
# Library Lab
|
||||||
set_rule(world.get_location("Library Lab - Page 1"),
|
set_rule(world.get_location("Library Lab - Page 1"),
|
||||||
lambda state: has_stick(state, player) or state.has_any((fire_wand, gun), player))
|
lambda state: has_melee(state, player) or state.has_any((fire_wand, gun), player))
|
||||||
set_rule(world.get_location("Library Lab - Page 2"),
|
set_rule(world.get_location("Library Lab - Page 2"),
|
||||||
lambda state: has_stick(state, player) or state.has_any((fire_wand, gun), player))
|
lambda state: has_melee(state, player) or state.has_any((fire_wand, gun), player))
|
||||||
set_rule(world.get_location("Library Lab - Page 3"),
|
set_rule(world.get_location("Library Lab - Page 3"),
|
||||||
lambda state: has_stick(state, player) or state.has_any((fire_wand, gun), player))
|
lambda state: has_melee(state, player) or state.has_any((fire_wand, gun), player))
|
||||||
|
|
||||||
# Eastern Vault Fortress
|
# Eastern Vault Fortress
|
||||||
# yes, you can clear the leaves with dagger
|
# yes, you can clear the leaves with dagger
|
||||||
# gun isn't included since it can only break one leaf pile at a time, and we don't check how much mana you have
|
# gun isn't included since it can only break one leaf pile at a time, and we don't check how much mana you have
|
||||||
# but really, I expect the player to just throw a bomb at them if they don't have melee
|
# but really, I expect the player to just throw a bomb at them if they don't have melee
|
||||||
set_rule(world.get_location("Fortress Leaf Piles - Secret Chest"),
|
set_rule(world.get_location("Fortress Leaf Piles - Secret Chest"),
|
||||||
lambda state: state.has(laurels, player) and (has_stick(state, player) or state.has(ice_dagger, player)))
|
lambda state: state.has(laurels, player) and (has_melee(state, player) or state.has(ice_dagger, player)))
|
||||||
set_rule(world.get_location("Fortress Arena - Siege Engine/Vault Key Pickup"),
|
set_rule(world.get_location("Fortress Arena - Siege Engine/Vault Key Pickup"),
|
||||||
lambda state: has_sword(state, player)
|
lambda state: has_sword(state, player)
|
||||||
and (has_ability(prayer, state, world)
|
and (has_ability(prayer, state, world)
|
||||||
|
@ -324,9 +323,9 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||||
|
|
||||||
# Beneath the Vault
|
# Beneath the Vault
|
||||||
set_rule(world.get_location("Beneath the Fortress - Bridge"),
|
set_rule(world.get_location("Beneath the Fortress - Bridge"),
|
||||||
lambda state: has_stick(state, player) or state.has_any({laurels, fire_wand}, player))
|
lambda state: has_melee(state, player) or state.has_any({laurels, fire_wand}, player))
|
||||||
set_rule(world.get_location("Beneath the Fortress - Obscured Behind Waterfall"),
|
set_rule(world.get_location("Beneath the Fortress - Obscured Behind Waterfall"),
|
||||||
lambda state: has_stick(state, player) and has_lantern(state, world))
|
lambda state: has_melee(state, player) and has_lantern(state, world))
|
||||||
|
|
||||||
# Quarry
|
# Quarry
|
||||||
set_rule(world.get_location("Quarry - [Central] Above Ladder Dash Chest"),
|
set_rule(world.get_location("Quarry - [Central] Above Ladder Dash Chest"),
|
||||||
|
|
|
@ -3,6 +3,8 @@ from .. import options
|
||||||
|
|
||||||
|
|
||||||
class TestAccess(TunicTestBase):
|
class TestAccess(TunicTestBase):
|
||||||
|
options = {options.CombatLogic.internal_name: options.CombatLogic.option_off}
|
||||||
|
|
||||||
# test whether you can get into the temple without laurels
|
# test whether you can get into the temple without laurels
|
||||||
def test_temple_access(self) -> None:
|
def test_temple_access(self) -> None:
|
||||||
self.collect_all_but(["Hero's Laurels", "Lantern"])
|
self.collect_all_but(["Hero's Laurels", "Lantern"])
|
||||||
|
@ -61,7 +63,9 @@ class TestNormalGoal(TunicTestBase):
|
||||||
class TestER(TunicTestBase):
|
class TestER(TunicTestBase):
|
||||||
options = {options.EntranceRando.internal_name: options.EntranceRando.option_yes,
|
options = {options.EntranceRando.internal_name: options.EntranceRando.option_yes,
|
||||||
options.AbilityShuffling.internal_name: options.AbilityShuffling.option_true,
|
options.AbilityShuffling.internal_name: options.AbilityShuffling.option_true,
|
||||||
options.HexagonQuest.internal_name: options.HexagonQuest.option_false}
|
options.HexagonQuest.internal_name: options.HexagonQuest.option_false,
|
||||||
|
options.CombatLogic.internal_name: options.CombatLogic.option_off,
|
||||||
|
options.FixedShop.internal_name: options.FixedShop.option_true}
|
||||||
|
|
||||||
def test_overworld_hc_chest(self) -> None:
|
def test_overworld_hc_chest(self) -> None:
|
||||||
# test to see that static connections are working properly -- this chest requires holy cross and is in Overworld
|
# test to see that static connections are working properly -- this chest requires holy cross and is in Overworld
|
||||||
|
|
Loading…
Reference in New Issue