From 90f80ce1c18b2577b2ce6a6e4b21f00cb4c46d99 Mon Sep 17 00:00:00 2001 From: Mysteryem Date: Fri, 17 Jan 2025 02:10:41 +0000 Subject: [PATCH] AHiT: Various logic fixes (#4492) * Fix Director boss photo logic The rules were being added to for the "Director" boss in `set_enemy_rules()`, which didn't exist because the boss created was called "Conductor" instead. The name of the boss has been changed to "Director", to match, because it is more accurate due to DJ Grooves possibly being the boss instead of The Conductor. The missing logic was the `Hookshot Badge` requirement, however, the boss events are only used as part of the `Camera Tourist - All Clear` location, which requires every boss event to be reachable, and the Toxic Flower boss also has a `Hookshot Badge` requirement, so the missing `Hookshot Badge` for the Director boss had no effect on logic. The boss event locations are hidden from spoiler output, so to get a spoiler showing the Director boss event accessed before having `Hookshot Badge`, spoiler output had to be modified to also show the hidden locations. Example sphere from playthrough that should not be possible because it gets the `Hookshot Badge` and the `Conductor` event (now renamed to `Director`) in the same sphere: ``` 5: { Act Completion (Time Rift - Dead Bird Studio): Relic (Crayon Box) Conductor - Dead Bird Studio Basement: Conductor Dead Bird Studio (Rift) - Page: Behind Cardboard Planet: Time Piece Dead Bird Studio (Rift) - Page: Near Time Rift Gate: Hookshot Badge Picture Perfect - Hats Buy Building: Metro Ticket - Blue Snatcher - Your Contract has Expired: Snatcher } ``` * Add missing Hookshot + Painting logic for Toilet boss picture Includes the Hard logic of crossing the gap with a cherry bridge instead of hookshot and the expert logic of being able to skip the boss firewall with a cherry hover. * Fix Alpine Skyline - Goat Outpost Horn region `Alpine Skyline - Goat Outpost Horn` is accessible from The Illness has Spread, but was being added to the region that is only accessible from Alpine Free Roam. `Alpine Skyline - Goat Outpost Horn` has been moved to the region that is accessible from both The Illness has Spread and Alpine Free Roam. * Add missing HitType.umbrella logic for Top of HQ Coin in Beat the Heat Like Heating up Mafia Town, the cannon to the Mafia HQ area only opens once all the faucets have been turned off by hitting them. This requires the Umbrella when umbrella logic is enabled, but the Snatcher Coin on top of Mafia HQ was missing this requirement when accessed from Beat the Heat. * Add missing Main Objective requirement for auto-completed Bonus Stamps When a Main Objective is not excluded, but the bonuses are excluded, the bonuses auto-complete once the Main Objective is completed. The requirement to complete the Main Objective was missing, so the logic was incorrectly awarding bonus stamps as soon as a Contract was unlocked, even when it was not possible to complete the Main Objective of that Contract. * Add missing Hookshot requirement for The Arctic Cruise - Toilet from Bon Voyage! `The Arctic Cruise - Toilet` is accessed from the `Cruise Ship` region, but it is only present in the Ship Shape and Bon Voyage! acts. Ship Shape and Rock the Boat can access `Cruise Ship` without any items, but Bon Voyage! requires the Hookshot Badge to reach `Cruise Ship`. With how the logic was set up, it was incorrectly giving access to `The Arctic Cruise - Toilet` if the player had access to Bon Voyage! but only had access to `Cruise Ship` through Rock the Boat. * Fix Expert logic Rush Hour-only ticket skips The code was checking `if not world.options.NoTicketSkips:`, but that would only be `True` for `False`. For "rush_hour" (for Rush Hour-only ticket skips), it would be `False`, causing Rush Hour-only ticket skips to act as if ticket skips were disabled. * Remove Mystifying Time Mesa: Zipline gaining Hookshot requirement in moderate logic Alpine Skyline - Mystifying Time Mesa: Zipline does not normally require Hookshot Badge because it is an implied requirement due to only being accessible from Alpine Free Roam which does require Hookshot Badge. In normal logic difficulty, the location does not have an explicit Hookshot Badge requirement, but moderate logic was adding a Hookshot Badge requirement. This extraneous Hookshot Badge requirement has been removed. * Fix Act Completion (Queen Vanessa's Manor) not being accessible with Dweller Mask/Brewing Hat It was logically requiring the Umbrella hit type only, whereas all the other locations in Queen Vanessa's Manor require the Dweller Bell hit type which additionally allows Dweller Mask or Brewing Hat. * Remove Dweller Mask requirement for Subcon Forest - Tall Tree Hookshot Swing The Dweller Mask is not used in the intended vanilla route to get this item, so this requirement seems to have been a mistake. * Remove unused SDJ option for Subcon Forest - Long Tree Climb Chest Hard logic can already reach this location with nothing (other than paintings), so the "or" logic of being able to perform an SDJ was unused. * Require any non-HUMT Mafia Town act for Hot Air Balloon with nothing Two buckets/beach balls are required to bucket/ball hover, but there is only a single beach ball accessible in Heating Up Mafia Town, and no accessible buckets. There is an alternative strategy for Top of Lighthouse that only requires a single beach ball, so that location can still be reached with nothing from Heating Up Mafia Town. * Use `get_difficulty()` helper in `set_enemy_rules` Co-authored-by: Exempt-Medic <60412657+exempt-medic@users.noreply.github.com> --------- Co-authored-by: Exempt-Medic <60412657+exempt-medic@users.noreply.github.com> --- worlds/ahit/DeathWishRules.py | 18 +++++++++++++++--- worlds/ahit/Locations.py | 7 +++---- worlds/ahit/Rules.py | 14 +++++++------- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/worlds/ahit/DeathWishRules.py b/worlds/ahit/DeathWishRules.py index 1432ef5c..76723d39 100644 --- a/worlds/ahit/DeathWishRules.py +++ b/worlds/ahit/DeathWishRules.py @@ -141,9 +141,12 @@ def set_dw_rules(world: "HatInTimeWorld"): 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 + # Only set bonus stamp rules to require All Clear 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) + else: + # As soon as the Main Objective is completed, the bonuses auto-complete. + add_rule(bonus_stamps, main_objective.access_rule) if world.options.DWShuffle: for i in range(len(world.dw_shuffle)-1): @@ -343,6 +346,7 @@ def create_enemy_events(world: "HatInTimeWorld"): def set_enemy_rules(world: "HatInTimeWorld"): no_tourist = "Camera Tourist" in world.excluded_dws or "Camera Tourist" in world.excluded_bonuses + difficulty = get_difficulty(world) for enemy, regions in hit_list.items(): if no_tourist and enemy in bosses: @@ -372,6 +376,14 @@ def set_enemy_rules(world: "HatInTimeWorld"): or state.has("Zipline Unlock - The Lava Cake Path", world.player) or state.has("Zipline Unlock - The Windmill Path", world.player)) + elif enemy == "Toilet": + if area == "Toilet of Doom": + # The boss firewall is in the way and can only be skipped on Expert logic using a cherry hover. + add_rule(event, lambda state: has_paintings(state, world, 1, allow_skip=difficulty == Difficulty.EXPERT)) + if difficulty < Difficulty.HARD: + # Hard logic and above can cross the boss arena gap with a cherry bridge. + add_rule(event, lambda state: can_use_hookshot(state, world)) + elif enemy == "Director": if area == "Dead Bird Studio Basement": add_rule(event, lambda state: can_use_hookshot(state, world)) @@ -430,7 +442,7 @@ hit_list = { # Bosses "Mafia Boss": ["Down with the Mafia!", "Encore! Encore!", "Boss Rush"], - "Conductor": ["Dead Bird Studio Basement", "Killing Two Birds", "Boss Rush"], + "Director": ["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", @@ -454,7 +466,7 @@ triple_enemy_locations = [ bosses = [ "Mafia Boss", - "Conductor", + "Director", "Toilet", "Snatcher", "Toxic Flower", diff --git a/worlds/ahit/Locations.py b/worlds/ahit/Locations.py index 9954514e..b34e6bb4 100644 --- a/worlds/ahit/Locations.py +++ b/worlds/ahit/Locations.py @@ -264,7 +264,6 @@ ahit_locations = { required_hats=[HatType.DWELLER], paintings=3), "Subcon Forest - Tall Tree Hookshot Swing": LocData(2000324766, "Subcon Forest Area", - required_hats=[HatType.DWELLER], hookshot=True, paintings=3), @@ -323,7 +322,7 @@ ahit_locations = { "Alpine Skyline - The Twilight Path": LocData(2000334434, "Alpine Skyline Area", required_hats=[HatType.DWELLER]), "Alpine Skyline - The Twilight Bell: Wide Purple Platform": LocData(2000336478, "The Twilight Bell"), "Alpine Skyline - The Twilight Bell: Ice Platform": LocData(2000335826, "The Twilight Bell"), - "Alpine Skyline - Goat Outpost Horn": LocData(2000334760, "Alpine Skyline Area"), + "Alpine Skyline - Goat Outpost Horn": LocData(2000334760, "Alpine Skyline Area (TIHS)", hookshot=True), "Alpine Skyline - Windy Passage": LocData(2000334776, "Alpine Skyline Area (TIHS)", hookshot=True), "Alpine Skyline - The Windmill: Inside Pon Cluster": LocData(2000336395, "The Windmill"), "Alpine Skyline - The Windmill: Entrance": LocData(2000335783, "The Windmill"), @@ -407,7 +406,7 @@ act_completions = { hit_type=HitType.umbrella_or_brewing, hookshot=True, paintings=1), "Act Completion (Queen Vanessa's Manor)": LocData(2000312017, "Queen Vanessa's Manor", - hit_type=HitType.umbrella, paintings=1), + hit_type=HitType.dweller_bell, paintings=1), "Act Completion (Mail Delivery Service)": LocData(2000312032, "Mail Delivery Service", required_hats=[HatType.SPRINT]), @@ -878,7 +877,7 @@ snatcher_coins = { dlc_flags=HatDLC.death_wish), "Snatcher Coin - Top of HQ (DW: BTH)": LocData(0, "Beat the Heat", snatcher_coin="Snatcher Coin - Top of HQ", - dlc_flags=HatDLC.death_wish), + hit_type=HitType.umbrella, dlc_flags=HatDLC.death_wish), "Snatcher Coin - Top of Tower": LocData(0, "Mafia Town Area (HUMT)", snatcher_coin="Snatcher Coin - Top of Tower", dlc_flags=HatDLC.death_wish), diff --git a/worlds/ahit/Rules.py b/worlds/ahit/Rules.py index 183248a0..6753b8eb 100644 --- a/worlds/ahit/Rules.py +++ b/worlds/ahit/Rules.py @@ -414,7 +414,7 @@ def set_moderate_rules(world: "HatInTimeWorld"): # Moderate: Mystifying Time Mesa time trial without hats set_rule(world.multiworld.get_location("Alpine Skyline - Mystifying Time Mesa: Zipline", world.player), - lambda state: can_use_hookshot(state, world)) + lambda state: True) # Moderate: Goat Refinery from TIHS with Sprint only add_rule(world.multiworld.get_location("Alpine Skyline - Goat Refinery", world.player), @@ -493,9 +493,6 @@ def set_hard_rules(world: "HatInTimeWorld"): 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_use_hat(state, world, HatType.SPRINT) and has_paintings(state, world, 2), "or") - add_rule(world.multiworld.get_location("Act Completion (Time Rift - Curly Tail Trail)", world.player), lambda state: can_use_hat(state, world, HatType.SPRINT), "or") @@ -533,7 +530,10 @@ def set_expert_rules(world: "HatInTimeWorld"): # 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) + # There are not enough buckets/beach balls to bucket/ball hover in Heating Up Mafia Town, so any other Mafia Town + # act is required. + add_rule(world.multiworld.get_location("Mafia Town - Hot Air Balloon", world.player), + lambda state: state.can_reach_region("Mafia Town Area", world.player), "or") # Expert: Clear Dead Bird Studio with nothing for loc in world.multiworld.get_region("Dead Bird Studio - Post Elevator Area", world.player).locations: @@ -590,7 +590,7 @@ def set_expert_rules(world: "HatInTimeWorld"): if world.is_dlc2(): # Expert: clear Rush Hour with nothing - if not world.options.NoTicketSkips: + if world.options.NoTicketSkips != NoTicketSkips.option_true: 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), @@ -739,7 +739,7 @@ def set_dlc1_rules(world: "HatInTimeWorld"): # 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) + lambda state: (state.can_reach("Bon Voyage!", "Region", world.player) and can_use_hookshot(state, world)) or state.can_reach("Ship Shape", "Region", world.player))