0.9b - cleanup + expanded logic difficulty

This commit is contained in:
CookieCat
2023-08-31 21:43:40 -04:00
parent 949f152ec2
commit d673b87b7e
6 changed files with 398 additions and 234 deletions

View File

@@ -19,11 +19,11 @@ def item_dlc_enabled(world: World, name: str) -> bool:
if data.dlc_flags == HatDLC.none:
return True
elif data.dlc_flags == HatDLC.dlc1 and world.multiworld.EnableDLC1[world.player].value > 0:
elif data.dlc_flags == HatDLC.dlc1 and world.is_dlc1():
return True
elif data.dlc_flags == HatDLC.dlc2 and world.multiworld.EnableDLC2[world.player].value > 0:
elif data.dlc_flags == HatDLC.dlc2 and world.is_dlc2():
return True
elif data.dlc_flags == HatDLC.death_wish and world.multiworld.EnableDeathWish[world.player].value > 0:
elif data.dlc_flags == HatDLC.death_wish and world.is_dw():
return True
return False
@@ -31,10 +31,10 @@ def item_dlc_enabled(world: World, name: str) -> bool:
def get_total_time_pieces(world: World) -> int:
count: int = 40
if world.multiworld.EnableDLC1[world.player].value > 0:
if world.is_dlc1():
count += 6
if world.multiworld.EnableDLC2[world.player].value > 0:
if world.is_dlc2():
count += 10
return min(40+world.multiworld.MaxExtraTimePieces[world.player].value, count)
@@ -110,7 +110,7 @@ ahit_items = {
"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),
"No Bonk Badge": ItemData(300029, ItemClassification.progression),
"Compass Badge": ItemData(300030, ItemClassification.useful),
"Scooter Badge": ItemData(300031, ItemClassification.progression),
"Badge Pin": ItemData(300043, ItemClassification.useful),

View File

@@ -15,7 +15,7 @@ class LocData(NamedTuple):
# For UmbrellaLogic setting
umbrella: Optional[bool] = False # Umbrella required for this check
dweller_bell: Optional[int] = 0 # Dweller bell hit required, 1 means must hit bell, 2 means can bypass w/mask
hit_requirement: Optional[int] = 0 # Hit required. 1 = Umbrella/Brewing only, 2 = bypass w/Dweller Mask (bells)
# Other
act_complete_event: Optional[bool] = True # Only used for event locations. Copy access rule from act completion
@@ -33,7 +33,7 @@ def get_total_locations(world: World) -> int:
if is_location_valid(world, name):
total += 1
if world.multiworld.EnableDLC1[world.player].value > 0 and world.multiworld.Tasksanity[world.player].value > 0:
if world.is_dlc1() and world.multiworld.Tasksanity[world.player].value > 0:
total += world.multiworld.TasksanityCheckCount[world.player].value
return total
@@ -44,11 +44,11 @@ def location_dlc_enabled(world: World, location: str) -> bool:
if data.dlc_flags == HatDLC.none:
return True
elif data.dlc_flags == HatDLC.dlc1 and world.multiworld.EnableDLC1[world.player].value > 0:
elif data.dlc_flags == HatDLC.dlc1 and world.is_dlc1():
return True
elif data.dlc_flags == HatDLC.dlc2 and world.multiworld.EnableDLC2[world.player].value > 0:
elif data.dlc_flags == HatDLC.dlc2 and world.is_dlc2():
return True
elif data.dlc_flags == HatDLC.death_wish and world.multiworld.EnableDeathWish[world.player].value > 0:
elif data.dlc_flags == HatDLC.death_wish and world.is_dw():
return True
return False
@@ -82,7 +82,7 @@ def get_tasksanity_start_id() -> int:
ahit_locations = {
"Spaceship - Rumbi Abuse": LocData(301000, "Spaceship", dweller_bell=1),
"Spaceship - Rumbi Abuse": LocData(301000, "Spaceship", hit_requirement=1),
# 300000 range - Mafia Town/Batle of the Birds
"Welcome to Mafia Town - Umbrella": LocData(301002, "Welcome to Mafia Town"),
@@ -136,10 +136,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", umbrella=True),
"Dead Bird Studio - Tightrope Chest": LocData(303898, "Dead Bird Studio", umbrella=True),
"Dead Bird Studio - Tepee Chest": LocData(303899, "Dead Bird Studio", umbrella=True),
"Dead Bird Studio - Conductor Chest": LocData(303900, "Dead Bird Studio", umbrella=True),
"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),
"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"),
@@ -223,7 +223,7 @@ ahit_locations = {
required_hats=[HatType.DWELLER], paintings=2),
"Subcon Forest - Boss Arena Chest": LocData(323735, "Subcon Forest Area"),
"Subcon Forest - Manor Rooftop": LocData(325466, "Subcon Forest Area", dweller_bell=2, paintings=1),
"Subcon Forest - Manor Rooftop": LocData(325466, "Subcon Forest Area", hit_requirement=2, paintings=1),
"Subcon Forest - Infinite Yarn Bush": LocData(325478, "Subcon Forest Area",
required_hats=[HatType.BREWING], paintings=2),
@@ -231,15 +231,15 @@ ahit_locations = {
"Subcon Forest - Magnet Badge Bush": LocData(325479, "Subcon Forest Area",
required_hats=[HatType.BREWING], paintings=3),
"Subcon Well - Hookshot Badge Chest": LocData(324114, "The Subcon Well", dweller_bell=1, paintings=1),
"Subcon Well - Above Chest": LocData(324612, "The Subcon Well", dweller_bell=1, paintings=1),
"Subcon Well - On Pipe": LocData(324311, "The Subcon Well", hookshot=True, dweller_bell=1, paintings=1),
"Subcon Well - Mushroom": LocData(325318, "The Subcon Well", dweller_bell=1, paintings=1),
"Subcon Well - Hookshot Badge Chest": LocData(324114, "The Subcon Well", hit_requirement=1, paintings=1),
"Subcon Well - Above Chest": LocData(324612, "The Subcon Well", hit_requirement=1, paintings=1),
"Subcon Well - On Pipe": LocData(324311, "The Subcon Well", hookshot=True, hit_requirement=1, paintings=1),
"Subcon Well - Mushroom": LocData(325318, "The Subcon Well", hit_requirement=1, paintings=1),
"Queen Vanessa's Manor - Cellar": LocData(324841, "Queen Vanessa's Manor", dweller_bell=2, paintings=1),
"Queen Vanessa's Manor - Bedroom Chest": LocData(323808, "Queen Vanessa's Manor", dweller_bell=2, paintings=1),
"Queen Vanessa's Manor - Hall Chest": LocData(323896, "Queen Vanessa's Manor", dweller_bell=2, paintings=1),
"Queen Vanessa's Manor - Chandelier": LocData(325546, "Queen Vanessa's Manor", dweller_bell=2, paintings=1),
"Queen Vanessa's Manor - Cellar": LocData(324841, "Queen Vanessa's Manor", hit_requirement=2, paintings=1),
"Queen Vanessa's Manor - Bedroom Chest": LocData(323808, "Queen Vanessa's Manor", hit_requirement=2, paintings=1),
"Queen Vanessa's Manor - Hall Chest": LocData(323896, "Queen Vanessa's Manor", hit_requirement=2, paintings=1),
"Queen Vanessa's Manor - Chandelier": LocData(325546, "Queen Vanessa's Manor", hit_requirement=2, paintings=1),
# 330000 range - Alpine Skyline
"Alpine Skyline - Goat Village: Below Hookpoint": LocData(334856, "Goat Village"),
@@ -298,26 +298,26 @@ ahit_locations = {
"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),
"Nyakuza Metro - Yellow Overpass Station Crate": LocData(305413, "Yellow Overpass Station",
dlc_flags=HatDLC.dlc2,
required_hats=[HatType.BREWING]),
"Yellow Overpass Station - Brewing Crate": LocData(305413, "Yellow Overpass Station",
dlc_flags=HatDLC.dlc2,
required_hats=[HatType.BREWING]),
"Nyakuza Metro - Bluefin Tunnel Cat Vacuum": LocData(305111, "Bluefin Tunnel", dlc_flags=HatDLC.dlc2),
"Bluefin Tunnel - Cat Vacuum": LocData(305111, "Bluefin Tunnel", dlc_flags=HatDLC.dlc2),
"Nyakuza Metro - Pink Paw Station Cat Vacuum": LocData(305110, "Pink Paw Station",
dlc_flags=HatDLC.dlc2,
hookshot=True,
required_hats=[HatType.DWELLER]),
"Pink Paw Station - Cat Vacuum": LocData(305110, "Pink Paw Station",
dlc_flags=HatDLC.dlc2,
hookshot=True,
required_hats=[HatType.DWELLER]),
"Nyakuza Metro - Pink Paw Station Behind Fan": LocData(304106, "Pink Paw Station",
dlc_flags=HatDLC.dlc2,
hookshot=True,
required_hats=[HatType.TIME_STOP, HatType.DWELLER]),
"Pink Paw Station - Behind Fan": LocData(304106, "Pink Paw Station",
dlc_flags=HatDLC.dlc2,
hookshot=True,
required_hats=[HatType.TIME_STOP, HatType.DWELLER]),
}
act_completions = {
# 310000 range - Act Completions
"Act Completion (Time Rift - Gallery)": LocData(312758, "Time Rift - Gallery", required_hats=[HatType.BREWING]),
"Act Completion (Time Rift - Gallery)": LocData(312758, "Time Rift - Gallery"),
"Act Completion (Time Rift - The Lab)": LocData(312838, "Time Rift - The Lab"),
"Act Completion (Welcome to Mafia Town)": LocData(311771, "Welcome to Mafia Town"),
@@ -331,7 +331,7 @@ act_completions = {
"Act Completion (Time Rift - Sewers)": LocData(312484, "Time Rift - Sewers"),
"Act Completion (Time Rift - Mafia of Cooks)": LocData(311855, "Time Rift - Mafia of Cooks"),
"Act Completion (Dead Bird Studio)": LocData(311383, "Dead Bird Studio", umbrella=True),
"Act Completion (Dead Bird Studio)": LocData(311383, "Dead Bird Studio", hit_requirement=1),
"Act Completion (Murder on the Owl Express)": LocData(311544, "Murder on the Owl Express"),
"Act Completion (Picture Perfect)": LocData(311587, "Picture Perfect"),
"Act Completion (Train Rush)": LocData(312481, "Train Rush", hookshot=True),
@@ -389,7 +389,7 @@ act_completions = {
"Act Completion (Green Clean Manhole)": LocData(311388, "Green Clean Manhole",
dlc_flags=HatDLC.dlc2,
required_hats=[HatType.ICE, HatType.DWELLER]),
required_hats=[HatType.ICE]),
"Act Completion (Bluefin Tunnel)": LocData(311208, "Bluefin Tunnel", dlc_flags=HatDLC.dlc2),
@@ -498,7 +498,8 @@ shop_locations = {
"Yellow Overpass Station - Yellow Ticket Booth": LocData(301014, "Yellow Overpass Station", dlc_flags=HatDLC.dlc2),
"Green Clean Station - Green Ticket Booth": LocData(301015, "Green Clean Station", dlc_flags=HatDLC.dlc2),
"Bluefin Tunnel - Blue Ticket Booth": LocData(301016, "Bluefin Tunnel", dlc_flags=HatDLC.dlc2),
"Pink Paw Station - Pink Ticket Booth": LocData(301017, "Pink Paw Station", dlc_flags=HatDLC.dlc2),
"Pink Paw Station - Pink Ticket Booth": LocData(301017, "Pink Paw Station", dlc_flags=HatDLC.dlc2,
hookshot=True, required_hats=[HatType.DWELLER]),
"Main Station Thug A - Item 1": LocData(301048, "Nyakuza Free Roam", dlc_flags=HatDLC.dlc2,
nyakuza_thug="Hat_NPC_NyakuzaShop_0"),
@@ -647,6 +648,7 @@ tihs_locations = [
event_locs = {
"HUMT Access": LocData(0, "Heating Up Mafia Town", act_complete_event=False),
"Subcon Forest Access": LocData(0, "Subcon Forest Area", act_complete_event=False),
"Birdhouse Cleared": LocData(0, "The Birdhouse"),
"Lava Cake Cleared": LocData(0, "The Lava Cake"),
"Windmill Cleared": LocData(0, "The Windmill"),

View File

@@ -93,7 +93,7 @@ class NoFreeRoamFinale(Toggle):
class LogicDifficulty(Choice):
"""Choose the difficulty setting for logic. Note that Hard or above will force SDJ logic on."""
"""Choose the difficulty setting for logic."""
display_name = "Logic Difficulty"
option_normal = 0
option_hard = 1
@@ -101,12 +101,36 @@ class LogicDifficulty(Choice):
default = 0
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"
default = 0
class RandomizeHatOrder(Toggle):
"""Randomize the order that hats are stitched in."""
display_name = "Randomize Hat Order"
default = 1
class YarnBalancePercent(Range):
"""How much (in percentage) of the yarn in the pool that will be progression balanced."""
display_name = "Yarn Balance Percentage"
default = 20
range_start = 0
range_end = 100
class TimePieceBalancePercent(Range):
"""How much (in percentage) of time pieces in the pool that will be progression balanced."""
display_name = "Time Piece Balance Percentage"
default = 35
range_start = 0
range_end = 100
class UmbrellaLogic(Toggle):
"""Makes Hat Kid's default punch attack do absolutely nothing, making the Umbrella much more relevant and useful"""
display_name = "Umbrella Logic"
@@ -161,12 +185,6 @@ class StartingChapter(Choice):
default = 1
class SDJLogic(Toggle):
"""Allow the SDJ (Sprint Double Jump) technique to be considered in logic."""
display_name = "SDJ Logic"
default = 0
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"
@@ -445,6 +463,9 @@ ahit_options: typing.Dict[str, type(Option)] = {
"VanillaAlpine": VanillaAlpine,
"NoFreeRoamFinale": NoFreeRoamFinale,
"LogicDifficulty": LogicDifficulty,
"KnowledgeChecks": KnowledgeChecks,
"YarnBalancePercent": YarnBalancePercent,
"TimePieceBalancePercent": TimePieceBalancePercent,
"RandomizeHatOrder": RandomizeHatOrder,
"UmbrellaLogic": UmbrellaLogic,
"StartWithCompassBadge": StartWithCompassBadge,
@@ -453,7 +474,6 @@ ahit_options: typing.Dict[str, type(Option)] = {
"ShuffleActContracts": ShuffleActContracts,
"ShuffleSubconPaintings": ShuffleSubconPaintings,
"StartingChapter": StartingChapter,
"SDJLogic": SDJLogic,
"CTRWithSprint": CTRWithSprint,
"EnableDLC1": EnableDLC1,
@@ -502,13 +522,13 @@ slot_data_options: typing.Dict[str, type(Option)] = {
"ActRandomizer": ActRandomizer,
"ShuffleAlpineZiplines": ShuffleAlpineZiplines,
"LogicDifficulty": LogicDifficulty,
"KnowledgeChecks": KnowledgeChecks,
"RandomizeHatOrder": RandomizeHatOrder,
"UmbrellaLogic": UmbrellaLogic,
"CompassBadgeMode": CompassBadgeMode,
"ShuffleStorybookPages": ShuffleStorybookPages,
"ShuffleActContracts": ShuffleActContracts,
"ShuffleSubconPaintings": ShuffleSubconPaintings,
"SDJLogic": SDJLogic,
"EnableDLC1": EnableDLC1,
"Tasksanity": Tasksanity,

View File

@@ -250,7 +250,6 @@ chapter_finales = [
# entrance: region
blacklisted_acts = {
"Battle of the Birds - Finale A": "Award Ceremony",
"Time's End - Act 1": "The Finale",
}
@@ -359,7 +358,7 @@ def create_regions(world: World):
create_region_and_connect(w, "The Finale", "Time's End - Act 1", times_end)
# ------------------------------------------- DLC1 ------------------------------------------------- #
if mw.EnableDLC1[p].value > 0:
if w.is_dlc1():
arctic_cruise = create_region_and_connect(w, "The Arctic Cruise", "Telescope -> The Arctic Cruise", spaceship)
cruise_ship = create_region(w, "Cruise Ship")
@@ -382,7 +381,7 @@ def create_regions(world: World):
connect_regions(mw.get_region("Cruise Ship", p), badge_seller, "CS -> Badge Seller", p)
if mw.EnableDLC2[p].value > 0:
if w.is_dlc2():
nyakuza_metro = create_region_and_connect(w, "Nyakuza Metro", "Telescope -> Nyakuza Metro", spaceship)
metro_freeroam = create_region_and_connect(w, "Nyakuza Free Roam", "Nyakuza Metro - Free Roam", nyakuza_metro)
create_region_and_connect(w, "Rush Hour", "Nyakuza Metro - Finale", nyakuza_metro)
@@ -471,36 +470,6 @@ def randomize_act_entrances(world: World):
# Look for candidates to map this act to
candidate_list: typing.List[Region] = []
for candidate in region_list:
if world.multiworld.VanillaAlpine[world.player].value > 0 and region.name == "Alpine Free Roam" \
or world.multiworld.VanillaAlpine[world.player].value == 2 and region.name == "The Illness has Spread":
candidate_list.clear()
candidate_list.append(region)
break
if world.multiworld.VanillaAlpine[world.player].value > 0 and candidate.name == "Alpine Free Roam" \
or world.multiworld.VanillaAlpine[world.player].value == 2 and candidate.name == "The Illness has Spread":
continue
if world.multiworld.VanillaMetro[world.player].value > 0 and region.name == "Nyakuza Free Roam":
candidate_list.clear()
candidate_list.append(region)
break
if world.multiworld.VanillaMetro[world.player].value > 0 and candidate.name == "Nyakuza Free Roam":
continue
if candidate.name == "Rush Hour" and world.multiworld.EndGoal[world.player].value == 2 or \
world.multiworld.VanillaMetro[world.player].value == 2:
continue
if region.name == "Rush Hour":
if world.multiworld.EndGoal[world.player].value == 2 or \
world.multiworld.VanillaMetro[world.player].value == 2:
candidate_list.clear()
candidate_list.append(region)
break
# We're mapping something to the first act, make sure it is valid
if not has_guaranteed:
if candidate.name not in guaranteed_first_acts:
@@ -618,6 +587,19 @@ def is_act_blacklisted(world: World, name: str) -> bool:
if name == "The Finale":
return world.multiworld.EndGoal[world.player].value == 1
if name == "Alpine Free Roam":
return world.multiworld.VanillaAlpine[world.player].value > 0
if name == "The Illness has Spread":
return world.multiworld.VanillaAlpine[world.player].value == 2
if name == "Nyakuza Free Roam":
return world.multiworld.VanillaMetro[world.player].value > 0
if name == "Rush Hour":
return world.multiworld.EndGoal[world.player].value == 2 \
or world.multiworld.VanillaMetro[world.player].value == 2
return name in blacklisted_acts.values()

View File

@@ -49,10 +49,60 @@ def can_sdj(state: CollectionState, world: World):
return can_use_hat(state, world, HatType.SPRINT)
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
def has_paintings(state: CollectionState, world: World, count: int) -> bool:
if not painting_logic(world):
return True
# Cherry Hover
if get_difficulty(world) == 2:
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):
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:
return True
return paintings >= count
def zipline_logic(world: World) -> bool:
return world.multiworld.ShuffleAlpineZiplines[world.player].value > 0
def can_use_hookshot(state: CollectionState, world: World):
return state.has("Hookshot Badge", world.player)
def can_hit(state: CollectionState, world: World):
if world.multiworld.UmbrellaLogic[world.player].value == 0:
return True
return state.has("Umbrella", world.player) or can_use_hat(state, world, HatType.BREWING)
def can_surf(state: CollectionState, world: World):
return state.has("No Bonk Badge", world.player)
def has_relic_combo(state: CollectionState, world: World, relic: str) -> bool:
return state.has_group(relic, world.player, len(world.item_name_groups[relic]))
@@ -61,13 +111,6 @@ def get_relic_count(state: CollectionState, world: World, relic: str) -> int:
return state.count_group(relic, world.player)
def can_hit_bells(state: CollectionState, world: World):
if world.multiworld.UmbrellaLogic[world.player].value == 0:
return True
return state.has("Umbrella", world.player) or can_use_hat(state, world, HatType.BREWING)
# Only use for rifts
def can_clear_act(state: CollectionState, world: World, act_entrance: str) -> bool:
entrance: Entrance = world.multiworld.get_entrance(act_entrance, world.player)
@@ -98,9 +141,6 @@ def can_clear_metro(state: CollectionState, world: World) -> bool:
def set_rules(world: World):
dlc1: bool = bool(world.multiworld.EnableDLC1[world.player].value > 0)
dlc2: bool = bool(world.multiworld.EnableDLC2[world.player].value > 0)
# First, chapter access
starting_chapter = ChapterIndex(world.multiworld.StartingChapter[world.player].value)
world.set_chapter_cost(starting_chapter, 0)
@@ -114,25 +154,26 @@ def set_rules(world: World):
final_chapter = ChapterIndex.METRO
chapter_list.append(ChapterIndex.FINALE)
if dlc1:
if world.is_dlc1():
chapter_list.append(ChapterIndex.CRUISE)
if dlc2 and final_chapter is not ChapterIndex.METRO:
if world.is_dlc2() and final_chapter is not ChapterIndex.METRO:
chapter_list.append(ChapterIndex.METRO)
chapter_list.remove(starting_chapter)
world.multiworld.random.shuffle(chapter_list)
if starting_chapter is not ChapterIndex.ALPINE and (dlc1 or dlc2):
if starting_chapter is not ChapterIndex.ALPINE and (world.is_dlc1() or world.is_dlc2()):
index1: int = 69
index2: int = 69
pos: int
lowest_index: int
chapter_list.remove(ChapterIndex.ALPINE)
if dlc1:
if world.is_dlc1():
index1 = chapter_list.index(ChapterIndex.CRUISE)
if dlc2 and final_chapter is not ChapterIndex.METRO:
if world.is_dlc2() and final_chapter is not ChapterIndex.METRO:
index2 = chapter_list.index(ChapterIndex.METRO)
lowest_index = min(index1, index2)
@@ -143,6 +184,14 @@ def set_rules(world: World):
chapter_list.insert(pos, ChapterIndex.ALPINE)
if world.is_dlc1() and world.is_dlc2() and final_chapter is not ChapterIndex.METRO:
chapter_list.remove(ChapterIndex.METRO)
index = chapter_list.index(ChapterIndex.CRUISE)
if index >= len(chapter_list):
chapter_list.append(ChapterIndex.METRO)
else:
chapter_list.insert(world.multiworld.random.randint(index+1, len(chapter_list)), ChapterIndex.METRO)
lowest_cost: int = world.multiworld.LowestChapterCost[world.player].value
highest_cost: int = world.multiworld.HighestChapterCost[world.player].value
@@ -190,12 +239,12 @@ def set_rules(world: World):
lambda state: state.has("Time Piece", world.player, world.get_chapter_cost(ChapterIndex.FINALE))
and can_use_hat(state, world, HatType.BREWING) and can_use_hat(state, world, HatType.DWELLER))
if dlc1:
if world.is_dlc1():
add_rule(world.multiworld.get_entrance("Telescope -> The Arctic Cruise", world.player),
lambda state: state.has("Time Piece", world.player, world.get_chapter_cost(ChapterIndex.ALPINE))
and state.has("Time Piece", world.player, world.get_chapter_cost(ChapterIndex.CRUISE)))
if dlc2:
if world.is_dlc2():
add_rule(world.multiworld.get_entrance("Telescope -> Nyakuza Metro", world.player),
lambda state: state.has("Time Piece", world.player, world.get_chapter_cost(ChapterIndex.ALPINE))
and state.has("Time Piece", world.player, world.get_chapter_cost(ChapterIndex.METRO))
@@ -237,28 +286,21 @@ 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:
state.count("Progressive Painting Unlock", world.player) >= paintings)
add_rule(location, lambda state, paintings=data.paintings: has_paintings(state, world, paintings))
if data.dweller_bell > 0:
if data.dweller_bell == 1: # Required to be hit regardless of Dweller Mask
add_rule(location, lambda state: can_hit_bells(state, world))
else: # Can bypass with Dweller Mask
add_rule(location, lambda state: can_hit_bells(state, world) or can_use_hat(state, world, HatType.DWELLER))
if data.hit_requirement > 0:
if data.hit_requirement == 1:
add_rule(location, lambda state: can_hit(state, world))
else: # Can bypass with Dweller Mask (dweller bells)
add_rule(location, lambda state: can_hit(state, world) or can_use_hat(state, world, HatType.DWELLER))
if get_difficulty(world) >= 1:
world.multiworld.KnowledgeChecks[world.player].value = 1
set_specific_rules(world)
if world.multiworld.LogicDifficulty[world.player].value >= 1:
world.multiworld.SDJLogic[world.player].value = 1
if world.multiworld.SDJLogic[world.player].value > 0:
set_sdj_rules(world)
if world.multiworld.ShuffleAlpineZiplines[world.player].value > 0:
set_alps_zipline_rules(world)
for (key, acts) in act_connections.items():
if "Arctic Cruise" in key and not dlc1:
if "Arctic Cruise" in key and not world.is_dlc1():
continue
i: int = 1
@@ -304,11 +346,6 @@ def set_rules(world: World):
def set_specific_rules(world: World):
dlc1: bool = bool(world.multiworld.EnableDLC1[world.player].value > 0)
add_rule(world.multiworld.get_entrance("Alpine Skyline - Finale", world.player),
lambda state: can_clear_alpine(state, world))
add_rule(world.multiworld.get_location("Mafia Boss Shop Item", world.player),
lambda state: state.has("Time Piece", world.player, 12)
and state.has("Time Piece", world.player, world.get_chapter_cost(ChapterIndex.BIRDS)))
@@ -316,37 +353,158 @@ def set_specific_rules(world: World):
add_rule(world.multiworld.get_location("Spaceship - Rumbi Abuse", world.player),
lambda state: state.has("Time Piece", world.player, 4))
# Normal logic
if world.multiworld.LogicDifficulty[world.player].value == 0:
add_rule(world.multiworld.get_entrance("-> The Birdhouse", world.player),
lambda state: can_use_hat(state, world, HatType.BREWING))
set_mafia_town_rules(world)
set_subcon_rules(world)
set_alps_rules(world)
add_rule(world.multiworld.get_location("Alpine Skyline - Yellow Band Hills", world.player),
lambda state: can_use_hat(state, world, HatType.BREWING))
if world.is_dlc1():
set_dlc1_rules(world)
if dlc1:
add_rule(world.multiworld.get_location("Act Completion (Time Rift - Deep Sea)", world.player),
lambda state: can_use_hat(state, world, HatType.DWELLER))
if world.is_dlc2():
set_dlc2_rules(world)
add_rule(world.multiworld.get_location("Rock the Boat - Post Captain Rescue", world.player),
lambda state: can_use_hat(state, world, HatType.ICE))
difficulty: int = get_difficulty(world)
if is_player_knowledgeable(world) or difficulty >= 1:
set_knowledge_rules(world)
add_rule(world.multiworld.get_location("Act Completion (Rock the Boat)", world.player),
lambda state: can_use_hat(state, world, HatType.ICE))
if difficulty == 0:
set_normal_rules(world)
# Hard logic, includes SDJ stuff
if world.multiworld.LogicDifficulty[world.player].value >= 1:
add_rule(world.multiworld.get_location("Act Completion (Time Rift - The Twilight Bell)", world.player),
lambda state: can_use_hat(state, world, HatType.SPRINT)
and state.has("Scooter Badge", world.player), "or")
if difficulty >= 1:
set_hard_rules(world)
# Expert logic
if world.multiworld.LogicDifficulty[world.player].value >= 2:
set_rule(world.multiworld.get_location("Alpine Skyline - The Twilight Path", world.player), lambda state: True)
else:
add_rule(world.multiworld.get_entrance("-> The Twilight Bell", world.player),
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))
add_rule(world.multiworld.get_location("Alpine Skyline - Yellow Band Hills", world.player),
lambda state: can_use_hat(state, world, HatType.BREWING))
# 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))
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))
# 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))
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))
def set_hard_rules(world: World):
# Hard: clear Time Rift - The Twilight Bell with Sprint+Scooter only
add_rule(world.multiworld.get_location("Act Completion (Time Rift - The Twilight Bell)", world.player),
lambda state: can_use_hat(state, world, HatType.SPRINT)
and state.has("Scooter Badge", world.player), "or")
# Hard: Cross Subcon boss arena gap with No Bonk + SDJ,
# allowing access to the boss arena chest, and Toilet of Doom without Hookshot
# 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")
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("Act Completion (Time Rift - Curly Tail Trail)", world.player),
lambda state: can_sdj(state, world), "or")
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")
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")
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")
# Expert: enter and clear The Subcon Well with No Bonk Badge only
for loc in world.multiworld.get_region("The Subcon Well", world.player).locations:
add_rule(loc, lambda state: can_surf(state, world) and has_paintings(state, world, 1), "or")
# 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)
# 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)
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))
def set_mafia_town_rules(world: World):
add_rule(world.multiworld.get_location("Mafia Town - Behind HQ Chest", world.player),
lambda state: state.can_reach("Act Completion (Heating Up Mafia Town)", "Location", world.player)
or state.can_reach("Down with the Mafia!", "Region", world.player)
@@ -398,15 +556,16 @@ def set_specific_rules(world: World):
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))
def set_subcon_rules(world: World):
set_rule(world.multiworld.get_location("Subcon Forest - Boss Arena Chest", world.player),
lambda state: state.can_reach("Toilet of Doom", "Region", world.player)
and (world.multiworld.ShuffleSubconPaintings[world.player].value == 0
or state.has("Progressive Painting Unlock", world.player, 1))
and (not painting_logic(world) or has_paintings(state, world, 1))
or state.can_reach("Your Contract has Expired", "Region", 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: state.has("Umbrella", world.player) or can_use_hat(state, world, HatType.BREWING))
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)
@@ -424,90 +583,84 @@ def set_specific_rules(world: World):
add_rule(world.multiworld.get_entrance("Subcon Forest - Act 5", world.player),
lambda state: state.has("Snatcher's Contract - Mail Delivery Service", world.player))
if world.multiworld.ShuffleSubconPaintings[world.player].value > 0:
if painting_logic(world):
for key in contract_locations:
if key == "Snatcher's Contract - The Subcon Well":
continue
add_rule(world.multiworld.get_location(key, world.player),
lambda state: state.has("Progressive Painting Unlock", world.player, 1))
add_rule(world.multiworld.get_location(key, world.player), lambda state: has_paintings(state, world, 1))
def set_alps_rules(world: World):
add_rule(world.multiworld.get_entrance("-> The Birdhouse", world.player),
lambda state: can_use_hookshot(state, world))
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))
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))
if world.multiworld.EnableDLC1[world.player].value > 0:
add_rule(world.multiworld.get_entrance("Cruise Ship Entrance BV", world.player),
lambda state: can_use_hookshot(state, world))
add_rule(world.multiworld.get_entrance("Alpine Skyline - Finale", world.player),
lambda state: can_clear_alpine(state, world))
# This particular item isn't present in Act 3 for some reason, yes in vanilla too
add_rule(world.multiworld.get_location("The Arctic Cruise - Toilet", world.player),
lambda state: state.can_reach("Bon Voyage!", "Region", world.player)
or state.can_reach("Ship Shape", "Region", 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))
if world.multiworld.EnableDLC2[world.player].value > 0:
add_rule(world.multiworld.get_entrance("-> Bluefin Tunnel", world.player),
lambda state: state.has("Metro Ticket - Green", world.player)
or state.has("Metro Ticket - Blue", world.player))
add_rule(world.multiworld.get_entrance("-> The Lava Cake", world.player),
lambda state: state.has("Zipline Unlock - The Lava Cake Path", world.player))
add_rule(world.multiworld.get_entrance("-> Pink Paw Station", world.player),
lambda state: state.has("Metro Ticket - Pink", world.player)
or state.has("Metro Ticket - Yellow", world.player) and state.has("Metro Ticket - Blue", world.player))
add_rule(world.multiworld.get_entrance("-> The Windmill", world.player),
lambda state: state.has("Zipline Unlock - The Windmill Path", world.player))
add_rule(world.multiworld.get_entrance("Nyakuza Metro - Finale", world.player),
lambda state: can_clear_metro(state, world))
add_rule(world.multiworld.get_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 (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))
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 key in shop_locations.keys():
if "Green Clean Station Thug B" in key and is_location_valid(world, key):
add_rule(world.multiworld.get_location(key, world.player),
lambda state: state.has("Metro Ticket - Yellow", world.player), "or")
for (loc, zipline) in zipline_unlocks.items():
add_rule(world.multiworld.get_location(loc, world.player), lambda state: state.has(zipline, world.player))
def set_sdj_rules(world: World):
add_rule(world.multiworld.get_location("Subcon Forest - Long Tree Climb Chest", world.player),
lambda state: can_sdj(state, world)
and (world.multiworld.ShuffleSubconPaintings[world.player].value == 0
or state.count("Progressive Painting Unlock", world.player) >= 2), "or")
def set_dlc1_rules(world: World):
add_rule(world.multiworld.get_entrance("Cruise Ship Entrance BV", world.player),
lambda state: can_use_hookshot(state, world))
add_rule(world.multiworld.get_location("Subcon Forest - Green and Purple Dweller Rocks", world.player),
lambda state: can_sdj(state, world)
and (world.multiworld.ShuffleSubconPaintings[world.player].value == 0
or state.count("Progressive Painting Unlock", world.player) >= 3), "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("Act Completion (Time Rift - Gallery)", world.player),
lambda state: 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")
# This particular item isn't present in Act 3 for some reason, yes in vanilla too
add_rule(world.multiworld.get_location("The Arctic Cruise - Toilet", world.player),
lambda state: state.can_reach("Bon Voyage!", "Region", world.player)
or state.can_reach("Ship Shape", "Region", world.player))
def set_alps_zipline_rules(world: World):
add_rule(world.multiworld.get_entrance("-> The Birdhouse", world.player),
lambda state: state.has("Zipline Unlock - The Birdhouse Path", world.player))
def set_dlc2_rules(world: World):
add_rule(world.multiworld.get_entrance("-> Bluefin Tunnel", world.player),
lambda state: state.has("Metro Ticket - Green", world.player)
or state.has("Metro Ticket - Blue", world.player))
add_rule(world.multiworld.get_entrance("-> The Lava Cake", world.player),
lambda state: state.has("Zipline Unlock - The Lava Cake Path", world.player))
add_rule(world.multiworld.get_entrance("-> Pink Paw Station", world.player),
lambda state: state.has("Metro Ticket - Pink", world.player)
or state.has("Metro Ticket - Yellow", world.player) and state.has("Metro Ticket - Blue", world.player))
add_rule(world.multiworld.get_entrance("-> The Windmill", world.player),
lambda state: state.has("Zipline Unlock - The Windmill Path", world.player))
add_rule(world.multiworld.get_entrance("Nyakuza Metro - Finale", world.player),
lambda state: can_clear_metro(state, world))
add_rule(world.multiworld.get_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 (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))
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: state.has(zipline, world.player))
for key in shop_locations.keys():
if "Green Clean Station Thug B" in key and is_location_valid(world, key):
add_rule(world.multiworld.get_location(key, world.player),
lambda state: state.has("Metro Ticket - Yellow", world.player), "or")
def reg_act_connection(world: World, region: typing.Union[str, Region], unlocked_entrance: typing.Union[str, Entrance]):
@@ -575,22 +728,21 @@ def set_rift_rules(world: World, regions: typing.Dict[str, Region]):
add_rule(entrance, lambda state: can_clear_act(state, world, "Subcon Forest - Act 2"))
reg_act_connection(world, world.multiworld.get_entrance("Subcon Forest - Act 2",
world.player).connected_region, entrance)
if world.multiworld.ShuffleSubconPaintings[world.player].value > 0:
add_rule(entrance, lambda state: state.has("Progressive Painting Unlock", world.player, 2))
if painting_logic(world):
add_rule(entrance, lambda state: has_paintings(state, world, 2))
for entrance in regions["Time Rift - Village"].entrances:
add_rule(entrance, lambda state: can_clear_act(state, world, "Subcon Forest - Act 4"))
reg_act_connection(world, world.multiworld.get_entrance("Subcon Forest - Act 4",
world.player).connected_region, entrance)
if world.multiworld.ShuffleSubconPaintings[world.player].value > 0:
add_rule(entrance, lambda state: state.has("Progressive Painting Unlock", world.player, 2))
if painting_logic(world):
add_rule(entrance, lambda state: has_paintings(state, world, 2))
for entrance in regions["Time Rift - Sleepy Subcon"].entrances:
add_rule(entrance, lambda state: has_relic_combo(state, world, "UFO"))
if world.multiworld.ShuffleSubconPaintings[world.player].value > 0:
add_rule(entrance, lambda state: state.has("Progressive Painting Unlock", world.player, 3))
if painting_logic(world):
add_rule(entrance, lambda state: has_paintings(state, world, 3))
for entrance in regions["Time Rift - Curly Tail Trail"].entrances:
add_rule(entrance, lambda state: state.has("Windmill Cleared", world.player))
@@ -601,14 +753,14 @@ def set_rift_rules(world: World, regions: typing.Dict[str, Region]):
for entrance in regions["Time Rift - Alpine Skyline"].entrances:
add_rule(entrance, lambda state: has_relic_combo(state, world, "Crayon"))
if world.multiworld.EnableDLC1[world.player].value > 0:
if world.is_dlc1() > 0:
for entrance in regions["Time Rift - Balcony"].entrances:
add_rule(entrance, lambda state: can_clear_act(state, world, "The Arctic Cruise - Finale"))
for entrance in regions["Time Rift - Deep Sea"].entrances:
add_rule(entrance, lambda state: has_relic_combo(state, world, "Cake"))
if world.multiworld.EnableDLC2[world.player].value > 0:
if world.is_dlc2() > 0:
for entrance in regions["Time Rift - Rumbi Factory"].entrances:
add_rule(entrance, lambda state: has_relic_combo(state, world, "Necklace"))
@@ -654,19 +806,19 @@ def set_default_rift_rules(world: World):
for entrance in world.multiworld.get_region("Time Rift - Pipe", world.player).entrances:
add_rule(entrance, lambda state: can_clear_act(state, world, "Subcon Forest - Act 2"))
reg_act_connection(world, "The Subcon Well", entrance.name)
if world.multiworld.ShuffleSubconPaintings[world.player].value > 0:
add_rule(entrance, lambda state: state.has("Progressive Painting Unlock", world.player, 2))
if painting_logic(world):
add_rule(entrance, lambda state: has_paintings(state, world, 2))
for entrance in world.multiworld.get_region("Time Rift - Village", world.player).entrances:
add_rule(entrance, lambda state: can_clear_act(state, world, "Subcon Forest - Act 4"))
reg_act_connection(world, "Queen Vanessa's Manor", entrance.name)
if world.multiworld.ShuffleSubconPaintings[world.player].value > 0:
add_rule(entrance, lambda state: state.has("Progressive Painting Unlock", world.player, 2))
if painting_logic(world):
add_rule(entrance, lambda state: has_paintings(state, world, 2))
for entrance in world.multiworld.get_region("Time Rift - Sleepy Subcon", world.player).entrances:
add_rule(entrance, lambda state: has_relic_combo(state, world, "UFO"))
if world.multiworld.ShuffleSubconPaintings[world.player].value > 0:
add_rule(entrance, lambda state: state.has("Progressive Painting Unlock", world.player, 3))
if painting_logic(world):
add_rule(entrance, lambda state: has_paintings(state, world, 3))
for entrance in world.multiworld.get_region("Time Rift - Curly Tail Trail", world.player).entrances:
add_rule(entrance, lambda state: state.has("Windmill Cleared", world.player))
@@ -677,14 +829,14 @@ def set_default_rift_rules(world: World):
for entrance in world.multiworld.get_region("Time Rift - Alpine Skyline", world.player).entrances:
add_rule(entrance, lambda state: has_relic_combo(state, world, "Crayon"))
if world.multiworld.EnableDLC1[world.player].value > 0:
if world.is_dlc1():
for entrance in world.multiworld.get_region("Time Rift - Balcony", world.player).entrances:
add_rule(entrance, lambda state: can_clear_act(state, world, "The Arctic Cruise - Finale"))
for entrance in world.multiworld.get_region("Time Rift - Deep Sea", world.player).entrances:
add_rule(entrance, lambda state: has_relic_combo(state, world, "Cake"))
if world.multiworld.EnableDLC2[world.player].value > 0:
if world.is_dlc2():
for entrance in world.multiworld.get_region("Time Rift - Rumbi Factory", world.player).entrances:
add_rule(entrance, lambda state: has_relic_combo(state, world, "Necklace"))

View File

@@ -90,7 +90,7 @@ class HatInTimeWorld(World):
yarn_pool: typing.List[Item] = create_multiple_items(self, "Yarn", self.multiworld.YarnAvailable[self.player].value)
# 1/5 is progression balanced
for i in range(len(yarn_pool) // 5):
for i in range(int(len(yarn_pool) * (0.01 * self.multiworld.YarnBalancePercent[self.player].value))):
yarn_pool[i].classification = ItemClassification.progression
itempool += yarn_pool
@@ -125,17 +125,16 @@ class HatInTimeWorld(World):
if name == "Time Piece":
tp_count: int = 40
max_extra: int = 0
if self.multiworld.EnableDLC1[self.player].value > 0:
if self.is_dlc1():
max_extra += 6
if self.multiworld.EnableDLC2[self.player].value > 0:
if self.is_dlc2():
max_extra += 10
tp_count += min(max_extra, self.multiworld.MaxExtraTimePieces[self.player].value)
tp_list: typing.List[Item] = create_multiple_items(self, name, tp_count)
# 1/5 is progression balanced
for i in range(len(tp_list) // 5):
for i in range(int(len(tp_list) * (0.01 * self.multiworld.TimePieceBalancePercent[self.player].value))):
tp_list[i].classification = ItemClassification.progression
itempool += tp_list
@@ -191,7 +190,7 @@ class HatInTimeWorld(World):
for name in self.act_connections.keys():
slot_data[name] = self.act_connections[name]
if self.multiworld.EnableDLC2[self.player].value > 0:
if self.is_dlc2():
for name in self.nyakuza_thug_items.keys():
slot_data[name] = self.nyakuza_thug_items[name]
@@ -224,7 +223,7 @@ class HatInTimeWorld(World):
new_hint_data[location.address] = self.get_shuffled_region(region_name)
if self.multiworld.EnableDLC1[self.player].value > 0 and self.multiworld.Tasksanity[self.player].value > 0:
if self.is_dlc1() and self.multiworld.Tasksanity[self.player].value > 0:
ship_shape_region = self.get_shuffled_region("Ship Shape")
id_start: int = get_tasksanity_start_id()
for i in range(self.multiworld.TasksanityCheckCount[self.player].value):
@@ -288,3 +287,12 @@ class HatInTimeWorld(World):
def get_chapter_costs(self):
return chapter_timepiece_costs[self.player]
def is_dlc1(self) -> bool:
return self.multiworld.EnableDLC1[self.player].value > 0
def is_dlc2(self) -> bool:
return self.multiworld.EnableDLC2[self.player].value > 0
def is_dw(self) -> bool:
return self.multiworld.EnableDeathWish[self.player].value > 0