This commit is contained in:
CookieCat
2023-10-13 16:49:09 -04:00
parent 1afb5694b7
commit 13acb67bd0
9 changed files with 840 additions and 665 deletions

View File

@@ -1,10 +1,11 @@
from .Locations import HatInTimeLocation, death_wishes
from .Items import HatInTimeItem
from .Types import HatInTimeLocation, HatInTimeItem
from .Regions import connect_regions, create_region
from BaseClasses import Region, LocationProgressType, ItemClassification
from worlds.generic.Rules import add_rule
from worlds.AutoWorld import World
from typing import List
from .Locations import death_wishes
dw_prereqs = {
"So You're Back From Outer Space": ["Beat the Heat"],
@@ -82,11 +83,9 @@ annoying_bonuses = [
"Snatcher's Hit List",
"10 Seconds until Self-Destruct",
"Killing Two Birds",
"Snatcher Coins in Battle of the Birds",
"Zero Jumps",
"Bird Sanctuary",
"Wound-Up Windmill",
"Snatcher Coins in Alpine Skyline",
"Seal the Deal",
]

View File

@@ -1,12 +1,12 @@
from worlds.AutoWorld import World, CollectionState
from .Locations import LocData, death_wishes, HatInTimeLocation
from .Rules import can_use_hat, can_use_hookshot, can_hit, zipline_logic, has_paintings, get_difficulty
from .Types import HatType, Difficulty
from .Rules import can_use_hat, can_use_hookshot, can_hit, zipline_logic, get_difficulty
from .Types import HatType, Difficulty, HatInTimeLocation, HatInTimeItem, LocData, HatDLC
from .DeathWishLocations import dw_prereqs, dw_candles
from .Items import HatInTimeItem
from BaseClasses import Entrance, Location, ItemClassification
from worlds.generic.Rules import add_rule, set_rule
from typing import List, Callable
from .Regions import act_chapters
from .Locations import zero_jumps, zero_jumps_expert, zero_jumps_hard, death_wishes
# Any speedruns expect the player to have Sprint Hat
dw_requirements = {
@@ -81,6 +81,23 @@ dw_stamp_costs = {
"Seal the Deal": 70,
}
required_snatcher_coins = {
"Snatcher Coins in Mafia Town": ["Snatcher Coin - Top of HQ", "Snatcher Coin - Top of Tower",
"Snatcher Coin - Under Ruined Tower"],
"Snatcher Coins in Battle of the Birds": ["Snatcher Coin - Top of Red House", "Snatcher Coin - Train Rush",
"Snatcher Coin - Picture Perfect"],
"Snatcher Coins in Subcon Forest": ["Snatcher Coin - Swamp Tree", "Snatcher Coin - Manor Roof",
"Snatcher Coin - Giant Time Piece"],
"Snatcher Coins in Alpine Skyline": ["Snatcher Coin - Goat Village Top", "Snatcher Coin - Lava Cake",
"Snatcher Coin - Windmill"],
"Snatcher Coins in Nyakuza Metro": ["Snatcher Coin - Green Clean Tower", "Snatcher Coin - Bluefin Cat Train",
"Snatcher Coin - Pink Paw Fence"],
}
def set_dw_rules(world: World):
if "Snatcher's Hit List" not in world.get_excluded_dws() \
@@ -219,11 +236,8 @@ def modify_dw_rules(world: World, name: str):
add_rule(main_objective, lambda state: state.has("Umbrella", world.player))
elif name == "The Mustache Gauntlet":
# Need a way to kill fire crows without being burned.
add_rule(main_objective, lambda state: state.has("Umbrella", world.player)
or can_use_hat(state, world, HatType.ICE) or can_use_hat(state, world, HatType.BREWING))
add_rule(full_clear, lambda state: state.has("Umbrella", world.player)
or can_use_hat(state, world, HatType.ICE))
elif name == "Vault Codes in the Wind":
# Sprint is normally expected here
@@ -236,16 +250,21 @@ def modify_dw_rules(world: World, name: str):
set_rule(main_objective, lambda state: True)
elif name == "Mafia's Jumps":
# Main objective without Ice, still expected for bonuses
if difficulty >= Difficulty.HARD:
set_rule(main_objective, lambda state: True)
set_rule(full_clear, lambda state: can_use_hat(state, world, HatType.ICE))
set_rule(full_clear, lambda state: True)
elif name == "So You're Back from Outer Space":
# Without Hookshot
if difficulty >= Difficulty.HARD:
set_rule(main_objective, lambda state: True)
elif name == "Wound-Up Windmill":
# No badge pin required. Player can switch to One Hit Hero after the checkpoint and do level without it.
if difficulty >= Difficulty.MODERATE:
set_rule(full_clear, lambda state: can_use_hookshot(state, world)
and state.has("One-Hit Hero Badge", world.player))
if name in dw_candles:
set_candle_dw_rules(name, world)
@@ -268,7 +287,7 @@ def get_total_dw_stamps(state: CollectionState, world: World) -> int:
if state.has(f"2 Stamps - {name}", world.player):
count += 2
elif name not in dw_candles:
# all non-candle bonus requirements allow the player to get the other stamp (like not having One Hit Hero)
# most non-candle bonus requirements allow the player to get the other stamp (like not having One Hit Hero)
count += 1
return count
@@ -281,7 +300,13 @@ def set_candle_dw_rules(name: str, world: World):
if name == "Zero Jumps":
add_rule(main_objective, lambda state: get_zero_jump_clear_count(state, world) >= 1)
add_rule(full_clear, lambda state: get_zero_jump_clear_count(state, world) >= 4
and state.has("Train Rush Cleared", world.player) and can_use_hat(state, world, HatType.ICE))
and state.has("Train Rush (Zero Jumps)", world.player) and can_use_hat(state, world, HatType.ICE))
# No Ice Hat/painting required in Expert for Toilet Zero Jumps
if get_difficulty(world) >= Difficulty.EXPERT:
set_rule(world.multiworld.get_location("Toilet of Doom (Zero Jumps)", world.player),
lambda state: can_use_hookshot(state, world)
and can_hit(state, world))
elif name == "Snatcher's Hit List":
add_rule(main_objective, lambda state: state.has("Mafia Goon", world.player))
@@ -289,74 +314,33 @@ def set_candle_dw_rules(name: str, world: World):
elif name == "Camera Tourist":
add_rule(main_objective, lambda state: get_reachable_enemy_count(state, world) >= 8)
add_rule(full_clear, lambda state: can_reach_all_bosses(state, world))
add_rule(full_clear, lambda state: can_reach_all_bosses(state, world)
and state.has("Triple Enemy Picture", world.player))
elif name == "Snatcher Coins in Mafia Town":
add_rule(main_objective, lambda state: state.has("MT Access", world.player)
or state.has("HUMT Access", world.player))
add_rule(full_clear, lambda state: state.has("CTR Access", world.player)
or state.has("HUMT Access", world.player)
and can_hit(state, world, True)
or state.has("DWTM Access", world.player)
or state.has("TGV Access", world.player))
elif name == "Snatcher Coins in Battle of the Birds":
add_rule(main_objective, lambda state: state.has("PP Access", world.player)
or state.has("DBS Access", world.player)
or state.has("Train Rush Cleared", world.player))
add_rule(full_clear, lambda state: state.has("PP Access", world.player)
and state.has("DBS Access", world.player)
and state.has("Train Rush Cleared", world.player))
elif name == "Snatcher Coins in Subcon Forest":
add_rule(main_objective, lambda state: state.has("SF Access", world.player))
add_rule(main_objective, lambda state: has_paintings(state, world, 1) and (can_use_hookshot(state, world)
or can_hit(state, world) or can_use_hat(state, world, HatType.DWELLER))
or has_paintings(state, world, 3))
add_rule(full_clear, lambda state: has_paintings(state, world, 3) and can_use_hookshot(state, world)
and (can_hit(state, world) or can_use_hat(state, world, HatType.DWELLER)))
elif name == "Snatcher Coins in Alpine Skyline":
add_rule(main_objective, lambda state: state.has("LC Access", world.player)
or state.has("WM Access", world.player))
add_rule(full_clear, lambda state: state.has("LC Access", world.player)
and state.has("WM Access", world.player))
elif name == "Snatcher Coins in Nyakuza Metro":
add_rule(main_objective, lambda state: state.has("Bluefin Tunnel Cleared", world.player)
or (state.has("Nyakuza Intro Cleared", world.player)
and (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(full_clear, lambda state: state.has("Bluefin Tunnel Cleared", world.player)
and (state.has("Nyakuza Intro Cleared", world.player)
and (state.has("Metro Ticket - Pink", world.player)
or state.has("Metro Ticket - Yellow", world.player)
and state.has("Metro Ticket - Blue", world.player))))
elif "Snatcher Coins" in name:
for coin in required_snatcher_coins[name]:
add_rule(main_objective, lambda state: state.has(coin, world.player), "or")
add_rule(full_clear, lambda state: state.has(coin, world.player))
def get_zero_jump_clear_count(state: CollectionState, world: World) -> int:
total: int = 0
for name, hats in zero_jumps.items():
if not state.has(f"{name} Cleared", world.player):
for name in act_chapters.keys():
n = f"{name} (Zero Jumps)"
if n not in zero_jumps:
continue
valid: bool = True
if get_difficulty(world) < Difficulty.HARD and n in zero_jumps_hard:
continue
for hat in hats:
if not can_use_hat(state, world, hat):
valid = False
break
if get_difficulty(world) < Difficulty.EXPERT and n in zero_jumps_expert:
continue
if valid:
total += 1
if not state.has(n, world.player):
continue
total += 1
return total
@@ -399,7 +383,7 @@ def create_enemy_events(world: World):
if area == "Bluefin Tunnel" and not world.is_dlc2():
continue
if world.multiworld.DWShuffle[world.player].value > 0 and area in death_wishes \
if world.multiworld.DWShuffle[world.player].value > 0 and area in death_wishes.keys() \
and area not in world.get_dw_shuffle():
continue
@@ -409,6 +393,22 @@ def create_enemy_events(world: World):
region.locations.append(event)
event.show_in_spoiler = False
for name in triple_enemy_locations:
if name == "Time Rift - Tour" and (not world.is_dlc1() or world.multiworld.ExcludeTour[world.player].value > 0):
continue
if world.multiworld.DWShuffle[world.player].value > 0 and name in death_wishes.keys() \
and name not in world.get_dw_shuffle():
continue
region = world.multiworld.get_region(name, world.player)
event = HatInTimeLocation(world.player, f"Triple Enemy Picture - {name}", None, region)
event.place_locked_item(HatInTimeItem("Triple Enemy Picture", ItemClassification.progression, None, world.player))
region.locations.append(event)
event.show_in_spoiler = False
if name == "The Mustache Gauntlet":
add_rule(event, lambda state: can_use_hookshot(state, world) and can_use_hat(state, world, HatType.DWELLER))
def set_enemy_rules(world: World):
no_tourist = "Camera Tourist" in world.get_excluded_dws() or "Camera Tourist" in world.get_excluded_bonuses()
@@ -422,7 +422,7 @@ def set_enemy_rules(world: World):
continue
if area == "Time Rift - Tour" and (not world.is_dlc1()
or world.multiworld.ExcludeTour[world.player].value > 0):
or world.multiworld.ExcludeTour[world.player].value > 0):
continue
if area == "Bluefin Tunnel" and not world.is_dlc2():
@@ -463,17 +463,6 @@ def set_enemy_rules(world: World):
add_rule(event, lambda state: can_use_hookshot(state, world))
# Zero Jumps completable levels, with required hats if any
zero_jumps = {
"Welcome to Mafia Town": [],
"Cheating the Race": [HatType.TIME_STOP],
"Picture Perfect": [],
"Train Rush": [HatType.ICE],
"Contractual Obligations": [],
"Your Contract has Expired": [],
"Mail Delivery Service": [], # rule for needing sprint is already on act completion
}
# Enemies for Snatcher's Hit List/Camera Tourist, and where to find them
hit_list = {
"Mafia Goon": ["Mafia Town Area", "Time Rift - Mafia of Cooks", "Time Rift - Tour",
@@ -523,6 +512,17 @@ hit_list = {
"Mustache Girl": ["The Finale", "Boss Rush", "No More Bad Guys"],
}
# Camera Tourist has a bonus that requires getting three different types of enemies in one picture.
triple_enemy_locations = [
"She Came from Outer Space",
"She Speedran from Outer Space",
"Mafia's Jumps",
"The Mustache Gauntlet",
"The Birdhouse",
"Bird Sanctuary",
"Time Rift - Tour",
]
bosses = [
"Mafia Boss",
"Conductor",

View File

@@ -1,19 +1,9 @@
from BaseClasses import Item, ItemClassification
from worlds.AutoWorld import World
from .Types import HatDLC, HatType, hat_type_to_item, Difficulty
from .Types import HatDLC, HatType, hat_type_to_item, Difficulty, ItemData, HatInTimeItem
from .Locations import get_total_locations
from .Rules import get_difficulty
from typing import Optional, NamedTuple, List, Dict
class ItemData(NamedTuple):
code: Optional[int]
classification: ItemClassification
dlc_flags: Optional[HatDLC] = HatDLC.none
class HatInTimeItem(Item):
game: str = "A Hat in Time"
from typing import Optional, List, Dict
def create_itempool(world: World) -> List[Item]:
@@ -185,86 +175,86 @@ def create_junk_items(world: World, count: int) -> List[Item]:
ahit_items = {
"Yarn": ItemData(300001, ItemClassification.progression_skip_balancing),
"Time Piece": ItemData(300002, ItemClassification.progression_skip_balancing),
"Yarn": ItemData(2000300001, ItemClassification.progression_skip_balancing),
"Time Piece": ItemData(2000300002, ItemClassification.progression_skip_balancing),
# for HatItems option
"Sprint Hat": ItemData(300049, ItemClassification.progression),
"Brewing Hat": ItemData(300050, ItemClassification.progression),
"Ice Hat": ItemData(300051, ItemClassification.progression),
"Dweller Mask": ItemData(300052, ItemClassification.progression),
"Time Stop Hat": ItemData(300053, ItemClassification.progression),
"Sprint Hat": ItemData(2000300049, ItemClassification.progression),
"Brewing Hat": ItemData(2000300050, ItemClassification.progression),
"Ice Hat": ItemData(2000300051, ItemClassification.progression),
"Dweller Mask": ItemData(2000300052, ItemClassification.progression),
"Time Stop Hat": ItemData(2000300053, ItemClassification.progression),
# Relics
"Relic (Burger Patty)": ItemData(300006, ItemClassification.progression),
"Relic (Burger Cushion)": ItemData(300007, ItemClassification.progression),
"Relic (Mountain Set)": ItemData(300008, ItemClassification.progression),
"Relic (Train)": ItemData(300009, ItemClassification.progression),
"Relic (UFO)": ItemData(300010, ItemClassification.progression),
"Relic (Cow)": ItemData(300011, ItemClassification.progression),
"Relic (Cool Cow)": ItemData(300012, ItemClassification.progression),
"Relic (Tin-foil Hat Cow)": ItemData(300013, ItemClassification.progression),
"Relic (Crayon Box)": ItemData(300014, ItemClassification.progression),
"Relic (Red Crayon)": ItemData(300015, ItemClassification.progression),
"Relic (Blue Crayon)": ItemData(300016, ItemClassification.progression),
"Relic (Green Crayon)": ItemData(300017, ItemClassification.progression),
"Relic (Burger Patty)": ItemData(2000300006, ItemClassification.progression),
"Relic (Burger Cushion)": ItemData(2000300007, ItemClassification.progression),
"Relic (Mountain Set)": ItemData(2000300008, ItemClassification.progression),
"Relic (Train)": ItemData(2000300009, ItemClassification.progression),
"Relic (UFO)": ItemData(2000300010, ItemClassification.progression),
"Relic (Cow)": ItemData(2000300011, ItemClassification.progression),
"Relic (Cool Cow)": ItemData(2000300012, ItemClassification.progression),
"Relic (Tin-foil Hat Cow)": ItemData(2000300013, ItemClassification.progression),
"Relic (Crayon Box)": ItemData(2000300014, ItemClassification.progression),
"Relic (Red Crayon)": ItemData(2000300015, ItemClassification.progression),
"Relic (Blue Crayon)": ItemData(2000300016, ItemClassification.progression),
"Relic (Green Crayon)": ItemData(2000300017, ItemClassification.progression),
# Badges
"Projectile Badge": ItemData(300024, ItemClassification.useful),
"Fast Hatter Badge": ItemData(300025, ItemClassification.useful),
"Hover Badge": ItemData(300026, ItemClassification.useful),
"Hookshot Badge": ItemData(300027, ItemClassification.progression),
"Item Magnet Badge": ItemData(300028, ItemClassification.useful),
"No Bonk Badge": ItemData(300029, ItemClassification.useful),
"Compass Badge": ItemData(300030, ItemClassification.useful),
"Scooter Badge": ItemData(300031, ItemClassification.useful),
"One-Hit Hero Badge": ItemData(300038, ItemClassification.progression, HatDLC.death_wish),
"Camera Badge": ItemData(300042, ItemClassification.progression, HatDLC.death_wish),
"Projectile Badge": ItemData(2000300024, ItemClassification.useful),
"Fast Hatter Badge": ItemData(2000300025, ItemClassification.useful),
"Hover Badge": ItemData(2000300026, ItemClassification.useful),
"Hookshot Badge": ItemData(2000300027, ItemClassification.progression),
"Item Magnet Badge": ItemData(2000300028, ItemClassification.useful),
"No Bonk Badge": ItemData(2000300029, ItemClassification.useful),
"Compass Badge": ItemData(2000300030, ItemClassification.useful),
"Scooter Badge": ItemData(2000300031, ItemClassification.useful),
"One-Hit Hero Badge": ItemData(2000300038, ItemClassification.progression, HatDLC.death_wish),
"Camera Badge": ItemData(2000300042, ItemClassification.progression, HatDLC.death_wish),
# Other
"Badge Pin": ItemData(300043, ItemClassification.useful),
"Umbrella": ItemData(300033, ItemClassification.progression),
"Progressive Painting Unlock": ItemData(300003, ItemClassification.progression),
"Badge Pin": ItemData(2000300043, ItemClassification.useful),
"Umbrella": ItemData(2000300033, ItemClassification.progression),
"Progressive Painting Unlock": ItemData(2000300003, ItemClassification.progression),
# Garbage items
"25 Pons": ItemData(300034, ItemClassification.filler),
"50 Pons": ItemData(300035, ItemClassification.filler),
"100 Pons": ItemData(300036, ItemClassification.filler),
"Health Pon": ItemData(300037, ItemClassification.filler),
"Random Cosmetic": ItemData(300044, ItemClassification.filler),
"25 Pons": ItemData(2000300034, ItemClassification.filler),
"50 Pons": ItemData(2000300035, ItemClassification.filler),
"100 Pons": ItemData(2000300036, ItemClassification.filler),
"Health Pon": ItemData(2000300037, ItemClassification.filler),
"Random Cosmetic": ItemData(2000300044, ItemClassification.filler),
# Traps
"Baby Trap": ItemData(300039, ItemClassification.trap),
"Laser Trap": ItemData(300040, ItemClassification.trap),
"Parade Trap": ItemData(300041, ItemClassification.trap),
"Baby Trap": ItemData(2000300039, ItemClassification.trap),
"Laser Trap": ItemData(2000300040, ItemClassification.trap),
"Parade Trap": ItemData(2000300041, ItemClassification.trap),
# DLC1 items
"Relic (Cake Stand)": ItemData(300018, ItemClassification.progression, HatDLC.dlc1),
"Relic (Cake)": ItemData(300019, ItemClassification.progression, HatDLC.dlc1),
"Relic (Cake Slice)": ItemData(300020, ItemClassification.progression, HatDLC.dlc1),
"Relic (Shortcake)": ItemData(300021, ItemClassification.progression, HatDLC.dlc1),
"Relic (Cake Stand)": ItemData(2000300018, ItemClassification.progression, HatDLC.dlc1),
"Relic (Cake)": ItemData(2000300019, ItemClassification.progression, HatDLC.dlc1),
"Relic (Cake Slice)": ItemData(2000300020, ItemClassification.progression, HatDLC.dlc1),
"Relic (Shortcake)": ItemData(2000300021, ItemClassification.progression, HatDLC.dlc1),
# DLC2 items
"Relic (Necklace Bust)": ItemData(300022, ItemClassification.progression, HatDLC.dlc2),
"Relic (Necklace)": ItemData(300023, ItemClassification.progression, HatDLC.dlc2),
"Metro Ticket - Yellow": ItemData(300045, ItemClassification.progression, HatDLC.dlc2),
"Metro Ticket - Green": ItemData(300046, ItemClassification.progression, HatDLC.dlc2),
"Metro Ticket - Blue": ItemData(300047, ItemClassification.progression, HatDLC.dlc2),
"Metro Ticket - Pink": ItemData(300048, ItemClassification.progression, HatDLC.dlc2),
"Relic (Necklace Bust)": ItemData(2000300022, ItemClassification.progression, HatDLC.dlc2),
"Relic (Necklace)": ItemData(2000300023, ItemClassification.progression, HatDLC.dlc2),
"Metro Ticket - Yellow": ItemData(2000300045, ItemClassification.progression, HatDLC.dlc2),
"Metro Ticket - Green": ItemData(2000300046, ItemClassification.progression, HatDLC.dlc2),
"Metro Ticket - Blue": ItemData(2000300047, ItemClassification.progression, HatDLC.dlc2),
"Metro Ticket - Pink": ItemData(2000300048, ItemClassification.progression, HatDLC.dlc2),
}
act_contracts = {
"Snatcher's Contract - The Subcon Well": ItemData(300200, ItemClassification.progression),
"Snatcher's Contract - Toilet of Doom": ItemData(300201, ItemClassification.progression),
"Snatcher's Contract - Queen Vanessa's Manor": ItemData(300202, ItemClassification.progression),
"Snatcher's Contract - Mail Delivery Service": ItemData(300203, ItemClassification.progression),
"Snatcher's Contract - The Subcon Well": ItemData(2000300200, ItemClassification.progression),
"Snatcher's Contract - Toilet of Doom": ItemData(2000300201, ItemClassification.progression),
"Snatcher's Contract - Queen Vanessa's Manor": ItemData(2000300202, ItemClassification.progression),
"Snatcher's Contract - Mail Delivery Service": ItemData(2000300203, ItemClassification.progression),
}
alps_hooks = {
"Zipline Unlock - The Birdhouse Path": ItemData(300204, ItemClassification.progression),
"Zipline Unlock - The Lava Cake Path": ItemData(300205, ItemClassification.progression),
"Zipline Unlock - The Windmill Path": ItemData(300206, ItemClassification.progression),
"Zipline Unlock - The Twilight Bell Path": ItemData(300207, ItemClassification.progression),
"Zipline Unlock - The Birdhouse Path": ItemData(2000300204, ItemClassification.progression),
"Zipline Unlock - The Lava Cake Path": ItemData(2000300205, ItemClassification.progression),
"Zipline Unlock - The Windmill Path": ItemData(2000300206, ItemClassification.progression),
"Zipline Unlock - The Twilight Bell Path": ItemData(2000300207, ItemClassification.progression),
}
relic_groups = {

File diff suppressed because it is too large Load Diff

View File

@@ -532,11 +532,9 @@ class DWExcludeAnnoyingBonuses(Toggle):
- Snatcher's Hit List
- 10 Seconds until Self-Destruct
- Killing Two Birds
- Snatcher Coins in Battle of the Birds
- Zero Jumps
- Bird Sanctuary
- Wound-Up Windmill
- Snatcher Coins in Alpine Skyline
- Seal the Deal"""
display_name = "Exclude Annoying Death Wish Full Completions"
default = 1
@@ -674,6 +672,7 @@ slot_data_options: typing.Dict[str, type(Option)] = {
"CTRLogic": CTRLogic,
"RandomizeHatOrder": RandomizeHatOrder,
"UmbrellaLogic": UmbrellaLogic,
"StartWithCompassBadge": StartWithCompassBadge,
"CompassBadgeMode": CompassBadgeMode,
"ShuffleStorybookPages": ShuffleStorybookPages,
"ShuffleActContracts": ShuffleActContracts,

View File

@@ -1,11 +1,10 @@
from worlds.AutoWorld import World
from BaseClasses import Region, Entrance, ItemClassification, Location
from .Locations import HatInTimeLocation, location_table, storybook_pages, event_locs, is_location_valid, \
shop_locations, get_tasksanity_start_id
from .Items import HatInTimeItem
from .Types import ChapterIndex, Difficulty
from .Types import ChapterIndex, Difficulty, HatInTimeLocation, HatInTimeItem
from .Locations import location_table, storybook_pages, event_locs, is_location_valid, \
shop_locations, get_tasksanity_start_id, snatcher_coins, zero_jumps, zero_jumps_expert, zero_jumps_hard
import typing
from .Rules import set_rift_rules
from .Rules import set_rift_rules, get_difficulty
# ChapterIndex: region
@@ -881,6 +880,16 @@ def create_events(world: World) -> int:
if not is_location_valid(world, name):
continue
if world.is_dw():
if name in snatcher_coins.keys():
name = f"{name} ({data.region})"
elif name in zero_jumps:
if get_difficulty(world) < Difficulty.HARD and name in zero_jumps_hard:
continue
if get_difficulty(world) < Difficulty.EXPERT and name in zero_jumps_expert:
continue
event: Location = create_event(name, world.multiworld.get_region(data.region, world.player), world)
event.show_in_spoiler = False
count += 1

View File

@@ -1,8 +1,8 @@
from worlds.AutoWorld import World, CollectionState
from worlds.generic.Rules import add_rule, set_rule
from .Locations import location_table, zipline_unlocks, is_location_valid, contract_locations, \
shop_locations, event_locs
from .Types import HatType, ChapterIndex, hat_type_to_item, Difficulty
shop_locations, event_locs, snatcher_coins
from .Types import HatType, ChapterIndex, hat_type_to_item, Difficulty, HatDLC
from BaseClasses import Location, Entrance, Region
import typing
@@ -62,7 +62,7 @@ def get_difficulty(world: World) -> Difficulty:
return Difficulty(world.multiworld.LogicDifficulty[world.player].value)
def has_paintings(state: CollectionState, world: World, count: int) -> bool:
def has_paintings(state: CollectionState, world: World, count: int, surf: bool = True) -> bool:
if not painting_logic(world):
return True
@@ -71,11 +71,11 @@ def has_paintings(state: CollectionState, world: World, count: int) -> bool:
return True
# All paintings can be skipped with No Bonk, very easily, if the player knows
if get_difficulty(world) >= Difficulty.MODERATE and can_surf(state, world):
if surf and get_difficulty(world) >= Difficulty.MODERATE and can_surf(state, world):
return True
paintings: int = state.count("Progressive Painting Unlock", world.player)
if get_difficulty(world) >= Difficulty.MODERATE:
if surf and get_difficulty(world) >= Difficulty.MODERATE:
# Green+Yellow paintings can also be skipped easily
if count == 1 or paintings >= 1 and count == 3:
return True
@@ -264,6 +264,10 @@ def set_rules(world: World):
if key in contract_locations.keys():
continue
if data.dlc_flags is HatDLC.death_wish or data.dlc_flags is HatDLC.dlc2_dw:
if key in snatcher_coins.keys():
key = f"{key} ({data.region})"
location = world.multiworld.get_location(key, world.player)
for hat in data.required_hats:
@@ -277,7 +281,10 @@ def set_rules(world: World):
add_rule(location, lambda state: state.has("Umbrella", world.player))
if data.paintings > 0 and world.multiworld.ShuffleSubconPaintings[world.player].value > 0:
add_rule(location, lambda state, paintings=data.paintings: has_paintings(state, world, paintings))
if "Toilet of Doom" not in key:
add_rule(location, lambda state, paintings=data.paintings: has_paintings(state, world, paintings))
else:
add_rule(location, lambda state, paintings=data.paintings: has_paintings(state, world, paintings, False))
if data.hit_requirement > 0:
if data.hit_requirement == 1:
@@ -402,6 +409,10 @@ def set_moderate_rules(world: World):
# 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
add_rule(world.multiworld.get_location("Mafia Town - Clock Tower Chest", world.player), lambda state: True)
add_rule(world.multiworld.get_location("Mafia Town - Top of Ruined Tower", world.player), lambda state: True)
@@ -430,6 +441,13 @@ def set_moderate_rules(world: World):
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: 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
add_rule(world.multiworld.get_location("Rock the Boat - Post Captain Rescue", world.player), lambda state: True)
@@ -451,13 +469,6 @@ def set_moderate_rules(world: World):
# The player can quite literally walk past the fan from the side without Time Stop.
set_rule(world.multiworld.get_location("Pink Paw Station - Behind Fan", world.player), lambda state: True)
# The player can't jump back down to the manhole due to a fall damage volume preventing them from doing so
set_rule(world.multiworld.get_location("Act Completion (Pink Paw Manhole)", 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))
and can_use_hat(state, world, HatType.ICE))
# 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)
@@ -473,14 +484,13 @@ def set_hard_rules(world: World):
lambda state: can_use_hat(state, world, HatType.SPRINT)
and state.has("Scooter Badge", world.player), "or")
# Hard: Cross Subcon boss arena gap with No Bonk + SDJ, allowing access to the boss arena chest
# Doing this in reverse from YCHE is expert logic, which expects you to cherry hover
add_rule(world.multiworld.get_location("Subcon Forest - Boss Arena Chest", world.player),
lambda state: can_surf(state, world) and can_sdj(state, world), "or")
set_rule(world.multiworld.get_location("Subcon Forest - Dweller Floating Rocks", 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))
# SDJ
add_rule(world.multiworld.get_location("Subcon Forest - Long Tree Climb Chest", world.player),
lambda state: can_sdj(state, world)
@@ -492,13 +502,14 @@ def set_hard_rules(world: World):
add_rule(world.multiworld.get_location("Act Completion (Time Rift - Curly Tail Trail)", world.player),
lambda state: can_sdj(state, world), "or")
add_rule(world.multiworld.get_location("Act Completion (The Finale)", world.player),
lambda state: can_use_hat(state, world, HatType.DWELLER) and can_sdj(state, world), "or")
# Hard: Mystifying Time Mesa time trial without hats
set_rule(world.multiworld.get_location("Alpine Skyline - Mystifying Time Mesa: Zipline", world.player),
lambda state: can_use_hookshot(state, world))
# Finale Telescope with only Ice Hat
add_rule(world.multiworld.get_entrance("Telescope -> Time's End", world.player),
lambda state: can_use_hat(state, world, HatType.ICE), "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),
@@ -515,6 +526,10 @@ def set_hard_rules(world: World):
def set_expert_rules(world: World):
# 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.get_chapter_cost(ChapterIndex.FINALE)))
# Expert: Mafia Town - Above Boats with nothing
set_rule(world.multiworld.get_location("Mafia Town - Above Boats", world.player), lambda state: True)
@@ -526,8 +541,6 @@ def set_expert_rules(world: World):
# 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.
set_rule(world.multiworld.get_location("Alpine Skyline - The Twilight Path", world.player), lambda state: True)
add_rule(world.multiworld.get_entrance("-> The Twilight Bell", world.player),
lambda state: can_use_hookshot(state, world), "or")
@@ -640,10 +653,6 @@ def set_subcon_rules(world: World):
and (not painting_logic(world) or has_paintings(state, world, 1))
or state.has("YCHE Access", world.player))
if world.multiworld.UmbrellaLogic[world.player].value > 0:
add_rule(world.multiworld.get_location("Act Completion (Toilet of Doom)", world.player),
lambda state: can_hit(state, world))
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))
@@ -907,9 +916,12 @@ def set_event_rules(world: World):
if not is_location_valid(world, name):
continue
if (data.dlc_flags is HatDLC.death_wish or data.dlc_flags is HatDLC.dlc2_dw) and name in snatcher_coins.keys():
name = f"{name} ({data.region})"
event: Location = world.multiworld.get_location(name, world.player)
if data.act_complete_event:
if data.act_event:
add_rule(event, world.multiworld.get_location(f"Act Completion ({data.region})", world.player).access_rule)

View File

@@ -1,4 +1,14 @@
from enum import IntEnum, IntFlag
from typing import NamedTuple, Optional, List
from BaseClasses import Location, Item, ItemClassification
class HatInTimeLocation(Location):
game: str = "A Hat in Time"
class HatInTimeItem(Item):
game: str = "A Hat in Time"
class HatType(IntEnum):
@@ -15,6 +25,7 @@ class HatDLC(IntFlag):
dlc1 = 0b001
dlc2 = 0b010
death_wish = 0b100
dlc2_dw = 0b0110 # for Snatcher Coins in Nyakuza Metro
class ChapterIndex(IntEnum):
@@ -35,6 +46,30 @@ class Difficulty(IntEnum):
EXPERT = 2
class LocData(NamedTuple):
id: Optional[int] = 0
region: Optional[str] = ""
required_hats: Optional[List[HatType]] = [HatType.NONE]
hookshot: Optional[bool] = False
dlc_flags: Optional[HatDLC] = HatDLC.none
paintings: Optional[int] = 0 # Paintings required for Subcon painting shuffle
misc_required: Optional[List[str]] = []
# For UmbrellaLogic setting
umbrella: Optional[bool] = False # Umbrella required for this check
hit_requirement: Optional[int] = 0 # Hit required. 1 = Umbrella/Brewing only, 2 = bypass w/Dweller Mask (bells)
# Other
act_event: Optional[bool] = False # Only used for event locations. Copy access rule from act completion
nyakuza_thug: Optional[str] = "" # Name of Nyakuza thug NPC (for metro shops)
class ItemData(NamedTuple):
code: Optional[int]
classification: ItemClassification
dlc_flags: Optional[HatDLC] = HatDLC.none
hat_type_to_item = {
HatType.SPRINT: "Sprint Hat",
HatType.BREWING: "Brewing Hat",

View File

@@ -1,10 +1,10 @@
from BaseClasses import Item, ItemClassification, LocationProgressType, Tutorial
from .Items import HatInTimeItem, item_table, create_item, relic_groups, act_contracts, create_itempool
from BaseClasses import Item, ItemClassification, Tutorial
from .Items import item_table, create_item, relic_groups, act_contracts, create_itempool
from .Regions import create_regions, randomize_act_entrances, chapter_act_info, create_events, get_shuffled_region
from .Locations import location_table, contract_locations, is_location_valid, get_location_names, get_tasksanity_start_id
from .Rules import set_rules
from .Options import ahit_options, slot_data_options, adjust_options
from .Types import HatType, ChapterIndex
from .Types import HatType, ChapterIndex, HatInTimeItem
from .DeathWishLocations import create_dw_regions, dw_classes, death_wishes
from .DeathWishRules import set_dw_rules, create_enemy_events
from worlds.AutoWorld import World, WebWorld