Merge branch 'main' into multishop
This commit is contained in:
commit
f4281f81f5
|
@ -2251,7 +2251,10 @@ def plando_connect(world, player: int):
|
||||||
if world.plando_connections[player]:
|
if world.plando_connections[player]:
|
||||||
for connection in world.plando_connections[player]:
|
for connection in world.plando_connections[player]:
|
||||||
func = lookup[connection.direction]
|
func = lookup[connection.direction]
|
||||||
func(world, connection.entrance, connection.exit, player)
|
try:
|
||||||
|
func(world, connection.entrance, connection.exit, player)
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception(f"Could not connect using {connection}") from e
|
||||||
|
|
||||||
|
|
||||||
LW_Dungeon_Entrances = ['Desert Palace Entrance (South)',
|
LW_Dungeon_Entrances = ['Desert Palace Entrance (South)',
|
||||||
|
|
12
Fill.py
12
Fill.py
|
@ -262,6 +262,11 @@ def balance_multiworld_progression(world):
|
||||||
|
|
||||||
reachable_locations_count = {player: 0 for player in range(1, world.players + 1)}
|
reachable_locations_count = {player: 0 for player in range(1, world.players + 1)}
|
||||||
|
|
||||||
|
def event_key(location):
|
||||||
|
return location.event and (
|
||||||
|
world.keyshuffle[location.item.player] or not location.item.smallkey) and (
|
||||||
|
world.bigkeyshuffle[location.item.player] or not location.item.bigkey)
|
||||||
|
|
||||||
def get_sphere_locations(sphere_state, locations):
|
def get_sphere_locations(sphere_state, locations):
|
||||||
sphere_state.sweep_for_events(key_only=True, locations=locations)
|
sphere_state.sweep_for_events(key_only=True, locations=locations)
|
||||||
return [loc for loc in locations if sphere_state.can_reach(loc)]
|
return [loc for loc in locations if sphere_state.can_reach(loc)]
|
||||||
|
@ -284,9 +289,7 @@ def balance_multiworld_progression(world):
|
||||||
candidate_items = []
|
candidate_items = []
|
||||||
while True:
|
while True:
|
||||||
for location in balancing_sphere:
|
for location in balancing_sphere:
|
||||||
if location.event and (
|
if event_key(location):
|
||||||
world.keyshuffle[location.item.player] or not location.item.smallkey) and (
|
|
||||||
world.bigkeyshuffle[location.item.player] or not location.item.bigkey):
|
|
||||||
balancing_state.collect(location.item, True, location)
|
balancing_state.collect(location.item, True, location)
|
||||||
if location.item.player in balancing_players and not location.locked:
|
if location.item.player in balancing_players and not location.locked:
|
||||||
candidate_items.append(location)
|
candidate_items.append(location)
|
||||||
|
@ -348,8 +351,7 @@ def balance_multiworld_progression(world):
|
||||||
sphere_locations.append(location)
|
sphere_locations.append(location)
|
||||||
|
|
||||||
for location in sphere_locations:
|
for location in sphere_locations:
|
||||||
if location.event and (world.keyshuffle[location.item.player] or not location.item.smallkey) and (
|
if event_key(location):
|
||||||
world.bigkeyshuffle[location.item.player] or not location.item.bigkey):
|
|
||||||
state.collect(location.item, True, location)
|
state.collect(location.item, True, location)
|
||||||
checked_locations.extend(sphere_locations)
|
checked_locations.extend(sphere_locations)
|
||||||
|
|
||||||
|
|
12
Mystery.py
12
Mystery.py
|
@ -563,7 +563,7 @@ def roll_settings(weights, plando_options: typing.Set[str] = frozenset(("bosses"
|
||||||
|
|
||||||
ret.plando_items = []
|
ret.plando_items = []
|
||||||
if "items" in plando_options:
|
if "items" in plando_options:
|
||||||
default_placement = PlandoItem(item="", location="")
|
|
||||||
def add_plando_item(item: str, location: str):
|
def add_plando_item(item: str, location: str):
|
||||||
if item not in item_table:
|
if item not in item_table:
|
||||||
raise Exception(f"Could not plando item {item} as the item was not recognized")
|
raise Exception(f"Could not plando item {item} as the item was not recognized")
|
||||||
|
@ -574,9 +574,9 @@ def roll_settings(weights, plando_options: typing.Set[str] = frozenset(("bosses"
|
||||||
options = weights.get("plando_items", [])
|
options = weights.get("plando_items", [])
|
||||||
for placement in options:
|
for placement in options:
|
||||||
if roll_percentage(get_choice("percentage", placement, 100)):
|
if roll_percentage(get_choice("percentage", placement, 100)):
|
||||||
from_pool = get_choice("from_pool", placement, default_placement.from_pool)
|
from_pool = get_choice("from_pool", placement, PlandoItem.from_pool)
|
||||||
location_world = get_choice("world", placement, default_placement.world)
|
location_world = get_choice("world", placement, PlandoItem.world)
|
||||||
force = get_choice("force", placement, default_placement.force)
|
force = get_choice("force", placement, PlandoItem.force)
|
||||||
if "items" in placement and "locations" in placement:
|
if "items" in placement and "locations" in placement:
|
||||||
items = placement["items"]
|
items = placement["items"]
|
||||||
locations = placement["locations"]
|
locations = placement["locations"]
|
||||||
|
@ -589,9 +589,7 @@ def roll_settings(weights, plando_options: typing.Set[str] = frozenset(("bosses"
|
||||||
raise Exception("You must specify at least one item and one location to place items.")
|
raise Exception("You must specify at least one item and one location to place items.")
|
||||||
random.shuffle(items)
|
random.shuffle(items)
|
||||||
random.shuffle(locations)
|
random.shuffle(locations)
|
||||||
while items and locations:
|
for item, location in zip(items, locations):
|
||||||
item = items.pop()
|
|
||||||
location = locations.pop()
|
|
||||||
add_plando_item(item, location)
|
add_plando_item(item, location)
|
||||||
else:
|
else:
|
||||||
item = get_choice("item", placement, get_choice("items", placement))
|
item = get_choice("item", placement, get_choice("items", placement))
|
||||||
|
|
|
@ -69,6 +69,20 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Plando Tutorial",
|
||||||
|
"description": "A guide to creating Multiworld Plandos",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"language": "English",
|
||||||
|
"filename": "zelda3/plando_en.md",
|
||||||
|
"link": "zelda3/plando/en",
|
||||||
|
"authors": [
|
||||||
|
"Berserker"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
# A Link to the Past Randomizer Plando Guide
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
1. Plando features have to be enabled first, before they can be used (opt-in).
|
||||||
|
2. To do so, go to your installation directory (Windows default: C:\ProgramData\BerserkerMultiWorld),
|
||||||
|
then open the host.yaml file therein with a text editor.
|
||||||
|
3. In it, you're looking for the option key "plando_options",
|
||||||
|
to enable all plando modules you can set the value to "bosses, items, texts, connections"
|
||||||
|
|
||||||
|
## Modules
|
||||||
|
|
||||||
|
### Bosses
|
||||||
|
|
||||||
|
- This module is enabled by default and available to be used on
|
||||||
|
[https://archipelago.gg/generate](https://archipelago.gg/generate)
|
||||||
|
- Plando versions of boss shuffles can be added like any other boss shuffle option in a yaml and weighted.
|
||||||
|
- Boss Plando works as a list of instructions from left to right, if any arenas are empty at the end,
|
||||||
|
it defaults to vanilla
|
||||||
|
- Instructions are separated by a semicolon
|
||||||
|
- Available Instructions:
|
||||||
|
- Direct Placement:
|
||||||
|
- Example: "Eastern Palace-Trinexx"
|
||||||
|
- Takes a particular Arena and particular boss, then places that boss into that arena
|
||||||
|
- Ganons Tower has 3 placements, "Ganons Tower Top", "Ganons Tower Middle" and "Ganons Tower Bottom"
|
||||||
|
- Boss Placement:
|
||||||
|
- Example: "Trinexx"
|
||||||
|
- Takes a particular boss and places that boss in any remaining slots in which this boss can function.
|
||||||
|
- In this example, it would fill Desert Palace, but not Tower of Hera.
|
||||||
|
- Boss Shuffle:
|
||||||
|
- Example: "simple"
|
||||||
|
- Runs a particular boss shuffle mode to finish construction instead of vanilla placement, typically used as a last instruction.
|
||||||
|
- [Available Bosses](https://github.com/Berserker66/MultiWorld-Utilities/blob/65fa39df95c90c9b66141aee8b16b7e560d00819/Bosses.py#L135)
|
||||||
|
- [Available Arenas](https://github.com/Berserker66/MultiWorld-Utilities/blob/65fa39df95c90c9b66141aee8b16b7e560d00819/Bosses.py#L186)
|
||||||
|
|
||||||
|
#### Examples:
|
||||||
|
```yaml
|
||||||
|
boss_shuffle:
|
||||||
|
Turtle Rock-Trinexx;basic: 1
|
||||||
|
full: 2
|
||||||
|
Mothula: 3
|
||||||
|
Ganons Tower Bottom-Kholdstare;Trinexx;Kholdstare: 4
|
||||||
|
```
|
||||||
|
1. Would be basic boss shuffle but prevent Trinexx from appearing outside of Turtle Rock,
|
||||||
|
as there's only one Trinexx in the pool
|
||||||
|
2. Regular full boss shuffle. With a 2 in 10 chance to occur.
|
||||||
|
3. A Mothula Singularity, as Mothula works in any arena.
|
||||||
|
4. A Trinexx -> Kholdstare Singularity that prevents ice Trinexx in GT
|
||||||
|
|
||||||
|
|
||||||
|
### Items
|
||||||
|
- This module is disabled by default.
|
||||||
|
- Has the options from_pool, world, percentage and either item and location or items and locations
|
||||||
|
- All of these options support subweights
|
||||||
|
- percentage is the percentage chance for this block to trigger
|
||||||
|
- is a number in the range [0, 100], can be omitted entirely for 100%
|
||||||
|
- from_pool denotes if the item should be taken from the item pool, or be an additional item entirely.
|
||||||
|
- can be true or false, defaults to true when omitted
|
||||||
|
- world is the target world to place the item
|
||||||
|
- ignored if only one world is generated
|
||||||
|
- can be a number, to target that slot in the multiworld
|
||||||
|
- can be a name, to target that player's world
|
||||||
|
- can be true, to target any other player's world
|
||||||
|
- can be false, to target own world and is the default
|
||||||
|
- can be null, to target a random world
|
||||||
|
- Single Placement
|
||||||
|
- place a single item at a single location
|
||||||
|
- item denotes the Item to place
|
||||||
|
- location denotes the Location to place it into
|
||||||
|
- Multi Placement
|
||||||
|
- place multiple items into multiple locations, until either list is exhausted.
|
||||||
|
- items denotes the items to use, can be given a number to have multiple of that item
|
||||||
|
- locations lists the possible locations those items can be placed in
|
||||||
|
- placements are picked randomly, not sorted in any way
|
||||||
|
- [Available Items](https://github.com/Berserker66/MultiWorld-Utilities/blob/3b5ba161dea223b96e9b1fc890e03469d9c6eb59/Items.py#L26)
|
||||||
|
- [Available Locations](https://github.com/Berserker66/MultiWorld-Utilities/blob/3b5ba161dea223b96e9b1fc890e03469d9c6eb59/Regions.py#L418)
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
```yaml
|
||||||
|
plando_items:
|
||||||
|
- item:
|
||||||
|
Lamp: 1
|
||||||
|
Fire Rod: 1
|
||||||
|
location: Link's House
|
||||||
|
from_pool: true
|
||||||
|
world: true
|
||||||
|
percentage: 50
|
||||||
|
- items:
|
||||||
|
Progressive Sword: 4
|
||||||
|
Progressive Bow: 1
|
||||||
|
Progressive Bow (Alt): 1
|
||||||
|
locations:
|
||||||
|
- Desert Palace - Big Chest
|
||||||
|
- Eastern Palace - Big Chest
|
||||||
|
- Tower of Hera - Big Chest
|
||||||
|
- Swamp Palace - Big Chest
|
||||||
|
- Thieves' Town - Big Chest
|
||||||
|
- Skull Woods - Big Chest
|
||||||
|
- Ice Palace - Big Chest
|
||||||
|
- Misery Mire - Big Chest
|
||||||
|
- Turtle Rock - Big Chest
|
||||||
|
- Palace of Darkness - Big Chest
|
||||||
|
world: false
|
||||||
|
```
|
||||||
|
|
||||||
|
The first example has a 50% chance to occur, which if it does places either the Lamp or Fire Rod in one's own
|
||||||
|
Link's House and removes the picked item from the item pool.
|
||||||
|
|
||||||
|
The second example always triggers and places the Swords and Bows into one's own Big Chests
|
||||||
|
|
||||||
|
### Texts
|
||||||
|
- This module is disabled by default.
|
||||||
|
- Has the options "text", "at" and "percentage"
|
||||||
|
- percentage is the percentage chance for this text to be placed, can be omitted entirely for 100%
|
||||||
|
- text is the text to be placed.
|
||||||
|
- can be weighted.
|
||||||
|
- \n is a newline.
|
||||||
|
- @ is the entered player's name.
|
||||||
|
- Warning: Text Mapper does not support full unicode.
|
||||||
|
- [Alphabet](https://github.com/Berserker66/MultiWorld-Utilities/blob/65fa39df95c90c9b66141aee8b16b7e560d00819/Text.py#L756)
|
||||||
|
- at is the location within the game to attach the text to.
|
||||||
|
- can be weighted.
|
||||||
|
- [List of targets](https://github.com/Berserker66/MultiWorld-Utilities/blob/65fa39df95c90c9b66141aee8b16b7e560d00819/Text.py#L1498)
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```yaml
|
||||||
|
plando_texts:
|
||||||
|
- text: "This is a plando.\nYou've been warned."
|
||||||
|
at:
|
||||||
|
uncle_leaving_text: 1
|
||||||
|
uncle_dying_sewer: 1
|
||||||
|
percentage: 50
|
||||||
|
```
|
||||||
|

|
||||||
|
This has a 50% chance to trigger at all, if it does,
|
||||||
|
it throws a coin between "uncle_leaving_text" and "uncle_dying_sewer", then places the text
|
||||||
|
"This is a plando.\nYou've been warned." at that location.
|
||||||
|
|
||||||
|
### Connections
|
||||||
|
- This module is disabled by default.
|
||||||
|
- Has the options "percentage", "entrance", "exit" and "direction".
|
||||||
|
- All options support subweights
|
||||||
|
- percentage is the percentage chance for this to be connected, can be omitted entirely for 100%
|
||||||
|
- Any Door has 4 total directions, as a door can be unlinked like in insanity ER
|
||||||
|
- entrance is the overworld door
|
||||||
|
- exit is the underworld exit
|
||||||
|
- direction can be "both", "entrance" or "exit"
|
||||||
|
- doors can be found in [this file](https://github.com/Berserker66/MultiWorld-Utilities/blob/main/EntranceShuffle.py)
|
||||||
|
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
```yaml
|
||||||
|
plando_connections:
|
||||||
|
- entrance: Links House
|
||||||
|
exit: Hyrule Castle Exit (West)
|
||||||
|
direction: both
|
||||||
|
- entrance: Hyrule Castle Entrance (West)
|
||||||
|
exit: Links House Exit
|
||||||
|
direction: both
|
||||||
|
```
|
||||||
|
|
||||||
|
The first block connects the overworld entrance that normally leads to Link's House
|
||||||
|
to put you into the HC West Wing instead, exiting from within there will put you at the Overworld exiting Link's House.
|
||||||
|
|
||||||
|
Without the second block, you'd still exit from within Link's House to outside Link's House and the left side
|
||||||
|
Balcony Entrance would still lead into HC West Wing
|
|
@ -1017,7 +1017,7 @@
|
||||||
"expert": {
|
"expert": {
|
||||||
"keyString": "enemy_health.expert",
|
"keyString": "enemy_health.expert",
|
||||||
"friendlyName": "Armor-Plated",
|
"friendlyName": "Armor-Plated",
|
||||||
"description": "Enemies will be very heard to defeat.",
|
"description": "Enemies will be very hard to defeat.",
|
||||||
"defaultValue": 0
|
"defaultValue": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@
|
||||||
{% for patch in seed.patches %}
|
{% for patch in seed.patches %}
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ url_for("download_raw_patch", seed_id=seed.id, player_id=patch.player) }}">Player {{ patch.player }}</a>
|
<a href="{{ url_for("download_raw_patch", seed_id=seed.id, player_id=patch.player, team_id=0) }}">Player {{ patch.player }}</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue