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:
|
def call_all(world: "MultiWorld", method_name: str, *args: Any) -> None:
|
||||||
world_types: Set[AutoWorldRegister] = set()
|
world_types: Set[AutoWorldRegister] = set()
|
||||||
for player in world.player_ids:
|
for player in world.player_ids:
|
||||||
|
prev_item_count = len(world.itempool)
|
||||||
world_types.add(world.worlds[player].__class__)
|
world_types.add(world.worlds[player].__class__)
|
||||||
call_single(world, method_name, player, *args)
|
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:
|
for world_type in world_types:
|
||||||
stage_callable = getattr(world_type, f"stage_{method_name}", None)
|
stage_callable = getattr(world_type, f"stage_{method_name}", None)
|
||||||
|
|
|
@ -65,10 +65,6 @@ class DKC3World(World):
|
||||||
"active_levels": self.active_level_list,
|
"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:
|
def fill_slot_data(self) -> dict:
|
||||||
slot_data = self._get_slot_data()
|
slot_data = self._get_slot_data()
|
||||||
for option_name in dkc3_options:
|
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 = (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)
|
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.bonus_coin) for _ in range(number_of_bonus_coins)]
|
||||||
itempool += [self.create_item(ItemName.dk_coin)] * 41
|
itempool += [self.create_item(ItemName.dk_coin) for _ in range(41)]
|
||||||
itempool += [self.create_item(ItemName.banana_bird)] * number_of_banana_birds
|
itempool += [self.create_item(ItemName.banana_bird) for _ in range(number_of_banana_birds)]
|
||||||
itempool += [self.create_item(ItemName.krematoa_cog)] * number_of_cogs
|
itempool += [self.create_item(ItemName.krematoa_cog) for _ in range(number_of_cogs)]
|
||||||
itempool += [self.create_item(ItemName.progressive_boat)] * 3
|
itempool += [self.create_item(ItemName.progressive_boat) for _ in range(3)]
|
||||||
|
|
||||||
total_junk_count = total_required_locations - len(itempool)
|
total_junk_count = total_required_locations - len(itempool)
|
||||||
|
|
||||||
junk_pool = []
|
junk_pool = []
|
||||||
for item_name in self.world.random.choices(list(junk_table.keys()), k=total_junk_count):
|
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
|
itempool += junk_pool
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ class OriBlindForest(World):
|
||||||
|
|
||||||
def generate_basic(self):
|
def generate_basic(self):
|
||||||
for item_name, count in default_pool.items():
|
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:
|
def create_item(self, name: str) -> Item:
|
||||||
return Item(name,
|
return Item(name,
|
||||||
|
|
|
@ -66,7 +66,7 @@ class LegacyWorld(World):
|
||||||
|
|
||||||
def _create_items(self, name: str):
|
def _create_items(self, name: str):
|
||||||
data = item_table[name]
|
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:
|
def fill_slot_data(self) -> dict:
|
||||||
slot_data = self._get_slot_data()
|
slot_data = self._get_slot_data()
|
||||||
|
@ -89,20 +89,20 @@ class LegacyWorld(World):
|
||||||
|
|
||||||
# Blueprints
|
# Blueprints
|
||||||
if self.world.progressive_blueprints[self.player]:
|
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:
|
else:
|
||||||
for item in blueprints_table:
|
for item in blueprints_table:
|
||||||
itempool += self._create_items(item)
|
itempool += self._create_items(item)
|
||||||
|
|
||||||
# Check Pool settings to add a certain amount of these items.
|
# 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.health) for _ in range(self.world.health_pool[self.player])]
|
||||||
itempool += [self.create_item(ItemName.mana)] * int(self.world.mana_pool[self.player])
|
itempool += [self.create_item(ItemName.mana) for _ in range(self.world.mana_pool[self.player])]
|
||||||
itempool += [self.create_item(ItemName.attack)] * int(self.world.attack_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)] * int(self.world.magic_damage_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)] * int(self.world.armor_pool[self.player])
|
itempool += [self.create_item(ItemName.armor) for _ in range(self.world.armor_pool[self.player])]
|
||||||
itempool += [self.create_item(ItemName.equip)] * int(self.world.equip_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)] * int(self.world.crit_chance_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)] * int(self.world.crit_damage_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]
|
classes = self.world.available_classes[self.player]
|
||||||
if "Dragon" in classes:
|
if "Dragon" in classes:
|
||||||
|
@ -153,12 +153,12 @@ class LegacyWorld(World):
|
||||||
if self.world.architect[self.player] == "start_unlocked":
|
if self.world.architect[self.player] == "start_unlocked":
|
||||||
self.world.push_precollected(self.world.create_item(ItemName.architect, self.player))
|
self.world.push_precollected(self.world.create_item(ItemName.architect, self.player))
|
||||||
elif self.world.architect[self.player] != "disabled":
|
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
|
# Fill item pool with the remaining
|
||||||
for _ in range(len(itempool), total_required_locations):
|
for _ in range(len(itempool), total_required_locations):
|
||||||
item = self.world.random.choice(list(misc_items_table.keys()))
|
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
|
self.world.itempool += itempool
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ class SA2BWorld(World):
|
||||||
|
|
||||||
def _create_items(self, name: str):
|
def _create_items(self, name: str):
|
||||||
data = item_table[name]
|
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:
|
def fill_slot_data(self) -> dict:
|
||||||
slot_data = self._get_slot_data()
|
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)
|
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)
|
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)
|
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))
|
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
|
# Carve Traps out of junk_count
|
||||||
trap_weights = []
|
trap_weights = []
|
||||||
|
@ -219,14 +219,14 @@ class SA2BWorld(World):
|
||||||
junk_keys = list(junk_table.keys())
|
junk_keys = list(junk_table.keys())
|
||||||
for i in range(junk_count):
|
for i in range(junk_count):
|
||||||
junk_item = self.world.random.choice(junk_keys)
|
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
|
itempool += junk_pool
|
||||||
|
|
||||||
trap_pool = []
|
trap_pool = []
|
||||||
for i in range(trap_count):
|
for i in range(trap_count):
|
||||||
trap_item = self.world.random.choice(trap_weights)
|
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 += trap_pool
|
||||||
|
|
||||||
|
|
|
@ -65,10 +65,6 @@ class SMWWorld(World):
|
||||||
"active_levels": self.active_level_dict,
|
"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:
|
def fill_slot_data(self) -> dict:
|
||||||
slot_data = self._get_slot_data()
|
slot_data = self._get_slot_data()
|
||||||
for option_name in smw_options:
|
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_switch)]
|
||||||
itempool += [self.create_item(ItemName.p_balloon)]
|
itempool += [self.create_item(ItemName.p_balloon)]
|
||||||
itempool += [self.create_item(ItemName.super_star_active)]
|
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.yellow_switch_palace)]
|
||||||
itempool += [self.create_item(ItemName.green_switch_palace)]
|
itempool += [self.create_item(ItemName.green_switch_palace)]
|
||||||
itempool += [self.create_item(ItemName.red_switch_palace)]
|
itempool += [self.create_item(ItemName.red_switch_palace)]
|
||||||
itempool += [self.create_item(ItemName.blue_switch_palace)]
|
itempool += [self.create_item(ItemName.blue_switch_palace)]
|
||||||
|
|
||||||
if self.world.goal[self.player] == "yoshi_egg_hunt":
|
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))
|
self.world.get_location(LocationName.yoshis_house, self.player).place_locked_item(self.create_item(ItemName.victory))
|
||||||
else:
|
else:
|
||||||
self.world.get_location(LocationName.bowser, self.player).place_locked_item(self.create_item(ItemName.victory))
|
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 = []
|
trap_pool = []
|
||||||
for i in range(trap_count):
|
for i in range(trap_count):
|
||||||
trap_item = self.world.random.choice(trap_weights)
|
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 += 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,
|
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,
|
LocationName.twin_bridges_koopaling, LocationName.forest_koopaling, LocationName.chocolate_koopaling,
|
||||||
|
|
|
@ -117,9 +117,9 @@ class WitnessWorld(World):
|
||||||
pool.remove(items_by_name[item])
|
pool.remove(items_by_name[item])
|
||||||
|
|
||||||
for item in self.items.EXTRA_AMOUNTS:
|
for item in self.items.EXTRA_AMOUNTS:
|
||||||
witness_item = self.create_item(item)
|
|
||||||
for i in range(0, self.items.EXTRA_AMOUNTS[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:
|
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)
|
pool.append(witness_item)
|
||||||
|
|
||||||
# Put in junk items to fill the rest
|
# Put in junk items to fill the rest
|
||||||
|
|
Loading…
Reference in New Issue