Item Plando overhaul (#205)
This commit is contained in:
parent
fc8e3d1787
commit
dc6f1c4dd2
|
@ -334,11 +334,14 @@ class MultiWorld():
|
||||||
return [location for location in self.get_locations() if
|
return [location for location in self.get_locations() if
|
||||||
(player is None or location.player == player) and location.item is None and location.can_reach(state)]
|
(player is None or location.player == player) and location.item is None and location.can_reach(state)]
|
||||||
|
|
||||||
def get_unfilled_locations_for_players(self, location_name: str, players: Iterable[int]):
|
def get_unfilled_locations_for_players(self, locations, players: Iterable[int]):
|
||||||
for player in players:
|
for player in players:
|
||||||
location = self.get_location(location_name, player)
|
if len(locations) == 0:
|
||||||
if location.item is None:
|
locations = [location.name for location in self.get_unfilled_locations(player)]
|
||||||
yield location
|
for location_name in locations:
|
||||||
|
location = self._location_cache.get((location_name, player), None)
|
||||||
|
if location is not None and location.item is None:
|
||||||
|
yield location
|
||||||
|
|
||||||
def unlocks_new_location(self, item) -> bool:
|
def unlocks_new_location(self, item) -> bool:
|
||||||
temp_state = self.state.copy()
|
temp_state = self.state.copy()
|
||||||
|
|
230
Fill.py
230
Fill.py
|
@ -6,7 +6,7 @@ from collections import Counter, deque
|
||||||
|
|
||||||
|
|
||||||
from BaseClasses import CollectionState, Location, LocationProgressType, MultiWorld, Item
|
from BaseClasses import CollectionState, Location, LocationProgressType, MultiWorld, Item
|
||||||
from worlds.generic import PlandoItem
|
|
||||||
from worlds.AutoWorld import call_all
|
from worlds.AutoWorld import call_all
|
||||||
|
|
||||||
|
|
||||||
|
@ -407,80 +407,182 @@ def swap_location_item(location_1: Location, location_2: Location, check_locked=
|
||||||
location_1.item.location = location_1
|
location_1.item.location = location_1
|
||||||
location_2.item.location = location_2
|
location_2.item.location = location_2
|
||||||
location_1.event, location_2.event = location_2.event, location_1.event
|
location_1.event, location_2.event = location_2.event, location_1.event
|
||||||
|
|
||||||
|
|
||||||
def distribute_planned(world: MultiWorld):
|
def distribute_planned(world: MultiWorld):
|
||||||
|
def warn(warning: str, force):
|
||||||
|
if force in ['true', 'fail', 'failure', 'none', 'false', 'warn', 'warning']:
|
||||||
|
logging.warning(f'{warning}')
|
||||||
|
else:
|
||||||
|
logging.debug(f'{warning}')
|
||||||
|
|
||||||
|
def failed(warning: str, force):
|
||||||
|
if force in ['true', 'fail', 'failure']:
|
||||||
|
raise Exception(warning)
|
||||||
|
else:
|
||||||
|
warn(warning, force)
|
||||||
|
|
||||||
# TODO: remove. Preferably by implementing key drop
|
# TODO: remove. Preferably by implementing key drop
|
||||||
from worlds.alttp.Regions import key_drop_data
|
from worlds.alttp.Regions import key_drop_data
|
||||||
world_name_lookup = world.world_name_lookup
|
world_name_lookup = world.world_name_lookup
|
||||||
|
|
||||||
for player in world.player_ids:
|
plando_blocks = []
|
||||||
|
player_ids = set(world.player_ids)
|
||||||
|
for player in player_ids:
|
||||||
|
for block in world.plando_items[player]:
|
||||||
|
block['player'] = player
|
||||||
|
if 'force' not in block:
|
||||||
|
block['force'] = 'silent'
|
||||||
|
if 'from_pool' not in block:
|
||||||
|
block['from_pool'] = True
|
||||||
|
if 'world' not in block:
|
||||||
|
block['world'] = False
|
||||||
|
items = []
|
||||||
|
if "items" in block:
|
||||||
|
items = block["items"]
|
||||||
|
if 'count' not in block:
|
||||||
|
block['count'] = False
|
||||||
|
elif "item" in block:
|
||||||
|
items = block["item"]
|
||||||
|
if 'count' not in block:
|
||||||
|
block['count'] = 1
|
||||||
|
else:
|
||||||
|
failed("You must specify at least one item to place items with plando.", block['forced'])
|
||||||
|
if isinstance(items, dict):
|
||||||
|
item_list = []
|
||||||
|
for key, value in items.items():
|
||||||
|
item_list += [key] * value
|
||||||
|
items = item_list
|
||||||
|
if isinstance(items, str):
|
||||||
|
items = [items]
|
||||||
|
block['items'] = items
|
||||||
|
|
||||||
|
locations = []
|
||||||
|
if 'locations' in block:
|
||||||
|
locations.extend(block['locations'])
|
||||||
|
elif 'location' in block:
|
||||||
|
locations.extend(block['location'])
|
||||||
|
|
||||||
|
if isinstance(locations, dict):
|
||||||
|
location_list = []
|
||||||
|
for key, value in locations.items():
|
||||||
|
location_list += [key] * value
|
||||||
|
locations = location_list
|
||||||
|
if isinstance(locations, str):
|
||||||
|
locations = [locations]
|
||||||
|
block['locations'] = locations
|
||||||
|
c = block['count']
|
||||||
|
|
||||||
|
if not block['count']:
|
||||||
|
block['count'] = (min(len(block['items']), len(block['locations'])) if len(block['locations'])
|
||||||
|
> 0 else len(block['items']))
|
||||||
|
if isinstance(block['count'], int):
|
||||||
|
block['count'] = {'min': block['count'], 'max': block['count']}
|
||||||
|
if 'min' not in block['count']:
|
||||||
|
block['count']['min'] = 0
|
||||||
|
if 'max' not in block['count']:
|
||||||
|
block['count']['max'] = (min(len(block['items']), len(block['locations'])) if len(block['locations'])
|
||||||
|
> 0 else len(block['items']))
|
||||||
|
if block['count']['max'] > len(block['items']):
|
||||||
|
count = block['count']
|
||||||
|
failed(f"Plando count {count} greater than items specified", block['force'])
|
||||||
|
block['count'] = len(block['items'])
|
||||||
|
if block['count']['max'] > len(block['locations']) > 0:
|
||||||
|
count = block['count']
|
||||||
|
failed(f"Plando count {count} greater than locations specified for player ", block['force'])
|
||||||
|
block['count'] = len(block['locations'])
|
||||||
|
block['count']['target'] = world.random.randint(block['count']['min'], block['count']['max'])
|
||||||
|
c = block['count']
|
||||||
|
|
||||||
|
if block['count']['target'] > 0:
|
||||||
|
plando_blocks.append(block)
|
||||||
|
|
||||||
|
# shuffle, but then sort blocks by number of items, so blocks with fewer items get priority
|
||||||
|
world.random.shuffle(plando_blocks)
|
||||||
|
plando_blocks.sort(key=lambda block: block['count']['target'])
|
||||||
|
|
||||||
|
for placement in plando_blocks:
|
||||||
|
player = placement['player']
|
||||||
try:
|
try:
|
||||||
placement: PlandoItem
|
target_world = placement['world']
|
||||||
for placement in world.plando_items[player]:
|
locations = placement['locations']
|
||||||
if placement.location in key_drop_data:
|
items = placement['items']
|
||||||
placement.warn(
|
maxcount = placement['count']['target']
|
||||||
f"Can't place '{placement.item}' at '{placement.location}', as key drop shuffle locations are not supported yet.")
|
from_pool = placement['from_pool']
|
||||||
|
if target_world is False or world.players == 1:
|
||||||
|
worlds = {player}
|
||||||
|
elif target_world is True:
|
||||||
|
worlds = set(world.player_ids) - {player}
|
||||||
|
elif target_world is None:
|
||||||
|
worlds = set(world.player_ids)
|
||||||
|
elif type(target_world) == list:
|
||||||
|
worlds = []
|
||||||
|
for listed_world in target_world:
|
||||||
|
if listed_world not in world_name_lookup:
|
||||||
|
failed(f"Cannot place item to {target_world}'s world as that world does not exist.",
|
||||||
|
placement['force'])
|
||||||
|
continue
|
||||||
|
worlds.append(world_name_lookup[listed_world])
|
||||||
|
worlds = set(worlds)
|
||||||
|
elif type(target_world) == int:
|
||||||
|
if target_world not in range(1, world.players + 1):
|
||||||
|
failed(
|
||||||
|
f"Cannot place item in world {target_world} as it is not in range of (1, {world.players})",
|
||||||
|
placement['forced'])
|
||||||
continue
|
continue
|
||||||
item = world.worlds[player].create_item(placement.item)
|
worlds = {target_world}
|
||||||
target_world: int = placement.world
|
else: # find world by name
|
||||||
if target_world is False or world.players == 1:
|
if target_world not in world_name_lookup:
|
||||||
target_world = player # in own world
|
failed(f"Cannot place item to {target_world}'s world as that world does not exist.",
|
||||||
elif target_world is True: # in any other world
|
placement['force'])
|
||||||
unfilled = list(location for location in world.get_unfilled_locations_for_players(
|
|
||||||
placement.location,
|
|
||||||
set(world.player_ids) - {player}) if location.item_rule(item)
|
|
||||||
)
|
|
||||||
if not unfilled:
|
|
||||||
placement.failed(f"Could not find a world with an unfilled location {placement.location}",
|
|
||||||
FillError)
|
|
||||||
continue
|
|
||||||
|
|
||||||
target_world = world.random.choice(unfilled).player
|
|
||||||
|
|
||||||
elif target_world is None: # any random world
|
|
||||||
unfilled = list(location for location in world.get_unfilled_locations_for_players(
|
|
||||||
placement.location,
|
|
||||||
set(world.player_ids)) if location.item_rule(item)
|
|
||||||
)
|
|
||||||
if not unfilled:
|
|
||||||
placement.failed(f"Could not find a world with an unfilled location {placement.location}",
|
|
||||||
FillError)
|
|
||||||
continue
|
|
||||||
|
|
||||||
target_world = world.random.choice(unfilled).player
|
|
||||||
|
|
||||||
elif type(target_world) == int: # target world by player id
|
|
||||||
if target_world not in range(1, world.players + 1):
|
|
||||||
placement.failed(
|
|
||||||
f"Cannot place item in world {target_world} as it is not in range of (1, {world.players})",
|
|
||||||
ValueError)
|
|
||||||
continue
|
|
||||||
else: # find world by name
|
|
||||||
if target_world not in world_name_lookup:
|
|
||||||
placement.failed(f"Cannot place item to {target_world}'s world as that world does not exist.",
|
|
||||||
ValueError)
|
|
||||||
continue
|
|
||||||
target_world = world_name_lookup[target_world]
|
|
||||||
|
|
||||||
location = world.get_location(placement.location, target_world)
|
|
||||||
if location.item:
|
|
||||||
placement.failed(f"Cannot place item into already filled location {location}.")
|
|
||||||
continue
|
continue
|
||||||
|
worlds = {world_name_lookup[target_world]}
|
||||||
|
|
||||||
if location.can_fill(world.state, item, False):
|
candidates = list(location for location in world.get_unfilled_locations_for_players(locations,
|
||||||
world.push_item(location, item, collect=False)
|
worlds))
|
||||||
location.event = True # flag location to be checked during fill
|
|
||||||
location.locked = True
|
|
||||||
logging.debug(f"Plando placed {item} at {location}")
|
|
||||||
else:
|
|
||||||
placement.failed(f"Can't place {item} at {location} due to fill condition not met.")
|
|
||||||
continue
|
|
||||||
|
|
||||||
if placement.from_pool: # Should happen AFTER the item is placed, in case it was allowed to skip failed placement.
|
world.random.shuffle(candidates)
|
||||||
|
world.random.shuffle(items)
|
||||||
|
count = 0
|
||||||
|
err = "Unknown error"
|
||||||
|
successful_pairs = []
|
||||||
|
for item_name in items:
|
||||||
|
item = world.worlds[player].create_item(item_name)
|
||||||
|
for location in reversed(candidates):
|
||||||
|
if location in key_drop_data:
|
||||||
|
warn(
|
||||||
|
f"Can't place '{item_name}' at '{placement.location}', as key drop shuffle locations are not supported yet.")
|
||||||
|
if not location.item:
|
||||||
|
if location.item_rule(item):
|
||||||
|
if location.can_fill(world.state, item, False):
|
||||||
|
successful_pairs.append([item, location])
|
||||||
|
candidates.remove(location)
|
||||||
|
count = count + 1
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
err = f"Can't place item at {location} due to fill condition not met."
|
||||||
|
else:
|
||||||
|
err = f"{item_name} not allowed at {location}."
|
||||||
|
else:
|
||||||
|
err = f"Cannot place {item_name} into already filled location {location}."
|
||||||
|
if count == maxcount:
|
||||||
|
break
|
||||||
|
if count < placement['count']['min']:
|
||||||
|
failed(
|
||||||
|
f"Plando block failed to place item(s) for {world.player_name[player]}, most recent cause: {err}",
|
||||||
|
placement['force'])
|
||||||
|
continue
|
||||||
|
for (item, location) in successful_pairs:
|
||||||
|
world.push_item(location, item, collect=False)
|
||||||
|
location.event = True # flag location to be checked during fill
|
||||||
|
location.locked = True
|
||||||
|
logging.debug(f"Plando placed {item} at {location}")
|
||||||
|
if from_pool:
|
||||||
try:
|
try:
|
||||||
world.itempool.remove(item)
|
world.itempool.remove(item)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
placement.warn(f"Could not remove {item} from pool as it's already missing from it.")
|
warn(
|
||||||
|
f"Could not remove {item} from pool for {world.player_name[player]} as it's already missing from it.",
|
||||||
|
placement['force'])
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise Exception(f"Error running plando for player {player} ({world.player_name[player]})") from e
|
raise Exception(
|
||||||
|
f"Error running plando for player {player} ({world.player_name[player]})") from e
|
||||||
|
|
41
Generate.py
41
Generate.py
|
@ -494,7 +494,7 @@ def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("b
|
||||||
if not (option_key in Options.common_options and option_key not in game_weights):
|
if not (option_key in Options.common_options and option_key not in game_weights):
|
||||||
handle_option(ret, game_weights, option_key, option)
|
handle_option(ret, game_weights, option_key, option)
|
||||||
if "items" in plando_options:
|
if "items" in plando_options:
|
||||||
ret.plando_items = roll_item_plando(world_type, game_weights)
|
ret.plando_items = game_weights.get("plando_items", [])
|
||||||
if ret.game == "Minecraft" or ret.game == "Ocarina of Time":
|
if ret.game == "Minecraft" or ret.game == "Ocarina of Time":
|
||||||
# bad hardcoded behavior to make this work for now
|
# bad hardcoded behavior to make this work for now
|
||||||
ret.plando_connections = []
|
ret.plando_connections = []
|
||||||
|
@ -513,45 +513,6 @@ def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("b
|
||||||
raise Exception(f"Unsupported game {ret.game}")
|
raise Exception(f"Unsupported game {ret.game}")
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def roll_item_plando(world_type, weights):
|
|
||||||
plando_items = []
|
|
||||||
|
|
||||||
def add_plando_item(item: str, location: str):
|
|
||||||
if item not in world_type.item_name_to_id:
|
|
||||||
raise Exception(f"Could not plando item {item} as the item was not recognized")
|
|
||||||
if location not in world_type.location_name_to_id:
|
|
||||||
raise Exception(
|
|
||||||
f"Could not plando item {item} at location {location} as the location was not recognized")
|
|
||||||
plando_items.append(PlandoItem(item, location, location_world, from_pool, force))
|
|
||||||
|
|
||||||
options = weights.get("plando_items", [])
|
|
||||||
for placement in options:
|
|
||||||
if roll_percentage(get_choice("percentage", placement, 100)):
|
|
||||||
from_pool = get_choice("from_pool", placement, PlandoItem._field_defaults["from_pool"])
|
|
||||||
location_world = get_choice("world", placement, PlandoItem._field_defaults["world"])
|
|
||||||
force = str(get_choice("force", placement, PlandoItem._field_defaults["force"])).lower()
|
|
||||||
if "items" in placement and "locations" in placement:
|
|
||||||
items = placement["items"]
|
|
||||||
locations = placement["locations"]
|
|
||||||
if isinstance(items, dict):
|
|
||||||
item_list = []
|
|
||||||
for key, value in items.items():
|
|
||||||
item_list += [key] * value
|
|
||||||
items = item_list
|
|
||||||
if not items or not locations:
|
|
||||||
raise Exception("You must specify at least one item and one location to place items.")
|
|
||||||
random.shuffle(items)
|
|
||||||
random.shuffle(locations)
|
|
||||||
for item, location in zip(items, locations):
|
|
||||||
add_plando_item(item, location)
|
|
||||||
else:
|
|
||||||
item = get_choice("item", placement, get_choice("items", placement))
|
|
||||||
location = get_choice("location", placement)
|
|
||||||
add_plando_item(item, location)
|
|
||||||
return plando_items
|
|
||||||
|
|
||||||
|
|
||||||
def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options):
|
def roll_alttp_settings(ret: argparse.Namespace, weights, plando_options):
|
||||||
if "dungeon_items" in weights and get_choice_legacy('dungeon_items', weights, "none") != "none":
|
if "dungeon_items" in weights and get_choice_legacy('dungeon_items', weights, "none") != "none":
|
||||||
raise Exception(f"dungeon_items key in A Link to the Past was removed, but is present in these weights as {get_choice_legacy('dungeon_items', weights, False)}.")
|
raise Exception(f"dungeon_items key in A Link to the Past was removed, but is present in these weights as {get_choice_legacy('dungeon_items', weights, False)}.")
|
||||||
|
|
|
@ -30,24 +30,22 @@ enabled (opt-in).
|
||||||
```
|
```
|
||||||
|
|
||||||
## Item Plando
|
## Item Plando
|
||||||
|
|
||||||
Item plando allows a player to place an item in a specific location or specific locations, place multiple items into a
|
Item plando allows a player to place an item in a specific location or specific locations, place multiple items into a
|
||||||
list of specific locations both in their own game or in another player's game. **Note that there's a very good chance
|
list of specific locations both in their own game or in another player's game.
|
||||||
that cross-game plando could very well be broken i.e. placing on of your items in someone else's world playing a
|
|
||||||
different game.**
|
|
||||||
|
|
||||||
* The options for item plando are `from_pool`, `world`, `percentage`, `force`, and either item and location, or items
|
* The options for item plando are `from_pool`, `world`, `percentage`, `force`, `count`, and either item and location, or items
|
||||||
and locations.
|
and locations.
|
||||||
* `from_pool` determines if the item should be taken *from* the item pool or *added* to it. This can be true or
|
* `from_pool` determines if the item should be taken *from* the item pool or *added* to it. This can be true or
|
||||||
false and defaults to true if omitted.
|
false and defaults to true if omitted.
|
||||||
* `world` is the target world to place the item in.
|
* `world` is the target world to place the item in.
|
||||||
* It gets ignored if only one world is generated.
|
* It gets ignored if only one world is generated.
|
||||||
* Can be a number, name, true, false, or null. False is the default.
|
* Can be a number, name, true, false, null, or a list. False is the default.
|
||||||
* If a number is used it targets that slot or player number in the multiworld.
|
* If a number is used it targets that slot or player number in the multiworld.
|
||||||
* If a name is used it will target the world with that player name.
|
* If a name is used it will target the world with that player name.
|
||||||
* If set to true it will be any player's world besides your own.
|
* If set to true it will be any player's world besides your own.
|
||||||
* If set to false it will target your own world.
|
* If set to false it will target your own world.
|
||||||
* If set to null it will target a random world in the multiworld.
|
* If set to null it will target a random world in the multiworld.
|
||||||
|
* If a list of names is used, it will target the games with the player names specified.
|
||||||
* `force` determines whether the generator will fail if the item can't be placed in the location can be true, false,
|
* `force` determines whether the generator will fail if the item can't be placed in the location can be true, false,
|
||||||
or silent. Silent is the default.
|
or silent. Silent is the default.
|
||||||
* If set to true the item must be placed and the generator will throw an error if it is unable to do so.
|
* If set to true the item must be placed and the generator will throw an error if it is unable to do so.
|
||||||
|
@ -61,6 +59,11 @@ different game.**
|
||||||
* `items` defines the items to use and a number letting you place multiple of it.
|
* `items` defines the items to use and a number letting you place multiple of it.
|
||||||
* `locations` is a list of possible locations those items can be placed in.
|
* `locations` is a list of possible locations those items can be placed in.
|
||||||
* Using the multi placement method, placements are picked randomly.
|
* Using the multi placement method, placements are picked randomly.
|
||||||
|
* `count` can be used to set the maximum number of items placed from the block. The default is 1 if using `item` and False if using `items`
|
||||||
|
* If a number is used it will try to place this number of items.
|
||||||
|
* If set to false it will try to place as many items from the block as it can.
|
||||||
|
* If `min` and `max` are defined, it will try to place a number of items between these two numbers at random
|
||||||
|
|
||||||
|
|
||||||
### Available Items
|
### Available Items
|
||||||
|
|
||||||
|
@ -68,23 +71,29 @@ different game.**
|
||||||
* [Factorio Non-Progressive](https://wiki.factorio.com/Technologies) Note that these use the *internal names*. For
|
* [Factorio Non-Progressive](https://wiki.factorio.com/Technologies) Note that these use the *internal names*. For
|
||||||
example, `advanced-electronics`
|
example, `advanced-electronics`
|
||||||
* [Factorio Progressive](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/factorio/Technologies.py#L374)
|
* [Factorio Progressive](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/factorio/Technologies.py#L374)
|
||||||
|
* [Final Fantasy 1](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/ff1/data/items.json)
|
||||||
* [Minecraft](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/minecraft/Items.py#L14)
|
* [Minecraft](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/minecraft/Items.py#L14)
|
||||||
* [Ocarina of Time](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/oot/Items.py#L61)
|
* [Ocarina of Time](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/oot/Items.py#L61)
|
||||||
* [Risk of Rain 2](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/ror2/Items.py#L8)
|
* [Risk of Rain 2](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/ror2/Items.py#L8)
|
||||||
|
* [Rogue Legacy](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/rogue-legacy/Names/ItemName.py)
|
||||||
* [Slay the Spire](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/spire/Items.py#L13)
|
* [Slay the Spire](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/spire/Items.py#L13)
|
||||||
* [Subnautica](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/subnautica/items.json)
|
* [Subnautica](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/subnautica/items.json)
|
||||||
|
* [Super Metroid](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/sm/variaRandomizer/rando/Items.py#L37) Look for "Name="
|
||||||
* [Timespinner](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/timespinner/Items.py#L11)
|
* [Timespinner](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/timespinner/Items.py#L11)
|
||||||
|
|
||||||
### Available Locations
|
### Available Locations
|
||||||
|
|
||||||
* [A Link to the Past](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/alttp/Regions.py#L429)
|
* [A Link to the Past](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/alttp/Regions.py#L429)
|
||||||
* [Factorio](https://wiki.factorio.com/Technologies) Same as items
|
* [Factorio](https://wiki.factorio.com/Technologies) Same as items
|
||||||
|
* [Final Fantasy 1](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/ff1/data/locations.json)
|
||||||
* [Minecraft](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/minecraft/Locations.py#L18)
|
* [Minecraft](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/minecraft/Locations.py#L18)
|
||||||
* [Ocarina of Time](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/oot/LocationList.py#L38)
|
* [Ocarina of Time](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/oot/LocationList.py#L38)
|
||||||
* [Risk of Rain 2](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/ror2/Locations.py#L17) This is a
|
* [Risk of Rain 2](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/ror2/Locations.py#L17) This is a
|
||||||
special case. The locations are "ItemPickup[number]" up to the maximum set in the yaml.
|
special case. The locations are "ItemPickup[number]" up to the maximum set in the yaml.
|
||||||
|
* [Rogue Legacy](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/rogue-legacy/Names/LocationName.py)
|
||||||
* [Slay the Spire](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/spire/Locations.py)
|
* [Slay the Spire](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/spire/Locations.py)
|
||||||
* [Subnautica](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/subnautica/locations.json)
|
* [Subnautica](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/subnautica/locations.json)
|
||||||
|
* [Super Metroid](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/sm/variaRandomizer/graph/location.py#L132)
|
||||||
* [Timespinner](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/timespinner/Locations.py#L13)
|
* [Timespinner](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/timespinner/Locations.py#L13)
|
||||||
|
|
||||||
A list of all available items and locations can also be found in the [server's datapackage](/api/datapackage).
|
A list of all available items and locations can also be found in the [server's datapackage](/api/datapackage).
|
||||||
|
@ -126,9 +135,9 @@ plando_items:
|
||||||
- items:
|
- items:
|
||||||
Boss Relic: 3
|
Boss Relic: 3
|
||||||
locations:
|
locations:
|
||||||
Boss Relic 1
|
- Boss Relic 1
|
||||||
Boss Relic 2
|
- Boss Relic 2
|
||||||
Boss Relic 3
|
- Boss Relic 3
|
||||||
|
|
||||||
# example block 4 - Factorio
|
# example block 4 - Factorio
|
||||||
- items:
|
- items:
|
||||||
|
@ -136,21 +145,46 @@ plando_items:
|
||||||
electric-energy-accumulators: 1
|
electric-energy-accumulators: 1
|
||||||
progressive-turret: 2
|
progressive-turret: 2
|
||||||
locations:
|
locations:
|
||||||
military
|
- military
|
||||||
gun-turret
|
- gun-turret
|
||||||
logistic-science-pack
|
- logistic-science-pack
|
||||||
steel-processing
|
- steel-processing
|
||||||
percentage: 80
|
percentage: 80
|
||||||
force: true
|
force: true
|
||||||
```
|
|
||||||
|
|
||||||
1. This block has a 50% chance to occur, and if it does will place either the Empire Orb or Radiant Orb on another
|
# example block 5 - Secret of Evermore
|
||||||
player's Starter Chest 1 and removes the chosen item from the item pool.
|
- items:
|
||||||
|
Levitate: 1
|
||||||
|
Revealer: 1
|
||||||
|
Energize: 1
|
||||||
|
locations:
|
||||||
|
- Master Sword Pedestal
|
||||||
|
- Boss Relic 1
|
||||||
|
world: true
|
||||||
|
count: 2
|
||||||
|
|
||||||
|
# example block 6 - A Link to the Past
|
||||||
|
- items:
|
||||||
|
Progressive Sword: 4
|
||||||
|
world:
|
||||||
|
- BobsSlaytheSpire
|
||||||
|
- BobsRogueLegacy
|
||||||
|
count:
|
||||||
|
min: 1
|
||||||
|
max: 4
|
||||||
|
```
|
||||||
|
1. This block has a 50% chance to occur, and if it does will place either the Empire Orb or Radiant Orb on another player's
|
||||||
|
Starter Chest 1 and removes the chosen item from the item pool.
|
||||||
2. This block will always trigger and will place the player's swords, bow, magic meter, strength upgrades, and hookshots
|
2. This block will always trigger and will place the player's swords, bow, magic meter, strength upgrades, and hookshots
|
||||||
in their own dungeon major item chests.
|
in their own dungeon major item chests.
|
||||||
3. This block will always trigger and will lock boss relics on the bosses.
|
3. This block will always trigger and will lock boss relics on the bosses.
|
||||||
4. This block has an 80% chance of occuring and when it does will place all but 1 of the items randomly among the four
|
4. This block has an 80% chance of occurring and when it does will place all but 1 of the items randomly among the four
|
||||||
locations chosen here.
|
locations chosen here.
|
||||||
|
5. This block will always trigger and will attempt to place a random 2 of Levitate, Revealer and Energize into
|
||||||
|
other players' Master Sword Pedestals or Boss Relic 1 locations.
|
||||||
|
6. This block will always trigger and will attempt to place a random number, between 1 and 4, of progressive swords
|
||||||
|
into any locations within the game slots named BobsSlaytheSpire and BobsRogueLegacy
|
||||||
|
|
||||||
|
|
||||||
## Boss Plando
|
## Boss Plando
|
||||||
|
|
||||||
|
@ -208,4 +242,4 @@ plando_connections:
|
||||||
the lake hylia cave shop. Walking into the entrance for the old man cave and Agahnim's Tower entrance will both take
|
the lake hylia cave shop. Walking into the entrance for the old man cave and Agahnim's Tower entrance will both take
|
||||||
you to their locations as normal but leaving old man cave will exit at Agahnim's Tower.
|
you to their locations as normal but leaving old man cave will exit at Agahnim's Tower.
|
||||||
2. This will force a nether fortress and a village to be the overworld structures for your game. Note that for the
|
2. This will force a nether fortress and a village to be the overworld structures for your game. Note that for the
|
||||||
Minecraft connection plando to work structure shuffle must be enabled.
|
Minecraft connection plando to work structure shuffle must be enabled.
|
||||||
|
|
|
@ -83,7 +83,8 @@
|
||||||
"filename": "archipelago/plando_en.md",
|
"filename": "archipelago/plando_en.md",
|
||||||
"link": "archipelago/plando/en",
|
"link": "archipelago/plando/en",
|
||||||
"authors": [
|
"authors": [
|
||||||
"alwaysintreble"
|
"alwaysintreble",
|
||||||
|
"Alchav"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue