ItemLinks: allow linking replacement items as well (#1274)
This commit is contained in:
parent
449973687b
commit
7c3af68e59
|
@ -26,6 +26,7 @@ class Group(TypedDict, total=False):
|
||||||
replacement_items: Dict[int, Optional[str]]
|
replacement_items: Dict[int, Optional[str]]
|
||||||
local_items: Set[str]
|
local_items: Set[str]
|
||||||
non_local_items: Set[str]
|
non_local_items: Set[str]
|
||||||
|
link_replacement: bool
|
||||||
|
|
||||||
|
|
||||||
class MultiWorld():
|
class MultiWorld():
|
||||||
|
@ -222,27 +223,32 @@ class MultiWorld():
|
||||||
|
|
||||||
def set_item_links(self):
|
def set_item_links(self):
|
||||||
item_links = {}
|
item_links = {}
|
||||||
|
replacement_prio = [False, True, None]
|
||||||
for player in self.player_ids:
|
for player in self.player_ids:
|
||||||
for item_link in self.item_links[player].value:
|
for item_link in self.item_links[player].value:
|
||||||
if item_link["name"] in item_links:
|
if item_link["name"] in item_links:
|
||||||
if item_links[item_link["name"]]["game"] != self.game[player]:
|
if item_links[item_link["name"]]["game"] != self.game[player]:
|
||||||
raise Exception(f"Cannot ItemLink across games. Link: {item_link['name']}")
|
raise Exception(f"Cannot ItemLink across games. Link: {item_link['name']}")
|
||||||
item_links[item_link["name"]]["players"][player] = item_link["replacement_item"]
|
current_link = item_links[item_link["name"]]
|
||||||
item_links[item_link["name"]]["item_pool"] &= set(item_link["item_pool"])
|
current_link["players"][player] = item_link["replacement_item"]
|
||||||
item_links[item_link["name"]]["exclude"] |= set(item_link.get("exclude", []))
|
current_link["item_pool"] &= set(item_link["item_pool"])
|
||||||
item_links[item_link["name"]]["local_items"] &= set(item_link.get("local_items", []))
|
current_link["exclude"] |= set(item_link.get("exclude", []))
|
||||||
item_links[item_link["name"]]["non_local_items"] &= set(item_link.get("non_local_items", []))
|
current_link["local_items"] &= set(item_link.get("local_items", []))
|
||||||
|
current_link["non_local_items"] &= set(item_link.get("non_local_items", []))
|
||||||
|
current_link["link_replacement"] = min(current_link["link_replacement"],
|
||||||
|
replacement_prio.index(item_link["link_replacement"]))
|
||||||
else:
|
else:
|
||||||
if item_link["name"] in self.player_name.values():
|
if item_link["name"] in self.player_name.values():
|
||||||
raise Exception(f"Cannot name a ItemLink group the same as a player ({item_link['name']}) ({self.get_player_name(player)}).")
|
raise Exception(f"Cannot name a ItemLink group the same as a player ({item_link['name']}) "
|
||||||
|
f"({self.get_player_name(player)}).")
|
||||||
item_links[item_link["name"]] = {
|
item_links[item_link["name"]] = {
|
||||||
"players": {player: item_link["replacement_item"]},
|
"players": {player: item_link["replacement_item"]},
|
||||||
"item_pool": set(item_link["item_pool"]),
|
"item_pool": set(item_link["item_pool"]),
|
||||||
"exclude": set(item_link.get("exclude", [])),
|
"exclude": set(item_link.get("exclude", [])),
|
||||||
"game": self.game[player],
|
"game": self.game[player],
|
||||||
"local_items": set(item_link.get("local_items", [])),
|
"local_items": set(item_link.get("local_items", [])),
|
||||||
"non_local_items": set(item_link.get("non_local_items", []))
|
"non_local_items": set(item_link.get("non_local_items", [])),
|
||||||
|
"link_replacement": replacement_prio.index(item_link["link_replacement"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, item_link in item_links.items():
|
for name, item_link in item_links.items():
|
||||||
|
@ -267,10 +273,12 @@ class MultiWorld():
|
||||||
for group_name, item_link in item_links.items():
|
for group_name, item_link in item_links.items():
|
||||||
game = item_link["game"]
|
game = item_link["game"]
|
||||||
group_id, group = self.add_group(group_name, game, set(item_link["players"]))
|
group_id, group = self.add_group(group_name, game, set(item_link["players"]))
|
||||||
|
|
||||||
group["item_pool"] = item_link["item_pool"]
|
group["item_pool"] = item_link["item_pool"]
|
||||||
group["replacement_items"] = item_link["players"]
|
group["replacement_items"] = item_link["players"]
|
||||||
group["local_items"] = item_link["local_items"]
|
group["local_items"] = item_link["local_items"]
|
||||||
group["non_local_items"] = item_link["non_local_items"]
|
group["non_local_items"] = item_link["non_local_items"]
|
||||||
|
group["link_replacement"] = replacement_prio[item_link["link_replacement"]]
|
||||||
|
|
||||||
# intended for unittests
|
# intended for unittests
|
||||||
def set_default_common_options(self):
|
def set_default_common_options(self):
|
||||||
|
|
12
Main.py
12
Main.py
|
@ -210,11 +210,15 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
|
||||||
while itemcount > len(world.itempool):
|
while itemcount > len(world.itempool):
|
||||||
items_to_add = []
|
items_to_add = []
|
||||||
for player in group["players"]:
|
for player in group["players"]:
|
||||||
if group["replacement_items"][player]:
|
if group["link_replacement"]:
|
||||||
items_to_add.append(
|
item_player = group_id
|
||||||
AutoWorld.call_single(world, "create_item", player, group["replacement_items"][player]))
|
|
||||||
else:
|
else:
|
||||||
items_to_add.append(AutoWorld.call_single(world, "create_filler", player))
|
item_player = player
|
||||||
|
if group["replacement_items"][player]:
|
||||||
|
items_to_add.append(AutoWorld.call_single(world, "create_item", item_player,
|
||||||
|
group["replacement_items"][player]))
|
||||||
|
else:
|
||||||
|
items_to_add.append(AutoWorld.call_single(world, "create_filler", item_player))
|
||||||
world.random.shuffle(items_to_add)
|
world.random.shuffle(items_to_add)
|
||||||
world.itempool.extend(items_to_add[:itemcount - len(world.itempool)])
|
world.itempool.extend(items_to_add[:itemcount - len(world.itempool)])
|
||||||
|
|
||||||
|
|
|
@ -927,7 +927,8 @@ class ItemLinks(OptionList):
|
||||||
Optional("exclude"): [And(str, len)],
|
Optional("exclude"): [And(str, len)],
|
||||||
"replacement_item": Or(And(str, len), None),
|
"replacement_item": Or(And(str, len), None),
|
||||||
Optional("local_items"): [And(str, len)],
|
Optional("local_items"): [And(str, len)],
|
||||||
Optional("non_local_items"): [And(str, len)]
|
Optional("non_local_items"): [And(str, len)],
|
||||||
|
Optional("link_replacement"): Or(None, bool),
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
@ -950,6 +951,7 @@ class ItemLinks(OptionList):
|
||||||
return pool
|
return pool
|
||||||
|
|
||||||
def verify(self, world, player_name: str, plando_options) -> None:
|
def verify(self, world, player_name: str, plando_options) -> None:
|
||||||
|
link: dict
|
||||||
super(ItemLinks, self).verify(world, player_name, plando_options)
|
super(ItemLinks, self).verify(world, player_name, plando_options)
|
||||||
existing_links = set()
|
existing_links = set()
|
||||||
for link in self.value:
|
for link in self.value:
|
||||||
|
@ -974,7 +976,9 @@ class ItemLinks(OptionList):
|
||||||
|
|
||||||
intersection = local_items.intersection(non_local_items)
|
intersection = local_items.intersection(non_local_items)
|
||||||
if intersection:
|
if intersection:
|
||||||
raise Exception(f"item_link {link['name']} has {intersection} items in both its local_items and non_local_items pool.")
|
raise Exception(f"item_link {link['name']} has {intersection} "
|
||||||
|
f"items in both its local_items and non_local_items pool.")
|
||||||
|
link.setdefault("link_replacement", None)
|
||||||
|
|
||||||
|
|
||||||
per_game_common_options = {
|
per_game_common_options = {
|
||||||
|
|
|
@ -184,6 +184,7 @@ A Link to the Past:
|
||||||
- Fire Rod
|
- Fire Rod
|
||||||
- Ice Rod
|
- Ice Rod
|
||||||
replacement_item: "Rupee (1)"
|
replacement_item: "Rupee (1)"
|
||||||
|
link_replacement: true
|
||||||
triggers:
|
triggers:
|
||||||
- option_category: A Link to the Past
|
- option_category: A Link to the Past
|
||||||
option_name: smallkey_shuffle
|
option_name: smallkey_shuffle
|
||||||
|
@ -241,7 +242,7 @@ Timespinner:
|
||||||
* `exclude_locations` forces a not important item to be placed on the `Cave 45` location.
|
* `exclude_locations` forces a not important item to be placed on the `Cave 45` location.
|
||||||
* `item_links`
|
* `item_links`
|
||||||
* For `A Link to the Past` all players in the `rods` item link group will share their fire and ice rods and the player
|
* For `A Link to the Past` all players in the `rods` item link group will share their fire and ice rods and the player
|
||||||
items will be replaced with single rupees.
|
items will be replaced with single rupees. The rupee will also be shared among those players.
|
||||||
* For `Timespinner` all players in the `TSAll` item link group will share their entire item pool and the `Twin Pyramid
|
* For `Timespinner` all players in the `TSAll` item link group will share their entire item pool and the `Twin Pyramid
|
||||||
Key` and `Timespinner Wheel` will be forced among the worlds of those in the group. The `null` replacement item will,
|
Key` and `Timespinner Wheel` will be forced among the worlds of those in the group. The `null` replacement item will,
|
||||||
instead of forcing a specific chosen item, allow the generator to randomly pick a filler item to replace the player items.
|
instead of forcing a specific chosen item, allow the generator to randomly pick a filler item to replace the player items.
|
||||||
|
|
Loading…
Reference in New Issue