This commit is contained in:
CookieCat
2023-11-05 14:09:29 -05:00
parent f4f405fc76
commit 74715c397c
7 changed files with 80 additions and 26 deletions

View File

@@ -197,6 +197,7 @@ def create_dw_regions(world: World):
if i == 0:
connect_regions(dw_map, dw, f"-> {name}", world.player)
else:
# noinspection PyUnboundLocalVariable
connect_regions(prev_dw, dw, f"{prev_dw.name} -> {name}", world.player)
loc_id = death_wishes[name]

View File

@@ -283,7 +283,7 @@ ahit_locations = {
# Alpine Skyline
"Alpine Skyline - Goat Village: Below Hookpoint": LocData(2000334856, "Alpine Skyline Area (TIHS)"),
"Alpine Skyline - Goat Village: Hidden Branch": LocData(2000334855, "Alpine Skyline Area (TIHS)"),
"Alpine Skyline - Goat Refinery": LocData(2000333635, "Alpine Skyline Area"),
"Alpine Skyline - Goat Refinery": LocData(2000333635, "Alpine Skyline Area (TIHS)"),
"Alpine Skyline - Bird Pass Fork": LocData(2000335911, "Alpine Skyline Area (TIHS)"),
"Alpine Skyline - Yellow Band Hills": LocData(2000335756, "Alpine Skyline Area (TIHS)",
@@ -900,6 +900,8 @@ event_locs = {
"HUMT Access": LocData(0, "Heating Up Mafia Town"),
"TOD Access": LocData(0, "Toilet of Doom"),
"YCHE Access": LocData(0, "Your Contract has Expired"),
"AFR Access": LocData(0, "Alpine Free Roam"),
"TIHS Access": LocData(0, "The Illness has Spread"),
"Birdhouse Cleared": LocData(0, "The Birdhouse", act_event=True),
"Lava Cake Cleared": LocData(0, "The Lava Cake", act_event=True),

View File

@@ -458,6 +458,15 @@ class NyakuzaThugMaxShopItems(Range):
default = 4
class NoTicketSkips(Choice):
"""Prevent metro gate skips from being in logic on higher difficulties.
Rush Hour option will only consider the ticket skips for Rush Hour in logic."""
display_name = "No Ticket Skips"
option_false = 0
option_true = 1
option_rush_hour = 2
class BaseballBat(Toggle):
"""Replace the Umbrella with the baseball bat from Nyakuza Metro.
DLC2 content does not have to be shuffled for this option but Nyakuza Metro still needs to be installed."""
@@ -656,6 +665,7 @@ ahit_options: typing.Dict[str, type(Option)] = {
"MetroMaxPonCost": MetroMaxPonCost,
"NyakuzaThugMinShopItems": NyakuzaThugMinShopItems,
"NyakuzaThugMaxShopItems": NyakuzaThugMaxShopItems,
"NoTicketSkips": NoTicketSkips,
"LowestChapterCost": LowestChapterCost,
"HighestChapterCost": HighestChapterCost,
@@ -720,6 +730,7 @@ slot_data_options: typing.Dict[str, type(Option)] = {
"MetroMinPonCost": MetroMinPonCost,
"MetroMaxPonCost": MetroMaxPonCost,
"BaseballBat": BaseballBat,
"NoTicketSkips": NoTicketSkips,
"MinPonCost": MinPonCost,
"MaxPonCost": MaxPonCost,

View File

@@ -309,10 +309,10 @@ def create_regions(world: World):
# 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)
post_ev_area = create_region_and_connect(w, "Dead Bird Studio - Post Elevator Area", "DBS -> Post Elevator Area", dbs)
post_ev = create_region_and_connect(w, "Dead Bird Studio - Post Elevator Area", "DBS -> Post Elevator Area", dbs)
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)
connect_regions(basement, post_ev, "DBS Basement -> Post Elevator Area", p)
# ------------------------------------------- SUBCON FOREST --------------------------------------- #
subcon_forest = create_region_and_connect(w, "Subcon Forest", "Telescope -> Subcon Forest", spaceship)
@@ -501,12 +501,12 @@ def randomize_act_entrances(world: World):
region_list.append(region)
for region in region_list.copy():
if "Time Rift" in region.name:
if region.name in chapter_finales:
region_list.remove(region)
region_list.append(region)
for region in region_list.copy():
if region.name in chapter_finales:
if "Time Rift" in region.name:
region_list.remove(region)
region_list.append(region)
@@ -631,8 +631,8 @@ def randomize_act_entrances(world: World):
candidate = c
break
# noinspection PyUnboundLocalVariable
shuffled_list.append(candidate)
# print(region, candidate)
# Vanilla
if candidate.name == region.name:

View File

@@ -317,9 +317,24 @@ def set_rules(world: World):
for loc in world.multiworld.get_region("Alpine Skyline Area (TIHS)", world.player).locations:
if "Goat Village" in loc.name:
continue
# This needs some special handling
if loc.name == "Alpine Skyline - Goat Refinery":
add_rule(loc, lambda state: state.has("AFR Access", world.player)
and can_use_hookshot(state, world)
and can_hit(state, world, True))
difficulty: Difficulty = Difficulty(world.multiworld.LogicDifficulty[world.player].value)
if difficulty >= Difficulty.MODERATE:
add_rule(loc, lambda state: state.has("TIHS Access", world.player)
and can_use_hat(state, world, HatType.SPRINT), "or")
elif difficulty >= Difficulty.HARD:
add_rule(loc, lambda state: state.has("TIHS Access", world.player, "or"))
continue
add_rule(loc, lambda state: can_use_hookshot(state, world))
dummy_entrances: typing.List[Entrance] = []
for (key, acts) in act_connections.items():
if "Arctic Cruise" in key and not world.is_dlc1():
continue
@@ -328,7 +343,7 @@ def set_rules(world: World):
entrance: Entrance = world.multiworld.get_entrance(key, world.player)
region: Region = entrance.connected_region
access_rules: typing.List[typing.Callable[[CollectionState], bool]] = []
entrance.parent_region.exits.remove(entrance)
dummy_entrances.append(entrance)
# Entrances to this act that we have to set access_rules on
entrances: typing.List[Entrance] = []
@@ -354,6 +369,9 @@ def set_rules(world: World):
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.multiworld.EndGoal[world.player].value == 1:
@@ -448,13 +466,12 @@ def set_moderate_rules(world: World):
# 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():
set_rule(world.multiworld.get_entrance("-> Pink Paw Station", world.player), lambda state: True)
# 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)
# 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)
# Moderate: clear Rush Hour without Hookshot
@@ -465,8 +482,10 @@ def set_moderate_rules(world: World):
and can_use_hat(state, world, HatType.ICE)
and can_use_hat(state, world, HatType.BREWING))
# Moderate: Bluefin Tunnel without tickets
set_rule(world.multiworld.get_entrance("-> Bluefin Tunnel", world.player), lambda state: True)
# Moderate: Bluefin Tunnel + Pink Paw Station without tickets
if world.multiworld.NoTicketSkips[world.player].value == 0:
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: World):
@@ -483,6 +502,13 @@ def set_hard_rules(world: World):
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("Subcon Forest - Long Tree Climb Chest", world.player),
lambda state: can_sdj(state, world) and has_paintings(state, world, 2), "or")
@@ -508,8 +534,15 @@ def set_hard_rules(world: World):
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))
if world.multiworld.NoTicketSkips[world.player].value != 1:
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: World):
@@ -517,8 +550,10 @@ def set_expert_rules(world: World):
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
# 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)
set_rule(world.multiworld.get_location("Mafia Town - Hot Air Balloon", 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:
@@ -561,13 +596,9 @@ def set_expert_rules(world: World):
# 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 - 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 - Dweller Platforming Tree B", world.player),
lambda state: has_paintings(state, world, 3, True))
set_rule(world.multiworld.get_location("Subcon Forest - Tall Tree Hookshot Swing", world.player),
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
@@ -579,7 +610,13 @@ def set_expert_rules(world: World):
if world.is_dlc2():
# Expert: clear Rush Hour with nothing
set_rule(world.multiworld.get_location("Act Completion (Rush Hour)", world.player), lambda state: True)
if world.multiworld.NoTicketSkips[world.player].value == 0:
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))
def set_mafia_town_rules(world: World):

View File

@@ -1,7 +1,8 @@
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, TASKSANITY_START_ID
from .Locations import location_table, contract_locations, is_location_valid, get_location_names, TASKSANITY_START_ID, \
get_total_locations
from .Rules import set_rules
from .Options import ahit_options, slot_data_options, adjust_options
from .Types import HatType, ChapterIndex, HatInTimeItem
@@ -173,7 +174,8 @@ class HatInTimeWorld(World):
"Chapter7Cost": chapter_timepiece_costs[self.player][ChapterIndex.METRO],
"BadgeSellerItemCount": badge_seller_count[self.player],
"SeedNumber": str(self.multiworld.seed), # For shop prices
"SeedName": self.multiworld.seed_name}
"SeedName": self.multiworld.seed_name,
"TotalLocations": get_total_locations(self)}
if self.multiworld.HatItems[self.player].value == 0:
slot_data.setdefault("SprintYarnCost", hat_yarn_costs[self.player][HatType.SPRINT])

View File

@@ -1,4 +1,5 @@
from worlds.ahit.Regions import act_chapters
from worlds.ahit.Rules import act_connections
from worlds.ahit.test.TestBase import HatInTimeTestBase
@@ -6,9 +7,6 @@ class TestActs(HatInTimeTestBase):
def run_default_tests(self) -> bool:
return False
def testAllStateCanReachEverything(self):
pass
options = {
"ActRandomizer": 2,
"EnableDLC1": 1,
@@ -24,6 +22,9 @@ class TestActs(HatInTimeTestBase):
for name in act_chapters.keys():
region = self.multiworld.get_region(name, 1)
for entrance in region.entrances:
if entrance.name in act_connections.keys():
continue
self.assertTrue(self.can_reach_entrance(entrance.name),
f"Can't reach {name} from {entrance}\n"
f"{entrance.parent_region.entrances[0]} -> {entrance.parent_region} "