Archipelago/worlds/ahit/Rules.py

965 lines
51 KiB
Python
Raw Normal View History

from worlds.AutoWorld import CollectionState
from worlds.generic.Rules import add_rule, set_rule
AHIT: Generation error fixes and some other bug fixes (#3663) * duh * Fuck it * Major fixes * a * b * Even more fixes * New option - NoFreeRoamFinale * a * Hat Logic Fix * Just to be safe * multiworld.random to world.random * KeyError fix * Update .gitignore * Update __init__.py * Zoinks Scoob * ffs * Ruh Roh Raggy, more r-r-r-random bugs! * 0.9b - cleanup + expanded logic difficulty * Update Rules.py * Update Regions.py * AttributeError fix * 0.10b - New Options * 1.0 Preparations * Docs * Docs 2 * Fixes * Update __init__.py * Fixes * variable capture my beloathed * Fixes * a * 10 Seconds logic fix * 1.1 * 1.2 * a * New client * More client changes * 1.3 * Final touch-ups for 1.3 * 1.3.1 * 1.3.3 * Zero Jumps gen error fix * more fixes * Formatting improvements * typo * Update __init__.py * Revert "Update __init__.py" This reverts commit e178a7c0a6904ace803241cab3021d7b97177e90. * init * Update to new options API * Missed some * Snatcher Coins fix * Missed some more * some slight touch ups * rewind * a * fix things * Revert "Merge branch 'main' of https://github.com/CookieCat45/Archipelago-ahit" This reverts commit a2360fe197e77a723bb70006c5eb5725c7ed3826, reversing changes made to b8948bc4958855c6e342e18bdb8dc81cfcf09455. * Update .gitignore * 1.3.6 * Final touch-ups * Fix client and leftover old options api * Delete setup-ahitclient.py * Update .gitignore * old python version fix * proper warnings for invalid act plandos * Update worlds/ahit/docs/en_A Hat in Time.md Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> * Update worlds/ahit/docs/setup_en.md Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> * 120 char per line * "settings" to "options" * Update DeathWishRules.py * Update worlds/ahit/docs/en_A Hat in Time.md Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> * No more loading the data package * cleanup + act plando fixes * almost forgot * Update Rules.py * a * Update worlds/ahit/Options.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * Options stuff * oop * no unnecessary type hints * warn about depot download length in setup guide * Update worlds/ahit/Options.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * typo Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * Update worlds/ahit/Rules.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * review stuff * More stuff from review * comment * 1.5 Update * link fix? * link fix 2 * Update setup_en.md * Update setup_en.md * Update setup_en.md * Evil * Good fucking lord * Review stuff again + Logic fixes * More review stuff * Even more review stuff - we're almost done * DW review stuff * Finish up review stuff * remove leftover stuff * a * assert item * add A Hat in Time to readme/codeowners files * Fix range options not being corrected properly * 120 chars per line in docs * Update worlds/ahit/Regions.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Update worlds/ahit/DeathWishLocations.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Remove some unnecessary option.class.value * Remove data_version and more option.class.value * Update worlds/ahit/Items.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Remove the rest of option.class.value * Update worlds/ahit/DeathWishLocations.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * review stuff * Replace connect_regions with Region.connect * review stuff * Remove unnecessary Optional from LocData * Remove HatType.NONE * Update worlds/ahit/test/TestActs.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * fix so default tests actually don't run * Improve performance for death wish rules * rename test file * change test imports * 1000 is probably unnecessary * a * change state.count to state.has * stuff * starting inventory hats fix * shouldn't have done this lol * make ship shape task goal equal to number of tasksanity checks if set to 0 * a * change act shuffle starting acts + logic updates * dumb * option groups + lambda capture cringe + typo * a * b * missing option in groups * c * Fix Your Contract Has Expired being placed on first level when it shouldn't * yche fix * formatting * major logic bug fix for death wish * Update Regions.py * Add missing indirect connections * Fix generation error from chapter 2 start with act shuffle off * a * Revert "a" This reverts commit df58bbcd998585760cc6ac9ea54b6fdf142b4fd1. * Revert "Fix generation error from chapter 2 start with act shuffle off" This reverts commit 0f4d441824af34bf7a7cff19f5f14161752d8661. * bunch of fixes * Update Regions.py * Update __init__.py * Update __init__.py * Update __init__.py * Update Regions.py * Update worlds/ahit/__init__.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Update __init__.py * Update __init__.py --------- Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> Co-authored-by: Ixrec <ericrhitchcock@gmail.com> Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> Co-authored-by: Fabian Dill <Berserker66@users.noreply.github.com>
2024-07-27 17:16:52 +00:00
from .Locations import location_table, zipline_unlocks, is_location_valid, shop_locations, event_locs
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)
AHIT: Generation error fixes and some other bug fixes (#3663) * duh * Fuck it * Major fixes * a * b * Even more fixes * New option - NoFreeRoamFinale * a * Hat Logic Fix * Just to be safe * multiworld.random to world.random * KeyError fix * Update .gitignore * Update __init__.py * Zoinks Scoob * ffs * Ruh Roh Raggy, more r-r-r-random bugs! * 0.9b - cleanup + expanded logic difficulty * Update Rules.py * Update Regions.py * AttributeError fix * 0.10b - New Options * 1.0 Preparations * Docs * Docs 2 * Fixes * Update __init__.py * Fixes * variable capture my beloathed * Fixes * a * 10 Seconds logic fix * 1.1 * 1.2 * a * New client * More client changes * 1.3 * Final touch-ups for 1.3 * 1.3.1 * 1.3.3 * Zero Jumps gen error fix * more fixes * Formatting improvements * typo * Update __init__.py * Revert "Update __init__.py" This reverts commit e178a7c0a6904ace803241cab3021d7b97177e90. * init * Update to new options API * Missed some * Snatcher Coins fix * Missed some more * some slight touch ups * rewind * a * fix things * Revert "Merge branch 'main' of https://github.com/CookieCat45/Archipelago-ahit" This reverts commit a2360fe197e77a723bb70006c5eb5725c7ed3826, reversing changes made to b8948bc4958855c6e342e18bdb8dc81cfcf09455. * Update .gitignore * 1.3.6 * Final touch-ups * Fix client and leftover old options api * Delete setup-ahitclient.py * Update .gitignore * old python version fix * proper warnings for invalid act plandos * Update worlds/ahit/docs/en_A Hat in Time.md Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> * Update worlds/ahit/docs/setup_en.md Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> * 120 char per line * "settings" to "options" * Update DeathWishRules.py * Update worlds/ahit/docs/en_A Hat in Time.md Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> * No more loading the data package * cleanup + act plando fixes * almost forgot * Update Rules.py * a * Update worlds/ahit/Options.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * Options stuff * oop * no unnecessary type hints * warn about depot download length in setup guide * Update worlds/ahit/Options.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * typo Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * Update worlds/ahit/Rules.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * review stuff * More stuff from review * comment * 1.5 Update * link fix? * link fix 2 * Update setup_en.md * Update setup_en.md * Update setup_en.md * Evil * Good fucking lord * Review stuff again + Logic fixes * More review stuff * Even more review stuff - we're almost done * DW review stuff * Finish up review stuff * remove leftover stuff * a * assert item * add A Hat in Time to readme/codeowners files * Fix range options not being corrected properly * 120 chars per line in docs * Update worlds/ahit/Regions.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Update worlds/ahit/DeathWishLocations.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Remove some unnecessary option.class.value * Remove data_version and more option.class.value * Update worlds/ahit/Items.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Remove the rest of option.class.value * Update worlds/ahit/DeathWishLocations.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * review stuff * Replace connect_regions with Region.connect * review stuff * Remove unnecessary Optional from LocData * Remove HatType.NONE * Update worlds/ahit/test/TestActs.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * fix so default tests actually don't run * Improve performance for death wish rules * rename test file * change test imports * 1000 is probably unnecessary * a * change state.count to state.has * stuff * starting inventory hats fix * shouldn't have done this lol * make ship shape task goal equal to number of tasksanity checks if set to 0 * a * change act shuffle starting acts + logic updates * dumb * option groups + lambda capture cringe + typo * a * b * missing option in groups * c * Fix Your Contract Has Expired being placed on first level when it shouldn't * yche fix * formatting * major logic bug fix for death wish * Update Regions.py * Add missing indirect connections * Fix generation error from chapter 2 start with act shuffle off * a * Revert "a" This reverts commit df58bbcd998585760cc6ac9ea54b6fdf142b4fd1. * Revert "Fix generation error from chapter 2 start with act shuffle off" This reverts commit 0f4d441824af34bf7a7cff19f5f14161752d8661. * bunch of fixes * Update Regions.py * Update __init__.py * Update __init__.py * Update __init__.py * Update Regions.py * Update worlds/ahit/__init__.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Update __init__.py * Update __init__.py --------- Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> Co-authored-by: Ixrec <ericrhitchcock@gmail.com> Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> Co-authored-by: Fabian Dill <Berserker66@users.noreply.github.com>
2024-07-27 17:16:52 +00:00
if world.is_dlc2() and final_chapter != ChapterIndex.METRO:
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
AHIT: Generation error fixes and some other bug fixes (#3663) * duh * Fuck it * Major fixes * a * b * Even more fixes * New option - NoFreeRoamFinale * a * Hat Logic Fix * Just to be safe * multiworld.random to world.random * KeyError fix * Update .gitignore * Update __init__.py * Zoinks Scoob * ffs * Ruh Roh Raggy, more r-r-r-random bugs! * 0.9b - cleanup + expanded logic difficulty * Update Rules.py * Update Regions.py * AttributeError fix * 0.10b - New Options * 1.0 Preparations * Docs * Docs 2 * Fixes * Update __init__.py * Fixes * variable capture my beloathed * Fixes * a * 10 Seconds logic fix * 1.1 * 1.2 * a * New client * More client changes * 1.3 * Final touch-ups for 1.3 * 1.3.1 * 1.3.3 * Zero Jumps gen error fix * more fixes * Formatting improvements * typo * Update __init__.py * Revert "Update __init__.py" This reverts commit e178a7c0a6904ace803241cab3021d7b97177e90. * init * Update to new options API * Missed some * Snatcher Coins fix * Missed some more * some slight touch ups * rewind * a * fix things * Revert "Merge branch 'main' of https://github.com/CookieCat45/Archipelago-ahit" This reverts commit a2360fe197e77a723bb70006c5eb5725c7ed3826, reversing changes made to b8948bc4958855c6e342e18bdb8dc81cfcf09455. * Update .gitignore * 1.3.6 * Final touch-ups * Fix client and leftover old options api * Delete setup-ahitclient.py * Update .gitignore * old python version fix * proper warnings for invalid act plandos * Update worlds/ahit/docs/en_A Hat in Time.md Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> * Update worlds/ahit/docs/setup_en.md Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> * 120 char per line * "settings" to "options" * Update DeathWishRules.py * Update worlds/ahit/docs/en_A Hat in Time.md Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> * No more loading the data package * cleanup + act plando fixes * almost forgot * Update Rules.py * a * Update worlds/ahit/Options.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * Options stuff * oop * no unnecessary type hints * warn about depot download length in setup guide * Update worlds/ahit/Options.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * typo Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * Update worlds/ahit/Rules.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * review stuff * More stuff from review * comment * 1.5 Update * link fix? * link fix 2 * Update setup_en.md * Update setup_en.md * Update setup_en.md * Evil * Good fucking lord * Review stuff again + Logic fixes * More review stuff * Even more review stuff - we're almost done * DW review stuff * Finish up review stuff * remove leftover stuff * a * assert item * add A Hat in Time to readme/codeowners files * Fix range options not being corrected properly * 120 chars per line in docs * Update worlds/ahit/Regions.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Update worlds/ahit/DeathWishLocations.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Remove some unnecessary option.class.value * Remove data_version and more option.class.value * Update worlds/ahit/Items.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Remove the rest of option.class.value * Update worlds/ahit/DeathWishLocations.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * review stuff * Replace connect_regions with Region.connect * review stuff * Remove unnecessary Optional from LocData * Remove HatType.NONE * Update worlds/ahit/test/TestActs.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * fix so default tests actually don't run * Improve performance for death wish rules * rename test file * change test imports * 1000 is probably unnecessary * a * change state.count to state.has * stuff * starting inventory hats fix * shouldn't have done this lol * make ship shape task goal equal to number of tasksanity checks if set to 0 * a * change act shuffle starting acts + logic updates * dumb * option groups + lambda capture cringe + typo * a * b * missing option in groups * c * Fix Your Contract Has Expired being placed on first level when it shouldn't * yche fix * formatting * major logic bug fix for death wish * Update Regions.py * Add missing indirect connections * Fix generation error from chapter 2 start with act shuffle off * a * Revert "a" This reverts commit df58bbcd998585760cc6ac9ea54b6fdf142b4fd1. * Revert "Fix generation error from chapter 2 start with act shuffle off" This reverts commit 0f4d441824af34bf7a7cff19f5f14161752d8661. * bunch of fixes * Update Regions.py * Update __init__.py * Update __init__.py * Update __init__.py * Update Regions.py * Update worlds/ahit/__init__.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Update __init__.py * Update __init__.py --------- Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> Co-authored-by: Ixrec <ericrhitchcock@gmail.com> Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> Co-authored-by: Fabian Dill <Berserker66@users.noreply.github.com>
2024-07-27 17:16:52 +00:00
if starting_chapter != ChapterIndex.ALPINE and (world.is_dlc1() or world.is_dlc2()):
index1 = 69
index2 = 69
pos: int
lowest_index: int
chapter_list.remove(ChapterIndex.ALPINE)
if world.is_dlc1():
index1 = chapter_list.index(ChapterIndex.CRUISE)
AHIT: Generation error fixes and some other bug fixes (#3663) * duh * Fuck it * Major fixes * a * b * Even more fixes * New option - NoFreeRoamFinale * a * Hat Logic Fix * Just to be safe * multiworld.random to world.random * KeyError fix * Update .gitignore * Update __init__.py * Zoinks Scoob * ffs * Ruh Roh Raggy, more r-r-r-random bugs! * 0.9b - cleanup + expanded logic difficulty * Update Rules.py * Update Regions.py * AttributeError fix * 0.10b - New Options * 1.0 Preparations * Docs * Docs 2 * Fixes * Update __init__.py * Fixes * variable capture my beloathed * Fixes * a * 10 Seconds logic fix * 1.1 * 1.2 * a * New client * More client changes * 1.3 * Final touch-ups for 1.3 * 1.3.1 * 1.3.3 * Zero Jumps gen error fix * more fixes * Formatting improvements * typo * Update __init__.py * Revert "Update __init__.py" This reverts commit e178a7c0a6904ace803241cab3021d7b97177e90. * init * Update to new options API * Missed some * Snatcher Coins fix * Missed some more * some slight touch ups * rewind * a * fix things * Revert "Merge branch 'main' of https://github.com/CookieCat45/Archipelago-ahit" This reverts commit a2360fe197e77a723bb70006c5eb5725c7ed3826, reversing changes made to b8948bc4958855c6e342e18bdb8dc81cfcf09455. * Update .gitignore * 1.3.6 * Final touch-ups * Fix client and leftover old options api * Delete setup-ahitclient.py * Update .gitignore * old python version fix * proper warnings for invalid act plandos * Update worlds/ahit/docs/en_A Hat in Time.md Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> * Update worlds/ahit/docs/setup_en.md Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> * 120 char per line * "settings" to "options" * Update DeathWishRules.py * Update worlds/ahit/docs/en_A Hat in Time.md Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> * No more loading the data package * cleanup + act plando fixes * almost forgot * Update Rules.py * a * Update worlds/ahit/Options.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * Options stuff * oop * no unnecessary type hints * warn about depot download length in setup guide * Update worlds/ahit/Options.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * typo Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * Update worlds/ahit/Rules.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * review stuff * More stuff from review * comment * 1.5 Update * link fix? * link fix 2 * Update setup_en.md * Update setup_en.md * Update setup_en.md * Evil * Good fucking lord * Review stuff again + Logic fixes * More review stuff * Even more review stuff - we're almost done * DW review stuff * Finish up review stuff * remove leftover stuff * a * assert item * add A Hat in Time to readme/codeowners files * Fix range options not being corrected properly * 120 chars per line in docs * Update worlds/ahit/Regions.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Update worlds/ahit/DeathWishLocations.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Remove some unnecessary option.class.value * Remove data_version and more option.class.value * Update worlds/ahit/Items.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Remove the rest of option.class.value * Update worlds/ahit/DeathWishLocations.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * review stuff * Replace connect_regions with Region.connect * review stuff * Remove unnecessary Optional from LocData * Remove HatType.NONE * Update worlds/ahit/test/TestActs.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * fix so default tests actually don't run * Improve performance for death wish rules * rename test file * change test imports * 1000 is probably unnecessary * a * change state.count to state.has * stuff * starting inventory hats fix * shouldn't have done this lol * make ship shape task goal equal to number of tasksanity checks if set to 0 * a * change act shuffle starting acts + logic updates * dumb * option groups + lambda capture cringe + typo * a * b * missing option in groups * c * Fix Your Contract Has Expired being placed on first level when it shouldn't * yche fix * formatting * major logic bug fix for death wish * Update Regions.py * Add missing indirect connections * Fix generation error from chapter 2 start with act shuffle off * a * Revert "a" This reverts commit df58bbcd998585760cc6ac9ea54b6fdf142b4fd1. * Revert "Fix generation error from chapter 2 start with act shuffle off" This reverts commit 0f4d441824af34bf7a7cff19f5f14161752d8661. * bunch of fixes * Update Regions.py * Update __init__.py * Update __init__.py * Update __init__.py * Update Regions.py * Update worlds/ahit/__init__.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Update __init__.py * Update __init__.py --------- Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> Co-authored-by: Ixrec <ericrhitchcock@gmail.com> Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> Co-authored-by: Fabian Dill <Berserker66@users.noreply.github.com>
2024-07-27 17:16:52 +00:00
if world.is_dlc2() and final_chapter != ChapterIndex.METRO:
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))
AHIT: Generation error fixes and some other bug fixes (#3663) * duh * Fuck it * Major fixes * a * b * Even more fixes * New option - NoFreeRoamFinale * a * Hat Logic Fix * Just to be safe * multiworld.random to world.random * KeyError fix * Update .gitignore * Update __init__.py * Zoinks Scoob * ffs * Ruh Roh Raggy, more r-r-r-random bugs! * 0.9b - cleanup + expanded logic difficulty * Update Rules.py * Update Regions.py * AttributeError fix * 0.10b - New Options * 1.0 Preparations * Docs * Docs 2 * Fixes * Update __init__.py * Fixes * variable capture my beloathed * Fixes * a * 10 Seconds logic fix * 1.1 * 1.2 * a * New client * More client changes * 1.3 * Final touch-ups for 1.3 * 1.3.1 * 1.3.3 * Zero Jumps gen error fix * more fixes * Formatting improvements * typo * Update __init__.py * Revert "Update __init__.py" This reverts commit e178a7c0a6904ace803241cab3021d7b97177e90. * init * Update to new options API * Missed some * Snatcher Coins fix * Missed some more * some slight touch ups * rewind * a * fix things * Revert "Merge branch 'main' of https://github.com/CookieCat45/Archipelago-ahit" This reverts commit a2360fe197e77a723bb70006c5eb5725c7ed3826, reversing changes made to b8948bc4958855c6e342e18bdb8dc81cfcf09455. * Update .gitignore * 1.3.6 * Final touch-ups * Fix client and leftover old options api * Delete setup-ahitclient.py * Update .gitignore * old python version fix * proper warnings for invalid act plandos * Update worlds/ahit/docs/en_A Hat in Time.md Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> * Update worlds/ahit/docs/setup_en.md Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> * 120 char per line * "settings" to "options" * Update DeathWishRules.py * Update worlds/ahit/docs/en_A Hat in Time.md Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> * No more loading the data package * cleanup + act plando fixes * almost forgot * Update Rules.py * a * Update worlds/ahit/Options.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * Options stuff * oop * no unnecessary type hints * warn about depot download length in setup guide * Update worlds/ahit/Options.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * typo Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * Update worlds/ahit/Rules.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * review stuff * More stuff from review * comment * 1.5 Update * link fix? * link fix 2 * Update setup_en.md * Update setup_en.md * Update setup_en.md * Evil * Good fucking lord * Review stuff again + Logic fixes * More review stuff * Even more review stuff - we're almost done * DW review stuff * Finish up review stuff * remove leftover stuff * a * assert item * add A Hat in Time to readme/codeowners files * Fix range options not being corrected properly * 120 chars per line in docs * Update worlds/ahit/Regions.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Update worlds/ahit/DeathWishLocations.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Remove some unnecessary option.class.value * Remove data_version and more option.class.value * Update worlds/ahit/Items.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Remove the rest of option.class.value * Update worlds/ahit/DeathWishLocations.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * review stuff * Replace connect_regions with Region.connect * review stuff * Remove unnecessary Optional from LocData * Remove HatType.NONE * Update worlds/ahit/test/TestActs.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * fix so default tests actually don't run * Improve performance for death wish rules * rename test file * change test imports * 1000 is probably unnecessary * a * change state.count to state.has * stuff * starting inventory hats fix * shouldn't have done this lol * make ship shape task goal equal to number of tasksanity checks if set to 0 * a * change act shuffle starting acts + logic updates * dumb * option groups + lambda capture cringe + typo * a * b * missing option in groups * c * Fix Your Contract Has Expired being placed on first level when it shouldn't * yche fix * formatting * major logic bug fix for death wish * Update Regions.py * Add missing indirect connections * Fix generation error from chapter 2 start with act shuffle off * a * Revert "a" This reverts commit df58bbcd998585760cc6ac9ea54b6fdf142b4fd1. * Revert "Fix generation error from chapter 2 start with act shuffle off" This reverts commit 0f4d441824af34bf7a7cff19f5f14161752d8661. * bunch of fixes * Update Regions.py * Update __init__.py * Update __init__.py * Update __init__.py * Update Regions.py * Update worlds/ahit/__init__.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Update __init__.py * Update __init__.py --------- Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> Co-authored-by: Ixrec <ericrhitchcock@gmail.com> Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> Co-authored-by: Fabian Dill <Berserker66@users.noreply.github.com>
2024-07-27 17:16:52 +00:00
if data.hit_type != HitType.none and world.options.UmbrellaLogic:
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
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)
# 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)
# 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
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)
# 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
AHIT: Generation error fixes and some other bug fixes (#3663) * duh * Fuck it * Major fixes * a * b * Even more fixes * New option - NoFreeRoamFinale * a * Hat Logic Fix * Just to be safe * multiworld.random to world.random * KeyError fix * Update .gitignore * Update __init__.py * Zoinks Scoob * ffs * Ruh Roh Raggy, more r-r-r-random bugs! * 0.9b - cleanup + expanded logic difficulty * Update Rules.py * Update Regions.py * AttributeError fix * 0.10b - New Options * 1.0 Preparations * Docs * Docs 2 * Fixes * Update __init__.py * Fixes * variable capture my beloathed * Fixes * a * 10 Seconds logic fix * 1.1 * 1.2 * a * New client * More client changes * 1.3 * Final touch-ups for 1.3 * 1.3.1 * 1.3.3 * Zero Jumps gen error fix * more fixes * Formatting improvements * typo * Update __init__.py * Revert "Update __init__.py" This reverts commit e178a7c0a6904ace803241cab3021d7b97177e90. * init * Update to new options API * Missed some * Snatcher Coins fix * Missed some more * some slight touch ups * rewind * a * fix things * Revert "Merge branch 'main' of https://github.com/CookieCat45/Archipelago-ahit" This reverts commit a2360fe197e77a723bb70006c5eb5725c7ed3826, reversing changes made to b8948bc4958855c6e342e18bdb8dc81cfcf09455. * Update .gitignore * 1.3.6 * Final touch-ups * Fix client and leftover old options api * Delete setup-ahitclient.py * Update .gitignore * old python version fix * proper warnings for invalid act plandos * Update worlds/ahit/docs/en_A Hat in Time.md Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> * Update worlds/ahit/docs/setup_en.md Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> * 120 char per line * "settings" to "options" * Update DeathWishRules.py * Update worlds/ahit/docs/en_A Hat in Time.md Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> * No more loading the data package * cleanup + act plando fixes * almost forgot * Update Rules.py * a * Update worlds/ahit/Options.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * Options stuff * oop * no unnecessary type hints * warn about depot download length in setup guide * Update worlds/ahit/Options.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * typo Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * Update worlds/ahit/Rules.py Co-authored-by: Ixrec <ericrhitchcock@gmail.com> * review stuff * More stuff from review * comment * 1.5 Update * link fix? * link fix 2 * Update setup_en.md * Update setup_en.md * Update setup_en.md * Evil * Good fucking lord * Review stuff again + Logic fixes * More review stuff * Even more review stuff - we're almost done * DW review stuff * Finish up review stuff * remove leftover stuff * a * assert item * add A Hat in Time to readme/codeowners files * Fix range options not being corrected properly * 120 chars per line in docs * Update worlds/ahit/Regions.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Update worlds/ahit/DeathWishLocations.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Remove some unnecessary option.class.value * Remove data_version and more option.class.value * Update worlds/ahit/Items.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Remove the rest of option.class.value * Update worlds/ahit/DeathWishLocations.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * review stuff * Replace connect_regions with Region.connect * review stuff * Remove unnecessary Optional from LocData * Remove HatType.NONE * Update worlds/ahit/test/TestActs.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * fix so default tests actually don't run * Improve performance for death wish rules * rename test file * change test imports * 1000 is probably unnecessary * a * change state.count to state.has * stuff * starting inventory hats fix * shouldn't have done this lol * make ship shape task goal equal to number of tasksanity checks if set to 0 * a * change act shuffle starting acts + logic updates * dumb * option groups + lambda capture cringe + typo * a * b * missing option in groups * c * Fix Your Contract Has Expired being placed on first level when it shouldn't * yche fix * formatting * major logic bug fix for death wish * Update Regions.py * Add missing indirect connections * Fix generation error from chapter 2 start with act shuffle off * a * Revert "a" This reverts commit df58bbcd998585760cc6ac9ea54b6fdf142b4fd1. * Revert "Fix generation error from chapter 2 start with act shuffle off" This reverts commit 0f4d441824af34bf7a7cff19f5f14161752d8661. * bunch of fixes * Update Regions.py * Update __init__.py * Update __init__.py * Update __init__.py * Update Regions.py * Update worlds/ahit/__init__.py Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> * Update __init__.py * Update __init__.py --------- Co-authored-by: Danaël V. <104455676+ReverM@users.noreply.github.com> Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> Co-authored-by: Ixrec <ericrhitchcock@gmail.com> Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com> Co-authored-by: Fabian Dill <Berserker66@users.noreply.github.com>
2024-07-27 17:16:52 +00:00
if world.options.NoTicketSkips != NoTicketSkips.option_true:
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")
# 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:
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))
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"))
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))
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"))
reg_act_connection(world, world.multiworld.get_entrance("The Arctic Cruise - Finale",
world.player).connected_region, entrance)
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"))
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))
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"))
reg_act_connection(world, "Rock the Boat", entrance.name)
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)