From 3f0c3ed810eb9451a76cf29c18e665c51428c1bf Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 2 Dec 2025 09:46:09 -0700 Subject: [PATCH] fix: perf optimization fix: playthrough calc and drop rules refactor: using compass_mode for map info --- BaseClasses.py | 4 ++-- Fill.py | 2 ++ RELEASENOTES.md | 4 +++- Rom.py | 2 +- Rules.py | 11 ++++++----- data/base2current.bps | Bin 118335 -> 118393 bytes 6 files changed, 14 insertions(+), 9 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 3b06f10b..3d7e80b5 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -2279,7 +2279,7 @@ class Location(object): self.staleness_count = 0 self.locked = False self.real = not crystal - self.always_allow = lambda item, state: False + self.always_allow = None self.access_rule = lambda state: True self.verbose_rule = None self.item_rule = lambda item: True @@ -2293,7 +2293,7 @@ class Location(object): def can_fill(self, state, item, check_access=True): if not self.valid_multiworld(state, item): return False - return self.always_allow(state, item) or (self.parent_region.can_fill(item) and self.item_rule(item) and (not check_access or self.can_reach(state))) + return (self.always_allow and self.always_allow(state, item)) or (self.parent_region.can_fill(item) and self.item_rule(item) and (not check_access or self.can_reach(state))) def valid_multiworld(self, state, item): if self.type == LocationType.Pot and self.player != item.player: diff --git a/Fill.py b/Fill.py index c6189460..70864fce 100644 --- a/Fill.py +++ b/Fill.py @@ -137,6 +137,8 @@ def fill_restrictive(world, base_state, locations, itempool, key_pool=None, sing spot_to_fill = None item_locations = filter_locations(item_to_place, locations, world, vanilla) + if is_dungeon_item(item_to_place, world): + item_locations = [l for l in item_locations if valid_dungeon_placement(item_to_place, l, world)] verify(item_to_place, item_locations, maximum_exploration_state, single_player_placement, perform_access_check, key_pool, world) for location in item_locations: diff --git a/RELEASENOTES.md b/RELEASENOTES.md index cdcadb7b..a5d2e8a1 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,9 +2,10 @@ * 1.5.0 * Logic: Fixed vanilla key logic for GT basement + * Logic (Playthrough): Fixed an issue where enemy kill rules were not applied during playthrough calculation. (Thanks Catobat for the catch) * Enemy Drop: Added "spies" and shadows for hidden enemies when enemy drop shuffled is enabled * Keysanity/Keydrop Menu for DR: - * Map is no longer required to see key counts for dungeons if not shuffled. This information is available right away in the menu. + * Map key information is now controlled by the Dungeon Chest Counts setting. If set to always on, this information will be available right away in the menu. And will be on the HUD even when the map is not obtained. * The key counter on the HUD for the current dungeon now accounts for keys from enemies or pots that are from vanilla key locations. * The first number on the HUD represents all keys collected either in that dungeon or elsewhere. * The second number on the HUD is the total keys that can be collected either in that dungeon or elsewhere. @@ -13,3 +14,4 @@ * The second number is how many keys left to find in chests (not those from pots/enemies unless those item pools are enabled) * Customizer: free_lamp_cone option added. The logic will account for this, and place the lamp without regard to dark rooms. * Customizer: force_enemy option added that makes all enemies the specified type if possible. There are known gfx glitches in the overworld. + * Optimization: Improved generation performance (Thanks Catobat!) diff --git a/Rom.py b/Rom.py index e578b626..c810534e 100644 --- a/Rom.py +++ b/Rom.py @@ -42,7 +42,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '53a99b36f47fcb81c372d03e3559c590' +RANDOMIZERBASEHASH = '76d6a1915f52950e1b4b6d0fba95296b' class JsonRom(object): diff --git a/Rules.py b/Rules.py index 19dc9922..a1ebe18a 100644 --- a/Rules.py +++ b/Rules.py @@ -996,13 +996,14 @@ def drop_rules(world, player): for super_tile, enemy_list in data_tables.uw_enemy_table.room_map.items(): for enemy in enemy_list: if enemy.location: - rule = defeat_rule_single(world, player, enemy, enemy.location.parent_region) - if enemy.location.parent_region.name in special_rules_check: - rule = special_rules_for_region(world, player, enemy.location.parent_region.name, - enemy.location, rule) + true_location = world.get_location(enemy.location.name, player) + rule = defeat_rule_single(world, player, enemy, true_location.parent_region) + if true_location.parent_region.name in special_rules_check: + rule = special_rules_for_region(world, player, true_location.parent_region.name, + true_location, rule) if rule.rule_lambda is None: raise Exception(f'Bad rule for enemy drop. Need to inspect this case: {hex(enemy.kind)}') - add_rule_new(enemy.location, rule) + add_rule_new(true_location, rule) def ow_inverted_rules(world, player): diff --git a/data/base2current.bps b/data/base2current.bps index 10648e5996a60f78b6841926803221e6af09f7d3..7528406538289ca931deb536e97b8a6718bebb73 100644 GIT binary patch delta 9245 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