From 8d28c34f95f2d1cb9f39a0d9546e89c688b6a02f Mon Sep 17 00:00:00 2001 From: Ziktofel Date: Fri, 12 Apr 2024 00:46:15 +0200 Subject: [PATCH] SC2: Fix unused_items refill to respect item dependencies. (#3116) --- worlds/sc2/PoolFilter.py | 56 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/worlds/sc2/PoolFilter.py b/worlds/sc2/PoolFilter.py index d1b58f9b..e94dc4e2 100644 --- a/worlds/sc2/PoolFilter.py +++ b/worlds/sc2/PoolFilter.py @@ -244,8 +244,8 @@ class ValidInventory: def generate_reduced_inventory(self, inventory_size: int, mission_requirements: List[Tuple[str, Callable]]) -> List[Item]: """Attempts to generate a reduced inventory that can fulfill the mission requirements.""" - inventory = list(self.item_pool) - locked_items = list(self.locked_items) + inventory: List[Item] = list(self.item_pool) + locked_items: List[Item] = list(self.locked_items) item_list = get_full_item_list() self.logical_inventory = [ item.name for item in inventory + locked_items + self.existing_items @@ -348,7 +348,7 @@ class ValidInventory: removable_generic_items.append(item) # Main cull process - unused_items = [] # Reusable items for the second pass + unused_items: List[str] = [] # Reusable items for the second pass while len(inventory) + len(locked_items) > inventory_size: if len(inventory) == 0: # There are more items than locations and all of them are already locked due to YAML or logic. @@ -396,18 +396,35 @@ class ValidInventory: if attempt_removal(item): unused_items.append(item.name) + pool_items: List[str] = [item.name for item in (inventory + locked_items + self.existing_items)] + unused_items = [ + unused_item for unused_item in unused_items + if item_list[unused_item].parent_item is None + or item_list[unused_item].parent_item in pool_items + ] + # Removing extra dependencies # WoL logical_inventory_set = set(self.logical_inventory) if not spider_mine_sources & logical_inventory_set: inventory = [item for item in inventory if not item.name.endswith("(Spider Mine)")] + unused_items = [item_name for item_name in unused_items if not item_name.endswith("(Spider Mine)")] if not BARRACKS_UNITS & logical_inventory_set: - inventory = [item for item in inventory if - not (item.name.startswith(ItemNames.TERRAN_INFANTRY_UPGRADE_PREFIX) or item.name == ItemNames.ORBITAL_STRIKE)] + inventory = [ + item for item in inventory + if not (item.name.startswith(ItemNames.TERRAN_INFANTRY_UPGRADE_PREFIX) + or item.name == ItemNames.ORBITAL_STRIKE)] + unused_items = [ + item_name for item_name in unused_items + if not (item_name.startswith( + ItemNames.TERRAN_INFANTRY_UPGRADE_PREFIX) + or item_name == ItemNames.ORBITAL_STRIKE)] if not FACTORY_UNITS & logical_inventory_set: inventory = [item for item in inventory if not item.name.startswith(ItemNames.TERRAN_VEHICLE_UPGRADE_PREFIX)] + unused_items = [item_name for item_name in unused_items if not item_name.startswith(ItemNames.TERRAN_VEHICLE_UPGRADE_PREFIX)] if not STARPORT_UNITS & logical_inventory_set: inventory = [item for item in inventory if not item.name.startswith(ItemNames.TERRAN_SHIP_UPGRADE_PREFIX)] + unused_items = [item_name for item_name in unused_items if not item_name.startswith(ItemNames.TERRAN_SHIP_UPGRADE_PREFIX)] # HotS # Baneling without sources => remove Baneling and upgrades if (ItemNames.ZERGLING_BANELING_ASPECT in self.logical_inventory @@ -416,6 +433,8 @@ class ValidInventory: ): inventory = [item for item in inventory if item.name != ItemNames.ZERGLING_BANELING_ASPECT] inventory = [item for item in inventory if item_list[item.name].parent_item != ItemNames.ZERGLING_BANELING_ASPECT] + unused_items = [item_name for item_name in unused_items if item_name != ItemNames.ZERGLING_BANELING_ASPECT] + unused_items = [item_name for item_name in unused_items if item_list[item_name].parent_item != ItemNames.ZERGLING_BANELING_ASPECT] # Spawn Banelings without Zergling => remove Baneling unit, keep upgrades except macro ones if (ItemNames.ZERGLING_BANELING_ASPECT in self.logical_inventory and ItemNames.ZERGLING not in self.logical_inventory @@ -423,9 +442,12 @@ class ValidInventory: ): inventory = [item for item in inventory if item.name != ItemNames.ZERGLING_BANELING_ASPECT] inventory = [item for item in inventory if item.name != ItemNames.BANELING_RAPID_METAMORPH] + unused_items = [item_name for item_name in unused_items if item_name != ItemNames.ZERGLING_BANELING_ASPECT] + unused_items = [item_name for item_name in unused_items if item_name != ItemNames.BANELING_RAPID_METAMORPH] if not {ItemNames.MUTALISK, ItemNames.CORRUPTOR, ItemNames.SCOURGE} & logical_inventory_set: inventory = [item for item in inventory if not item.name.startswith(ItemNames.ZERG_FLYER_UPGRADE_PREFIX)] locked_items = [item for item in locked_items if not item.name.startswith(ItemNames.ZERG_FLYER_UPGRADE_PREFIX)] + unused_items = [item_name for item_name in unused_items if not item_name.startswith(ItemNames.ZERG_FLYER_UPGRADE_PREFIX)] # T3 items removal rules - remove morph and its upgrades if the basic unit isn't in if not {ItemNames.MUTALISK, ItemNames.CORRUPTOR} & logical_inventory_set: inventory = [item for item in inventory if not item.name.endswith("(Mutalisk/Corruptor)")] @@ -433,45 +455,69 @@ class ValidInventory: inventory = [item for item in inventory if item_list[item.name].parent_item != ItemNames.MUTALISK_CORRUPTOR_DEVOURER_ASPECT] inventory = [item for item in inventory if item_list[item.name].parent_item != ItemNames.MUTALISK_CORRUPTOR_BROOD_LORD_ASPECT] inventory = [item for item in inventory if item_list[item.name].parent_item != ItemNames.MUTALISK_CORRUPTOR_VIPER_ASPECT] + unused_items = [item_name for item_name in unused_items if not item_name.endswith("(Mutalisk/Corruptor)")] + unused_items = [item_name for item_name in unused_items if item_list[item_name].parent_item != ItemNames.MUTALISK_CORRUPTOR_GUARDIAN_ASPECT] + unused_items = [item_name for item_name in unused_items if item_list[item_name].parent_item != ItemNames.MUTALISK_CORRUPTOR_DEVOURER_ASPECT] + unused_items = [item_name for item_name in unused_items if item_list[item_name].parent_item != ItemNames.MUTALISK_CORRUPTOR_BROOD_LORD_ASPECT] + unused_items = [item_name for item_name in unused_items if item_list[item_name].parent_item != ItemNames.MUTALISK_CORRUPTOR_VIPER_ASPECT] if ItemNames.ROACH not in logical_inventory_set: inventory = [item for item in inventory if item.name != ItemNames.ROACH_RAVAGER_ASPECT] inventory = [item for item in inventory if item_list[item.name].parent_item != ItemNames.ROACH_RAVAGER_ASPECT] + unused_items = [item_name for item_name in unused_items if item_name != ItemNames.ROACH_RAVAGER_ASPECT] + unused_items = [item_name for item_name in unused_items if item_list[item_name].parent_item != ItemNames.ROACH_RAVAGER_ASPECT] if ItemNames.HYDRALISK not in logical_inventory_set: inventory = [item for item in inventory if not item.name.endswith("(Hydralisk)")] inventory = [item for item in inventory if item_list[item.name].parent_item != ItemNames.HYDRALISK_LURKER_ASPECT] inventory = [item for item in inventory if item_list[item.name].parent_item != ItemNames.HYDRALISK_IMPALER_ASPECT] + unused_items = [item_name for item_name in unused_items if not item_name.endswith("(Hydralisk)")] + unused_items = [item_name for item_name in unused_items if item_list[item_name].parent_item != ItemNames.HYDRALISK_LURKER_ASPECT] + unused_items = [item_name for item_name in unused_items if item_list[item_name].parent_item != ItemNames.HYDRALISK_IMPALER_ASPECT] # LotV # Shared unit upgrades between several units if not {ItemNames.STALKER, ItemNames.INSTIGATOR, ItemNames.SLAYER} & logical_inventory_set: inventory = [item for item in inventory if not item.name.endswith("(Stalker/Instigator/Slayer)")] + unused_items = [item_name for item_name in unused_items if not item_name.endswith("(Stalker/Instigator/Slayer)")] if not {ItemNames.PHOENIX, ItemNames.MIRAGE} & logical_inventory_set: inventory = [item for item in inventory if not item.name.endswith("(Phoenix/Mirage)")] + unused_items = [item_name for item_name in unused_items if not item_name.endswith("(Phoenix/Mirage)")] if not {ItemNames.VOID_RAY, ItemNames.DESTROYER} & logical_inventory_set: inventory = [item for item in inventory if not item.name.endswith("(Void Ray/Destroyer)")] + unused_items = [item_name for item_name in unused_items if not item_name.endswith("(Void Ray/Destroyer)")] if not {ItemNames.IMMORTAL, ItemNames.ANNIHILATOR} & logical_inventory_set: inventory = [item for item in inventory if not item.name.endswith("(Immortal/Annihilator)")] + unused_items = [item_name for item_name in unused_items if not item_name.endswith("(Immortal/Annihilator)")] if not {ItemNames.DARK_TEMPLAR, ItemNames.AVENGER, ItemNames.BLOOD_HUNTER} & logical_inventory_set: inventory = [item for item in inventory if not item.name.endswith("(Dark Templar/Avenger/Blood Hunter)")] + unused_items = [item_name for item_name in unused_items if not item_name.endswith("(Dark Templar/Avenger/Blood Hunter)")] if not {ItemNames.HIGH_TEMPLAR, ItemNames.SIGNIFIER, ItemNames.ASCENDANT, ItemNames.DARK_TEMPLAR} & logical_inventory_set: inventory = [item for item in inventory if not item.name.endswith("(Archon)")] + unused_items = [item_name for item_name in unused_items if not item_name.endswith("(Archon)")] logical_inventory_set.difference_update([item_name for item_name in logical_inventory_set if item_name.endswith("(Archon)")]) if not {ItemNames.HIGH_TEMPLAR, ItemNames.SIGNIFIER, ItemNames.ARCHON_HIGH_ARCHON} & logical_inventory_set: inventory = [item for item in inventory if not item.name.endswith("(High Templar/Signifier)")] + unused_items = [item_name for item_name in unused_items if not item_name.endswith("(High Templar/Signifier)")] if ItemNames.SUPPLICANT not in logical_inventory_set: inventory = [item for item in inventory if item.name != ItemNames.ASCENDANT_POWER_OVERWHELMING] + unused_items = [item_name for item_name in unused_items if item_name != ItemNames.ASCENDANT_POWER_OVERWHELMING] if not {ItemNames.DARK_ARCHON, ItemNames.DARK_TEMPLAR_DARK_ARCHON_MELD} & logical_inventory_set: inventory = [item for item in inventory if not item.name.endswith("(Dark Archon)")] + unused_items = [item_name for item_name in unused_items if not item_name.endswith("(Dark Archon)")] if not {ItemNames.SENTRY, ItemNames.ENERGIZER, ItemNames.HAVOC} & logical_inventory_set: inventory = [item for item in inventory if not item.name.endswith("(Sentry/Energizer/Havoc)")] + unused_items = [item_name for item_name in unused_items if not item_name.endswith("(Sentry/Energizer/Havoc)")] if not {ItemNames.SENTRY, ItemNames.ENERGIZER, ItemNames.HAVOC, ItemNames.SHIELD_BATTERY} & logical_inventory_set: inventory = [item for item in inventory if not item.name.endswith("(Sentry/Energizer/Havoc/Shield Battery)")] + unused_items = [item_name for item_name in unused_items if not item_name.endswith("(Sentry/Energizer/Havoc/Shield Battery)")] if not {ItemNames.ZEALOT, ItemNames.CENTURION, ItemNames.SENTINEL} & logical_inventory_set: inventory = [item for item in inventory if not item.name.endswith("(Zealot/Sentinel/Centurion)")] + unused_items = [item_name for item_name in unused_items if not item_name.endswith("(Zealot/Sentinel/Centurion)")] # Static defense upgrades only if static defense present if not {ItemNames.PHOTON_CANNON, ItemNames.KHAYDARIN_MONOLITH, ItemNames.NEXUS_OVERCHARGE, ItemNames.SHIELD_BATTERY} & logical_inventory_set: inventory = [item for item in inventory if item.name != ItemNames.ENHANCED_TARGETING] + unused_items = [item_name for item_name in unused_items if item_name != ItemNames.ENHANCED_TARGETING] if not {ItemNames.PHOTON_CANNON, ItemNames.KHAYDARIN_MONOLITH, ItemNames.NEXUS_OVERCHARGE} & logical_inventory_set: inventory = [item for item in inventory if item.name != ItemNames.OPTIMIZED_ORDNANCE] + unused_items = [item_name for item_name in unused_items if item_name != ItemNames.OPTIMIZED_ORDNANCE] # Cull finished, adding locked items back into inventory inventory += locked_items