mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-29 22:23:24 -07:00
1.2
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
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
|
||||
from .Types import HatType
|
||||
from .Rules import can_use_hat, can_use_hookshot, can_hit, zipline_logic, has_paintings, get_difficulty
|
||||
from .Types import HatType, Difficulty
|
||||
from .DeathWishLocations import dw_prereqs, dw_candles
|
||||
from .Items import HatInTimeItem
|
||||
from BaseClasses import Entrance, Location, ItemClassification
|
||||
from worlds.generic.Rules import add_rule
|
||||
from worlds.generic.Rules import add_rule, set_rule
|
||||
from typing import List, Callable
|
||||
|
||||
# Any speedruns expect the player to have Sprint Hat
|
||||
@@ -21,18 +21,18 @@ dw_requirements = {
|
||||
"Community Rift: Rhythm Jump Studio": LocData(required_hats=[HatType.ICE]),
|
||||
|
||||
"Speedrun Well": LocData(hookshot=True, hit_requirement=1, required_hats=[HatType.SPRINT]),
|
||||
"Boss Rush": LocData(hit_requirement=1, umbrella=True),
|
||||
"Boss Rush": LocData(umbrella=True, hookshot=True),
|
||||
"Community Rift: Twilight Travels": LocData(hookshot=True, required_hats=[HatType.DWELLER]),
|
||||
|
||||
"Bird Sanctuary": LocData(hookshot=True),
|
||||
"Wound-Up Windmill": LocData(hookshot=True),
|
||||
"The Illness has Speedrun": LocData(hookshot=True, required_hats=[HatType.SPRINT]),
|
||||
"The Illness has Speedrun": LocData(hookshot=True),
|
||||
"Community Rift: The Mountain Rift": LocData(hookshot=True, required_hats=[HatType.DWELLER]),
|
||||
"Camera Tourist": LocData(misc_required=["Camera Badge"]),
|
||||
|
||||
"The Mustache Gauntlet": LocData(hookshot=True, required_hats=[HatType.DWELLER]),
|
||||
|
||||
"Rift Collapse - Deep Sea": LocData(hookshot=True, required_hats=[HatType.DWELLER]),
|
||||
"Rift Collapse - Deep Sea": LocData(hookshot=True),
|
||||
}
|
||||
|
||||
# Includes main objective requirements
|
||||
@@ -43,15 +43,16 @@ dw_bonus_requirements = {
|
||||
|
||||
"10 Seconds until Self-Destruct": LocData(misc_required=["One-Hit Hero Badge", "Badge Pin"]),
|
||||
|
||||
"Boss Rush": LocData(misc_required=["One-Hit Hero Badge"]),
|
||||
"Boss Rush": LocData(misc_required=["One-Hit Hero Badge", "Badge Pin"]),
|
||||
"Community Rift: Twilight Travels": LocData(required_hats=[HatType.BREWING]),
|
||||
|
||||
"Bird Sanctuary": LocData(misc_required=["One-Hit Hero Badge", "Badge Pin"], required_hats=[HatType.DWELLER]),
|
||||
"Wound-Up Windmill": LocData(misc_required=["One-Hit Hero Badge", "Badge Pin"]),
|
||||
"The Illness has Speedrun": LocData(required_hats=[HatType.SPRINT]),
|
||||
|
||||
"The Mustache Gauntlet": LocData(required_hats=[HatType.ICE]),
|
||||
|
||||
"Rift Collapse - Deep Sea": LocData(required_hats=[HatType.SPRINT]),
|
||||
"Rift Collapse - Deep Sea": LocData(required_hats=[HatType.DWELLER]),
|
||||
}
|
||||
|
||||
dw_stamp_costs = {
|
||||
@@ -122,21 +123,9 @@ def set_dw_rules(world: World):
|
||||
continue
|
||||
|
||||
# Specific Rules
|
||||
if name == "The Illness has Speedrun":
|
||||
# killing the flowers without the umbrella is way too slow
|
||||
add_rule(main_objective, lambda state: state.has("Umbrella", world.player))
|
||||
elif name == "The Mustache Gauntlet":
|
||||
# don't get burned bonus requires a way to kill fire crows without being burned
|
||||
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":
|
||||
add_rule(main_objective, lambda state: can_use_hat(state, world, HatType.TIME_STOP), "or")
|
||||
|
||||
if name in dw_candles:
|
||||
set_candle_dw_rules(name, world)
|
||||
modify_dw_rules(world, name)
|
||||
|
||||
main_rule: Callable[[CollectionState], bool]
|
||||
|
||||
for i in range(len(temp_list)):
|
||||
loc = temp_list[i]
|
||||
data: LocData
|
||||
@@ -217,13 +206,55 @@ def set_dw_rules(world: World):
|
||||
world.player)
|
||||
|
||||
|
||||
def modify_dw_rules(world: World, name: str):
|
||||
difficulty: Difficulty = get_difficulty(world)
|
||||
main_objective = world.multiworld.get_location(f"{name} - Main Objective", world.player)
|
||||
full_clear = world.multiworld.get_location(f"{name} - All Clear", world.player)
|
||||
|
||||
if name == "The Illness has Speedrun":
|
||||
# All stamps with hookshot only in Expert
|
||||
if difficulty >= Difficulty.EXPERT:
|
||||
set_rule(full_clear, lambda state: True)
|
||||
else:
|
||||
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
|
||||
if difficulty >= Difficulty.HARD:
|
||||
set_rule(main_objective, lambda state: True)
|
||||
|
||||
elif name == "Speedrun Well":
|
||||
# All stamps with nothing :)
|
||||
if difficulty >= Difficulty.EXPERT:
|
||||
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))
|
||||
|
||||
elif name == "So You're Back from Outer Space":
|
||||
# Without Hookshot
|
||||
if difficulty >= Difficulty.HARD:
|
||||
set_rule(main_objective, lambda state: True)
|
||||
|
||||
if name in dw_candles:
|
||||
set_candle_dw_rules(name, world)
|
||||
|
||||
|
||||
def get_total_dw_stamps(state: CollectionState, world: World) -> int:
|
||||
if world.multiworld.DWShuffle[world.player].value > 0:
|
||||
return 999 # no stamp costs in death wish shuffle
|
||||
|
||||
count: int = 0
|
||||
peace_and_tranquility: bool = world.multiworld.DWEnableBonus[world.player].value == 0 \
|
||||
and world.multiworld.DWAutoCompleteBonuses[world.player].value == 0
|
||||
|
||||
for name in death_wishes:
|
||||
if name == "Snatcher Coins in Nyakuza Metro" and not world.is_dlc2():
|
||||
@@ -234,12 +265,6 @@ def get_total_dw_stamps(state: CollectionState, world: World) -> int:
|
||||
else:
|
||||
continue
|
||||
|
||||
# If bonus rewards and auto bonus completion is off, obtaining stamps via P&T is in logic
|
||||
# Candles don't have P&T
|
||||
if peace_and_tranquility and name not in dw_candles:
|
||||
count += 2
|
||||
continue
|
||||
|
||||
if state.has(f"2 Stamps - {name}", world.player):
|
||||
count += 2
|
||||
elif name not in dw_candles:
|
||||
@@ -272,7 +297,7 @@ def set_candle_dw_rules(name: str, world: World):
|
||||
|
||||
add_rule(full_clear, lambda state: state.has("CTR Access", world.player)
|
||||
or state.has("HUMT Access", world.player)
|
||||
and (world.multiworld.UmbrellaLogic[world.player].value == 0 or state.has("Umbrella", world.player))
|
||||
and can_hit(state, world, True)
|
||||
or state.has("DWTM Access", world.player)
|
||||
or state.has("TGV Access", world.player))
|
||||
|
||||
@@ -424,8 +449,10 @@ def set_enemy_rules(world: World):
|
||||
|
||||
elif enemy == "Snatcher" or enemy == "Mustache Girl":
|
||||
if area == "Boss Rush":
|
||||
# need to be able to kill toilet
|
||||
add_rule(event, lambda state: can_hit(state, world))
|
||||
# need to be able to kill toilet and snatcher
|
||||
add_rule(event, lambda state: can_hit(state, world) and can_use_hookshot(state, world))
|
||||
if enemy == "Mustache Girl":
|
||||
add_rule(event, lambda state: can_hit(state, world, True) and can_use_hookshot(state, world))
|
||||
|
||||
elif area == "The Finale" and enemy == "Mustache Girl":
|
||||
add_rule(event, lambda state: can_use_hookshot(state, world)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from BaseClasses import Item, ItemClassification
|
||||
from worlds.AutoWorld import World
|
||||
from .Types import HatDLC, HatType, hat_type_to_item
|
||||
from .Types import HatDLC, HatType, hat_type_to_item, Difficulty
|
||||
from .Locations import get_total_locations
|
||||
from .Rules import get_difficulty, is_player_knowledgeable
|
||||
from .Rules import get_difficulty
|
||||
from typing import Optional, NamedTuple, List, Dict
|
||||
|
||||
|
||||
@@ -45,10 +45,13 @@ def create_itempool(world: World) -> List[Item]:
|
||||
if item_type is ItemClassification.progression \
|
||||
or item_type is ItemClassification.progression_skip_balancing:
|
||||
continue
|
||||
|
||||
if get_difficulty(world) >= 1 or is_player_knowledgeable(world) \
|
||||
and (name == "Scooter Badge" or name == "No Bonk Badge") and not world.is_dw_only():
|
||||
item_type = ItemClassification.progression
|
||||
else:
|
||||
if name == "Scooter Badge":
|
||||
if world.multiworld.CTRLogic[world.player].value >= 1 or get_difficulty(world) >= Difficulty.MODERATE:
|
||||
item_type = ItemClassification.progression
|
||||
elif name == "No Bonk Badge":
|
||||
if get_difficulty(world) >= Difficulty.MODERATE:
|
||||
item_type = ItemClassification.progression
|
||||
|
||||
# some death wish bonuses require one hit hero + hookshot
|
||||
if world.is_dw() and name == "Badge Pin" and not world.is_dw_only():
|
||||
|
||||
@@ -170,10 +170,10 @@ ahit_locations = {
|
||||
"Dead Bird Studio - Red Building Top": LocData(305024, "Dead Bird Studio - Elevator Area"),
|
||||
"Dead Bird Studio - Behind Water Tower": LocData(305248, "Dead Bird Studio - Elevator Area"),
|
||||
"Dead Bird Studio - Side of House": LocData(305247, "Dead Bird Studio - Elevator Area"),
|
||||
"Dead Bird Studio - DJ Grooves Sign Chest": LocData(303901, "Dead Bird Studio", hit_requirement=1),
|
||||
"Dead Bird Studio - Tightrope Chest": LocData(303898, "Dead Bird Studio", hit_requirement=1),
|
||||
"Dead Bird Studio - Tepee Chest": LocData(303899, "Dead Bird Studio", hit_requirement=1),
|
||||
"Dead Bird Studio - Conductor Chest": LocData(303900, "Dead Bird Studio", hit_requirement=1),
|
||||
"Dead Bird Studio - DJ Grooves Sign Chest": LocData(303901, "Dead Bird Studio - Post Elevator Area", hit_requirement=1),
|
||||
"Dead Bird Studio - Tightrope Chest": LocData(303898, "Dead Bird Studio - Post Elevator Area", hit_requirement=1),
|
||||
"Dead Bird Studio - Tepee Chest": LocData(303899, "Dead Bird Studio - Post Elevator Area", hit_requirement=1),
|
||||
"Dead Bird Studio - Conductor Chest": LocData(303900, "Dead Bird Studio - Post Elevator Area", hit_requirement=1),
|
||||
|
||||
"Murder on the Owl Express - Cafeteria": LocData(305313, "Murder on the Owl Express"),
|
||||
"Murder on the Owl Express - Luggage Room Top": LocData(305090, "Murder on the Owl Express"),
|
||||
@@ -276,11 +276,11 @@ ahit_locations = {
|
||||
"Queen Vanessa's Manor - Chandelier": LocData(325546, "Queen Vanessa's Manor", hit_requirement=2, paintings=1),
|
||||
|
||||
# Alpine Skyline
|
||||
"Alpine Skyline - Goat Village: Below Hookpoint": LocData(334856, "Goat Village"),
|
||||
"Alpine Skyline - Goat Village: Hidden Branch": LocData(334855, "Goat Village"),
|
||||
"Alpine Skyline - Goat Village: Below Hookpoint": LocData(334856, "Alpine Skyline Area (TIHS)"),
|
||||
"Alpine Skyline - Goat Village: Hidden Branch": LocData(334855, "Alpine Skyline Area (TIHS)"),
|
||||
"Alpine Skyline - Goat Refinery": LocData(333635, "Alpine Skyline Area"),
|
||||
"Alpine Skyline - Bird Pass Fork": LocData(335911, "Alpine Skyline Area"),
|
||||
"Alpine Skyline - Yellow Band Hills": LocData(335756, "Alpine Skyline Area", required_hats=[HatType.BREWING]),
|
||||
"Alpine Skyline - Bird Pass Fork": LocData(335911, "Alpine Skyline Area (TIHS)"),
|
||||
"Alpine Skyline - Yellow Band Hills": LocData(335756, "Alpine Skyline Area (TIHS)", required_hats=[HatType.BREWING]),
|
||||
"Alpine Skyline - The Purrloined Village: Horned Stone": LocData(335561, "Alpine Skyline Area"),
|
||||
"Alpine Skyline - The Purrloined Village: Chest Reward": LocData(334831, "Alpine Skyline Area"),
|
||||
"Alpine Skyline - The Birdhouse: Triple Crow Chest": LocData(334758, "The Birdhouse"),
|
||||
@@ -295,7 +295,7 @@ ahit_locations = {
|
||||
|
||||
"Alpine Skyline - Mystifying Time Mesa: Zipline": LocData(337058, "Alpine Skyline Area"),
|
||||
"Alpine Skyline - Mystifying Time Mesa: Gate Puzzle": LocData(336052, "Alpine Skyline Area"),
|
||||
"Alpine Skyline - Ember Summit": LocData(336311, "Alpine Skyline Area"),
|
||||
"Alpine Skyline - Ember Summit": LocData(336311, "Alpine Skyline Area (TIHS)"),
|
||||
"Alpine Skyline - The Lava Cake: Center Fence Cage": LocData(335448, "The Lava Cake"),
|
||||
"Alpine Skyline - The Lava Cake: Outer Island Chest": LocData(334291, "The Lava Cake"),
|
||||
"Alpine Skyline - The Lava Cake: Dweller Pillars": LocData(335417, "The Lava Cake"),
|
||||
@@ -304,7 +304,7 @@ ahit_locations = {
|
||||
"Alpine Skyline - The Twilight Bell: Wide Purple Platform": LocData(336478, "The Twilight Bell"),
|
||||
"Alpine Skyline - The Twilight Bell: Ice Platform": LocData(335826, "The Twilight Bell"),
|
||||
"Alpine Skyline - Goat Outpost Horn": LocData(334760, "Alpine Skyline Area"),
|
||||
"Alpine Skyline - Windy Passage": LocData(334776, "Alpine Skyline Area"),
|
||||
"Alpine Skyline - Windy Passage": LocData(334776, "Alpine Skyline Area (TIHS)"),
|
||||
"Alpine Skyline - The Windmill: Inside Pon Cluster": LocData(336395, "The Windmill"),
|
||||
"Alpine Skyline - The Windmill: Entrance": LocData(335783, "The Windmill"),
|
||||
"Alpine Skyline - The Windmill: Dropdown": LocData(335815, "The Windmill"),
|
||||
@@ -327,7 +327,8 @@ ahit_locations = {
|
||||
"Rock the Boat - Reception Room - Under Desk": LocData(304047, "Rock the Boat", dlc_flags=HatDLC.dlc1),
|
||||
"Rock the Boat - Lamp Post": LocData(304048, "Rock the Boat", dlc_flags=HatDLC.dlc1),
|
||||
"Rock the Boat - Iceberg Top": LocData(304046, "Rock the Boat", dlc_flags=HatDLC.dlc1),
|
||||
"Rock the Boat - Post Captain Rescue": LocData(304049, "Rock the Boat", dlc_flags=HatDLC.dlc1),
|
||||
"Rock the Boat - Post Captain Rescue": LocData(304049, "Rock the Boat", dlc_flags=HatDLC.dlc1,
|
||||
required_hats=[HatType.ICE]),
|
||||
|
||||
"Nyakuza Metro - Main Station Dining Area": LocData(304105, "Nyakuza Free Roam", dlc_flags=HatDLC.dlc2),
|
||||
"Nyakuza Metro - Top of Ramen Shop": LocData(304104, "Nyakuza Free Roam", dlc_flags=HatDLC.dlc2),
|
||||
@@ -350,14 +351,14 @@ ahit_locations = {
|
||||
}
|
||||
|
||||
act_completions = {
|
||||
"Act Completion (Time Rift - Gallery)": LocData(312758, "Time Rift - Gallery"),
|
||||
"Act Completion (Time Rift - Gallery)": LocData(312758, "Time Rift - Gallery", required_hats=[HatType.BREWING]),
|
||||
"Act Completion (Time Rift - The Lab)": LocData(312838, "Time Rift - The Lab"),
|
||||
|
||||
"Act Completion (Welcome to Mafia Town)": LocData(311771, "Welcome to Mafia Town"),
|
||||
"Act Completion (Barrel Battle)": LocData(311958, "Barrel Battle"),
|
||||
"Act Completion (She Came from Outer Space)": LocData(312262, "She Came from Outer Space"),
|
||||
"Act Completion (Down with the Mafia!)": LocData(311326, "Down with the Mafia!"),
|
||||
"Act Completion (Cheating the Race)": LocData(312318, "Cheating the Race"),
|
||||
"Act Completion (Cheating the Race)": LocData(312318, "Cheating the Race", required_hats=[HatType.TIME_STOP]),
|
||||
"Act Completion (Heating Up Mafia Town)": LocData(311481, "Heating Up Mafia Town", umbrella=True),
|
||||
"Act Completion (The Golden Vault)": LocData(312250, "The Golden Vault"),
|
||||
"Act Completion (Time Rift - Bazaar)": LocData(312465, "Time Rift - Bazaar"),
|
||||
@@ -404,9 +405,10 @@ act_completions = {
|
||||
|
||||
"Act Completion (Bon Voyage!)": LocData(311520, "Bon Voyage!", dlc_flags=HatDLC.dlc1, hookshot=True),
|
||||
"Act Completion (Ship Shape)": LocData(311451, "Ship Shape", dlc_flags=HatDLC.dlc1),
|
||||
"Act Completion (Rock the Boat)": LocData(311437, "Rock the Boat", dlc_flags=HatDLC.dlc1),
|
||||
"Act Completion (Rock the Boat)": LocData(311437, "Rock the Boat", dlc_flags=HatDLC.dlc1, required_hats=[HatType.ICE]),
|
||||
"Act Completion (Time Rift - Balcony)": LocData(312226, "Time Rift - Balcony", dlc_flags=HatDLC.dlc1, hookshot=True),
|
||||
"Act Completion (Time Rift - Deep Sea)": LocData(312434, "Time Rift - Deep Sea", dlc_flags=HatDLC.dlc1, hookshot=True),
|
||||
"Act Completion (Time Rift - Deep Sea)": LocData(312434, "Time Rift - Deep Sea", dlc_flags=HatDLC.dlc1,
|
||||
hookshot=True, required_hats=[HatType.DWELLER, HatType.ICE]),
|
||||
|
||||
"Act Completion (Nyakuza Metro Intro)": LocData(311138, "Nyakuza Free Roam", dlc_flags=HatDLC.dlc2),
|
||||
|
||||
@@ -422,7 +424,7 @@ act_completions = {
|
||||
|
||||
"Act Completion (Green Clean Manhole)": LocData(311388, "Green Clean Manhole",
|
||||
dlc_flags=HatDLC.dlc2,
|
||||
required_hats=[HatType.ICE]),
|
||||
required_hats=[HatType.ICE, HatType.DWELLER]),
|
||||
|
||||
"Act Completion (Bluefin Tunnel)": LocData(311208, "Bluefin Tunnel", dlc_flags=HatDLC.dlc2),
|
||||
|
||||
@@ -669,16 +671,6 @@ zipline_unlocks = {
|
||||
"Alpine Skyline - The Twilight Path": "Zipline Unlock - The Twilight Bell Path",
|
||||
}
|
||||
|
||||
# Locations in Alpine that are available in The Illness has Spread
|
||||
# Goat Village locations don't need to be put here
|
||||
tihs_locations = [
|
||||
"Alpine Skyline - Bird Pass Fork",
|
||||
"Alpine Skyline - Yellow Band Hills",
|
||||
"Alpine Skyline - Ember Summit",
|
||||
"Alpine Skyline - Goat Outpost Horn",
|
||||
"Alpine Skyline - Windy Passage",
|
||||
]
|
||||
|
||||
event_locs = {
|
||||
"HUMT Access": LocData(0, "Heating Up Mafia Town", act_complete_event=False),
|
||||
"TOD Access": LocData(0, "Toilet of Doom", act_complete_event=False),
|
||||
|
||||
@@ -60,7 +60,7 @@ def adjust_options(world: World):
|
||||
world.multiworld.ShuffleStorybookPages[world.player].value = 0
|
||||
world.multiworld.ShuffleActContracts[world.player].value = 0
|
||||
world.multiworld.EnableDLC1[world.player].value = 0
|
||||
world.multiworld.LogicDifficulty[world.player].value = 0
|
||||
world.multiworld.LogicDifficulty[world.player].value = -1
|
||||
world.multiworld.KnowledgeChecks[world.player].value = 0
|
||||
world.multiworld.DWTimePieceRequirement[world.player].value = 0
|
||||
|
||||
@@ -118,17 +118,20 @@ class FinaleShuffle(Toggle):
|
||||
class LogicDifficulty(Choice):
|
||||
"""Choose the difficulty setting for logic."""
|
||||
display_name = "Logic Difficulty"
|
||||
option_normal = 0
|
||||
option_normal = -1
|
||||
option_moderate = 0
|
||||
option_hard = 1
|
||||
option_expert = 2
|
||||
default = 0
|
||||
default = -1
|
||||
|
||||
|
||||
class KnowledgeChecks(Toggle):
|
||||
"""Put tricks into logic that are not necessarily difficult,
|
||||
but require knowledge that is not obvious or commonly known. Can include glitches such as No Bonk Surfing.
|
||||
This option will be forced on if logic difficulty is at least hard."""
|
||||
display_name = "Knowledge Checks"
|
||||
class CTRLogic(Choice):
|
||||
"""Choose how you want to logically clear Cheating the Race."""
|
||||
display_name = "Cheating the Race Logic"
|
||||
option_time_stop_only = 0
|
||||
option_scooter = 1
|
||||
option_sprint = 2
|
||||
option_nothing = 3
|
||||
default = 0
|
||||
|
||||
|
||||
@@ -350,12 +353,6 @@ class BadgeSellerMaxItems(Range):
|
||||
default = 8
|
||||
|
||||
|
||||
class CTRWithSprint(Toggle):
|
||||
"""If enabled, clearing Cheating the Race with just Sprint Hat can be in logic."""
|
||||
display_name = "Cheating the Race with Sprint Hat"
|
||||
default = 0
|
||||
|
||||
|
||||
class EnableDLC1(Toggle):
|
||||
"""Shuffle content from The Arctic Cruise (Chapter 6) into the game. This also includes the Tour time rift.
|
||||
DO NOT ENABLE THIS OPTION IF YOU DO NOT HAVE SEAL THE DEAL DLC INSTALLED!!!"""
|
||||
@@ -396,7 +393,7 @@ class ExcludeTour(Toggle):
|
||||
class ShipShapeCustomTaskGoal(Range):
|
||||
"""Change the amount of tasks required to complete Ship Shape. This will not affect Cruisin' for a Bruisin'."""
|
||||
display_name = "Ship Shape Custom Task Goal"
|
||||
range_start = 5
|
||||
range_start = 1
|
||||
range_end = 30
|
||||
default = 18
|
||||
|
||||
@@ -602,7 +599,6 @@ ahit_options: typing.Dict[str, type(Option)] = {
|
||||
"ShuffleAlpineZiplines": ShuffleAlpineZiplines,
|
||||
"FinaleShuffle": FinaleShuffle,
|
||||
"LogicDifficulty": LogicDifficulty,
|
||||
"KnowledgeChecks": KnowledgeChecks,
|
||||
"YarnBalancePercent": YarnBalancePercent,
|
||||
"TimePieceBalancePercent": TimePieceBalancePercent,
|
||||
"RandomizeHatOrder": RandomizeHatOrder,
|
||||
@@ -613,7 +609,7 @@ ahit_options: typing.Dict[str, type(Option)] = {
|
||||
"ShuffleActContracts": ShuffleActContracts,
|
||||
"ShuffleSubconPaintings": ShuffleSubconPaintings,
|
||||
"StartingChapter": StartingChapter,
|
||||
"CTRWithSprint": CTRWithSprint,
|
||||
"CTRLogic": CTRLogic,
|
||||
|
||||
"EnableDLC1": EnableDLC1,
|
||||
"Tasksanity": Tasksanity,
|
||||
@@ -675,7 +671,7 @@ slot_data_options: typing.Dict[str, type(Option)] = {
|
||||
"ActRandomizer": ActRandomizer,
|
||||
"ShuffleAlpineZiplines": ShuffleAlpineZiplines,
|
||||
"LogicDifficulty": LogicDifficulty,
|
||||
"KnowledgeChecks": KnowledgeChecks,
|
||||
"CTRLogic": CTRLogic,
|
||||
"RandomizeHatOrder": RandomizeHatOrder,
|
||||
"UmbrellaLogic": UmbrellaLogic,
|
||||
"CompassBadgeMode": CompassBadgeMode,
|
||||
|
||||
@@ -3,7 +3,7 @@ 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
|
||||
from .Types import ChapterIndex, Difficulty
|
||||
import typing
|
||||
from .Rules import set_rift_rules
|
||||
|
||||
@@ -308,9 +308,13 @@ def create_regions(world: World):
|
||||
create_rift_connections(w, create_region(w, "Time Rift - The Owl Express"))
|
||||
create_rift_connections(w, create_region(w, "Time Rift - The Moon"))
|
||||
|
||||
# Items near the Dead Bird Studio elevator can be reached from the basement act
|
||||
# Items near the Dead Bird Studio elevator can be reached from the basement act, and beyond in Expert
|
||||
ev_area = create_region_and_connect(w, "Dead Bird Studio - Elevator Area", "DBS -> Elevator Area", dbs)
|
||||
connect_regions(mw.get_region("Dead Bird Studio Basement", p), ev_area, "DBS Basement -> Elevator Area", p)
|
||||
post_ev_area = create_region_and_connect(w, "Dead Bird Studio - Post Elevator Area", "DBS -> Post Elevator Area", dbs)
|
||||
basement = mw.get_region("Dead Bird Studio Basement", p)
|
||||
connect_regions(basement, ev_area, "DBS Basement -> Elevator Area", p)
|
||||
if world.multiworld.LogicDifficulty[world.player].value >= int(Difficulty.EXPERT):
|
||||
connect_regions(basement, post_ev_area, "DBS Basement -> Post Elevator Area", p)
|
||||
|
||||
# ------------------------------------------- SUBCON FOREST --------------------------------------- #
|
||||
subcon_forest = create_region_and_connect(w, "Subcon Forest", "Telescope -> Subcon Forest", spaceship)
|
||||
@@ -325,7 +329,9 @@ def create_regions(world: World):
|
||||
alpine_skyline = create_region_and_connect(w, "Alpine Skyline", "Telescope -> Alpine Skyline", spaceship)
|
||||
alpine_freeroam = create_region_and_connect(w, "Alpine Free Roam", "Alpine Skyline - Free Roam", alpine_skyline)
|
||||
alpine_area = create_region_and_connect(w, "Alpine Skyline Area", "AFR -> Alpine Skyline Area", alpine_freeroam)
|
||||
goat_village = create_region_and_connect(w, "Goat Village", "ASA -> Goat Village", alpine_area)
|
||||
|
||||
# Needs to be separate because there are a lot of locations in Alpine that can't be accessed from Illness
|
||||
alpine_area_tihs = create_region_and_connect(w, "Alpine Skyline Area (TIHS)", "-> Alpine Skyline Area (TIHS)", alpine_area)
|
||||
|
||||
create_region_and_connect(w, "The Birdhouse", "-> The Birdhouse", alpine_area)
|
||||
create_region_and_connect(w, "The Lava Cake", "-> The Lava Cake", alpine_area)
|
||||
@@ -333,8 +339,7 @@ def create_regions(world: World):
|
||||
create_region_and_connect(w, "The Twilight Bell", "-> The Twilight Bell", alpine_area)
|
||||
|
||||
illness = create_region_and_connect(w, "The Illness has Spread", "Alpine Skyline - Finale", alpine_skyline)
|
||||
connect_regions(illness, alpine_area, "TIHS -> Alpine Skyline Area", p)
|
||||
connect_regions(illness, goat_village, "TIHS -> Goat Village", p)
|
||||
connect_regions(illness, alpine_area_tihs, "TIHS -> Alpine Skyline Area (TIHS)", p)
|
||||
create_rift_connections(w, create_region(w, "Time Rift - Alpine Skyline"))
|
||||
create_rift_connections(w, create_region(w, "Time Rift - The Twilight Bell"))
|
||||
create_rift_connections(w, create_region(w, "Time Rift - Curly Tail Trail"))
|
||||
@@ -373,7 +378,7 @@ def create_regions(world: World):
|
||||
connect_regions(mw.get_region("Dead Bird Studio", p), badge_seller, "DBS -> Badge Seller", p)
|
||||
connect_regions(mw.get_region("Picture Perfect", p), badge_seller, "PP -> Badge Seller", p)
|
||||
connect_regions(mw.get_region("Train Rush", p), badge_seller, "TR -> Badge Seller", p)
|
||||
connect_regions(mw.get_region("Goat Village", p), badge_seller, "GV -> Badge Seller", p)
|
||||
connect_regions(mw.get_region("Alpine Skyline Area (TIHS)", p), badge_seller, "ASA -> Badge Seller", p)
|
||||
|
||||
times_end = create_region_and_connect(w, "Time's End", "Telescope -> Time's End", spaceship)
|
||||
create_region_and_connect(w, "The Finale", "Time's End - Act 1", times_end)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from worlds.AutoWorld import World, CollectionState
|
||||
from worlds.generic.Rules import add_rule, set_rule
|
||||
from .Locations import location_table, tihs_locations, zipline_unlocks, is_location_valid, contract_locations, \
|
||||
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
|
||||
from .Types import HatType, ChapterIndex, hat_type_to_item, Difficulty
|
||||
from BaseClasses import Location, Entrance, Region
|
||||
import typing
|
||||
|
||||
@@ -57,13 +57,9 @@ def painting_logic(world: World) -> bool:
|
||||
return world.multiworld.ShuffleSubconPaintings[world.player].value > 0
|
||||
|
||||
|
||||
def is_player_knowledgeable(world: World) -> bool:
|
||||
return world.multiworld.KnowledgeChecks[world.player].value > 0
|
||||
|
||||
|
||||
# 0 = Normal, 1 = Hard, 2 = Expert
|
||||
def get_difficulty(world: World) -> int:
|
||||
return world.multiworld.LogicDifficulty[world.player].value
|
||||
# -1 = Normal, 0 = Moderate, 1 = Hard, 2 = Expert
|
||||
def get_difficulty(world: World) -> Difficulty:
|
||||
return Difficulty(world.multiworld.LogicDifficulty[world.player].value)
|
||||
|
||||
|
||||
def has_paintings(state: CollectionState, world: World, count: int) -> bool:
|
||||
@@ -71,18 +67,17 @@ def has_paintings(state: CollectionState, world: World, count: int) -> bool:
|
||||
return True
|
||||
|
||||
# Cherry Hover
|
||||
if get_difficulty(world) == 2:
|
||||
if get_difficulty(world) >= Difficulty.EXPERT:
|
||||
return True
|
||||
|
||||
# All paintings can be skipped with No Bonk, very easily, if the player knows
|
||||
if is_player_knowledgeable(world) and can_surf(state, world):
|
||||
if get_difficulty(world) >= Difficulty.MODERATE and can_surf(state, world):
|
||||
return True
|
||||
|
||||
paintings: int = state.count("Progressive Painting Unlock", world.player)
|
||||
|
||||
if is_player_knowledgeable(world):
|
||||
# Green paintings can also be skipped very easily without No Bonk
|
||||
if paintings >= 1 and count == 3:
|
||||
if get_difficulty(world) >= Difficulty.MODERATE:
|
||||
# Green+Yellow paintings can also be skipped easily
|
||||
if count == 1 or paintings >= 1 and count == 3:
|
||||
return True
|
||||
|
||||
return paintings >= count
|
||||
@@ -96,11 +91,11 @@ def can_use_hookshot(state: CollectionState, world: World):
|
||||
return state.has("Hookshot Badge", world.player)
|
||||
|
||||
|
||||
def can_hit(state: CollectionState, world: World):
|
||||
def can_hit(state: CollectionState, world: World, umbrella_only: bool = False):
|
||||
if world.multiworld.UmbrellaLogic[world.player].value == 0:
|
||||
return True
|
||||
|
||||
return state.has("Umbrella", world.player) or can_use_hat(state, world, HatType.BREWING)
|
||||
return state.has("Umbrella", world.player) or not umbrella_only and can_use_hat(state, world, HatType.BREWING)
|
||||
|
||||
|
||||
def can_surf(state: CollectionState, world: World):
|
||||
@@ -271,18 +266,6 @@ def set_rules(world: World):
|
||||
|
||||
location = world.multiworld.get_location(key, world.player)
|
||||
|
||||
# Not all locations in Alpine can be reached from The Illness has Spread
|
||||
# as many of the ziplines are blocked off
|
||||
if data.region == "Alpine Skyline Area":
|
||||
if key not in tihs_locations:
|
||||
add_rule(location, lambda state: state.can_reach("Alpine Free Roam", "Region", world.player), "and")
|
||||
else:
|
||||
add_rule(location, lambda state: can_use_hookshot(state, world))
|
||||
|
||||
if data.region == "The Birdhouse" or data.region == "The Lava Cake" \
|
||||
or data.region == "The Windmill" or data.region == "The Twilight Bell":
|
||||
add_rule(location, lambda state: state.can_reach("Alpine Free Roam", "Region", world.player), "and")
|
||||
|
||||
for hat in data.required_hats:
|
||||
if hat is not HatType.NONE:
|
||||
add_rule(location, lambda state, h=hat: can_use_hat(state, world, h))
|
||||
@@ -305,11 +288,44 @@ def set_rules(world: World):
|
||||
for misc in data.misc_required:
|
||||
add_rule(location, lambda state, item=misc: state.has(item, world.player))
|
||||
|
||||
if get_difficulty(world) >= 1:
|
||||
world.multiworld.KnowledgeChecks[world.player].value = 1
|
||||
|
||||
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.multiworld.UmbrellaLogic[world.player].value > 0:
|
||||
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))
|
||||
|
||||
for loc in world.multiworld.get_region("Alpine Skyline Area (TIHS)", world.player).locations:
|
||||
if "Goat Village" in loc.name:
|
||||
continue
|
||||
|
||||
add_rule(loc, lambda state: can_use_hookshot(state, world))
|
||||
|
||||
for (key, acts) in act_connections.items():
|
||||
if "Arctic Cruise" in key and not world.is_dlc1():
|
||||
continue
|
||||
@@ -346,11 +362,6 @@ def set_rules(world: World):
|
||||
|
||||
set_event_rules(world)
|
||||
|
||||
for entrance in world.multiworld.get_region("Alpine Free Roam", world.player).entrances:
|
||||
add_rule(entrance, lambda state: can_use_hookshot(state, world))
|
||||
if world.multiworld.UmbrellaLogic[world.player].value > 0:
|
||||
add_rule(entrance, lambda state: state.has("Umbrella", world.player))
|
||||
|
||||
if world.multiworld.EndGoal[world.player].value == 1:
|
||||
world.multiworld.completion_condition[world.player] = lambda state: state.has("Time Piece Cluster", world.player)
|
||||
elif world.multiworld.EndGoal[world.player].value == 2:
|
||||
@@ -375,48 +386,85 @@ def set_specific_rules(world: World):
|
||||
if world.is_dlc2():
|
||||
set_dlc2_rules(world)
|
||||
|
||||
difficulty: int = get_difficulty(world)
|
||||
if is_player_knowledgeable(world) or difficulty >= 1:
|
||||
set_knowledge_rules(world)
|
||||
difficulty: Difficulty = get_difficulty(world)
|
||||
|
||||
if difficulty == 0:
|
||||
set_normal_rules(world)
|
||||
if difficulty >= Difficulty.MODERATE:
|
||||
set_moderate_rules(world)
|
||||
|
||||
if difficulty >= 1:
|
||||
if difficulty >= Difficulty.HARD:
|
||||
set_hard_rules(world)
|
||||
|
||||
if difficulty >= 2:
|
||||
set_expert_rules(world)
|
||||
|
||||
|
||||
def set_normal_rules(world: World):
|
||||
# Hard: get to Birdhouse without Brewing Hat
|
||||
add_rule(world.multiworld.get_entrance("-> The Birdhouse", world.player),
|
||||
lambda state: can_use_hat(state, world, HatType.BREWING))
|
||||
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)
|
||||
|
||||
add_rule(world.multiworld.get_location("Alpine Skyline - Yellow Band Hills", world.player),
|
||||
lambda state: can_use_hat(state, world, HatType.BREWING))
|
||||
# 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)
|
||||
|
||||
# Hard: gallery without Brewing Hat
|
||||
add_rule(world.multiworld.get_location("Act Completion (Time Rift - Gallery)", world.player),
|
||||
lambda state: can_use_hat(state, world, HatType.BREWING))
|
||||
# Moderate: hitting the bell is not required to enter Subcon Well, however hookshot is still expected to clear
|
||||
set_rule(world.multiworld.get_location("Subcon Well - Hookshot Badge Chest", world.player),
|
||||
lambda state: has_paintings(state, world, 1))
|
||||
set_rule(world.multiworld.get_location("Subcon Well - Above Chest", world.player),
|
||||
lambda state: has_paintings(state, world, 1))
|
||||
set_rule(world.multiworld.get_location("Subcon Well - Mushroom", world.player),
|
||||
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: True)
|
||||
|
||||
set_rule(world.multiworld.get_location("Subcon Forest - Manor Rooftop", 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)
|
||||
|
||||
if world.is_dlc1():
|
||||
# Hard: clear Deep Sea without Dweller Mask
|
||||
add_rule(world.multiworld.get_location("Act Completion (Time Rift - Deep Sea)", world.player),
|
||||
lambda state: can_use_hat(state, world, HatType.DWELLER))
|
||||
# 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)
|
||||
add_rule(world.multiworld.get_location("Act Completion (Rock the Boat)", world.player), lambda state: True)
|
||||
|
||||
# Hard: clear Rock the Boat without Ice Hat
|
||||
add_rule(world.multiworld.get_location("Rock the Boat - Post Captain Rescue", world.player),
|
||||
lambda state: can_use_hat(state, world, HatType.ICE))
|
||||
|
||||
add_rule(world.multiworld.get_location("Act Completion (Rock the Boat)", world.player),
|
||||
lambda state: can_use_hat(state, world, HatType.ICE))
|
||||
# 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():
|
||||
# Hard: clear Green Clean Manhole without Dweller Mask
|
||||
add_rule(world.multiworld.get_location("Act Completion (Green Clean Manhole)", world.player),
|
||||
lambda state: can_use_hat(state, world, HatType.DWELLER))
|
||||
set_rule(world.multiworld.get_entrance("-> Pink Paw Station", world.player), lambda state: True)
|
||||
set_rule(world.multiworld.get_location("Act Completion (Yellow Overpass Station)", world.player),
|
||||
lambda state: True)
|
||||
|
||||
set_rule(world.multiworld.get_location("Pink Paw Station - Cat Vacuum", world.player), lambda state: True)
|
||||
|
||||
# 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)
|
||||
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))
|
||||
|
||||
|
||||
def set_hard_rules(world: World):
|
||||
@@ -425,95 +473,105 @@ 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, and Toilet of Doom without Hookshot
|
||||
# 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("Act Completion (Toilet of Doom)", world.player),
|
||||
lambda state: can_surf(state, world) and can_sdj(state, world) and can_hit(state, world), "or")
|
||||
|
||||
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))
|
||||
|
||||
# SDJ
|
||||
add_rule(world.multiworld.get_location("Subcon Forest - Long Tree Climb Chest", world.player),
|
||||
lambda state: can_sdj(state, world)
|
||||
and has_paintings(state, world, 2), "or")
|
||||
|
||||
add_rule(world.multiworld.get_location("Alpine Skyline - The Birdhouse: Dweller Platforms Relic", world.player),
|
||||
lambda state: can_sdj(state, world), "or")
|
||||
add_rule(world.multiworld.get_location("Subcon Forest - Dweller Platforming Tree B", world.player),
|
||||
lambda state: has_paintings(state, world, 3) and can_sdj(state, world), "or")
|
||||
|
||||
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))
|
||||
|
||||
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
|
||||
set_rule(world.multiworld.get_location("Act Completion (Rush Hour)", world.player),
|
||||
lambda state: can_use_hat(state, world, HatType.BREWING))
|
||||
|
||||
|
||||
def set_expert_rules(world: World):
|
||||
# Expert: get to and clear Twilight Bell without Dweller Mask using SDJ. Brewing Hat required to complete act.
|
||||
add_rule(world.multiworld.get_location("Alpine Skyline - The Twilight Path", world.player),
|
||||
lambda state: can_sdj(state, world)
|
||||
and (not zipline_logic(world) or state.has("Zipline Unlock - The Twilight Bell Path", world.player)), "or")
|
||||
# Expert: Mafia Town - Above Boats with nothing
|
||||
set_rule(world.multiworld.get_location("Mafia Town - Above Boats", world.player), lambda state: True)
|
||||
|
||||
# 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: 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_sdj(state, world) and can_use_hookshot(state, world)
|
||||
and (not zipline_logic(world) or state.has("Zipline Unlock - The Twilight Bell Path", world.player)), "or")
|
||||
lambda state: can_use_hookshot(state, world), "or")
|
||||
|
||||
add_rule(world.multiworld.get_location("Act Completion (The Twilight Bell)", world.player),
|
||||
lambda state: can_sdj(state, world) and can_use_hat(state, world, HatType.BREWING), "or")
|
||||
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: enter and clear The Subcon Well with No Bonk Badge only
|
||||
# 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: enter and clear The Subcon Well with nothing
|
||||
for loc in world.multiworld.get_region("The Subcon Well", world.player).locations:
|
||||
add_rule(loc, lambda state: can_surf(state, world), "or")
|
||||
set_rule(loc, lambda state: True)
|
||||
|
||||
# Expert: Cherry Hovering
|
||||
connect_regions(world.multiworld.get_region("Your Contract has Expired", world.player),
|
||||
world.multiworld.get_region("Subcon Forest Area", world.player),
|
||||
"Subcon Forest Entrance YCHE", world.player)
|
||||
|
||||
set_rule(world.multiworld.get_location("Act Completion (Toilet of Doom)", world.player),
|
||||
lambda state: can_hit(state, world))
|
||||
|
||||
set_rule(world.multiworld.get_location("Subcon Forest - Boss Arena Chest", world.player), lambda state: True)
|
||||
set_rule(world.multiworld.get_location("Subcon Forest - Noose Treehouse", world.player), lambda state: True)
|
||||
set_rule(world.multiworld.get_location("Subcon Forest - Long Tree Climb Chest", world.player), lambda state: True)
|
||||
set_rule(world.multiworld.get_location("Subcon Forest - Noose Treehouse", world.player), lambda state: True)
|
||||
set_rule(world.multiworld.get_location("Subcon Forest - Dweller Platforming Tree B", world.player), lambda state: True)
|
||||
set_rule(world.multiworld.get_location("Subcon Forest - Tall Tree Hookshot Swing", world.player), lambda state: True)
|
||||
|
||||
# Manor hover with 1 painting unlock
|
||||
for loc in world.multiworld.get_region("Queen Vanessa's Manor", world.player).locations:
|
||||
set_rule(loc, lambda state: not painting_logic(world)
|
||||
or state.count("Progressive Painting Unlock", world.player) >= 1)
|
||||
# You can cherry hover to Snatcher's post-fight cutscene, which completes the level without having to fight him
|
||||
connect_regions(world.multiworld.get_region("Subcon Forest Area", world.player),
|
||||
world.multiworld.get_region("Your Contract has Expired", world.player),
|
||||
"Snatcher Hover", world.player)
|
||||
set_rule(world.multiworld.get_location("Act Completion (Your Contract has Expired)", world.player),
|
||||
lambda state: True)
|
||||
|
||||
set_rule(world.multiworld.get_location("Subcon Forest - Manor Rooftop", world.player),
|
||||
lambda state: not painting_logic(world)
|
||||
or state.count("Progressive Painting Unlock", world.player) >= 1)
|
||||
|
||||
|
||||
def set_knowledge_rules(world: World):
|
||||
# Can jump down from HQ to get these
|
||||
add_rule(world.multiworld.get_location("Mafia Town - Clock Tower Chest", world.player),
|
||||
lambda state: state.can_reach("Act Completion (Heating Up Mafia Town)", "Location", world.player)
|
||||
or state.can_reach("Cheating the Race", "Region", world.player)
|
||||
or state.can_reach("The Golden Vault", "Region", world.player), "or")
|
||||
|
||||
add_rule(world.multiworld.get_location("Mafia Town - Top of Ruined Tower", world.player),
|
||||
lambda state: state.can_reach("Act Completion (Heating Up Mafia Town)", "Location", world.player)
|
||||
or state.can_reach("Cheating the Race", "Region", world.player)
|
||||
or state.can_reach("The Golden Vault", "Region", world.player), "or")
|
||||
|
||||
# Dweller Mask requirement in Pink Paw can also be skipped by jumping on lamp post.
|
||||
# The item behind the Time Stop fan can be walked past without Time Stop hat as well.
|
||||
# (just set hookshot rule, because dweller requirement is skipped, but hookshot is still necessary)
|
||||
if world.is_dlc2():
|
||||
# There is a glitched fall damage volume near the Yellow Overpass time piece that warps the player to Pink Paw
|
||||
add_rule(world.multiworld.get_entrance("-> Pink Paw Station", world.player),
|
||||
lambda state: can_use_hookshot(state, world), "or")
|
||||
|
||||
for loc in world.multiworld.get_region("Pink Paw Station", world.player).locations:
|
||||
|
||||
# Can't jump back down to the manhole due to a fall damage trigger.
|
||||
if loc.name == "Act Completion (Pink Paw Manhole)":
|
||||
set_rule(loc, 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))
|
||||
|
||||
continue
|
||||
|
||||
set_rule(loc, lambda state: can_use_hookshot(state, world))
|
||||
# Expert: clear Rush Hour with nothing
|
||||
set_rule(world.multiworld.get_location("Act Completion (Rush Hour)", world.player), lambda state: True)
|
||||
|
||||
|
||||
def set_mafia_town_rules(world: World):
|
||||
@@ -564,9 +622,16 @@ def set_mafia_town_rules(world: World):
|
||||
add_rule(world.multiworld.get_location("Mafia Town - Above Boats", world.player),
|
||||
lambda state: state.has("HUMT Access", world.player), "or")
|
||||
|
||||
set_rule(world.multiworld.get_location("Act Completion (Cheating the Race)", world.player),
|
||||
lambda state: can_use_hat(state, world, HatType.TIME_STOP)
|
||||
or world.multiworld.CTRWithSprint[world.player].value > 0 and can_use_hat(state, world, HatType.SPRINT))
|
||||
ctr_logic: int = world.multiworld.CTRLogic[world.player].value
|
||||
if ctr_logic == 3:
|
||||
set_rule(world.multiworld.get_location("Act Completion (Cheating the Race)", world.player), lambda state: True)
|
||||
elif ctr_logic == 2:
|
||||
add_rule(world.multiworld.get_location("Act Completion (Cheating the Race)", world.player),
|
||||
lambda state: can_use_hat(state, world, HatType.SPRINT), "or")
|
||||
elif ctr_logic == 1:
|
||||
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_subcon_rules(world: World):
|
||||
@@ -608,14 +673,16 @@ def set_subcon_rules(world: World):
|
||||
|
||||
def set_alps_rules(world: World):
|
||||
add_rule(world.multiworld.get_entrance("-> The Birdhouse", world.player),
|
||||
lambda state: can_use_hookshot(state, world))
|
||||
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_hat(state, world, HatType.DWELLER) and can_use_hookshot(state, world))
|
||||
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))
|
||||
@@ -623,28 +690,6 @@ def set_alps_rules(world: World):
|
||||
add_rule(world.multiworld.get_entrance("Alpine Skyline - Finale", world.player),
|
||||
lambda state: can_clear_alpine(state, world))
|
||||
|
||||
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))
|
||||
|
||||
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))
|
||||
|
||||
|
||||
def set_dlc1_rules(world: World):
|
||||
add_rule(world.multiworld.get_entrance("Cruise Ship Entrance BV", world.player),
|
||||
|
||||
@@ -28,6 +28,13 @@ class ChapterIndex(IntEnum):
|
||||
METRO = 7
|
||||
|
||||
|
||||
class Difficulty(IntEnum):
|
||||
NORMAL = -1
|
||||
MODERATE = 0
|
||||
HARD = 1
|
||||
EXPERT = 2
|
||||
|
||||
|
||||
hat_type_to_item = {
|
||||
HatType.SPRINT: "Sprint Hat",
|
||||
HatType.BREWING: "Brewing Hat",
|
||||
|
||||
@@ -98,11 +98,6 @@ class HatInTimeWorld(World):
|
||||
if self.multiworld.ShuffleActContracts[self.player].value == 0:
|
||||
for name in contract_locations.keys():
|
||||
self.multiworld.get_location(name, self.player).place_locked_item(create_item(self, name))
|
||||
else:
|
||||
# The bag trap contract check needs to be excluded, because if the player has the Subcon Well contract,
|
||||
# the trap will not activate, locking the player out of the check permanently
|
||||
self.multiworld.get_location("Snatcher's Contract - The Subcon Well",
|
||||
self.player).progress_type = LocationProgressType.EXCLUDED
|
||||
|
||||
def create_items(self):
|
||||
hat_yarn_costs[self.player] = {HatType.SPRINT: -1, HatType.BREWING: -1, HatType.ICE: -1,
|
||||
|
||||
@@ -7,6 +7,7 @@ class TestActs(HatInTimeTestBase):
|
||||
"ActRandomizer": 2,
|
||||
"EnableDLC1": 1,
|
||||
"EnableDLC2": 1,
|
||||
"ShuffleActContracts": 0,
|
||||
}
|
||||
|
||||
def test_act_shuffle(self):
|
||||
|
||||
Reference in New Issue
Block a user