From 25330bd730ce15fd0fac593e3d0ef5c6528a24fd Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sat, 16 Jan 2021 00:32:52 +0100 Subject: [PATCH 1/7] Fix displaying Race Seeds --- WebHostLib/templates/viewSeed.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebHostLib/templates/viewSeed.html b/WebHostLib/templates/viewSeed.html index 64ebac20..36937809 100644 --- a/WebHostLib/templates/viewSeed.html +++ b/WebHostLib/templates/viewSeed.html @@ -70,7 +70,7 @@ {% for patch in seed.patches %}
  • - Player {{ patch.player }} + Player {{ patch.player }}
  • From 65fa39df95c90c9b66141aee8b16b7e560d00819 Mon Sep 17 00:00:00 2001 From: Fischfilet89 <73077482+Fischfilet89@users.noreply.github.com> Date: Sat, 16 Jan 2021 00:38:55 +0100 Subject: [PATCH 2/7] Update weightedSettings.json (#162) fixed a typo at line 1020 regarding enemy health on the setting "armor-plated" --- WebHostLib/static/static/weightedSettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebHostLib/static/static/weightedSettings.json b/WebHostLib/static/static/weightedSettings.json index e6ee539f..0984d39e 100644 --- a/WebHostLib/static/static/weightedSettings.json +++ b/WebHostLib/static/static/weightedSettings.json @@ -1017,7 +1017,7 @@ "expert": { "keyString": "enemy_health.expert", "friendlyName": "Armor-Plated", - "description": "Enemies will be very heard to defeat.", + "description": "Enemies will be very hard to defeat.", "defaultValue": 0 } } From 3b5ba161dea223b96e9b1fc890e03469d9c6eb59 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 17 Jan 2021 02:15:46 +0100 Subject: [PATCH 3/7] first two plando modules documented --- Mystery.py | 4 +- .../assets/tutorial/zelda3/plando_en.md | 77 +++++++++++++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 WebHostLib/static/assets/tutorial/zelda3/plando_en.md diff --git a/Mystery.py b/Mystery.py index 5cc51850..0e0ab39e 100644 --- a/Mystery.py +++ b/Mystery.py @@ -587,9 +587,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.") random.shuffle(items) random.shuffle(locations) - while items and locations: - item = items.pop() - location = locations.pop() + for item, location in zip(items, locations): add_plando_item(item, location) else: item = get_choice("item", placement, get_choice("items", placement)) diff --git a/WebHostLib/static/assets/tutorial/zelda3/plando_en.md b/WebHostLib/static/assets/tutorial/zelda3/plando_en.md new file mode 100644 index 00000000..08cff395 --- /dev/null +++ b/WebHostLib/static/assets/tutorial/zelda3/plando_en.md @@ -0,0 +1,77 @@ +# 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 + + + +### Text +- 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 +``` +![Uncle Example](https://cdn.discordapp.com/attachments/731214280439103580/794953870903083058/unknown.png) +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. From 446893c5049484a0270db7ec4cb164211c2d3b51 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 17 Jan 2021 17:58:58 +0100 Subject: [PATCH 4/7] Document Item Plando --- Mystery.py | 8 +- .../static/assets/tutorial/tutorials.json | 14 ++++ .../assets/tutorial/zelda3/plando_en.md | 79 ++++++++++++++++--- 3 files changed, 87 insertions(+), 14 deletions(-) diff --git a/Mystery.py b/Mystery.py index 0e0ab39e..9bfcea8a 100644 --- a/Mystery.py +++ b/Mystery.py @@ -561,7 +561,7 @@ def roll_settings(weights, plando_options: typing.Set[str] = frozenset(("bosses" ret.plando_items = [] if "items" in plando_options: - default_placement = PlandoItem(item="", location="") + def add_plando_item(item: str, location: str): if item not in item_table: raise Exception(f"Could not plando item {item} as the item was not recognized") @@ -572,9 +572,9 @@ def roll_settings(weights, plando_options: typing.Set[str] = frozenset(("bosses" options = weights.get("plando_items", []) for placement in options: if roll_percentage(get_choice("percentage", placement, 100)): - from_pool = get_choice("from_pool", placement, default_placement.from_pool) - location_world = get_choice("world", placement, default_placement.world) - force = get_choice("force", placement, default_placement.force) + from_pool = get_choice("from_pool", placement, PlandoItem.from_pool) + location_world = get_choice("world", placement, PlandoItem.world) + force = get_choice("force", placement, PlandoItem.force) if "items" in placement and "locations" in placement: items = placement["items"] locations = placement["locations"] diff --git a/WebHostLib/static/assets/tutorial/tutorials.json b/WebHostLib/static/assets/tutorial/tutorials.json index 6b91343a..a1d659fa 100644 --- a/WebHostLib/static/assets/tutorial/tutorials.json +++ b/WebHostLib/static/assets/tutorial/tutorials.json @@ -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" + ] + } + ] } ] } diff --git a/WebHostLib/static/assets/tutorial/zelda3/plando_en.md b/WebHostLib/static/assets/tutorial/zelda3/plando_en.md index 08cff395..46713c0e 100644 --- a/WebHostLib/static/assets/tutorial/zelda3/plando_en.md +++ b/WebHostLib/static/assets/tutorial/zelda3/plando_en.md @@ -18,7 +18,7 @@ it defaults to vanilla - Instructions are separated by a semicolon - Available Instructions: - - Direct Placement: + - 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" @@ -47,20 +47,79 @@ boss_shuffle: 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 + - 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) -### Text +#### 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) +- 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) + - can be weighted. + - [List of targets](https://github.com/Berserker66/MultiWorld-Utilities/blob/65fa39df95c90c9b66141aee8b16b7e560d00819/Text.py#L1498) #### Example ```yaml From 26314f7c13a581e5ba9c80eb879e0eaada4243e1 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 17 Jan 2021 18:28:10 +0100 Subject: [PATCH 5/7] Document Connection Plando and show where Connection Plando Errors come from --- EntranceShuffle.py | 5 +- .../assets/tutorial/zelda3/plando_en.md | 95 ++++++++++++------- 2 files changed, 66 insertions(+), 34 deletions(-) diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 96dfb583..f4c68d1c 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -2251,7 +2251,10 @@ def plando_connect(world, player: int): if world.plando_connections[player]: for connection in world.plando_connections[player]: 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)', diff --git a/WebHostLib/static/assets/tutorial/zelda3/plando_en.md b/WebHostLib/static/assets/tutorial/zelda3/plando_en.md index 46713c0e..626485a9 100644 --- a/WebHostLib/static/assets/tutorial/zelda3/plando_en.md +++ b/WebHostLib/static/assets/tutorial/zelda3/plando_en.md @@ -18,17 +18,17 @@ 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. + - 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) @@ -52,25 +52,25 @@ boss_shuffle: - 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% + - 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 + - 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 - - can be null, to target a random world + - 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 + - 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 + - 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 + - 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) @@ -112,14 +112,14 @@ The second example always triggers and places the Swords and Bows into one's own - 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) + - 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) + - can be weighted. + - [List of targets](https://github.com/Berserker66/MultiWorld-Utilities/blob/65fa39df95c90c9b66141aee8b16b7e560d00819/Text.py#L1498) #### Example ```yaml @@ -134,3 +134,32 @@ plando_texts: 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 \ No newline at end of file From 0286a817551d662b41045de08ab1f1398e995f39 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 17 Jan 2021 21:42:28 +0100 Subject: [PATCH 6/7] plando world false is default --- WebHostLib/static/assets/tutorial/zelda3/plando_en.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebHostLib/static/assets/tutorial/zelda3/plando_en.md b/WebHostLib/static/assets/tutorial/zelda3/plando_en.md index 626485a9..493ffb59 100644 --- a/WebHostLib/static/assets/tutorial/zelda3/plando_en.md +++ b/WebHostLib/static/assets/tutorial/zelda3/plando_en.md @@ -60,7 +60,7 @@ boss_shuffle: - 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 + - 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 From a87a79eb005132711c8d4b706e12484ce101f678 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 17 Jan 2021 22:08:28 +0100 Subject: [PATCH 7/7] remove some duplicate code --- Fill.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Fill.py b/Fill.py index 47038a31..0c694898 100644 --- a/Fill.py +++ b/Fill.py @@ -257,6 +257,11 @@ def balance_multiworld_progression(world): 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): sphere_state.sweep_for_events(key_only=True, locations=locations) return [loc for loc in locations if sphere_state.can_reach(loc)] @@ -279,9 +284,7 @@ def balance_multiworld_progression(world): candidate_items = [] while True: for location in balancing_sphere: - if location.event and ( - world.keyshuffle[location.item.player] or not location.item.smallkey) and ( - world.bigkeyshuffle[location.item.player] or not location.item.bigkey): + if event_key(location): balancing_state.collect(location.item, True, location) if location.item.player in balancing_players and not location.locked: candidate_items.append(location) @@ -342,8 +345,7 @@ def balance_multiworld_progression(world): sphere_locations.append(location) for location in sphere_locations: - if location.event and (world.keyshuffle[location.item.player] or not location.item.smallkey) and ( - world.bigkeyshuffle[location.item.player] or not location.item.bigkey): + if event_key(location): state.collect(location.item, True, location) checked_locations.extend(sphere_locations)