2024-05-20 07:04:06 +00:00
|
|
|
from worlds.AutoWorld import CollectionState
|
|
|
|
from worlds.generic.Rules import add_rule, set_rule
|
2024-07-27 17:16:52 +00:00
|
|
|
from .Locations import location_table, zipline_unlocks, is_location_valid, shop_locations, event_locs
|
2024-05-20 07:04:06 +00:00
|
|
|
from .Types import HatType, ChapterIndex, hat_type_to_item, Difficulty, HitType
|
|
|
|
from BaseClasses import Location, Entrance, Region
|
|
|
|
from typing import TYPE_CHECKING, List, Callable, Union, Dict
|
|
|
|
from .Options import EndGoal, CTRLogic, NoTicketSkips
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
from . import HatInTimeWorld
|
|
|
|
|
|
|
|
|
|
|
|
act_connections = {
|
|
|
|
"Mafia Town - Act 2": ["Mafia Town - Act 1"],
|
|
|
|
"Mafia Town - Act 3": ["Mafia Town - Act 1"],
|
|
|
|
"Mafia Town - Act 4": ["Mafia Town - Act 2", "Mafia Town - Act 3"],
|
|
|
|
"Mafia Town - Act 6": ["Mafia Town - Act 4"],
|
|
|
|
"Mafia Town - Act 7": ["Mafia Town - Act 4"],
|
|
|
|
"Mafia Town - Act 5": ["Mafia Town - Act 6", "Mafia Town - Act 7"],
|
|
|
|
|
|
|
|
"Battle of the Birds - Act 2": ["Battle of the Birds - Act 1"],
|
|
|
|
"Battle of the Birds - Act 3": ["Battle of the Birds - Act 1"],
|
|
|
|
"Battle of the Birds - Act 4": ["Battle of the Birds - Act 2", "Battle of the Birds - Act 3"],
|
|
|
|
"Battle of the Birds - Act 5": ["Battle of the Birds - Act 2", "Battle of the Birds - Act 3"],
|
|
|
|
"Battle of the Birds - Finale A": ["Battle of the Birds - Act 4", "Battle of the Birds - Act 5"],
|
|
|
|
"Battle of the Birds - Finale B": ["Battle of the Birds - Finale A"],
|
|
|
|
|
|
|
|
"Subcon Forest - Finale": ["Subcon Forest - Act 1", "Subcon Forest - Act 2",
|
|
|
|
"Subcon Forest - Act 3", "Subcon Forest - Act 4",
|
|
|
|
"Subcon Forest - Act 5"],
|
|
|
|
|
|
|
|
"The Arctic Cruise - Act 2": ["The Arctic Cruise - Act 1"],
|
|
|
|
"The Arctic Cruise - Finale": ["The Arctic Cruise - Act 2"],
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def can_use_hat(state: CollectionState, world: "HatInTimeWorld", hat: HatType) -> bool:
|
|
|
|
if world.options.HatItems:
|
|
|
|
return state.has(hat_type_to_item[hat], world.player)
|
|
|
|
|
|
|
|
if world.hat_yarn_costs[hat] <= 0: # this means the hat was put into starting inventory
|
|
|
|
return True
|
|
|
|
|
|
|
|
return state.has("Yarn", world.player, get_hat_cost(world, hat))
|
|
|
|
|
|
|
|
|
|
|
|
def get_hat_cost(world: "HatInTimeWorld", hat: HatType) -> int:
|
|
|
|
cost = 0
|
|
|
|
for h in world.hat_craft_order:
|
|
|
|
cost += world.hat_yarn_costs[h]
|
|
|
|
if h == hat:
|
|
|
|
break
|
|
|
|
|
|
|
|
return cost
|
|
|
|
|
|
|
|
|
|
|
|
def painting_logic(world: "HatInTimeWorld") -> bool:
|
|
|
|
return bool(world.options.ShuffleSubconPaintings)
|
|
|
|
|
|
|
|
|
|
|
|
# -1 = Normal, 0 = Moderate, 1 = Hard, 2 = Expert
|
|
|
|
def get_difficulty(world: "HatInTimeWorld") -> Difficulty:
|
|
|
|
return Difficulty(world.options.LogicDifficulty)
|
|
|
|
|
|
|
|
|
|
|
|
def has_paintings(state: CollectionState, world: "HatInTimeWorld", count: int, allow_skip: bool = True) -> bool:
|
|
|
|
if not painting_logic(world):
|
|
|
|
return True
|
|
|
|
|
|
|
|
if not world.options.NoPaintingSkips and allow_skip:
|
|
|
|
# In Moderate there is a very easy trick to skip all the walls, except for the one guarding the boss arena
|
|
|
|
if get_difficulty(world) >= Difficulty.MODERATE:
|
|
|
|
return True
|
|
|
|
|
|
|
|
return state.has("Progressive Painting Unlock", world.player, count)
|
|
|
|
|
|
|
|
|
|
|
|
def zipline_logic(world: "HatInTimeWorld") -> bool:
|
|
|
|
return bool(world.options.ShuffleAlpineZiplines)
|
|
|
|
|
|
|
|
|
|
|
|
def can_use_hookshot(state: CollectionState, world: "HatInTimeWorld"):
|
|
|
|
return state.has("Hookshot Badge", world.player)
|
|
|
|
|
|
|
|
|
|
|
|
def can_hit(state: CollectionState, world: "HatInTimeWorld", umbrella_only: bool = False):
|
|
|
|
if not world.options.UmbrellaLogic:
|
|
|
|
return True
|
|
|
|
|
|
|
|
return state.has("Umbrella", world.player) or not umbrella_only and can_use_hat(state, world, HatType.BREWING)
|
|
|
|
|
|
|
|
|
|
|
|
def has_relic_combo(state: CollectionState, world: "HatInTimeWorld", relic: str) -> bool:
|
|
|
|
return state.has_group(relic, world.player, len(world.item_name_groups[relic]))
|
|
|
|
|
|
|
|
|
|
|
|
def get_relic_count(state: CollectionState, world: "HatInTimeWorld", relic: str) -> int:
|
|
|
|
return state.count_group(relic, world.player)
|
|
|
|
|
|
|
|
|
|
|
|
# This is used to determine if the player can clear an act that's required to unlock a Time Rift
|
|
|
|
def can_clear_required_act(state: CollectionState, world: "HatInTimeWorld", act_entrance: str) -> bool:
|
|
|
|
entrance: Entrance = world.multiworld.get_entrance(act_entrance, world.player)
|
|
|
|
if not state.can_reach(entrance.connected_region, "Region", world.player):
|
|
|
|
return False
|
|
|
|
|
|
|
|
if "Free Roam" in entrance.connected_region.name:
|
|
|
|
return True
|
|
|
|
|
|
|
|
name: str = f"Act Completion ({entrance.connected_region.name})"
|
|
|
|
return world.multiworld.get_location(name, world.player).access_rule(state)
|
|
|
|
|
|
|
|
|
|
|
|
def can_clear_alpine(state: CollectionState, world: "HatInTimeWorld") -> bool:
|
|
|
|
return state.has("Birdhouse Cleared", world.player) and state.has("Lava Cake Cleared", world.player) \
|
|
|
|
and state.has("Windmill Cleared", world.player) and state.has("Twilight Bell Cleared", world.player)
|
|
|
|
|
|
|
|
|
|
|
|
def can_clear_metro(state: CollectionState, world: "HatInTimeWorld") -> bool:
|
|
|
|
return state.has("Nyakuza Intro Cleared", world.player) \
|
|
|
|
and state.has("Yellow Overpass Station Cleared", world.player) \
|
|
|
|
and state.has("Yellow Overpass Manhole Cleared", world.player) \
|
|
|
|
and state.has("Green Clean Station Cleared", world.player) \
|
|
|
|
and state.has("Green Clean Manhole Cleared", world.player) \
|
|
|
|
and state.has("Bluefin Tunnel Cleared", world.player) \
|
|
|
|
and state.has("Pink Paw Station Cleared", world.player) \
|
|
|
|
and state.has("Pink Paw Manhole Cleared", world.player)
|
|
|
|
|
|
|
|
|
|
|
|
def set_rules(world: "HatInTimeWorld"):
|
|
|
|
# First, chapter access
|
|
|
|
starting_chapter = ChapterIndex(world.options.StartingChapter)
|
|
|
|
world.chapter_timepiece_costs[starting_chapter] = 0
|
|
|
|
|
|
|
|
# Chapter costs increase progressively. Randomly decide the chapter order, except for Finale
|
|
|
|
chapter_list: List[ChapterIndex] = [ChapterIndex.MAFIA, ChapterIndex.BIRDS,
|
|
|
|
ChapterIndex.SUBCON, ChapterIndex.ALPINE]
|
|
|
|
|
|
|
|
final_chapter = ChapterIndex.FINALE
|
|
|
|
if world.options.EndGoal == EndGoal.option_rush_hour:
|
|
|
|
final_chapter = ChapterIndex.METRO
|
|
|
|
chapter_list.append(ChapterIndex.FINALE)
|
|
|
|
elif world.options.EndGoal == EndGoal.option_seal_the_deal:
|
|
|
|
final_chapter = None
|
|
|
|
chapter_list.append(ChapterIndex.FINALE)
|
|
|
|
|
|
|
|
if world.is_dlc1():
|
|
|
|
chapter_list.append(ChapterIndex.CRUISE)
|
|
|
|
|
2024-07-27 17:16:52 +00:00
|
|
|
if world.is_dlc2() and final_chapter != ChapterIndex.METRO:
|
2024-05-20 07:04:06 +00:00
|
|
|
chapter_list.append(ChapterIndex.METRO)
|
|
|
|
|
|
|
|
chapter_list.remove(starting_chapter)
|
|
|
|
world.random.shuffle(chapter_list)
|
|
|
|
|
|
|
|
# Make sure Alpine is unlocked before any DLC chapters are, as the Alpine door needs to be open to access them
|
2024-07-27 17:16:52 +00:00
|
|
|
if starting_chapter != ChapterIndex.ALPINE and (world.is_dlc1() or world.is_dlc2()):
|
2024-05-20 07:04:06 +00:00
|
|
|
index1 = 69
|
|
|
|
index2 = 69
|
|
|
|
pos: int
|
|
|
|
lowest_index: int
|
|
|
|
chapter_list.remove(ChapterIndex.ALPINE)
|
|
|
|
|
|
|
|
if world.is_dlc1():
|
|
|
|
index1 = chapter_list.index(ChapterIndex.CRUISE)
|
|
|
|
|
2024-07-27 17:16:52 +00:00
|
|
|
if world.is_dlc2() and final_chapter != ChapterIndex.METRO:
|
2024-05-20 07:04:06 +00:00
|
|
|
index2 = chapter_list.index(ChapterIndex.METRO)
|
|
|
|
|
|
|
|
lowest_index = min(index1, index2)
|
|
|
|
if lowest_index == 0:
|
|
|
|
pos = 0
|
|
|
|
else:
|
|
|
|
pos = world.random.randint(0, lowest_index)
|
|
|
|
|
|
|
|
chapter_list.insert(pos, ChapterIndex.ALPINE)
|
|
|
|
|
|
|
|
lowest_cost: int = world.options.LowestChapterCost.value
|
|
|
|
highest_cost: int = world.options.HighestChapterCost.value
|
|
|
|
cost_increment: int = world.options.ChapterCostIncrement.value
|
|
|
|
min_difference: int = world.options.ChapterCostMinDifference.value
|
|
|
|
last_cost = 0
|
|
|
|
|
|
|
|
for i, chapter in enumerate(chapter_list):
|
|
|
|
min_range: int = lowest_cost + (cost_increment * i)
|
|
|
|
if min_range >= highest_cost:
|
|
|
|
min_range = highest_cost-1
|
|
|
|
|
|
|
|
value: int = world.random.randint(min_range, min(highest_cost, max(lowest_cost, last_cost + cost_increment)))
|
|
|
|
cost = world.random.randint(value, min(value + cost_increment, highest_cost))
|
|
|
|
if i >= 1:
|
|
|
|
if last_cost + min_difference > cost:
|
|
|
|
cost = last_cost + min_difference
|
|
|
|
|
|
|
|
cost = min(cost, highest_cost)
|
|
|
|
world.chapter_timepiece_costs[chapter] = cost
|
|
|
|
last_cost = cost
|
|
|
|
|
|
|
|
if final_chapter is not None:
|
|
|
|
final_chapter_cost: int
|
|
|
|
if world.options.FinalChapterMinCost == world.options.FinalChapterMaxCost:
|
|
|
|
final_chapter_cost = world.options.FinalChapterMaxCost.value
|
|
|
|
else:
|
|
|
|
final_chapter_cost = world.random.randint(world.options.FinalChapterMinCost.value,
|
|
|
|
world.options.FinalChapterMaxCost.value)
|
|
|
|
|
|
|
|
world.chapter_timepiece_costs[final_chapter] = final_chapter_cost
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_entrance("Telescope -> Mafia Town", world.player),
|
|
|
|
lambda state: state.has("Time Piece", world.player, world.chapter_timepiece_costs[ChapterIndex.MAFIA]))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_entrance("Telescope -> Battle of the Birds", world.player),
|
|
|
|
lambda state: state.has("Time Piece", world.player, world.chapter_timepiece_costs[ChapterIndex.BIRDS]))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_entrance("Telescope -> Subcon Forest", world.player),
|
|
|
|
lambda state: state.has("Time Piece", world.player, world.chapter_timepiece_costs[ChapterIndex.SUBCON]))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_entrance("Telescope -> Alpine Skyline", world.player),
|
|
|
|
lambda state: state.has("Time Piece", world.player, world.chapter_timepiece_costs[ChapterIndex.ALPINE]))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_entrance("Telescope -> Time's End", world.player),
|
|
|
|
lambda state: state.has("Time Piece", world.player, world.chapter_timepiece_costs[ChapterIndex.FINALE])
|
|
|
|
and can_use_hat(state, world, HatType.BREWING) and can_use_hat(state, world, HatType.DWELLER))
|
|
|
|
|
|
|
|
if world.is_dlc1():
|
|
|
|
add_rule(world.multiworld.get_entrance("Telescope -> Arctic Cruise", world.player),
|
|
|
|
lambda state: state.has("Time Piece", world.player, world.chapter_timepiece_costs[ChapterIndex.ALPINE])
|
|
|
|
and state.has("Time Piece", world.player, world.chapter_timepiece_costs[ChapterIndex.CRUISE]))
|
|
|
|
|
|
|
|
if world.is_dlc2():
|
|
|
|
add_rule(world.multiworld.get_entrance("Telescope -> Nyakuza Metro", world.player),
|
|
|
|
lambda state: state.has("Time Piece", world.player, world.chapter_timepiece_costs[ChapterIndex.ALPINE])
|
|
|
|
and state.has("Time Piece", world.player, world.chapter_timepiece_costs[ChapterIndex.METRO])
|
|
|
|
and can_use_hat(state, world, HatType.DWELLER) and can_use_hat(state, world, HatType.ICE))
|
|
|
|
|
|
|
|
if not world.options.ActRandomizer:
|
|
|
|
set_default_rift_rules(world)
|
|
|
|
|
|
|
|
table = {**location_table, **event_locs}
|
|
|
|
for (key, data) in table.items():
|
|
|
|
if not is_location_valid(world, key):
|
|
|
|
continue
|
|
|
|
|
|
|
|
loc = world.multiworld.get_location(key, world.player)
|
|
|
|
|
|
|
|
for hat in data.required_hats:
|
|
|
|
add_rule(loc, lambda state, h=hat: can_use_hat(state, world, h))
|
|
|
|
|
|
|
|
if data.hookshot:
|
|
|
|
add_rule(loc, lambda state: can_use_hookshot(state, world))
|
|
|
|
|
|
|
|
if data.paintings > 0 and world.options.ShuffleSubconPaintings:
|
|
|
|
add_rule(loc, lambda state, paintings=data.paintings: has_paintings(state, world, paintings))
|
|
|
|
|
2024-07-27 17:16:52 +00:00
|
|
|
if data.hit_type != HitType.none and world.options.UmbrellaLogic:
|
2024-05-20 07:04:06 +00:00
|
|
|
if data.hit_type == HitType.umbrella:
|
|
|
|
add_rule(loc, lambda state: state.has("Umbrella", world.player))
|
|
|
|
|
|
|
|
elif data.hit_type == HitType.umbrella_or_brewing:
|
|
|
|
add_rule(loc, lambda state: state.has("Umbrella", world.player)
|
|
|
|
or can_use_hat(state, world, HatType.BREWING))
|
|
|
|
|
|
|
|
elif data.hit_type == HitType.dweller_bell:
|
|
|
|
add_rule(loc, lambda state: state.has("Umbrella", world.player)
|
|
|
|
or can_use_hat(state, world, HatType.BREWING)
|
|
|
|
or can_use_hat(state, world, HatType.DWELLER))
|
|
|
|
|
|
|
|
for misc in data.misc_required:
|
|
|
|
add_rule(loc, lambda state, item=misc: state.has(item, world.player))
|
|
|
|
|
|
|
|
set_specific_rules(world)
|
|
|
|
|
|
|
|
# Putting all of this here, so it doesn't get overridden by anything
|
|
|
|
# Illness starts the player past the intro
|
|
|
|
alpine_entrance = world.multiworld.get_entrance("AFR -> Alpine Skyline Area", world.player)
|
|
|
|
add_rule(alpine_entrance, lambda state: can_use_hookshot(state, world))
|
|
|
|
if world.options.UmbrellaLogic:
|
|
|
|
add_rule(alpine_entrance, lambda state: state.has("Umbrella", world.player))
|
|
|
|
|
|
|
|
if zipline_logic(world):
|
|
|
|
add_rule(world.multiworld.get_entrance("-> The Birdhouse", world.player),
|
|
|
|
lambda state: state.has("Zipline Unlock - The Birdhouse Path", world.player))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_entrance("-> The Lava Cake", world.player),
|
|
|
|
lambda state: state.has("Zipline Unlock - The Lava Cake Path", world.player))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_entrance("-> The Windmill", world.player),
|
|
|
|
lambda state: state.has("Zipline Unlock - The Windmill Path", world.player))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_entrance("-> The Twilight Bell", world.player),
|
|
|
|
lambda state: state.has("Zipline Unlock - The Twilight Bell Path", world.player))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_location("Act Completion (The Illness has Spread)", world.player),
|
|
|
|
lambda state: state.has("Zipline Unlock - The Birdhouse Path", world.player)
|
|
|
|
and state.has("Zipline Unlock - The Lava Cake Path", world.player)
|
|
|
|
and state.has("Zipline Unlock - The Windmill Path", world.player))
|
|
|
|
|
|
|
|
if zipline_logic(world):
|
|
|
|
for (loc, zipline) in zipline_unlocks.items():
|
|
|
|
add_rule(world.multiworld.get_location(loc, world.player),
|
|
|
|
lambda state, z=zipline: state.has(z, world.player))
|
|
|
|
|
|
|
|
dummy_entrances: List[Entrance] = []
|
|
|
|
|
|
|
|
for (key, acts) in act_connections.items():
|
|
|
|
if "Arctic Cruise" in key and not world.is_dlc1():
|
|
|
|
continue
|
|
|
|
|
|
|
|
entrance: Entrance = world.multiworld.get_entrance(key, world.player)
|
|
|
|
region: Region = entrance.connected_region
|
|
|
|
access_rules: List[Callable[[CollectionState], bool]] = []
|
|
|
|
dummy_entrances.append(entrance)
|
|
|
|
|
|
|
|
# Entrances to this act that we have to set access_rules on
|
|
|
|
entrances: List[Entrance] = []
|
|
|
|
|
|
|
|
for i, act in enumerate(acts, start=1):
|
|
|
|
act_entrance: Entrance = world.multiworld.get_entrance(act, world.player)
|
|
|
|
access_rules.append(act_entrance.access_rule)
|
|
|
|
required_region = act_entrance.connected_region
|
|
|
|
name: str = f"{key}: Connection {i}"
|
|
|
|
new_entrance: Entrance = required_region.connect(region, name)
|
|
|
|
entrances.append(new_entrance)
|
|
|
|
|
|
|
|
# Copy access rules from act completions
|
|
|
|
if "Free Roam" not in required_region.name:
|
|
|
|
rule: Callable[[CollectionState], bool]
|
|
|
|
name = f"Act Completion ({required_region.name})"
|
|
|
|
rule = world.multiworld.get_location(name, world.player).access_rule
|
|
|
|
access_rules.append(rule)
|
|
|
|
|
|
|
|
for e in entrances:
|
|
|
|
for rules in access_rules:
|
|
|
|
add_rule(e, rules)
|
|
|
|
|
|
|
|
for e in dummy_entrances:
|
|
|
|
set_rule(e, lambda state: False)
|
|
|
|
|
|
|
|
set_event_rules(world)
|
|
|
|
|
|
|
|
if world.options.EndGoal == EndGoal.option_finale:
|
|
|
|
world.multiworld.completion_condition[world.player] = lambda state: state.has("Time Piece Cluster", world.player)
|
|
|
|
elif world.options.EndGoal == EndGoal.option_rush_hour:
|
|
|
|
world.multiworld.completion_condition[world.player] = lambda state: state.has("Rush Hour Cleared", world.player)
|
|
|
|
|
|
|
|
|
|
|
|
def set_specific_rules(world: "HatInTimeWorld"):
|
|
|
|
add_rule(world.multiworld.get_location("Mafia Boss Shop Item", world.player),
|
|
|
|
lambda state: state.has("Time Piece", world.player, 12)
|
|
|
|
and state.has("Time Piece", world.player, world.chapter_timepiece_costs[ChapterIndex.BIRDS]))
|
|
|
|
|
|
|
|
set_mafia_town_rules(world)
|
|
|
|
set_botb_rules(world)
|
|
|
|
set_subcon_rules(world)
|
|
|
|
set_alps_rules(world)
|
|
|
|
|
|
|
|
if world.is_dlc1():
|
|
|
|
set_dlc1_rules(world)
|
|
|
|
|
|
|
|
if world.is_dlc2():
|
|
|
|
set_dlc2_rules(world)
|
|
|
|
|
|
|
|
difficulty: Difficulty = get_difficulty(world)
|
|
|
|
|
|
|
|
if difficulty >= Difficulty.MODERATE:
|
|
|
|
set_moderate_rules(world)
|
|
|
|
|
|
|
|
if difficulty >= Difficulty.HARD:
|
|
|
|
set_hard_rules(world)
|
|
|
|
|
|
|
|
if difficulty >= Difficulty.EXPERT:
|
|
|
|
set_expert_rules(world)
|
|
|
|
|
|
|
|
|
|
|
|
def set_moderate_rules(world: "HatInTimeWorld"):
|
|
|
|
# Moderate: Gallery without Brewing Hat
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Time Rift - Gallery)", world.player), lambda state: True)
|
|
|
|
|
|
|
|
# Moderate: Above Boats via Ice Hat Sliding
|
|
|
|
add_rule(world.multiworld.get_location("Mafia Town - Above Boats", world.player),
|
|
|
|
lambda state: can_use_hat(state, world, HatType.ICE), "or")
|
|
|
|
|
|
|
|
# Moderate: Clock Tower Chest + Ruined Tower with nothing
|
2024-08-29 06:11:02 +00:00
|
|
|
set_rule(world.multiworld.get_location("Mafia Town - Clock Tower Chest", world.player), lambda state: True)
|
|
|
|
set_rule(world.multiworld.get_location("Mafia Town - Top of Ruined Tower", world.player), lambda state: True)
|
2024-05-20 07:04:06 +00:00
|
|
|
|
|
|
|
# Moderate: enter and clear The Subcon Well without Hookshot and without hitting the bell
|
|
|
|
for loc in world.multiworld.get_region("The Subcon Well", world.player).locations:
|
|
|
|
set_rule(loc, lambda state: has_paintings(state, world, 1))
|
|
|
|
|
|
|
|
# Moderate: Vanessa Manor with nothing
|
|
|
|
for loc in world.multiworld.get_region("Queen Vanessa's Manor", world.player).locations:
|
|
|
|
set_rule(loc, lambda state: has_paintings(state, world, 1))
|
|
|
|
|
|
|
|
set_rule(world.multiworld.get_location("Subcon Forest - Manor Rooftop", world.player),
|
|
|
|
lambda state: has_paintings(state, world, 1))
|
|
|
|
|
|
|
|
# Moderate: Village Time Rift with nothing IF umbrella logic is off
|
|
|
|
if not world.options.UmbrellaLogic:
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Time Rift - Village)", world.player), lambda state: True)
|
|
|
|
|
|
|
|
# Moderate: get to Birdhouse/Yellow Band Hills without Brewing Hat
|
|
|
|
set_rule(world.multiworld.get_entrance("-> The Birdhouse", world.player),
|
|
|
|
lambda state: can_use_hookshot(state, world))
|
|
|
|
set_rule(world.multiworld.get_location("Alpine Skyline - Yellow Band Hills", world.player),
|
|
|
|
lambda state: can_use_hookshot(state, world))
|
|
|
|
|
|
|
|
# Moderate: The Birdhouse - Dweller Platforms Relic with only Birdhouse access
|
|
|
|
set_rule(world.multiworld.get_location("Alpine Skyline - The Birdhouse: Dweller Platforms Relic", world.player),
|
|
|
|
lambda state: True)
|
|
|
|
|
|
|
|
# Moderate: Twilight Path without Dweller Mask
|
|
|
|
set_rule(world.multiworld.get_location("Alpine Skyline - The Twilight Path", world.player), lambda state: True)
|
|
|
|
|
|
|
|
# Moderate: Mystifying Time Mesa time trial without hats
|
|
|
|
set_rule(world.multiworld.get_location("Alpine Skyline - Mystifying Time Mesa: Zipline", world.player),
|
AHiT: Various logic fixes (#4492)
* Fix Director boss photo logic
The rules were being added to for the "Director" boss in
`set_enemy_rules()`, which didn't exist because the boss created was
called "Conductor" instead.
The name of the boss has been changed to "Director", to match, because
it is more accurate due to DJ Grooves possibly being the boss instead of
The Conductor.
The missing logic was the `Hookshot Badge` requirement, however, the
boss events are only used as part of the `Camera Tourist - All Clear`
location, which requires every boss event to be reachable, and the
Toxic Flower boss also has a `Hookshot Badge` requirement, so the
missing `Hookshot Badge` for the Director boss had no effect on logic.
The boss event locations are hidden from spoiler output, so to get a
spoiler showing the Director boss event accessed before having
`Hookshot Badge`, spoiler output had to be modified to also show the
hidden locations. Example sphere from playthrough that should not be
possible because it gets the `Hookshot Badge` and the `Conductor` event
(now renamed to `Director`) in the same sphere:
```
5: {
Act Completion (Time Rift - Dead Bird Studio): Relic (Crayon Box)
Conductor - Dead Bird Studio Basement: Conductor
Dead Bird Studio (Rift) - Page: Behind Cardboard Planet: Time Piece
Dead Bird Studio (Rift) - Page: Near Time Rift Gate: Hookshot Badge
Picture Perfect - Hats Buy Building: Metro Ticket - Blue
Snatcher - Your Contract has Expired: Snatcher
}
```
* Add missing Hookshot + Painting logic for Toilet boss picture
Includes the Hard logic of crossing the gap with a cherry bridge instead
of hookshot and the expert logic of being able to skip the boss firewall
with a cherry hover.
* Fix Alpine Skyline - Goat Outpost Horn region
`Alpine Skyline - Goat Outpost Horn` is accessible from The Illness has
Spread, but was being added to the region that is only accessible from
Alpine Free Roam. `Alpine Skyline - Goat Outpost Horn` has been moved to
the region that is accessible from both The Illness has Spread and
Alpine Free Roam.
* Add missing HitType.umbrella logic for Top of HQ Coin in Beat the Heat
Like Heating up Mafia Town, the cannon to the Mafia HQ area only opens
once all the faucets have been turned off by hitting them. This requires
the Umbrella when umbrella logic is enabled, but the Snatcher Coin on
top of Mafia HQ was missing this requirement when accessed from Beat the
Heat.
* Add missing Main Objective requirement for auto-completed Bonus Stamps
When a Main Objective is not excluded, but the bonuses are excluded, the
bonuses auto-complete once the Main Objective is completed. The
requirement to complete the Main Objective was missing, so the logic was
incorrectly awarding bonus stamps as soon as a Contract was unlocked,
even when it was not possible to complete the Main Objective of that
Contract.
* Add missing Hookshot requirement for The Arctic Cruise - Toilet from Bon Voyage!
`The Arctic Cruise - Toilet` is accessed from the `Cruise Ship` region,
but it is only present in the Ship Shape and Bon Voyage! acts.
Ship Shape and Rock the Boat can access `Cruise Ship` without any items,
but Bon Voyage! requires the Hookshot Badge to reach `Cruise Ship`.
With how the logic was set up, it was incorrectly giving access to
`The Arctic Cruise - Toilet` if the player had access to Bon Voyage!
but only had access to `Cruise Ship` through Rock the Boat.
* Fix Expert logic Rush Hour-only ticket skips
The code was checking `if not world.options.NoTicketSkips:`, but that
would only be `True` for `False`. For "rush_hour" (for Rush Hour-only
ticket skips), it would be `False`, causing Rush Hour-only ticket skips
to act as if ticket skips were disabled.
* Remove Mystifying Time Mesa: Zipline gaining Hookshot requirement in moderate logic
Alpine Skyline - Mystifying Time Mesa: Zipline does not normally
require Hookshot Badge because it is an implied requirement due to only
being accessible from Alpine Free Roam which does require Hookshot
Badge. In normal logic difficulty, the location does not have an
explicit Hookshot Badge requirement, but moderate logic was adding a
Hookshot Badge requirement. This extraneous Hookshot Badge requirement
has been removed.
* Fix Act Completion (Queen Vanessa's Manor) not being accessible with Dweller Mask/Brewing Hat
It was logically requiring the Umbrella hit type only, whereas all the
other locations in Queen Vanessa's Manor require the Dweller Bell hit
type which additionally allows Dweller Mask or Brewing Hat.
* Remove Dweller Mask requirement for Subcon Forest - Tall Tree Hookshot Swing
The Dweller Mask is not used in the intended vanilla route to get this
item, so this requirement seems to have been a mistake.
* Remove unused SDJ option for Subcon Forest - Long Tree Climb Chest
Hard logic can already reach this location with nothing (other than
paintings), so the "or" logic of being able to perform an SDJ was
unused.
* Require any non-HUMT Mafia Town act for Hot Air Balloon with nothing
Two buckets/beach balls are required to bucket/ball hover, but there is
only a single beach ball accessible in Heating Up Mafia Town, and
no accessible buckets.
There is an alternative strategy for Top of Lighthouse that only
requires a single beach ball, so that location can still be reached with
nothing from Heating Up Mafia Town.
* Use `get_difficulty()` helper in `set_enemy_rules`
Co-authored-by: Exempt-Medic <60412657+exempt-medic@users.noreply.github.com>
---------
Co-authored-by: Exempt-Medic <60412657+exempt-medic@users.noreply.github.com>
2025-01-17 02:10:41 +00:00
|
|
|
lambda state: True)
|
2024-05-20 07:04:06 +00:00
|
|
|
|
|
|
|
# Moderate: Goat Refinery from TIHS with Sprint only
|
|
|
|
add_rule(world.multiworld.get_location("Alpine Skyline - Goat Refinery", world.player),
|
|
|
|
lambda state: state.has("TIHS Access", world.player)
|
|
|
|
and can_use_hat(state, world, HatType.SPRINT), "or")
|
|
|
|
|
|
|
|
# Moderate: Finale Telescope with only Ice Hat
|
|
|
|
add_rule(world.multiworld.get_entrance("Telescope -> Time's End", world.player),
|
|
|
|
lambda state: state.has("Time Piece", world.player, world.chapter_timepiece_costs[ChapterIndex.FINALE])
|
|
|
|
and can_use_hat(state, world, HatType.ICE), "or")
|
|
|
|
|
|
|
|
# Moderate: Finale without Hookshot
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (The Finale)", world.player),
|
|
|
|
lambda state: can_use_hat(state, world, HatType.DWELLER))
|
|
|
|
|
|
|
|
if world.is_dlc1():
|
|
|
|
# Moderate: clear Rock the Boat without Ice Hat
|
2024-08-29 06:11:02 +00:00
|
|
|
set_rule(world.multiworld.get_location("Rock the Boat - Post Captain Rescue", world.player), lambda state: True)
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Rock the Boat)", world.player), lambda state: True)
|
2024-05-20 07:04:06 +00:00
|
|
|
|
|
|
|
# Moderate: clear Deep Sea without Ice Hat
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Time Rift - Deep Sea)", world.player),
|
|
|
|
lambda state: can_use_hookshot(state, world) and can_use_hat(state, world, HatType.DWELLER))
|
|
|
|
|
|
|
|
# There is a glitched fall damage volume near the Yellow Overpass time piece that warps the player to Pink Paw.
|
|
|
|
# Yellow Overpass time piece can also be reached without Hookshot quite easily.
|
|
|
|
if world.is_dlc2():
|
|
|
|
# No Hookshot
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Yellow Overpass Station)", world.player),
|
|
|
|
lambda state: True)
|
|
|
|
|
|
|
|
# No Dweller, Hookshot, or Time Stop for these
|
|
|
|
set_rule(world.multiworld.get_location("Pink Paw Station - Cat Vacuum", world.player), lambda state: True)
|
|
|
|
set_rule(world.multiworld.get_location("Pink Paw Station - Behind Fan", world.player), lambda state: True)
|
|
|
|
set_rule(world.multiworld.get_location("Pink Paw Station - Pink Ticket Booth", world.player), lambda state: True)
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Pink Paw Station)", world.player), lambda state: True)
|
|
|
|
for key in shop_locations.keys():
|
|
|
|
if "Pink Paw Station Thug" in key and is_location_valid(world, key):
|
|
|
|
set_rule(world.multiworld.get_location(key, world.player), lambda state: True)
|
|
|
|
|
|
|
|
# Moderate: clear Rush Hour without Hookshot
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Rush Hour)", world.player),
|
|
|
|
lambda state: state.has("Metro Ticket - Pink", world.player)
|
|
|
|
and state.has("Metro Ticket - Yellow", world.player)
|
|
|
|
and state.has("Metro Ticket - Blue", world.player)
|
|
|
|
and can_use_hat(state, world, HatType.ICE)
|
|
|
|
and can_use_hat(state, world, HatType.BREWING))
|
|
|
|
|
|
|
|
# Moderate: Bluefin Tunnel + Pink Paw Station without tickets
|
|
|
|
if not world.options.NoTicketSkips:
|
|
|
|
set_rule(world.multiworld.get_entrance("-> Pink Paw Station", world.player), lambda state: True)
|
|
|
|
set_rule(world.multiworld.get_entrance("-> Bluefin Tunnel", world.player), lambda state: True)
|
|
|
|
|
|
|
|
|
|
|
|
def set_hard_rules(world: "HatInTimeWorld"):
|
|
|
|
# Hard: clear Time Rift - The Twilight Bell with Sprint+Scooter only
|
|
|
|
add_rule(world.multiworld.get_location("Act Completion (Time Rift - The Twilight Bell)", world.player),
|
|
|
|
lambda state: can_use_hat(state, world, HatType.SPRINT)
|
|
|
|
and state.has("Scooter Badge", world.player), "or")
|
|
|
|
|
|
|
|
# No Dweller Mask required
|
|
|
|
set_rule(world.multiworld.get_location("Subcon Forest - Dweller Floating Rocks", world.player),
|
|
|
|
lambda state: has_paintings(state, world, 3))
|
|
|
|
set_rule(world.multiworld.get_location("Subcon Forest - Dweller Platforming Tree B", world.player),
|
|
|
|
lambda state: has_paintings(state, world, 3))
|
|
|
|
|
|
|
|
# Cherry bridge over boss arena gap (painting still expected)
|
|
|
|
set_rule(world.multiworld.get_location("Subcon Forest - Boss Arena Chest", world.player),
|
|
|
|
lambda state: has_paintings(state, world, 1, False) or state.has("YCHE Access", world.player))
|
|
|
|
|
|
|
|
set_rule(world.multiworld.get_location("Subcon Forest - Noose Treehouse", world.player),
|
|
|
|
lambda state: has_paintings(state, world, 2, True))
|
|
|
|
set_rule(world.multiworld.get_location("Subcon Forest - Long Tree Climb Chest", world.player),
|
|
|
|
lambda state: has_paintings(state, world, 2, True))
|
|
|
|
set_rule(world.multiworld.get_location("Subcon Forest - Tall Tree Hookshot Swing", world.player),
|
|
|
|
lambda state: has_paintings(state, world, 3, True))
|
|
|
|
|
|
|
|
# SDJ
|
|
|
|
add_rule(world.multiworld.get_location("Act Completion (Time Rift - Curly Tail Trail)", world.player),
|
|
|
|
lambda state: can_use_hat(state, world, HatType.SPRINT), "or")
|
|
|
|
|
|
|
|
# Hard: Goat Refinery from TIHS with nothing
|
|
|
|
add_rule(world.multiworld.get_location("Alpine Skyline - Goat Refinery", world.player),
|
|
|
|
lambda state: state.has("TIHS Access", world.player), "or")
|
|
|
|
|
|
|
|
if world.is_dlc1():
|
|
|
|
# Hard: clear Deep Sea without Dweller Mask
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Time Rift - Deep Sea)", world.player),
|
|
|
|
lambda state: can_use_hookshot(state, world))
|
|
|
|
|
|
|
|
if world.is_dlc2():
|
|
|
|
# Hard: clear Green Clean Manhole without Dweller Mask
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Green Clean Manhole)", world.player),
|
|
|
|
lambda state: can_use_hat(state, world, HatType.ICE))
|
|
|
|
|
|
|
|
# Hard: clear Rush Hour with Brewing Hat only
|
2024-07-27 17:16:52 +00:00
|
|
|
if world.options.NoTicketSkips != NoTicketSkips.option_true:
|
2024-05-20 07:04:06 +00:00
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Rush Hour)", world.player),
|
|
|
|
lambda state: can_use_hat(state, world, HatType.BREWING))
|
|
|
|
else:
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Rush Hour)", world.player),
|
|
|
|
lambda state: can_use_hat(state, world, HatType.BREWING)
|
|
|
|
and state.has("Metro Ticket - Yellow", world.player)
|
|
|
|
and state.has("Metro Ticket - Blue", world.player)
|
|
|
|
and state.has("Metro Ticket - Pink", world.player))
|
|
|
|
|
|
|
|
|
|
|
|
def set_expert_rules(world: "HatInTimeWorld"):
|
|
|
|
# Finale Telescope with no hats
|
|
|
|
set_rule(world.multiworld.get_entrance("Telescope -> Time's End", world.player),
|
|
|
|
lambda state: state.has("Time Piece", world.player, world.chapter_timepiece_costs[ChapterIndex.FINALE]))
|
|
|
|
|
|
|
|
# Expert: Mafia Town - Above Boats, Top of Lighthouse, and Hot Air Balloon with nothing
|
|
|
|
set_rule(world.multiworld.get_location("Mafia Town - Above Boats", world.player), lambda state: True)
|
|
|
|
set_rule(world.multiworld.get_location("Mafia Town - Top of Lighthouse", world.player), lambda state: True)
|
AHiT: Various logic fixes (#4492)
* Fix Director boss photo logic
The rules were being added to for the "Director" boss in
`set_enemy_rules()`, which didn't exist because the boss created was
called "Conductor" instead.
The name of the boss has been changed to "Director", to match, because
it is more accurate due to DJ Grooves possibly being the boss instead of
The Conductor.
The missing logic was the `Hookshot Badge` requirement, however, the
boss events are only used as part of the `Camera Tourist - All Clear`
location, which requires every boss event to be reachable, and the
Toxic Flower boss also has a `Hookshot Badge` requirement, so the
missing `Hookshot Badge` for the Director boss had no effect on logic.
The boss event locations are hidden from spoiler output, so to get a
spoiler showing the Director boss event accessed before having
`Hookshot Badge`, spoiler output had to be modified to also show the
hidden locations. Example sphere from playthrough that should not be
possible because it gets the `Hookshot Badge` and the `Conductor` event
(now renamed to `Director`) in the same sphere:
```
5: {
Act Completion (Time Rift - Dead Bird Studio): Relic (Crayon Box)
Conductor - Dead Bird Studio Basement: Conductor
Dead Bird Studio (Rift) - Page: Behind Cardboard Planet: Time Piece
Dead Bird Studio (Rift) - Page: Near Time Rift Gate: Hookshot Badge
Picture Perfect - Hats Buy Building: Metro Ticket - Blue
Snatcher - Your Contract has Expired: Snatcher
}
```
* Add missing Hookshot + Painting logic for Toilet boss picture
Includes the Hard logic of crossing the gap with a cherry bridge instead
of hookshot and the expert logic of being able to skip the boss firewall
with a cherry hover.
* Fix Alpine Skyline - Goat Outpost Horn region
`Alpine Skyline - Goat Outpost Horn` is accessible from The Illness has
Spread, but was being added to the region that is only accessible from
Alpine Free Roam. `Alpine Skyline - Goat Outpost Horn` has been moved to
the region that is accessible from both The Illness has Spread and
Alpine Free Roam.
* Add missing HitType.umbrella logic for Top of HQ Coin in Beat the Heat
Like Heating up Mafia Town, the cannon to the Mafia HQ area only opens
once all the faucets have been turned off by hitting them. This requires
the Umbrella when umbrella logic is enabled, but the Snatcher Coin on
top of Mafia HQ was missing this requirement when accessed from Beat the
Heat.
* Add missing Main Objective requirement for auto-completed Bonus Stamps
When a Main Objective is not excluded, but the bonuses are excluded, the
bonuses auto-complete once the Main Objective is completed. The
requirement to complete the Main Objective was missing, so the logic was
incorrectly awarding bonus stamps as soon as a Contract was unlocked,
even when it was not possible to complete the Main Objective of that
Contract.
* Add missing Hookshot requirement for The Arctic Cruise - Toilet from Bon Voyage!
`The Arctic Cruise - Toilet` is accessed from the `Cruise Ship` region,
but it is only present in the Ship Shape and Bon Voyage! acts.
Ship Shape and Rock the Boat can access `Cruise Ship` without any items,
but Bon Voyage! requires the Hookshot Badge to reach `Cruise Ship`.
With how the logic was set up, it was incorrectly giving access to
`The Arctic Cruise - Toilet` if the player had access to Bon Voyage!
but only had access to `Cruise Ship` through Rock the Boat.
* Fix Expert logic Rush Hour-only ticket skips
The code was checking `if not world.options.NoTicketSkips:`, but that
would only be `True` for `False`. For "rush_hour" (for Rush Hour-only
ticket skips), it would be `False`, causing Rush Hour-only ticket skips
to act as if ticket skips were disabled.
* Remove Mystifying Time Mesa: Zipline gaining Hookshot requirement in moderate logic
Alpine Skyline - Mystifying Time Mesa: Zipline does not normally
require Hookshot Badge because it is an implied requirement due to only
being accessible from Alpine Free Roam which does require Hookshot
Badge. In normal logic difficulty, the location does not have an
explicit Hookshot Badge requirement, but moderate logic was adding a
Hookshot Badge requirement. This extraneous Hookshot Badge requirement
has been removed.
* Fix Act Completion (Queen Vanessa's Manor) not being accessible with Dweller Mask/Brewing Hat
It was logically requiring the Umbrella hit type only, whereas all the
other locations in Queen Vanessa's Manor require the Dweller Bell hit
type which additionally allows Dweller Mask or Brewing Hat.
* Remove Dweller Mask requirement for Subcon Forest - Tall Tree Hookshot Swing
The Dweller Mask is not used in the intended vanilla route to get this
item, so this requirement seems to have been a mistake.
* Remove unused SDJ option for Subcon Forest - Long Tree Climb Chest
Hard logic can already reach this location with nothing (other than
paintings), so the "or" logic of being able to perform an SDJ was
unused.
* Require any non-HUMT Mafia Town act for Hot Air Balloon with nothing
Two buckets/beach balls are required to bucket/ball hover, but there is
only a single beach ball accessible in Heating Up Mafia Town, and
no accessible buckets.
There is an alternative strategy for Top of Lighthouse that only
requires a single beach ball, so that location can still be reached with
nothing from Heating Up Mafia Town.
* Use `get_difficulty()` helper in `set_enemy_rules`
Co-authored-by: Exempt-Medic <60412657+exempt-medic@users.noreply.github.com>
---------
Co-authored-by: Exempt-Medic <60412657+exempt-medic@users.noreply.github.com>
2025-01-17 02:10:41 +00:00
|
|
|
# There are not enough buckets/beach balls to bucket/ball hover in Heating Up Mafia Town, so any other Mafia Town
|
|
|
|
# act is required.
|
|
|
|
add_rule(world.multiworld.get_location("Mafia Town - Hot Air Balloon", world.player),
|
|
|
|
lambda state: state.can_reach_region("Mafia Town Area", world.player), "or")
|
2024-05-20 07:04:06 +00:00
|
|
|
|
|
|
|
# Expert: Clear Dead Bird Studio with nothing
|
|
|
|
for loc in world.multiworld.get_region("Dead Bird Studio - Post Elevator Area", world.player).locations:
|
|
|
|
set_rule(loc, lambda state: True)
|
|
|
|
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Dead Bird Studio)", world.player), lambda state: True)
|
|
|
|
|
|
|
|
# Expert: Clear Dead Bird Studio Basement without Hookshot
|
|
|
|
for loc in world.multiworld.get_region("Dead Bird Studio Basement", world.player).locations:
|
|
|
|
set_rule(loc, lambda state: True)
|
|
|
|
|
|
|
|
# Expert: get to and clear Twilight Bell without Dweller Mask.
|
|
|
|
# Dweller Mask OR Sprint Hat OR Brewing Hat OR Time Stop + Umbrella required to complete act.
|
|
|
|
add_rule(world.multiworld.get_entrance("-> The Twilight Bell", world.player),
|
|
|
|
lambda state: can_use_hookshot(state, world), "or")
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_location("Act Completion (The Twilight Bell)", world.player),
|
|
|
|
lambda state: can_use_hat(state, world, HatType.BREWING)
|
|
|
|
or can_use_hat(state, world, HatType.DWELLER)
|
|
|
|
or can_use_hat(state, world, HatType.SPRINT)
|
|
|
|
or (can_use_hat(state, world, HatType.TIME_STOP) and state.has("Umbrella", world.player)))
|
|
|
|
|
|
|
|
# Expert: Time Rift - Curly Tail Trail with nothing
|
|
|
|
# Time Rift - Twilight Bell and Time Rift - Village with nothing
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Time Rift - Curly Tail Trail)", world.player),
|
|
|
|
lambda state: True)
|
|
|
|
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Time Rift - Village)", world.player), lambda state: True)
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Time Rift - The Twilight Bell)", world.player),
|
|
|
|
lambda state: True)
|
|
|
|
|
|
|
|
# Expert: Cherry Hovering
|
|
|
|
subcon_area = world.multiworld.get_region("Subcon Forest Area", world.player)
|
|
|
|
yche = world.multiworld.get_region("Your Contract has Expired", world.player)
|
|
|
|
entrance = yche.connect(subcon_area, "Subcon Forest Entrance YCHE")
|
|
|
|
|
|
|
|
if world.options.NoPaintingSkips:
|
|
|
|
add_rule(entrance, lambda state: has_paintings(state, world, 1))
|
|
|
|
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Toilet of Doom)", world.player),
|
|
|
|
lambda state: can_use_hookshot(state, world) and can_hit(state, world)
|
|
|
|
and has_paintings(state, world, 1, True))
|
|
|
|
|
|
|
|
# Set painting rules only. Skipping paintings is determined in has_paintings
|
|
|
|
set_rule(world.multiworld.get_location("Subcon Forest - Boss Arena Chest", world.player),
|
|
|
|
lambda state: has_paintings(state, world, 1, True))
|
|
|
|
set_rule(world.multiworld.get_location("Subcon Forest - Magnet Badge Bush", world.player),
|
|
|
|
lambda state: has_paintings(state, world, 3, True))
|
|
|
|
|
|
|
|
# You can cherry hover to Snatcher's post-fight cutscene, which completes the level without having to fight him
|
|
|
|
subcon_area.connect(yche, "Snatcher Hover")
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Your Contract has Expired)", world.player),
|
|
|
|
lambda state: True)
|
|
|
|
|
|
|
|
if world.is_dlc2():
|
|
|
|
# Expert: clear Rush Hour with nothing
|
AHiT: Various logic fixes (#4492)
* Fix Director boss photo logic
The rules were being added to for the "Director" boss in
`set_enemy_rules()`, which didn't exist because the boss created was
called "Conductor" instead.
The name of the boss has been changed to "Director", to match, because
it is more accurate due to DJ Grooves possibly being the boss instead of
The Conductor.
The missing logic was the `Hookshot Badge` requirement, however, the
boss events are only used as part of the `Camera Tourist - All Clear`
location, which requires every boss event to be reachable, and the
Toxic Flower boss also has a `Hookshot Badge` requirement, so the
missing `Hookshot Badge` for the Director boss had no effect on logic.
The boss event locations are hidden from spoiler output, so to get a
spoiler showing the Director boss event accessed before having
`Hookshot Badge`, spoiler output had to be modified to also show the
hidden locations. Example sphere from playthrough that should not be
possible because it gets the `Hookshot Badge` and the `Conductor` event
(now renamed to `Director`) in the same sphere:
```
5: {
Act Completion (Time Rift - Dead Bird Studio): Relic (Crayon Box)
Conductor - Dead Bird Studio Basement: Conductor
Dead Bird Studio (Rift) - Page: Behind Cardboard Planet: Time Piece
Dead Bird Studio (Rift) - Page: Near Time Rift Gate: Hookshot Badge
Picture Perfect - Hats Buy Building: Metro Ticket - Blue
Snatcher - Your Contract has Expired: Snatcher
}
```
* Add missing Hookshot + Painting logic for Toilet boss picture
Includes the Hard logic of crossing the gap with a cherry bridge instead
of hookshot and the expert logic of being able to skip the boss firewall
with a cherry hover.
* Fix Alpine Skyline - Goat Outpost Horn region
`Alpine Skyline - Goat Outpost Horn` is accessible from The Illness has
Spread, but was being added to the region that is only accessible from
Alpine Free Roam. `Alpine Skyline - Goat Outpost Horn` has been moved to
the region that is accessible from both The Illness has Spread and
Alpine Free Roam.
* Add missing HitType.umbrella logic for Top of HQ Coin in Beat the Heat
Like Heating up Mafia Town, the cannon to the Mafia HQ area only opens
once all the faucets have been turned off by hitting them. This requires
the Umbrella when umbrella logic is enabled, but the Snatcher Coin on
top of Mafia HQ was missing this requirement when accessed from Beat the
Heat.
* Add missing Main Objective requirement for auto-completed Bonus Stamps
When a Main Objective is not excluded, but the bonuses are excluded, the
bonuses auto-complete once the Main Objective is completed. The
requirement to complete the Main Objective was missing, so the logic was
incorrectly awarding bonus stamps as soon as a Contract was unlocked,
even when it was not possible to complete the Main Objective of that
Contract.
* Add missing Hookshot requirement for The Arctic Cruise - Toilet from Bon Voyage!
`The Arctic Cruise - Toilet` is accessed from the `Cruise Ship` region,
but it is only present in the Ship Shape and Bon Voyage! acts.
Ship Shape and Rock the Boat can access `Cruise Ship` without any items,
but Bon Voyage! requires the Hookshot Badge to reach `Cruise Ship`.
With how the logic was set up, it was incorrectly giving access to
`The Arctic Cruise - Toilet` if the player had access to Bon Voyage!
but only had access to `Cruise Ship` through Rock the Boat.
* Fix Expert logic Rush Hour-only ticket skips
The code was checking `if not world.options.NoTicketSkips:`, but that
would only be `True` for `False`. For "rush_hour" (for Rush Hour-only
ticket skips), it would be `False`, causing Rush Hour-only ticket skips
to act as if ticket skips were disabled.
* Remove Mystifying Time Mesa: Zipline gaining Hookshot requirement in moderate logic
Alpine Skyline - Mystifying Time Mesa: Zipline does not normally
require Hookshot Badge because it is an implied requirement due to only
being accessible from Alpine Free Roam which does require Hookshot
Badge. In normal logic difficulty, the location does not have an
explicit Hookshot Badge requirement, but moderate logic was adding a
Hookshot Badge requirement. This extraneous Hookshot Badge requirement
has been removed.
* Fix Act Completion (Queen Vanessa's Manor) not being accessible with Dweller Mask/Brewing Hat
It was logically requiring the Umbrella hit type only, whereas all the
other locations in Queen Vanessa's Manor require the Dweller Bell hit
type which additionally allows Dweller Mask or Brewing Hat.
* Remove Dweller Mask requirement for Subcon Forest - Tall Tree Hookshot Swing
The Dweller Mask is not used in the intended vanilla route to get this
item, so this requirement seems to have been a mistake.
* Remove unused SDJ option for Subcon Forest - Long Tree Climb Chest
Hard logic can already reach this location with nothing (other than
paintings), so the "or" logic of being able to perform an SDJ was
unused.
* Require any non-HUMT Mafia Town act for Hot Air Balloon with nothing
Two buckets/beach balls are required to bucket/ball hover, but there is
only a single beach ball accessible in Heating Up Mafia Town, and
no accessible buckets.
There is an alternative strategy for Top of Lighthouse that only
requires a single beach ball, so that location can still be reached with
nothing from Heating Up Mafia Town.
* Use `get_difficulty()` helper in `set_enemy_rules`
Co-authored-by: Exempt-Medic <60412657+exempt-medic@users.noreply.github.com>
---------
Co-authored-by: Exempt-Medic <60412657+exempt-medic@users.noreply.github.com>
2025-01-17 02:10:41 +00:00
|
|
|
if world.options.NoTicketSkips != NoTicketSkips.option_true:
|
2024-05-20 07:04:06 +00:00
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Rush Hour)", world.player), lambda state: True)
|
|
|
|
else:
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Rush Hour)", world.player),
|
|
|
|
lambda state: state.has("Metro Ticket - Yellow", world.player)
|
|
|
|
and state.has("Metro Ticket - Blue", world.player)
|
|
|
|
and state.has("Metro Ticket - Pink", world.player))
|
|
|
|
|
|
|
|
# Expert: Yellow/Green Manhole with nothing using a Boop Clip
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Yellow Overpass Manhole)", world.player),
|
|
|
|
lambda state: True)
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Green Clean Manhole)", world.player),
|
|
|
|
lambda state: True)
|
|
|
|
|
|
|
|
|
|
|
|
def set_mafia_town_rules(world: "HatInTimeWorld"):
|
|
|
|
add_rule(world.multiworld.get_location("Mafia Town - Behind HQ Chest", world.player),
|
|
|
|
lambda state: state.can_reach("Act Completion (Heating Up Mafia Town)", "Location", world.player)
|
|
|
|
or state.can_reach("Down with the Mafia!", "Region", world.player)
|
|
|
|
or state.can_reach("Cheating the Race", "Region", world.player)
|
|
|
|
or state.can_reach("The Golden Vault", "Region", world.player))
|
|
|
|
|
|
|
|
# Old guys don't appear in SCFOS
|
|
|
|
add_rule(world.multiworld.get_location("Mafia Town - Old Man (Steel Beams)", world.player),
|
|
|
|
lambda state: state.can_reach("Welcome to Mafia Town", "Region", world.player)
|
|
|
|
or state.can_reach("Barrel Battle", "Region", world.player)
|
|
|
|
or state.can_reach("Cheating the Race", "Region", world.player)
|
|
|
|
or state.can_reach("The Golden Vault", "Region", world.player)
|
|
|
|
or state.can_reach("Down with the Mafia!", "Region", world.player))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_location("Mafia Town - Old Man (Seaside Spaghetti)", world.player),
|
|
|
|
lambda state: state.can_reach("Welcome to Mafia Town", "Region", world.player)
|
|
|
|
or state.can_reach("Barrel Battle", "Region", world.player)
|
|
|
|
or state.can_reach("Cheating the Race", "Region", world.player)
|
|
|
|
or state.can_reach("The Golden Vault", "Region", world.player)
|
|
|
|
or state.can_reach("Down with the Mafia!", "Region", world.player))
|
|
|
|
|
|
|
|
# Only available outside She Came from Outer Space
|
|
|
|
add_rule(world.multiworld.get_location("Mafia Town - Mafia Geek Platform", world.player),
|
|
|
|
lambda state: state.can_reach("Welcome to Mafia Town", "Region", world.player)
|
|
|
|
or state.can_reach("Barrel Battle", "Region", world.player)
|
|
|
|
or state.can_reach("Down with the Mafia!", "Region", world.player)
|
|
|
|
or state.can_reach("Cheating the Race", "Region", world.player)
|
|
|
|
or state.can_reach("Heating Up Mafia Town", "Region", world.player)
|
|
|
|
or state.can_reach("The Golden Vault", "Region", world.player))
|
|
|
|
|
|
|
|
# Only available outside Down with the Mafia! (for some reason)
|
|
|
|
add_rule(world.multiworld.get_location("Mafia Town - On Scaffolding", world.player),
|
|
|
|
lambda state: state.can_reach("Welcome to Mafia Town", "Region", world.player)
|
|
|
|
or state.can_reach("Barrel Battle", "Region", world.player)
|
|
|
|
or state.can_reach("She Came from Outer Space", "Region", world.player)
|
|
|
|
or state.can_reach("Cheating the Race", "Region", world.player)
|
|
|
|
or state.can_reach("Heating Up Mafia Town", "Region", world.player)
|
|
|
|
or state.can_reach("The Golden Vault", "Region", world.player))
|
|
|
|
|
|
|
|
# For some reason, the brewing crate is removed in HUMT
|
|
|
|
add_rule(world.multiworld.get_location("Mafia Town - Secret Cave", world.player),
|
|
|
|
lambda state: state.has("HUMT Access", world.player), "or")
|
|
|
|
|
|
|
|
# Can bounce across the lava to get this without Hookshot (need to die though)
|
|
|
|
add_rule(world.multiworld.get_location("Mafia Town - Above Boats", world.player),
|
|
|
|
lambda state: state.has("HUMT Access", world.player), "or")
|
|
|
|
|
|
|
|
if world.options.CTRLogic == CTRLogic.option_nothing:
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Cheating the Race)", world.player), lambda state: True)
|
|
|
|
elif world.options.CTRLogic == CTRLogic.option_sprint:
|
|
|
|
add_rule(world.multiworld.get_location("Act Completion (Cheating the Race)", world.player),
|
|
|
|
lambda state: can_use_hat(state, world, HatType.SPRINT), "or")
|
|
|
|
elif world.options.CTRLogic == CTRLogic.option_scooter:
|
|
|
|
add_rule(world.multiworld.get_location("Act Completion (Cheating the Race)", world.player),
|
|
|
|
lambda state: can_use_hat(state, world, HatType.SPRINT)
|
|
|
|
and state.has("Scooter Badge", world.player), "or")
|
|
|
|
|
|
|
|
|
|
|
|
def set_botb_rules(world: "HatInTimeWorld"):
|
|
|
|
if not world.options.UmbrellaLogic and get_difficulty(world) < Difficulty.MODERATE:
|
|
|
|
set_rule(world.multiworld.get_location("Dead Bird Studio - DJ Grooves Sign Chest", world.player),
|
|
|
|
lambda state: state.has("Umbrella", world.player) or can_use_hat(state, world, HatType.BREWING))
|
|
|
|
set_rule(world.multiworld.get_location("Dead Bird Studio - Tepee Chest", world.player),
|
|
|
|
lambda state: state.has("Umbrella", world.player) or can_use_hat(state, world, HatType.BREWING))
|
|
|
|
set_rule(world.multiworld.get_location("Dead Bird Studio - Conductor Chest", world.player),
|
|
|
|
lambda state: state.has("Umbrella", world.player) or can_use_hat(state, world, HatType.BREWING))
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Dead Bird Studio)", world.player),
|
|
|
|
lambda state: state.has("Umbrella", world.player) or can_use_hat(state, world, HatType.BREWING))
|
|
|
|
|
|
|
|
|
|
|
|
def set_subcon_rules(world: "HatInTimeWorld"):
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Time Rift - Village)", world.player),
|
|
|
|
lambda state: can_use_hat(state, world, HatType.BREWING) or state.has("Umbrella", world.player)
|
|
|
|
or can_use_hat(state, world, HatType.DWELLER))
|
|
|
|
|
|
|
|
# You can't skip over the boss arena wall without cherry hover, so these two need to be set this way
|
|
|
|
set_rule(world.multiworld.get_location("Subcon Forest - Boss Arena Chest", world.player),
|
|
|
|
lambda state: state.has("TOD Access", world.player) and can_use_hookshot(state, world)
|
|
|
|
and has_paintings(state, world, 1, False) or state.has("YCHE Access", world.player))
|
|
|
|
|
|
|
|
# The painting wall can't be skipped without cherry hover, which is Expert
|
|
|
|
set_rule(world.multiworld.get_location("Act Completion (Toilet of Doom)", world.player),
|
|
|
|
lambda state: can_use_hookshot(state, world) and can_hit(state, world)
|
|
|
|
and has_paintings(state, world, 1, False))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_entrance("Subcon Forest - Act 2", world.player),
|
|
|
|
lambda state: state.has("Snatcher's Contract - The Subcon Well", world.player))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_entrance("Subcon Forest - Act 3", world.player),
|
|
|
|
lambda state: state.has("Snatcher's Contract - Toilet of Doom", world.player))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_entrance("Subcon Forest - Act 4", world.player),
|
|
|
|
lambda state: state.has("Snatcher's Contract - Queen Vanessa's Manor", world.player))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_entrance("Subcon Forest - Act 5", world.player),
|
|
|
|
lambda state: state.has("Snatcher's Contract - Mail Delivery Service", world.player))
|
|
|
|
|
|
|
|
if painting_logic(world):
|
|
|
|
add_rule(world.multiworld.get_location("Act Completion (Contractual Obligations)", world.player),
|
|
|
|
lambda state: has_paintings(state, world, 1, False))
|
|
|
|
|
|
|
|
|
|
|
|
def set_alps_rules(world: "HatInTimeWorld"):
|
|
|
|
add_rule(world.multiworld.get_entrance("-> The Birdhouse", world.player),
|
|
|
|
lambda state: can_use_hookshot(state, world) and can_use_hat(state, world, HatType.BREWING))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_entrance("-> The Lava Cake", world.player),
|
|
|
|
lambda state: can_use_hookshot(state, world))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_entrance("-> The Windmill", world.player),
|
|
|
|
lambda state: can_use_hookshot(state, world))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_entrance("-> The Twilight Bell", world.player),
|
|
|
|
lambda state: can_use_hookshot(state, world) and can_use_hat(state, world, HatType.DWELLER))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_location("Alpine Skyline - Mystifying Time Mesa: Zipline", world.player),
|
|
|
|
lambda state: can_use_hat(state, world, HatType.SPRINT) or can_use_hat(state, world, HatType.TIME_STOP))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_entrance("Alpine Skyline - Finale", world.player),
|
|
|
|
lambda state: can_clear_alpine(state, world))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_location("Alpine Skyline - Goat Refinery", world.player),
|
|
|
|
lambda state: state.has("AFR Access", world.player)
|
|
|
|
and can_use_hookshot(state, world)
|
|
|
|
and can_hit(state, world, True))
|
|
|
|
|
|
|
|
|
|
|
|
def set_dlc1_rules(world: "HatInTimeWorld"):
|
|
|
|
add_rule(world.multiworld.get_entrance("Cruise Ship Entrance BV", world.player),
|
|
|
|
lambda state: can_use_hookshot(state, world))
|
|
|
|
|
|
|
|
# This particular item isn't present in Act 3 for some reason, yes in vanilla too
|
|
|
|
add_rule(world.multiworld.get_location("The Arctic Cruise - Toilet", world.player),
|
AHiT: Various logic fixes (#4492)
* Fix Director boss photo logic
The rules were being added to for the "Director" boss in
`set_enemy_rules()`, which didn't exist because the boss created was
called "Conductor" instead.
The name of the boss has been changed to "Director", to match, because
it is more accurate due to DJ Grooves possibly being the boss instead of
The Conductor.
The missing logic was the `Hookshot Badge` requirement, however, the
boss events are only used as part of the `Camera Tourist - All Clear`
location, which requires every boss event to be reachable, and the
Toxic Flower boss also has a `Hookshot Badge` requirement, so the
missing `Hookshot Badge` for the Director boss had no effect on logic.
The boss event locations are hidden from spoiler output, so to get a
spoiler showing the Director boss event accessed before having
`Hookshot Badge`, spoiler output had to be modified to also show the
hidden locations. Example sphere from playthrough that should not be
possible because it gets the `Hookshot Badge` and the `Conductor` event
(now renamed to `Director`) in the same sphere:
```
5: {
Act Completion (Time Rift - Dead Bird Studio): Relic (Crayon Box)
Conductor - Dead Bird Studio Basement: Conductor
Dead Bird Studio (Rift) - Page: Behind Cardboard Planet: Time Piece
Dead Bird Studio (Rift) - Page: Near Time Rift Gate: Hookshot Badge
Picture Perfect - Hats Buy Building: Metro Ticket - Blue
Snatcher - Your Contract has Expired: Snatcher
}
```
* Add missing Hookshot + Painting logic for Toilet boss picture
Includes the Hard logic of crossing the gap with a cherry bridge instead
of hookshot and the expert logic of being able to skip the boss firewall
with a cherry hover.
* Fix Alpine Skyline - Goat Outpost Horn region
`Alpine Skyline - Goat Outpost Horn` is accessible from The Illness has
Spread, but was being added to the region that is only accessible from
Alpine Free Roam. `Alpine Skyline - Goat Outpost Horn` has been moved to
the region that is accessible from both The Illness has Spread and
Alpine Free Roam.
* Add missing HitType.umbrella logic for Top of HQ Coin in Beat the Heat
Like Heating up Mafia Town, the cannon to the Mafia HQ area only opens
once all the faucets have been turned off by hitting them. This requires
the Umbrella when umbrella logic is enabled, but the Snatcher Coin on
top of Mafia HQ was missing this requirement when accessed from Beat the
Heat.
* Add missing Main Objective requirement for auto-completed Bonus Stamps
When a Main Objective is not excluded, but the bonuses are excluded, the
bonuses auto-complete once the Main Objective is completed. The
requirement to complete the Main Objective was missing, so the logic was
incorrectly awarding bonus stamps as soon as a Contract was unlocked,
even when it was not possible to complete the Main Objective of that
Contract.
* Add missing Hookshot requirement for The Arctic Cruise - Toilet from Bon Voyage!
`The Arctic Cruise - Toilet` is accessed from the `Cruise Ship` region,
but it is only present in the Ship Shape and Bon Voyage! acts.
Ship Shape and Rock the Boat can access `Cruise Ship` without any items,
but Bon Voyage! requires the Hookshot Badge to reach `Cruise Ship`.
With how the logic was set up, it was incorrectly giving access to
`The Arctic Cruise - Toilet` if the player had access to Bon Voyage!
but only had access to `Cruise Ship` through Rock the Boat.
* Fix Expert logic Rush Hour-only ticket skips
The code was checking `if not world.options.NoTicketSkips:`, but that
would only be `True` for `False`. For "rush_hour" (for Rush Hour-only
ticket skips), it would be `False`, causing Rush Hour-only ticket skips
to act as if ticket skips were disabled.
* Remove Mystifying Time Mesa: Zipline gaining Hookshot requirement in moderate logic
Alpine Skyline - Mystifying Time Mesa: Zipline does not normally
require Hookshot Badge because it is an implied requirement due to only
being accessible from Alpine Free Roam which does require Hookshot
Badge. In normal logic difficulty, the location does not have an
explicit Hookshot Badge requirement, but moderate logic was adding a
Hookshot Badge requirement. This extraneous Hookshot Badge requirement
has been removed.
* Fix Act Completion (Queen Vanessa's Manor) not being accessible with Dweller Mask/Brewing Hat
It was logically requiring the Umbrella hit type only, whereas all the
other locations in Queen Vanessa's Manor require the Dweller Bell hit
type which additionally allows Dweller Mask or Brewing Hat.
* Remove Dweller Mask requirement for Subcon Forest - Tall Tree Hookshot Swing
The Dweller Mask is not used in the intended vanilla route to get this
item, so this requirement seems to have been a mistake.
* Remove unused SDJ option for Subcon Forest - Long Tree Climb Chest
Hard logic can already reach this location with nothing (other than
paintings), so the "or" logic of being able to perform an SDJ was
unused.
* Require any non-HUMT Mafia Town act for Hot Air Balloon with nothing
Two buckets/beach balls are required to bucket/ball hover, but there is
only a single beach ball accessible in Heating Up Mafia Town, and
no accessible buckets.
There is an alternative strategy for Top of Lighthouse that only
requires a single beach ball, so that location can still be reached with
nothing from Heating Up Mafia Town.
* Use `get_difficulty()` helper in `set_enemy_rules`
Co-authored-by: Exempt-Medic <60412657+exempt-medic@users.noreply.github.com>
---------
Co-authored-by: Exempt-Medic <60412657+exempt-medic@users.noreply.github.com>
2025-01-17 02:10:41 +00:00
|
|
|
lambda state: (state.can_reach("Bon Voyage!", "Region", world.player) and can_use_hookshot(state, world))
|
2024-05-20 07:04:06 +00:00
|
|
|
or state.can_reach("Ship Shape", "Region", world.player))
|
|
|
|
|
|
|
|
|
|
|
|
def set_dlc2_rules(world: "HatInTimeWorld"):
|
|
|
|
add_rule(world.multiworld.get_entrance("-> Bluefin Tunnel", world.player),
|
|
|
|
lambda state: state.has("Metro Ticket - Green", world.player)
|
|
|
|
or state.has("Metro Ticket - Blue", world.player))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_entrance("-> Pink Paw Station", world.player),
|
|
|
|
lambda state: state.has("Metro Ticket - Pink", world.player)
|
|
|
|
or state.has("Metro Ticket - Yellow", world.player) and state.has("Metro Ticket - Blue", world.player))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_entrance("Nyakuza Metro - Finale", world.player),
|
|
|
|
lambda state: can_clear_metro(state, world))
|
|
|
|
|
|
|
|
add_rule(world.multiworld.get_location("Act Completion (Rush Hour)", world.player),
|
|
|
|
lambda state: state.has("Metro Ticket - Yellow", world.player)
|
|
|
|
and state.has("Metro Ticket - Blue", world.player)
|
|
|
|
and state.has("Metro Ticket - Pink", world.player))
|
|
|
|
|
|
|
|
for key in shop_locations.keys():
|
|
|
|
if "Green Clean Station Thug B" in key and is_location_valid(world, key):
|
|
|
|
add_rule(world.multiworld.get_location(key, world.player),
|
|
|
|
lambda state: state.has("Metro Ticket - Yellow", world.player), "or")
|
|
|
|
|
|
|
|
|
|
|
|
def reg_act_connection(world: "HatInTimeWorld", region: Union[str, Region], unlocked_entrance: Union[str, Entrance]):
|
|
|
|
reg: Region
|
|
|
|
entrance: Entrance
|
|
|
|
if isinstance(region, str):
|
|
|
|
reg = world.multiworld.get_region(region, world.player)
|
|
|
|
else:
|
|
|
|
reg = region
|
|
|
|
|
|
|
|
if isinstance(unlocked_entrance, str):
|
|
|
|
entrance = world.multiworld.get_entrance(unlocked_entrance, world.player)
|
|
|
|
else:
|
|
|
|
entrance = unlocked_entrance
|
|
|
|
|
|
|
|
world.multiworld.register_indirect_condition(reg, entrance)
|
|
|
|
|
|
|
|
|
|
|
|
# See randomize_act_entrances in Regions.py
|
|
|
|
# Called before set_rules
|
|
|
|
def set_rift_rules(world: "HatInTimeWorld", regions: Dict[str, Region]):
|
|
|
|
|
|
|
|
# This is accessing the regions in place of these time rifts, so we can set the rules on all the entrances.
|
|
|
|
for entrance in regions["Time Rift - Gallery"].entrances:
|
|
|
|
add_rule(entrance, lambda state: can_use_hat(state, world, HatType.BREWING)
|
|
|
|
and state.has("Time Piece", world.player, world.chapter_timepiece_costs[ChapterIndex.BIRDS]))
|
|
|
|
|
|
|
|
for entrance in regions["Time Rift - The Lab"].entrances:
|
|
|
|
add_rule(entrance, lambda state: can_use_hat(state, world, HatType.DWELLER)
|
|
|
|
and state.has("Time Piece", world.player, world.chapter_timepiece_costs[ChapterIndex.ALPINE]))
|
|
|
|
|
|
|
|
for entrance in regions["Time Rift - Sewers"].entrances:
|
|
|
|
add_rule(entrance, lambda state: can_clear_required_act(state, world, "Mafia Town - Act 4"))
|
|
|
|
reg_act_connection(world, world.multiworld.get_entrance("Mafia Town - Act 4",
|
|
|
|
world.player).connected_region, entrance)
|
|
|
|
|
|
|
|
for entrance in regions["Time Rift - Bazaar"].entrances:
|
|
|
|
add_rule(entrance, lambda state: can_clear_required_act(state, world, "Mafia Town - Act 6"))
|
|
|
|
reg_act_connection(world, world.multiworld.get_entrance("Mafia Town - Act 6",
|
|
|
|
world.player).connected_region, entrance)
|
|
|
|
|
|
|
|
for entrance in regions["Time Rift - Mafia of Cooks"].entrances:
|
|
|
|
add_rule(entrance, lambda state: has_relic_combo(state, world, "Burger"))
|
|
|
|
|
|
|
|
for entrance in regions["Time Rift - The Owl Express"].entrances:
|
|
|
|
add_rule(entrance, lambda state: can_clear_required_act(state, world, "Battle of the Birds - Act 2"))
|
|
|
|
add_rule(entrance, lambda state: can_clear_required_act(state, world, "Battle of the Birds - Act 3"))
|
|
|
|
reg_act_connection(world, world.multiworld.get_entrance("Battle of the Birds - Act 2",
|
|
|
|
world.player).connected_region, entrance)
|
|
|
|
reg_act_connection(world, world.multiworld.get_entrance("Battle of the Birds - Act 3",
|
|
|
|
world.player).connected_region, entrance)
|
|
|
|
|
|
|
|
for entrance in regions["Time Rift - The Moon"].entrances:
|
|
|
|
add_rule(entrance, lambda state: can_clear_required_act(state, world, "Battle of the Birds - Act 4"))
|
|
|
|
add_rule(entrance, lambda state: can_clear_required_act(state, world, "Battle of the Birds - Act 5"))
|
|
|
|
reg_act_connection(world, world.multiworld.get_entrance("Battle of the Birds - Act 4",
|
|
|
|
world.player).connected_region, entrance)
|
|
|
|
reg_act_connection(world, world.multiworld.get_entrance("Battle of the Birds - Act 5",
|
|
|
|
world.player).connected_region, entrance)
|
|
|
|
|
|
|
|
for entrance in regions["Time Rift - Dead Bird Studio"].entrances:
|
|
|
|
add_rule(entrance, lambda state: has_relic_combo(state, world, "Train"))
|
|
|
|
|
|
|
|
for entrance in regions["Time Rift - Pipe"].entrances:
|
|
|
|
add_rule(entrance, lambda state: can_clear_required_act(state, world, "Subcon Forest - Act 2"))
|
|
|
|
reg_act_connection(world, world.multiworld.get_entrance("Subcon Forest - Act 2",
|
|
|
|
world.player).connected_region, entrance)
|
|
|
|
if painting_logic(world):
|
|
|
|
add_rule(entrance, lambda state: has_paintings(state, world, 2))
|
|
|
|
|
|
|
|
for entrance in regions["Time Rift - Village"].entrances:
|
|
|
|
add_rule(entrance, lambda state: can_clear_required_act(state, world, "Subcon Forest - Act 4"))
|
|
|
|
reg_act_connection(world, world.multiworld.get_entrance("Subcon Forest - Act 4",
|
|
|
|
world.player).connected_region, entrance)
|
|
|
|
|
|
|
|
if painting_logic(world):
|
|
|
|
add_rule(entrance, lambda state: has_paintings(state, world, 2))
|
|
|
|
|
|
|
|
for entrance in regions["Time Rift - Sleepy Subcon"].entrances:
|
|
|
|
add_rule(entrance, lambda state: has_relic_combo(state, world, "UFO"))
|
|
|
|
if painting_logic(world):
|
|
|
|
add_rule(entrance, lambda state: has_paintings(state, world, 3))
|
|
|
|
|
|
|
|
for entrance in regions["Time Rift - Curly Tail Trail"].entrances:
|
|
|
|
add_rule(entrance, lambda state: state.has("Windmill Cleared", world.player))
|
|
|
|
|
|
|
|
for entrance in regions["Time Rift - The Twilight Bell"].entrances:
|
|
|
|
add_rule(entrance, lambda state: state.has("Twilight Bell Cleared", world.player))
|
|
|
|
|
|
|
|
for entrance in regions["Time Rift - Alpine Skyline"].entrances:
|
|
|
|
add_rule(entrance, lambda state: has_relic_combo(state, world, "Crayon"))
|
2024-08-29 06:11:42 +00:00
|
|
|
if entrance.parent_region.name == "Alpine Free Roam":
|
|
|
|
add_rule(entrance,
|
|
|
|
lambda state: can_use_hookshot(state, world) and can_hit(state, world, umbrella_only=True))
|
2024-05-20 07:04:06 +00:00
|
|
|
|
|
|
|
if world.is_dlc1():
|
|
|
|
for entrance in regions["Time Rift - Balcony"].entrances:
|
|
|
|
add_rule(entrance, lambda state: can_clear_required_act(state, world, "The Arctic Cruise - Finale"))
|
2024-07-14 12:17:05 +00:00
|
|
|
reg_act_connection(world, world.multiworld.get_entrance("The Arctic Cruise - Finale",
|
|
|
|
world.player).connected_region, entrance)
|
2024-05-20 07:04:06 +00:00
|
|
|
|
|
|
|
for entrance in regions["Time Rift - Deep Sea"].entrances:
|
|
|
|
add_rule(entrance, lambda state: has_relic_combo(state, world, "Cake"))
|
|
|
|
|
|
|
|
if world.is_dlc2():
|
|
|
|
for entrance in regions["Time Rift - Rumbi Factory"].entrances:
|
|
|
|
add_rule(entrance, lambda state: has_relic_combo(state, world, "Necklace"))
|
|
|
|
|
|
|
|
|
|
|
|
# Basically the same as above, but without the need of the dict since we are just setting defaults
|
|
|
|
# Called if Act Rando is disabled
|
|
|
|
def set_default_rift_rules(world: "HatInTimeWorld"):
|
|
|
|
|
|
|
|
for entrance in world.multiworld.get_region("Time Rift - Gallery", world.player).entrances:
|
|
|
|
add_rule(entrance, lambda state: can_use_hat(state, world, HatType.BREWING)
|
|
|
|
and state.has("Time Piece", world.player, world.chapter_timepiece_costs[ChapterIndex.BIRDS]))
|
|
|
|
|
|
|
|
for entrance in world.multiworld.get_region("Time Rift - The Lab", world.player).entrances:
|
|
|
|
add_rule(entrance, lambda state: can_use_hat(state, world, HatType.DWELLER)
|
|
|
|
and state.has("Time Piece", world.player, world.chapter_timepiece_costs[ChapterIndex.ALPINE]))
|
|
|
|
|
|
|
|
for entrance in world.multiworld.get_region("Time Rift - Sewers", world.player).entrances:
|
|
|
|
add_rule(entrance, lambda state: can_clear_required_act(state, world, "Mafia Town - Act 4"))
|
|
|
|
reg_act_connection(world, "Down with the Mafia!", entrance.name)
|
|
|
|
|
|
|
|
for entrance in world.multiworld.get_region("Time Rift - Bazaar", world.player).entrances:
|
|
|
|
add_rule(entrance, lambda state: can_clear_required_act(state, world, "Mafia Town - Act 6"))
|
|
|
|
reg_act_connection(world, "Heating Up Mafia Town", entrance.name)
|
|
|
|
|
|
|
|
for entrance in world.multiworld.get_region("Time Rift - Mafia of Cooks", world.player).entrances:
|
|
|
|
add_rule(entrance, lambda state: has_relic_combo(state, world, "Burger"))
|
|
|
|
|
|
|
|
for entrance in world.multiworld.get_region("Time Rift - The Owl Express", world.player).entrances:
|
|
|
|
add_rule(entrance, lambda state: can_clear_required_act(state, world, "Battle of the Birds - Act 2"))
|
|
|
|
add_rule(entrance, lambda state: can_clear_required_act(state, world, "Battle of the Birds - Act 3"))
|
|
|
|
reg_act_connection(world, "Murder on the Owl Express", entrance.name)
|
|
|
|
reg_act_connection(world, "Picture Perfect", entrance.name)
|
|
|
|
|
|
|
|
for entrance in world.multiworld.get_region("Time Rift - The Moon", world.player).entrances:
|
|
|
|
add_rule(entrance, lambda state: can_clear_required_act(state, world, "Battle of the Birds - Act 4"))
|
|
|
|
add_rule(entrance, lambda state: can_clear_required_act(state, world, "Battle of the Birds - Act 5"))
|
|
|
|
reg_act_connection(world, "Train Rush", entrance.name)
|
|
|
|
reg_act_connection(world, "The Big Parade", entrance.name)
|
|
|
|
|
|
|
|
for entrance in world.multiworld.get_region("Time Rift - Dead Bird Studio", world.player).entrances:
|
|
|
|
add_rule(entrance, lambda state: has_relic_combo(state, world, "Train"))
|
|
|
|
|
|
|
|
for entrance in world.multiworld.get_region("Time Rift - Pipe", world.player).entrances:
|
|
|
|
add_rule(entrance, lambda state: can_clear_required_act(state, world, "Subcon Forest - Act 2"))
|
|
|
|
reg_act_connection(world, "The Subcon Well", entrance.name)
|
|
|
|
if painting_logic(world):
|
|
|
|
add_rule(entrance, lambda state: has_paintings(state, world, 2))
|
|
|
|
|
|
|
|
for entrance in world.multiworld.get_region("Time Rift - Village", world.player).entrances:
|
|
|
|
add_rule(entrance, lambda state: can_clear_required_act(state, world, "Subcon Forest - Act 4"))
|
|
|
|
reg_act_connection(world, "Queen Vanessa's Manor", entrance.name)
|
|
|
|
if painting_logic(world):
|
|
|
|
add_rule(entrance, lambda state: has_paintings(state, world, 2))
|
|
|
|
|
|
|
|
for entrance in world.multiworld.get_region("Time Rift - Sleepy Subcon", world.player).entrances:
|
|
|
|
add_rule(entrance, lambda state: has_relic_combo(state, world, "UFO"))
|
|
|
|
if painting_logic(world):
|
|
|
|
add_rule(entrance, lambda state: has_paintings(state, world, 3))
|
|
|
|
|
|
|
|
for entrance in world.multiworld.get_region("Time Rift - Curly Tail Trail", world.player).entrances:
|
|
|
|
add_rule(entrance, lambda state: state.has("Windmill Cleared", world.player))
|
|
|
|
|
|
|
|
for entrance in world.multiworld.get_region("Time Rift - The Twilight Bell", world.player).entrances:
|
|
|
|
add_rule(entrance, lambda state: state.has("Twilight Bell Cleared", world.player))
|
|
|
|
|
|
|
|
for entrance in world.multiworld.get_region("Time Rift - Alpine Skyline", world.player).entrances:
|
|
|
|
add_rule(entrance, lambda state: has_relic_combo(state, world, "Crayon"))
|
2024-08-29 06:11:42 +00:00
|
|
|
if entrance.parent_region.name == "Alpine Free Roam":
|
|
|
|
add_rule(entrance,
|
|
|
|
lambda state: can_use_hookshot(state, world) and can_hit(state, world, umbrella_only=True))
|
2024-05-20 07:04:06 +00:00
|
|
|
|
|
|
|
if world.is_dlc1():
|
|
|
|
for entrance in world.multiworld.get_region("Time Rift - Balcony", world.player).entrances:
|
|
|
|
add_rule(entrance, lambda state: can_clear_required_act(state, world, "The Arctic Cruise - Finale"))
|
2024-07-14 12:17:05 +00:00
|
|
|
reg_act_connection(world, "Rock the Boat", entrance.name)
|
2024-05-20 07:04:06 +00:00
|
|
|
|
|
|
|
for entrance in world.multiworld.get_region("Time Rift - Deep Sea", world.player).entrances:
|
|
|
|
add_rule(entrance, lambda state: has_relic_combo(state, world, "Cake"))
|
|
|
|
|
|
|
|
if world.is_dlc2():
|
|
|
|
for entrance in world.multiworld.get_region("Time Rift - Rumbi Factory", world.player).entrances:
|
|
|
|
add_rule(entrance, lambda state: has_relic_combo(state, world, "Necklace"))
|
|
|
|
|
|
|
|
|
|
|
|
def set_event_rules(world: "HatInTimeWorld"):
|
|
|
|
for (name, data) in event_locs.items():
|
|
|
|
if not is_location_valid(world, name):
|
|
|
|
continue
|
|
|
|
|
|
|
|
event: Location = world.multiworld.get_location(name, world.player)
|
|
|
|
|
|
|
|
if data.act_event:
|
|
|
|
add_rule(event, world.multiworld.get_location(f"Act Completion ({data.region})", world.player).access_rule)
|