From 3f3f303eeddf1761cea158234fc9579230f99745 Mon Sep 17 00:00:00 2001 From: nicopop <6759630+nicopop@users.noreply.github.com> Date: Tue, 14 Oct 2025 13:54:18 -0400 Subject: [PATCH 1/3] add remove_specific_item --- src/Helpers.py | 14 ++++++++++++++ src/__init__.py | 8 ++++---- src/hooks/World.py | 6 +++--- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/Helpers.py b/src/Helpers.py index 4d037e38..f48b736c 100644 --- a/src/Helpers.py +++ b/src/Helpers.py @@ -211,6 +211,20 @@ def format_to_valid_identifier(input: str) -> str: input = "_" + input return input.replace(" ", "_") +def remove_specific_item(source: list[Item], item: Item) -> Item: + """Remove and return an item from a list in a more precise way, base AP only check for name and player id before removing. + \nThis also verify that the classification is the same. + \nRaise ValueError if the item is not in the list.""" + element = None + for i in range(len(source)): # check all elements of the list like a normal remove does + other = source[i] + if item == other and item.classification == other.classification: + element = source.pop(i) # remove element by index to be sure that it is what we need + break + if element is None: + raise ValueError(f"Item {item.name} could not be found in source list") + return element + class ProgItemsCat(IntEnum): VALUE = 1 CATEGORY = 2 diff --git a/src/__init__.py b/src/__init__.py index 1961bcbf..69dd9d13 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -20,7 +20,7 @@ from .Items import ManualItem from .Rules import set_rules from .Options import manual_options_data -from .Helpers import is_item_enabled, get_option_value, get_items_for_player, resolve_yaml_option, format_state_prog_items_key, ProgItemsCat +from .Helpers import is_item_enabled, get_option_value, remove_specific_item, resolve_yaml_option, format_state_prog_items_key, ProgItemsCat from BaseClasses import CollectionState, ItemClassification, Item from Options import PerGameCommonOptions @@ -245,7 +245,7 @@ def stringCheck(string: str) -> ItemClassification: for starting_item in items: items_started.append(starting_item) self.multiworld.push_precollected(starting_item) - pool.remove(starting_item) + remove_specific_item(pool, starting_item) self.start_inventory = {i.name: items_started.count(i) for i in items_started} @@ -379,7 +379,7 @@ def generate_basic(self): location.place_locked_item(item_to_place) # remove the item we're about to place from the pool so it isn't placed twice - self.multiworld.itempool.remove(item_to_place) + remove_specific_item(self.multiworld.itempool, item_to_place) after_generate_basic(self, self.multiworld, self.player) @@ -490,7 +490,7 @@ def adjust_filler_items(self, item_pool, traps): else: logging.warning("Could not remove enough non-progression items from the pool.") break - item_pool.remove(popped) + remove_specific_item(item_pool, popped) return item_pool diff --git a/src/hooks/World.py b/src/hooks/World.py index fe2e82e9..62db224a 100644 --- a/src/hooks/World.py +++ b/src/hooks/World.py @@ -12,7 +12,7 @@ from ..Data import game_table, item_table, location_table, region_table # These helper methods allow you to determine if an option has been set, or what its value is, for any player in the multiworld -from ..Helpers import is_option_enabled, get_option_value, format_state_prog_items_key, ProgItemsCat +from ..Helpers import is_option_enabled, get_option_value, format_state_prog_items_key, ProgItemsCat, remove_specific_item # calling logging.info("message") anywhere below in this file will output the message to both console and log file import logging @@ -80,7 +80,7 @@ def before_create_items_filler(item_pool: list, world: World, multiworld: MultiW for itemName in itemNamesToRemove: item = next(i for i in item_pool if i.name == itemName) - item_pool.remove(item) + remove_specific_item(item_pool, item) return item_pool @@ -90,7 +90,7 @@ def before_create_items_filler(item_pool: list, world: World, multiworld: MultiW # location = next(l for l in multiworld.get_unfilled_locations(player=player) if l.name == "Location Name") # item_to_place = next(i for i in item_pool if i.name == "Item Name") # location.place_locked_item(item_to_place) - # item_pool.remove(item_to_place) + # remove_specific_item(item_pool, item_to_place) # The complete item pool prior to being set for generation is provided here, in case you want to make changes to it def after_create_items(item_pool: list, world: World, multiworld: MultiWorld, player: int) -> list: From a2a6cdd586b0c828e7ca45aae0529478c7faaa54 Mon Sep 17 00:00:00 2001 From: nicopop <6759630+nicopop@users.noreply.github.com> Date: Tue, 14 Oct 2025 15:12:32 -0400 Subject: [PATCH 2/3] added source --- src/Helpers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Helpers.py b/src/Helpers.py index f48b736c..13c06c46 100644 --- a/src/Helpers.py +++ b/src/Helpers.py @@ -215,6 +215,7 @@ def remove_specific_item(source: list[Item], item: Item) -> Item: """Remove and return an item from a list in a more precise way, base AP only check for name and player id before removing. \nThis also verify that the classification is the same. \nRaise ValueError if the item is not in the list.""" + # Inspired by https://stackoverflow.com/a/58761459 element = None for i in range(len(source)): # check all elements of the list like a normal remove does other = source[i] From 0f3e7d011223e583a1ab4fc3b8e593f81e2bb7a1 Mon Sep 17 00:00:00 2001 From: nicopop <6759630+nicopop@users.noreply.github.com> Date: Wed, 15 Oct 2025 13:09:53 -0400 Subject: [PATCH 3/3] make remove_specific_item use 'is' instead of additional checks --- src/Helpers.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Helpers.py b/src/Helpers.py index 13c06c46..316aa43c 100644 --- a/src/Helpers.py +++ b/src/Helpers.py @@ -213,18 +213,15 @@ def format_to_valid_identifier(input: str) -> str: def remove_specific_item(source: list[Item], item: Item) -> Item: """Remove and return an item from a list in a more precise way, base AP only check for name and player id before removing. - \nThis also verify that the classification is the same. + \nThis checks that the item IS the exact same in the list. \nRaise ValueError if the item is not in the list.""" # Inspired by https://stackoverflow.com/a/58761459 - element = None for i in range(len(source)): # check all elements of the list like a normal remove does - other = source[i] - if item == other and item.classification == other.classification: - element = source.pop(i) # remove element by index to be sure that it is what we need - break - if element is None: - raise ValueError(f"Item {item.name} could not be found in source list") - return element + if item is source[i]: + return source.pop(i) + + # if we reach here we didn't get any item + raise ValueError(f"Item '{item.name}' could not be found in source list") class ProgItemsCat(IntEnum): VALUE = 1