Zillion: validate rescue item links (#1140)
This commit is contained in:
parent
53974d568b
commit
f298b8d6e7
16
Main.py
16
Main.py
|
@ -8,9 +8,9 @@ import concurrent.futures
|
|||
import pickle
|
||||
import tempfile
|
||||
import zipfile
|
||||
from typing import Dict, Tuple, Optional, Set
|
||||
from typing import Dict, List, Tuple, Optional, Set
|
||||
|
||||
from BaseClasses import MultiWorld, CollectionState, Region, RegionType, LocationProgressType, Location
|
||||
from BaseClasses import Item, MultiWorld, CollectionState, Region, RegionType, LocationProgressType, Location
|
||||
from worlds.alttp.Items import item_name_groups
|
||||
from worlds.alttp.Regions import is_main_entrance
|
||||
from Fill import distribute_items_restrictive, flood_items, balance_multiworld_progression, distribute_planned
|
||||
|
@ -154,8 +154,10 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
|
|||
|
||||
# temporary home for item links, should be moved out of Main
|
||||
for group_id, group in world.groups.items():
|
||||
def find_common_pool(players: Set[int], shared_pool: Set[str]):
|
||||
classifications = collections.defaultdict(int)
|
||||
def find_common_pool(players: Set[int], shared_pool: Set[str]) -> Tuple[
|
||||
Optional[Dict[int, Dict[str, int]]], Optional[Dict[str, int]]
|
||||
]:
|
||||
classifications: Dict[str, int] = collections.defaultdict(int)
|
||||
counters = {player: {name: 0 for name in shared_pool} for player in players}
|
||||
for item in world.itempool:
|
||||
if item.player in counters and item.name in shared_pool:
|
||||
|
@ -165,7 +167,7 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
|
|||
for player in players.copy():
|
||||
if all([counters[player][item] == 0 for item in shared_pool]):
|
||||
players.remove(player)
|
||||
del(counters[player])
|
||||
del (counters[player])
|
||||
|
||||
if not players:
|
||||
return None, None
|
||||
|
@ -177,14 +179,14 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
|
|||
counters[player][item] = count
|
||||
else:
|
||||
for player in players:
|
||||
del(counters[player][item])
|
||||
del (counters[player][item])
|
||||
return counters, classifications
|
||||
|
||||
common_item_count, classifications = find_common_pool(group["players"], group["item_pool"])
|
||||
if not common_item_count:
|
||||
continue
|
||||
|
||||
new_itempool = []
|
||||
new_itempool: List[Item] = []
|
||||
for item_name, item_count in next(iter(common_item_count.values())).items():
|
||||
for _ in range(item_count):
|
||||
new_item = group["world"].create_item(item_name)
|
||||
|
|
|
@ -90,6 +90,8 @@ def call_all(world: "MultiWorld", method_name: str, *args: Any) -> None:
|
|||
f"Duplicate item reference of \"{item.name}\" in \"{world.worlds[player].game}\" "
|
||||
f"of player \"{world.player_name[player]}\". Please make a copy instead.")
|
||||
|
||||
# TODO: investigate: Iterating through a set is not a deterministic order.
|
||||
# If any random is used, this could make unreproducible seed.
|
||||
for world_type in world_types:
|
||||
stage_callable = getattr(world_type, f"stage_{method_name}", None)
|
||||
if stage_callable:
|
||||
|
|
|
@ -11,7 +11,7 @@ from BaseClasses import ItemClassification, LocationProgressType, \
|
|||
from Options import AssembleOptions
|
||||
from .logic import cs_to_zz_locs
|
||||
from .region import ZillionLocation, ZillionRegion
|
||||
from .options import zillion_options, validate
|
||||
from .options import ZillionStartChar, zillion_options, validate
|
||||
from .id_maps import item_name_to_id as _item_name_to_id, \
|
||||
loc_name_to_id as _loc_name_to_id, make_id_to_others, \
|
||||
zz_reg_name_to_reg_name, base_id
|
||||
|
@ -242,6 +242,42 @@ class ZillionWorld(World):
|
|||
self.world.completion_condition[self.player] = \
|
||||
lambda state: state.has("Win", self.player)
|
||||
|
||||
@staticmethod
|
||||
def stage_generate_basic(multiworld: MultiWorld, *args: Any) -> None:
|
||||
# item link pools are about to be created in main
|
||||
# JJ can't be an item link unless all the players share the same start_char
|
||||
# (The reason for this is that the JJ ZillionItem will have a different ZzItem depending
|
||||
# on whether the start char is Apple or Champ, and the logic depends on that ZzItem.)
|
||||
for group in multiworld.groups.values():
|
||||
# TODO: remove asserts on group when we can specify which members of TypedDict are optional
|
||||
assert "game" in group
|
||||
if group["game"] == "Zillion":
|
||||
assert "item_pool" in group
|
||||
item_pool = group["item_pool"]
|
||||
to_stay = "JJ"
|
||||
if "JJ" in item_pool:
|
||||
assert "players" in group
|
||||
group_players = group["players"]
|
||||
start_chars = cast(Dict[int, ZillionStartChar], getattr(multiworld, "start_char"))
|
||||
players_start_chars = [
|
||||
(player, start_chars[player].get_current_option_name())
|
||||
for player in group_players
|
||||
]
|
||||
start_char_counts = Counter(sc for _, sc in players_start_chars)
|
||||
# majority rules
|
||||
if start_char_counts["Apple"] > start_char_counts["Champ"]:
|
||||
to_stay = "Apple"
|
||||
elif start_char_counts["Champ"] > start_char_counts["Apple"]:
|
||||
to_stay = "Champ"
|
||||
else: # equal
|
||||
to_stay = multiworld.random.choice(("Apple", "Champ"))
|
||||
|
||||
for p, sc in players_start_chars:
|
||||
if sc != to_stay:
|
||||
group_players.remove(p)
|
||||
assert "world" in group
|
||||
cast(ZillionWorld, group["world"])._make_item_maps(to_stay)
|
||||
|
||||
def post_fill(self) -> None:
|
||||
"""Optional Method that is called after regular fill. Can be used to do adjustments before output generation.
|
||||
This happens before progression balancing, so the items may not be in their final locations yet."""
|
||||
|
|
Loading…
Reference in New Issue