From f61ab0442fde6c26654471e6f5f30fb85e54c49a Mon Sep 17 00:00:00 2001 From: CookieCat Date: Mon, 13 May 2024 17:04:35 -0400 Subject: [PATCH] Improve performance for death wish rules --- worlds/ahit/DeathWishLocations.py | 2 +- worlds/ahit/DeathWishRules.py | 80 +++---------------------------- worlds/ahit/__init__.py | 42 +++++++++++++++- 3 files changed, 48 insertions(+), 76 deletions(-) diff --git a/worlds/ahit/DeathWishLocations.py b/worlds/ahit/DeathWishLocations.py index 9a072dc213..ef74cadcaa 100644 --- a/worlds/ahit/DeathWishLocations.py +++ b/worlds/ahit/DeathWishLocations.py @@ -230,7 +230,7 @@ def create_dw_locations(world: "HatInTimeWorld", dw: Region): dw.locations.append(bonus_stamps) main_stamp.place_locked_item(HatInTimeItem(f"1 Stamp - {dw.name}", ItemClassification.progression, None, world.player)) - bonus_stamps.place_locked_item(HatInTimeItem(f"2 Stamps - {dw.name}", + bonus_stamps.place_locked_item(HatInTimeItem(f"2 Stamp - {dw.name}", ItemClassification.progression, None, world.player)) if dw.name in world.excluded_dws: diff --git a/worlds/ahit/DeathWishRules.py b/worlds/ahit/DeathWishRules.py index 1825795f32..96638cf8db 100644 --- a/worlds/ahit/DeathWishRules.py +++ b/worlds/ahit/DeathWishRules.py @@ -5,8 +5,7 @@ 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 .Regions import act_chapters -from .Locations import zero_jumps, zero_jumps_expert, zero_jumps_hard, death_wishes +from .Locations import death_wishes from .Options import EndGoal if TYPE_CHECKING: @@ -121,7 +120,7 @@ def set_dw_rules(world: "HatInTimeWorld"): 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: get_total_dw_stamps(state, world) >= dw_stamp_costs[n]) + 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) @@ -259,36 +258,13 @@ def modify_dw_rules(world: "HatInTimeWorld", name: str): set_candle_dw_rules(name, world) -def get_total_dw_stamps(state: CollectionState, world: "HatInTimeWorld") -> int: - if world.options.DWShuffle: - return 999 # no stamp costs in death wish shuffle - - count = 0 - - for name in death_wishes: - if name == "Snatcher Coins in Nyakuza Metro" and not world.is_dlc2(): - continue - - if state.has(f"1 Stamp - {name}", world.player): - count += 1 - else: - continue - - if state.has(f"2 Stamps - {name}", world.player): - count += 2 - elif name not in dw_candles: - count += 1 - - return count - - 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: get_zero_jump_clear_count(state, world) >= 1) - add_rule(full_clear, lambda state: get_zero_jump_clear_count(state, world) >= 4 + 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 @@ -306,11 +282,11 @@ def set_candle_dw_rules(name: str, world: "HatInTimeWorld"): elif name == "Snatcher's Hit List": add_rule(main_objective, lambda state: state.has("Mafia Goon", world.player)) - add_rule(full_clear, lambda state: get_reachable_enemy_count(state, world) >= 12) + add_rule(full_clear, lambda state: state.has("Enemy", world.player, 12)) elif name == "Camera Tourist": - add_rule(main_objective, lambda state: get_reachable_enemy_count(state, world) >= 8) - add_rule(full_clear, lambda state: can_reach_all_bosses(state, world) + 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: @@ -325,48 +301,6 @@ def set_candle_dw_rules(name: str, world: "HatInTimeWorld"): or state.has(coins[2], world.player)) -def get_zero_jump_clear_count(state: CollectionState, world: "HatInTimeWorld") -> int: - total = 0 - - for name in act_chapters.keys(): - n = f"{name} (Zero Jumps)" - if n not in zero_jumps: - continue - - if get_difficulty(world) < Difficulty.HARD and n in zero_jumps_hard: - continue - - if get_difficulty(world) < Difficulty.EXPERT and n in zero_jumps_expert: - continue - - if not state.has(n, world.player): - continue - - total += 1 - - return total - - -def get_reachable_enemy_count(state: CollectionState, world: "HatInTimeWorld") -> int: - count = 0 - for enemy in hit_list.keys(): - if enemy in bosses: - continue - - if state.has(enemy, world.player): - count += 1 - - return count - - -def can_reach_all_bosses(state: CollectionState, world: "HatInTimeWorld") -> bool: - for boss in bosses: - if not state.has(boss, world.player): - return False - - return True - - def create_enemy_events(world: "HatInTimeWorld"): no_tourist = "Camera Tourist" in world.excluded_dws for enemy, regions in hit_list.items(): diff --git a/worlds/ahit/__init__.py b/worlds/ahit/__init__.py index 62cfc3f423..759e9e52e2 100644 --- a/worlds/ahit/__init__.py +++ b/worlds/ahit/__init__.py @@ -8,8 +8,8 @@ from .Rules import set_rules from .Options import AHITOptions, slot_data_options, adjust_options, RandomizeHatOrder, EndGoal from .Types import HatType, ChapterIndex, HatInTimeItem from .DeathWishLocations import create_dw_regions, dw_classes, death_wishes -from .DeathWishRules import set_dw_rules, create_enemy_events -from worlds.AutoWorld import World, WebWorld +from .DeathWishRules import set_dw_rules, create_enemy_events, hit_list, bosses +from worlds.AutoWorld import World, WebWorld, CollectionState from typing import List, Dict, TextIO from worlds.LauncherComponents import Component, components, icon_paths, launch_subprocess, Type from Utils import local_path @@ -288,6 +288,44 @@ class HatInTimeWorld(World): for hat in self.hat_craft_order: spoiler_handle.write("Hat Cost: %s: %i\n" % (hat, self.hat_yarn_costs[hat])) + def collect(self, state: "CollectionState", item: "Item") -> bool: + old_count: int = state.count(item.name, self.player) + change = super().collect(state, item) + if change and old_count == 0: + if "Stamp" in item.name: + if "2 Stamp" in item.name: + state.prog_items[self.player]["Stamps"] += 2 + else: + state.prog_items[self.player]["Stamps"] += 1 + elif "(Zero Jumps)" in item.name: + state.prog_items[self.player]["Zero Jumps"] += 1 + elif item.name in hit_list.keys(): + if item.name not in bosses: + state.prog_items[self.player]["Enemy"] += 1 + else: + state.prog_items[self.player]["Boss"] += 1 + + return change + + def remove(self, state: "CollectionState", item: "Item") -> bool: + old_count: int = state.count(item.name, self.player) + change = super().collect(state, item) + if change and old_count == 1: + if "Stamp" in item.name: + if "2 Stamp" in item.name: + state.prog_items[self.player]["Stamps"] -= 2 + else: + state.prog_items[self.player]["Stamps"] -= 1 + elif "(Zero Jumps)" in item.name: + state.prog_items[self.player]["Zero Jumps"] -= 1 + elif item.name in hit_list.keys(): + if item.name not in bosses: + state.prog_items[self.player]["Enemy"] -= 1 + else: + state.prog_items[self.player]["Boss"] -= 1 + + return change + def has_yarn(self) -> bool: return not self.is_dw_only() and not self.options.HatItems