| 
									
										
										
										
											2024-05-20 03:04:06 -04:00
										 |  |  | from worlds.AutoWorld import CollectionState | 
					
						
							|  |  |  | from .Rules import can_use_hat, can_use_hookshot, can_hit, zipline_logic, get_difficulty, has_paintings | 
					
						
							|  |  |  | from .Types import HatType, Difficulty, HatInTimeLocation, HatInTimeItem, LocData, HitType | 
					
						
							|  |  |  | from .DeathWishLocations import dw_prereqs, dw_candles | 
					
						
							|  |  |  | from BaseClasses import Entrance, Location, ItemClassification | 
					
						
							|  |  |  | from worlds.generic.Rules import add_rule, set_rule | 
					
						
							|  |  |  | from typing import List, Callable, TYPE_CHECKING | 
					
						
							|  |  |  | from .Locations import death_wishes | 
					
						
							|  |  |  | from .Options import EndGoal | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if TYPE_CHECKING: | 
					
						
							|  |  |  |     from . import HatInTimeWorld | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Any speedruns expect the player to have Sprint Hat | 
					
						
							|  |  |  | dw_requirements = { | 
					
						
							|  |  |  |     "Beat the Heat": LocData(hit_type=HitType.umbrella), | 
					
						
							|  |  |  |     "So You're Back From Outer Space": LocData(hookshot=True), | 
					
						
							|  |  |  |     "Mafia's Jumps": LocData(required_hats=[HatType.ICE]), | 
					
						
							|  |  |  |     "Vault Codes in the Wind": LocData(required_hats=[HatType.SPRINT]), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Security Breach": LocData(hit_type=HitType.umbrella_or_brewing), | 
					
						
							|  |  |  |     "10 Seconds until Self-Destruct": LocData(hookshot=True), | 
					
						
							|  |  |  |     "Community Rift: Rhythm Jump Studio": LocData(required_hats=[HatType.ICE]), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Speedrun Well": LocData(hookshot=True, hit_type=HitType.umbrella_or_brewing), | 
					
						
							|  |  |  |     "Boss Rush": LocData(hit_type=HitType.umbrella, 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), | 
					
						
							|  |  |  |     "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]), | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-04 15:15:28 -04:00
										 |  |  |     "Rift Collapse: Deep Sea": LocData(hookshot=True), | 
					
						
							| 
									
										
										
										
											2024-05-20 03:04:06 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Includes main objective requirements | 
					
						
							|  |  |  | dw_bonus_requirements = { | 
					
						
							|  |  |  |     # Some One-Hit Hero requirements need badge pins as well because of Hookshot | 
					
						
							|  |  |  |     "So You're Back From Outer Space": LocData(required_hats=[HatType.SPRINT]), | 
					
						
							|  |  |  |     "Encore! Encore!": LocData(misc_required=["One-Hit Hero Badge"]), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "10 Seconds until Self-Destruct": LocData(misc_required=["One-Hit Hero Badge", "Badge Pin"]), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "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]), | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-04 15:15:28 -04:00
										 |  |  |     "Rift Collapse: Deep Sea": LocData(required_hats=[HatType.DWELLER]), | 
					
						
							| 
									
										
										
										
											2024-05-20 03:04:06 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | dw_stamp_costs = { | 
					
						
							|  |  |  |     "So You're Back From Outer Space":  2, | 
					
						
							|  |  |  |     "Collect-a-thon":                   5, | 
					
						
							|  |  |  |     "She Speedran from Outer Space":    8, | 
					
						
							|  |  |  |     "Encore! Encore!":                  10, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Security Breach":                  4, | 
					
						
							|  |  |  |     "The Great Big Hootenanny":         7, | 
					
						
							|  |  |  |     "10 Seconds until Self-Destruct":   15, | 
					
						
							|  |  |  |     "Killing Two Birds":                25, | 
					
						
							|  |  |  |     "Snatcher Coins in Nyakuza Metro":  30, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Speedrun Well":                10, | 
					
						
							|  |  |  |     "Boss Rush":                    15, | 
					
						
							|  |  |  |     "Quality Time with Snatcher":   20, | 
					
						
							|  |  |  |     "Breaching the Contract":       40, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Bird Sanctuary":           15, | 
					
						
							|  |  |  |     "Wound-Up Windmill":        30, | 
					
						
							|  |  |  |     "The Illness has Speedrun": 35, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "The Mustache Gauntlet":    35, | 
					
						
							|  |  |  |     "No More Bad Guys":         50, | 
					
						
							|  |  |  |     "Seal the Deal":            70, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | required_snatcher_coins = { | 
					
						
							|  |  |  |     "Snatcher Coins in Mafia Town": ["Snatcher Coin - Top of HQ", "Snatcher Coin - Top of Tower", | 
					
						
							|  |  |  |                                      "Snatcher Coin - Under Ruined Tower"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Snatcher Coins in Battle of the Birds": ["Snatcher Coin - Top of Red House", "Snatcher Coin - Train Rush", | 
					
						
							|  |  |  |                                               "Snatcher Coin - Picture Perfect"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Snatcher Coins in Subcon Forest": ["Snatcher Coin - Swamp Tree", "Snatcher Coin - Manor Roof", | 
					
						
							|  |  |  |                                         "Snatcher Coin - Giant Time Piece"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Snatcher Coins in Alpine Skyline": ["Snatcher Coin - Goat Village Top", "Snatcher Coin - Lava Cake", | 
					
						
							|  |  |  |                                          "Snatcher Coin - Windmill"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Snatcher Coins in Nyakuza Metro": ["Snatcher Coin - Green Clean Tower", "Snatcher Coin - Bluefin Cat Train", | 
					
						
							|  |  |  |                                         "Snatcher Coin - Pink Paw Fence"], | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def set_dw_rules(world: "HatInTimeWorld"): | 
					
						
							|  |  |  |     if "Snatcher's Hit List" not in world.excluded_dws or "Camera Tourist" not in world.excluded_dws: | 
					
						
							|  |  |  |         set_enemy_rules(world) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     dw_list: List[str] = [] | 
					
						
							|  |  |  |     if world.options.DWShuffle: | 
					
						
							|  |  |  |         dw_list = world.dw_shuffle | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         for name in death_wishes.keys(): | 
					
						
							|  |  |  |             dw_list.append(name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for name in dw_list: | 
					
						
							|  |  |  |         if name == "Snatcher Coins in Nyakuza Metro" and not world.is_dlc2(): | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         dw = world.multiworld.get_region(name, world.player) | 
					
						
							|  |  |  |         if not world.options.DWShuffle and name in dw_stamp_costs.keys(): | 
					
						
							|  |  |  |             for entrance in dw.entrances: | 
					
						
							|  |  |  |                 add_rule(entrance, lambda state, n=name: state.has("Stamps", world.player, dw_stamp_costs[n])) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         main_objective = world.multiworld.get_location(f"{name} - Main Objective", world.player) | 
					
						
							|  |  |  |         all_clear = world.multiworld.get_location(f"{name} - All Clear", world.player) | 
					
						
							|  |  |  |         main_stamp = world.multiworld.get_location(f"Main Stamp - {name}", world.player) | 
					
						
							|  |  |  |         bonus_stamps = world.multiworld.get_location(f"Bonus Stamps - {name}", world.player) | 
					
						
							|  |  |  |         if not world.options.DWEnableBonus: | 
					
						
							|  |  |  |             # place nothing, but let the locations exist still, so we can use them for bonus stamp rules | 
					
						
							|  |  |  |             all_clear.address = None | 
					
						
							|  |  |  |             all_clear.place_locked_item(HatInTimeItem("Nothing", ItemClassification.filler, None, world.player)) | 
					
						
							|  |  |  |             all_clear.show_in_spoiler = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # No need for rules if excluded - stamps will be auto-granted | 
					
						
							|  |  |  |         if world.is_dw_excluded(name): | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         modify_dw_rules(world, name) | 
					
						
							|  |  |  |         add_dw_rules(world, main_objective) | 
					
						
							|  |  |  |         add_dw_rules(world, all_clear) | 
					
						
							|  |  |  |         add_rule(main_stamp, main_objective.access_rule) | 
					
						
							|  |  |  |         add_rule(all_clear, main_objective.access_rule) | 
					
						
							|  |  |  |         # Only set bonus stamp rules if we don't auto complete bonuses | 
					
						
							|  |  |  |         if not world.options.DWAutoCompleteBonuses and not world.is_bonus_excluded(all_clear.name): | 
					
						
							|  |  |  |             add_rule(bonus_stamps, all_clear.access_rule) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if world.options.DWShuffle: | 
					
						
							|  |  |  |         for i in range(len(world.dw_shuffle)-1): | 
					
						
							|  |  |  |             name = world.dw_shuffle[i+1] | 
					
						
							|  |  |  |             prev_dw = world.multiworld.get_region(world.dw_shuffle[i], world.player) | 
					
						
							|  |  |  |             entrance = world.multiworld.get_entrance(f"{prev_dw.name} -> {name}", world.player) | 
					
						
							|  |  |  |             add_rule(entrance, lambda state, n=prev_dw.name: state.has(f"1 Stamp - {n}", world.player)) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         for key, reqs in dw_prereqs.items(): | 
					
						
							|  |  |  |             if key == "Snatcher Coins in Nyakuza Metro" and not world.is_dlc2(): | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             access_rules: List[Callable[[CollectionState], bool]] = [] | 
					
						
							|  |  |  |             entrances: List[Entrance] = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for parent in reqs: | 
					
						
							|  |  |  |                 entrance = world.multiworld.get_entrance(f"{parent} -> {key}", world.player) | 
					
						
							|  |  |  |                 entrances.append(entrance) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if not world.is_dw_excluded(parent): | 
					
						
							|  |  |  |                     access_rules.append(lambda state, n=parent: state.has(f"1 Stamp - {n}", world.player)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for entrance in entrances: | 
					
						
							|  |  |  |                 for rule in access_rules: | 
					
						
							|  |  |  |                     add_rule(entrance, rule) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if world.options.EndGoal == EndGoal.option_seal_the_deal: | 
					
						
							|  |  |  |         world.multiworld.completion_condition[world.player] = lambda state: \ | 
					
						
							|  |  |  |             state.has("1 Stamp - Seal the Deal", world.player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def add_dw_rules(world: "HatInTimeWorld", loc: Location): | 
					
						
							|  |  |  |     bonus: bool = "All Clear" in loc.name | 
					
						
							|  |  |  |     if not bonus: | 
					
						
							| 
									
										
										
										
											2024-06-04 15:15:28 -04:00
										 |  |  |         data = dw_requirements.get(loc.parent_region.name) | 
					
						
							| 
									
										
										
										
											2024-05-20 03:04:06 -04:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2024-06-04 15:15:28 -04:00
										 |  |  |         data = dw_bonus_requirements.get(loc.parent_region.name) | 
					
						
							| 
									
										
										
										
											2024-05-20 03:04:06 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if data is None: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if data.hookshot: | 
					
						
							|  |  |  |         add_rule(loc, lambda state: can_use_hookshot(state, world)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for hat in data.required_hats: | 
					
						
							|  |  |  |         add_rule(loc, lambda state, h=hat: can_use_hat(state, world, h)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for misc in data.misc_required: | 
					
						
							|  |  |  |         add_rule(loc, lambda state, item=misc: state.has(item, world.player)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if data.paintings > 0 and world.options.ShuffleSubconPaintings: | 
					
						
							|  |  |  |         add_rule(loc, lambda state, paintings=data.paintings: has_paintings(state, world, paintings)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if data.hit_type is not HitType.none and world.options.UmbrellaLogic: | 
					
						
							|  |  |  |         if data.hit_type == HitType.umbrella: | 
					
						
							|  |  |  |             add_rule(loc, lambda state: state.has("Umbrella", world.player)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         elif data.hit_type == HitType.umbrella_or_brewing: | 
					
						
							|  |  |  |             add_rule(loc, lambda state: state.has("Umbrella", world.player) | 
					
						
							|  |  |  |                      or can_use_hat(state, world, HatType.BREWING)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         elif data.hit_type == HitType.dweller_bell: | 
					
						
							|  |  |  |             add_rule(loc, lambda state: state.has("Umbrella", world.player) | 
					
						
							|  |  |  |                      or can_use_hat(state, world, HatType.BREWING) | 
					
						
							|  |  |  |                      or can_use_hat(state, world, HatType.DWELLER)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def modify_dw_rules(world: "HatInTimeWorld", 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": | 
					
						
							|  |  |  |         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)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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": | 
					
						
							|  |  |  |         if difficulty >= Difficulty.HARD: | 
					
						
							|  |  |  |             set_rule(main_objective, lambda state: True) | 
					
						
							|  |  |  |             set_rule(full_clear, lambda state: True) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     elif name == "So You're Back from Outer Space": | 
					
						
							|  |  |  |         # Without Hookshot | 
					
						
							|  |  |  |         if difficulty >= Difficulty.HARD: | 
					
						
							|  |  |  |             set_rule(main_objective, lambda state: True) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     elif name == "Wound-Up Windmill": | 
					
						
							|  |  |  |         # No badge pin required. Player can switch to One Hit Hero after the checkpoint and do level without it. | 
					
						
							|  |  |  |         if difficulty >= Difficulty.MODERATE: | 
					
						
							|  |  |  |             set_rule(full_clear, lambda state: can_use_hookshot(state, world) | 
					
						
							|  |  |  |                      and state.has("One-Hit Hero Badge", world.player)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if name in dw_candles: | 
					
						
							|  |  |  |         set_candle_dw_rules(name, world) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def set_candle_dw_rules(name: str, world: "HatInTimeWorld"): | 
					
						
							|  |  |  |     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 == "Zero Jumps": | 
					
						
							|  |  |  |         add_rule(main_objective, lambda state: state.has("Zero Jumps", world.player)) | 
					
						
							|  |  |  |         add_rule(full_clear, lambda state: state.has("Zero Jumps", world.player, 4) | 
					
						
							|  |  |  |                  and state.has("Train Rush (Zero Jumps)", world.player) and can_use_hat(state, world, HatType.ICE)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # No Ice Hat/painting required in Expert for Toilet Zero Jumps | 
					
						
							|  |  |  |         # This painting wall can only be skipped via cherry hover. | 
					
						
							|  |  |  |         if get_difficulty(world) < Difficulty.EXPERT or world.options.NoPaintingSkips: | 
					
						
							|  |  |  |             set_rule(world.multiworld.get_location("Toilet of Doom (Zero Jumps)", world.player), | 
					
						
							|  |  |  |                      lambda state: can_use_hookshot(state, world) and can_hit(state, world) | 
					
						
							|  |  |  |                      and has_paintings(state, world, 1, False)) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             set_rule(world.multiworld.get_location("Toilet of Doom (Zero Jumps)", world.player), | 
					
						
							|  |  |  |                      lambda state: can_use_hookshot(state, world) and can_hit(state, world)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         set_rule(world.multiworld.get_location("Contractual Obligations (Zero Jumps)", world.player), | 
					
						
							|  |  |  |                  lambda state: has_paintings(state, world, 1, False)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     elif name == "Snatcher's Hit List": | 
					
						
							|  |  |  |         add_rule(main_objective, lambda state: state.has("Mafia Goon", world.player)) | 
					
						
							|  |  |  |         add_rule(full_clear, lambda state: state.has("Enemy", world.player, 12)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     elif name == "Camera Tourist": | 
					
						
							|  |  |  |         add_rule(main_objective, lambda state: state.has("Enemy", world.player, 8)) | 
					
						
							|  |  |  |         add_rule(full_clear, lambda state: state.has("Boss", world.player, 6) | 
					
						
							|  |  |  |                  and state.has("Triple Enemy Photo", world.player)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     elif "Snatcher Coins" in name: | 
					
						
							|  |  |  |         coins: List[str] = [] | 
					
						
							|  |  |  |         for coin in required_snatcher_coins[name]: | 
					
						
							|  |  |  |             coins.append(coin) | 
					
						
							|  |  |  |             add_rule(full_clear, lambda state, c=coin: state.has(c, world.player)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # any coin works for the main objective | 
					
						
							|  |  |  |         add_rule(main_objective, lambda state: state.has(coins[0], world.player) | 
					
						
							|  |  |  |                  or state.has(coins[1], world.player) | 
					
						
							|  |  |  |                  or state.has(coins[2], world.player)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def create_enemy_events(world: "HatInTimeWorld"): | 
					
						
							|  |  |  |     no_tourist = "Camera Tourist" in world.excluded_dws | 
					
						
							|  |  |  |     for enemy, regions in hit_list.items(): | 
					
						
							|  |  |  |         if no_tourist and enemy in bosses: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for area in regions: | 
					
						
							|  |  |  |             if (area == "Bon Voyage!" or area == "Time Rift - Deep Sea") and not world.is_dlc1(): | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if area == "Time Rift - Tour" and (not world.is_dlc1() or world.options.ExcludeTour): | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if area == "Bluefin Tunnel" and not world.is_dlc2(): | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if world.options.DWShuffle and area in death_wishes.keys() and area not in world.dw_shuffle: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             region = world.multiworld.get_region(area, world.player) | 
					
						
							|  |  |  |             event = HatInTimeLocation(world.player, f"{enemy} - {area}", None, region) | 
					
						
							|  |  |  |             event.place_locked_item(HatInTimeItem(enemy, ItemClassification.progression, None, world.player)) | 
					
						
							|  |  |  |             region.locations.append(event) | 
					
						
							|  |  |  |             event.show_in_spoiler = False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for name in triple_enemy_locations: | 
					
						
							|  |  |  |         if name == "Time Rift - Tour" and (not world.is_dlc1() or world.options.ExcludeTour): | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if world.options.DWShuffle and name in death_wishes.keys() and name not in world.dw_shuffle: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         region = world.multiworld.get_region(name, world.player) | 
					
						
							|  |  |  |         event = HatInTimeLocation(world.player, f"Triple Enemy Photo - {name}", None, region) | 
					
						
							|  |  |  |         event.place_locked_item(HatInTimeItem("Triple Enemy Photo", ItemClassification.progression, None, world.player)) | 
					
						
							|  |  |  |         region.locations.append(event) | 
					
						
							|  |  |  |         event.show_in_spoiler = False | 
					
						
							|  |  |  |         if name == "The Mustache Gauntlet": | 
					
						
							|  |  |  |             add_rule(event, lambda state: can_use_hookshot(state, world) and can_use_hat(state, world, HatType.DWELLER)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def set_enemy_rules(world: "HatInTimeWorld"): | 
					
						
							|  |  |  |     no_tourist = "Camera Tourist" in world.excluded_dws or "Camera Tourist" in world.excluded_bonuses | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for enemy, regions in hit_list.items(): | 
					
						
							|  |  |  |         if no_tourist and enemy in bosses: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for area in regions: | 
					
						
							|  |  |  |             if (area == "Bon Voyage!" or area == "Time Rift - Deep Sea") and not world.is_dlc1(): | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if area == "Time Rift - Tour" and (not world.is_dlc1() or world.options.ExcludeTour): | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if area == "Bluefin Tunnel" and not world.is_dlc2(): | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if world.options.DWShuffle and area in death_wishes and area not in world.dw_shuffle: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             event = world.multiworld.get_location(f"{enemy} - {area}", world.player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if enemy == "Toxic Flower": | 
					
						
							|  |  |  |                 add_rule(event, lambda state: can_use_hookshot(state, world)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if area == "The Illness has Spread": | 
					
						
							|  |  |  |                     add_rule(event, lambda state: not zipline_logic(world) or | 
					
						
							|  |  |  |                              state.has("Zipline Unlock - The Birdhouse Path", world.player) | 
					
						
							|  |  |  |                              or state.has("Zipline Unlock - The Lava Cake Path", world.player) | 
					
						
							|  |  |  |                              or state.has("Zipline Unlock - The Windmill Path", world.player)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             elif enemy == "Director": | 
					
						
							|  |  |  |                 if area == "Dead Bird Studio Basement": | 
					
						
							|  |  |  |                     add_rule(event, lambda state: can_use_hookshot(state, world)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             elif enemy == "Snatcher" or enemy == "Mustache Girl": | 
					
						
							|  |  |  |                 if area == "Boss Rush": | 
					
						
							|  |  |  |                     # 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) | 
					
						
							|  |  |  |                              and can_use_hat(state, world, HatType.DWELLER)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             elif enemy == "Shock Squid" or enemy == "Ninja Cat": | 
					
						
							|  |  |  |                 if area == "Time Rift - Deep Sea": | 
					
						
							|  |  |  |                     add_rule(event, lambda state: can_use_hookshot(state, world)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Enemies for Snatcher's Hit List/Camera Tourist, and where to find them | 
					
						
							|  |  |  | hit_list = { | 
					
						
							|  |  |  |     "Mafia Goon":       ["Mafia Town Area", "Time Rift - Mafia of Cooks", "Time Rift - Tour", | 
					
						
							|  |  |  |                          "Bon Voyage!", "The Mustache Gauntlet", "Rift Collapse: Mafia of Cooks", | 
					
						
							|  |  |  |                          "So You're Back From Outer Space"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Sleepy Raccoon":   ["She Came from Outer Space", "Down with the Mafia!", "The Twilight Bell", | 
					
						
							|  |  |  |                          "She Speedran from Outer Space", "Mafia's Jumps", "The Mustache Gauntlet", | 
					
						
							|  |  |  |                          "Time Rift - Sleepy Subcon", "Rift Collapse: Sleepy Subcon"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "UFO":              ["Picture Perfect", "So You're Back From Outer Space", "Community Rift: Rhythm Jump Studio"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Rat":              ["Down with the Mafia!", "Bluefin Tunnel"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Shock Squid":      ["Bon Voyage!", "Time Rift - Sleepy Subcon", "Time Rift - Deep Sea", | 
					
						
							|  |  |  |                          "Rift Collapse: Sleepy Subcon"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Shromb Egg":       ["The Birdhouse", "Bird Sanctuary"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Spider":           ["Subcon Forest Area", "The Mustache Gauntlet", "Speedrun Well", | 
					
						
							|  |  |  |                          "The Lava Cake", "The Windmill"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Crow":             ["Mafia Town Area", "The Birdhouse", "Time Rift - Tour", "Bird Sanctuary", | 
					
						
							|  |  |  |                          "Time Rift - Alpine Skyline", "Rift Collapse: Alpine Skyline"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Pompous Crow":     ["The Birdhouse", "Time Rift - The Lab", "Bird Sanctuary", "The Mustache Gauntlet"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Fiery Crow":       ["The Finale", "The Lava Cake", "The Mustache Gauntlet"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Express Owl":      ["The Finale", "Time Rift - The Owl Express", "Time Rift - Deep Sea"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Ninja Cat":        ["The Birdhouse", "The Windmill", "Bluefin Tunnel", "The Mustache Gauntlet", | 
					
						
							|  |  |  |                          "Time Rift - Curly Tail Trail", "Time Rift - Alpine Skyline", "Time Rift - Deep Sea", | 
					
						
							|  |  |  |                          "Rift Collapse: Alpine Skyline"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Bosses | 
					
						
							|  |  |  |     "Mafia Boss":       ["Down with the Mafia!", "Encore! Encore!", "Boss Rush"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Conductor":        ["Dead Bird Studio Basement", "Killing Two Birds", "Boss Rush"], | 
					
						
							|  |  |  |     "Toilet":           ["Toilet of Doom", "Boss Rush"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Snatcher":         ["Your Contract has Expired", "Breaching the Contract", "Boss Rush", | 
					
						
							|  |  |  |                          "Quality Time with Snatcher"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Toxic Flower":     ["The Illness has Spread", "The Illness has Speedrun"], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     "Mustache Girl":    ["The Finale", "Boss Rush", "No More Bad Guys"], | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Camera Tourist has a bonus that requires getting three different types of enemies in one photo. | 
					
						
							|  |  |  | triple_enemy_locations = [ | 
					
						
							|  |  |  |     "She Came from Outer Space", | 
					
						
							|  |  |  |     "She Speedran from Outer Space", | 
					
						
							|  |  |  |     "Mafia's Jumps", | 
					
						
							|  |  |  |     "The Mustache Gauntlet", | 
					
						
							|  |  |  |     "The Birdhouse", | 
					
						
							|  |  |  |     "Bird Sanctuary", | 
					
						
							|  |  |  |     "Time Rift - Tour", | 
					
						
							|  |  |  | ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bosses = [ | 
					
						
							|  |  |  |     "Mafia Boss", | 
					
						
							|  |  |  |     "Conductor", | 
					
						
							|  |  |  |     "Toilet", | 
					
						
							|  |  |  |     "Snatcher", | 
					
						
							|  |  |  |     "Toxic Flower", | 
					
						
							|  |  |  |     "Mustache Girl", | 
					
						
							|  |  |  | ] |