Core: assert that items have a single reference (#1075)
* Core: assert that items have a single reference * Fix duplicate item reference in The Witness * Ori: fix duplicate item references * DKC3: fix duplicate item references * RL: fix duplicate item references * SA2B: fix duplicate item references * SMW: fix duplicate item references Co-authored-by: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com>
This commit is contained in:
parent
40b7e78178
commit
ed76c13961
|
@ -79,8 +79,16 @@ def call_single(world: "MultiWorld", method_name: str, player: int, *args: Any)
|
|||
def call_all(world: "MultiWorld", method_name: str, *args: Any) -> None:
|
||||
world_types: Set[AutoWorldRegister] = set()
|
||||
for player in world.player_ids:
|
||||
prev_item_count = len(world.itempool)
|
||||
world_types.add(world.worlds[player].__class__)
|
||||
call_single(world, method_name, player, *args)
|
||||
if __debug__:
|
||||
new_items = world.itempool[prev_item_count:]
|
||||
for i, item in enumerate(new_items):
|
||||
for other in new_items[i+1:]:
|
||||
assert item is not other, (
|
||||
f"Duplicate item reference of \"{item.name}\" in \"{world.worlds[player].game}\" "
|
||||
f"of player \"{world.player_name[player]}\". Please make a copy instead.")
|
||||
|
||||
for world_type in world_types:
|
||||
stage_callable = getattr(world_type, f"stage_{method_name}", None)
|
||||
|
|
|
@ -65,10 +65,6 @@ class DKC3World(World):
|
|||
"active_levels": self.active_level_list,
|
||||
}
|
||||
|
||||
def _create_items(self, name: str):
|
||||
data = item_table[name]
|
||||
return [self.create_item(name)] * data.quantity
|
||||
|
||||
def fill_slot_data(self) -> dict:
|
||||
slot_data = self._get_slot_data()
|
||||
for option_name in dkc3_options:
|
||||
|
@ -113,17 +109,17 @@ class DKC3World(World):
|
|||
number_of_bonus_coins = (self.world.krematoa_bonus_coin_cost[self.player] * 5)
|
||||
number_of_bonus_coins += math.ceil((85 - number_of_bonus_coins) * self.world.percentage_of_extra_bonus_coins[self.player] / 100)
|
||||
|
||||
itempool += [self.create_item(ItemName.bonus_coin)] * number_of_bonus_coins
|
||||
itempool += [self.create_item(ItemName.dk_coin)] * 41
|
||||
itempool += [self.create_item(ItemName.banana_bird)] * number_of_banana_birds
|
||||
itempool += [self.create_item(ItemName.krematoa_cog)] * number_of_cogs
|
||||
itempool += [self.create_item(ItemName.progressive_boat)] * 3
|
||||
itempool += [self.create_item(ItemName.bonus_coin) for _ in range(number_of_bonus_coins)]
|
||||
itempool += [self.create_item(ItemName.dk_coin) for _ in range(41)]
|
||||
itempool += [self.create_item(ItemName.banana_bird) for _ in range(number_of_banana_birds)]
|
||||
itempool += [self.create_item(ItemName.krematoa_cog) for _ in range(number_of_cogs)]
|
||||
itempool += [self.create_item(ItemName.progressive_boat) for _ in range(3)]
|
||||
|
||||
total_junk_count = total_required_locations - len(itempool)
|
||||
|
||||
junk_pool = []
|
||||
for item_name in self.world.random.choices(list(junk_table.keys()), k=total_junk_count):
|
||||
junk_pool += [self.create_item(item_name)]
|
||||
junk_pool.append(self.create_item(item_name))
|
||||
|
||||
itempool += junk_pool
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ class OriBlindForest(World):
|
|||
|
||||
def generate_basic(self):
|
||||
for item_name, count in default_pool.items():
|
||||
self.world.itempool.extend([self.create_item(item_name)] * count)
|
||||
self.world.itempool.extend([self.create_item(item_name) for _ in range(count)])
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
return Item(name,
|
||||
|
|
|
@ -66,7 +66,7 @@ class LegacyWorld(World):
|
|||
|
||||
def _create_items(self, name: str):
|
||||
data = item_table[name]
|
||||
return [self.create_item(name)] * data.quantity
|
||||
return [self.create_item(name) for _ in range(data.quantity)]
|
||||
|
||||
def fill_slot_data(self) -> dict:
|
||||
slot_data = self._get_slot_data()
|
||||
|
@ -89,20 +89,20 @@ class LegacyWorld(World):
|
|||
|
||||
# Blueprints
|
||||
if self.world.progressive_blueprints[self.player]:
|
||||
itempool += [self.create_item(ItemName.progressive_blueprints)] * 15
|
||||
itempool += [self.create_item(ItemName.progressive_blueprints) for _ in range(15)]
|
||||
else:
|
||||
for item in blueprints_table:
|
||||
itempool += self._create_items(item)
|
||||
|
||||
# Check Pool settings to add a certain amount of these items.
|
||||
itempool += [self.create_item(ItemName.health)] * int(self.world.health_pool[self.player])
|
||||
itempool += [self.create_item(ItemName.mana)] * int(self.world.mana_pool[self.player])
|
||||
itempool += [self.create_item(ItemName.attack)] * int(self.world.attack_pool[self.player])
|
||||
itempool += [self.create_item(ItemName.magic_damage)] * int(self.world.magic_damage_pool[self.player])
|
||||
itempool += [self.create_item(ItemName.armor)] * int(self.world.armor_pool[self.player])
|
||||
itempool += [self.create_item(ItemName.equip)] * int(self.world.equip_pool[self.player])
|
||||
itempool += [self.create_item(ItemName.crit_chance)] * int(self.world.crit_chance_pool[self.player])
|
||||
itempool += [self.create_item(ItemName.crit_damage)] * int(self.world.crit_damage_pool[self.player])
|
||||
itempool += [self.create_item(ItemName.health) for _ in range(self.world.health_pool[self.player])]
|
||||
itempool += [self.create_item(ItemName.mana) for _ in range(self.world.mana_pool[self.player])]
|
||||
itempool += [self.create_item(ItemName.attack) for _ in range(self.world.attack_pool[self.player])]
|
||||
itempool += [self.create_item(ItemName.magic_damage) for _ in range(self.world.magic_damage_pool[self.player])]
|
||||
itempool += [self.create_item(ItemName.armor) for _ in range(self.world.armor_pool[self.player])]
|
||||
itempool += [self.create_item(ItemName.equip) for _ in range(self.world.equip_pool[self.player])]
|
||||
itempool += [self.create_item(ItemName.crit_chance) for _ in range(self.world.crit_chance_pool[self.player])]
|
||||
itempool += [self.create_item(ItemName.crit_damage) for _ in range(self.world.crit_damage_pool[self.player])]
|
||||
|
||||
classes = self.world.available_classes[self.player]
|
||||
if "Dragon" in classes:
|
||||
|
@ -153,12 +153,12 @@ class LegacyWorld(World):
|
|||
if self.world.architect[self.player] == "start_unlocked":
|
||||
self.world.push_precollected(self.world.create_item(ItemName.architect, self.player))
|
||||
elif self.world.architect[self.player] != "disabled":
|
||||
itempool += [self.create_item(ItemName.architect)]
|
||||
itempool.append(self.create_item(ItemName.architect))
|
||||
|
||||
# Fill item pool with the remaining
|
||||
for _ in range(len(itempool), total_required_locations):
|
||||
item = self.world.random.choice(list(misc_items_table.keys()))
|
||||
itempool += [self.create_item(item)]
|
||||
itempool.append(self.create_item(item))
|
||||
|
||||
self.world.itempool += itempool
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ class SA2BWorld(World):
|
|||
|
||||
def _create_items(self, name: str):
|
||||
data = item_table[name]
|
||||
return [self.create_item(name)] * data.quantity
|
||||
return [self.create_item(name) for _ in range(data.quantity)]
|
||||
|
||||
def fill_slot_data(self) -> dict:
|
||||
slot_data = self._get_slot_data()
|
||||
|
@ -198,11 +198,11 @@ class SA2BWorld(World):
|
|||
connect_regions(self.world, self.player, gates, self.emblems_for_cannons_core, self.gate_bosses)
|
||||
|
||||
max_required_emblems = max(max(emblem_requirement_list), self.emblems_for_cannons_core)
|
||||
itempool += [self.create_item(ItemName.emblem)] * max_required_emblems
|
||||
itempool += [self.create_item(ItemName.emblem) for _ in range(max_required_emblems)]
|
||||
|
||||
non_required_emblems = (total_emblem_count - max_required_emblems)
|
||||
junk_count = math.floor(non_required_emblems * (self.world.junk_fill_percentage[self.player].value / 100.0))
|
||||
itempool += [self.create_item(ItemName.emblem, True)] * (non_required_emblems - junk_count)
|
||||
itempool += [self.create_item(ItemName.emblem, True) for _ in range(non_required_emblems - junk_count)]
|
||||
|
||||
# Carve Traps out of junk_count
|
||||
trap_weights = []
|
||||
|
@ -219,14 +219,14 @@ class SA2BWorld(World):
|
|||
junk_keys = list(junk_table.keys())
|
||||
for i in range(junk_count):
|
||||
junk_item = self.world.random.choice(junk_keys)
|
||||
junk_pool += [self.create_item(junk_item)]
|
||||
junk_pool.append(self.create_item(junk_item))
|
||||
|
||||
itempool += junk_pool
|
||||
|
||||
trap_pool = []
|
||||
for i in range(trap_count):
|
||||
trap_item = self.world.random.choice(trap_weights)
|
||||
trap_pool += [self.create_item(trap_item)]
|
||||
trap_pool.append(self.create_item(trap_item))
|
||||
|
||||
itempool += trap_pool
|
||||
|
||||
|
|
|
@ -65,10 +65,6 @@ class SMWWorld(World):
|
|||
"active_levels": self.active_level_dict,
|
||||
}
|
||||
|
||||
def _create_items(self, name: str):
|
||||
data = item_table[name]
|
||||
return [self.create_item(name)] * data.quantity
|
||||
|
||||
def fill_slot_data(self) -> dict:
|
||||
slot_data = self._get_slot_data()
|
||||
for option_name in smw_options:
|
||||
|
@ -105,14 +101,15 @@ class SMWWorld(World):
|
|||
itempool += [self.create_item(ItemName.p_switch)]
|
||||
itempool += [self.create_item(ItemName.p_balloon)]
|
||||
itempool += [self.create_item(ItemName.super_star_active)]
|
||||
itempool += [self.create_item(ItemName.progressive_powerup)] * 3
|
||||
itempool += [self.create_item(ItemName.progressive_powerup) for _ in range(3)]
|
||||
itempool += [self.create_item(ItemName.yellow_switch_palace)]
|
||||
itempool += [self.create_item(ItemName.green_switch_palace)]
|
||||
itempool += [self.create_item(ItemName.red_switch_palace)]
|
||||
itempool += [self.create_item(ItemName.blue_switch_palace)]
|
||||
|
||||
if self.world.goal[self.player] == "yoshi_egg_hunt":
|
||||
itempool += [self.create_item(ItemName.yoshi_egg)] * self.world.number_of_yoshi_eggs[self.player]
|
||||
itempool += [self.create_item(ItemName.yoshi_egg)
|
||||
for _ in range(self.world.number_of_yoshi_eggs[self.player])]
|
||||
self.world.get_location(LocationName.yoshis_house, self.player).place_locked_item(self.create_item(ItemName.victory))
|
||||
else:
|
||||
self.world.get_location(LocationName.bowser, self.player).place_locked_item(self.create_item(ItemName.victory))
|
||||
|
@ -128,11 +125,11 @@ class SMWWorld(World):
|
|||
trap_pool = []
|
||||
for i in range(trap_count):
|
||||
trap_item = self.world.random.choice(trap_weights)
|
||||
trap_pool += [self.create_item(trap_item)]
|
||||
trap_pool.append(self.create_item(trap_item))
|
||||
|
||||
itempool += trap_pool
|
||||
|
||||
itempool += [self.create_item(ItemName.one_up_mushroom)] * junk_count
|
||||
itempool += [self.create_item(ItemName.one_up_mushroom) for _ in range(junk_count)]
|
||||
|
||||
boss_location_names = [LocationName.yoshis_island_koopaling, LocationName.donut_plains_koopaling, LocationName.vanilla_dome_koopaling,
|
||||
LocationName.twin_bridges_koopaling, LocationName.forest_koopaling, LocationName.chocolate_koopaling,
|
||||
|
|
|
@ -117,9 +117,9 @@ class WitnessWorld(World):
|
|||
pool.remove(items_by_name[item])
|
||||
|
||||
for item in self.items.EXTRA_AMOUNTS:
|
||||
witness_item = self.create_item(item)
|
||||
for i in range(0, self.items.EXTRA_AMOUNTS[item]):
|
||||
if len(pool) < len(self.locat.CHECK_LOCATION_TABLE) - len(self.locat.EVENT_LOCATION_TABLE) - less_junk:
|
||||
witness_item = self.create_item(item)
|
||||
pool.append(witness_item)
|
||||
|
||||
# Put in junk items to fill the rest
|
||||
|
|
Loading…
Reference in New Issue