From 536845186754a9ae56e9d4a4fae97d661e272691 Mon Sep 17 00:00:00 2001 From: Mathx2 Date: Thu, 7 Sep 2023 13:23:42 -0700 Subject: [PATCH 001/144] Timespinner: Options.py Typo (#2154) Line 63, changed the commend from (Reccomended) to (Recommended) --- worlds/timespinner/Options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/timespinner/Options.py b/worlds/timespinner/Options.py index 5f4d230688..8b11184944 100644 --- a/worlds/timespinner/Options.py +++ b/worlds/timespinner/Options.py @@ -60,7 +60,7 @@ class BossRando(Toggle): class BossScaling(DefaultOnToggle): - "When Boss Rando is enabled, scales the bosses' HP, XP, and ATK to the stats of the location they replace (Reccomended)" + "When Boss Rando is enabled, scales the bosses' HP, XP, and ATK to the stats of the location they replace (Recommended)" display_name = "Scale Random Boss Stats" From 2b9e8fa273ceee19c59139e8744bb3a905c33e7b Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sat, 9 Sep 2023 05:02:05 +0200 Subject: [PATCH 002/144] WebHost: flask caching doesn't do lazy init anymore (#2155) --- WebHost.py | 3 ++- WebHostLib/__init__.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/WebHost.py b/WebHost.py index 45d017cf1f..36645ad27d 100644 --- a/WebHost.py +++ b/WebHost.py @@ -14,7 +14,7 @@ import settings Utils.local_path.cached_path = os.path.dirname(__file__) or "." # py3.8 is not abs. remove "." when dropping 3.8 -from WebHostLib import register, app as raw_app +from WebHostLib import register, cache, app as raw_app from waitress import serve from WebHostLib.models import db @@ -40,6 +40,7 @@ def get_app(): app.config["HOST_ADDRESS"] = Utils.get_public_ipv4() logging.info(f"HOST_ADDRESS was set to {app.config['HOST_ADDRESS']}") + cache.init_app(app) db.bind(**app.config["PONY"]) db.generate_mapping(create_tables=True) return app diff --git a/WebHostLib/__init__.py b/WebHostLib/__init__.py index a59e3aa553..441f3272fd 100644 --- a/WebHostLib/__init__.py +++ b/WebHostLib/__init__.py @@ -49,11 +49,11 @@ app.config["PONY"] = { 'create_db': True } app.config["MAX_ROLL"] = 20 -app.config["CACHE_TYPE"] = "flask_caching.backends.SimpleCache" +app.config["CACHE_TYPE"] = "SimpleCache" app.config["JSON_AS_ASCII"] = False app.config["HOST_ADDRESS"] = "" -cache = Cache(app) +cache = Cache() Compress(app) From f6dafa2b560ff1f33070903ff0655d7340d9dfd2 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sat, 9 Sep 2023 05:02:53 +0200 Subject: [PATCH 003/144] Core: collect errors from generate_output at same step as multidata --- Main.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Main.py b/Main.py index fe56dc7d9e..860be6347c 100644 --- a/Main.py +++ b/Main.py @@ -392,7 +392,7 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No f.write(bytes([3])) # version of format f.write(multidata) - multidata_task = pool.submit(write_multidata) + output_file_futures.append(pool.submit(write_multidata)) if not check_accessibility_task.result(): if not world.can_beat_game(): raise Exception("Game appears as unbeatable. Aborting.") @@ -400,7 +400,6 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No logger.warning("Location Accessibility requirements not fulfilled.") # retrieve exceptions via .result() if they occurred. - multidata_task.result() for i, future in enumerate(concurrent.futures.as_completed(output_file_futures), start=1): if i % 10 == 0 or i == len(output_file_futures): logger.info(f'Generating output files ({i}/{len(output_file_futures)}).') From 29f8053d6ed256f87963ce17815dec9ea2484d85 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 10 Sep 2023 00:33:36 +0200 Subject: [PATCH 004/144] Factorio: fix website multitracker (#2126) Co-authored-by: Remy Jette --- .../templates/multiFactorioTracker.html | 16 +++--- WebHostLib/tracker.py | 50 +++++++++++++------ 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/WebHostLib/templates/multiFactorioTracker.html b/WebHostLib/templates/multiFactorioTracker.html index e8fa7b152c..faca756ee9 100644 --- a/WebHostLib/templates/multiFactorioTracker.html +++ b/WebHostLib/templates/multiFactorioTracker.html @@ -27,14 +27,14 @@ {% endblock %} {% block custom_table_row scoped %} {% if games[player] == "Factorio" %} -{% set player_inventory = inventory[team][player] %} -{% set prog_science = player_inventory[custom_items["progressive-science-pack"]] %} -{% if player_inventory[custom_items["logistic-science-pack"]] or prog_science %}✔{% endif %} -{% if player_inventory[custom_items["military-science-pack"]] or prog_science > 1%}✔{% endif %} -{% if player_inventory[custom_items["chemical-science-pack"]] or prog_science > 2%}✔{% endif %} -{% if player_inventory[custom_items["production-science-pack"]] or prog_science > 3%}✔{% endif %} -{% if player_inventory[custom_items["utility-science-pack"]] or prog_science > 4%}✔{% endif %} -{% if player_inventory[custom_items["space-science-pack"]] or prog_science > 5%}✔{% endif %} +{% set player_inventory = named_inventory[team][player] %} +{% set prog_science = player_inventory["progressive-science-pack"] %} +{% if player_inventory["logistic-science-pack"] or prog_science %}✔{% endif %} +{% if player_inventory["military-science-pack"] or prog_science > 1%}✔{% endif %} +{% if player_inventory["chemical-science-pack"] or prog_science > 2%}✔{% endif %} +{% if player_inventory["production-science-pack"] or prog_science > 3%}✔{% endif %} +{% if player_inventory["utility-science-pack"] or prog_science > 4%}✔{% endif %} +{% if player_inventory["space-science-pack"] or prog_science > 5%}✔{% endif %} {% else %} ❌ ❌ diff --git a/WebHostLib/tracker.py b/WebHostLib/tracker.py index 80ccc720a3..5b89495ecc 100644 --- a/WebHostLib/tracker.py +++ b/WebHostLib/tracker.py @@ -11,7 +11,7 @@ from werkzeug.exceptions import abort from MultiServer import Context, get_saving_second from NetUtils import SlotType, NetworkSlot from Utils import restricted_loads -from worlds import lookup_any_item_id_to_name, lookup_any_location_id_to_name, network_data_package +from worlds import lookup_any_item_id_to_name, lookup_any_location_id_to_name, network_data_package, games from worlds.alttp import Items from . import app, cache from .models import GameDataPackage, Room @@ -1423,9 +1423,12 @@ def _get_multiworld_tracker_data(tracker: UUID) -> typing.Optional[typing.Dict[s ) -def _get_inventory_data(data: typing.Dict[str, typing.Any]) -> typing.Dict[int, typing.Dict[int, int]]: - inventory = {teamnumber: {playernumber: collections.Counter() for playernumber in team_data} - for teamnumber, team_data in data["checks_done"].items()} +def _get_inventory_data(data: typing.Dict[str, typing.Any]) \ + -> typing.Dict[int, typing.Dict[int, typing.Dict[int, int]]]: + inventory: typing.Dict[int, typing.Dict[int, typing.Dict[int, int]]] = { + teamnumber: {playernumber: collections.Counter() for playernumber in team_data} + for teamnumber, team_data in data["checks_done"].items() + } groups = data["groups"] @@ -1444,6 +1447,17 @@ def _get_inventory_data(data: typing.Dict[str, typing.Any]) -> typing.Dict[int, return inventory +def _get_named_inventory(inventory: typing.Dict[int, int], custom_items: typing.Dict[int, str] = None) \ + -> typing.Dict[str, int]: + """slow""" + if custom_items: + mapping = collections.ChainMap(custom_items, lookup_any_item_id_to_name) + else: + mapping = lookup_any_item_id_to_name + + return collections.Counter({mapping.get(item_id, None): count for item_id, count in inventory.items()}) + + @app.route('/tracker/') @cache.memoize(timeout=60) # multisave is currently created at most every minute def get_multiworld_tracker(tracker: UUID): @@ -1455,18 +1469,22 @@ def get_multiworld_tracker(tracker: UUID): return render_template("multiTracker.html", **data) +if "Factorio" in games: + @app.route('/tracker//Factorio') + @cache.memoize(timeout=60) # multisave is currently created at most every minute + def get_Factorio_multiworld_tracker(tracker: UUID): + data = _get_multiworld_tracker_data(tracker) + if not data: + abort(404) -@app.route('/tracker//Factorio') -@cache.memoize(timeout=60) # multisave is currently created at most every minute -def get_Factorio_multiworld_tracker(tracker: UUID): - data = _get_multiworld_tracker_data(tracker) - if not data: - abort(404) + data["inventory"] = _get_inventory_data(data) + data["named_inventory"] = {team_id : { + player_id: _get_named_inventory(inventory, data["custom_items"]) + for player_id, inventory in team_inventory.items() + } for team_id, team_inventory in data["inventory"].items()} + data["enabled_multiworld_trackers"] = get_enabled_multiworld_trackers(data["room"], "Factorio") - data["inventory"] = _get_inventory_data(data) - data["enabled_multiworld_trackers"] = get_enabled_multiworld_trackers(data["room"], "Factorio") - - return render_template("multiFactorioTracker.html", **data) + return render_template("multiFactorioTracker.html", **data) @app.route('/tracker//A Link to the Past') @@ -1596,5 +1614,7 @@ game_specific_trackers: typing.Dict[str, typing.Callable] = { multi_trackers: typing.Dict[str, typing.Callable] = { "A Link to the Past": get_LttP_multiworld_tracker, - "Factorio": get_Factorio_multiworld_tracker, } + +if "Factorio" in games: + multi_trackers["Factorio"] = get_Factorio_multiworld_tracker From a1418ccb66c5e8814e61ddb41469dd5ea307d4ea Mon Sep 17 00:00:00 2001 From: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> Date: Sat, 9 Sep 2023 21:30:03 -0400 Subject: [PATCH 005/144] Docs: Small typo and proofreading edits (#2078) * Slight rewording of DS3 game page Lists made more concise, space added between "generated weapons" and open parenthesis * Proofread Final Fantasy pages Fixed minor typos and reworded sentences for conciseness. * Edited Kingdom Hearts 2 Game Page Refined style, capitalization, and sentence structure for clarity * Fixed nested list in Minecraft game page Each nest needed an additional 2 spaces * Edited Risk of Rain 2 Game Page Made various edits to redundancy within the page as well as omitted/unclear information * Edited Stardew Valley game page Small capitalization consistency edits and slight rewording for conciseness * Update worlds/ff1/docs/multiworld_en.md Co-authored-by: Silvris <58583688+Silvris@users.noreply.github.com> * Update worlds/kh2/docs/en_Kingdom Hearts 2.md Co-authored-by: Silvris <58583688+Silvris@users.noreply.github.com> * Update worlds/kh2/docs/en_Kingdom Hearts 2.md Co-authored-by: Silvris <58583688+Silvris@users.noreply.github.com> * Add information for EXP multiplier Include Drive Forms and Summons * Correction for Newt Altars RoR2 Co-Authored-By: kindasneaki <19377912+kindasneaki@users.noreply.github.com> --------- Co-authored-by: Silvris <58583688+Silvris@users.noreply.github.com> Co-authored-by: kindasneaki <19377912+kindasneaki@users.noreply.github.com> --- worlds/dark_souls_3/docs/en_Dark Souls III.md | 15 +- worlds/ff1/docs/en_Final Fantasy.md | 18 +-- worlds/ff1/docs/multiworld_en.md | 8 +- worlds/kh2/docs/en_Kingdom Hearts 2.md | 29 ++-- worlds/minecraft/docs/en_Minecraft.md | 128 +++++++++--------- worlds/ror2/docs/en_Risk of Rain 2.md | 55 ++++---- .../stardew_valley/docs/en_Stardew Valley.md | 14 +- 7 files changed, 138 insertions(+), 129 deletions(-) diff --git a/worlds/dark_souls_3/docs/en_Dark Souls III.md b/worlds/dark_souls_3/docs/en_Dark Souls III.md index 3ad8236ccf..1b55593bad 100644 --- a/worlds/dark_souls_3/docs/en_Dark Souls III.md +++ b/worlds/dark_souls_3/docs/en_Dark Souls III.md @@ -7,19 +7,18 @@ config file. ## What does randomization do to this game? -In Dark Souls III, all unique items you can earn from a static corpse, a chest or the death of a Boss/NPC are +In Dark Souls III, all unique items you can earn from a static corpse, chest, or the death of a Boss/NPC are randomized. -An option is available from the settings page to also randomize the upgrade materials, the Estus shards and the -consumables. -Another option is available to randomize the level of the generated weapons(from +0 to +10/+5) +An option is available from the settings page to also randomize upgrade materials, Estus shards, and consumables. +Another option is available to randomize the level of the generated weapons (from +0 to +10/+5). -To beat the game you need to collect the 4 "Cinders of a Lord" randomized in the multiworld -and kill the final boss "Soul of Cinder" +To beat the game, you need to collect the 4 "Cinders of a Lord" randomized in the multiworld +and kill the final boss "Soul of Cinder." ## What Dark Souls III items can appear in other players' worlds? -Every unique item from Dark Souls III can appear in other player's worlds, such as a piece of armor, an upgraded weapon, -or a key item. +Every unique item from Dark Souls III can appear in other player's worlds, such as a piece of armor, upgraded weapons, +or key items. ## What does another world's item look like in Dark Souls III? diff --git a/worlds/ff1/docs/en_Final Fantasy.md b/worlds/ff1/docs/en_Final Fantasy.md index a62b5ec126..29d4d29f80 100644 --- a/worlds/ff1/docs/en_Final Fantasy.md +++ b/worlds/ff1/docs/en_Final Fantasy.md @@ -8,19 +8,19 @@ website: [FF1R Website](https://finalfantasyrandomizer.com/) ## What does randomization do to this game? -A better questions is what isn't randomized at this point. Enemies stats and spell, character spells, shop inventory and -boss stats and spells are all commonly randomized. Unlike most other randomizers it is also most standard to shuffle -progression items and non-progression items into separate pools and then redistribute them to their respective -locations. So, for example, Princess Sarah may have the CANOE instead of the LUTE; however, she will never have a Heal -Pot or some armor. There are plenty of other things that can be randomized on the main randomizer -site: [FF1R Website](https://finalfantasyrandomizer.com/) +Enemy stats and spell, boss stats and spells, character spells, and shop inventories are all commonly randomized. Unlike +most other randomizers, it is standard to shuffle progression items and non-progression items into separate pools +and then redistribute them to their respective locations. For example, Princess Sarah may have the CANOE instead +of the LUTE; however, she will never have a Heal Pot or armor. + +Plenty of other things to be randomized can be found on the main randomizer site: +[FF1R Website](https://finalfantasyrandomizer.com/) ## What Final Fantasy items can appear in other players' worlds? -All items can appear in other players worlds. This includes consumables, shards, weapons, armor and, of course, key -items. +All items can appear in other players worlds, including consumables, shards, weapons, armor, and key items. ## What does another world's item look like in Final Fantasy -All local and remote items appear the same. It will say that you received an item and then BOTH the client log and the +All local and remote items appear the same. Final Fantasy will say that you received an item, then BOTH the client log and the emulator will display what was found external to the in-game text box. diff --git a/worlds/ff1/docs/multiworld_en.md b/worlds/ff1/docs/multiworld_en.md index 51fcd9b7bf..d3dc457f01 100644 --- a/worlds/ff1/docs/multiworld_en.md +++ b/worlds/ff1/docs/multiworld_en.md @@ -32,14 +32,14 @@ Generate a game by going to the site and performing the following steps: prefer, or it is your first time we suggest starting with the 'Shard Hunt' preset (which requires you to collect a number of shards to go to the end dungeon) or the 'Beginner' preset if you prefer to kill the original fiends. 2. Go to the `Goal` tab and ensure `Archipelago` is enabled. Set your player name to any name that represents you. -3. Upload you `Final Fantasy(USA).nes` (and click `Remember ROM` for the future!) +3. Upload your `Final Fantasy(USA).nes` (and click `Remember ROM` for the future!) 4. Press the `NEW` button beside `Seed` a few times 5. Click `GENERATE ROM` -It should download two files. One is the `*.nes` file which your emulator will run and the other is the yaml file +It should download two files. One is the `*.nes` file which your emulator will run, and the other is the yaml file required by Archipelago.gg -At this point you are ready to join the multiworld. If you are uncertain on how to generate, host or join a multiworld +At this point, you are ready to join the multiworld. If you are uncertain on how to generate, host, or join a multiworld, please refer to the [game agnostic setup guide](/tutorial/Archipelago/setup/en). ## Running the Client Program and Connecting to the Server @@ -67,7 +67,7 @@ Once the Archipelago server has been hosted: ## Play the game -When the client shows both NES and server are connected you are good to go. You can check the connection status of the +When the client shows both NES and server are connected, you are good to go. You can check the connection status of the NES at any time by running `/nes` ### Other Client Commands diff --git a/worlds/kh2/docs/en_Kingdom Hearts 2.md b/worlds/kh2/docs/en_Kingdom Hearts 2.md index d132b29ca4..8258a099cc 100644 --- a/worlds/kh2/docs/en_Kingdom Hearts 2.md +++ b/worlds/kh2/docs/en_Kingdom Hearts 2.md @@ -2,7 +2,7 @@

Changes from the vanilla game

-This randomizer takes Kingdom Hearts 2 and randomizes the locations of the items for a more dynamic play experience. The items that randomize currently are all items within Chests, Popups, Get Bonuses, Form Levels, and Sora's Levels. This allows abilities that Sora would normally have to also be placed on Keyblades with random stats. With several options on ways to finish the game. +This randomizer creates a more dynamic play experience by randomizing the locations of most items in Kingdom Hearts 2. Currently all items within Chests, Popups, Get Bonuses, Form Levels, and Sora's Levels are randomized. This allows abilities that Sora would normally have to be placed on Keyblades with random stats. Additionally, there are several options for ways to finish the game, allowing for different goals beyond beating the final boss.

Where is the settings page

@@ -12,12 +12,18 @@ The [player settings page for this game](../player-settings) contains all the op

What is randomized in this game?

-The Chests, Popups, Get Bonuses, Form Levels, and Sora's Levels. +- Chests +- Popups +- Get Bonuses +- Form Levels +- Sora's Levels +- Keyblade Stats +- Keyblade Abilities

What Kingdom Hearts 2 items can appear in other players' worlds?

-Every item in the game with the exception being party members' abilities. +Every item in the game except for party members' abilities.

What is The Garden of Assemblage "GoA"?

@@ -37,10 +43,10 @@ It is added to your inventory. If you obtain magic, you will need to pause your

What Happens if I die before Room Saving?

-When you die in Kingdom Hearts 2, you are reverted to the last non-boss room you entered and your status is reverted to what it was at that time. However, in archipelago, any item that you have sent/received will not be taken away from the player, any chest you have opened will remain open, and you will keep your level but lose the expereince. Unlike vanilla Kingdom Hearts 2. +When you die in vanilla Kingdom Hearts 2, you are reverted to the last non-boss room you entered and your status is reverted to what it was at that time. However, in archipelago, any item that you have sent/received will not be taken away from the player, any chest you have opened will remain open, and you will keep your level, but lose the experience. -For example, if you are fighting Roxas and you receive Reflect Element and you die fighting Roxas, you will keep that reflect. You will still need to pause your game to have it show up in your inventory, then enter a new room for it to become properly usable. +For example, if you are fighting Roxas, receive Reflect Element, then die mid-fight, you will keep that Reflect Element. You will still need to pause your game to have it show up in your inventory, then enter a new room for it to become properly usable.

Customization options:

@@ -49,13 +55,12 @@ For example, if you are fighting Roxas and you receive Reflect Element and you d 1. Obtain Three Proofs. 2. Obtain a desired amount of Lucky Emblems. 3. Obtain a desired amount of Bounties that are on late locations. -- Customize how many World Locking Items You Need to Progress in that World. -- Customize the Amount of World Locking Items You Start With. -- Customize how many locations you want on Sora's Levels. -- Customize the EXP Multiplier of everything that affects Sora. -- Customize the Available Abilities on Keyblades. -- Customize the level of Progressive Movement (Growth Abilities) you start with. -- Customize the amount of Progressive Movement (Growth Abilities) you start with. +- Customize how many World-Locking Items you need to progress in that world. +- Customize the amount of World-Locking Items you start with. +- Customize how many of Sora's Levels are locations. +- Customize the EXP multiplier for Sora, his Drive Forms, and his Summons. +- Customize the available abilities on keyblades. +- Customize the amount and level of progressive movement (Growth Abilities) you start with. - Customize start inventory, i.e., begin every run with certain items or spells of your choice.

Quality of life:

diff --git a/worlds/minecraft/docs/en_Minecraft.md b/worlds/minecraft/docs/en_Minecraft.md index 2d4f063b79..1ef347983b 100644 --- a/worlds/minecraft/docs/en_Minecraft.md +++ b/worlds/minecraft/docs/en_Minecraft.md @@ -29,82 +29,82 @@ sequence either by skipping it or watching hit play out. ## Which recipes are locked? * Archery - * Bow - * Arrow - * Crossbow + * Bow + * Arrow + * Crossbow * Brewing - * Blaze Powder - * Brewing Stand + * Blaze Powder + * Brewing Stand * Enchanting - * Enchanting Table - * Bookshelf + * Enchanting Table + * Bookshelf * Bucket * Flint & Steel * All Beds * Bottles * Shield * Fishing Rod - * Fishing Rod - * Carrot on a Stick - * Warped Fungus on a Stick + * Fishing Rod + * Carrot on a Stick + * Warped Fungus on a Stick * Campfire - * Campfire - * Soul Campfire + * Campfire + * Soul Campfire * Spyglass * Lead * Progressive Weapons - * Tier I - * Stone Sword - * Stone Axe - * Tier II - * Iron Sword - * Iron Axe - * Tier III - * Diamond Sword - * Diamond Axe + * Tier I + * Stone Sword + * Stone Axe + * Tier II + * Iron Sword + * Iron Axe + * Tier III + * Diamond Sword + * Diamond Axe * Progessive Tools - * Tier I - * Stone Shovel - * Stone Hoe - * Tier II - * Iron Shovel - * Iron Hoe - * Tier III - * Diamond Shovel - * Diamond Hoe - * Netherite Ingot + * Tier I + * Stone Shovel + * Stone Hoe + * Tier II + * Iron Shovel + * Iron Hoe + * Tier III + * Diamond Shovel + * Diamond Hoe + * Netherite Ingot * Progressive Armor - * Tier I - * Iron Helmet - * Iron Chestplate - * Iron Leggings - * Iron Boots - * Tier II - * Diamond Helmet - * Diamond Chestplate - * Diamond Leggings - * Diamond Boots + * Tier I + * Iron Helmet + * Iron Chestplate + * Iron Leggings + * Iron Boots + * Tier II + * Diamond Helmet + * Diamond Chestplate + * Diamond Leggings + * Diamond Boots * Progressive Resource Crafting - * Tier I - * Iron Ingot from Nuggets - * Iron Nugget - * Gold Ingot from Nuggets - * Gold Nugget - * Furnace - * Blast Furnace - * Tier II - * Redstone - * Redstone Block - * Glowstone - * Iron Ingot from Iron Block - * Iron Block - * Gold Ingot from Gold Block - * Gold Block - * Diamond - * Diamond Block - * Netherite Block - * Netherite Ingot from Netherite Block - * Anvil - * Emerald - * Emerald Block - * Copper Block + * Tier I + * Iron Ingot from Nuggets + * Iron Nugget + * Gold Ingot from Nuggets + * Gold Nugget + * Furnace + * Blast Furnace + * Tier II + * Redstone + * Redstone Block + * Glowstone + * Iron Ingot from Iron Block + * Iron Block + * Gold Ingot from Gold Block + * Gold Block + * Diamond + * Diamond Block + * Netherite Block + * Netherite Ingot from Netherite Block + * Anvil + * Emerald + * Emerald Block + * Copper Block diff --git a/worlds/ror2/docs/en_Risk of Rain 2.md b/worlds/ror2/docs/en_Risk of Rain 2.md index ca22d1a44d..d30edf8889 100644 --- a/worlds/ror2/docs/en_Risk of Rain 2.md +++ b/worlds/ror2/docs/en_Risk of Rain 2.md @@ -8,7 +8,7 @@ config file. ## What does randomization do to this game? Risk of Rain is already a random game, by virtue of being a roguelite. The Archipelago mod implements pure multiworld -functionality in which certain chests (made clear via a location check progress bar) will send an item out to the +functionality in which certain chests will send an item out to the multiworld. The items that _would have been_ in those chests will be returned to the Risk of Rain player via grants by other players in other worlds. @@ -16,28 +16,30 @@ There are two modes in risk of rain. Classic Mode and Explore Mode Classic Mode: - - Classic mode implements pure multiworld -functionality in which certain chests (made clear via a location check progress bar) will send an item out to the -multiworld. The items that _would have been_ in those chests will be returned to the Risk of Rain player via grants by -other players in other worlds. + - Certain chests (made clear via a location check progress bar) will send an item out to the + multiworld. The location of these chests do not matter, since all environments share a unified location pool. Explore Mode: - - Just like in Classic mode chests will send out an item to the multiworld. The difference is that each environment - will have a set amount that can be sent out and shrines along with other things that will need to be checked. - Also, each environment is an item and, you'll need it to be able to access it. + - Chests will continue to work as they did in Classic Mode, the difference being that each environment + will have a set amount of items that can be sent out. In addition, shrines, radio scanners, newt altars, + and scavenger bags will need to be checked, depending on your settings. + This mode also makes each environment an item. In order to access a particular stage, you'll need it to be + sent in the multiworld. ## What is the goal of Risk of Rain 2 in Archipelago? -Just like in the original game, any way to "beat the game" counts as a win. Alternatively, if you are new to the game and +Just like in the original game, any way to "beat the game" counts as a win. This means beating one of the bosses +on Commencement, The Planetarium, or A Moment, Whole. Alternatively, if you are new to the game and aren't very confident in being able to "beat the game", you can set **Final Stage Death is Win** to true -(You can turn this on in your player settings.) This will make it so if you die on either Commencement or The Planetarium, -it will count as your goal, and **Obliterating yourself** will count as well. +(You can turn this on in your player settings.) This will make it so dying on either Commencement or The Planetarium, +or **obliterating yourself in A Moment, Fractured** will count as your goal. **You do not need to complete all the location checks** to win; any item you don't collect may be released if the server options allow. If you die before you accomplish your goal, you can start a new run. You will start the run with any items that you -received from other players. Any items that you picked up the "normal" way will be lost. +received from other players. However, these items will be randomized within their rarity at the start of each run. +Any items that you picked up the "normal" way will be lost. Note, you can play Simulacrum mode as part of an Archipelago, but you can't achieve any of the victory conditions in Simulacrum. So you could, for example, collect most of your items through a Simulacrum run(only works in classic mode), @@ -72,10 +74,10 @@ The Risk of Rain items are: Each item grants you a random in-game item from the category it belongs to. -When an item is granted by another world to the Risk of Rain player (one of the items listed above) then a random +When an item is granted by another world to the Risk of Rain player then a random in-game item of that tier will appear in the Risk of Rain player's inventory. If the item grant is an `Equipment` and -the player already has an equipment item equipped then the _item that was equipped_ will be dropped on the ground and _ -the new equipment_ will take it's place. (If you want the old one back, pick it up.) +the player already has an equipment item equipped then the _item that was equipped_ will be dropped on the ground and +_the new equipment_ will take it's place. Explore Mode items are: @@ -93,10 +95,10 @@ Dlc_Sotv items * `Sulfur Pools` * `Void Locus` -When a explore item is granted it will unlock that environment and will now be accessible to progress to victory! The -game will still pick randomly which environment is next but it will first check to see if they are available. If you have -them unlocked it will weight the game to have a ***higher chance*** to go to one you have checks versus one you have -already completed. You will still not be able to goto a stage 3 environment from a stage 1 environment. +When an explore item is granted, it will unlock that environment and will now be accessible! The +game will still pick randomly which environment is next, but it will first check to see if they are available. If you have +multiple of the next environments unlocked, it will weight the game to have a ***higher chance*** to go to one you +have checks in versus one you have already completed. You will still be unable to go to a stage 3 environment from a stage 1 environment. @@ -108,9 +110,9 @@ to 250** items. The number of items will be randomized between all players, so y item pickup step based on how many items the other players in the multiworld have. (Around 100 seems to be a good ballpark if you want to have a similar number of items to most other games.) -In explore mode the amount of checks base on how many **chests, shrines, scavengers, radio scanners and, newt altars** -are in the pool. With just the base game the numbers are **52 to 516** and with the dlc its **60 to 660** with -everything on default being **216** +In explore mode, the amount of checks are based on how many **chests, shrines, scavengers, radio scanners, and newt altars** +are in the pool. With just the base game, checks can range from **52 to 516**, with the DLC expanding it to **60 to 660**. +Leaving everything on default, the total number of checks comes out to **216** locations. After you have completed the specified number of checks, you won't send anything else to the multiworld. You can receive up to the specified number of randomized items from the multiworld as the players find them. In either case, @@ -120,12 +122,15 @@ you can continue to collect items as normal in Risk of Rain 2 if you've already When the Risk of Rain player fills up their location check bar then the next spawned item will become an item grant for another player's world (or possibly get sent back to yourself). The item in Risk of Rain will disappear in a poof of -smoke and the grant will automatically go out to the multiworld. +smoke and the grant will automatically go out to the multiworld. Additionally, you will see a message in the chat saying +what item you sent out. If the message does not appear, this likely means that another game has collected their items from you. ## What is the item pickup step? -The item pickup step is a YAML setting which allows you to set how many items you need to spawn before the _next_ item -that is spawned disappears (in a poof of smoke) and goes out to the multiworld. +The item pickup step is a setting in the YAML which allows you to set how many items you need to spawn before the _next_ item +that is spawned disappears (in a poof of smoke) and goes out to the multiworld. For instance, an item step of **1** means that +every other chest will send an item to the multiworld. An item step of **2** means that every third chest sends out an item +just as an item step of **0** would send an item on **each chest.** ## Is Archipelago compatible with other Risk of Rain 2 mods? diff --git a/worlds/stardew_valley/docs/en_Stardew Valley.md b/worlds/stardew_valley/docs/en_Stardew Valley.md index 042163343e..a880a40b97 100644 --- a/worlds/stardew_valley/docs/en_Stardew Valley.md +++ b/worlds/stardew_valley/docs/en_Stardew Valley.md @@ -7,7 +7,7 @@ config file. ## What does randomization do to this game? -A vast number of optional objectives in stardew valley can be shuffled around the multiworld. Most of these are optional, and the player can customize their experience in their YAML file. +A vast number of objectives in Stardew Valley can be shuffled around the multiworld. Most of these are optional, and the player can customize their experience in their YAML file. For these objectives, if they have a vanilla reward, this reward will instead be an item in the multiworld. For the remaining number of such objectives, there are a number of "Resource Pack" items, which are simply an item or a stack of items that may be useful to the player. @@ -28,24 +28,24 @@ The player can choose from a number of goals, using their YAML settings. Location checks in Stardew Valley always include: - [Community Center Bundles](https://stardewvalleywiki.com/Bundles) -- [Mineshaft chest rewards](https://stardewvalleywiki.com/The_Mines#Remixed_Rewards) +- [Mineshaft Chest Rewards](https://stardewvalleywiki.com/The_Mines#Remixed_Rewards) - [Story Quests](https://stardewvalleywiki.com/Quests#List_of_Story_Quests) -- [Traveling Merchant items](https://stardewvalleywiki.com/Traveling_Cart) +- [Traveling Merchant Items](https://stardewvalleywiki.com/Traveling_Cart) - Isolated objectives such as the [beach bridge](https://stardewvalleywiki.com/The_Beach#Tide_Pools), [Old Master Cannoli](https://stardewvalleywiki.com/Secret_Woods#Old_Master_Cannoli), [Grim Reaper Statue](https://stardewvalleywiki.com/Golden_Scythe), etc There also are a number of location checks that are optional, and individual players choose to include them or not in their shuffling: - Tools and Fishing Rod Upgrades - Carpenter Buildings - Backpack Upgrades -- Mine elevator levels +- Mine Elevator Levels - Skill Levels - Arcade Machines -- Help Wanted quests +- Help Wanted Quests - Participating in Festivals - Special Orders from the town board, or from Mr Qi -- Cropsanity: Growing and harvesting individual crop types +- Cropsanity: Growing and Harvesting individual crop types - Fishsanity: Catching individual fish -- Museumsanity: Donating individual items to the museum, or reaching the museum milestones for donations +- Museumsanity: Donating individual items, or reaching milestones for museum donations - Friendsanity: Reaching specific friendship levels with NPCs ## Which items can be in another player's world? From faf4887616042eafa7e890a0e430b01a08e309c8 Mon Sep 17 00:00:00 2001 From: Brooty Johnson <83629348+Br00ty@users.noreply.github.com> Date: Sat, 9 Sep 2023 21:33:57 -0400 Subject: [PATCH 006/144] DS3: add more options to slot_data for autotracking (#2148) --- worlds/dark_souls_3/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/worlds/dark_souls_3/__init__.py b/worlds/dark_souls_3/__init__.py index faf6c28121..5d845e3ccc 100644 --- a/worlds/dark_souls_3/__init__.py +++ b/worlds/dark_souls_3/__init__.py @@ -502,6 +502,15 @@ class DarkSouls3World(World): slot_data = { "options": { + "enable_weapon_locations": self.multiworld.enable_weapon_locations[self.player].value, + "enable_shield_locations": self.multiworld.enable_shield_locations[self.player].value, + "enable_armor_locations": self.multiworld.enable_armor_locations[self.player].value, + "enable_ring_locations": self.multiworld.enable_ring_locations[self.player].value, + "enable_spell_locations": self.multiworld.enable_spell_locations[self.player].value, + "enable_key_locations": self.multiworld.enable_key_locations[self.player].value, + "enable_boss_locations": self.multiworld.enable_boss_locations[self.player].value, + "enable_npc_locations": self.multiworld.enable_npc_locations[self.player].value, + "enable_misc_locations": self.multiworld.enable_misc_locations[self.player].value, "auto_equip": self.multiworld.auto_equip[self.player].value, "lock_equip": self.multiworld.lock_equip[self.player].value, "no_weapon_requirements": self.multiworld.no_weapon_requirements[self.player].value, From bf685dc85045286b9984bec24401b70246286b09 Mon Sep 17 00:00:00 2001 From: Bicoloursnake <60069210+Bicoloursnake@users.noreply.github.com> Date: Sat, 9 Sep 2023 21:51:12 -0400 Subject: [PATCH 007/144] Docs, SM64, SC2: Minor Documentation Updates (#2008) * Update SC2 setup guide Removed a sentence that made sense when I included sudo in the command in the previous sentence, but does not make sense otherwise. * Update en_Super Mario 64.md It turns out castle has a lowercase l in it. --- worlds/sc2wol/docs/setup_en.md | 2 +- worlds/sm64ex/docs/en_Super Mario 64.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/worlds/sc2wol/docs/setup_en.md b/worlds/sc2wol/docs/setup_en.md index 13c7cb91e3..419f98a733 100644 --- a/worlds/sc2wol/docs/setup_en.md +++ b/worlds/sc2wol/docs/setup_en.md @@ -49,7 +49,7 @@ specific description of what's going wrong and attach your log file to your mess ## Running in macOS -To run StarCraft 2 through Archipelago in macOS, you will need to run the client via source as seen here: [macOS Guide](https://archipelago.gg/tutorial/Archipelago/mac/en). Note: when running the client, you will need to run the command `python3 Starcraft2Client.py`. This is done to make sure that `/download_data` works correctly. +To run StarCraft 2 through Archipelago in macOS, you will need to run the client via source as seen here: [macOS Guide](https://archipelago.gg/tutorial/Archipelago/mac/en). Note: when running the client, you will need to run the command `python3 Starcraft2Client.py`. ## Running in Linux diff --git a/worlds/sm64ex/docs/en_Super Mario 64.md b/worlds/sm64ex/docs/en_Super Mario 64.md index 4586369e5e..def6e2a375 100644 --- a/worlds/sm64ex/docs/en_Super Mario 64.md +++ b/worlds/sm64ex/docs/en_Super Mario 64.md @@ -14,7 +14,7 @@ as different Items from within SM64. As in most Mario Games, save the Princess! ## Which items can be in another player's world? -Any of the 120 Stars, and the two Caste Keys. Additionally, Cap Switches are also considered "Items" and the "!"-Boxes will only be active +Any of the 120 Stars, and the two Castle Keys. Additionally, Cap Switches are also considered "Items" and the "!"-Boxes will only be active when someone collects the corresponding Cap Switch Item. ## What does another world's item look like in SM64EX? @@ -25,4 +25,4 @@ and who will receive it. When you receive an Item, a Message will pop up to inform you where you received the Item from, and which one it is. -NOTE: The Secret Star count in the Menu is broken. \ No newline at end of file +NOTE: The Secret Star count in the Menu is broken. From 2bdb1b2029c76258b9101ecede4f585f22542888 Mon Sep 17 00:00:00 2001 From: Bryce Wilson Date: Sat, 9 Sep 2023 19:41:52 -0700 Subject: [PATCH 008/144] DS3: Update game page (#2163) * DS3: Update game page * DS3: Split long sentence in game page docs Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> * DS3: Minor word change --------- Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> --- worlds/dark_souls_3/docs/en_Dark Souls III.md | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/worlds/dark_souls_3/docs/en_Dark Souls III.md b/worlds/dark_souls_3/docs/en_Dark Souls III.md index 1b55593bad..e844925df1 100644 --- a/worlds/dark_souls_3/docs/en_Dark Souls III.md +++ b/worlds/dark_souls_3/docs/en_Dark Souls III.md @@ -7,19 +7,22 @@ config file. ## What does randomization do to this game? -In Dark Souls III, all unique items you can earn from a static corpse, chest, or the death of a Boss/NPC are -randomized. -An option is available from the settings page to also randomize upgrade materials, Estus shards, and consumables. -Another option is available to randomize the level of the generated weapons (from +0 to +10/+5). +Items that can be picked up from static corpses, taken from chests, or earned from defeating enemies or NPCs can be +randomized. Common pickups like titanite shards or firebombs can be randomized as "progressive" items. That is, the +location "Titanite Shard #5" is the fifth titanite shard you pick up, no matter where it was from. This is also what +happens when you randomize Estus Shards and Undead Bone Shards. -To beat the game, you need to collect the 4 "Cinders of a Lord" randomized in the multiworld -and kill the final boss "Soul of Cinder." +It's also possible to randomize the upgrade level of weapons and shields as well as their infusions (if they can have +one). Additionally, there are settings that can make the randomized experience more convenient or more interesting, such as +removing weapon requirements or auto-equipping whatever equipment you most recently received. + +The goal is to find the four "Cinders of a Lord" items randomized into the multiworld and defeat the Soul of Cinder. ## What Dark Souls III items can appear in other players' worlds? -Every unique item from Dark Souls III can appear in other player's worlds, such as a piece of armor, upgraded weapons, -or key items. +Practically anything can be found in other worlds including pieces of armor, upgraded weapons, key items, consumables, +spells, upgrade materials, etc... ## What does another world's item look like in Dark Souls III? -In Dark Souls III, items which need to be sent to other worlds appear as a Prism Stone. +In Dark Souls III, items which are sent to other worlds appear as Prism Stones. From 72b44be41c5b35205fcb2d0c40085a9a91c7463f Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 10 Sep 2023 07:19:40 +0200 Subject: [PATCH 009/144] SNIClient: fix /snes command if tree (#791) --- SNIClient.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/SNIClient.py b/SNIClient.py index 66d0b2ca9c..0909c61382 100644 --- a/SNIClient.py +++ b/SNIClient.py @@ -68,12 +68,11 @@ class SNIClientCommandProcessor(ClientCommandProcessor): options = snes_options.split() num_options = len(options) - if num_options > 0: - snes_device_number = int(options[0]) - if num_options > 1: snes_address = options[0] snes_device_number = int(options[1]) + elif num_options > 0: + snes_device_number = int(options[0]) self.ctx.snes_reconnect_address = None if self.ctx.snes_connect_task: From e01eb4e00c6e99840f196a6b62535e7fbd95b1fa Mon Sep 17 00:00:00 2001 From: Doug Hoskisson Date: Sun, 10 Sep 2023 14:03:22 -0700 Subject: [PATCH 010/144] Zillion: webhost config fix (#2145) --- worlds/zillion/__init__.py | 4 ++-- worlds/zillion/config.py | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/worlds/zillion/__init__.py b/worlds/zillion/__init__.py index 2ea7ffdea5..7c927c10eb 100644 --- a/worlds/zillion/__init__.py +++ b/worlds/zillion/__init__.py @@ -10,6 +10,7 @@ import logging from BaseClasses import ItemClassification, LocationProgressType, \ MultiWorld, Item, CollectionState, Entrance, Tutorial +from .config import detect_test from .logic import cs_to_zz_locs from .region import ZillionLocation, ZillionRegion from .options import ZillionStartChar, zillion_options, validate @@ -145,8 +146,7 @@ class ZillionWorld(World): self._item_counts = item_counts - import __main__ - rom_dir_name = "" if "test" in __main__.__file__ else os.path.dirname(get_base_rom_path()) + rom_dir_name = "" if detect_test() else os.path.dirname(get_base_rom_path()) with redirect_stdout(self.lsi): # type: ignore self.zz_system.make_patcher(rom_dir_name) self.zz_system.make_randomizer(zz_op) diff --git a/worlds/zillion/config.py b/worlds/zillion/config.py index ca02f9a99f..db61d0c453 100644 --- a/worlds/zillion/config.py +++ b/worlds/zillion/config.py @@ -2,3 +2,20 @@ import os base_id = 8675309 zillion_map = os.path.join(os.path.dirname(__file__), "empty-zillion-map-row-col-labels-281.png") + + +def detect_test() -> bool: + """ + Parts of generation that are in unit tests need the rom. + This is to detect whether we are running unit tests + so we can work around the need for the rom. + """ + import __main__ + try: + if "test" in __main__.__file__: + return True + except AttributeError: + # In some environments, __main__ doesn't have __file__ + # We'll assume that's not unit tests. + pass + return False From fbd64651e48cd1bd41436eec0fc8cf1726f308b2 Mon Sep 17 00:00:00 2001 From: Remy Jette Date: Sun, 10 Sep 2023 14:24:09 -0700 Subject: [PATCH 011/144] Pokemon RB: Fix typo on the game info page (#2142) Thanks Shiny for pointing it out https://discord.com/channels/731205301247803413/1043592720603693167/1147300361883893790 --- worlds/pokemon_rb/docs/en_Pokemon Red and Blue.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/pokemon_rb/docs/en_Pokemon Red and Blue.md b/worlds/pokemon_rb/docs/en_Pokemon Red and Blue.md index 5350541827..daefd6b2f7 100644 --- a/worlds/pokemon_rb/docs/en_Pokemon Red and Blue.md +++ b/worlds/pokemon_rb/docs/en_Pokemon Red and Blue.md @@ -20,7 +20,7 @@ Many baseline changes are made to the game, including: * PC item storage increased to 64 slots (up from 50). * You can hold B to run (or bike extra fast!). * You can hold select while talking to a trainer to re-battle them. -* You can select "Pallet Warp" below the "Continue" option to warp to Pallet Towna s you load your save. +* You can select "Pallet Warp" below the "Continue" option to warp to Pallet Town as you load your save. * Mew can be encountered at the S.S. Anne dock truck. This can be randomized depending on your settings. * The S.S. Anne will never depart. * Seafoam Islands entrances are swapped. This means you need Strength to travel through from Cinnabar Island to Fuchsia From 8649b157873b26ea9bac37b8317529f7feb73a4c Mon Sep 17 00:00:00 2001 From: Trevor L <80716066+TRPG0@users.noreply.github.com> Date: Sun, 10 Sep 2023 15:24:33 -0600 Subject: [PATCH 012/144] Blasphemous: Add missing logic (#2165) * Blasphemous: Set rules for events later * Blasphemous: More misc logic fixes * Update worlds/blasphemous/Rules.py Co-authored-by: Fabian Dill * Update worlds/blasphemous/Rules.py Co-authored-by: Fabian Dill * Blasphemous: Some cleanup * Blasphemous: Add missing logic --------- Co-authored-by: Fabian Dill --- worlds/blasphemous/Rules.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/worlds/blasphemous/Rules.py b/worlds/blasphemous/Rules.py index 2ef36c575e..277a1b15dc 100644 --- a/worlds/blasphemous/Rules.py +++ b/worlds/blasphemous/Rules.py @@ -2451,6 +2451,8 @@ def rules(blasphemousworld): # Items set_rule(world.get_location("PotSS: 4th meeting with Redento", player), lambda state: redento(state, blasphemousworld, player, 4)) + set_rule(world.get_location("PotSS: Amanecida of the Chiselled Steel", player), + lambda state: can_beat_boss(state, "Patio", logic, player)) # No doors From 6c844750aede98811c834e0e407d99ff7348dbf4 Mon Sep 17 00:00:00 2001 From: blastron Date: Sun, 10 Sep 2023 14:29:42 -0700 Subject: [PATCH 013/144] Witness: fix items being modified by other slots (#2161) --- worlds/witness/items.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/witness/items.py b/worlds/witness/items.py index 7e083534c9..82c79047f3 100644 --- a/worlds/witness/items.py +++ b/worlds/witness/items.py @@ -152,7 +152,7 @@ class WitnessPlayerItems: """ Returns the list of items that must be in the pool for the game to successfully generate. """ - return self._mandatory_items + return self._mandatory_items.copy() def get_filler_items(self, quantity: int) -> Dict[str, int]: """ From 5eef7a34d3de1b1a82607d7460abb3bd0119e828 Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Sun, 10 Sep 2023 23:34:20 +0200 Subject: [PATCH 014/144] The Witness: Fix Expert Tutorial Gate Close (#2164) --- worlds/witness/WitnessLogicExpert.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/witness/WitnessLogicExpert.txt b/worlds/witness/WitnessLogicExpert.txt index a55c22441e..b373c7417c 100644 --- a/worlds/witness/WitnessLogicExpert.txt +++ b/worlds/witness/WitnessLogicExpert.txt @@ -14,7 +14,7 @@ Tutorial (Tutorial) - Outside Tutorial - True: 158005 - 0x0A3B5 (Back Left) - True - Dots & Full Dots 158006 - 0x0A3B2 (Back Right) - True - Dots & Full Dots 158007 - 0x03629 (Gate Open) - 0x002C2 - Symmetry & Dots -158008 - 0x03505 (Gate Close) - 0x2FAF6 & 0x03629 - False +158008 - 0x03505 (Gate Close) - 0x2FAF6 & 0x03629 - True 158009 - 0x0C335 (Pillar) - True - Triangles 158010 - 0x0C373 (Patio Floor) - 0x0C335 - Dots 159512 - 0x33530 (Cloud EP) - True - True From 0e21a3e121f020bbd2b44d71de199dfd8d07cfde Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Sun, 10 Sep 2023 17:38:56 -0400 Subject: [PATCH 015/144] =?UTF-8?q?Pok=C3=A9mon=20R/B:=20Fix=20broken=20op?= =?UTF-8?q?tions=20(#2162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- worlds/pokemon_rb/__init__.py | 2 +- worlds/pokemon_rb/logic.py | 2 +- worlds/pokemon_rb/rom.py | 25 +++++++++++++------------ 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/worlds/pokemon_rb/__init__.py b/worlds/pokemon_rb/__init__.py index 64f62adddb..3d6e463251 100644 --- a/worlds/pokemon_rb/__init__.py +++ b/worlds/pokemon_rb/__init__.py @@ -138,7 +138,7 @@ class PokemonRedBlueWorld(World): if self.multiworld.key_items_only[self.player]: self.multiworld.trainersanity[self.player] = self.multiworld.trainersanity[self.player].from_text("off") - self.multiworld.dexsanity[self.player] = self.multiworld.dexsanity[self.player].from_text("false") + self.multiworld.dexsanity[self.player].value = 0 self.multiworld.randomize_hidden_items[self.player] = \ self.multiworld.randomize_hidden_items[self.player].from_text("off") diff --git a/worlds/pokemon_rb/logic.py b/worlds/pokemon_rb/logic.py index 87398c7267..cbe28e0ddb 100644 --- a/worlds/pokemon_rb/logic.py +++ b/worlds/pokemon_rb/logic.py @@ -53,7 +53,7 @@ def has_key_items(state, count, player): "Hideout Key", "Card Key 2F", "Card Key 3F", "Card Key 4F", "Card Key 5F", "Card Key 6F", "Card Key 7F", "Card Key 8F", "Card Key 9F", "Card Key 10F", "Card Key 11F", "Exp. All", "Fire Stone", "Thunder Stone", "Water Stone", - "Leaf Stone"] if state.has(item, player)]) + "Leaf Stone", "Moon Stone"] if state.has(item, player)]) + min(state.count("Progressive Card Key", player), 10)) return key_items >= count diff --git a/worlds/pokemon_rb/rom.py b/worlds/pokemon_rb/rom.py index 0757d33435..4b191d9176 100644 --- a/worlds/pokemon_rb/rom.py +++ b/worlds/pokemon_rb/rom.py @@ -238,18 +238,19 @@ def generate_output(self, output_directory: str): data[address] = 0 if "Elevator" in connected_map_name else warp_to_ids[i] data[address + 1] = map_ids[connected_map_name] - for i, gym_leader in enumerate(("Pewter Gym - Brock TM", "Cerulean Gym - Misty TM", - "Vermilion Gym - Lt. Surge TM", "Celadon Gym - Erika TM", - "Fuchsia Gym - Koga TM", "Saffron Gym - Sabrina TM", - "Cinnabar Gym - Blaine TM", "Viridian Gym - Giovanni TM")): - item_name = self.multiworld.get_location(gym_leader, self.player).item.name - if item_name.startswith("TM"): - try: - tm = int(item_name[2:4]) - move = poke_data.moves[self.local_tms[tm - 1]]["id"] - data[rom_addresses["Gym_Leader_Moves"] + (2 * i)] = move - except KeyError: - pass + if not self.multiworld.key_items_only[self.player]: + for i, gym_leader in enumerate(("Pewter Gym - Brock TM", "Cerulean Gym - Misty TM", + "Vermilion Gym - Lt. Surge TM", "Celadon Gym - Erika TM", + "Fuchsia Gym - Koga TM", "Saffron Gym - Sabrina TM", + "Cinnabar Gym - Blaine TM", "Viridian Gym - Giovanni TM")): + item_name = self.multiworld.get_location(gym_leader, self.player).item.name + if item_name.startswith("TM"): + try: + tm = int(item_name[2:4]) + move = poke_data.moves[self.local_tms[tm - 1]]["id"] + data[rom_addresses["Gym_Leader_Moves"] + (2 * i)] = move + except KeyError: + pass def set_trade_mon(address, loc): mon = self.multiworld.get_location(loc, self.player).item.name From 3e95ccd06c8955762c20b58555961244fbc09ffe Mon Sep 17 00:00:00 2001 From: Trevor L <80716066+TRPG0@users.noreply.github.com> Date: Sun, 10 Sep 2023 16:04:57 -0600 Subject: [PATCH 016/144] Blasphemous: Fixed Amanecidas not requiring Petrified Bell (#2166) --- worlds/blasphemous/Rules.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/worlds/blasphemous/Rules.py b/worlds/blasphemous/Rules.py index 277a1b15dc..248ff645bc 100644 --- a/worlds/blasphemous/Rules.py +++ b/worlds/blasphemous/Rules.py @@ -367,25 +367,25 @@ def can_beat_boss(state: CollectionState, boss: str, logic: int, player: int) -> elif boss == "Graveyard": return ( has_boss_strength("amanecida") - and state.has_all({"D01BZ07S01[Santos]", "D02Z03S23[E]", "D02Z02S14[W]", "Wall Climb Ability"}, player) + and state.has_all({"D01Z06S01[Santos]", "D02Z03S23[E]", "D02Z02S14[W]", "Wall Climb Ability"}, player) ) elif boss == "Jondo": return ( has_boss_strength("amanecida") - and state.has("D01BZ07S01[Santos]", player) + and state.has("D01Z06S01[Santos]", player) and state.has_any({"D20Z01S05[W]", "D20Z01S05[E]"}, player) and state.has_any({"D03Z01S03[W]", "D03Z01S03[SW]"}, player) ) elif boss == "Patio": return ( has_boss_strength("amanecida") - and state.has_all({"D01BZ07S01[Santos]", "D06Z01S18[E]"}, player) + and state.has_all({"D01Z06S01[Santos]", "D06Z01S18[E]"}, player) and state.has_any({"D04Z01S04[W]", "D04Z01S04[E]", "D04Z01S04[Cherubs]"}, player) ) elif boss == "Wall": return ( has_boss_strength("amanecida") - and state.has_all({"D01BZ07S01[Santos]", "D09BZ01S01[Cell24]"}, player) + and state.has_all({"D01Z06S01[Santos]", "D09BZ01S01[Cell24]"}, player) and state.has_any({"D09Z01S01[W]", "D09Z01S01[E]"}, player) ) elif boss == "Hall": From 3d9837678c9a4989fa88da1f97109995e07e0812 Mon Sep 17 00:00:00 2001 From: Rob B Date: Sun, 10 Sep 2023 17:13:39 -0500 Subject: [PATCH 017/144] Factorio: better Technology Tree Information description (#2121) * Fix typo in Factorio options tooltip * Fix typo, add details * Apply code review suggestion It doesn't let me apply more than one change to the same line in a batch. Co-authored-by: Scipio Wright * Apply code review suggestion from @nicholassaylor It doesn't let me apply more than one change to the same line in a batch. --------- Co-authored-by: Scipio Wright --- worlds/factorio/Options.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/factorio/Options.py b/worlds/factorio/Options.py index 0331c2d013..2b579658fc 100644 --- a/worlds/factorio/Options.py +++ b/worlds/factorio/Options.py @@ -146,8 +146,8 @@ class TechTreeLayout(Choice): class TechTreeInformation(Choice): """How much information should be displayed in the tech tree. - None: No indication what a research unlocks - Advancement: Indicators which researches unlock items that are considered logical advancements + None: No indication of what a research unlocks. + Advancement: Indicates if a research unlocks an item that is considered logical advancement, but not who it is for. Full: Labels with exact names and recipients of unlocked items; all researches are prefilled into the !hint command. """ display_name = "Technology Tree Information" From 57c13ff2732691c0a44b34e69742cfbdc2ad38f8 Mon Sep 17 00:00:00 2001 From: Remy Jette Date: Mon, 11 Sep 2023 13:57:14 -0700 Subject: [PATCH 018/144] WebHost: Support multi-select during check/generate file upload (#2138) * Support multi-select during check/generate file upload This will allow the user to select multiple YAML files via Shift-Click or Control-Click in their browser when generating a game via the site instead of having to zip them locally first. * Update generate.html: File -> File(s) * Change check.html button text to "Upload File(s)" to match generate.html --- WebHostLib/check.py | 47 ++++++++++++++++-------------- WebHostLib/generate.py | 4 +-- WebHostLib/templates/check.html | 4 +-- WebHostLib/templates/generate.html | 4 +-- 4 files changed, 31 insertions(+), 28 deletions(-) diff --git a/WebHostLib/check.py b/WebHostLib/check.py index 0c1e090dbe..c5dfd9f556 100644 --- a/WebHostLib/check.py +++ b/WebHostLib/check.py @@ -24,8 +24,8 @@ def check(): if 'file' not in request.files: flash('No file part') else: - file = request.files['file'] - options = get_yaml_data(file) + files = request.files.getlist('file') + options = get_yaml_data(files) if isinstance(options, str): flash(options) else: @@ -39,30 +39,33 @@ def mysterycheck(): return redirect(url_for("check"), 301) -def get_yaml_data(file) -> Union[Dict[str, str], str, Markup]: +def get_yaml_data(files) -> Union[Dict[str, str], str, Markup]: options = {} - # if user does not select file, browser also - # submit an empty part without filename - if file.filename == '': - return 'No selected file' - elif file and allowed_file(file.filename): - if file.filename.endswith(".zip"): + for file in files: + # if user does not select file, browser also + # submit an empty part without filename + if file.filename == '': + return 'No selected file' + elif file.filename in options: + return f'Conflicting files named {file.filename} submitted' + elif file and allowed_file(file.filename): + if file.filename.endswith(".zip"): - with zipfile.ZipFile(file, 'r') as zfile: - infolist = zfile.infolist() + with zipfile.ZipFile(file, 'r') as zfile: + infolist = zfile.infolist() - if any(file.filename.endswith(".archipelago") for file in infolist): - return Markup("Error: Your .zip file contains an .archipelago file. " - 'Did you mean to host a game?') + if any(file.filename.endswith(".archipelago") for file in infolist): + return Markup("Error: Your .zip file contains an .archipelago file. " + 'Did you mean to host a game?') - for file in infolist: - if file.filename.endswith(banned_zip_contents): - return "Uploaded data contained a rom file, which is likely to contain copyrighted material. " \ - "Your file was deleted." - elif file.filename.endswith((".yaml", ".json", ".yml", ".txt")): - options[file.filename] = zfile.open(file, "r").read() - else: - options = {file.filename: file.read()} + for file in infolist: + if file.filename.endswith(banned_zip_contents): + return "Uploaded data contained a rom file, which is likely to contain copyrighted material. " \ + "Your file was deleted." + elif file.filename.endswith((".yaml", ".json", ".yml", ".txt")): + options[file.filename] = zfile.open(file, "r").read() + else: + options[file.filename] = file.read() if not options: return "Did not find a .yaml file to process." return options diff --git a/WebHostLib/generate.py b/WebHostLib/generate.py index 91d7594a1f..ddcc5ffb6c 100644 --- a/WebHostLib/generate.py +++ b/WebHostLib/generate.py @@ -64,8 +64,8 @@ def generate(race=False): if 'file' not in request.files: flash('No file part') else: - file = request.files['file'] - options = get_yaml_data(file) + files = request.files.getlist('file') + options = get_yaml_data(files) if isinstance(options, str): flash(options) else: diff --git a/WebHostLib/templates/check.html b/WebHostLib/templates/check.html index 04b51340b5..8a3da7db47 100644 --- a/WebHostLib/templates/check.html +++ b/WebHostLib/templates/check.html @@ -17,9 +17,9 @@

- +
- +
diff --git a/WebHostLib/templates/generate.html b/WebHostLib/templates/generate.html index dd25a90804..33f8dbc09e 100644 --- a/WebHostLib/templates/generate.html +++ b/WebHostLib/templates/generate.html @@ -203,10 +203,10 @@ Warning: playthrough can take a significant amount of time for larger multiworld
- +
- + From 1756a30accb1b2fd8e434ab8a75c11db7ef024d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fri=C3=B0berg?= Date: Mon, 11 Sep 2023 21:17:11 +0000 Subject: [PATCH 019/144] WebHost: Clean up the exported yaml in weighted settings (#2167) * Trim output yaml in weighted options Remove options that have only one possible outcome as well as empty arrays, when building yaml. * fix quotes --- WebHostLib/static/assets/weighted-settings.js | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/WebHostLib/static/assets/weighted-settings.js b/WebHostLib/static/assets/weighted-settings.js index 6e86d470f0..07157cb579 100644 --- a/WebHostLib/static/assets/weighted-settings.js +++ b/WebHostLib/static/assets/weighted-settings.js @@ -1134,8 +1134,8 @@ const validateSettings = () => { return; } - // Remove any disabled options Object.keys(settings[game]).forEach((setting) => { + // Remove any disabled options Object.keys(settings[game][setting]).forEach((option) => { if (settings[game][setting][option] === 0) { delete settings[game][setting][option]; @@ -1149,6 +1149,32 @@ const validateSettings = () => { ) { errorMessage = `${game} // ${setting} has no values above zero!`; } + + // Remove weights from options with only one possibility + if ( + Object.keys(settings[game][setting]).length === 1 && + !Array.isArray(settings[game][setting]) && + setting !== 'start_inventory' + ) { + settings[game][setting] = Object.keys(settings[game][setting])[0]; + } + + // Remove empty arrays + else if ( + ['exclude_locations', 'priority_locations', 'local_items', + 'non_local_items', 'start_hints', 'start_location_hints'].includes(setting) && + settings[game][setting].length === 0 + ) { + delete settings[game][setting]; + } + + // Remove empty start inventory + else if ( + setting === 'start_inventory' && + Object.keys(settings[game]['start_inventory']).length === 0 + ) { + delete settings[game]['start_inventory']; + } }); }); @@ -1156,6 +1182,11 @@ const validateSettings = () => { errorMessage = 'You have not chosen a game to play!'; } + // Remove weights if there is only one game + else if (Object.keys(settings.game).length === 1) { + settings.game = Object.keys(settings.game)[0]; + } + // If an error occurred, alert the user and do not export the file if (errorMessage) { userMessage.innerText = errorMessage; From c3cfbf8e1cc4304659a28c1b2519070a43a3f809 Mon Sep 17 00:00:00 2001 From: Seldom <38388947+Seldom-SE@users.noreply.github.com> Date: Thu, 14 Sep 2023 00:46:29 -0700 Subject: [PATCH 020/144] Terraria: Add the rest of the settings to slot data (#2116) * Add the rest of the Terraria settings to slot data * Update worlds/terraria/__init__.py Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> --------- Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> --- worlds/terraria/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/worlds/terraria/__init__.py b/worlds/terraria/__init__.py index a56f47608b..a8c823bcb8 100644 --- a/worlds/terraria/__init__.py +++ b/worlds/terraria/__init__.py @@ -338,5 +338,7 @@ class TerrariaWorld(World): def fill_slot_data(self) -> Dict[str, object]: return { "goal": list(self.goal_locations), + "achievements": self.multiworld.achievements[self.player].value, + "fill_extra_checks_with": self.multiworld.fill_extra_checks_with[self.player].value, "deathlink": bool(self.multiworld.death_link[self.player]), } From 8ee743ac8a1e782e7bdb36ccff4c9078a4d77cf3 Mon Sep 17 00:00:00 2001 From: lordlou <87331798+lordlou@users.noreply.github.com> Date: Thu, 14 Sep 2023 15:49:11 -0400 Subject: [PATCH 021/144] SM: 0.4.2 fixes (#2175) ## What is this fixing or adding? - fixed failing generation with disabled layout patch by moving door_indicators_plms.ips to AP instead of the base patch (reported at https://discord.com/channels/731205301247803413/1149509811529072751/1149509811529072751) (part of the fix is in the Basepatch with this commit https://github.com/lordlou/SMBasepatch/commit/46bbda980cd6eec69c59c71355bd3f975b827456) - fixed broken map data saving when using fast_save (reported at https://discord.com/channels/731205301247803413/1138163133089849344/1138163133089849344) (part of the fix is in the Basepatch with this commit https://github.com/lordlou/SMBasepatch/commit/54a82774c9287274bdee70da8401d14d8d3720b9) --- .../multiworld-basepatch.ips | Bin 19126 -> 19144 bytes .../data/SMBasepatch_prebuilt/multiworld.sym | 315 +++++++++--------- .../sm-basepatch-symbols.json | 5 +- .../SMBasepatch_prebuilt/variapatches.ips | Bin 35247 -> 34458 bytes worlds/sm/variaRandomizer/rom/rompatcher.py | 4 +- 5 files changed, 167 insertions(+), 157 deletions(-) diff --git a/worlds/sm/data/SMBasepatch_prebuilt/multiworld-basepatch.ips b/worlds/sm/data/SMBasepatch_prebuilt/multiworld-basepatch.ips index 0455364d8a7b68251975be054fbb7e8ffbde4791..67863bb9f00228aee09d1c8257bf41cbd17e8a90 100644 GIT binary patch delta 62 zcmdlsmGQ(>#t9P`uTGp8q_aMVvrdVjp%FxDGIOokuie;~8OFFO!$Xtd|Lgfo3xSu0g#XZ!;z6v8Yuw(>y4iWXh|YlqxW)J3YG{ zjA{;}l9RN(ifM0G%Gu5bgKiyzUM@_lUogsmhTVXMTt*7?8T7tMKR_dnY0ZM}gT@=0sW8nGO;i?E+s zwPb+~NUhjA&^gfmV|j4eMA5WTuo|b$gPwu5J}vp6k1b+3{@mZwe!+gXtPNs|1=AW- zTCsJ|dC Date: Thu, 14 Sep 2023 15:49:57 -0400 Subject: [PATCH 022/144] =?UTF-8?q?Pok=C3=A9mon=20R/B:=20More=20tracker=20?= =?UTF-8?q?slot=20data=20(#2174)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- worlds/pokemon_rb/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/worlds/pokemon_rb/__init__.py b/worlds/pokemon_rb/__init__.py index 3d6e463251..2c70f28416 100644 --- a/worlds/pokemon_rb/__init__.py +++ b/worlds/pokemon_rb/__init__.py @@ -717,6 +717,15 @@ class PokemonRedBlueWorld(World): "death_link": self.multiworld.death_link[self.player].value, "prizesanity": self.multiworld.prizesanity[self.player].value, "key_items_only": self.multiworld.key_items_only[self.player].value, + "poke_doll_skip": self.multiworld.poke_doll_skip[self.player].value, + "bicycle_gate_skips": self.multiworld.bicycle_gate_skips[self.player].value, + "stonesanity": self.multiworld.stonesanity[self.player].value, + "door_shuffle": self.multiworld.door_shuffle[self.player].value, + "warp_tile_shuffle": self.multiworld.warp_tile_shuffle[self.player].value, + "dark_rock_tunnel_logic": self.multiworld.dark_rock_tunnel_logic[self.player].value, + "split_card_key": self.multiworld.split_card_key[self.player].value, + "all_elevators_locked": self.multiworld.all_elevators_locked[self.player].value, + } From fdac50523b58afc7aaf660b67f9411ddc1f84bde Mon Sep 17 00:00:00 2001 From: agilbert1412 Date: Thu, 14 Sep 2023 17:56:13 -0400 Subject: [PATCH 023/144] Stardew Valley: Added missing logic rules for dating and marriage (#2160) * - Added missing logic rules where, to earn hearts above 8 and 10, you need access to dating and marriage respectively. * - Slight cleanup based on Black Sliver's suggestion --- worlds/stardew_valley/logic.py | 24 +++++++--- worlds/stardew_valley/test/TestRules.py | 60 +++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/worlds/stardew_valley/logic.py b/worlds/stardew_valley/logic.py index 46580d549a..00b60696a9 100644 --- a/worlds/stardew_valley/logic.py +++ b/worlds/stardew_valley/logic.py @@ -1043,11 +1043,12 @@ class StardewLogic: def has_relationship(self, npc: str, hearts: int = 1) -> StardewRule: if hearts <= 0: return True_() - if self.options[options.Friendsanity] == options.Friendsanity.option_none: + friendsanity = self.options[options.Friendsanity] + if friendsanity == options.Friendsanity.option_none: return self.can_earn_relationship(npc, hearts) if npc not in all_villagers_by_name: if npc == NPC.pet: - if self.options[options.Friendsanity] == options.Friendsanity.option_bachelors: + if friendsanity == options.Friendsanity.option_bachelors: return self.can_befriend_pet(hearts) return self.received_hearts(NPC.pet, hearts) if npc == Generic.any or npc == Generic.bachelor: @@ -1077,12 +1078,12 @@ class StardewLogic: if not self.npc_is_in_current_slot(npc): return True_() villager = all_villagers_by_name[npc] - if self.options[options.Friendsanity] == options.Friendsanity.option_bachelors and not villager.bachelor: + if friendsanity == options.Friendsanity.option_bachelors and not villager.bachelor: return self.can_earn_relationship(npc, hearts) - if self.options[options.Friendsanity] == options.Friendsanity.option_starting_npcs and not villager.available: + if friendsanity == options.Friendsanity.option_starting_npcs and not villager.available: return self.can_earn_relationship(npc, hearts) - if self.options[ - options.Friendsanity] != options.Friendsanity.option_all_with_marriage and villager.bachelor and hearts > 8: + is_capped_at_8 = villager.bachelor and friendsanity != options.Friendsanity.option_all_with_marriage + if is_capped_at_8 and hearts > 8: return self.received_hearts(villager, 8) & self.can_earn_relationship(npc, hearts) return self.received_hearts(villager, hearts) @@ -1136,11 +1137,22 @@ class StardewLogic: rule_if_birthday = self.has_season(villager.birthday) & self.has_any_universal_love() & self.has_lived_months(hearts // 2) rule_if_not_birthday = self.has_lived_months(hearts) earn_rule = self.can_meet(npc) & (rule_if_birthday | rule_if_not_birthday) + if villager.bachelor: + if hearts > 8: + earn_rule = earn_rule & self.can_date(npc) + if hearts > 10: + earn_rule = earn_rule & self.can_marry(npc) else: earn_rule = self.has_lived_months(min(hearts // 2, 8)) return previous_heart_rule & earn_rule + def can_date(self, npc: str) -> StardewRule: + return self.has_relationship(npc, 8) & self.has(Gift.bouquet) + + def can_marry(self, npc: str) -> StardewRule: + return self.has_relationship(npc, 10) & self.has(Gift.mermaid_pendant) + def can_befriend_pet(self, hearts: int): if hearts <= 0: return True_() diff --git a/worlds/stardew_valley/test/TestRules.py b/worlds/stardew_valley/test/TestRules.py index 8556dac1d8..0847d8a63b 100644 --- a/worlds/stardew_valley/test/TestRules.py +++ b/worlds/stardew_valley/test/TestRules.py @@ -444,3 +444,63 @@ def collect_all_except(multiworld, item_to_not_collect: str): for item in multiworld.get_items(): if item.name != item_to_not_collect: multiworld.state.collect(item) + + +class TestFriendsanityDatingRules(SVTestBase): + options = { + options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized_not_winter, + options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage, + options.FriendsanityHeartSize.internal_name: 3 + } + + def test_earning_dating_heart_requires_dating(self): + month_name = "Month End" + for i in range(12): + month_item = self.world.create_item(month_name) + self.multiworld.state.collect(month_item, event=True) + self.multiworld.state.collect(self.world.create_item("Beach Bridge"), event=False) + self.multiworld.state.collect(self.world.create_item("Progressive House"), event=False) + self.multiworld.state.collect(self.world.create_item("Adventurer's Guild"), event=False) + self.multiworld.state.collect(self.world.create_item("Galaxy Hammer"), event=False) + for i in range(3): + self.multiworld.state.collect(self.world.create_item("Progressive Pickaxe"), event=False) + self.multiworld.state.collect(self.world.create_item("Progressive Axe"), event=False) + self.multiworld.state.collect(self.world.create_item("Progressive Barn"), event=False) + for i in range(10): + self.multiworld.state.collect(self.world.create_item("Foraging Level"), event=False) + self.multiworld.state.collect(self.world.create_item("Farming Level"), event=False) + self.multiworld.state.collect(self.world.create_item("Mining Level"), event=False) + self.multiworld.state.collect(self.world.create_item("Combat Level"), event=False) + self.multiworld.state.collect(self.world.create_item("Progressive Mine Elevator"), event=False) + self.multiworld.state.collect(self.world.create_item("Progressive Mine Elevator"), event=False) + + npc = "Abigail" + heart_name = f"{npc} <3" + step = 3 + + self.assert_can_reach_heart_up_to(npc, 3, step) + self.multiworld.state.collect(self.world.create_item(heart_name), event=False) + self.assert_can_reach_heart_up_to(npc, 6, step) + self.multiworld.state.collect(self.world.create_item(heart_name), event=False) + self.assert_can_reach_heart_up_to(npc, 8, step) + self.multiworld.state.collect(self.world.create_item(heart_name), event=False) + self.assert_can_reach_heart_up_to(npc, 10, step) + self.multiworld.state.collect(self.world.create_item(heart_name), event=False) + self.assert_can_reach_heart_up_to(npc, 14, step) + + def assert_can_reach_heart_up_to(self, npc: str, max_reachable: int, step: int): + prefix = "Friendsanity: " + suffix = " <3" + for i in range(1, max_reachable + 1): + if i % step != 0 and i != 14: + continue + location = f"{prefix}{npc} {i}{suffix}" + can_reach = self.world.logic.can_reach_location(location)(self.multiworld.state) + self.assertTrue(can_reach, f"Should be able to earn relationship up to {i} hearts") + for i in range(max_reachable + 1, 14 + 1): + if i % step != 0 and i != 14: + continue + location = f"{prefix}{npc} {i}{suffix}" + can_reach = self.world.logic.can_reach_location(location)(self.multiworld.state) + self.assertFalse(can_reach, f"Should not be able to earn relationship up to {i} hearts") + From 47cf3e06c0bb74355da1a0e627f4b673454a1eac Mon Sep 17 00:00:00 2001 From: BadMagic100 Date: Thu, 14 Sep 2023 14:57:36 -0700 Subject: [PATCH 024/144] Hollow Knight: Update outdated setup documentation (#2171) * Hollow Knight: Update outdated setup documentation * Update a reference from Scarab to Scarab+ Co-authored-by: kindasneaki * Fix numbering --------- Co-authored-by: kindasneaki --- worlds/hk/docs/setup_en.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/worlds/hk/docs/setup_en.md b/worlds/hk/docs/setup_en.md index e25e6bc4ac..adf975ff51 100644 --- a/worlds/hk/docs/setup_en.md +++ b/worlds/hk/docs/setup_en.md @@ -4,13 +4,10 @@ * Download and unzip the Scarab+ Mod Manager from the [Scarab+ website](https://themulhima.github.io/Scarab/). * A legal copy of Hollow Knight. -## Optional Software -* Archipelago Map Mod from Scarab+ - * Ensure that both RandoMapMod and MapChanger are uninstalled or disabled as they are incompatible with Archipelago Map Mod. - -## Installing the Archipelago Mod using Scarab +## Installing the Archipelago Mod using Scarab+ 1. Launch Scarab+ and ensure it locates your Hollow Knight installation directory. 2. Click the "Install" button near the "Archipelago" mod entry. + * If desired, also install "Archipelago Map Mod" to use as an in-game tracker. 3. Launch the game, you're all set! ### What to do if Scarab+ fails to find your XBox Game Pass installation directory From 648d682add36ae4bae939a9252972e35e03eb8e1 Mon Sep 17 00:00:00 2001 From: Ziktofel Date: Fri, 15 Sep 2023 02:22:10 +0200 Subject: [PATCH 025/144] SC2 WoL - Mod, Item and Location update (#2113) Migrates SC2 WoL world to the new mod with new items and locations. The new mod has a different architecture making it more future proof (with planned adding of other campaigns). Also gets rid of several old bugs Adds new short game formats intended for sync games (Tiny Grid, Mini Gauntlet). The final mission isn't decided by campaign length anymore but it's configurable instead. Allow excluding missions for Vanilla Shuffled, corrected some documentation. NOTE: This is a squashed commit with Salz' HotS excluded (not ready for the release and I plan multi-campaign instead) --------- Co-authored-by: Matthew --- Starcraft2Client.py | 1050 +------------- .../static/icons/sc2/SC2_Lab_BioSteel_L1.png | Bin 0 -> 5945 bytes .../static/icons/sc2/SC2_Lab_BioSteel_L2.png | Bin 0 -> 6699 bytes .../static/icons/sc2/advanceballistics.png | Bin 0 -> 11624 bytes .../static/icons/sc2/autoturretblackops.png | Bin 0 -> 8834 bytes .../static/icons/sc2/biomechanicaldrone.png | Bin 0 -> 6999 bytes .../static/icons/sc2/burstcapacitors.png | Bin 0 -> 2579 bytes .../icons/sc2/crossspectrumdampeners.png | Bin 0 -> 5344 bytes .../static/static/icons/sc2/cyclone.png | Bin 0 -> 8524 bytes .../static/icons/sc2/cyclonerangeupgrade.png | Bin 0 -> 12682 bytes .../static/static/icons/sc2/drillingclaws.png | Bin 0 -> 8451 bytes .../static/icons/sc2/emergencythrusters.png | Bin 0 -> 6796 bytes .../static/icons/sc2/hellionbattlemode.png | Bin 0 -> 8210 bytes .../icons/sc2/high-explosive-spidermine.png | Bin 0 -> 14334 bytes .../static/icons/sc2/hyperflightrotors.png | Bin 0 -> 14285 bytes .../static/static/icons/sc2/hyperfluxor.png | Bin 0 -> 9261 bytes .../static/static/icons/sc2/impalerrounds.png | Bin 0 -> 4347 bytes .../static/icons/sc2/improvedburstlaser.png | Bin 0 -> 11115 bytes .../static/icons/sc2/improvedsiegemode.png | Bin 0 -> 14293 bytes .../static/icons/sc2/interferencematrix.png | Bin 0 -> 10537 bytes .../icons/sc2/internalizedtechmodule.png | Bin 0 -> 16425 bytes .../static/static/icons/sc2/jotunboosters.png | Bin 0 -> 5740 bytes .../static/static/icons/sc2/jumpjets.png | Bin 0 -> 17881 bytes .../static/icons/sc2/lasertargetingsystem.png | Bin 0 -> 14802 bytes .../static/static/icons/sc2/liberator.png | Bin 0 -> 9012 bytes .../static/static/icons/sc2/lockdown.png | Bin 0 -> 8289 bytes .../static/icons/sc2/magfieldaccelerator.png | Bin 0 -> 11459 bytes .../static/icons/sc2/magrailmunitions.png | Bin 0 -> 19193 bytes .../icons/sc2/medivacemergencythrusters.png | Bin 0 -> 8954 bytes .../icons/sc2/neosteelfortifiedarmor.png | Bin 0 -> 13032 bytes .../static/static/icons/sc2/opticalflare.png | Bin 0 -> 11440 bytes .../static/icons/sc2/optimizedlogistics.png | Bin 0 -> 13116 bytes .../static/icons/sc2/reapercombatdrugs.png | Bin 0 -> 7102 bytes .../static/static/icons/sc2/restoration.png | Bin 0 -> 7754 bytes .../static/icons/sc2/ripwavemissiles.png | Bin 0 -> 13628 bytes .../static/icons/sc2/shreddermissile.png | Bin 0 -> 9827 bytes .../icons/sc2/siegetank-spidermines.png | Bin 0 -> 12764 bytes .../static/icons/sc2/siegetankrange.png | Bin 0 -> 11833 bytes .../static/icons/sc2/specialordance.png | Bin 0 -> 12992 bytes .../static/static/icons/sc2/spidermine.png | Bin 0 -> 3872 bytes .../static/icons/sc2/staticempblast.png | Bin 0 -> 12094 bytes .../static/static/icons/sc2/superstimpack.png | Bin 0 -> 14901 bytes .../static/icons/sc2/targetingoptics.png | Bin 0 -> 8431 bytes .../static/icons/sc2/terran-cloak-color.png | Bin 0 -> 8134 bytes .../static/icons/sc2/terran-emp-color.png | Bin 0 -> 7693 bytes .../sc2/terrandefendermodestructureattack.png | Bin 0 -> 14017 bytes .../static/static/icons/sc2/thorsiegemode.png | Bin 0 -> 11345 bytes .../static/icons/sc2/transformationservos.png | Bin 0 -> 9215 bytes .../static/static/icons/sc2/valkyrie.png | Bin 0 -> 7490 bytes .../static/static/icons/sc2/warpjump.png | Bin 0 -> 8665 bytes .../icons/sc2/widowmine-attackrange.png | Bin 0 -> 13367 bytes .../icons/sc2/widowmine-deathblossom.png | Bin 0 -> 12946 bytes .../static/static/icons/sc2/widowmine.png | Bin 0 -> 5671 bytes .../static/icons/sc2/widowminehidden.png | Bin 0 -> 12777 bytes WebHostLib/static/styles/sc2wolTracker.css | 6 +- WebHostLib/templates/sc2wolTracker.html | 324 +++-- WebHostLib/tracker.py | 234 +++- setup.py | 2 +- worlds/_sc2common/bot/maps.py | 27 +- worlds/sc2wol/Client.py | 1201 +++++++++++++++++ worlds/sc2wol/Items.py | 235 +++- worlds/sc2wol/Locations.py | 490 +++++-- worlds/sc2wol/LogicMixin.py | 76 +- worlds/sc2wol/MissionTables.py | 34 +- worlds/sc2wol/Options.py | 283 +++- worlds/sc2wol/PoolFilter.py | 155 ++- worlds/sc2wol/Regions.py | 47 +- worlds/sc2wol/__init__.py | 160 ++- 68 files changed, 2856 insertions(+), 1468 deletions(-) create mode 100644 WebHostLib/static/static/icons/sc2/SC2_Lab_BioSteel_L1.png create mode 100644 WebHostLib/static/static/icons/sc2/SC2_Lab_BioSteel_L2.png create mode 100644 WebHostLib/static/static/icons/sc2/advanceballistics.png create mode 100644 WebHostLib/static/static/icons/sc2/autoturretblackops.png create mode 100644 WebHostLib/static/static/icons/sc2/biomechanicaldrone.png create mode 100644 WebHostLib/static/static/icons/sc2/burstcapacitors.png create mode 100644 WebHostLib/static/static/icons/sc2/crossspectrumdampeners.png create mode 100644 WebHostLib/static/static/icons/sc2/cyclone.png create mode 100644 WebHostLib/static/static/icons/sc2/cyclonerangeupgrade.png create mode 100644 WebHostLib/static/static/icons/sc2/drillingclaws.png create mode 100644 WebHostLib/static/static/icons/sc2/emergencythrusters.png create mode 100644 WebHostLib/static/static/icons/sc2/hellionbattlemode.png create mode 100644 WebHostLib/static/static/icons/sc2/high-explosive-spidermine.png create mode 100644 WebHostLib/static/static/icons/sc2/hyperflightrotors.png create mode 100644 WebHostLib/static/static/icons/sc2/hyperfluxor.png create mode 100644 WebHostLib/static/static/icons/sc2/impalerrounds.png create mode 100644 WebHostLib/static/static/icons/sc2/improvedburstlaser.png create mode 100644 WebHostLib/static/static/icons/sc2/improvedsiegemode.png create mode 100644 WebHostLib/static/static/icons/sc2/interferencematrix.png create mode 100644 WebHostLib/static/static/icons/sc2/internalizedtechmodule.png create mode 100644 WebHostLib/static/static/icons/sc2/jotunboosters.png create mode 100644 WebHostLib/static/static/icons/sc2/jumpjets.png create mode 100644 WebHostLib/static/static/icons/sc2/lasertargetingsystem.png create mode 100644 WebHostLib/static/static/icons/sc2/liberator.png create mode 100644 WebHostLib/static/static/icons/sc2/lockdown.png create mode 100644 WebHostLib/static/static/icons/sc2/magfieldaccelerator.png create mode 100644 WebHostLib/static/static/icons/sc2/magrailmunitions.png create mode 100644 WebHostLib/static/static/icons/sc2/medivacemergencythrusters.png create mode 100644 WebHostLib/static/static/icons/sc2/neosteelfortifiedarmor.png create mode 100644 WebHostLib/static/static/icons/sc2/opticalflare.png create mode 100644 WebHostLib/static/static/icons/sc2/optimizedlogistics.png create mode 100644 WebHostLib/static/static/icons/sc2/reapercombatdrugs.png create mode 100644 WebHostLib/static/static/icons/sc2/restoration.png create mode 100644 WebHostLib/static/static/icons/sc2/ripwavemissiles.png create mode 100644 WebHostLib/static/static/icons/sc2/shreddermissile.png create mode 100644 WebHostLib/static/static/icons/sc2/siegetank-spidermines.png create mode 100644 WebHostLib/static/static/icons/sc2/siegetankrange.png create mode 100644 WebHostLib/static/static/icons/sc2/specialordance.png create mode 100644 WebHostLib/static/static/icons/sc2/spidermine.png create mode 100644 WebHostLib/static/static/icons/sc2/staticempblast.png create mode 100644 WebHostLib/static/static/icons/sc2/superstimpack.png create mode 100644 WebHostLib/static/static/icons/sc2/targetingoptics.png create mode 100644 WebHostLib/static/static/icons/sc2/terran-cloak-color.png create mode 100644 WebHostLib/static/static/icons/sc2/terran-emp-color.png create mode 100644 WebHostLib/static/static/icons/sc2/terrandefendermodestructureattack.png create mode 100644 WebHostLib/static/static/icons/sc2/thorsiegemode.png create mode 100644 WebHostLib/static/static/icons/sc2/transformationservos.png create mode 100644 WebHostLib/static/static/icons/sc2/valkyrie.png create mode 100644 WebHostLib/static/static/icons/sc2/warpjump.png create mode 100644 WebHostLib/static/static/icons/sc2/widowmine-attackrange.png create mode 100644 WebHostLib/static/static/icons/sc2/widowmine-deathblossom.png create mode 100644 WebHostLib/static/static/icons/sc2/widowmine.png create mode 100644 WebHostLib/static/static/icons/sc2/widowminehidden.png create mode 100644 worlds/sc2wol/Client.py diff --git a/Starcraft2Client.py b/Starcraft2Client.py index cdcdb39a0b..87b50d3506 100644 --- a/Starcraft2Client.py +++ b/Starcraft2Client.py @@ -1,1049 +1,11 @@ from __future__ import annotations -import asyncio -import copy -import ctypes -import logging -import multiprocessing -import os.path -import re -import sys -import typing -import queue -import zipfile -import io -from pathlib import Path +import ModuleUpdate +ModuleUpdate.update() -# CommonClient import first to trigger ModuleUpdater -from CommonClient import CommonContext, server_loop, ClientCommandProcessor, gui_enabled, get_base_parser -from Utils import init_logging, is_windows +from worlds.sc2wol.Client import launch +import Utils if __name__ == "__main__": - init_logging("SC2Client", exception_logger="Client") - -logger = logging.getLogger("Client") -sc2_logger = logging.getLogger("Starcraft2") - -import nest_asyncio -from worlds._sc2common import bot -from worlds._sc2common.bot.data import Race -from worlds._sc2common.bot.main import run_game -from worlds._sc2common.bot.player import Bot -from worlds.sc2wol import SC2WoLWorld -from worlds.sc2wol.Items import lookup_id_to_name, item_table, ItemData, type_flaggroups -from worlds.sc2wol.Locations import SC2WOL_LOC_ID_OFFSET -from worlds.sc2wol.MissionTables import lookup_id_to_mission -from worlds.sc2wol.Regions import MissionInfo - -import colorama -from NetUtils import ClientStatus, NetworkItem, RawJSONtoTextParser -from MultiServer import mark_raw - -nest_asyncio.apply() -max_bonus: int = 8 -victory_modulo: int = 100 - - -class StarcraftClientProcessor(ClientCommandProcessor): - ctx: SC2Context - - def _cmd_difficulty(self, difficulty: str = "") -> bool: - """Overrides the current difficulty set for the seed. Takes the argument casual, normal, hard, or brutal""" - options = difficulty.split() - num_options = len(options) - - if num_options > 0: - difficulty_choice = options[0].lower() - if difficulty_choice == "casual": - self.ctx.difficulty_override = 0 - elif difficulty_choice == "normal": - self.ctx.difficulty_override = 1 - elif difficulty_choice == "hard": - self.ctx.difficulty_override = 2 - elif difficulty_choice == "brutal": - self.ctx.difficulty_override = 3 - else: - self.output("Unable to parse difficulty '" + options[0] + "'") - return False - - self.output("Difficulty set to " + options[0]) - return True - - else: - if self.ctx.difficulty == -1: - self.output("Please connect to a seed before checking difficulty.") - else: - self.output("Current difficulty: " + ["Casual", "Normal", "Hard", "Brutal"][self.ctx.difficulty]) - self.output("To change the difficulty, add the name of the difficulty after the command.") - return False - - def _cmd_disable_mission_check(self) -> bool: - """Disables the check to see if a mission is available to play. Meant for co-op runs where one player can play - the next mission in a chain the other player is doing.""" - self.ctx.missions_unlocked = True - sc2_logger.info("Mission check has been disabled") - return True - - def _cmd_play(self, mission_id: str = "") -> bool: - """Start a Starcraft 2 mission""" - - options = mission_id.split() - num_options = len(options) - - if num_options > 0: - mission_number = int(options[0]) - - self.ctx.play_mission(mission_number) - - else: - sc2_logger.info( - "Mission ID needs to be specified. Use /unfinished or /available to view ids for available missions.") - return False - - return True - - def _cmd_available(self) -> bool: - """Get what missions are currently available to play""" - - request_available_missions(self.ctx) - return True - - def _cmd_unfinished(self) -> bool: - """Get what missions are currently available to play and have not had all locations checked""" - - request_unfinished_missions(self.ctx) - return True - - @mark_raw - def _cmd_set_path(self, path: str = '') -> bool: - """Manually set the SC2 install directory (if the automatic detection fails).""" - if path: - os.environ["SC2PATH"] = path - is_mod_installed_correctly() - return True - else: - sc2_logger.warning("When using set_path, you must type the path to your SC2 install directory.") - return False - - def _cmd_download_data(self) -> bool: - """Download the most recent release of the necessary files for playing SC2 with - Archipelago. Will overwrite existing files.""" - if "SC2PATH" not in os.environ: - check_game_install_path() - - if os.path.exists(os.environ["SC2PATH"]+"ArchipelagoSC2Version.txt"): - with open(os.environ["SC2PATH"]+"ArchipelagoSC2Version.txt", "r") as f: - current_ver = f.read() - else: - current_ver = None - - tempzip, version = download_latest_release_zip('TheCondor07', 'Starcraft2ArchipelagoData', - current_version=current_ver, force_download=True) - - if tempzip != '': - try: - zipfile.ZipFile(tempzip).extractall(path=os.environ["SC2PATH"]) - sc2_logger.info(f"Download complete. Version {version} installed.") - with open(os.environ["SC2PATH"]+"ArchipelagoSC2Version.txt", "w") as f: - f.write(version) - finally: - os.remove(tempzip) - else: - sc2_logger.warning("Download aborted/failed. Read the log for more information.") - return False - return True - - -class SC2Context(CommonContext): - command_processor = StarcraftClientProcessor - game = "Starcraft 2 Wings of Liberty" - items_handling = 0b111 - difficulty = -1 - all_in_choice = 0 - mission_order = 0 - mission_req_table: typing.Dict[str, MissionInfo] = {} - final_mission: int = 29 - announcements = queue.Queue() - sc2_run_task: typing.Optional[asyncio.Task] = None - missions_unlocked: bool = False # allow launching missions ignoring requirements - current_tooltip = None - last_loc_list = None - difficulty_override = -1 - mission_id_to_location_ids: typing.Dict[int, typing.List[int]] = {} - last_bot: typing.Optional[ArchipelagoBot] = None - - def __init__(self, *args, **kwargs): - super(SC2Context, self).__init__(*args, **kwargs) - self.raw_text_parser = RawJSONtoTextParser(self) - - async def server_auth(self, password_requested: bool = False): - if password_requested and not self.password: - await super(SC2Context, self).server_auth(password_requested) - await self.get_username() - await self.send_connect() - - def on_package(self, cmd: str, args: dict): - if cmd in {"Connected"}: - self.difficulty = args["slot_data"]["game_difficulty"] - self.all_in_choice = args["slot_data"]["all_in_map"] - slot_req_table = args["slot_data"]["mission_req"] - # Maintaining backwards compatibility with older slot data - self.mission_req_table = { - mission: MissionInfo( - **{field: value for field, value in mission_info.items() if field in MissionInfo._fields} - ) - for mission, mission_info in slot_req_table.items() - } - self.mission_order = args["slot_data"].get("mission_order", 0) - self.final_mission = args["slot_data"].get("final_mission", 29) - - self.build_location_to_mission_mapping() - - # Looks for the required maps and mods for SC2. Runs check_game_install_path. - maps_present = is_mod_installed_correctly() - if os.path.exists(os.environ["SC2PATH"] + "ArchipelagoSC2Version.txt"): - with open(os.environ["SC2PATH"] + "ArchipelagoSC2Version.txt", "r") as f: - current_ver = f.read() - if is_mod_update_available("TheCondor07", "Starcraft2ArchipelagoData", current_ver): - sc2_logger.info("NOTICE: Update for required files found. Run /download_data to install.") - elif maps_present: - sc2_logger.warning("NOTICE: Your map files may be outdated (version number not found). " - "Run /download_data to update them.") - - - def on_print_json(self, args: dict): - # goes to this world - if "receiving" in args and self.slot_concerns_self(args["receiving"]): - relevant = True - # found in this world - elif "item" in args and self.slot_concerns_self(args["item"].player): - relevant = True - # not related - else: - relevant = False - - if relevant: - self.announcements.put(self.raw_text_parser(copy.deepcopy(args["data"]))) - - super(SC2Context, self).on_print_json(args) - - def run_gui(self): - from kvui import GameManager, HoverBehavior, ServerToolTip - from kivy.app import App - from kivy.clock import Clock - from kivy.uix.tabbedpanel import TabbedPanelItem - from kivy.uix.gridlayout import GridLayout - from kivy.lang import Builder - from kivy.uix.label import Label - from kivy.uix.button import Button - from kivy.uix.floatlayout import FloatLayout - from kivy.properties import StringProperty - - class HoverableButton(HoverBehavior, Button): - pass - - class MissionButton(HoverableButton): - tooltip_text = StringProperty("Test") - ctx: SC2Context - - def __init__(self, *args, **kwargs): - super(HoverableButton, self).__init__(*args, **kwargs) - self.layout = FloatLayout() - self.popuplabel = ServerToolTip(text=self.text) - self.layout.add_widget(self.popuplabel) - - def on_enter(self): - self.popuplabel.text = self.tooltip_text - - if self.ctx.current_tooltip: - App.get_running_app().root.remove_widget(self.ctx.current_tooltip) - - if self.tooltip_text == "": - self.ctx.current_tooltip = None - else: - App.get_running_app().root.add_widget(self.layout) - self.ctx.current_tooltip = self.layout - - def on_leave(self): - self.ctx.ui.clear_tooltip() - - @property - def ctx(self) -> CommonContext: - return App.get_running_app().ctx - - class MissionLayout(GridLayout): - pass - - class MissionCategory(GridLayout): - pass - - class SC2Manager(GameManager): - logging_pairs = [ - ("Client", "Archipelago"), - ("Starcraft2", "Starcraft2"), - ] - base_title = "Archipelago Starcraft 2 Client" - - mission_panel = None - last_checked_locations = {} - mission_id_to_button = {} - launching: typing.Union[bool, int] = False # if int -> mission ID - refresh_from_launching = True - first_check = True - ctx: SC2Context - - def __init__(self, ctx): - super().__init__(ctx) - - def clear_tooltip(self): - if self.ctx.current_tooltip: - App.get_running_app().root.remove_widget(self.ctx.current_tooltip) - - self.ctx.current_tooltip = None - - def build(self): - container = super().build() - - panel = TabbedPanelItem(text="Starcraft 2 Launcher") - self.mission_panel = panel.content = MissionLayout() - - self.tabs.add_widget(panel) - - Clock.schedule_interval(self.build_mission_table, 0.5) - - return container - - def build_mission_table(self, dt): - if (not self.launching and (not self.last_checked_locations == self.ctx.checked_locations or - not self.refresh_from_launching)) or self.first_check: - self.refresh_from_launching = True - - self.mission_panel.clear_widgets() - if self.ctx.mission_req_table: - self.last_checked_locations = self.ctx.checked_locations.copy() - self.first_check = False - - self.mission_id_to_button = {} - categories = {} - available_missions, unfinished_missions = calc_unfinished_missions(self.ctx) - - # separate missions into categories - for mission in self.ctx.mission_req_table: - if not self.ctx.mission_req_table[mission].category in categories: - categories[self.ctx.mission_req_table[mission].category] = [] - - categories[self.ctx.mission_req_table[mission].category].append(mission) - - for category in categories: - category_panel = MissionCategory() - if category.startswith('_'): - category_display_name = '' - else: - category_display_name = category - category_panel.add_widget( - Label(text=category_display_name, size_hint_y=None, height=50, outline_width=1)) - - for mission in categories[category]: - text: str = mission - tooltip: str = "" - mission_id: int = self.ctx.mission_req_table[mission].id - # Map has uncollected locations - if mission in unfinished_missions: - text = f"[color=6495ED]{text}[/color]" - elif mission in available_missions: - text = f"[color=FFFFFF]{text}[/color]" - # Map requirements not met - else: - text = f"[color=a9a9a9]{text}[/color]" - tooltip = f"Requires: " - if self.ctx.mission_req_table[mission].required_world: - tooltip += ", ".join(list(self.ctx.mission_req_table)[req_mission - 1] for - req_mission in - self.ctx.mission_req_table[mission].required_world) - - if self.ctx.mission_req_table[mission].number: - tooltip += " and " - if self.ctx.mission_req_table[mission].number: - tooltip += f"{self.ctx.mission_req_table[mission].number} missions completed" - remaining_location_names: typing.List[str] = [ - self.ctx.location_names[loc] for loc in self.ctx.locations_for_mission(mission) - if loc in self.ctx.missing_locations] - - if mission_id == self.ctx.final_mission: - if mission in available_missions: - text = f"[color=FFBC95]{mission}[/color]" - else: - text = f"[color=D0C0BE]{mission}[/color]" - if tooltip: - tooltip += "\n" - tooltip += "Final Mission" - - if remaining_location_names: - if tooltip: - tooltip += "\n" - tooltip += f"Uncollected locations:\n" - tooltip += "\n".join(remaining_location_names) - - mission_button = MissionButton(text=text, size_hint_y=None, height=50) - mission_button.tooltip_text = tooltip - mission_button.bind(on_press=self.mission_callback) - self.mission_id_to_button[mission_id] = mission_button - category_panel.add_widget(mission_button) - - category_panel.add_widget(Label(text="")) - self.mission_panel.add_widget(category_panel) - - elif self.launching: - self.refresh_from_launching = False - - self.mission_panel.clear_widgets() - self.mission_panel.add_widget(Label(text="Launching Mission: " + - lookup_id_to_mission[self.launching])) - if self.ctx.ui: - self.ctx.ui.clear_tooltip() - - def mission_callback(self, button): - if not self.launching: - mission_id: int = next(k for k, v in self.mission_id_to_button.items() if v == button) - self.ctx.play_mission(mission_id) - self.launching = mission_id - Clock.schedule_once(self.finish_launching, 10) - - def finish_launching(self, dt): - self.launching = False - - self.ui = SC2Manager(self) - self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI") - import pkgutil - data = pkgutil.get_data(SC2WoLWorld.__module__, "Starcraft2.kv").decode() - Builder.load_string(data) - - async def shutdown(self): - await super(SC2Context, self).shutdown() - if self.last_bot: - self.last_bot.want_close = True - if self.sc2_run_task: - self.sc2_run_task.cancel() - - def play_mission(self, mission_id: int): - if self.missions_unlocked or \ - is_mission_available(self, mission_id): - if self.sc2_run_task: - if not self.sc2_run_task.done(): - sc2_logger.warning("Starcraft 2 Client is still running!") - self.sc2_run_task.cancel() # doesn't actually close the game, just stops the python task - if self.slot is None: - sc2_logger.warning("Launching Mission without Archipelago authentication, " - "checks will not be registered to server.") - self.sc2_run_task = asyncio.create_task(starcraft_launch(self, mission_id), - name="Starcraft 2 Launch") - else: - sc2_logger.info( - f"{lookup_id_to_mission[mission_id]} is not currently unlocked. " - f"Use /unfinished or /available to see what is available.") - - def build_location_to_mission_mapping(self): - mission_id_to_location_ids: typing.Dict[int, typing.Set[int]] = { - mission_info.id: set() for mission_info in self.mission_req_table.values() - } - - for loc in self.server_locations: - mission_id, objective = divmod(loc - SC2WOL_LOC_ID_OFFSET, victory_modulo) - mission_id_to_location_ids[mission_id].add(objective) - self.mission_id_to_location_ids = {mission_id: sorted(objectives) for mission_id, objectives in - mission_id_to_location_ids.items()} - - def locations_for_mission(self, mission: str): - mission_id: int = self.mission_req_table[mission].id - objectives = self.mission_id_to_location_ids[self.mission_req_table[mission].id] - for objective in objectives: - yield SC2WOL_LOC_ID_OFFSET + mission_id * 100 + objective - - -async def main(): - multiprocessing.freeze_support() - parser = get_base_parser() - parser.add_argument('--name', default=None, help="Slot Name to connect as.") - args = parser.parse_args() - - ctx = SC2Context(args.connect, args.password) - ctx.auth = args.name - if ctx.server_task is None: - ctx.server_task = asyncio.create_task(server_loop(ctx), name="ServerLoop") - - if gui_enabled: - ctx.run_gui() - ctx.run_cli() - - await ctx.exit_event.wait() - - await ctx.shutdown() - - -maps_table = [ - "ap_traynor01", "ap_traynor02", "ap_traynor03", - "ap_thanson01", "ap_thanson02", "ap_thanson03a", "ap_thanson03b", - "ap_ttychus01", "ap_ttychus02", "ap_ttychus03", "ap_ttychus04", "ap_ttychus05", - "ap_ttosh01", "ap_ttosh02", "ap_ttosh03a", "ap_ttosh03b", - "ap_thorner01", "ap_thorner02", "ap_thorner03", "ap_thorner04", "ap_thorner05s", - "ap_tzeratul01", "ap_tzeratul02", "ap_tzeratul03", "ap_tzeratul04", - "ap_tvalerian01", "ap_tvalerian02a", "ap_tvalerian02b", "ap_tvalerian03" -] - -wol_default_categories = [ - "Mar Sara", "Mar Sara", "Mar Sara", "Colonist", "Colonist", "Colonist", "Colonist", - "Artifact", "Artifact", "Artifact", "Artifact", "Artifact", "Covert", "Covert", "Covert", "Covert", - "Rebellion", "Rebellion", "Rebellion", "Rebellion", "Rebellion", "Prophecy", "Prophecy", "Prophecy", "Prophecy", - "Char", "Char", "Char", "Char" -] -wol_default_category_names = [ - "Mar Sara", "Colonist", "Artifact", "Covert", "Rebellion", "Prophecy", "Char" -] - - -def calculate_items(items: typing.List[NetworkItem]) -> typing.List[int]: - network_item: NetworkItem - accumulators: typing.List[int] = [0 for _ in type_flaggroups] - - for network_item in items: - name: str = lookup_id_to_name[network_item.item] - item_data: ItemData = item_table[name] - - # exists exactly once - if item_data.quantity == 1: - accumulators[type_flaggroups[item_data.type]] |= 1 << item_data.number - - # exists multiple times - elif item_data.type == "Upgrade": - accumulators[type_flaggroups[item_data.type]] += 1 << item_data.number - - # sum - else: - accumulators[type_flaggroups[item_data.type]] += item_data.number - - return accumulators - - -def calc_difficulty(difficulty): - if difficulty == 0: - return 'C' - elif difficulty == 1: - return 'N' - elif difficulty == 2: - return 'H' - elif difficulty == 3: - return 'B' - - return 'X' - - -async def starcraft_launch(ctx: SC2Context, mission_id: int): - sc2_logger.info(f"Launching {lookup_id_to_mission[mission_id]}. If game does not launch check log file for errors.") - - with DllDirectory(None): - run_game(bot.maps.get(maps_table[mission_id - 1]), [Bot(Race.Terran, ArchipelagoBot(ctx, mission_id), - name="Archipelago", fullscreen=True)], realtime=True) - - -class ArchipelagoBot(bot.bot_ai.BotAI): - game_running: bool = False - mission_completed: bool = False - boni: typing.List[bool] - setup_done: bool - ctx: SC2Context - mission_id: int - want_close: bool = False - can_read_game = False - - last_received_update: int = 0 - - def __init__(self, ctx: SC2Context, mission_id): - self.setup_done = False - self.ctx = ctx - self.ctx.last_bot = self - self.mission_id = mission_id - self.boni = [False for _ in range(max_bonus)] - - super(ArchipelagoBot, self).__init__() - - async def on_step(self, iteration: int): - if self.want_close: - self.want_close = False - await self._client.leave() - return - game_state = 0 - if not self.setup_done: - self.setup_done = True - start_items = calculate_items(self.ctx.items_received) - if self.ctx.difficulty_override >= 0: - difficulty = calc_difficulty(self.ctx.difficulty_override) - else: - difficulty = calc_difficulty(self.ctx.difficulty) - await self.chat_send("ArchipelagoLoad {} {} {} {} {} {} {} {} {} {} {} {} {}".format( - difficulty, - start_items[0], start_items[1], start_items[2], start_items[3], start_items[4], - start_items[5], start_items[6], start_items[7], start_items[8], start_items[9], - self.ctx.all_in_choice, start_items[10])) - self.last_received_update = len(self.ctx.items_received) - - else: - if not self.ctx.announcements.empty(): - message = self.ctx.announcements.get(timeout=1) - await self.chat_send("SendMessage " + message) - self.ctx.announcements.task_done() - - # Archipelago reads the health - for unit in self.all_own_units(): - if unit.health_max == 38281: - game_state = int(38281 - unit.health) - self.can_read_game = True - - if iteration == 160 and not game_state & 1: - await self.chat_send("SendMessage Warning: Archipelago unable to connect or has lost connection to " + - "Starcraft 2 (This is likely a map issue)") - - if self.last_received_update < len(self.ctx.items_received): - current_items = calculate_items(self.ctx.items_received) - await self.chat_send("UpdateTech {} {} {} {} {} {} {} {}".format( - current_items[0], current_items[1], current_items[2], current_items[3], current_items[4], - current_items[5], current_items[6], current_items[7])) - self.last_received_update = len(self.ctx.items_received) - - if game_state & 1: - if not self.game_running: - print("Archipelago Connected") - self.game_running = True - - if self.can_read_game: - if game_state & (1 << 1) and not self.mission_completed: - if self.mission_id != self.ctx.final_mission: - print("Mission Completed") - await self.ctx.send_msgs( - [{"cmd": 'LocationChecks', - "locations": [SC2WOL_LOC_ID_OFFSET + victory_modulo * self.mission_id]}]) - self.mission_completed = True - else: - print("Game Complete") - await self.ctx.send_msgs([{"cmd": 'StatusUpdate', "status": ClientStatus.CLIENT_GOAL}]) - self.mission_completed = True - - for x, completed in enumerate(self.boni): - if not completed and game_state & (1 << (x + 2)): - await self.ctx.send_msgs( - [{"cmd": 'LocationChecks', - "locations": [SC2WOL_LOC_ID_OFFSET + victory_modulo * self.mission_id + x + 1]}]) - self.boni[x] = True - - else: - await self.chat_send("LostConnection - Lost connection to game.") - - -def request_unfinished_missions(ctx: SC2Context): - if ctx.mission_req_table: - message = "Unfinished Missions: " - unlocks = initialize_blank_mission_dict(ctx.mission_req_table) - unfinished_locations = initialize_blank_mission_dict(ctx.mission_req_table) - - _, unfinished_missions = calc_unfinished_missions(ctx, unlocks=unlocks) - - # Removing All-In from location pool - final_mission = lookup_id_to_mission[ctx.final_mission] - if final_mission in unfinished_missions.keys(): - message = f"Final Mission Available: {final_mission}[{ctx.final_mission}]\n" + message - if unfinished_missions[final_mission] == -1: - unfinished_missions.pop(final_mission) - - message += ", ".join(f"{mark_up_mission_name(ctx, mission, unlocks)}[{ctx.mission_req_table[mission].id}] " + - mark_up_objectives( - f"[{len(unfinished_missions[mission])}/" - f"{sum(1 for _ in ctx.locations_for_mission(mission))}]", - ctx, unfinished_locations, mission) - for mission in unfinished_missions) - - if ctx.ui: - ctx.ui.log_panels['All'].on_message_markup(message) - ctx.ui.log_panels['Starcraft2'].on_message_markup(message) - else: - sc2_logger.info(message) - else: - sc2_logger.warning("No mission table found, you are likely not connected to a server.") - - -def calc_unfinished_missions(ctx: SC2Context, unlocks=None): - unfinished_missions = [] - locations_completed = [] - - if not unlocks: - unlocks = initialize_blank_mission_dict(ctx.mission_req_table) - - available_missions = calc_available_missions(ctx, unlocks) - - for name in available_missions: - objectives = set(ctx.locations_for_mission(name)) - if objectives: - objectives_completed = ctx.checked_locations & objectives - if len(objectives_completed) < len(objectives): - unfinished_missions.append(name) - locations_completed.append(objectives_completed) - - else: # infer that this is the final mission as it has no objectives - unfinished_missions.append(name) - locations_completed.append(-1) - - return available_missions, dict(zip(unfinished_missions, locations_completed)) - - -def is_mission_available(ctx: SC2Context, mission_id_to_check): - unfinished_missions = calc_available_missions(ctx) - - return any(mission_id_to_check == ctx.mission_req_table[mission].id for mission in unfinished_missions) - - -def mark_up_mission_name(ctx: SC2Context, mission, unlock_table): - """Checks if the mission is required for game completion and adds '*' to the name to mark that.""" - - if ctx.mission_req_table[mission].completion_critical: - if ctx.ui: - message = "[color=AF99EF]" + mission + "[/color]" - else: - message = "*" + mission + "*" - else: - message = mission - - if ctx.ui: - unlocks = unlock_table[mission] - - if len(unlocks) > 0: - pre_message = f"[ref={list(ctx.mission_req_table).index(mission)}|Unlocks: " - pre_message += ", ".join(f"{unlock}({ctx.mission_req_table[unlock].id})" for unlock in unlocks) - pre_message += f"]" - message = pre_message + message + "[/ref]" - - return message - - -def mark_up_objectives(message, ctx, unfinished_locations, mission): - formatted_message = message - - if ctx.ui: - locations = unfinished_locations[mission] - - pre_message = f"[ref={list(ctx.mission_req_table).index(mission) + 30}|" - pre_message += "
".join(location for location in locations) - pre_message += f"]" - formatted_message = pre_message + message + "[/ref]" - - return formatted_message - - -def request_available_missions(ctx: SC2Context): - if ctx.mission_req_table: - message = "Available Missions: " - - # Initialize mission unlock table - unlocks = initialize_blank_mission_dict(ctx.mission_req_table) - - missions = calc_available_missions(ctx, unlocks) - message += \ - ", ".join(f"{mark_up_mission_name(ctx, mission, unlocks)}" - f"[{ctx.mission_req_table[mission].id}]" - for mission in missions) - - if ctx.ui: - ctx.ui.log_panels['All'].on_message_markup(message) - ctx.ui.log_panels['Starcraft2'].on_message_markup(message) - else: - sc2_logger.info(message) - else: - sc2_logger.warning("No mission table found, you are likely not connected to a server.") - - -def calc_available_missions(ctx: SC2Context, unlocks=None): - available_missions = [] - missions_complete = 0 - - # Get number of missions completed - for loc in ctx.checked_locations: - if loc % victory_modulo == 0: - missions_complete += 1 - - for name in ctx.mission_req_table: - # Go through the required missions for each mission and fill up unlock table used later for hover-over tooltips - if unlocks: - for unlock in ctx.mission_req_table[name].required_world: - unlocks[list(ctx.mission_req_table)[unlock - 1]].append(name) - - if mission_reqs_completed(ctx, name, missions_complete): - available_missions.append(name) - - return available_missions - - -def mission_reqs_completed(ctx: SC2Context, mission_name: str, missions_complete: int): - """Returns a bool signifying if the mission has all requirements complete and can be done - - Arguments: - ctx -- instance of SC2Context - locations_to_check -- the mission string name to check - missions_complete -- an int of how many missions have been completed - mission_path -- a list of missions that have already been checked -""" - if len(ctx.mission_req_table[mission_name].required_world) >= 1: - # A check for when the requirements are being or'd - or_success = False - - # Loop through required missions - for req_mission in ctx.mission_req_table[mission_name].required_world: - req_success = True - - # Check if required mission has been completed - if not (ctx.mission_req_table[list(ctx.mission_req_table)[req_mission - 1]].id * - victory_modulo + SC2WOL_LOC_ID_OFFSET) in ctx.checked_locations: - if not ctx.mission_req_table[mission_name].or_requirements: - return False - else: - req_success = False - - # Grid-specific logic (to avoid long path checks and infinite recursion) - if ctx.mission_order in (3, 4): - if req_success: - return True - else: - if req_mission is ctx.mission_req_table[mission_name].required_world[-1]: - return False - else: - continue - - # Recursively check required mission to see if it's requirements are met, in case !collect has been done - # Skipping recursive check on Grid settings to speed up checks and avoid infinite recursion - if not mission_reqs_completed(ctx, list(ctx.mission_req_table)[req_mission - 1], missions_complete): - if not ctx.mission_req_table[mission_name].or_requirements: - return False - else: - req_success = False - - # If requirement check succeeded mark or as satisfied - if ctx.mission_req_table[mission_name].or_requirements and req_success: - or_success = True - - if ctx.mission_req_table[mission_name].or_requirements: - # Return false if or requirements not met - if not or_success: - return False - - # Check number of missions - if missions_complete >= ctx.mission_req_table[mission_name].number: - return True - else: - return False - else: - return True - - -def initialize_blank_mission_dict(location_table): - unlocks = {} - - for mission in list(location_table): - unlocks[mission] = [] - - return unlocks - - -def check_game_install_path() -> bool: - # First thing: go to the default location for ExecuteInfo. - # An exception for Windows is included because it's very difficult to find ~\Documents if the user moved it. - if is_windows: - # The next five lines of utterly inscrutable code are brought to you by copy-paste from Stack Overflow. - # https://stackoverflow.com/questions/6227590/finding-the-users-my-documents-path/30924555# - import ctypes.wintypes - CSIDL_PERSONAL = 5 # My Documents - SHGFP_TYPE_CURRENT = 0 # Get current, not default value - - buf = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH) - ctypes.windll.shell32.SHGetFolderPathW(None, CSIDL_PERSONAL, None, SHGFP_TYPE_CURRENT, buf) - documentspath = buf.value - einfo = str(documentspath / Path("StarCraft II\\ExecuteInfo.txt")) - else: - einfo = str(bot.paths.get_home() / Path(bot.paths.USERPATH[bot.paths.PF])) - - # Check if the file exists. - if os.path.isfile(einfo): - - # Open the file and read it, picking out the latest executable's path. - with open(einfo) as f: - content = f.read() - if content: - try: - base = re.search(r" = (.*)Versions", content).group(1) - except AttributeError: - sc2_logger.warning(f"Found {einfo}, but it was empty. Run SC2 through the Blizzard launcher, then " - f"try again.") - return False - if os.path.exists(base): - executable = bot.paths.latest_executeble(Path(base).expanduser() / "Versions") - - # Finally, check the path for an actual executable. - # If we find one, great. Set up the SC2PATH. - if os.path.isfile(executable): - sc2_logger.info(f"Found an SC2 install at {base}!") - sc2_logger.debug(f"Latest executable at {executable}.") - os.environ["SC2PATH"] = base - sc2_logger.debug(f"SC2PATH set to {base}.") - return True - else: - sc2_logger.warning(f"We may have found an SC2 install at {base}, but couldn't find {executable}.") - else: - sc2_logger.warning(f"{einfo} pointed to {base}, but we could not find an SC2 install there.") - else: - sc2_logger.warning(f"Couldn't find {einfo}. Run SC2 through the Blizzard launcher, then try again. " - f"If that fails, please run /set_path with your SC2 install directory.") - return False - - -def is_mod_installed_correctly() -> bool: - """Searches for all required files.""" - if "SC2PATH" not in os.environ: - check_game_install_path() - - mapdir = os.environ['SC2PATH'] / Path('Maps/ArchipelagoCampaign') - modfile = os.environ["SC2PATH"] / Path("Mods/Archipelago.SC2Mod") - wol_required_maps = [ - "ap_thanson01.SC2Map", "ap_thanson02.SC2Map", "ap_thanson03a.SC2Map", "ap_thanson03b.SC2Map", - "ap_thorner01.SC2Map", "ap_thorner02.SC2Map", "ap_thorner03.SC2Map", "ap_thorner04.SC2Map", "ap_thorner05s.SC2Map", - "ap_traynor01.SC2Map", "ap_traynor02.SC2Map", "ap_traynor03.SC2Map", - "ap_ttosh01.SC2Map", "ap_ttosh02.SC2Map", "ap_ttosh03a.SC2Map", "ap_ttosh03b.SC2Map", - "ap_ttychus01.SC2Map", "ap_ttychus02.SC2Map", "ap_ttychus03.SC2Map", "ap_ttychus04.SC2Map", "ap_ttychus05.SC2Map", - "ap_tvalerian01.SC2Map", "ap_tvalerian02a.SC2Map", "ap_tvalerian02b.SC2Map", "ap_tvalerian03.SC2Map", - "ap_tzeratul01.SC2Map", "ap_tzeratul02.SC2Map", "ap_tzeratul03.SC2Map", "ap_tzeratul04.SC2Map" - ] - needs_files = False - - # Check for maps. - missing_maps = [] - for mapfile in wol_required_maps: - if not os.path.isfile(mapdir / mapfile): - missing_maps.append(mapfile) - if len(missing_maps) >= 19: - sc2_logger.warning(f"All map files missing from {mapdir}.") - needs_files = True - elif len(missing_maps) > 0: - for map in missing_maps: - sc2_logger.debug(f"Missing {map} from {mapdir}.") - sc2_logger.warning(f"Missing {len(missing_maps)} map files.") - needs_files = True - else: # Must be no maps missing - sc2_logger.info(f"All maps found in {mapdir}.") - - # Check for mods. - if os.path.isfile(modfile): - sc2_logger.info(f"Archipelago mod found at {modfile}.") - else: - sc2_logger.warning(f"Archipelago mod could not be found at {modfile}.") - needs_files = True - - # Final verdict. - if needs_files: - sc2_logger.warning(f"Required files are missing. Run /download_data to acquire them.") - return False - else: - return True - - -class DllDirectory: - # Credit to Black Sliver for this code. - # More info: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setdlldirectoryw - _old: typing.Optional[str] = None - _new: typing.Optional[str] = None - - def __init__(self, new: typing.Optional[str]): - self._new = new - - def __enter__(self): - old = self.get() - if self.set(self._new): - self._old = old - - def __exit__(self, *args): - if self._old is not None: - self.set(self._old) - - @staticmethod - def get() -> typing.Optional[str]: - if sys.platform == "win32": - n = ctypes.windll.kernel32.GetDllDirectoryW(0, None) - buf = ctypes.create_unicode_buffer(n) - ctypes.windll.kernel32.GetDllDirectoryW(n, buf) - return buf.value - # NOTE: other OS may support os.environ["LD_LIBRARY_PATH"], but this fix is windows-specific - return None - - @staticmethod - def set(s: typing.Optional[str]) -> bool: - if sys.platform == "win32": - return ctypes.windll.kernel32.SetDllDirectoryW(s) != 0 - # NOTE: other OS may support os.environ["LD_LIBRARY_PATH"], but this fix is windows-specific - return False - - -def download_latest_release_zip(owner: str, repo: str, current_version: str = None, force_download=False) -> (str, str): - """Downloads the latest release of a GitHub repo to the current directory as a .zip file.""" - import requests - - headers = {"Accept": 'application/vnd.github.v3+json'} - url = f"https://api.github.com/repos/{owner}/{repo}/releases/latest" - - r1 = requests.get(url, headers=headers) - if r1.status_code == 200: - latest_version = r1.json()["tag_name"] - sc2_logger.info(f"Latest version: {latest_version}.") - else: - sc2_logger.warning(f"Status code: {r1.status_code}") - sc2_logger.warning(f"Failed to reach GitHub. Could not find download link.") - sc2_logger.warning(f"text: {r1.text}") - return "", current_version - - if (force_download is False) and (current_version == latest_version): - sc2_logger.info("Latest version already installed.") - return "", current_version - - sc2_logger.info(f"Attempting to download version {latest_version} of {repo}.") - download_url = r1.json()["assets"][0]["browser_download_url"] - - r2 = requests.get(download_url, headers=headers) - if r2.status_code == 200 and zipfile.is_zipfile(io.BytesIO(r2.content)): - with open(f"{repo}.zip", "wb") as fh: - fh.write(r2.content) - sc2_logger.info(f"Successfully downloaded {repo}.zip.") - return f"{repo}.zip", latest_version - else: - sc2_logger.warning(f"Status code: {r2.status_code}") - sc2_logger.warning("Download failed.") - sc2_logger.warning(f"text: {r2.text}") - return "", current_version - - -def is_mod_update_available(owner: str, repo: str, current_version: str) -> bool: - import requests - - headers = {"Accept": 'application/vnd.github.v3+json'} - url = f"https://api.github.com/repos/{owner}/{repo}/releases/latest" - - r1 = requests.get(url, headers=headers) - if r1.status_code == 200: - latest_version = r1.json()["tag_name"] - if current_version != latest_version: - return True - else: - return False - - else: - sc2_logger.warning(f"Failed to reach GitHub while checking for updates.") - sc2_logger.warning(f"Status code: {r1.status_code}") - sc2_logger.warning(f"text: {r1.text}") - return False - - -if __name__ == '__main__': - colorama.init() - asyncio.run(main()) - colorama.deinit() + Utils.init_logging("Starcraft2Client", exception_logger="Client") + launch() diff --git a/WebHostLib/static/static/icons/sc2/SC2_Lab_BioSteel_L1.png b/WebHostLib/static/static/icons/sc2/SC2_Lab_BioSteel_L1.png new file mode 100644 index 0000000000000000000000000000000000000000..8fb366b93ff0debd825faf213132da839c04a89b GIT binary patch literal 5945 zcmV-97slv`P)EX>4Tx04R}tkv&MmKpe$iTct%Rg6&YmAwzYtAS&XhRVYG*P%E_RU~=gfG-*gu zTpR`0f`cE6RRF*B8G^>Onpui)9@T$_we!cF2S?B&;2?2m4e9tpFljzbi*RvAfDc| zbk6(4QC5}|;&b9LgDyz?$aUG}H_j!8{X8>jWHa-`QDULg#c~(3vY`@B5yur(qkMnP zWrgz=XSG^q?R)YUh6~!tGS_JiBZWmQL4*JqbyQG=g#@h{DJC+spY-q#Iew8`GPx>X z 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 Eg6#q-ApigX literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/biomechanicaldrone.png b/WebHostLib/static/static/icons/sc2/biomechanicaldrone.png new file mode 100644 index 0000000000000000000000000000000000000000..e7ebf4031619f66461de686511e9212989c34ff6 GIT binary patch literal 6999 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 literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/burstcapacitors.png b/WebHostLib/static/static/icons/sc2/burstcapacitors.png new file mode 100644 index 0000000000000000000000000000000000000000..3af9b20a1698065fa07a7729b74cfed01b094a27 GIT binary patch literal 2579 zcmV+u3hecXP)EX>4Tx04R}tkv&MmKpe$iTct%Rg6&YmAwzYtAS&XhRVYG*P%E_RU~=gfG-*gu zTpR`0f`cE6RRF*B8G^>Onpui)9@T$_we!cF2S?B&;2?2m4e9tpFljzbi*RvAfDc| zbk6(4QC5}|;&b9LgDyz?$aUG}H_j!8{X8>jWHa-`QDULg#c~(3vY`@B5yur(qkMnP zWrgz=XSG^q?R)YUh6~!tGS_JiBZWmQL4*JqbyQG=g#@h{DJC+spY-q#Iew8`GPx>X zbWe2dEI& zG`&F}uE$B-+{bb3u|2bSm~YD~TYKzrZO`~}mbA6c*=L{2{?1x!e{1axx#W^dF1h5A zOD?(Ol1nbzMlzQ^ow>Asqn}&Y!I-_V84oS)V8EUO<};TjwsAbExXh)c@uaelYuNpA z5;y@Iid=gtb7^VUYgp#eBf!^zUmDO(0>20W1Ezt>uF$Z|rAITD?vGp=1imfJ@_;=C zybx&GLp$E-XD;1mPQM47i*$PkI3Q#$_+Fs!oUXUUgghV@yM9^fO+-pQ~S?>)ADbOCzVe$_O0mjM}X6c0bds&i76#uQD7X` z7;NzXU6zI|s+?I5o!IKLv)-_F*2dQQ*tLwIyjz%%t81 z27r%&Swlf;kx5`j0X8Z}KI&;eU==;)`>kVt5BOBjzosDjh``+5CYiZ(Aaki5$ivbe zUmw$;YdT*5&P!Vs1V%|3l>k>%_Ud`RmF7)sgobSeA9ey5i?lz}qW>M>T^&yXe*^vs z{H{e`Uq0GNK5Q&=>8}J{=9N$NylT(*D?n8s8~|>q#N;pPhXA`nJfXO?H7s-KGnq@j z64A0T;D>5^Hn?qGniMAeZnt>NO%*HWx2uLdExz@ZzF`>nzQ8D&z}e4o7i4}_k=O#w zjOi_DUQeI|PJI*kvPI3VcLmz)b~MS6;Cx7>*1{K6WP+A!gkh!akF`8L0=PrmLQ6D!u* z_~yQD0`|=opnX?>oekRm0gRjTHw9M3IOSH~H6Kacc)8HRm(EFp_nJ7gtFhz@#P=$A z(>p64X7low1lsStSsR~KFu?T$K>ahA8SP5Zq>9y`KGBnZ>->hyw%Sd=9sxeDXFUr1 zKxT48VD^idUo<|XsL9|%<8x-rwW2^uG+R#V_6@|X14Y9L6PNy`l-bri0=wuXN=FXdmz00!DI+Z5$0WxX6=(^m8CuNG&hr6k_KIp zS?$+#uRXVnhRrFWuruPnR1QkRj&>8UUYZM}CCx--WgwP}U%4))Uyk_AX$@pjob8&l z>asNSrYOU0nVHk`JeT@deJpRgX(oq$`Nw`eqaK7>9S3Z$KJ1G@g0Njbae?M?zfU>W zj31gX8d=qQTqLti@~ZTZ;bzApfUFfDx6b?hnBVF&4;zi}tSdD4XYG-h`ghF8XMLb} zaisw#9RjSQ8s^pdyqI_@)4Qzf!Jq==drB_eFsyxkg+V55fRwD`y5A$)=rmyW1cG0@ zs0P>GH|Mv5_xU8@k{Lv@2I3h`RxScgvdYh1>@;A15?BwI*sW@4$2}96C&BytCXv`m zw%v!?qggPC08)tbFPQ6%fY#W)U2*z1B5hMf!`jh~!YUM}9vN)x{z4mc$@cYhB$r%r p$t9Oua>*r^Tyn`Jms~m`{|C`oK4n}?j*$QW002ovPDHLkV1isK*}?z- literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/crossspectrumdampeners.png b/WebHostLib/static/static/icons/sc2/crossspectrumdampeners.png new file mode 100644 index 0000000000000000000000000000000000000000..d1c0c6c9a01050df51d171481302cdffc38326dd GIT binary patch literal 5344 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 Eg7O=+9{>OV literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/drillingclaws.png b/WebHostLib/static/static/icons/sc2/drillingclaws.png new file mode 100644 index 0000000000000000000000000000000000000000..2b067a6e44d4f787505814b9bfdbc54e006a6d2d GIT binary patch literal 8451 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 G{C@z-2s91= literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/hyperflightrotors.png b/WebHostLib/static/static/icons/sc2/hyperflightrotors.png new file mode 100644 index 0000000000000000000000000000000000000000..3753258458769357ecd299f44b44dc9a3be45e8d GIT binary patch literal 14285 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 literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/hyperfluxor.png b/WebHostLib/static/static/icons/sc2/hyperfluxor.png new file mode 100644 index 0000000000000000000000000000000000000000..cdd95bb515bef7d712352f33e40f92aca5fbb871 GIT binary patch literal 9261 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 P00000NkvXXu0mjfj`g)7 literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/impalerrounds.png b/WebHostLib/static/static/icons/sc2/impalerrounds.png new file mode 100644 index 0000000000000000000000000000000000000000..b00e0c475827388aa30c6470055aceecb04b8e27 GIT binary patch literal 4347 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

hpilznk6nM#o3T-srRT^HN9S9KCrk|Id z9YO(+MiPyrZmU<`&&%RJ22;9DA><|#W<`h%Sg&}X90{?JL`6R@HQI3j3;0G7dBFGc zvbS}!G@245GRCN?s)(qy))Zx_s7e$?Qaa~RFLf)ZqD~P-MRjZDw3CiFTjkZUwT zWPA2iN9Q3%P`P%XqJURM?gI_?^Ve%nGmW?IDS#EkgtLql5(68Hy<(xAMv=(Y_3h5* z+^J!sFZ@WW+V%8cqn3WSm*ofIC@RL9b?-_iQ5?(s+$?9#T~zg+y?f@lbah$$u+Q7( zM2$x-bRk2<1>qT|;DVHlDn`LqLNFSRdlX{?lURH#h=OJwGKWE-5n<8@RYX+7U~`9d zOKj;86}Bcww}mHw+Y+2YY+xcm>#^WK(irnVTN;N{F8!M;^4llY^0U`z;139Fbij|) zcDz}C>qNWtPvR(gU|4Pz^*Aoa+v6rH3R6#0ZaH*7uPm)do)_$zo7Lq`59cy{+q@N9 zczGJwaH@qu=Al%jRHW1}^5H)y+#{N$wJBMqC>cL{gRo1_2vztLYg zy--{l$z-Qu?R4y}dNEc@c8!mZ|E9$8n{uy!*2I9d7V8|w7?LO=jbj#9R(R#qYpnM& zrjv+P>@f;;3quv~iZ_54q?&K1BLh^73Qi%3pp}H#FBSSF^b4q2m`b6Ug7csTtU#ww zRx=^j@Yz>`(8{<+LbHGhcn{+->>GozSSStbYQRTl3_pYp4OO~nWi-{1!A!3NFyZz zA^~H zqCw535;)Kj##4b9=9}>GS;L1KhG>5f7RP~0@X)$>Yt6{)jZD(qNO-CmcRHO-?>)vC ziozk*vTJ^xdXmuZ^(mdBbdIrRgG2lFa&Z4%YDtWi1(7jCq8Jog2}`8N>~}JWg&cpktjxVYe?prfrR%nDI6FRCTd`kaNUp3 zSRShxPBhDN@OcD%!AoYCNNuvq&nY7nBujeu$fiY zl5G!cxKf88>O3(wV-h3@*;o!wQn&P4teyQkI-t=ao*EvHL;Pwl_et_zQYf14RoYg zv4O%V-5k^hKQq+`z6^rqDX=^9t~$s0+fHZ$c{)q9{bezceX)bllLY6SV5yDm%=9CxPz?-z^=#d&P0F zuOv~pcj>o4@ay`p9Y(8)D)m|oYYeV*#IdEBCcOK{9^~@!DqnxBWG4weT&s3Q&XRqAOf8^1e!hQ9aY4%Yisrh5Z+#N0@Vsz{o| z)N3grby4CdW^s9y7ry@j%{b;AZ+VzBvgAdHiX8^XG+E}m8hJ`%`E80H}| z&NiT(j?jKE!`#729|mqa72oNf1g)a(_VJk->}f*NLKF%0I{d_x;lpi9(gxmFX%@hm zfzJuNVt64}erLh6k||?}FvK$~;L19z!)Hw3QRQWVi9g&0qS)ehBhN9#y2rRWhSR*qO0c(}4gykV*x(#%NYke0xgcqwa zpTe$YaP*N8>NR-(wBZ9SL)4alvF@(Md$qz@7nBz=$8TS9tmM$N!cdi74wu(qX$T5> zGd3-{krb`9ib#M7a&2y)#?q|UNs@>WR1v`1C}0WoSP4#7D%g+}LS90{3S%`-5($0; zIZQ}}R)$L9Bw)BIK{q4wV;i1$r$i#mHiXFO&c|^G`lu?UE3wvYnMhGZ#VE3PAM%^RIh-n2uPj25Q%hqkO3grL7?S3(7#cMY z30hM)i-giuSsMaoak7~a#iFd^`$!l7!s z%Mvu}g7RAC`JKz2r5qAb`d-=S!=+8=mO>l_;M|CylWmb7KLE9EW|FkqEy~g{%ra2L z8jH1-(uIWFTA^OA@xsd|vDWa)$=8@SLNiHd7?>Vc$_k808X{rt1S5(y%H~j58$e$9 zF|%VjJ)ojUl>vK#X3aFAX22q}Yp@BtwFU2)HAqe1y&i6raFMH0X%?KyJfFDiSr|fO zf@ZCE;BpVT9+FrYPeKNQhN!G>0Jv`bz&WRRp3xp_5JeHrIm8;`II4sdVXehEPpeU< z*=R66Jxx8aOh`^Gf+U5h37Bd@Dj^a|<*~b&u$FD(t`UkKIdr?_=oRrHcO!wFg;60F0*)Rrkyl)-xD796t(hMZ~LmttCl?zg8$phZqau z@IH9$7{l(dgts0ZBkT2esqgv2euJ?_jdt1Nz?kDuD?XWSBau1L_{$NK`W9t3DK)4+1Ii>a%+Q^ zPB=7&1M_w6+SlT#(~gCHk0TR`HPGq9#xVE?L#SX#7fm60vyLtq(&4B3~5LMCH zR3WrxV4+Zk3bld27lVVzRC!{_^JiC-OoOln4wg6IN*78C?TT6&IjW(rG|O)RV*A62 zAU}X?(=??lOUf)mM6i(w9J!cK+A)U281}Sd4j*iAWZcvC1#j9TbbLvFP;hX6$f7%g zz-9Rn(<*_sQ6qT2!@)~Pbs7;&jWi30<0=>kh*4C9Rs<^re0tII-YG-fz=ne_EP1}O zri{hG2j&hoI>KT<_`QiL5~>DG?-j6CKygiNPW6Xr*!5cGMMTG1EdcU-1y{PQpNJ|G zsbwk>#!_H5=IESfZ_RUR6`o(}@R9j~`)4CgufWEjiafsJ`Khp2rD>r+67C$#P_ZI7 zfhejpLD=H&^D3FF2|Tl|ywGu2Aal@jA&YQ8Ucy>exY`Tnx6>f3QKH`~VLcC;T|LIm ztXp1B!>$dT#%QBamn4qK%M!01F(%N`J|hvs9dnioF{Dz$n9z(o-CTI~)gC{-*YPvA zMXY4PT30D$dz!33EW*Ht+Se9u=3y*Srp5$Iz+h3lqYA{RcnMCFo>L+OLFg7iAQ2&_ zbOfKb+JS|R;%(sbYbIR733M~qtj@EeN)}rjp5IXjzNv=ogcWo)H!&4-7-m_hjvDYK z>`CFyX~TCUqTP(3A*}U!M4YKGmMGJW zaLh>%7P^B?t)9@boh}uXTQLDLQ2PNLzz(;rj_RH zsCvJ#T+*wu+3krSl%=CtZ!+F);Y!C?sXRRCxpTVY?S~E1lVhN5zVvF&bOit9Ju!{M z@WLg>N=F#raVluVNJIjq*{<P6SLP4bIJT$Y$;Z#}{N@Coeda75J#3h4M7(s-v)qA>`mG$p$aYiBh%GgQG-sj? zu@RgJzRM$cvt5uK%|4a(q1TquR25@GC&}6dEcHO5;IrZ?4eo0QJOif{^C2P0zm8l; zJTwwm&2QXo6y3CcN8&`hmk%R+}-Nw~(1Ag@%f1S?ekY6|+akMey&3jVTdvJM0S@)2O;{CS2 zsqzC1LQ|A>L#U@gD2+HU1$k9%a%gp)>|j@Q+qqmv?HL38J}h^^CZSYktBhn&28>ns zZ3b6&R2oec8DoX{F_><{rQRF!VOxJ`9GkJ!ntYfsZWG=*p7A#ysd3ArxA5X~tNhyk z`W@!;4SwU%21bR;o65x{Sm=1xJq(R9EP^l+D~R_L6^NX)LaQcd4C@6XBHJ|eXf$58 zdM^MR+l3WVIep#1It_clZ%Gu|3QVN~-6B++eDz!1QJQlyD%2<`u)8I+TY)+8uhWYd z-K?{3I4qb;g||$W{KA{--15L~KJ)3<`1OB#f_Kg3{M9!%Ss%cqWzWhG)|}FFfiVp| zO~HRe)x#!)9*To4MH6AAP>fYtv20a|tb%tzW~Gj@f)U7+kPrK9A1DIZ09JcpB(0?K zbA(2gZnj+=_t(|1M?SCt>>7twBT!G?zkciD4K>WG`l(3t{&vBK?;mI1t@He&PoCmy zkDum0x!3acgLTfXDi>Cj6^hGo*jH@C^s5O)>)FB5>5Ti6>m~9!Ba>XIE;!xJA zLOIrfHz7|PRmwv*o>sJvY~eCvA@9R_H;kf@1gWT2!YV=rVXO0uUaMJ=1g>$u1Bu4qk#kw3WbH!2CGW8ll#pRb}4q=mWV?Z^aEO-;u#j@0rsV zdrf}jx1M8i?F#?PV=3b`%L@yR%N=FSc`_enuw8>mqa3>x9{THW{8nKs5B(8|g?pzB z6S1(b0sAHbW^x5sl>4R(A3kV#`&zrKnWJ6^=+onN81NG95;b;vG91YRs>`CFJILx*m!gNE}T`GPlFru<6+^~HI zaBI~NvJ8Aj0|)Q6!+xp3`H!B;4TBkvG6$*Jp}JZ1OlF&DkR61)3ZyR3Is5^an63$9^@uY|4p#=^Z?g#86crvic0^yQV4zhKj1kC(*AQa*di2g{j%0a5nNNbS zHu4a;O!bSpg+>}EbXz05NrmJ-%YB!GV>RJmTX|+dnLg;buU@tEFA1qprHal$|N z4m@+Ue0mjHpckn0FxVdfxB_(Y@K7V^3s95Lanr5^ zrWe|BlW-@CuTj~$rh;;VhkKVRbLM;ytUE0kDh zfY}Os-;=Du;VI#4ru0jppFzJ+N)wud9YR+1lsT2H?(FUEzftumbZ}25!t|t~kx-P| z7)luxVMZ;U+aS2tS2Pyn2P@yN-%#g0I;>iftPKFODZ+eHm}v$FKi&}Dbx*{p3_kXU zT|V-s8F{ay_{+y{<;2N8xiuVl;1vf1jXHz`B9ZVikSff@FgF#Vp}ML$rt0rEvVESB;ET&X z^m~=htm*(aGf4{&TO*aWm4)+6CT8x(Cd^0YQ)cQO8b+9@1yd{>OtytL-4*fWS4)2W z^Epe6c_gV3Z>;fPeT9$S>-nk29;B~L{^;|sFf&=_k$2vWi(-b~TcBo3%x!VdvW3dt zQ)nzhkqf<2Q4^ZzvOGlHUNNH21Mg)kWGj`R43HvB#=_jB5G8^u!}oRv|5mLr!dODv z=%50@Rex5$fX!Ubt>pU0x%2(lno;LgSvY@VSYZviX9jlH1UC$22J3>-+e`zxWDCnenmz z>ceE0kFfTcKVh;l!2VbRo+>H7rYw17sjQOd6vQcUE_8k1$jnyBbbD=A%r;<(fR=^H zNg#o;2o-mS;6=8I#eN4s&HIoIL-e!?gCh7&Bl@GkiZ~Ln#xoIn4oz9oL@2~CR)@^O zfAhs5zwxs2ru**Tfx8ay_{kntdc5PUck#}#WimG*|*Vo{tT;MeumlmOK`$#XB^)wm2L`BEh~!=`U5CRs7HdhQFW(cZH*v4 z44@%GGlkZ;LKL8bv6X=s}T%+JX2WtlW=GSJ;`tUekASBa=+6nSWxn z@iSMYHU0goaAwuh9~NBh9*PM14%WNM zl^%2}zTH;spOFZO2#E^qNI2LO_OxJr64F#~E|8Q~WXp6T2@Ncf4Slw*7c`5iiasnu z^d0%Nq2v6O%>QxjeD8Xbyk20gaGqT4+}OU`KK56B>U}f2?my08&p}Q{^L%b;n#Zr? zASH+P?qOJz^s|gxj~paTEVZ#Z-+JycNjk=V`q%H~x$mCm(yFB`8{Bq$KSgtr|Lxa5 zLpnCbXJ5{Vm%7~ENLV|o9Dcj9dk)sF1lX|B75Y2ix(TEJH6x@N+BEOqB}A!mvhTPu z@Zf~8*pO7E>rppkWQ15k-M|loVknqMutq3Kp>)Cd$JJyEonE*!^nbKNU^0@;4F#4Y z@w^>uer$DRFjH5{)L4VLy}P;Vf&1}Z_}yP9M|oi*=D+(tzfC=bpF3oj&!N^;=HDHtn)4?@ zakX23+VD`Rg~SLAg0OZsg@^VFi^GyXy{K%)HNL%Md0{!{Xj?ciX31R;(3ZAY@B{Ee zAxT2-ZB#9<8Hpchf{8Q^3x#d=V@ozrB-~UHXQKq+X zmwSnKy!AFd_4LaebZgvwZwu}&XfG=B2u~PSQ8BH^zE%vo+QMzK!j~6H z{{0opk-cLKwLv%am|}(b*m1mRxMV_AT?s2xa%XS`7}roK5(#P4sDE7ruRi3kDN2G+aGZH+(x=f$}n3>M8w3U{2M8u>n7| zfqmn`!QF<1wSrH6Gv{;%9@}k5q|4K%mAPg_t7%E2lus>tE}qU=?v`X&Y?4sAu%1A~ z5=9Y41ZxaYWJ7gHgrOMvnZuPOSy52>A6QECI=N)&%=ce7AvTI=x0<-pVa3pDx3Jcd z4@+)6w3p|ef0-vvc6iUj_we$W^PC3W^uWEm^vbKW#>Od4&Z$M=>RQ6z{^^JLm4EW* zeB&<``OEVqzxCdj2M^3Ogne_c-pTocmkbAwALZ};#4LyB zgmY(w(k#XC=3J-WWZ zxwGd;i~%Y98)>LhfMJ%Am5xTe&ei2r46ti%mLyF>mDwmyzq-bU-g1-|PJNjtzxxtz zy5~42&zz?{R_B3x@8t0(zeQ2z{LBYFz-Pa5k_Qf@>^iWUdwUo8{l^-t^*k?ZdenuG z?&AvC55xNK)WDQ6%uk2e0kHr830z4;K~xC+!t=Y&7TodhZT!Q3`cu?f3A{33)hGwE zl7)?o{^dU1VV~(4w@r==YVlT2HAi@LJtrRY$C~Cy64)#+sx_im{fxbGsM} z`>e08`!~#oiHLgV)Ok;Tm=RgaL~9HL;wUC73T&JZrwLzrae<=;cBA7Nubnza)A#8N zhb;65;Faf|d!F4j%M&l3<)OxaA3u_wmucaSvble3#9ag@dWV_!93uzAU#^tvFYrUMb5!J{w~xT!A%~3zru7?u#dQ z`2M>{law!f$b(`+`_-0YC$1&8+U zWqf=b@0Fq~iLIqI)}WRoSYvtd++}*54sqTK3sF0L#;2y(H$TfOCr|NIui(v-%4gr5 z(lLftHXP*uRtu%K)iM;YS0X}9;nud0spmJpp7XH}9_QzN`CeZ5{6+q+uWoSGS-h3N zmR1cbd0FC|!y1eC4!ox*gPA$+v3Tyj{TPc&%RKx2m-*=rJjUUJ2UuKQ;n=N*dG*y( z%K%VvGq(W#R;5BL-Pczu$vm zz(%*va^`V1=J26|+;zt>a*4RJ<@oITQgUl}wd1fEtaXEC>5i3YaTOW&*Mx)9!f!k` zGk?#!;GRR84iaGhZ%!B$EhbT z3i3Rs-|4cpyv&>KyNmVBO}_oqGaTBti_T`3we@v&&CJm4^%)E@vOM=c(gIWOoZlRF zblPySoK61Tv>z7s%U5`ekKw~y)YXK_a89)tC#v*sZa1P|G@*SzI>J6`_vVlbqQw7 z5?PB8gZE)82#c+dduSK(Sn{fwxm8Q?WdUi-!r~IMGs;xE#mldrrk=#iO^$Qw^cjvE zIl{#Fc(9--lAG-mbz^~f@8@S`^eqoRh_SG`w#m%&6#YS;B#No0H9FlMmlhV#qQI9K zoxEgfYLfA>23NWrk~s9ZK0FD#>hR1JPm(K(Yp||W->cuDU0xWE;K752uU^jhKhCE7 z!LQv-wz9z|{&0n_b|ZY#;+PJ63|o59Pz6D2g9?;GQIjEnj}}TMUO8kG}Jf(6jFx z-CmzlXV22^Ge4 z=lqXPJO0+s-plSr#IOJUMV?qmC|iakNw`{N71kQ!I4rt!-iN-%N^8Xk*4T;<^VihO zM1&-=q_rscxFSc4VYAyKiVQZuje(-Y8#c*$tyX$Z{FyI&nNF|AyB>K9QEU-}&0e2Z zUVV)$&zPN_!g3E?d+c$=fC*h&vUYuU>X)12^(wcTwYuxiDPDGrV(R819P{-{LCz3gh7^v#rh$5 zjh5zZEjm-cl_jc5ebfnLEOydXTTUqbUiW)NQFPu2(wBhrhZn!{SfGGo@=Evog$u*dyK>j;l=S;U)9LjzD@yJ6`?`B} zR^m7oK)bzxM#fT>xh(Y^=ew$l87%hERTs9zGC)nyiMEKXqrdg+kXM_t+GgYfSG7!y+BZEiEK4&O3ig-c6LNT?4H_l1IFEd*zvjE`&$=UEjXGGb*}e0P?sWMdV82AW zbMv{K`zP+)Wz&pylB@<01FoN!?sYC~b~L^y*KPzby1u(^Yp>VaE%W-;E4|4gSh>kg zoNr`j&zl|79d1g1K|e2DB?~i>L$qU0Q-9rlsKx6DNUz^a@g@S>xuw_5damSk1R*!r xTjT#JEz}*YW%33m_W#>||8H#U`yc(^{|#5@0jcp76VU(w002ovPDHLkV1kCEh^_zt literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/improvedsiegemode.png b/WebHostLib/static/static/icons/sc2/improvedsiegemode.png new file mode 100644 index 0000000000000000000000000000000000000000..f19dad952bb571357e72fe2bca831e127f3396db GIT binary patch literal 14293 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 D09+_4 literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/jotunboosters.png b/WebHostLib/static/static/icons/sc2/jotunboosters.png new file mode 100644 index 0000000000000000000000000000000000000000..25720306e5c2d580647e3abdcfb2bf86af30d0fe GIT binary patch literal 5740 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 IM6N<$g5

j{pDw literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/lasertargetingsystem.png b/WebHostLib/static/static/icons/sc2/lasertargetingsystem.png new file mode 100644 index 0000000000000000000000000000000000000000..c57899b270ff580c51980956089b24e629c18128 GIT binary patch literal 14802 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

UNj)5@82Mmt*U$~`bXx%B7W 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 literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/magfieldaccelerator.png b/WebHostLib/static/static/icons/sc2/magfieldaccelerator.png new file mode 100644 index 0000000000000000000000000000000000000000..0272b4b73892fa735ad850ba22ecaf0646b08a95 GIT binary patch literal 11459 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 literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/magrailmunitions.png b/WebHostLib/static/static/icons/sc2/magrailmunitions.png new file mode 100644 index 0000000000000000000000000000000000000000..ec303498ccdbffc072664429e7a943a128c84025 GIT binary patch literal 19193 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 QF8}}l07*qoM6N<$f)DZ>AOHXW literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/ripwavemissiles.png b/WebHostLib/static/static/icons/sc2/ripwavemissiles.png new file mode 100644 index 0000000000000000000000000000000000000000..f68e820397652a0e3177a9e03cda1954b1ab12a0 GIT binary patch literal 13628 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 literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/specialordance.png b/WebHostLib/static/static/icons/sc2/specialordance.png new file mode 100644 index 0000000000000000000000000000000000000000..4f7410d7ca9e250b0305fae35b0ccee603b2e5ee GIT binary patch literal 12992 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 literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/superstimpack.png b/WebHostLib/static/static/icons/sc2/superstimpack.png new file mode 100644 index 0000000000000000000000000000000000000000..0fba8ce5749ad42fe9fa5c86246280aade0e1fa4 GIT binary patch literal 14901 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 literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/targetingoptics.png b/WebHostLib/static/static/icons/sc2/targetingoptics.png new file mode 100644 index 0000000000000000000000000000000000000000..057a40f08e3097f095239cb437618258d58fcb59 GIT binary patch literal 8431 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 literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/thorsiegemode.png b/WebHostLib/static/static/icons/sc2/thorsiegemode.png new file mode 100644 index 0000000000000000000000000000000000000000..a298fb57de5a10de67b9d1c88ad2576884df5967 GIT binary patch literal 11345 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 M07*qoM6N<$f-r-FHvj+t literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/warpjump.png b/WebHostLib/static/static/icons/sc2/warpjump.png new file mode 100644 index 0000000000000000000000000000000000000000..ff0a7b1af4aaeb9fa6cd112b093c3300d8b46041 GIT binary patch literal 8665 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 N002ovPDHLkV1mnDw5$LC literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/widowmine-deathblossom.png b/WebHostLib/static/static/icons/sc2/widowmine-deathblossom.png new file mode 100644 index 0000000000000000000000000000000000000000..7097db05e6c0da7760362b090ad385232f2220b9 GIT binary patch literal 12946 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 IM6N<$g1}Pepa1{> literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/widowmine.png b/WebHostLib/static/static/icons/sc2/widowmine.png new file mode 100644 index 0000000000000000000000000000000000000000..802c49a83d882539becb20df50e6d8a360cc6001 GIT binary patch literal 5671 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 N002ovPDHLkV1hnhB6a`( literal 0 HcmV?d00001 diff --git a/WebHostLib/static/static/icons/sc2/widowminehidden.png b/WebHostLib/static/static/icons/sc2/widowminehidden.png new file mode 100644 index 0000000000000000000000000000000000000000..e568742e8a50c5a1084e6d6de4a43309908f49de GIT binary patch literal 12777 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 literal 0 HcmV?d00001 diff --git a/WebHostLib/static/styles/sc2wolTracker.css b/WebHostLib/static/styles/sc2wolTracker.css index b68668ecf6..a7d8bd28c4 100644 --- a/WebHostLib/static/styles/sc2wolTracker.css +++ b/WebHostLib/static/styles/sc2wolTracker.css @@ -9,7 +9,7 @@ border-top-left-radius: 4px; border-top-right-radius: 4px; padding: 3px 3px 10px; - width: 500px; + width: 710px; background-color: #525494; } @@ -34,10 +34,12 @@ max-height: 40px; border: 1px solid #000000; filter: grayscale(100%) contrast(75%) brightness(20%); + background-color: black; } #inventory-table img.acquired{ filter: none; + background-color: black; } #inventory-table div.counted-item { @@ -52,7 +54,7 @@ } #location-table{ - width: 500px; + width: 710px; border-left: 2px solid #000000; border-right: 2px solid #000000; border-bottom: 2px solid #000000; diff --git a/WebHostLib/templates/sc2wolTracker.html b/WebHostLib/templates/sc2wolTracker.html index af27e30b27..49c31a5795 100644 --- a/WebHostLib/templates/sc2wolTracker.html +++ b/WebHostLib/templates/sc2wolTracker.html @@ -11,7 +11,7 @@

- @@ -26,7 +26,7 @@ --> - @@ -37,120 +37,266 @@ + + + - - - - - - + + + + + - + + + + + + - - + + + + + + + - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - + + + + + + + + - - - - - - - - + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + - - - - - - - - - - - - - - @@ -165,36 +311,18 @@ - - - - - - - - - - + - - - - - - - - - - - + - diff --git a/WebHostLib/tracker.py b/WebHostLib/tracker.py index 5b89495ecc..4261c27e09 100644 --- a/WebHostLib/tracker.py +++ b/WebHostLib/tracker.py @@ -990,6 +990,7 @@ def __renderSC2WoLTracker(multisave: Dict[str, Any], room: Room, locations: Dict SC2WOL_LOC_ID_OFFSET = 1000 SC2WOL_ITEM_ID_OFFSET = 1000 + icons = { "Starting Minerals": "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/icons/icon-mineral-protoss.png", "Starting Vespene": "https://sclegacy.com/images/uploaded/starcraftii_beta/gamefiles/icons/icon-gas-terran.png", @@ -1034,15 +1035,36 @@ def __renderSC2WoLTracker(multisave: Dict[str, Any], room: Room, locations: Dict "Reaper": "https://static.wikia.nocookie.net/starcraft/images/7/7d/Reaper_SC2_Icon1.jpg", "Stimpack (Marine)": "https://0rganics.org/archipelago/sc2wol/StimpacksCampaign.png", + "Super Stimpack (Marine)": "/static/static/icons/sc2/superstimpack.png", "Combat Shield (Marine)": "https://0rganics.org/archipelago/sc2wol/CombatShieldCampaign.png", + "Laser Targeting System (Marine)": "/static/static/icons/sc2/lasertargetingsystem.png", + "Magrail Munitions (Marine)": "/static/static/icons/sc2/magrailmunitions.png", + "Optimized Logistics (Marine)": "/static/static/icons/sc2/optimizedlogistics.png", "Advanced Medic Facilities (Medic)": "https://0rganics.org/archipelago/sc2wol/AdvancedMedicFacilities.png", "Stabilizer Medpacks (Medic)": "https://0rganics.org/archipelago/sc2wol/StabilizerMedpacks.png", + "Restoration (Medic)": "/static/static/icons/sc2/restoration.png", + "Optical Flare (Medic)": "/static/static/icons/sc2/opticalflare.png", + "Optimized Logistics (Medic)": "/static/static/icons/sc2/optimizedlogistics.png", "Incinerator Gauntlets (Firebat)": "https://0rganics.org/archipelago/sc2wol/IncineratorGauntlets.png", "Juggernaut Plating (Firebat)": "https://0rganics.org/archipelago/sc2wol/JuggernautPlating.png", + "Stimpack (Firebat)": "https://0rganics.org/archipelago/sc2wol/StimpacksCampaign.png", + "Super Stimpack (Firebat)": "/static/static/icons/sc2/superstimpack.png", + "Optimized Logistics (Firebat)": "/static/static/icons/sc2/optimizedlogistics.png", "Concussive Shells (Marauder)": "https://0rganics.org/archipelago/sc2wol/ConcussiveShellsCampaign.png", "Kinetic Foam (Marauder)": "https://0rganics.org/archipelago/sc2wol/KineticFoam.png", + "Stimpack (Marauder)": "https://0rganics.org/archipelago/sc2wol/StimpacksCampaign.png", + "Super Stimpack (Marauder)": "/static/static/icons/sc2/superstimpack.png", + "Laser Targeting System (Marauder)": "/static/static/icons/sc2/lasertargetingsystem.png", + "Magrail Munitions (Marauder)": "/static/static/icons/sc2/magrailmunitions.png", + "Internal Tech Module (Marauder)": "/static/static/icons/sc2/internalizedtechmodule.png", "U-238 Rounds (Reaper)": "https://0rganics.org/archipelago/sc2wol/U-238Rounds.png", "G-4 Clusterbomb (Reaper)": "https://0rganics.org/archipelago/sc2wol/G-4Clusterbomb.png", + "Stimpack (Reaper)": "https://0rganics.org/archipelago/sc2wol/StimpacksCampaign.png", + "Super Stimpack (Reaper)": "/static/static/icons/sc2/superstimpack.png", + "Laser Targeting System (Reaper)": "/static/static/icons/sc2/lasertargetingsystem.png", + "Advanced Cloaking Field (Reaper)": "/static/static/icons/sc2/terran-cloak-color.png", + "Spider Mines (Reaper)": "/static/static/icons/sc2/spidermine.png", + "Combat Drugs (Reaper)": "/static/static/icons/sc2/reapercombatdrugs.png", "Hellion": "https://static.wikia.nocookie.net/starcraft/images/5/56/Hellion_SC2_Icon1.jpg", "Vulture": "https://static.wikia.nocookie.net/starcraft/images/d/da/Vulture_WoL.jpg", @@ -1052,14 +1074,35 @@ def __renderSC2WoLTracker(multisave: Dict[str, Any], room: Room, locations: Dict "Twin-Linked Flamethrower (Hellion)": "https://0rganics.org/archipelago/sc2wol/Twin-LinkedFlamethrower.png", "Thermite Filaments (Hellion)": "https://0rganics.org/archipelago/sc2wol/ThermiteFilaments.png", - "Cerberus Mine (Vulture)": "https://0rganics.org/archipelago/sc2wol/CerberusMine.png", + "Hellbat Aspect (Hellion)": "/static/static/icons/sc2/hellionbattlemode.png", + "Smart Servos (Hellion)": "/static/static/icons/sc2/transformationservos.png", + "Optimized Logistics (Hellion)": "/static/static/icons/sc2/optimizedlogistics.png", + "Jump Jets (Hellion)": "/static/static/icons/sc2/jumpjets.png", + "Stimpack (Hellion)": "https://0rganics.org/archipelago/sc2wol/StimpacksCampaign.png", + "Super Stimpack (Hellion)": "/static/static/icons/sc2/superstimpack.png", + "Cerberus Mine (Spider Mine)": "https://0rganics.org/archipelago/sc2wol/CerberusMine.png", + "High Explosive Munition (Spider Mine)": "/static/static/icons/sc2/high-explosive-spidermine.png", "Replenishable Magazine (Vulture)": "https://0rganics.org/archipelago/sc2wol/ReplenishableMagazine.png", + "Ion Thrusters (Vulture)": "/static/static/icons/sc2/emergencythrusters.png", + "Auto Launchers (Vulture)": "/static/static/icons/sc2/jotunboosters.png", "Multi-Lock Weapons System (Goliath)": "https://0rganics.org/archipelago/sc2wol/Multi-LockWeaponsSystem.png", "Ares-Class Targeting System (Goliath)": "https://0rganics.org/archipelago/sc2wol/Ares-ClassTargetingSystem.png", + "Jump Jets (Goliath)": "/static/static/icons/sc2/jumpjets.png", + "Optimized Logistics (Goliath)": "/static/static/icons/sc2/optimizedlogistics.png", "Tri-Lithium Power Cell (Diamondback)": "https://0rganics.org/archipelago/sc2wol/Tri-LithiumPowerCell.png", "Shaped Hull (Diamondback)": "https://0rganics.org/archipelago/sc2wol/ShapedHull.png", + "Hyperfluxor (Diamondback)": "/static/static/icons/sc2/hyperfluxor.png", + "Burst Capacitors (Diamondback)": "/static/static/icons/sc2/burstcapacitors.png", + "Optimized Logistics (Diamondback)": "/static/static/icons/sc2/optimizedlogistics.png", "Maelstrom Rounds (Siege Tank)": "https://0rganics.org/archipelago/sc2wol/MaelstromRounds.png", "Shaped Blast (Siege Tank)": "https://0rganics.org/archipelago/sc2wol/ShapedBlast.png", + "Jump Jets (Siege Tank)": "/static/static/icons/sc2/jumpjets.png", + "Spider Mines (Siege Tank)": "/static/static/icons/sc2/siegetank-spidermines.png", + "Smart Servos (Siege Tank)": "/static/static/icons/sc2/transformationservos.png", + "Graduating Range (Siege Tank)": "/static/static/icons/sc2/siegetankrange.png", + "Laser Targeting System (Siege Tank)": "/static/static/icons/sc2/lasertargetingsystem.png", + "Advanced Siege Tech (Siege Tank)": "/static/static/icons/sc2/improvedsiegemode.png", + "Internal Tech Module (Siege Tank)": "/static/static/icons/sc2/internalizedtechmodule.png", "Medivac": "https://static.wikia.nocookie.net/starcraft/images/d/db/Medivac_SC2_Icon1.jpg", "Wraith": "https://static.wikia.nocookie.net/starcraft/images/7/75/Wraith_WoL.jpg", @@ -1069,25 +1112,77 @@ def __renderSC2WoLTracker(multisave: Dict[str, Any], room: Room, locations: Dict "Rapid Deployment Tube (Medivac)": "https://0rganics.org/archipelago/sc2wol/RapidDeploymentTube.png", "Advanced Healing AI (Medivac)": "https://0rganics.org/archipelago/sc2wol/AdvancedHealingAI.png", + "Expanded Hull (Medivac)": "/static/static/icons/sc2/neosteelfortifiedarmor.png", + "Afterburners (Medivac)": "/static/static/icons/sc2/medivacemergencythrusters.png", "Tomahawk Power Cells (Wraith)": "https://0rganics.org/archipelago/sc2wol/TomahawkPowerCells.png", "Displacement Field (Wraith)": "https://0rganics.org/archipelago/sc2wol/DisplacementField.png", + "Advanced Laser Technology (Wraith)": "/static/static/icons/sc2/improvedburstlaser.png", "Ripwave Missiles (Viking)": "https://0rganics.org/archipelago/sc2wol/RipwaveMissiles.png", "Phobos-Class Weapons System (Viking)": "https://0rganics.org/archipelago/sc2wol/Phobos-ClassWeaponsSystem.png", - "Cross-Spectrum Dampeners (Banshee)": "https://0rganics.org/archipelago/sc2wol/Cross-SpectrumDampeners.png", + "Smart Servos (Viking)": "/static/static/icons/sc2/transformationservos.png", + "Magrail Munitions (Viking)": "/static/static/icons/sc2/magrailmunitions.png", + "Cross-Spectrum Dampeners (Banshee)": "/static/static/icons/sc2/crossspectrumdampeners.png", + "Advanced Cross-Spectrum Dampeners (Banshee)": "https://0rganics.org/archipelago/sc2wol/Cross-SpectrumDampeners.png", "Shockwave Missile Battery (Banshee)": "https://0rganics.org/archipelago/sc2wol/ShockwaveMissileBattery.png", + "Hyperflight Rotors (Banshee)": "/static/static/icons/sc2/hyperflightrotors.png", + "Laser Targeting System (Banshee)": "/static/static/icons/sc2/lasertargetingsystem.png", + "Internal Tech Module (Banshee)": "/static/static/icons/sc2/internalizedtechmodule.png", "Missile Pods (Battlecruiser)": "https://0rganics.org/archipelago/sc2wol/MissilePods.png", "Defensive Matrix (Battlecruiser)": "https://0rganics.org/archipelago/sc2wol/DefensiveMatrix.png", + "Tactical Jump (Battlecruiser)": "/static/static/icons/sc2/warpjump.png", + "Cloak (Battlecruiser)": "/static/static/icons/sc2/terran-cloak-color.png", + "ATX Laser Battery (Battlecruiser)": "/static/static/icons/sc2/specialordance.png", + "Optimized Logistics (Battlecruiser)": "/static/static/icons/sc2/optimizedlogistics.png", + "Internal Tech Module (Battlecruiser)": "/static/static/icons/sc2/internalizedtechmodule.png", "Ghost": "https://static.wikia.nocookie.net/starcraft/images/6/6e/Ghost_SC2_Icon1.jpg", "Spectre": "https://static.wikia.nocookie.net/starcraft/images/0/0d/Spectre_WoL.jpg", "Thor": "https://static.wikia.nocookie.net/starcraft/images/e/ef/Thor_SC2_Icon1.jpg", + "Widow Mine": "/static/static/icons/sc2/widowmine.png", + "Cyclone": "/static/static/icons/sc2/cyclone.png", + "Liberator": "/static/static/icons/sc2/liberator.png", + "Valkyrie": "/static/static/icons/sc2/valkyrie.png", + "Ocular Implants (Ghost)": "https://0rganics.org/archipelago/sc2wol/OcularImplants.png", "Crius Suit (Ghost)": "https://0rganics.org/archipelago/sc2wol/CriusSuit.png", + "EMP Rounds (Ghost)": "/static/static/icons/sc2/terran-emp-color.png", + "Lockdown (Ghost)": "/static/static/icons/sc2/lockdown.png", "Psionic Lash (Spectre)": "https://0rganics.org/archipelago/sc2wol/PsionicLash.png", "Nyx-Class Cloaking Module (Spectre)": "https://0rganics.org/archipelago/sc2wol/Nyx-ClassCloakingModule.png", + "Impaler Rounds (Spectre)": "/static/static/icons/sc2/impalerrounds.png", "330mm Barrage Cannon (Thor)": "https://0rganics.org/archipelago/sc2wol/330mmBarrageCannon.png", "Immortality Protocol (Thor)": "https://0rganics.org/archipelago/sc2wol/ImmortalityProtocol.png", + "High Impact Payload (Thor)": "/static/static/icons/sc2/thorsiegemode.png", + "Smart Servos (Thor)": "/static/static/icons/sc2/transformationservos.png", + + "Optimized Logistics (Predator)": "/static/static/icons/sc2/optimizedlogistics.png", + "Drilling Claws (Widow Mine)": "/static/static/icons/sc2/drillingclaws.png", + "Concealment (Widow Mine)": "/static/static/icons/sc2/widowminehidden.png", + "Black Market Launchers (Widow Mine)": "/static/static/icons/sc2/widowmine-attackrange.png", + "Executioner Missiles (Widow Mine)": "/static/static/icons/sc2/widowmine-deathblossom.png", + "Mag-Field Accelerators (Cyclone)": "/static/static/icons/sc2/magfieldaccelerator.png", + "Mag-Field Launchers (Cyclone)": "/static/static/icons/sc2/cyclonerangeupgrade.png", + "Targeting Optics (Cyclone)": "/static/static/icons/sc2/targetingoptics.png", + "Rapid Fire Launchers (Cyclone)": "/static/static/icons/sc2/ripwavemissiles.png", + "Bio Mechanical Repair Drone (Raven)": "/static/static/icons/sc2/biomechanicaldrone.png", + "Spider Mines (Raven)": "/static/static/icons/sc2/siegetank-spidermines.png", + "Railgun Turret (Raven)": "/static/static/icons/sc2/autoturretblackops.png", + "Hunter-Seeker Weapon (Raven)": "/static/static/icons/sc2/specialordance.png", + "Interference Matrix (Raven)": "/static/static/icons/sc2/interferencematrix.png", + "Anti-Armor Missile (Raven)": "/static/static/icons/sc2/shreddermissile.png", + "Internal Tech Module (Raven)": "/static/static/icons/sc2/internalizedtechmodule.png", + "EMP Shockwave (Science Vessel)": "/static/static/icons/sc2/staticempblast.png", + "Defensive Matrix (Science Vessel)": "https://0rganics.org/archipelago/sc2wol/DefensiveMatrix.png", + "Advanced Ballistics (Liberator)": "/static/static/icons/sc2/advanceballistics.png", + "Raid Artillery (Liberator)": "/static/static/icons/sc2/terrandefendermodestructureattack.png", + "Cloak (Liberator)": "/static/static/icons/sc2/terran-cloak-color.png", + "Laser Targeting System (Liberator)": "/static/static/icons/sc2/lasertargetingsystem.png", + "Optimized Logistics (Liberator)": "/static/static/icons/sc2/optimizedlogistics.png", + "Enhanced Cluster Launchers (Valkyrie)": "https://0rganics.org/archipelago/sc2wol/HellstormBatteries.png", + "Shaped Hull (Valkyrie)": "https://0rganics.org/archipelago/sc2wol/ShapedHull.png", + "Burst Lasers (Valkyrie)": "/static/static/icons/sc2/improvedburstlaser.png", + "Afterburners (Valkyrie)": "/static/static/icons/sc2/medivacemergencythrusters.png", "War Pigs": "https://static.wikia.nocookie.net/starcraft/images/e/ed/WarPigs_SC2_Icon1.jpg", "Devil Dogs": "https://static.wikia.nocookie.net/starcraft/images/3/33/DevilDogs_SC2_Icon1.jpg", @@ -1109,14 +1204,15 @@ def __renderSC2WoLTracker(multisave: Dict[str, Any], room: Room, locations: Dict "Tech Reactor": "https://static.wikia.nocookie.net/starcraft/images/c/c5/SC2_Lab_Tech_Reactor_Icon.png", "Orbital Strike": "https://static.wikia.nocookie.net/starcraft/images/d/df/SC2_Lab_Orb_Strike_Icon.png", - "Shrike Turret": "https://static.wikia.nocookie.net/starcraft/images/4/44/SC2_Lab_Shrike_Turret_Icon.png", - "Fortified Bunker": "https://static.wikia.nocookie.net/starcraft/images/4/4f/SC2_Lab_FortBunker_Icon.png", + "Shrike Turret (Bunker)": "https://static.wikia.nocookie.net/starcraft/images/4/44/SC2_Lab_Shrike_Turret_Icon.png", + "Fortified Bunker (Bunker)": "https://static.wikia.nocookie.net/starcraft/images/4/4f/SC2_Lab_FortBunker_Icon.png", "Planetary Fortress": "https://static.wikia.nocookie.net/starcraft/images/0/0b/SC2_Lab_PlanetFortress_Icon.png", "Perdition Turret": "https://static.wikia.nocookie.net/starcraft/images/a/af/SC2_Lab_PerdTurret_Icon.png", "Predator": "https://static.wikia.nocookie.net/starcraft/images/8/83/SC2_Lab_Predator_Icon.png", "Hercules": "https://static.wikia.nocookie.net/starcraft/images/4/40/SC2_Lab_Hercules_Icon.png", "Cellular Reactor": "https://static.wikia.nocookie.net/starcraft/images/d/d8/SC2_Lab_CellReactor_Icon.png", - "Regenerative Bio-Steel": "https://static.wikia.nocookie.net/starcraft/images/d/d3/SC2_Lab_BioSteel_Icon.png", + "Regenerative Bio-Steel Level 1": "/static/static/icons/sc2/SC2_Lab_BioSteel_L1.png", + "Regenerative Bio-Steel Level 2": "/static/static/icons/sc2/SC2_Lab_BioSteel_L2.png", "Hive Mind Emulator": "https://static.wikia.nocookie.net/starcraft/images/b/bc/SC2_Lab_Hive_Emulator_Icon.png", "Psi Disrupter": "https://static.wikia.nocookie.net/starcraft/images/c/cf/SC2_Lab_Psi_Disruptor_Icon.png", @@ -1132,40 +1228,71 @@ def __renderSC2WoLTracker(multisave: Dict[str, Any], room: Room, locations: Dict "Nothing": "", } - sc2wol_location_ids = { - "Liberation Day": [SC2WOL_LOC_ID_OFFSET + 100, SC2WOL_LOC_ID_OFFSET + 101, SC2WOL_LOC_ID_OFFSET + 102, SC2WOL_LOC_ID_OFFSET + 103, SC2WOL_LOC_ID_OFFSET + 104, SC2WOL_LOC_ID_OFFSET + 105, SC2WOL_LOC_ID_OFFSET + 106], - "The Outlaws": [SC2WOL_LOC_ID_OFFSET + 200, SC2WOL_LOC_ID_OFFSET + 201], - "Zero Hour": [SC2WOL_LOC_ID_OFFSET + 300, SC2WOL_LOC_ID_OFFSET + 301, SC2WOL_LOC_ID_OFFSET + 302, SC2WOL_LOC_ID_OFFSET + 303], - "Evacuation": [SC2WOL_LOC_ID_OFFSET + 400, SC2WOL_LOC_ID_OFFSET + 401, SC2WOL_LOC_ID_OFFSET + 402, SC2WOL_LOC_ID_OFFSET + 403], - "Outbreak": [SC2WOL_LOC_ID_OFFSET + 500, SC2WOL_LOC_ID_OFFSET + 501, SC2WOL_LOC_ID_OFFSET + 502], - "Safe Haven": [SC2WOL_LOC_ID_OFFSET + 600, SC2WOL_LOC_ID_OFFSET + 601, SC2WOL_LOC_ID_OFFSET + 602, SC2WOL_LOC_ID_OFFSET + 603], - "Haven's Fall": [SC2WOL_LOC_ID_OFFSET + 700, SC2WOL_LOC_ID_OFFSET + 701, SC2WOL_LOC_ID_OFFSET + 702, SC2WOL_LOC_ID_OFFSET + 703], - "Smash and Grab": [SC2WOL_LOC_ID_OFFSET + 800, SC2WOL_LOC_ID_OFFSET + 801, SC2WOL_LOC_ID_OFFSET + 802, SC2WOL_LOC_ID_OFFSET + 803, SC2WOL_LOC_ID_OFFSET + 804], - "The Dig": [SC2WOL_LOC_ID_OFFSET + 900, SC2WOL_LOC_ID_OFFSET + 901, SC2WOL_LOC_ID_OFFSET + 902, SC2WOL_LOC_ID_OFFSET + 903], - "The Moebius Factor": [SC2WOL_LOC_ID_OFFSET + 1000, SC2WOL_LOC_ID_OFFSET + 1003, SC2WOL_LOC_ID_OFFSET + 1004, SC2WOL_LOC_ID_OFFSET + 1005, SC2WOL_LOC_ID_OFFSET + 1006, SC2WOL_LOC_ID_OFFSET + 1007, SC2WOL_LOC_ID_OFFSET + 1008], - "Supernova": [SC2WOL_LOC_ID_OFFSET + 1100, SC2WOL_LOC_ID_OFFSET + 1101, SC2WOL_LOC_ID_OFFSET + 1102, SC2WOL_LOC_ID_OFFSET + 1103, SC2WOL_LOC_ID_OFFSET + 1104], - "Maw of the Void": [SC2WOL_LOC_ID_OFFSET + 1200, SC2WOL_LOC_ID_OFFSET + 1201, SC2WOL_LOC_ID_OFFSET + 1202, SC2WOL_LOC_ID_OFFSET + 1203, SC2WOL_LOC_ID_OFFSET + 1204, SC2WOL_LOC_ID_OFFSET + 1205], - "Devil's Playground": [SC2WOL_LOC_ID_OFFSET + 1300, SC2WOL_LOC_ID_OFFSET + 1301, SC2WOL_LOC_ID_OFFSET + 1302], - "Welcome to the Jungle": [SC2WOL_LOC_ID_OFFSET + 1400, SC2WOL_LOC_ID_OFFSET + 1401, SC2WOL_LOC_ID_OFFSET + 1402, SC2WOL_LOC_ID_OFFSET + 1403], - "Breakout": [SC2WOL_LOC_ID_OFFSET + 1500, SC2WOL_LOC_ID_OFFSET + 1501, SC2WOL_LOC_ID_OFFSET + 1502], - "Ghost of a Chance": [SC2WOL_LOC_ID_OFFSET + 1600, SC2WOL_LOC_ID_OFFSET + 1601, SC2WOL_LOC_ID_OFFSET + 1602, SC2WOL_LOC_ID_OFFSET + 1603, SC2WOL_LOC_ID_OFFSET + 1604, SC2WOL_LOC_ID_OFFSET + 1605], - "The Great Train Robbery": [SC2WOL_LOC_ID_OFFSET + 1700, SC2WOL_LOC_ID_OFFSET + 1701, SC2WOL_LOC_ID_OFFSET + 1702, SC2WOL_LOC_ID_OFFSET + 1703], - "Cutthroat": [SC2WOL_LOC_ID_OFFSET + 1800, SC2WOL_LOC_ID_OFFSET + 1801, SC2WOL_LOC_ID_OFFSET + 1802, SC2WOL_LOC_ID_OFFSET + 1803, SC2WOL_LOC_ID_OFFSET + 1804], - "Engine of Destruction": [SC2WOL_LOC_ID_OFFSET + 1900, SC2WOL_LOC_ID_OFFSET + 1901, SC2WOL_LOC_ID_OFFSET + 1902, SC2WOL_LOC_ID_OFFSET + 1903, SC2WOL_LOC_ID_OFFSET + 1904, SC2WOL_LOC_ID_OFFSET + 1905], - "Media Blitz": [SC2WOL_LOC_ID_OFFSET + 2000, SC2WOL_LOC_ID_OFFSET + 2001, SC2WOL_LOC_ID_OFFSET + 2002, SC2WOL_LOC_ID_OFFSET + 2003, SC2WOL_LOC_ID_OFFSET + 2004], - "Piercing the Shroud": [SC2WOL_LOC_ID_OFFSET + 2100, SC2WOL_LOC_ID_OFFSET + 2101, SC2WOL_LOC_ID_OFFSET + 2102, SC2WOL_LOC_ID_OFFSET + 2103, SC2WOL_LOC_ID_OFFSET + 2104, SC2WOL_LOC_ID_OFFSET + 2105], - "Whispers of Doom": [SC2WOL_LOC_ID_OFFSET + 2200, SC2WOL_LOC_ID_OFFSET + 2201, SC2WOL_LOC_ID_OFFSET + 2202, SC2WOL_LOC_ID_OFFSET + 2203], - "A Sinister Turn": [SC2WOL_LOC_ID_OFFSET + 2300, SC2WOL_LOC_ID_OFFSET + 2301, SC2WOL_LOC_ID_OFFSET + 2302, SC2WOL_LOC_ID_OFFSET + 2303], - "Echoes of the Future": [SC2WOL_LOC_ID_OFFSET + 2400, SC2WOL_LOC_ID_OFFSET + 2401, SC2WOL_LOC_ID_OFFSET + 2402], - "In Utter Darkness": [SC2WOL_LOC_ID_OFFSET + 2500, SC2WOL_LOC_ID_OFFSET + 2501, SC2WOL_LOC_ID_OFFSET + 2502], - "Gates of Hell": [SC2WOL_LOC_ID_OFFSET + 2600, SC2WOL_LOC_ID_OFFSET + 2601], - "Belly of the Beast": [SC2WOL_LOC_ID_OFFSET + 2700, SC2WOL_LOC_ID_OFFSET + 2701, SC2WOL_LOC_ID_OFFSET + 2702, SC2WOL_LOC_ID_OFFSET + 2703], - "Shatter the Sky": [SC2WOL_LOC_ID_OFFSET + 2800, SC2WOL_LOC_ID_OFFSET + 2801, SC2WOL_LOC_ID_OFFSET + 2802, SC2WOL_LOC_ID_OFFSET + 2803, SC2WOL_LOC_ID_OFFSET + 2804, SC2WOL_LOC_ID_OFFSET + 2805], + "Liberation Day": range(SC2WOL_LOC_ID_OFFSET + 100, SC2WOL_LOC_ID_OFFSET + 200), + "The Outlaws": range(SC2WOL_LOC_ID_OFFSET + 200, SC2WOL_LOC_ID_OFFSET + 300), + "Zero Hour": range(SC2WOL_LOC_ID_OFFSET + 300, SC2WOL_LOC_ID_OFFSET + 400), + "Evacuation": range(SC2WOL_LOC_ID_OFFSET + 400, SC2WOL_LOC_ID_OFFSET + 500), + "Outbreak": range(SC2WOL_LOC_ID_OFFSET + 500, SC2WOL_LOC_ID_OFFSET + 600), + "Safe Haven": range(SC2WOL_LOC_ID_OFFSET + 600, SC2WOL_LOC_ID_OFFSET + 700), + "Haven's Fall": range(SC2WOL_LOC_ID_OFFSET + 700, SC2WOL_LOC_ID_OFFSET + 800), + "Smash and Grab": range(SC2WOL_LOC_ID_OFFSET + 800, SC2WOL_LOC_ID_OFFSET + 900), + "The Dig": range(SC2WOL_LOC_ID_OFFSET + 900, SC2WOL_LOC_ID_OFFSET + 1000), + "The Moebius Factor": range(SC2WOL_LOC_ID_OFFSET + 1000, SC2WOL_LOC_ID_OFFSET + 1100), + "Supernova": range(SC2WOL_LOC_ID_OFFSET + 1100, SC2WOL_LOC_ID_OFFSET + 1200), + "Maw of the Void": range(SC2WOL_LOC_ID_OFFSET + 1200, SC2WOL_LOC_ID_OFFSET + 1300), + "Devil's Playground": range(SC2WOL_LOC_ID_OFFSET + 1300, SC2WOL_LOC_ID_OFFSET + 1400), + "Welcome to the Jungle": range(SC2WOL_LOC_ID_OFFSET + 1400, SC2WOL_LOC_ID_OFFSET + 1500), + "Breakout": range(SC2WOL_LOC_ID_OFFSET + 1500, SC2WOL_LOC_ID_OFFSET + 1600), + "Ghost of a Chance": range(SC2WOL_LOC_ID_OFFSET + 1600, SC2WOL_LOC_ID_OFFSET + 1700), + "The Great Train Robbery": range(SC2WOL_LOC_ID_OFFSET + 1700, SC2WOL_LOC_ID_OFFSET + 1800), + "Cutthroat": range(SC2WOL_LOC_ID_OFFSET + 1800, SC2WOL_LOC_ID_OFFSET + 1900), + "Engine of Destruction": range(SC2WOL_LOC_ID_OFFSET + 1900, SC2WOL_LOC_ID_OFFSET + 2000), + "Media Blitz": range(SC2WOL_LOC_ID_OFFSET + 2000, SC2WOL_LOC_ID_OFFSET + 2100), + "Piercing the Shroud": range(SC2WOL_LOC_ID_OFFSET + 2100, SC2WOL_LOC_ID_OFFSET + 2200), + "Whispers of Doom": range(SC2WOL_LOC_ID_OFFSET + 2200, SC2WOL_LOC_ID_OFFSET + 2300), + "A Sinister Turn": range(SC2WOL_LOC_ID_OFFSET + 2300, SC2WOL_LOC_ID_OFFSET + 2400), + "Echoes of the Future": range(SC2WOL_LOC_ID_OFFSET + 2400, SC2WOL_LOC_ID_OFFSET + 2500), + "In Utter Darkness": range(SC2WOL_LOC_ID_OFFSET + 2500, SC2WOL_LOC_ID_OFFSET + 2600), + "Gates of Hell": range(SC2WOL_LOC_ID_OFFSET + 2600, SC2WOL_LOC_ID_OFFSET + 2700), + "Belly of the Beast": range(SC2WOL_LOC_ID_OFFSET + 2700, SC2WOL_LOC_ID_OFFSET + 2800), + "Shatter the Sky": range(SC2WOL_LOC_ID_OFFSET + 2800, SC2WOL_LOC_ID_OFFSET + 2900), } display_data = {} + # Grouped Items + grouped_item_ids = { + "Progressive Weapon Upgrade": 107 + SC2WOL_ITEM_ID_OFFSET, + "Progressive Armor Upgrade": 108 + SC2WOL_ITEM_ID_OFFSET, + "Progressive Infantry Upgrade": 109 + SC2WOL_ITEM_ID_OFFSET, + "Progressive Vehicle Upgrade": 110 + SC2WOL_ITEM_ID_OFFSET, + "Progressive Ship Upgrade": 111 + SC2WOL_ITEM_ID_OFFSET, + "Progressive Weapon/Armor Upgrade": 112 + SC2WOL_ITEM_ID_OFFSET + } + grouped_item_replacements = { + "Progressive Weapon Upgrade": ["Progressive Infantry Weapon", "Progressive Vehicle Weapon", "Progressive Ship Weapon"], + "Progressive Armor Upgrade": ["Progressive Infantry Armor", "Progressive Vehicle Armor", "Progressive Ship Armor"], + "Progressive Infantry Upgrade": ["Progressive Infantry Weapon", "Progressive Infantry Armor"], + "Progressive Vehicle Upgrade": ["Progressive Vehicle Weapon", "Progressive Vehicle Armor"], + "Progressive Ship Upgrade": ["Progressive Ship Weapon", "Progressive Ship Armor"] + } + grouped_item_replacements["Progressive Weapon/Armor Upgrade"] = grouped_item_replacements["Progressive Weapon Upgrade"] + grouped_item_replacements["Progressive Armor Upgrade"] + replacement_item_ids = { + "Progressive Infantry Weapon": 100 + SC2WOL_ITEM_ID_OFFSET, + "Progressive Infantry Armor": 102 + SC2WOL_ITEM_ID_OFFSET, + "Progressive Vehicle Weapon": 103 + SC2WOL_ITEM_ID_OFFSET, + "Progressive Vehicle Armor": 104 + SC2WOL_ITEM_ID_OFFSET, + "Progressive Ship Weapon": 105 + SC2WOL_ITEM_ID_OFFSET, + "Progressive Ship Armor": 106 + SC2WOL_ITEM_ID_OFFSET, + } + for grouped_item_name, grouped_item_id in grouped_item_ids.items(): + count: int = inventory[grouped_item_id] + if count > 0: + for replacement_item in grouped_item_replacements[grouped_item_name]: + replacement_id: int = replacement_item_ids[replacement_item] + inventory[replacement_id] = count + # Determine display for progressive items progressive_items = { "Progressive Infantry Weapon": 100 + SC2WOL_ITEM_ID_OFFSET, @@ -1173,7 +1300,15 @@ def __renderSC2WoLTracker(multisave: Dict[str, Any], room: Room, locations: Dict "Progressive Vehicle Weapon": 103 + SC2WOL_ITEM_ID_OFFSET, "Progressive Vehicle Armor": 104 + SC2WOL_ITEM_ID_OFFSET, "Progressive Ship Weapon": 105 + SC2WOL_ITEM_ID_OFFSET, - "Progressive Ship Armor": 106 + SC2WOL_ITEM_ID_OFFSET + "Progressive Ship Armor": 106 + SC2WOL_ITEM_ID_OFFSET, + "Progressive Stimpack (Marine)": 208 + SC2WOL_ITEM_ID_OFFSET, + "Progressive Stimpack (Firebat)": 226 + SC2WOL_ITEM_ID_OFFSET, + "Progressive Stimpack (Marauder)": 228 + SC2WOL_ITEM_ID_OFFSET, + "Progressive Stimpack (Reaper)": 250 + SC2WOL_ITEM_ID_OFFSET, + "Progressive Stimpack (Hellion)": 259 + SC2WOL_ITEM_ID_OFFSET, + "Progressive High Impact Payload (Thor)": 361 + SC2WOL_ITEM_ID_OFFSET, + "Progressive Cross-Spectrum Dampeners (Banshee)": 316 + SC2WOL_ITEM_ID_OFFSET, + "Progressive Regenerative Bio-Steel": 617 + SC2WOL_ITEM_ID_OFFSET } progressive_names = { "Progressive Infantry Weapon": ["Infantry Weapons Level 1", "Infantry Weapons Level 1", "Infantry Weapons Level 2", "Infantry Weapons Level 3"], @@ -1181,14 +1316,27 @@ def __renderSC2WoLTracker(multisave: Dict[str, Any], room: Room, locations: Dict "Progressive Vehicle Weapon": ["Vehicle Weapons Level 1", "Vehicle Weapons Level 1", "Vehicle Weapons Level 2", "Vehicle Weapons Level 3"], "Progressive Vehicle Armor": ["Vehicle Armor Level 1", "Vehicle Armor Level 1", "Vehicle Armor Level 2", "Vehicle Armor Level 3"], "Progressive Ship Weapon": ["Ship Weapons Level 1", "Ship Weapons Level 1", "Ship Weapons Level 2", "Ship Weapons Level 3"], - "Progressive Ship Armor": ["Ship Armor Level 1", "Ship Armor Level 1", "Ship Armor Level 2", "Ship Armor Level 3"] + "Progressive Ship Armor": ["Ship Armor Level 1", "Ship Armor Level 1", "Ship Armor Level 2", "Ship Armor Level 3"], + "Progressive Stimpack (Marine)": ["Stimpack (Marine)", "Stimpack (Marine)", "Super Stimpack (Marine)"], + "Progressive Stimpack (Firebat)": ["Stimpack (Firebat)", "Stimpack (Firebat)", "Super Stimpack (Firebat)"], + "Progressive Stimpack (Marauder)": ["Stimpack (Marauder)", "Stimpack (Marauder)", "Super Stimpack (Marauder)"], + "Progressive Stimpack (Reaper)": ["Stimpack (Reaper)", "Stimpack (Reaper)", "Super Stimpack (Reaper)"], + "Progressive Stimpack (Hellion)": ["Stimpack (Hellion)", "Stimpack (Hellion)", "Super Stimpack (Hellion)"], + "Progressive High Impact Payload (Thor)": ["High Impact Payload (Thor)", "High Impact Payload (Thor)", "Smart Servos (Thor)"], + "Progressive Cross-Spectrum Dampeners (Banshee)": ["Cross-Spectrum Dampeners (Banshee)", "Cross-Spectrum Dampeners (Banshee)", "Advanced Cross-Spectrum Dampeners (Banshee)"], + "Progressive Regenerative Bio-Steel": ["Regenerative Bio-Steel Level 1", "Regenerative Bio-Steel Level 1", "Regenerative Bio-Steel Level 2"] } for item_name, item_id in progressive_items.items(): level = min(inventory[item_id], len(progressive_names[item_name]) - 1) display_name = progressive_names[item_name][level] - base_name = item_name.split(maxsplit=1)[1].lower().replace(' ', '_') + base_name = (item_name.split(maxsplit=1)[1].lower() + .replace(' ', '_') + .replace("-", "") + .replace("(", "") + .replace(")", "")) display_data[base_name + "_level"] = level display_data[base_name + "_url"] = icons[display_name] + display_data[base_name + "_name"] = display_name # Multi-items multi_items = { @@ -1220,12 +1368,12 @@ def __renderSC2WoLTracker(multisave: Dict[str, Any], room: Room, locations: Dict checks_in_area['Total'] = sum(checks_in_area.values()) return render_template("sc2wolTracker.html", - inventory=inventory, icons=icons, - acquired_items={lookup_any_item_id_to_name[id] for id in inventory if - id in lookup_any_item_id_to_name}, - player=player, team=team, room=room, player_name=playerName, - checks_done=checks_done, checks_in_area=checks_in_area, location_info=location_info, - **display_data) + inventory=inventory, icons=icons, + acquired_items={lookup_any_item_id_to_name[id] for id in inventory if + id in lookup_any_item_id_to_name}, + player=player, team=team, room=room, player_name=playerName, + checks_done=checks_done, checks_in_area=checks_in_area, location_info=location_info, + **display_data) def __renderChecksfinder(multisave: Dict[str, Any], room: Room, locations: Dict[int, Dict[int, Tuple[int, int, int]]], inventory: Counter, team: int, player: int, playerName: str, diff --git a/setup.py b/setup.py index 212bcc5d09..ce35c0f1cc 100644 --- a/setup.py +++ b/setup.py @@ -80,7 +80,6 @@ non_apworlds: set = { "Raft", "Secret of Evermore", "Slay the Spire", - "Starcraft 2 Wings of Liberty", "Sudoku", "Super Mario 64", "VVVVVV", @@ -91,6 +90,7 @@ non_apworlds: set = { # LogicMixin is broken before 3.10 import revamp if sys.version_info < (3,10): non_apworlds.add("Hollow Knight") + non_apworlds.add("Starcraft 2 Wings of Liberty") def download_SNI(): print("Updating SNI") diff --git a/worlds/_sc2common/bot/maps.py b/worlds/_sc2common/bot/maps.py index f14b5af900..29ce9f6581 100644 --- a/worlds/_sc2common/bot/maps.py +++ b/worlds/_sc2common/bot/maps.py @@ -8,18 +8,31 @@ from .paths import Paths def get(name: str) -> Map: - # Iterate through 2 folder depths for map_dir in (p for p in Paths.MAPS.iterdir()): - if map_dir.is_dir(): - for map_file in (p for p in map_dir.iterdir()): - if Map.matches_target_map_name(map_file, name): - return Map(map_file) - elif Map.matches_target_map_name(map_dir, name): - return Map(map_dir) + map = find_map_in_dir(name, map_dir) + if map is not None: + return map raise KeyError(f"Map '{name}' was not found. Please put the map file in \"/StarCraft II/Maps/\".") +# Go deeper +def find_map_in_dir(name, path): + if Map.matches_target_map_name(path, name): + return Map(path) + + if path.name.endswith("SC2Map"): + return None + + if path.is_dir(): + for childPath in (p for p in path.iterdir()): + map = find_map_in_dir(name, childPath) + if map is not None: + return map + + return None + + class Map: def __init__(self, path: Path): diff --git a/worlds/sc2wol/Client.py b/worlds/sc2wol/Client.py new file mode 100644 index 0000000000..c544cf0c55 --- /dev/null +++ b/worlds/sc2wol/Client.py @@ -0,0 +1,1201 @@ +from __future__ import annotations + +import asyncio +import copy +import ctypes +import logging +import multiprocessing +import os.path +import re +import sys +import typing +import queue +import zipfile +import io +import random +from pathlib import Path + +# CommonClient import first to trigger ModuleUpdater +from CommonClient import CommonContext, server_loop, ClientCommandProcessor, gui_enabled, get_base_parser +from Utils import init_logging, is_windows + +if __name__ == "__main__": + init_logging("SC2Client", exception_logger="Client") + +logger = logging.getLogger("Client") +sc2_logger = logging.getLogger("Starcraft2") + +import nest_asyncio +from worlds._sc2common import bot +from worlds._sc2common.bot.data import Race +from worlds._sc2common.bot.main import run_game +from worlds._sc2common.bot.player import Bot +from worlds.sc2wol import SC2WoLWorld +from worlds.sc2wol.Items import lookup_id_to_name, get_full_item_list, ItemData, type_flaggroups, upgrade_numbers +from worlds.sc2wol.Locations import SC2WOL_LOC_ID_OFFSET +from worlds.sc2wol.MissionTables import lookup_id_to_mission +from worlds.sc2wol.Regions import MissionInfo + +import colorama +from NetUtils import ClientStatus, NetworkItem, RawJSONtoTextParser, JSONtoTextParser, JSONMessagePart +from MultiServer import mark_raw + +loop = asyncio.get_event_loop_policy().new_event_loop() +nest_asyncio.apply(loop) +max_bonus: int = 13 +victory_modulo: int = 100 + +# GitHub repo where the Map/mod data is hosted for /download_data command +DATA_REPO_OWNER = "Ziktofel" +DATA_REPO_NAME = "Archipelago-SC2-data" +DATA_API_VERSION = "API2" + + +# Data version file path. +# This file is used to tell if the downloaded data are outdated +# Associated with /download_data command +def get_metadata_file(): + return os.environ["SC2PATH"] + os.sep + "ArchipelagoSC2Metadata.txt" + + +class StarcraftClientProcessor(ClientCommandProcessor): + ctx: SC2Context + + def _cmd_difficulty(self, difficulty: str = "") -> bool: + """Overrides the current difficulty set for the world. Takes the argument casual, normal, hard, or brutal""" + options = difficulty.split() + num_options = len(options) + + if num_options > 0: + difficulty_choice = options[0].lower() + if difficulty_choice == "casual": + self.ctx.difficulty_override = 0 + elif difficulty_choice == "normal": + self.ctx.difficulty_override = 1 + elif difficulty_choice == "hard": + self.ctx.difficulty_override = 2 + elif difficulty_choice == "brutal": + self.ctx.difficulty_override = 3 + else: + self.output("Unable to parse difficulty '" + options[0] + "'") + return False + + self.output("Difficulty set to " + options[0]) + return True + + else: + if self.ctx.difficulty == -1: + self.output("Please connect to a seed before checking difficulty.") + else: + current_difficulty = self.ctx.difficulty + if self.ctx.difficulty_override >= 0: + current_difficulty = self.ctx.difficulty_override + self.output("Current difficulty: " + ["Casual", "Normal", "Hard", "Brutal"][current_difficulty]) + self.output("To change the difficulty, add the name of the difficulty after the command.") + return False + + + def _cmd_game_speed(self, game_speed: str = "") -> bool: + """Overrides the current game speed for the world. + Takes the arguments default, slower, slow, normal, fast, faster""" + options = game_speed.split() + num_options = len(options) + + if num_options > 0: + speed_choice = options[0].lower() + if speed_choice == "default": + self.ctx.game_speed_override = 0 + elif speed_choice == "slower": + self.ctx.game_speed_override = 1 + elif speed_choice == "slow": + self.ctx.game_speed_override = 2 + elif speed_choice == "normal": + self.ctx.game_speed_override = 3 + elif speed_choice == "fast": + self.ctx.game_speed_override = 4 + elif speed_choice == "faster": + self.ctx.game_speed_override = 5 + else: + self.output("Unable to parse game speed '" + options[0] + "'") + return False + + self.output("Game speed set to " + options[0]) + return True + + else: + if self.ctx.game_speed == -1: + self.output("Please connect to a seed before checking game speed.") + else: + current_speed = self.ctx.game_speed + if self.ctx.game_speed_override >= 0: + current_speed = self.ctx.game_speed_override + self.output("Current game speed: " + + ["Default", "Slower", "Slow", "Normal", "Fast", "Faster"][current_speed]) + self.output("To change the game speed, add the name of the speed after the command," + " or Default to select based on difficulty.") + return False + + def _cmd_color(self, color: str = "") -> bool: + player_colors = [ + "White", "Red", "Blue", "Teal", + "Purple", "Yellow", "Orange", "Green", + "LightPink", "Violet", "LightGrey", "DarkGreen", + "Brown", "LightGreen", "DarkGrey", "Pink", + "Rainbow", "Random", "Default" + ] + match_colors = [player_color.lower() for player_color in player_colors] + if color: + if color.lower() not in match_colors: + self.output(color + " is not a valid color. Available colors: " + ', '.join(player_colors)) + return False + if color.lower() == "random": + color = random.choice(player_colors[:16]) + self.ctx.player_color = match_colors.index(color.lower()) + self.output("Color set to " + player_colors[self.ctx.player_color]) + else: + self.output("Current player color: " + player_colors[self.ctx.player_color]) + self.output("To change your colors, add the name of the color after the command.") + self.output("Available colors: " + ', '.join(player_colors)) + + def _cmd_disable_mission_check(self) -> bool: + """Disables the check to see if a mission is available to play. Meant for co-op runs where one player can play + the next mission in a chain the other player is doing.""" + self.ctx.missions_unlocked = True + sc2_logger.info("Mission check has been disabled") + return True + + def _cmd_play(self, mission_id: str = "") -> bool: + """Start a Starcraft 2 mission""" + + options = mission_id.split() + num_options = len(options) + + if num_options > 0: + mission_number = int(options[0]) + + self.ctx.play_mission(mission_number) + + else: + sc2_logger.info( + "Mission ID needs to be specified. Use /unfinished or /available to view ids for available missions.") + return False + + return True + + def _cmd_available(self) -> bool: + """Get what missions are currently available to play""" + + request_available_missions(self.ctx) + return True + + def _cmd_unfinished(self) -> bool: + """Get what missions are currently available to play and have not had all locations checked""" + + request_unfinished_missions(self.ctx) + return True + + @mark_raw + def _cmd_set_path(self, path: str = '') -> bool: + """Manually set the SC2 install directory (if the automatic detection fails).""" + if path: + os.environ["SC2PATH"] = path + is_mod_installed_correctly() + return True + else: + sc2_logger.warning("When using set_path, you must type the path to your SC2 install directory.") + return False + + def _cmd_download_data(self) -> bool: + """Download the most recent release of the necessary files for playing SC2 with + Archipelago. Will overwrite existing files.""" + if "SC2PATH" not in os.environ: + check_game_install_path() + + if os.path.exists(get_metadata_file()): + with open(get_metadata_file(), "r") as f: + metadata = f.read() + else: + metadata = None + + tempzip, metadata = download_latest_release_zip(DATA_REPO_OWNER, DATA_REPO_NAME, DATA_API_VERSION, + metadata=metadata, force_download=True) + + if tempzip != '': + try: + zipfile.ZipFile(tempzip).extractall(path=os.environ["SC2PATH"]) + sc2_logger.info(f"Download complete. Package installed.") + with open(get_metadata_file(), "w") as f: + f.write(metadata) + finally: + os.remove(tempzip) + else: + sc2_logger.warning("Download aborted/failed. Read the log for more information.") + return False + return True + + +class SC2JSONtoTextParser(JSONtoTextParser): + def __init__(self, ctx): + self.handlers = { + "ItemSend": self._handle_color, + "ItemCheat": self._handle_color, + "Hint": self._handle_color, + } + super().__init__(ctx) + + def _handle_color(self, node: JSONMessagePart): + codes = node["color"].split(";") + buffer = "".join(self.color_code(code) for code in codes if code in self.color_codes) + return buffer + self._handle_text(node) + '' + + def color_code(self, code: str): + return '' + + +class SC2Context(CommonContext): + command_processor = StarcraftClientProcessor + game = "Starcraft 2 Wings of Liberty" + items_handling = 0b111 + difficulty = -1 + game_speed = -1 + all_in_choice = 0 + mission_order = 0 + player_color = 2 + mission_req_table: typing.Dict[str, MissionInfo] = {} + final_mission: int = 29 + announcements = queue.Queue() + sc2_run_task: typing.Optional[asyncio.Task] = None + missions_unlocked: bool = False # allow launching missions ignoring requirements + generic_upgrade_missions = 0 + generic_upgrade_research = 0 + generic_upgrade_items = 0 + current_tooltip = None + last_loc_list = None + difficulty_override = -1 + game_speed_override = -1 + mission_id_to_location_ids: typing.Dict[int, typing.List[int]] = {} + last_bot: typing.Optional[ArchipelagoBot] = None + + def __init__(self, *args, **kwargs): + super(SC2Context, self).__init__(*args, **kwargs) + self.raw_text_parser = SC2JSONtoTextParser(self) + + async def server_auth(self, password_requested: bool = False): + if password_requested and not self.password: + await super(SC2Context, self).server_auth(password_requested) + await self.get_username() + await self.send_connect() + + def on_package(self, cmd: str, args: dict): + if cmd in {"Connected"}: + self.difficulty = args["slot_data"]["game_difficulty"] + if "game_speed" in args["slot_data"]: + self.game_speed = args["slot_data"]["game_speed"] + else: + self.game_speed = 0 + self.all_in_choice = args["slot_data"]["all_in_map"] + slot_req_table = args["slot_data"]["mission_req"] + # Maintaining backwards compatibility with older slot data + self.mission_req_table = { + mission: MissionInfo( + **{field: value for field, value in mission_info.items() if field in MissionInfo._fields} + ) + for mission, mission_info in slot_req_table.items() + } + self.mission_order = args["slot_data"].get("mission_order", 0) + self.final_mission = args["slot_data"].get("final_mission", 29) + self.player_color = args["slot_data"].get("player_color", 2) + self.generic_upgrade_missions = args["slot_data"].get("generic_upgrade_missions", 0) + self.generic_upgrade_items = args["slot_data"].get("generic_upgrade_items", 0) + self.generic_upgrade_research = args["slot_data"].get("generic_upgrade_research", 0) + + self.build_location_to_mission_mapping() + + # Looks for the required maps and mods for SC2. Runs check_game_install_path. + maps_present = is_mod_installed_correctly() + if os.path.exists(get_metadata_file()): + with open(get_metadata_file(), "r") as f: + current_ver = f.read() + sc2_logger.debug(f"Current version: {current_ver}") + if is_mod_update_available(DATA_REPO_OWNER, DATA_REPO_NAME, DATA_API_VERSION, current_ver): + sc2_logger.info("NOTICE: Update for required files found. Run /download_data to install.") + elif maps_present: + sc2_logger.warning("NOTICE: Your map files may be outdated (version number not found). " + "Run /download_data to update them.") + + + def on_print_json(self, args: dict): + # goes to this world + if "receiving" in args and self.slot_concerns_self(args["receiving"]): + relevant = True + # found in this world + elif "item" in args and self.slot_concerns_self(args["item"].player): + relevant = True + # not related + else: + relevant = False + + if relevant: + self.announcements.put(self.raw_text_parser(copy.deepcopy(args["data"]))) + + super(SC2Context, self).on_print_json(args) + + def run_gui(self): + from kvui import GameManager, HoverBehavior, ServerToolTip + from kivy.app import App + from kivy.clock import Clock + from kivy.uix.tabbedpanel import TabbedPanelItem + from kivy.uix.gridlayout import GridLayout + from kivy.lang import Builder + from kivy.uix.label import Label + from kivy.uix.button import Button + from kivy.uix.floatlayout import FloatLayout + from kivy.properties import StringProperty + + class HoverableButton(HoverBehavior, Button): + pass + + class MissionButton(HoverableButton): + tooltip_text = StringProperty("Test") + ctx: SC2Context + + def __init__(self, *args, **kwargs): + super(HoverableButton, self).__init__(*args, **kwargs) + self.layout = FloatLayout() + self.popuplabel = ServerToolTip(text=self.text) + self.layout.add_widget(self.popuplabel) + + def on_enter(self): + self.popuplabel.text = self.tooltip_text + + if self.ctx.current_tooltip: + App.get_running_app().root.remove_widget(self.ctx.current_tooltip) + + if self.tooltip_text == "": + self.ctx.current_tooltip = None + else: + App.get_running_app().root.add_widget(self.layout) + self.ctx.current_tooltip = self.layout + + def on_leave(self): + self.ctx.ui.clear_tooltip() + + @property + def ctx(self) -> CommonContext: + return App.get_running_app().ctx + + class MissionLayout(GridLayout): + pass + + class MissionCategory(GridLayout): + pass + + class SC2Manager(GameManager): + logging_pairs = [ + ("Client", "Archipelago"), + ("Starcraft2", "Starcraft2"), + ] + base_title = "Archipelago Starcraft 2 Client" + + mission_panel = None + last_checked_locations = {} + mission_id_to_button = {} + launching: typing.Union[bool, int] = False # if int -> mission ID + refresh_from_launching = True + first_check = True + ctx: SC2Context + + def __init__(self, ctx): + super().__init__(ctx) + + def clear_tooltip(self): + if self.ctx.current_tooltip: + App.get_running_app().root.remove_widget(self.ctx.current_tooltip) + + self.ctx.current_tooltip = None + + def build(self): + container = super().build() + + panel = TabbedPanelItem(text="Starcraft 2 Launcher") + self.mission_panel = panel.content = MissionLayout() + + self.tabs.add_widget(panel) + + Clock.schedule_interval(self.build_mission_table, 0.5) + + return container + + def build_mission_table(self, dt): + if (not self.launching and (not self.last_checked_locations == self.ctx.checked_locations or + not self.refresh_from_launching)) or self.first_check: + self.refresh_from_launching = True + + self.mission_panel.clear_widgets() + if self.ctx.mission_req_table: + self.last_checked_locations = self.ctx.checked_locations.copy() + self.first_check = False + + self.mission_id_to_button = {} + categories = {} + available_missions, unfinished_missions = calc_unfinished_missions(self.ctx) + + # separate missions into categories + for mission in self.ctx.mission_req_table: + if not self.ctx.mission_req_table[mission].category in categories: + categories[self.ctx.mission_req_table[mission].category] = [] + + categories[self.ctx.mission_req_table[mission].category].append(mission) + + for category in categories: + category_panel = MissionCategory() + if category.startswith('_'): + category_display_name = '' + else: + category_display_name = category + category_panel.add_widget( + Label(text=category_display_name, size_hint_y=None, height=50, outline_width=1)) + + for mission in categories[category]: + text: str = mission + tooltip: str = "" + mission_id: int = self.ctx.mission_req_table[mission].id + # Map has uncollected locations + if mission in unfinished_missions: + text = f"[color=6495ED]{text}[/color]" + elif mission in available_missions: + text = f"[color=FFFFFF]{text}[/color]" + # Map requirements not met + else: + text = f"[color=a9a9a9]{text}[/color]" + tooltip = f"Requires: " + if self.ctx.mission_req_table[mission].required_world: + tooltip += ", ".join(list(self.ctx.mission_req_table)[req_mission - 1] for + req_mission in + self.ctx.mission_req_table[mission].required_world) + + if self.ctx.mission_req_table[mission].number: + tooltip += " and " + if self.ctx.mission_req_table[mission].number: + tooltip += f"{self.ctx.mission_req_table[mission].number} missions completed" + remaining_location_names: typing.List[str] = [ + self.ctx.location_names[loc] for loc in self.ctx.locations_for_mission(mission) + if loc in self.ctx.missing_locations] + + if mission_id == self.ctx.final_mission: + if mission in available_missions: + text = f"[color=FFBC95]{mission}[/color]" + else: + text = f"[color=D0C0BE]{mission}[/color]" + if tooltip: + tooltip += "\n" + tooltip += "Final Mission" + + if remaining_location_names: + if tooltip: + tooltip += "\n" + tooltip += f"Uncollected locations:\n" + tooltip += "\n".join(remaining_location_names) + + mission_button = MissionButton(text=text, size_hint_y=None, height=50) + mission_button.tooltip_text = tooltip + mission_button.bind(on_press=self.mission_callback) + self.mission_id_to_button[mission_id] = mission_button + category_panel.add_widget(mission_button) + + category_panel.add_widget(Label(text="")) + self.mission_panel.add_widget(category_panel) + + elif self.launching: + self.refresh_from_launching = False + + self.mission_panel.clear_widgets() + self.mission_panel.add_widget(Label(text="Launching Mission: " + + lookup_id_to_mission[self.launching])) + if self.ctx.ui: + self.ctx.ui.clear_tooltip() + + def mission_callback(self, button): + if not self.launching: + mission_id: int = next(k for k, v in self.mission_id_to_button.items() if v == button) + if self.ctx.play_mission(mission_id): + self.launching = mission_id + Clock.schedule_once(self.finish_launching, 10) + + def finish_launching(self, dt): + self.launching = False + + self.ui = SC2Manager(self) + self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI") + import pkgutil + data = pkgutil.get_data(SC2WoLWorld.__module__, "Starcraft2.kv").decode() + Builder.load_string(data) + + async def shutdown(self): + await super(SC2Context, self).shutdown() + if self.last_bot: + self.last_bot.want_close = True + if self.sc2_run_task: + self.sc2_run_task.cancel() + + def play_mission(self, mission_id: int) -> bool: + if self.missions_unlocked or \ + is_mission_available(self, mission_id): + if self.sc2_run_task: + if not self.sc2_run_task.done(): + sc2_logger.warning("Starcraft 2 Client is still running!") + self.sc2_run_task.cancel() # doesn't actually close the game, just stops the python task + if self.slot is None: + sc2_logger.warning("Launching Mission without Archipelago authentication, " + "checks will not be registered to server.") + self.sc2_run_task = asyncio.create_task(starcraft_launch(self, mission_id), + name="Starcraft 2 Launch") + return True + else: + sc2_logger.info( + f"{lookup_id_to_mission[mission_id]} is not currently unlocked. " + f"Use /unfinished or /available to see what is available.") + return False + + def build_location_to_mission_mapping(self): + mission_id_to_location_ids: typing.Dict[int, typing.Set[int]] = { + mission_info.id: set() for mission_info in self.mission_req_table.values() + } + + for loc in self.server_locations: + mission_id, objective = divmod(loc - SC2WOL_LOC_ID_OFFSET, victory_modulo) + mission_id_to_location_ids[mission_id].add(objective) + self.mission_id_to_location_ids = {mission_id: sorted(objectives) for mission_id, objectives in + mission_id_to_location_ids.items()} + + def locations_for_mission(self, mission: str): + mission_id: int = self.mission_req_table[mission].id + objectives = self.mission_id_to_location_ids[self.mission_req_table[mission].id] + for objective in objectives: + yield SC2WOL_LOC_ID_OFFSET + mission_id * 100 + objective + + +async def main(): + multiprocessing.freeze_support() + parser = get_base_parser() + parser.add_argument('--name', default=None, help="Slot Name to connect as.") + args = parser.parse_args() + + ctx = SC2Context(args.connect, args.password) + ctx.auth = args.name + if ctx.server_task is None: + ctx.server_task = asyncio.create_task(server_loop(ctx), name="ServerLoop") + + if gui_enabled: + ctx.run_gui() + ctx.run_cli() + + await ctx.exit_event.wait() + + await ctx.shutdown() + + +maps_table = [ + "ap_liberation_day", "ap_the_outlaws", "ap_zero_hour", + "ap_evacuation", "ap_outbreak", "ap_safe_haven", "ap_havens_fall", + "ap_smash_and_grab", "ap_the_dig", "ap_the_moebius_factor", "ap_supernova", "ap_maw_of_the_void", + "ap_devils_playground", "ap_welcome_to_the_jungle", "ap_breakout", "ap_ghost_of_a_chance", + "ap_the_great_train_robbery", "ap_cutthroat", "ap_engine_of_destruction", "ap_media_blitz", "ap_piercing_the_shroud", + "ap_whispers_of_doom", "ap_a_sinister_turn", "ap_echoes_of_the_future", "ap_in_utter_darkness", + "ap_gates_of_hell", "ap_belly_of_the_beast", "ap_shatter_the_sky", "ap_all_in" +] + +wol_default_categories = [ + "Mar Sara", "Mar Sara", "Mar Sara", "Colonist", "Colonist", "Colonist", "Colonist", + "Artifact", "Artifact", "Artifact", "Artifact", "Artifact", "Covert", "Covert", "Covert", "Covert", + "Rebellion", "Rebellion", "Rebellion", "Rebellion", "Rebellion", "Prophecy", "Prophecy", "Prophecy", "Prophecy", + "Char", "Char", "Char", "Char" +] +wol_default_category_names = [ + "Mar Sara", "Colonist", "Artifact", "Covert", "Rebellion", "Prophecy", "Char" +] + + +def calculate_items(ctx: SC2Context) -> typing.List[int]: + items = ctx.items_received + network_item: NetworkItem + accumulators: typing.List[int] = [0 for _ in type_flaggroups] + + for network_item in items: + name: str = lookup_id_to_name[network_item.item] + item_data: ItemData = get_full_item_list()[name] + + # exists exactly once + if item_data.quantity == 1: + accumulators[type_flaggroups[item_data.type]] |= 1 << item_data.number + + # exists multiple times + elif item_data.type == "Upgrade" or item_data.type == "Progressive Upgrade": + flaggroup = type_flaggroups[item_data.type] + + # Generic upgrades apply only to Weapon / Armor upgrades + if item_data.type != "Upgrade" or ctx.generic_upgrade_items == 0: + accumulators[flaggroup] += 1 << item_data.number + else: + for bundled_number in upgrade_numbers[item_data.number]: + accumulators[flaggroup] += 1 << bundled_number + + # sum + else: + accumulators[type_flaggroups[item_data.type]] += item_data.number + + # Upgrades from completed missions + if ctx.generic_upgrade_missions > 0: + upgrade_flaggroup = type_flaggroups["Upgrade"] + num_missions = ctx.generic_upgrade_missions * len(ctx.mission_req_table) + amounts = [ + num_missions // 100, + 2 * num_missions // 100, + 3 * num_missions // 100 + ] + upgrade_count = 0 + completed = len([id for id in ctx.mission_id_to_location_ids if SC2WOL_LOC_ID_OFFSET + victory_modulo * id in ctx.checked_locations]) + for amount in amounts: + if completed >= amount: + upgrade_count += 1 + # Equivalent to "Progressive Weapon/Armor Upgrade" item + for bundled_number in upgrade_numbers[5]: + accumulators[upgrade_flaggroup] += upgrade_count << bundled_number + + return accumulators + + +def calc_difficulty(difficulty): + if difficulty == 0: + return 'C' + elif difficulty == 1: + return 'N' + elif difficulty == 2: + return 'H' + elif difficulty == 3: + return 'B' + + return 'X' + + +async def starcraft_launch(ctx: SC2Context, mission_id: int): + sc2_logger.info(f"Launching {lookup_id_to_mission[mission_id]}. If game does not launch check log file for errors.") + + with DllDirectory(None): + run_game(bot.maps.get(maps_table[mission_id - 1]), [Bot(Race.Terran, ArchipelagoBot(ctx, mission_id), + name="Archipelago", fullscreen=True)], realtime=True) + + +class ArchipelagoBot(bot.bot_ai.BotAI): + game_running: bool = False + mission_completed: bool = False + boni: typing.List[bool] + setup_done: bool + ctx: SC2Context + mission_id: int + want_close: bool = False + can_read_game = False + last_received_update: int = 0 + + def __init__(self, ctx: SC2Context, mission_id): + self.setup_done = False + self.ctx = ctx + self.ctx.last_bot = self + self.mission_id = mission_id + self.boni = [False for _ in range(max_bonus)] + + super(ArchipelagoBot, self).__init__() + + async def on_step(self, iteration: int): + if self.want_close: + self.want_close = False + await self._client.leave() + return + game_state = 0 + if not self.setup_done: + self.setup_done = True + start_items = calculate_items(self.ctx) + if self.ctx.difficulty_override >= 0: + difficulty = calc_difficulty(self.ctx.difficulty_override) + else: + difficulty = calc_difficulty(self.ctx.difficulty) + if self.ctx.game_speed_override >= 0: + game_speed = self.ctx.game_speed_override + else: + game_speed = self.ctx.game_speed + await self.chat_send("?SetOptions {} {} {} {}".format( + difficulty, + self.ctx.generic_upgrade_research, + self.ctx.all_in_choice, + game_speed + )) + await self.chat_send("?GiveResources {} {} {}".format( + start_items[8], + start_items[9], + start_items[10] + )) + await self.chat_send("?GiveTerranTech {} {} {} {} {} {} {} {} {} {}".format( + start_items[0], start_items[1], start_items[2], start_items[3], start_items[4], + start_items[5], start_items[6], start_items[12], start_items[13], start_items[14])) + await self.chat_send("?GiveProtossTech {}".format(start_items[7])) + await self.chat_send("?SetColor rr " + str(self.ctx.player_color)) # TODO: Add faction color options + await self.chat_send("?LoadFinished") + self.last_received_update = len(self.ctx.items_received) + + else: + if not self.ctx.announcements.empty(): + message = self.ctx.announcements.get(timeout=1) + await self.chat_send("?SendMessage " + message) + self.ctx.announcements.task_done() + + # Archipelago reads the health + for unit in self.all_own_units(): + if unit.health_max == 38281: + game_state = int(38281 - unit.health) + self.can_read_game = True + + if iteration == 160 and not game_state & 1: + await self.chat_send("?SendMessage Warning: Archipelago unable to connect or has lost connection to " + + "Starcraft 2 (This is likely a map issue)") + + if self.last_received_update < len(self.ctx.items_received): + current_items = calculate_items(self.ctx) + await self.chat_send("?GiveTerranTech {} {} {} {} {} {} {} {} {} {}".format( + current_items[0], current_items[1], current_items[2], current_items[3], current_items[4], + current_items[5], current_items[6], current_items[12], current_items[13], current_items[14])) + await self.chat_send("?GiveProtossTech {}".format(current_items[7])) + self.last_received_update = len(self.ctx.items_received) + + if game_state & 1: + if not self.game_running: + print("Archipelago Connected") + self.game_running = True + + if self.can_read_game: + if game_state & (1 << 1) and not self.mission_completed: + if self.mission_id != self.ctx.final_mission: + print("Mission Completed") + await self.ctx.send_msgs( + [{"cmd": 'LocationChecks', + "locations": [SC2WOL_LOC_ID_OFFSET + victory_modulo * self.mission_id]}]) + self.mission_completed = True + else: + print("Game Complete") + await self.ctx.send_msgs([{"cmd": 'StatusUpdate', "status": ClientStatus.CLIENT_GOAL}]) + self.mission_completed = True + + for x, completed in enumerate(self.boni): + if not completed and game_state & (1 << (x + 2)): + await self.ctx.send_msgs( + [{"cmd": 'LocationChecks', + "locations": [SC2WOL_LOC_ID_OFFSET + victory_modulo * self.mission_id + x + 1]}]) + self.boni[x] = True + + else: + await self.chat_send("?SendMessage LostConnection - Lost connection to game.") + + +def request_unfinished_missions(ctx: SC2Context): + if ctx.mission_req_table: + message = "Unfinished Missions: " + unlocks = initialize_blank_mission_dict(ctx.mission_req_table) + unfinished_locations = initialize_blank_mission_dict(ctx.mission_req_table) + + _, unfinished_missions = calc_unfinished_missions(ctx, unlocks=unlocks) + + # Removing All-In from location pool + final_mission = lookup_id_to_mission[ctx.final_mission] + if final_mission in unfinished_missions.keys(): + message = f"Final Mission Available: {final_mission}[{ctx.final_mission}]\n" + message + if unfinished_missions[final_mission] == -1: + unfinished_missions.pop(final_mission) + + message += ", ".join(f"{mark_up_mission_name(ctx, mission, unlocks)}[{ctx.mission_req_table[mission].id}] " + + mark_up_objectives( + f"[{len(unfinished_missions[mission])}/" + f"{sum(1 for _ in ctx.locations_for_mission(mission))}]", + ctx, unfinished_locations, mission) + for mission in unfinished_missions) + + if ctx.ui: + ctx.ui.log_panels['All'].on_message_markup(message) + ctx.ui.log_panels['Starcraft2'].on_message_markup(message) + else: + sc2_logger.info(message) + else: + sc2_logger.warning("No mission table found, you are likely not connected to a server.") + + +def calc_unfinished_missions(ctx: SC2Context, unlocks=None): + unfinished_missions = [] + locations_completed = [] + + if not unlocks: + unlocks = initialize_blank_mission_dict(ctx.mission_req_table) + + available_missions = calc_available_missions(ctx, unlocks) + + for name in available_missions: + objectives = set(ctx.locations_for_mission(name)) + if objectives: + objectives_completed = ctx.checked_locations & objectives + if len(objectives_completed) < len(objectives): + unfinished_missions.append(name) + locations_completed.append(objectives_completed) + + else: # infer that this is the final mission as it has no objectives + unfinished_missions.append(name) + locations_completed.append(-1) + + return available_missions, dict(zip(unfinished_missions, locations_completed)) + + +def is_mission_available(ctx: SC2Context, mission_id_to_check): + unfinished_missions = calc_available_missions(ctx) + + return any(mission_id_to_check == ctx.mission_req_table[mission].id for mission in unfinished_missions) + + +def mark_up_mission_name(ctx: SC2Context, mission, unlock_table): + """Checks if the mission is required for game completion and adds '*' to the name to mark that.""" + + if ctx.mission_req_table[mission].completion_critical: + if ctx.ui: + message = "[color=AF99EF]" + mission + "[/color]" + else: + message = "*" + mission + "*" + else: + message = mission + + if ctx.ui: + unlocks = unlock_table[mission] + + if len(unlocks) > 0: + pre_message = f"[ref={list(ctx.mission_req_table).index(mission)}|Unlocks: " + pre_message += ", ".join(f"{unlock}({ctx.mission_req_table[unlock].id})" for unlock in unlocks) + pre_message += f"]" + message = pre_message + message + "[/ref]" + + return message + + +def mark_up_objectives(message, ctx, unfinished_locations, mission): + formatted_message = message + + if ctx.ui: + locations = unfinished_locations[mission] + + pre_message = f"[ref={list(ctx.mission_req_table).index(mission) + 30}|" + pre_message += "
".join(location for location in locations) + pre_message += f"]" + formatted_message = pre_message + message + "[/ref]" + + return formatted_message + + +def request_available_missions(ctx: SC2Context): + if ctx.mission_req_table: + message = "Available Missions: " + + # Initialize mission unlock table + unlocks = initialize_blank_mission_dict(ctx.mission_req_table) + + missions = calc_available_missions(ctx, unlocks) + message += \ + ", ".join(f"{mark_up_mission_name(ctx, mission, unlocks)}" + f"[{ctx.mission_req_table[mission].id}]" + for mission in missions) + + if ctx.ui: + ctx.ui.log_panels['All'].on_message_markup(message) + ctx.ui.log_panels['Starcraft2'].on_message_markup(message) + else: + sc2_logger.info(message) + else: + sc2_logger.warning("No mission table found, you are likely not connected to a server.") + + +def calc_available_missions(ctx: SC2Context, unlocks=None): + available_missions = [] + missions_complete = 0 + + # Get number of missions completed + for loc in ctx.checked_locations: + if loc % victory_modulo == 0: + missions_complete += 1 + + for name in ctx.mission_req_table: + # Go through the required missions for each mission and fill up unlock table used later for hover-over tooltips + if unlocks: + for unlock in ctx.mission_req_table[name].required_world: + unlocks[list(ctx.mission_req_table)[unlock - 1]].append(name) + + if mission_reqs_completed(ctx, name, missions_complete): + available_missions.append(name) + + return available_missions + + +def mission_reqs_completed(ctx: SC2Context, mission_name: str, missions_complete: int): + """Returns a bool signifying if the mission has all requirements complete and can be done + + Arguments: + ctx -- instance of SC2Context + locations_to_check -- the mission string name to check + missions_complete -- an int of how many missions have been completed + mission_path -- a list of missions that have already been checked +""" + if len(ctx.mission_req_table[mission_name].required_world) >= 1: + # A check for when the requirements are being or'd + or_success = False + + # Loop through required missions + for req_mission in ctx.mission_req_table[mission_name].required_world: + req_success = True + + # Check if required mission has been completed + if not (ctx.mission_req_table[list(ctx.mission_req_table)[req_mission - 1]].id * + victory_modulo + SC2WOL_LOC_ID_OFFSET) in ctx.checked_locations: + if not ctx.mission_req_table[mission_name].or_requirements: + return False + else: + req_success = False + + # Grid-specific logic (to avoid long path checks and infinite recursion) + if ctx.mission_order in (3, 4): + if req_success: + return True + else: + if req_mission is ctx.mission_req_table[mission_name].required_world[-1]: + return False + else: + continue + + # Recursively check required mission to see if it's requirements are met, in case !collect has been done + # Skipping recursive check on Grid settings to speed up checks and avoid infinite recursion + if not mission_reqs_completed(ctx, list(ctx.mission_req_table)[req_mission - 1], missions_complete): + if not ctx.mission_req_table[mission_name].or_requirements: + return False + else: + req_success = False + + # If requirement check succeeded mark or as satisfied + if ctx.mission_req_table[mission_name].or_requirements and req_success: + or_success = True + + if ctx.mission_req_table[mission_name].or_requirements: + # Return false if or requirements not met + if not or_success: + return False + + # Check number of missions + if missions_complete >= ctx.mission_req_table[mission_name].number: + return True + else: + return False + else: + return True + + +def initialize_blank_mission_dict(location_table): + unlocks = {} + + for mission in list(location_table): + unlocks[mission] = [] + + return unlocks + + +def check_game_install_path() -> bool: + # First thing: go to the default location for ExecuteInfo. + # An exception for Windows is included because it's very difficult to find ~\Documents if the user moved it. + if is_windows: + # The next five lines of utterly inscrutable code are brought to you by copy-paste from Stack Overflow. + # https://stackoverflow.com/questions/6227590/finding-the-users-my-documents-path/30924555# + import ctypes.wintypes + CSIDL_PERSONAL = 5 # My Documents + SHGFP_TYPE_CURRENT = 0 # Get current, not default value + + buf = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH) + ctypes.windll.shell32.SHGetFolderPathW(None, CSIDL_PERSONAL, None, SHGFP_TYPE_CURRENT, buf) + documentspath = buf.value + einfo = str(documentspath / Path("StarCraft II\\ExecuteInfo.txt")) + else: + einfo = str(bot.paths.get_home() / Path(bot.paths.USERPATH[bot.paths.PF])) + + # Check if the file exists. + if os.path.isfile(einfo): + + # Open the file and read it, picking out the latest executable's path. + with open(einfo) as f: + content = f.read() + if content: + try: + base = re.search(r" = (.*)Versions", content).group(1) + except AttributeError: + sc2_logger.warning(f"Found {einfo}, but it was empty. Run SC2 through the Blizzard launcher, then " + f"try again.") + return False + if os.path.exists(base): + executable = bot.paths.latest_executeble(Path(base).expanduser() / "Versions") + + # Finally, check the path for an actual executable. + # If we find one, great. Set up the SC2PATH. + if os.path.isfile(executable): + sc2_logger.info(f"Found an SC2 install at {base}!") + sc2_logger.debug(f"Latest executable at {executable}.") + os.environ["SC2PATH"] = base + sc2_logger.debug(f"SC2PATH set to {base}.") + return True + else: + sc2_logger.warning(f"We may have found an SC2 install at {base}, but couldn't find {executable}.") + else: + sc2_logger.warning(f"{einfo} pointed to {base}, but we could not find an SC2 install there.") + else: + sc2_logger.warning(f"Couldn't find {einfo}. Run SC2 through the Blizzard launcher, then try again. " + f"If that fails, please run /set_path with your SC2 install directory.") + return False + + +def is_mod_installed_correctly() -> bool: + """Searches for all required files.""" + if "SC2PATH" not in os.environ: + check_game_install_path() + + mapdir = os.environ['SC2PATH'] / Path('Maps/ArchipelagoCampaign') + mods = ["ArchipelagoCore", "ArchipelagoPlayer", "ArchipelagoPlayerWoL", "ArchipelagoTriggers"] + modfiles = [os.environ["SC2PATH"] / Path("Mods/" + mod + ".SC2Mod") for mod in mods] + wol_required_maps = ["WoL" + os.sep + map_name + ".SC2Map" for map_name in maps_table] + needs_files = False + + # Check for maps. + missing_maps = [] + for mapfile in wol_required_maps: + if not os.path.isfile(mapdir / mapfile): + missing_maps.append(mapfile) + if len(missing_maps) >= 19: + sc2_logger.warning(f"All map files missing from {mapdir}.") + needs_files = True + elif len(missing_maps) > 0: + for map in missing_maps: + sc2_logger.debug(f"Missing {map} from {mapdir}.") + sc2_logger.warning(f"Missing {len(missing_maps)} map files.") + needs_files = True + else: # Must be no maps missing + sc2_logger.info(f"All maps found in {mapdir}.") + + # Check for mods. + for modfile in modfiles: + if os.path.isfile(modfile) or os.path.isdir(modfile): + sc2_logger.info(f"Archipelago mod found at {modfile}.") + else: + sc2_logger.warning(f"Archipelago mod could not be found at {modfile}.") + needs_files = True + + # Final verdict. + if needs_files: + sc2_logger.warning(f"Required files are missing. Run /download_data to acquire them.") + return False + else: + sc2_logger.debug(f"All map/mod files are properly installed.") + return True + + +class DllDirectory: + # Credit to Black Sliver for this code. + # More info: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setdlldirectoryw + _old: typing.Optional[str] = None + _new: typing.Optional[str] = None + + def __init__(self, new: typing.Optional[str]): + self._new = new + + def __enter__(self): + old = self.get() + if self.set(self._new): + self._old = old + + def __exit__(self, *args): + if self._old is not None: + self.set(self._old) + + @staticmethod + def get() -> typing.Optional[str]: + if sys.platform == "win32": + n = ctypes.windll.kernel32.GetDllDirectoryW(0, None) + buf = ctypes.create_unicode_buffer(n) + ctypes.windll.kernel32.GetDllDirectoryW(n, buf) + return buf.value + # NOTE: other OS may support os.environ["LD_LIBRARY_PATH"], but this fix is windows-specific + return None + + @staticmethod + def set(s: typing.Optional[str]) -> bool: + if sys.platform == "win32": + return ctypes.windll.kernel32.SetDllDirectoryW(s) != 0 + # NOTE: other OS may support os.environ["LD_LIBRARY_PATH"], but this fix is windows-specific + return False + + +def download_latest_release_zip(owner: str, repo: str, api_version: str, metadata: str = None, force_download=False) -> (str, str): + """Downloads the latest release of a GitHub repo to the current directory as a .zip file.""" + import requests + + headers = {"Accept": 'application/vnd.github.v3+json'} + url = f"https://api.github.com/repos/{owner}/{repo}/releases/tags/{api_version}" + + r1 = requests.get(url, headers=headers) + if r1.status_code == 200: + latest_metadata = str(r1.json()) + # sc2_logger.info(f"Latest version: {latest_metadata}.") + else: + sc2_logger.warning(f"Status code: {r1.status_code}") + sc2_logger.warning(f"Failed to reach GitHub. Could not find download link.") + sc2_logger.warning(f"text: {r1.text}") + return "", metadata + + if (force_download is False) and (metadata == latest_metadata): + sc2_logger.info("Latest version already installed.") + return "", metadata + + sc2_logger.info(f"Attempting to download latest version of API version {api_version} of {repo}.") + download_url = r1.json()["assets"][0]["browser_download_url"] + + r2 = requests.get(download_url, headers=headers) + if r2.status_code == 200 and zipfile.is_zipfile(io.BytesIO(r2.content)): + with open(f"{repo}.zip", "wb") as fh: + fh.write(r2.content) + sc2_logger.info(f"Successfully downloaded {repo}.zip.") + return f"{repo}.zip", latest_metadata + else: + sc2_logger.warning(f"Status code: {r2.status_code}") + sc2_logger.warning("Download failed.") + sc2_logger.warning(f"text: {r2.text}") + return "", metadata + + +def is_mod_update_available(owner: str, repo: str, api_version: str, metadata: str) -> bool: + import requests + + headers = {"Accept": 'application/vnd.github.v3+json'} + url = f"https://api.github.com/repos/{owner}/{repo}/releases/tags/{api_version}" + + r1 = requests.get(url, headers=headers) + if r1.status_code == 200: + latest_metadata = str(r1.json()) + if metadata != latest_metadata: + return True + else: + return False + + else: + sc2_logger.warning(f"Failed to reach GitHub while checking for updates.") + sc2_logger.warning(f"Status code: {r1.status_code}") + sc2_logger.warning(f"text: {r1.text}") + return False + + +def launch(): + colorama.init() + asyncio.run(main()) + colorama.deinit() diff --git a/worlds/sc2wol/Items.py b/worlds/sc2wol/Items.py index ea495adf79..971a75375f 100644 --- a/worlds/sc2wol/Items.py +++ b/worlds/sc2wol/Items.py @@ -12,6 +12,7 @@ class ItemData(typing.NamedTuple): classification: ItemClassification = ItemClassification.useful quantity: int = 1 parent_item: str = None + origin: typing.Set[str] = {"wol"} class StarcraftWoLItem(Item): @@ -43,23 +44,36 @@ item_table = { "Ghost": ItemData(15 + SC2WOL_ITEM_ID_OFFSET, "Unit", 15, classification=ItemClassification.progression), "Spectre": ItemData(16 + SC2WOL_ITEM_ID_OFFSET, "Unit", 16, classification=ItemClassification.progression), "Thor": ItemData(17 + SC2WOL_ITEM_ID_OFFSET, "Unit", 17, classification=ItemClassification.progression), + # EE units + "Liberator": ItemData(18 + SC2WOL_ITEM_ID_OFFSET, "Unit", 18, classification=ItemClassification.progression, origin={"nco", "ext"}), + "Valkyrie": ItemData(19 + SC2WOL_ITEM_ID_OFFSET, "Unit", 19, classification=ItemClassification.progression, origin={"bw"}), + "Widow Mine": ItemData(20 + SC2WOL_ITEM_ID_OFFSET, "Unit", 20, classification=ItemClassification.progression, origin={"ext"}), + "Cyclone": ItemData(21 + SC2WOL_ITEM_ID_OFFSET, "Unit", 21, classification=ItemClassification.progression, origin={"ext"}), + # Some other items are moved to Upgrade group because of the way how the bot message is parsed "Progressive Infantry Weapon": ItemData(100 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 0, quantity=3), "Progressive Infantry Armor": ItemData(102 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 2, quantity=3), "Progressive Vehicle Weapon": ItemData(103 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 4, quantity=3), "Progressive Vehicle Armor": ItemData(104 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 6, quantity=3), "Progressive Ship Weapon": ItemData(105 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 8, quantity=3), "Progressive Ship Armor": ItemData(106 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 10, quantity=3), + # Upgrade bundle 'number' values are used as indices to get affected 'number's + "Progressive Weapon Upgrade": ItemData(107 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 0, quantity=3), + "Progressive Armor Upgrade": ItemData(108 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 1, quantity=3), + "Progressive Infantry Upgrade": ItemData(109 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 2, quantity=3), + "Progressive Vehicle Upgrade": ItemData(110 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 3, quantity=3), + "Progressive Ship Upgrade": ItemData(111 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 4, quantity=3), + "Progressive Weapon/Armor Upgrade": ItemData(112 + SC2WOL_ITEM_ID_OFFSET, "Upgrade", 5, quantity=3), "Projectile Accelerator (Bunker)": ItemData(200 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 0, parent_item="Bunker"), "Neosteel Bunker (Bunker)": ItemData(201 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 1, parent_item="Bunker"), "Titanium Housing (Missile Turret)": ItemData(202 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 2, classification=ItemClassification.filler, parent_item="Missile Turret"), "Hellstorm Batteries (Missile Turret)": ItemData(203 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 3, parent_item="Missile Turret"), - "Advanced Construction (SCV)": ItemData(204 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 4, parent_item="SCV"), - "Dual-Fusion Welders (SCV)": ItemData(205 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 5, parent_item="SCV"), + "Advanced Construction (SCV)": ItemData(204 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 4), + "Dual-Fusion Welders (SCV)": ItemData(205 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 5), "Fire-Suppression System (Building)": ItemData(206 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 6), "Orbital Command (Building)": ItemData(207 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 7), - "Stimpack (Marine)": ItemData(208 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 8, parent_item="Marine"), + "Progressive Stimpack (Marine)": ItemData(208 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 0, parent_item="Marine", quantity=2), "Combat Shield (Marine)": ItemData(209 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 9, classification=ItemClassification.progression, parent_item="Marine"), "Advanced Medic Facilities (Medic)": ItemData(210 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 10, classification=ItemClassification.filler, parent_item="Medic"), "Stabilizer Medpacks (Medic)": ItemData(211 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 11, classification=ItemClassification.progression, parent_item="Medic"), @@ -69,10 +83,59 @@ item_table = { "Kinetic Foam (Marauder)": ItemData(215 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 15, parent_item="Marauder"), "U-238 Rounds (Reaper)": ItemData(216 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 16, parent_item="Reaper"), "G-4 Clusterbomb (Reaper)": ItemData(217 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 17, classification=ItemClassification.progression, parent_item="Reaper"), + # Items from EE + "Mag-Field Accelerators (Cyclone)": ItemData(218 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 18, parent_item="Cyclone", origin={"ext"}), + "Mag-Field Launchers (Cyclone)": ItemData(219 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 19, parent_item="Cyclone", origin={"ext"}), + # Items from new mod + "Laser Targeting System (Marine)": ItemData(220 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 8, classification=ItemClassification.filler, parent_item="Marine", origin={"nco"}), # Freed slot from Stimpack + "Magrail Munitions (Marine)": ItemData(221 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 20, parent_item="Marine", origin={"nco"}), + "Optimized Logistics (Marine)": ItemData(222 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 21, classification=ItemClassification.filler, parent_item="Marine", origin={"nco"}), + "Restoration (Medic)": ItemData(223 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 22, classification=ItemClassification.filler, parent_item="Medic", origin={"bw"}), + "Optical Flare (Medic)": ItemData(224 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 23, classification=ItemClassification.filler, parent_item="Medic", origin={"bw"}), + "Optimized Logistics (Medic)": ItemData(225 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 24, classification=ItemClassification.filler, parent_item="Medic", origin={"bw"}), + "Progressive Stimpack (Firebat)": ItemData(226 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 6, parent_item="Firebat", quantity=2, origin={"bw"}), + "Optimized Logistics (Firebat)": ItemData(227 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 25, parent_item="Firebat", origin={"bw"}), + "Progressive Stimpack (Marauder)": ItemData(228 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 8, parent_item="Marauder", quantity=2, origin={"nco"}), + "Laser Targeting System (Marauder)": ItemData(229 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 26, classification=ItemClassification.filler, parent_item="Marauder", origin={"nco"}), + "Magrail Munitions (Marauder)": ItemData(230 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 27, classification=ItemClassification.filler, parent_item="Marauder", origin={"nco"}), + "Internal Tech Module (Marauder)": ItemData(231 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 28, classification=ItemClassification.filler, parent_item="Marauder", origin={"nco"}), + + # Items from new mod + "Progressive Stimpack (Reaper)": ItemData(250 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 10, parent_item="Reaper", quantity=2, origin={"nco"}), + "Laser Targeting System (Reaper)": ItemData(251 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 0, classification=ItemClassification.filler, parent_item="Reaper", origin={"nco"}), + "Advanced Cloaking Field (Reaper)": ItemData(252 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 1, parent_item="Reaper", origin={"nco"}), + "Spider Mines (Reaper)": ItemData(253 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 2, classification=ItemClassification.filler, parent_item="Reaper", origin={"nco"}), + "Combat Drugs (Reaper)": ItemData(254 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 3, classification=ItemClassification.filler, parent_item="Reaper", origin={"ext"}), + "Hellbat Aspect (Hellion)": ItemData(255 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 4, parent_item="Hellion", origin={"nco"}), + "Smart Servos (Hellion)": ItemData(256 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 5, parent_item="Hellion", origin={"nco"}), + "Optimized Logistics (Hellion)": ItemData(257 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 6, classification=ItemClassification.filler, parent_item="Hellion", origin={"nco"}), + "Jump Jets (Hellion)": ItemData(258 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 7, classification=ItemClassification.filler, parent_item="Hellion", origin={"nco"}), + "Progressive Stimpack (Hellion)": ItemData(259 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 12, parent_item="Hellion", quantity=2, origin={"nco"}), + "Ion Thrusters (Vulture)": ItemData(260 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 8, classification=ItemClassification.filler, parent_item="Vulture", origin={"bw"}), + "Auto Launchers (Vulture)": ItemData(261 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 9, parent_item="Vulture", origin={"bw"}), + "High Explosive Munition (Spider Mine)": ItemData(262 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 10, origin={"bw"}), + "Jump Jets (Goliath)": ItemData(263 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 11, classification=ItemClassification.filler, parent_item="Goliath", origin={"nco"}), + "Optimized Logistics (Goliath)": ItemData(264 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 12, classification=ItemClassification.filler, parent_item="Goliath", origin={"nco"}), + "Hyperfluxor (Diamondback)": ItemData(265 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 13, parent_item="Diamondback", origin={"ext"}), + "Burst Capacitors (Diamondback)": ItemData(266 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 14, classification=ItemClassification.filler, parent_item="Diamondback", origin={"ext"}), + "Optimized Logistics (Diamondback)": ItemData(267 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 15, parent_item="Diamondback", origin={"ext"}), + "Jump Jets (Siege Tank)": ItemData(268 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 16, parent_item="Siege Tank", origin={"nco"}), + "Spider Mines (Siege Tank)": ItemData(269 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 17, classification=ItemClassification.filler, parent_item="Siege Tank", origin={"nco"}), + "Smart Servos (Siege Tank)": ItemData(270 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 18, classification=ItemClassification.filler, parent_item="Siege Tank", origin={"nco"}), + "Graduating Range (Siege Tank)": ItemData(271 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 19, classification=ItemClassification.progression, parent_item="Siege Tank", origin={"ext"}), + "Laser Targeting System (Siege Tank)": ItemData(272 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 20, parent_item="Siege Tank", origin={"nco"}), + "Advanced Siege Tech (Siege Tank)": ItemData(273 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 21, parent_item="Siege Tank", origin={"ext"}), + "Internal Tech Module (Siege Tank)": ItemData(274 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 22, classification=ItemClassification.filler, parent_item="Siege Tank", origin={"nco"}), + "Optimized Logistics (Predator)": ItemData(275 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 23, classification=ItemClassification.filler, parent_item="Predator", origin={"ext"}), + "Expanded Hull (Medivac)": ItemData(276 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 24, classification=ItemClassification.filler, parent_item="Medivac", origin={"ext"}), + "Afterburners (Medivac)": ItemData(277 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 25, classification=ItemClassification.filler, parent_item="Medivac", origin={"ext"}), + "Advanced Laser Technology (Wraith)": ItemData(278 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 26, classification=ItemClassification.progression, parent_item="Wraith", origin={"ext"}), + "Smart Servos (Viking)": ItemData(279 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 27, parent_item="Viking", origin={"ext"}), + "Magrail Munitions (Viking)": ItemData(280 + SC2WOL_ITEM_ID_OFFSET, "Armory 3", 28, parent_item="Viking", origin={"ext"}), "Twin-Linked Flamethrower (Hellion)": ItemData(300 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 0, classification=ItemClassification.filler, parent_item="Hellion"), "Thermite Filaments (Hellion)": ItemData(301 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 1, parent_item="Hellion"), - "Cerberus Mine (Vulture)": ItemData(302 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 2, classification=ItemClassification.filler, parent_item="Vulture"), + "Cerberus Mine (Spider Mine)": ItemData(302 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 2, classification=ItemClassification.filler), "Replenishable Magazine (Vulture)": ItemData(303 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 3, classification=ItemClassification.filler, parent_item="Vulture"), "Multi-Lock Weapons System (Goliath)": ItemData(304 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 4, parent_item="Goliath"), "Ares-Class Targeting System (Goliath)": ItemData(305 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 5, parent_item="Goliath"), @@ -86,7 +149,7 @@ item_table = { "Displacement Field (Wraith)": ItemData(313 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 13, classification=ItemClassification.filler, parent_item="Wraith"), "Ripwave Missiles (Viking)": ItemData(314 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 14, parent_item="Viking"), "Phobos-Class Weapons System (Viking)": ItemData(315 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 15, parent_item="Viking"), - "Cross-Spectrum Dampeners (Banshee)": ItemData(316 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 16, classification=ItemClassification.filler, parent_item="Banshee"), + "Progressive Cross-Spectrum Dampeners (Banshee)": ItemData(316 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 2, classification=ItemClassification.filler, parent_item="Banshee", quantity=2), "Shockwave Missile Battery (Banshee)": ItemData(317 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 17, parent_item="Banshee"), "Missile Pods (Battlecruiser)": ItemData(318 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 18, classification=ItemClassification.filler, parent_item="Battlecruiser"), "Defensive Matrix (Battlecruiser)": ItemData(319 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 19, classification=ItemClassification.filler, parent_item="Battlecruiser"), @@ -96,6 +159,47 @@ item_table = { "Nyx-Class Cloaking Module (Spectre)": ItemData(323 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 23, parent_item="Spectre"), "330mm Barrage Cannon (Thor)": ItemData(324 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 24, classification=ItemClassification.filler, parent_item="Thor"), "Immortality Protocol (Thor)": ItemData(325 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 25, classification=ItemClassification.filler, parent_item="Thor"), + # Items from EE + "Advanced Ballistics (Liberator)": ItemData(326 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 26, parent_item="Liberator", origin={"ext"}), + "Raid Artillery (Liberator)": ItemData(327 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 27, classification=ItemClassification.progression, parent_item="Liberator", origin={"nco"}), + "Drilling Claws (Widow Mine)": ItemData(328 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 28, classification=ItemClassification.filler, parent_item="Widow Mine", origin={"ext"}), + "Concealment (Widow Mine)": ItemData(329 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 29, classification=ItemClassification.progression, parent_item="Widow Mine", origin={"ext"}), + + #Items from new mod + "Hyperflight Rotors (Banshee)": ItemData(350 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 0, classification=ItemClassification.filler, parent_item="Banshee", origin={"ext"}), + "Laser Targeting System (Banshee)": ItemData(351 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 1, classification=ItemClassification.filler, parent_item="Banshee", origin={"nco"}), + "Internal Tech Module (Banshee)": ItemData(352 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 2, classification=ItemClassification.filler, parent_item="Banshee", origin={"nco"}), + "Tactical Jump (Battlecruiser)": ItemData(353 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 3, parent_item="Battlecruiser", origin={"nco", "ext"}), + "Cloak (Battlecruiser)": ItemData(354 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 4, parent_item="Battlecruiser", origin={"nco"}), + "ATX Laser Battery (Battlecruiser)": ItemData(355 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 5, classification=ItemClassification.progression, parent_item="Battlecruiser", origin={"nco"}), + "Optimized Logistics (Battlecruiser)": ItemData(356 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 6, classification=ItemClassification.filler, parent_item="Battlecruiser", origin={"ext"}), + "Internal Tech Module (Battlecruiser)": ItemData(357 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 7, classification=ItemClassification.filler, parent_item="Battlecruiser", origin={"nco"}), + "EMP Rounds (Ghost)": ItemData(358 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 8, parent_item="Ghost", origin={"ext"}), + "Lockdown (Ghost)": ItemData(359 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 9, parent_item="Ghost", origin={"bw"}), + "Impaler Rounds (Spectre)": ItemData(360 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 10, parent_item="Spectre", origin={"ext"}), + "Progressive High Impact Payload (Thor)": ItemData(361 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 14, parent_item="Thor", quantity=2, origin={"ext"}), # L2 is Smart Servos + "Bio Mechanical Repair Drone (Raven)": ItemData(363 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 13, parent_item="Raven", origin={"nco"}), + "Spider Mines (Raven)": ItemData(364 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 14, parent_item="Raven", origin={"nco"}), + "Railgun Turret (Raven)": ItemData(365 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 15, parent_item="Raven", origin={"nco"}), + "Hunter-Seeker Weapon (Raven)": ItemData(366 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 16, parent_item="Raven", origin={"nco"}), + "Interference Matrix (Raven)": ItemData(367 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 17, parent_item="Raven", origin={"ext"}), + "Anti-Armor Missile (Raven)": ItemData(368 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 18, classification=ItemClassification.filler, parent_item="Raven", origin={"ext"}), + "Internal Tech Module (Raven)": ItemData(369 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 19, classification=ItemClassification.filler, parent_item="Raven", origin={"nco"}), + "EMP Shockwave (Science Vessel)": ItemData(370 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 20, parent_item="Science Vessel", origin={"bw"}), + "Defensive Matrix (Science Vessel)": ItemData(371 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 21, parent_item="Science Vessel", origin={"bw"}), + "Targeting Optics (Cyclone)": ItemData(372 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 22, parent_item="Cyclone", origin={"ext"}), + "Rapid Fire Launchers (Cyclone)": ItemData(373 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 23, parent_item="Cyclone", origin={"ext"}), + "Cloak (Liberator)": ItemData(374 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 24, classification=ItemClassification.filler, parent_item="Liberator", origin={"nco"}), + "Laser Targeting System (Liberator)": ItemData(375 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 25, classification=ItemClassification.filler, parent_item="Liberator", origin={"ext"}), + "Optimized Logistics (Liberator)": ItemData(376 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 26, classification=ItemClassification.filler, parent_item="Liberator", origin={"nco"}), + "Black Market Launchers (Widow Mine)": ItemData(377 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 27, classification=ItemClassification.filler, parent_item="Widow Mine", origin={"ext"}), + "Executioner Missiles (Widow Mine)": ItemData(378 + SC2WOL_ITEM_ID_OFFSET, "Armory 4", 28, parent_item="Widow Mine", origin={"ext"}), + + # Just lazy to create a new group for one unit + "Enhanced Cluster Launchers (Valkyrie)": ItemData(379 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 17, parent_item="Valkyrie", origin={"ext"}), + "Shaped Hull (Valkyrie)": ItemData(380 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 20, classification=ItemClassification.filler, parent_item="Valkyrie", origin={"ext"}), + "Burst Lasers (Valkyrie)": ItemData(381 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 21, parent_item="Valkyrie", origin={"ext"}), + "Afterburners (Valkyrie)": ItemData(382 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 22, classification=ItemClassification.filler, parent_item="Valkyrie", origin={"ext"}), "Bunker": ItemData(400 + SC2WOL_ITEM_ID_OFFSET, "Building", 0, classification=ItemClassification.progression), "Missile Turret": ItemData(401 + SC2WOL_ITEM_ID_OFFSET, "Building", 1, classification=ItemClassification.progression), @@ -120,14 +224,14 @@ item_table = { "Science Vessel": ItemData(607 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 7, classification=ItemClassification.progression), "Tech Reactor": ItemData(608 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 8), "Orbital Strike": ItemData(609 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 9), - "Shrike Turret": ItemData(610 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 10, parent_item="Bunker"), - "Fortified Bunker": ItemData(611 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 11, parent_item="Bunker"), + "Shrike Turret (Bunker)": ItemData(610 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 10, parent_item="Bunker"), + "Fortified Bunker (Bunker)": ItemData(611 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 11, parent_item="Bunker"), "Planetary Fortress": ItemData(612 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 12, classification=ItemClassification.progression), "Perdition Turret": ItemData(613 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 13, classification=ItemClassification.progression), "Predator": ItemData(614 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 14, classification=ItemClassification.filler), "Hercules": ItemData(615 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 15, classification=ItemClassification.progression), "Cellular Reactor": ItemData(616 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 16), - "Regenerative Bio-Steel": ItemData(617 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 17), + "Progressive Regenerative Bio-Steel": ItemData(617 + SC2WOL_ITEM_ID_OFFSET, "Progressive Upgrade", 4, quantity=2), "Hive Mind Emulator": ItemData(618 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 18, ItemClassification.progression), "Psi Disrupter": ItemData(619 + SC2WOL_ITEM_ID_OFFSET, "Laboratory", 19, classification=ItemClassification.progression), @@ -136,18 +240,24 @@ item_table = { "High Templar": ItemData(702 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 2, classification=ItemClassification.progression), "Dark Templar": ItemData(703 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 3, classification=ItemClassification.progression), "Immortal": ItemData(704 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 4, classification=ItemClassification.progression), - "Colossus": ItemData(705 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 5, classification=ItemClassification.progression), - "Phoenix": ItemData(706 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 6, classification=ItemClassification.progression), + "Colossus": ItemData(705 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 5), + "Phoenix": ItemData(706 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 6, classification=ItemClassification.filler), "Void Ray": ItemData(707 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 7, classification=ItemClassification.progression), "Carrier": ItemData(708 + SC2WOL_ITEM_ID_OFFSET, "Protoss", 8, classification=ItemClassification.progression), + # Filler items to fill remaining spots "+15 Starting Minerals": ItemData(800 + SC2WOL_ITEM_ID_OFFSET, "Minerals", 15, quantity=0, classification=ItemClassification.filler), "+15 Starting Vespene": ItemData(801 + SC2WOL_ITEM_ID_OFFSET, "Vespene", 15, quantity=0, classification=ItemClassification.filler), + # This Filler item isn't placed by the generator yet unless plando'd "+2 Starting Supply": ItemData(802 + SC2WOL_ITEM_ID_OFFSET, "Supply", 2, quantity=0, classification=ItemClassification.filler), + # This item is used to "remove" location from the game. Never placed unless plando'd + "Nothing": ItemData(803 + SC2WOL_ITEM_ID_OFFSET, "Nothing Group", 2, quantity=0, classification=ItemClassification.trap), # "Keystone Piece": ItemData(850 + SC2WOL_ITEM_ID_OFFSET, "Goal", 0, quantity=0, classification=ItemClassification.progression_skip_balancing) } +def get_item_table(multiworld: MultiWorld, player: int): + return item_table basic_units = { 'Marine', @@ -172,10 +282,49 @@ def get_basic_units(multiworld: MultiWorld, player: int) -> typing.Set[str]: item_name_groups = {} -for item, data in item_table.items(): +for item, data in get_full_item_list().items(): item_name_groups.setdefault(data.type, []).append(item) + if data.type in ("Armory 1", "Armory 2") and '(' in item: + short_name = item[:item.find(' (')] + item_name_groups[short_name] = [item] item_name_groups["Missions"] = ["Beat " + mission_name for mission_name in vanilla_mission_req_table] + +# Items that can be placed before resources if not already in +# General upgrades and Mercs +second_pass_placeable_items: typing.Tuple[str, ...] = ( + # Buildings without upgrades + "Sensor Tower", + "Hive Mind Emulator", + "Psi Disrupter", + "Perdition Turret", + # General upgrades without any dependencies + "Advanced Construction (SCV)", + "Dual-Fusion Welders (SCV)", + "Fire-Suppression System (Building)", + "Orbital Command (Building)", + "Ultra-Capacitors", + "Vanadium Plating", + "Orbital Depots", + "Micro-Filtering", + "Automated Refinery", + "Command Center Reactor", + "Tech Reactor", + "Planetary Fortress", + "Cellular Reactor", + "Progressive Regenerative Bio-Steel", # Place only L1 + # Mercenaries + "War Pigs", + "Devil Dogs", + "Hammer Securities", + "Spartan Company", + "Siege Breakers", + "Hel's Angel", + "Dusk Wings", + "Jackson's Revenge" +) + + filler_items: typing.Tuple[str, ...] = ( '+15 Starting Minerals', '+15 Starting Vespene' @@ -190,7 +339,10 @@ defense_ratings = { # Bunker w/ Marine/Marauder: 3, "Perdition Turret": 2, "Missile Turret": 2, - "Vulture": 2 + "Vulture": 2, + "Liberator": 2, + "Widow Mine": 2 + # "Concealment (Widow Mine)": 1 } zerg_defense_ratings = { "Perdition Turret": 2, @@ -199,14 +351,61 @@ zerg_defense_ratings = { "Psi Disruptor": 3 } +spider_mine_sources = { + "Vulture", + "Spider Mines (Reaper)", + "Spider Mines (Siege Tank)", + "Spider Mines (Raven)" +} + +progressive_if_nco = { + "Progressive Stimpack (Marine)", + "Progressive Stimpack (Firebat)", + "Progressive Cross-Spectrum Dampeners (Banshee)", + "Progressive Regenerative Bio-Steel" +} + +# 'number' values of upgrades for upgrade bundle items +upgrade_numbers = [ + {0, 4, 8}, # Weapon + {2, 6, 10}, # Armor + {0, 2}, # Infantry + {4, 6}, # Vehicle + {8, 10}, # Starship + {0, 2, 4, 6, 8, 10} # All +] +# Names of upgrades to be included for different options +upgrade_included_names = [ + { # Individual Items + "Progressive Infantry Weapon", + "Progressive Infantry Armor", + "Progressive Vehicle Weapon", + "Progressive Vehicle Armor", + "Progressive Ship Weapon", + "Progressive Ship Armor" + }, + { # Bundle Weapon And Armor + "Progressive Weapon Upgrade", + "Progressive Armor Upgrade" + }, + { # Bundle Unit Class + "Progressive Infantry Upgrade", + "Progressive Vehicle Upgrade", + "Progressive Starship Upgrade" + }, + { # Bundle All + "Progressive Weapon/Armor Upgrade" + } +] + lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in get_full_item_list().items() if data.code} # Map type to expected int type_flaggroups: typing.Dict[str, int] = { "Unit": 0, - "Upgrade": 1, - "Armory 1": 2, - "Armory 2": 3, + "Upgrade": 1, # Weapon / Armor upgrades + "Armory 1": 2, # Unit upgrades + "Armory 2": 3, # Unit upgrades "Building": 4, "Mercenary": 5, "Laboratory": 6, @@ -214,5 +413,9 @@ type_flaggroups: typing.Dict[str, int] = { "Minerals": 8, "Vespene": 9, "Supply": 10, - "Goal": 11 + "Goal": 11, + "Armory 3": 12, # Unit upgrades + "Armory 4": 13, # Unit upgrades + "Progressive Upgrade": 14, # Unit upgrades that exist multiple times (Stimpack / Super Stimpack) + "Nothing Group": 15 } diff --git a/worlds/sc2wol/Locations.py b/worlds/sc2wol/Locations.py index e91068c4f2..ae31fa8eaa 100644 --- a/worlds/sc2wol/Locations.py +++ b/worlds/sc2wol/Locations.py @@ -1,3 +1,4 @@ +from enum import IntEnum from typing import List, Tuple, Optional, Callable, NamedTuple from BaseClasses import MultiWorld from .Options import get_option_value @@ -11,10 +12,18 @@ class SC2WoLLocation(Location): game: str = "Starcraft2WoL" +class LocationType(IntEnum): + VICTORY = 0 # Winning a mission + MISSION_PROGRESS = 1 # All tasks done for progressing the mission normally towards victory. All cleaning of expansion bases falls here + BONUS = 2 # Bonus objective, getting a campaign or mission bonus in vanilla (credits, research, bonus units or resources) + CHALLENGE = 3 # Challenging objectives, often harder than just completing a mission + OPTIONAL_BOSS = 4 # Any boss that's not required to win the mission. All Brutalisks, Loki, etc. + class LocationData(NamedTuple): region: str name: str code: Optional[int] + type: LocationType rule: Callable = lambda state: True @@ -22,256 +31,473 @@ def get_locations(multiworld: Optional[MultiWorld], player: Optional[int]) -> Tu # Note: rules which are ended with or True are rules identified as needed later when restricted units is an option logic_level = get_option_value(multiworld, player, 'required_tactics') location_table: List[LocationData] = [ - LocationData("Liberation Day", "Liberation Day: Victory", SC2WOL_LOC_ID_OFFSET + 100), - LocationData("Liberation Day", "Liberation Day: First Statue", SC2WOL_LOC_ID_OFFSET + 101), - LocationData("Liberation Day", "Liberation Day: Second Statue", SC2WOL_LOC_ID_OFFSET + 102), - LocationData("Liberation Day", "Liberation Day: Third Statue", SC2WOL_LOC_ID_OFFSET + 103), - LocationData("Liberation Day", "Liberation Day: Fourth Statue", SC2WOL_LOC_ID_OFFSET + 104), - LocationData("Liberation Day", "Liberation Day: Fifth Statue", SC2WOL_LOC_ID_OFFSET + 105), - LocationData("Liberation Day", "Liberation Day: Sixth Statue", SC2WOL_LOC_ID_OFFSET + 106), - LocationData("The Outlaws", "The Outlaws: Victory", SC2WOL_LOC_ID_OFFSET + 200, + LocationData("Liberation Day", "Liberation Day: Victory", SC2WOL_LOC_ID_OFFSET + 100, LocationType.VICTORY), + LocationData("Liberation Day", "Liberation Day: First Statue", SC2WOL_LOC_ID_OFFSET + 101, LocationType.BONUS), + LocationData("Liberation Day", "Liberation Day: Second Statue", SC2WOL_LOC_ID_OFFSET + 102, LocationType.BONUS), + LocationData("Liberation Day", "Liberation Day: Third Statue", SC2WOL_LOC_ID_OFFSET + 103, LocationType.BONUS), + LocationData("Liberation Day", "Liberation Day: Fourth Statue", SC2WOL_LOC_ID_OFFSET + 104, LocationType.BONUS), + LocationData("Liberation Day", "Liberation Day: Fifth Statue", SC2WOL_LOC_ID_OFFSET + 105, LocationType.BONUS), + LocationData("Liberation Day", "Liberation Day: Sixth Statue", SC2WOL_LOC_ID_OFFSET + 106, LocationType.BONUS), + LocationData("Liberation Day", "Liberation Day: Special Delivery", SC2WOL_LOC_ID_OFFSET + 107, LocationType.MISSION_PROGRESS), + LocationData("The Outlaws", "The Outlaws: Victory", SC2WOL_LOC_ID_OFFSET + 200, LocationType.VICTORY, lambda state: state._sc2wol_has_common_unit(multiworld, player)), - LocationData("The Outlaws", "The Outlaws: Rebel Base", SC2WOL_LOC_ID_OFFSET + 201, + LocationData("The Outlaws", "The Outlaws: Rebel Base", SC2WOL_LOC_ID_OFFSET + 201, LocationType.BONUS, lambda state: state._sc2wol_has_common_unit(multiworld, player)), - LocationData("Zero Hour", "Zero Hour: Victory", SC2WOL_LOC_ID_OFFSET + 300, + LocationData("The Outlaws", "The Outlaws: North Resource Pickups", SC2WOL_LOC_ID_OFFSET + 202, LocationType.BONUS), + LocationData("The Outlaws", "The Outlaws: Bunker", SC2WOL_LOC_ID_OFFSET + 203, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_common_unit(multiworld, player)), + LocationData("Zero Hour", "Zero Hour: Victory", SC2WOL_LOC_ID_OFFSET + 300, LocationType.VICTORY, lambda state: state._sc2wol_has_common_unit(multiworld, player) and state._sc2wol_defense_rating(multiworld, player, True) >= 2 and (logic_level > 0 or state._sc2wol_has_anti_air(multiworld, player))), - LocationData("Zero Hour", "Zero Hour: First Group Rescued", SC2WOL_LOC_ID_OFFSET + 301), - LocationData("Zero Hour", "Zero Hour: Second Group Rescued", SC2WOL_LOC_ID_OFFSET + 302, + LocationData("Zero Hour", "Zero Hour: First Group Rescued", SC2WOL_LOC_ID_OFFSET + 301, LocationType.BONUS), + LocationData("Zero Hour", "Zero Hour: Second Group Rescued", SC2WOL_LOC_ID_OFFSET + 302, LocationType.BONUS, lambda state: state._sc2wol_has_common_unit(multiworld, player)), - LocationData("Zero Hour", "Zero Hour: Third Group Rescued", SC2WOL_LOC_ID_OFFSET + 303, + LocationData("Zero Hour", "Zero Hour: Third Group Rescued", SC2WOL_LOC_ID_OFFSET + 303, LocationType.BONUS, lambda state: state._sc2wol_has_common_unit(multiworld, player) and state._sc2wol_defense_rating(multiworld, player, True) >= 2), - LocationData("Evacuation", "Evacuation: Victory", SC2WOL_LOC_ID_OFFSET + 400, + LocationData("Zero Hour", "Zero Hour: First Hatchery", SC2WOL_LOC_ID_OFFSET + 304, LocationType.CHALLENGE, + lambda state: state._sc2wol_has_competent_comp(multiworld, player)), + LocationData("Zero Hour", "Zero Hour: Second Hatchery", SC2WOL_LOC_ID_OFFSET + 305, LocationType.CHALLENGE, + lambda state: state._sc2wol_has_competent_comp(multiworld, player)), + LocationData("Zero Hour", "Zero Hour: Third Hatchery", SC2WOL_LOC_ID_OFFSET + 306, LocationType.CHALLENGE, + lambda state: state._sc2wol_has_competent_comp(multiworld, player)), + LocationData("Zero Hour", "Zero Hour: Fourth Hatchery", SC2WOL_LOC_ID_OFFSET + 307, LocationType.CHALLENGE, + lambda state: state._sc2wol_has_competent_comp(multiworld, player)), + LocationData("Evacuation", "Evacuation: Victory", SC2WOL_LOC_ID_OFFSET + 400, LocationType.VICTORY, lambda state: state._sc2wol_has_common_unit(multiworld, player) and (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player) or state._sc2wol_has_competent_anti_air(multiworld, player))), - LocationData("Evacuation", "Evacuation: First Chysalis", SC2WOL_LOC_ID_OFFSET + 401), - LocationData("Evacuation", "Evacuation: Second Chysalis", SC2WOL_LOC_ID_OFFSET + 402, + LocationData("Evacuation", "Evacuation: First Chrysalis", SC2WOL_LOC_ID_OFFSET + 401, LocationType.BONUS), + LocationData("Evacuation", "Evacuation: Second Chrysalis", SC2WOL_LOC_ID_OFFSET + 402, LocationType.BONUS, lambda state: state._sc2wol_has_common_unit(multiworld, player)), - LocationData("Evacuation", "Evacuation: Third Chysalis", SC2WOL_LOC_ID_OFFSET + 403, + LocationData("Evacuation", "Evacuation: Third Chrysalis", SC2WOL_LOC_ID_OFFSET + 403, LocationType.BONUS, lambda state: state._sc2wol_has_common_unit(multiworld, player)), - LocationData("Outbreak", "Outbreak: Victory", SC2WOL_LOC_ID_OFFSET + 500, + LocationData("Evacuation", "Evacuation: Reach Hanson", SC2WOL_LOC_ID_OFFSET + 404, LocationType.MISSION_PROGRESS), + LocationData("Evacuation", "Evacuation: Secret Resource Stash", SC2WOL_LOC_ID_OFFSET + 405, LocationType.BONUS), + LocationData("Evacuation", "Evacuation: Flawless", SC2WOL_LOC_ID_OFFSET + 406, LocationType.CHALLENGE, + lambda state: state._sc2wol_has_common_unit(multiworld, player) and + state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and + (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player) + or state._sc2wol_has_competent_anti_air(multiworld, player))), + LocationData("Outbreak", "Outbreak: Victory", SC2WOL_LOC_ID_OFFSET + 500, LocationType.VICTORY, lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 4 and (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))), - LocationData("Outbreak", "Outbreak: Left Infestor", SC2WOL_LOC_ID_OFFSET + 501, + LocationData("Outbreak", "Outbreak: Left Infestor", SC2WOL_LOC_ID_OFFSET + 501, LocationType.BONUS, lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))), - LocationData("Outbreak", "Outbreak: Right Infestor", SC2WOL_LOC_ID_OFFSET + 502, + LocationData("Outbreak", "Outbreak: Right Infestor", SC2WOL_LOC_ID_OFFSET + 502, LocationType.BONUS, lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))), - LocationData("Safe Haven", "Safe Haven: Victory", SC2WOL_LOC_ID_OFFSET + 600, + LocationData("Outbreak", "Outbreak: North Infested Command Center", SC2WOL_LOC_ID_OFFSET + 503, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and + (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))), + LocationData("Outbreak", "Outbreak: South Infested Command Center", SC2WOL_LOC_ID_OFFSET + 504, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and + (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))), + LocationData("Outbreak", "Outbreak: Northwest Bar", SC2WOL_LOC_ID_OFFSET + 505, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and + (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))), + LocationData("Outbreak", "Outbreak: North Bar", SC2WOL_LOC_ID_OFFSET + 506, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and + (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))), + LocationData("Outbreak", "Outbreak: South Bar", SC2WOL_LOC_ID_OFFSET + 507, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_defense_rating(multiworld, player, True, False) >= 2 and + (state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))), + LocationData("Safe Haven", "Safe Haven: Victory", SC2WOL_LOC_ID_OFFSET + 600, LocationType.VICTORY, lambda state: state._sc2wol_has_common_unit(multiworld, player) and state._sc2wol_has_competent_anti_air(multiworld, player)), - LocationData("Safe Haven", "Safe Haven: North Nexus", SC2WOL_LOC_ID_OFFSET + 601, + LocationData("Safe Haven", "Safe Haven: North Nexus", SC2WOL_LOC_ID_OFFSET + 601, LocationType.MISSION_PROGRESS, lambda state: state._sc2wol_has_common_unit(multiworld, player) and state._sc2wol_has_competent_anti_air(multiworld, player)), - LocationData("Safe Haven", "Safe Haven: East Nexus", SC2WOL_LOC_ID_OFFSET + 602, + LocationData("Safe Haven", "Safe Haven: East Nexus", SC2WOL_LOC_ID_OFFSET + 602, LocationType.MISSION_PROGRESS, lambda state: state._sc2wol_has_common_unit(multiworld, player) and state._sc2wol_has_competent_anti_air(multiworld, player)), - LocationData("Safe Haven", "Safe Haven: South Nexus", SC2WOL_LOC_ID_OFFSET + 603, + LocationData("Safe Haven", "Safe Haven: South Nexus", SC2WOL_LOC_ID_OFFSET + 603, LocationType.MISSION_PROGRESS, lambda state: state._sc2wol_has_common_unit(multiworld, player) and state._sc2wol_has_competent_anti_air(multiworld, player)), - LocationData("Haven's Fall", "Haven's Fall: Victory", SC2WOL_LOC_ID_OFFSET + 700, + LocationData("Safe Haven", "Safe Haven: First Terror Fleet", SC2WOL_LOC_ID_OFFSET + 604, LocationType.BONUS, + lambda state: state._sc2wol_has_common_unit(multiworld, player) and + state._sc2wol_has_competent_anti_air(multiworld, player)), + LocationData("Safe Haven", "Safe Haven: Second Terror Fleet", SC2WOL_LOC_ID_OFFSET + 605, LocationType.BONUS, + lambda state: state._sc2wol_has_common_unit(multiworld, player) and + state._sc2wol_has_competent_anti_air(multiworld, player)), + LocationData("Safe Haven", "Safe Haven: Third Terror Fleet", SC2WOL_LOC_ID_OFFSET + 606, LocationType.BONUS, + lambda state: state._sc2wol_has_common_unit(multiworld, player) and + state._sc2wol_has_competent_anti_air(multiworld, player)), + LocationData("Haven's Fall", "Haven's Fall: Victory", SC2WOL_LOC_ID_OFFSET + 700, LocationType.VICTORY, lambda state: state._sc2wol_has_common_unit(multiworld, player) and state._sc2wol_has_competent_anti_air(multiworld, player) and state._sc2wol_defense_rating(multiworld, player, True) >= 3), - LocationData("Haven's Fall", "Haven's Fall: North Hive", SC2WOL_LOC_ID_OFFSET + 701, + LocationData("Haven's Fall", "Haven's Fall: North Hive", SC2WOL_LOC_ID_OFFSET + 701, LocationType.MISSION_PROGRESS, lambda state: state._sc2wol_has_common_unit(multiworld, player) and state._sc2wol_has_competent_anti_air(multiworld, player) and state._sc2wol_defense_rating(multiworld, player, True) >= 3), - LocationData("Haven's Fall", "Haven's Fall: East Hive", SC2WOL_LOC_ID_OFFSET + 702, + LocationData("Haven's Fall", "Haven's Fall: East Hive", SC2WOL_LOC_ID_OFFSET + 702, LocationType.MISSION_PROGRESS, lambda state: state._sc2wol_has_common_unit(multiworld, player) and state._sc2wol_has_competent_anti_air(multiworld, player) and state._sc2wol_defense_rating(multiworld, player, True) >= 3), - LocationData("Haven's Fall", "Haven's Fall: South Hive", SC2WOL_LOC_ID_OFFSET + 703, + LocationData("Haven's Fall", "Haven's Fall: South Hive", SC2WOL_LOC_ID_OFFSET + 703, LocationType.MISSION_PROGRESS, lambda state: state._sc2wol_has_common_unit(multiworld, player) and state._sc2wol_has_competent_anti_air(multiworld, player) and state._sc2wol_defense_rating(multiworld, player, True) >= 3), - LocationData("Smash and Grab", "Smash and Grab: Victory", SC2WOL_LOC_ID_OFFSET + 800, + LocationData("Haven's Fall", "Haven's Fall: Northeast Colony Base", SC2WOL_LOC_ID_OFFSET + 704, LocationType.CHALLENGE, + lambda state: state._sc2wol_can_respond_to_colony_infestations), + LocationData("Haven's Fall", "Haven's Fall: East Colony Base", SC2WOL_LOC_ID_OFFSET + 705, LocationType.CHALLENGE, + lambda state: state._sc2wol_can_respond_to_colony_infestations), + LocationData("Haven's Fall", "Haven's Fall: Middle Colony Base", SC2WOL_LOC_ID_OFFSET + 706, LocationType.CHALLENGE, + lambda state: state._sc2wol_can_respond_to_colony_infestations), + LocationData("Haven's Fall", "Haven's Fall: Southeast Colony Base", SC2WOL_LOC_ID_OFFSET + 707, LocationType.CHALLENGE, + lambda state: state._sc2wol_can_respond_to_colony_infestations), + LocationData("Haven's Fall", "Haven's Fall: Southwest Colony Base", SC2WOL_LOC_ID_OFFSET + 708, LocationType.CHALLENGE, + lambda state: state._sc2wol_can_respond_to_colony_infestations), + LocationData("Smash and Grab", "Smash and Grab: Victory", SC2WOL_LOC_ID_OFFSET + 800, LocationType.VICTORY, lambda state: state._sc2wol_has_common_unit(multiworld, player) and (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player) or state._sc2wol_has_competent_anti_air(multiworld, player))), - LocationData("Smash and Grab", "Smash and Grab: First Relic", SC2WOL_LOC_ID_OFFSET + 801), - LocationData("Smash and Grab", "Smash and Grab: Second Relic", SC2WOL_LOC_ID_OFFSET + 802), - LocationData("Smash and Grab", "Smash and Grab: Third Relic", SC2WOL_LOC_ID_OFFSET + 803, + LocationData("Smash and Grab", "Smash and Grab: First Relic", SC2WOL_LOC_ID_OFFSET + 801, LocationType.BONUS), + LocationData("Smash and Grab", "Smash and Grab: Second Relic", SC2WOL_LOC_ID_OFFSET + 802, LocationType.BONUS), + LocationData("Smash and Grab", "Smash and Grab: Third Relic", SC2WOL_LOC_ID_OFFSET + 803, LocationType.BONUS, lambda state: state._sc2wol_has_common_unit(multiworld, player) and (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player) or state._sc2wol_has_competent_anti_air(multiworld, player))), - LocationData("Smash and Grab", "Smash and Grab: Fourth Relic", SC2WOL_LOC_ID_OFFSET + 804, + LocationData("Smash and Grab", "Smash and Grab: Fourth Relic", SC2WOL_LOC_ID_OFFSET + 804, LocationType.BONUS, lambda state: state._sc2wol_has_common_unit(multiworld, player) and (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player) or state._sc2wol_has_competent_anti_air(multiworld, player))), - LocationData("The Dig", "The Dig: Victory", SC2WOL_LOC_ID_OFFSET + 900, + LocationData("Smash and Grab", "Smash and Grab: First Forcefield Area Busted", SC2WOL_LOC_ID_OFFSET + 805, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_common_unit(multiworld, player) and + (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player) + or state._sc2wol_has_competent_anti_air(multiworld, player))), + LocationData("Smash and Grab", "Smash and Grab: Second Forcefield Area Busted", SC2WOL_LOC_ID_OFFSET + 806, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_common_unit(multiworld, player) and + (logic_level > 0 and state._sc2wol_has_anti_air(multiworld, player) + or state._sc2wol_has_competent_anti_air(multiworld, player))), + LocationData("The Dig", "The Dig: Victory", SC2WOL_LOC_ID_OFFSET + 900, LocationType.VICTORY, lambda state: state._sc2wol_has_anti_air(multiworld, player) and state._sc2wol_defense_rating(multiworld, player, False) >= 7), - LocationData("The Dig", "The Dig: Left Relic", SC2WOL_LOC_ID_OFFSET + 901, + LocationData("The Dig", "The Dig: Left Relic", SC2WOL_LOC_ID_OFFSET + 901, LocationType.BONUS, lambda state: state._sc2wol_defense_rating(multiworld, player, False) >= 5), - LocationData("The Dig", "The Dig: Right Ground Relic", SC2WOL_LOC_ID_OFFSET + 902, + LocationData("The Dig", "The Dig: Right Ground Relic", SC2WOL_LOC_ID_OFFSET + 902, LocationType.BONUS, lambda state: state._sc2wol_defense_rating(multiworld, player, False) >= 5), - LocationData("The Dig", "The Dig: Right Cliff Relic", SC2WOL_LOC_ID_OFFSET + 903, + LocationData("The Dig", "The Dig: Right Cliff Relic", SC2WOL_LOC_ID_OFFSET + 903, LocationType.BONUS, lambda state: state._sc2wol_defense_rating(multiworld, player, False) >= 5), - LocationData("The Moebius Factor", "The Moebius Factor: Victory", SC2WOL_LOC_ID_OFFSET + 1000, + LocationData("The Dig", "The Dig: Moebius Base", SC2WOL_LOC_ID_OFFSET + 904, LocationType.MISSION_PROGRESS), + LocationData("The Moebius Factor", "The Moebius Factor: Victory", SC2WOL_LOC_ID_OFFSET + 1000, LocationType.VICTORY, lambda state: state._sc2wol_has_anti_air(multiworld, player) and (state._sc2wol_has_air(multiworld, player) or state.has_any({'Medivac', 'Hercules'}, player) and state._sc2wol_has_common_unit(multiworld, player))), - LocationData("The Moebius Factor", "The Moebius Factor: South Rescue", SC2WOL_LOC_ID_OFFSET + 1003, + LocationData("The Moebius Factor", "The Moebius Factor: 1st Data Core", SC2WOL_LOC_ID_OFFSET + 1001, LocationType.MISSION_PROGRESS), + LocationData("The Moebius Factor", "The Moebius Factor: 2nd Data Core", SC2WOL_LOC_ID_OFFSET + 1002, LocationType.MISSION_PROGRESS, + lambda state: (state._sc2wol_has_air(multiworld, player) + or state.has_any({'Medivac', 'Hercules'}, player) + and state._sc2wol_has_common_unit(multiworld, player))), + LocationData("The Moebius Factor", "The Moebius Factor: South Rescue", SC2WOL_LOC_ID_OFFSET + 1003, LocationType.BONUS, lambda state: state._sc2wol_able_to_rescue(multiworld, player)), - LocationData("The Moebius Factor", "The Moebius Factor: Wall Rescue", SC2WOL_LOC_ID_OFFSET + 1004, + LocationData("The Moebius Factor", "The Moebius Factor: Wall Rescue", SC2WOL_LOC_ID_OFFSET + 1004, LocationType.BONUS, lambda state: state._sc2wol_able_to_rescue(multiworld, player)), - LocationData("The Moebius Factor", "The Moebius Factor: Mid Rescue", SC2WOL_LOC_ID_OFFSET + 1005, + LocationData("The Moebius Factor", "The Moebius Factor: Mid Rescue", SC2WOL_LOC_ID_OFFSET + 1005, LocationType.BONUS, lambda state: state._sc2wol_able_to_rescue(multiworld, player)), - LocationData("The Moebius Factor", "The Moebius Factor: Nydus Roof Rescue", SC2WOL_LOC_ID_OFFSET + 1006, + LocationData("The Moebius Factor", "The Moebius Factor: Nydus Roof Rescue", SC2WOL_LOC_ID_OFFSET + 1006, LocationType.BONUS, lambda state: state._sc2wol_able_to_rescue(multiworld, player)), - LocationData("The Moebius Factor", "The Moebius Factor: Alive Inside Rescue", SC2WOL_LOC_ID_OFFSET + 1007, + LocationData("The Moebius Factor", "The Moebius Factor: Alive Inside Rescue", SC2WOL_LOC_ID_OFFSET + 1007, LocationType.BONUS, lambda state: state._sc2wol_able_to_rescue(multiworld, player)), - LocationData("The Moebius Factor", "The Moebius Factor: Brutalisk", SC2WOL_LOC_ID_OFFSET + 1008, + LocationData("The Moebius Factor", "The Moebius Factor: Brutalisk", SC2WOL_LOC_ID_OFFSET + 1008, LocationType.OPTIONAL_BOSS, lambda state: state._sc2wol_has_anti_air(multiworld, player) and (state._sc2wol_has_air(multiworld, player) or state.has_any({'Medivac', 'Hercules'}, player) and state._sc2wol_has_common_unit(multiworld, player))), - LocationData("Supernova", "Supernova: Victory", SC2WOL_LOC_ID_OFFSET + 1100, + LocationData("The Moebius Factor", "The Moebius Factor: 3rd Data Core", SC2WOL_LOC_ID_OFFSET + 1009, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_anti_air(multiworld, player) and + (state._sc2wol_has_air(multiworld, player) + or state.has_any({'Medivac', 'Hercules'}, player) + and state._sc2wol_has_common_unit(multiworld, player))), + LocationData("Supernova", "Supernova: Victory", SC2WOL_LOC_ID_OFFSET + 1100, LocationType.VICTORY, lambda state: state._sc2wol_beats_protoss_deathball(multiworld, player)), - LocationData("Supernova", "Supernova: West Relic", SC2WOL_LOC_ID_OFFSET + 1101), - LocationData("Supernova", "Supernova: North Relic", SC2WOL_LOC_ID_OFFSET + 1102), - LocationData("Supernova", "Supernova: South Relic", SC2WOL_LOC_ID_OFFSET + 1103, + LocationData("Supernova", "Supernova: West Relic", SC2WOL_LOC_ID_OFFSET + 1101, LocationType.BONUS), + LocationData("Supernova", "Supernova: North Relic", SC2WOL_LOC_ID_OFFSET + 1102, LocationType.BONUS), + LocationData("Supernova", "Supernova: South Relic", SC2WOL_LOC_ID_OFFSET + 1103, LocationType.BONUS, lambda state: state._sc2wol_beats_protoss_deathball(multiworld, player)), - LocationData("Supernova", "Supernova: East Relic", SC2WOL_LOC_ID_OFFSET + 1104, + LocationData("Supernova", "Supernova: East Relic", SC2WOL_LOC_ID_OFFSET + 1104, LocationType.BONUS, lambda state: state._sc2wol_beats_protoss_deathball(multiworld, player)), - LocationData("Maw of the Void", "Maw of the Void: Victory", SC2WOL_LOC_ID_OFFSET + 1200, + LocationData("Supernova", "Supernova: Landing Zone Cleared", SC2WOL_LOC_ID_OFFSET + 1105, LocationType.MISSION_PROGRESS), + LocationData("Supernova", "Supernova: Middle Base", SC2WOL_LOC_ID_OFFSET + 1106, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_beats_protoss_deathball(multiworld, player)), + LocationData("Supernova", "Supernova: Southeast Base", SC2WOL_LOC_ID_OFFSET + 1107, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_beats_protoss_deathball(multiworld, player)), + LocationData("Maw of the Void", "Maw of the Void: Victory", SC2WOL_LOC_ID_OFFSET + 1200, LocationType.VICTORY, lambda state: state._sc2wol_survives_rip_field(multiworld, player)), - LocationData("Maw of the Void", "Maw of the Void: Landing Zone Cleared", SC2WOL_LOC_ID_OFFSET + 1201), - LocationData("Maw of the Void", "Maw of the Void: Expansion Prisoners", SC2WOL_LOC_ID_OFFSET + 1202, + LocationData("Maw of the Void", "Maw of the Void: Landing Zone Cleared", SC2WOL_LOC_ID_OFFSET + 1201, LocationType.MISSION_PROGRESS), + LocationData("Maw of the Void", "Maw of the Void: Expansion Prisoners", SC2WOL_LOC_ID_OFFSET + 1202, LocationType.BONUS, lambda state: logic_level > 0 or state._sc2wol_survives_rip_field(multiworld, player)), - LocationData("Maw of the Void", "Maw of the Void: South Close Prisoners", SC2WOL_LOC_ID_OFFSET + 1203, + LocationData("Maw of the Void", "Maw of the Void: South Close Prisoners", SC2WOL_LOC_ID_OFFSET + 1203, LocationType.BONUS, lambda state: logic_level > 0 or state._sc2wol_survives_rip_field(multiworld, player)), - LocationData("Maw of the Void", "Maw of the Void: South Far Prisoners", SC2WOL_LOC_ID_OFFSET + 1204, + LocationData("Maw of the Void", "Maw of the Void: South Far Prisoners", SC2WOL_LOC_ID_OFFSET + 1204, LocationType.BONUS, lambda state: state._sc2wol_survives_rip_field(multiworld, player)), - LocationData("Maw of the Void", "Maw of the Void: North Prisoners", SC2WOL_LOC_ID_OFFSET + 1205, + LocationData("Maw of the Void", "Maw of the Void: North Prisoners", SC2WOL_LOC_ID_OFFSET + 1205, LocationType.BONUS, lambda state: state._sc2wol_survives_rip_field(multiworld, player)), - LocationData("Devil's Playground", "Devil's Playground: Victory", SC2WOL_LOC_ID_OFFSET + 1300, + LocationData("Maw of the Void", "Maw of the Void: Mothership", SC2WOL_LOC_ID_OFFSET + 1206, LocationType.OPTIONAL_BOSS, + lambda state: state._sc2wol_survives_rip_field(multiworld, player)), + LocationData("Maw of the Void", "Maw of the Void: Expansion Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1207, LocationType.MISSION_PROGRESS, + lambda state: logic_level > 0 or state._sc2wol_survives_rip_field(multiworld, player)), + LocationData("Maw of the Void", "Maw of the Void: Middle Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1208, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_survives_rip_field(multiworld, player)), + LocationData("Maw of the Void", "Maw of the Void: Southeast Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1209, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_survives_rip_field(multiworld, player)), + LocationData("Maw of the Void", "Maw of the Void: Stargate Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1210, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_survives_rip_field(multiworld, player)), + LocationData("Maw of the Void", "Maw of the Void: Northwest Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1211, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_survives_rip_field(multiworld, player)), + LocationData("Maw of the Void", "Maw of the Void: West Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1212, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_survives_rip_field(multiworld, player)), + LocationData("Maw of the Void", "Maw of the Void: Southwest Rip Field Generator", SC2WOL_LOC_ID_OFFSET + 1213, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_survives_rip_field(multiworld, player)), + LocationData("Devil's Playground", "Devil's Playground: Victory", SC2WOL_LOC_ID_OFFSET + 1300, LocationType.VICTORY, lambda state: logic_level > 0 or state._sc2wol_has_anti_air(multiworld, player) and ( state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))), - LocationData("Devil's Playground", "Devil's Playground: Tosh's Miners", SC2WOL_LOC_ID_OFFSET + 1301), - LocationData("Devil's Playground", "Devil's Playground: Brutalisk", SC2WOL_LOC_ID_OFFSET + 1302, + LocationData("Devil's Playground", "Devil's Playground: Tosh's Miners", SC2WOL_LOC_ID_OFFSET + 1301, LocationType.BONUS), + LocationData("Devil's Playground", "Devil's Playground: Brutalisk", SC2WOL_LOC_ID_OFFSET + 1302, LocationType.OPTIONAL_BOSS, lambda state: logic_level > 0 or state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player)), - LocationData("Welcome to the Jungle", "Welcome to the Jungle: Victory", SC2WOL_LOC_ID_OFFSET + 1400, - lambda state: state._sc2wol_has_common_unit(multiworld, player) and - state._sc2wol_has_competent_anti_air(multiworld, player)), - LocationData("Welcome to the Jungle", "Welcome to the Jungle: Close Relic", SC2WOL_LOC_ID_OFFSET + 1401), - LocationData("Welcome to the Jungle", "Welcome to the Jungle: West Relic", SC2WOL_LOC_ID_OFFSET + 1402, - lambda state: state._sc2wol_has_common_unit(multiworld, player) and - state._sc2wol_has_competent_anti_air(multiworld, player)), - LocationData("Welcome to the Jungle", "Welcome to the Jungle: North-East Relic", SC2WOL_LOC_ID_OFFSET + 1403, - lambda state: state._sc2wol_has_common_unit(multiworld, player) and - state._sc2wol_has_competent_anti_air(multiworld, player)), - LocationData("Breakout", "Breakout: Victory", SC2WOL_LOC_ID_OFFSET + 1500), - LocationData("Breakout", "Breakout: Diamondback Prison", SC2WOL_LOC_ID_OFFSET + 1501), - LocationData("Breakout", "Breakout: Siegetank Prison", SC2WOL_LOC_ID_OFFSET + 1502), - LocationData("Ghost of a Chance", "Ghost of a Chance: Victory", SC2WOL_LOC_ID_OFFSET + 1600), - LocationData("Ghost of a Chance", "Ghost of a Chance: Terrazine Tank", SC2WOL_LOC_ID_OFFSET + 1601), - LocationData("Ghost of a Chance", "Ghost of a Chance: Jorium Stockpile", SC2WOL_LOC_ID_OFFSET + 1602), - LocationData("Ghost of a Chance", "Ghost of a Chance: First Island Spectres", SC2WOL_LOC_ID_OFFSET + 1603), - LocationData("Ghost of a Chance", "Ghost of a Chance: Second Island Spectres", SC2WOL_LOC_ID_OFFSET + 1604), - LocationData("Ghost of a Chance", "Ghost of a Chance: Third Island Spectres", SC2WOL_LOC_ID_OFFSET + 1605), - LocationData("The Great Train Robbery", "The Great Train Robbery: Victory", SC2WOL_LOC_ID_OFFSET + 1700, + LocationData("Devil's Playground", "Devil's Playground: North Reapers", SC2WOL_LOC_ID_OFFSET + 1303, LocationType.BONUS), + LocationData("Devil's Playground", "Devil's Playground: Middle Reapers", SC2WOL_LOC_ID_OFFSET + 1304, LocationType.BONUS, + lambda state: logic_level > 0 or state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player)), + LocationData("Devil's Playground", "Devil's Playground: Southwest Reapers", SC2WOL_LOC_ID_OFFSET + 1305, LocationType.BONUS, + lambda state: logic_level > 0 or state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player)), + LocationData("Devil's Playground", "Devil's Playground: Southeast Reapers", SC2WOL_LOC_ID_OFFSET + 1306, LocationType.BONUS, + lambda state: logic_level > 0 or + state._sc2wol_has_anti_air(multiworld, player) and ( + state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))), + LocationData("Devil's Playground", "Devil's Playground: East Reapers", SC2WOL_LOC_ID_OFFSET + 1307, LocationType.BONUS, + lambda state: state._sc2wol_has_anti_air(multiworld, player) and + (logic_level > 0 or + state._sc2wol_has_common_unit(multiworld, player) or state.has("Reaper", player))), + LocationData("Welcome to the Jungle", "Welcome to the Jungle: Victory", SC2WOL_LOC_ID_OFFSET + 1400, LocationType.VICTORY, + lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player)), + LocationData("Welcome to the Jungle", "Welcome to the Jungle: Close Relic", SC2WOL_LOC_ID_OFFSET + 1401, LocationType.BONUS), + LocationData("Welcome to the Jungle", "Welcome to the Jungle: West Relic", SC2WOL_LOC_ID_OFFSET + 1402, LocationType.BONUS, + lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player)), + LocationData("Welcome to the Jungle", "Welcome to the Jungle: North-East Relic", SC2WOL_LOC_ID_OFFSET + 1403, LocationType.BONUS, + lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player)), + LocationData("Welcome to the Jungle", "Welcome to the Jungle: Middle Base", SC2WOL_LOC_ID_OFFSET + 1404, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player)), + LocationData("Welcome to the Jungle", "Welcome to the Jungle: Main Base", SC2WOL_LOC_ID_OFFSET + 1405, LocationType.CHALLENGE, + lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player) + and state._sc2wol_beats_protoss_deathball(multiworld, player)), + LocationData("Welcome to the Jungle", "Welcome to the Jungle: No Terrazine Nodes Sealed", SC2WOL_LOC_ID_OFFSET + 1406, LocationType.CHALLENGE, + lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player) + and state._sc2wol_has_competent_ground_to_air(multiworld, player) + and state._sc2wol_beats_protoss_deathball(multiworld, player)), + LocationData("Welcome to the Jungle", "Welcome to the Jungle: Up to 1 Terrazine Node Sealed", SC2WOL_LOC_ID_OFFSET + 1407, LocationType.CHALLENGE, + lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player) + and state._sc2wol_has_competent_ground_to_air(multiworld, player) + and state._sc2wol_beats_protoss_deathball(multiworld, player)), + LocationData("Welcome to the Jungle", "Welcome to the Jungle: Up to 2 Terrazine Nodes Sealed", SC2WOL_LOC_ID_OFFSET + 1408, LocationType.CHALLENGE, + lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player) + and state._sc2wol_beats_protoss_deathball(multiworld, player)), + LocationData("Welcome to the Jungle", "Welcome to the Jungle: Up to 3 Terrazine Nodes Sealed", SC2WOL_LOC_ID_OFFSET + 1409, LocationType.CHALLENGE, + lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player) + and state._sc2wol_has_competent_comp(multiworld, player)), + LocationData("Welcome to the Jungle", "Welcome to the Jungle: Up to 4 Terrazine Nodes Sealed", SC2WOL_LOC_ID_OFFSET + 1410, LocationType.CHALLENGE, + lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player)), + LocationData("Welcome to the Jungle", "Welcome to the Jungle: Up to 5 Terrazine Nodes Sealed", SC2WOL_LOC_ID_OFFSET + 1411, LocationType.CHALLENGE, + lambda state: state._sc2wol_welcome_to_the_jungle_requirement(multiworld, player)), + LocationData("Breakout", "Breakout: Victory", SC2WOL_LOC_ID_OFFSET + 1500, LocationType.VICTORY), + LocationData("Breakout", "Breakout: Diamondback Prison", SC2WOL_LOC_ID_OFFSET + 1501, LocationType.BONUS), + LocationData("Breakout", "Breakout: Siege Tank Prison", SC2WOL_LOC_ID_OFFSET + 1502, LocationType.BONUS), + LocationData("Breakout", "Breakout: First Checkpoint", SC2WOL_LOC_ID_OFFSET + 1503, LocationType.MISSION_PROGRESS), + LocationData("Breakout", "Breakout: Second Checkpoint", SC2WOL_LOC_ID_OFFSET + 1504, LocationType.MISSION_PROGRESS), + LocationData("Ghost of a Chance", "Ghost of a Chance: Victory", SC2WOL_LOC_ID_OFFSET + 1600, LocationType.VICTORY), + LocationData("Ghost of a Chance", "Ghost of a Chance: Terrazine Tank", SC2WOL_LOC_ID_OFFSET + 1601, LocationType.MISSION_PROGRESS), + LocationData("Ghost of a Chance", "Ghost of a Chance: Jorium Stockpile", SC2WOL_LOC_ID_OFFSET + 1602, LocationType.MISSION_PROGRESS), + LocationData("Ghost of a Chance", "Ghost of a Chance: First Island Spectres", SC2WOL_LOC_ID_OFFSET + 1603, LocationType.BONUS), + LocationData("Ghost of a Chance", "Ghost of a Chance: Second Island Spectres", SC2WOL_LOC_ID_OFFSET + 1604, LocationType.BONUS), + LocationData("Ghost of a Chance", "Ghost of a Chance: Third Island Spectres", SC2WOL_LOC_ID_OFFSET + 1605, LocationType.BONUS), + LocationData("The Great Train Robbery", "The Great Train Robbery: Victory", SC2WOL_LOC_ID_OFFSET + 1700, LocationType.VICTORY, lambda state: state._sc2wol_has_train_killers(multiworld, player) and state._sc2wol_has_anti_air(multiworld, player)), - LocationData("The Great Train Robbery", "The Great Train Robbery: North Defiler", SC2WOL_LOC_ID_OFFSET + 1701), - LocationData("The Great Train Robbery", "The Great Train Robbery: Mid Defiler", SC2WOL_LOC_ID_OFFSET + 1702), - LocationData("The Great Train Robbery", "The Great Train Robbery: South Defiler", SC2WOL_LOC_ID_OFFSET + 1703), - LocationData("Cutthroat", "Cutthroat: Victory", SC2WOL_LOC_ID_OFFSET + 1800, + LocationData("The Great Train Robbery", "The Great Train Robbery: North Defiler", SC2WOL_LOC_ID_OFFSET + 1701, LocationType.BONUS), + LocationData("The Great Train Robbery", "The Great Train Robbery: Mid Defiler", SC2WOL_LOC_ID_OFFSET + 1702, LocationType.BONUS), + LocationData("The Great Train Robbery", "The Great Train Robbery: South Defiler", SC2WOL_LOC_ID_OFFSET + 1703, LocationType.BONUS), + LocationData("The Great Train Robbery", "The Great Train Robbery: Close Diamondback", SC2WOL_LOC_ID_OFFSET + 1704, LocationType.BONUS), + LocationData("The Great Train Robbery", "The Great Train Robbery: Northwest Diamondback", SC2WOL_LOC_ID_OFFSET + 1705, LocationType.BONUS), + LocationData("The Great Train Robbery", "The Great Train Robbery: North Diamondback", SC2WOL_LOC_ID_OFFSET + 1706, LocationType.BONUS), + LocationData("The Great Train Robbery", "The Great Train Robbery: Northeast Diamondback", SC2WOL_LOC_ID_OFFSET + 1707, LocationType.BONUS), + LocationData("The Great Train Robbery", "The Great Train Robbery: Southwest Diamondback", SC2WOL_LOC_ID_OFFSET + 1708, LocationType.BONUS), + LocationData("The Great Train Robbery", "The Great Train Robbery: Southeast Diamondback", SC2WOL_LOC_ID_OFFSET + 1709, LocationType.BONUS), + LocationData("The Great Train Robbery", "The Great Train Robbery: Kill Team", SC2WOL_LOC_ID_OFFSET + 1710, LocationType.CHALLENGE, + lambda state: (logic_level > 0 or state._sc2wol_has_common_unit(multiworld, player)) and + state._sc2wol_has_train_killers(multiworld, player) and + state._sc2wol_has_anti_air(multiworld, player)), + LocationData("Cutthroat", "Cutthroat: Victory", SC2WOL_LOC_ID_OFFSET + 1800, LocationType.VICTORY, lambda state: state._sc2wol_has_common_unit(multiworld, player) and (logic_level > 0 or state._sc2wol_has_anti_air)), - LocationData("Cutthroat", "Cutthroat: Mira Han", SC2WOL_LOC_ID_OFFSET + 1801, + LocationData("Cutthroat", "Cutthroat: Mira Han", SC2WOL_LOC_ID_OFFSET + 1801, LocationType.MISSION_PROGRESS, lambda state: state._sc2wol_has_common_unit(multiworld, player)), - LocationData("Cutthroat", "Cutthroat: North Relic", SC2WOL_LOC_ID_OFFSET + 1802, + LocationData("Cutthroat", "Cutthroat: North Relic", SC2WOL_LOC_ID_OFFSET + 1802, LocationType.BONUS, lambda state: state._sc2wol_has_common_unit(multiworld, player)), - LocationData("Cutthroat", "Cutthroat: Mid Relic", SC2WOL_LOC_ID_OFFSET + 1803), - LocationData("Cutthroat", "Cutthroat: Southwest Relic", SC2WOL_LOC_ID_OFFSET + 1804, + LocationData("Cutthroat", "Cutthroat: Mid Relic", SC2WOL_LOC_ID_OFFSET + 1803, LocationType.BONUS), + LocationData("Cutthroat", "Cutthroat: Southwest Relic", SC2WOL_LOC_ID_OFFSET + 1804, LocationType.BONUS, lambda state: state._sc2wol_has_common_unit(multiworld, player)), - LocationData("Engine of Destruction", "Engine of Destruction: Victory", SC2WOL_LOC_ID_OFFSET + 1900, + LocationData("Cutthroat", "Cutthroat: North Command Center", SC2WOL_LOC_ID_OFFSET + 1805, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_common_unit(multiworld, player)), + LocationData("Cutthroat", "Cutthroat: South Command Center", SC2WOL_LOC_ID_OFFSET + 1806, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_common_unit(multiworld, player)), + LocationData("Cutthroat", "Cutthroat: West Command Center", SC2WOL_LOC_ID_OFFSET + 1807, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_common_unit(multiworld, player)), + LocationData("Engine of Destruction", "Engine of Destruction: Victory", SC2WOL_LOC_ID_OFFSET + 1900, LocationType.VICTORY, lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)), - LocationData("Engine of Destruction", "Engine of Destruction: Odin", SC2WOL_LOC_ID_OFFSET + 1901), - LocationData("Engine of Destruction", "Engine of Destruction: Loki", SC2WOL_LOC_ID_OFFSET + 1902, + LocationData("Engine of Destruction", "Engine of Destruction: Odin", SC2WOL_LOC_ID_OFFSET + 1901, LocationType.MISSION_PROGRESS), + LocationData("Engine of Destruction", "Engine of Destruction: Loki", SC2WOL_LOC_ID_OFFSET + 1902, LocationType.OPTIONAL_BOSS, lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)), - LocationData("Engine of Destruction", "Engine of Destruction: Lab Devourer", SC2WOL_LOC_ID_OFFSET + 1903), - LocationData("Engine of Destruction", "Engine of Destruction: North Devourer", SC2WOL_LOC_ID_OFFSET + 1904, + LocationData("Engine of Destruction", "Engine of Destruction: Lab Devourer", SC2WOL_LOC_ID_OFFSET + 1903, LocationType.BONUS), + LocationData("Engine of Destruction", "Engine of Destruction: North Devourer", SC2WOL_LOC_ID_OFFSET + 1904, LocationType.BONUS, lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)), - LocationData("Engine of Destruction", "Engine of Destruction: Southeast Devourer", SC2WOL_LOC_ID_OFFSET + 1905, + LocationData("Engine of Destruction", "Engine of Destruction: Southeast Devourer", SC2WOL_LOC_ID_OFFSET + 1905, LocationType.BONUS, lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)), - LocationData("Media Blitz", "Media Blitz: Victory", SC2WOL_LOC_ID_OFFSET + 2000, + LocationData("Engine of Destruction", "Engine of Destruction: West Base", SC2WOL_LOC_ID_OFFSET + 1906, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and + state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)), + LocationData("Engine of Destruction", "Engine of Destruction: Northwest Base", SC2WOL_LOC_ID_OFFSET + 1907, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and + state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)), + LocationData("Engine of Destruction", "Engine of Destruction: Northeast Base", SC2WOL_LOC_ID_OFFSET + 1908, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and + state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)), + LocationData("Engine of Destruction", "Engine of Destruction: Southeast Base", SC2WOL_LOC_ID_OFFSET + 1909, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_competent_anti_air(multiworld, player) and + state._sc2wol_has_common_unit(multiworld, player) or state.has('Wraith', player)), + LocationData("Media Blitz", "Media Blitz: Victory", SC2WOL_LOC_ID_OFFSET + 2000, LocationType.VICTORY, lambda state: state._sc2wol_has_competent_comp(multiworld, player)), - LocationData("Media Blitz", "Media Blitz: Tower 1", SC2WOL_LOC_ID_OFFSET + 2001, + LocationData("Media Blitz", "Media Blitz: Tower 1", SC2WOL_LOC_ID_OFFSET + 2001, LocationType.MISSION_PROGRESS, lambda state: state._sc2wol_has_competent_comp(multiworld, player)), - LocationData("Media Blitz", "Media Blitz: Tower 2", SC2WOL_LOC_ID_OFFSET + 2002, + LocationData("Media Blitz", "Media Blitz: Tower 2", SC2WOL_LOC_ID_OFFSET + 2002, LocationType.MISSION_PROGRESS, lambda state: state._sc2wol_has_competent_comp(multiworld, player)), - LocationData("Media Blitz", "Media Blitz: Tower 3", SC2WOL_LOC_ID_OFFSET + 2003, + LocationData("Media Blitz", "Media Blitz: Tower 3", SC2WOL_LOC_ID_OFFSET + 2003, LocationType.MISSION_PROGRESS, lambda state: state._sc2wol_has_competent_comp(multiworld, player)), - LocationData("Media Blitz", "Media Blitz: Science Facility", SC2WOL_LOC_ID_OFFSET + 2004), - LocationData("Piercing the Shroud", "Piercing the Shroud: Victory", SC2WOL_LOC_ID_OFFSET + 2100, + LocationData("Media Blitz", "Media Blitz: Science Facility", SC2WOL_LOC_ID_OFFSET + 2004, LocationType.BONUS), + LocationData("Media Blitz", "Media Blitz: All Barracks", SC2WOL_LOC_ID_OFFSET + 2005, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_competent_comp(multiworld, player)), + LocationData("Media Blitz", "Media Blitz: All Factories", SC2WOL_LOC_ID_OFFSET + 2006, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_competent_comp(multiworld, player)), + LocationData("Media Blitz", "Media Blitz: All Starports", SC2WOL_LOC_ID_OFFSET + 2007, LocationType.MISSION_PROGRESS, + lambda state: logic_level > 0 or state._sc2wol_has_competent_comp(multiworld, player)), + LocationData("Media Blitz", "Media Blitz: Odin Not Trashed", SC2WOL_LOC_ID_OFFSET + 2008, LocationType.CHALLENGE, + lambda state: state._sc2wol_has_competent_comp(multiworld, player)), + LocationData("Piercing the Shroud", "Piercing the Shroud: Victory", SC2WOL_LOC_ID_OFFSET + 2100, LocationType.VICTORY, lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)), - LocationData("Piercing the Shroud", "Piercing the Shroud: Holding Cell Relic", SC2WOL_LOC_ID_OFFSET + 2101), - LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk Relic", SC2WOL_LOC_ID_OFFSET + 2102, + LocationData("Piercing the Shroud", "Piercing the Shroud: Holding Cell Relic", SC2WOL_LOC_ID_OFFSET + 2101, LocationType.BONUS), + LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk Relic", SC2WOL_LOC_ID_OFFSET + 2102, LocationType.BONUS, lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)), - LocationData("Piercing the Shroud", "Piercing the Shroud: First Escape Relic", SC2WOL_LOC_ID_OFFSET + 2103, + LocationData("Piercing the Shroud", "Piercing the Shroud: First Escape Relic", SC2WOL_LOC_ID_OFFSET + 2103,LocationType.BONUS, lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)), - LocationData("Piercing the Shroud", "Piercing the Shroud: Second Escape Relic", SC2WOL_LOC_ID_OFFSET + 2104, + LocationData("Piercing the Shroud", "Piercing the Shroud: Second Escape Relic", SC2WOL_LOC_ID_OFFSET + 2104, LocationType.BONUS, lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)), - LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk", SC2WOL_LOC_ID_OFFSET + 2105, + LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk", SC2WOL_LOC_ID_OFFSET + 2105, LocationType.OPTIONAL_BOSS, lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)), - LocationData("Whispers of Doom", "Whispers of Doom: Victory", SC2WOL_LOC_ID_OFFSET + 2200), - LocationData("Whispers of Doom", "Whispers of Doom: First Hatchery", SC2WOL_LOC_ID_OFFSET + 2201), - LocationData("Whispers of Doom", "Whispers of Doom: Second Hatchery", SC2WOL_LOC_ID_OFFSET + 2202), - LocationData("Whispers of Doom", "Whispers of Doom: Third Hatchery", SC2WOL_LOC_ID_OFFSET + 2203), - LocationData("A Sinister Turn", "A Sinister Turn: Victory", SC2WOL_LOC_ID_OFFSET + 2300, + LocationData("Piercing the Shroud", "Piercing the Shroud: Fusion Reactor", SC2WOL_LOC_ID_OFFSET + 2106, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_mm_upgrade(multiworld, player)), + LocationData("Whispers of Doom", "Whispers of Doom: Victory", SC2WOL_LOC_ID_OFFSET + 2200, LocationType.VICTORY), + LocationData("Whispers of Doom", "Whispers of Doom: First Hatchery", SC2WOL_LOC_ID_OFFSET + 2201, LocationType.BONUS), + LocationData("Whispers of Doom", "Whispers of Doom: Second Hatchery", SC2WOL_LOC_ID_OFFSET + 2202, LocationType.BONUS), + LocationData("Whispers of Doom", "Whispers of Doom: Third Hatchery", SC2WOL_LOC_ID_OFFSET + 2203, LocationType.BONUS), + LocationData("Whispers of Doom", "Whispers of Doom: First Prophecy Fragment", SC2WOL_LOC_ID_OFFSET + 2204, LocationType.MISSION_PROGRESS), + LocationData("Whispers of Doom", "Whispers of Doom: Second Prophecy Fragment", SC2WOL_LOC_ID_OFFSET + 2205, LocationType.MISSION_PROGRESS), + LocationData("Whispers of Doom", "Whispers of Doom: Third Prophecy Fragment", SC2WOL_LOC_ID_OFFSET + 2206, LocationType.MISSION_PROGRESS), + LocationData("A Sinister Turn", "A Sinister Turn: Victory", SC2WOL_LOC_ID_OFFSET + 2300, LocationType.VICTORY, lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)), - LocationData("A Sinister Turn", "A Sinister Turn: Robotics Facility", SC2WOL_LOC_ID_OFFSET + 2301, + LocationData("A Sinister Turn", "A Sinister Turn: Robotics Facility", SC2WOL_LOC_ID_OFFSET + 2301, LocationType.BONUS, lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(multiworld, player)), - LocationData("A Sinister Turn", "A Sinister Turn: Dark Shrine", SC2WOL_LOC_ID_OFFSET + 2302, + LocationData("A Sinister Turn", "A Sinister Turn: Dark Shrine", SC2WOL_LOC_ID_OFFSET + 2302, LocationType.BONUS, lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(multiworld, player)), - LocationData("A Sinister Turn", "A Sinister Turn: Templar Archives", SC2WOL_LOC_ID_OFFSET + 2303, - lambda state: state._sc2wol_has_protoss_common_units(multiworld, player)), - LocationData("Echoes of the Future", "Echoes of the Future: Victory", SC2WOL_LOC_ID_OFFSET + 2400, + LocationData("A Sinister Turn", "A Sinister Turn: Templar Archives", SC2WOL_LOC_ID_OFFSET + 2303, LocationType.BONUS, + lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)), + LocationData("A Sinister Turn", "A Sinister Turn: Northeast Base", SC2WOL_LOC_ID_OFFSET + 2304, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)), + LocationData("A Sinister Turn", "A Sinister Turn: Southeast Base", SC2WOL_LOC_ID_OFFSET + 2305, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)), + LocationData("A Sinister Turn", "A Sinister Turn: Maar", SC2WOL_LOC_ID_OFFSET + 2306, LocationType.MISSION_PROGRESS, lambda state: logic_level > 0 or state._sc2wol_has_protoss_medium_units(multiworld, player)), - LocationData("Echoes of the Future", "Echoes of the Future: Close Obelisk", SC2WOL_LOC_ID_OFFSET + 2401), - LocationData("Echoes of the Future", "Echoes of the Future: West Obelisk", SC2WOL_LOC_ID_OFFSET + 2402, - lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(multiworld, player)), - LocationData("In Utter Darkness", "In Utter Darkness: Defeat", SC2WOL_LOC_ID_OFFSET + 2500), - LocationData("In Utter Darkness", "In Utter Darkness: Protoss Archive", SC2WOL_LOC_ID_OFFSET + 2501, + LocationData("A Sinister Turn", "A Sinister Turn: Northwest Preserver", SC2WOL_LOC_ID_OFFSET + 2307, LocationType.MISSION_PROGRESS, lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)), - LocationData("In Utter Darkness", "In Utter Darkness: Kills", SC2WOL_LOC_ID_OFFSET + 2502, + LocationData("A Sinister Turn", "A Sinister Turn: Southwest Preserver", SC2WOL_LOC_ID_OFFSET + 2308, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)), + LocationData("A Sinister Turn", "A Sinister Turn: East Preserver", SC2WOL_LOC_ID_OFFSET + 2309, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)), + LocationData("Echoes of the Future", "Echoes of the Future: Victory", SC2WOL_LOC_ID_OFFSET + 2400, LocationType.VICTORY, + lambda state: logic_level > 0 or state._sc2wol_has_protoss_medium_units(multiworld, player)), + LocationData("Echoes of the Future", "Echoes of the Future: Close Obelisk", SC2WOL_LOC_ID_OFFSET + 2401, LocationType.BONUS), + LocationData("Echoes of the Future", "Echoes of the Future: West Obelisk", SC2WOL_LOC_ID_OFFSET + 2402, LocationType.BONUS, + lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(multiworld, player)), + LocationData("Echoes of the Future", "Echoes of the Future: Base", SC2WOL_LOC_ID_OFFSET + 2403, LocationType.MISSION_PROGRESS), + LocationData("Echoes of the Future", "Echoes of the Future: Southwest Tendril", SC2WOL_LOC_ID_OFFSET + 2404, LocationType.MISSION_PROGRESS), + LocationData("Echoes of the Future", "Echoes of the Future: Southeast Tendril", SC2WOL_LOC_ID_OFFSET + 2405, LocationType.MISSION_PROGRESS, + lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(multiworld, player)), + LocationData("Echoes of the Future", "Echoes of the Future: Northeast Tendril", SC2WOL_LOC_ID_OFFSET + 2406, LocationType.MISSION_PROGRESS, + lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(multiworld, player)), + LocationData("Echoes of the Future", "Echoes of the Future: Northwest Tendril", SC2WOL_LOC_ID_OFFSET + 2407, LocationType.MISSION_PROGRESS, + lambda state: logic_level > 0 or state._sc2wol_has_protoss_common_units(multiworld, player)), + LocationData("In Utter Darkness", "In Utter Darkness: Defeat", SC2WOL_LOC_ID_OFFSET + 2500, LocationType.VICTORY), + LocationData("In Utter Darkness", "In Utter Darkness: Protoss Archive", SC2WOL_LOC_ID_OFFSET + 2501, LocationType.BONUS, + lambda state: state._sc2wol_has_protoss_medium_units(multiworld, player)), + LocationData("In Utter Darkness", "In Utter Darkness: Kills", SC2WOL_LOC_ID_OFFSET + 2502, LocationType.CHALLENGE, lambda state: state._sc2wol_has_protoss_common_units(multiworld, player)), - LocationData("Gates of Hell", "Gates of Hell: Victory", SC2WOL_LOC_ID_OFFSET + 2600, + LocationData("In Utter Darkness", "In Utter Darkness: Urun", SC2WOL_LOC_ID_OFFSET + 2503, LocationType.MISSION_PROGRESS), + LocationData("In Utter Darkness", "In Utter Darkness: Mohandar", SC2WOL_LOC_ID_OFFSET + 2504, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_protoss_common_units(multiworld, player)), + LocationData("In Utter Darkness", "In Utter Darkness: Selendis", SC2WOL_LOC_ID_OFFSET + 2505, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_protoss_common_units(multiworld, player)), + LocationData("In Utter Darkness", "In Utter Darkness: Artanis", SC2WOL_LOC_ID_OFFSET + 2506, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_protoss_common_units(multiworld, player)), + LocationData("Gates of Hell", "Gates of Hell: Victory", SC2WOL_LOC_ID_OFFSET + 2600, LocationType.VICTORY, lambda state: state._sc2wol_has_competent_comp(multiworld, player) and state._sc2wol_defense_rating(multiworld, player, True) > 6), - LocationData("Gates of Hell", "Gates of Hell: Large Army", SC2WOL_LOC_ID_OFFSET + 2601, + LocationData("Gates of Hell", "Gates of Hell: Large Army", SC2WOL_LOC_ID_OFFSET + 2601, LocationType.MISSION_PROGRESS, lambda state: state._sc2wol_has_competent_comp(multiworld, player) and state._sc2wol_defense_rating(multiworld, player, True) > 6), - LocationData("Belly of the Beast", "Belly of the Beast: Victory", SC2WOL_LOC_ID_OFFSET + 2700), - LocationData("Belly of the Beast", "Belly of the Beast: First Charge", SC2WOL_LOC_ID_OFFSET + 2701), - LocationData("Belly of the Beast", "Belly of the Beast: Second Charge", SC2WOL_LOC_ID_OFFSET + 2702), - LocationData("Belly of the Beast", "Belly of the Beast: Third Charge", SC2WOL_LOC_ID_OFFSET + 2703), - LocationData("Shatter the Sky", "Shatter the Sky: Victory", SC2WOL_LOC_ID_OFFSET + 2800, + LocationData("Gates of Hell", "Gates of Hell: 2 Drop Pods", SC2WOL_LOC_ID_OFFSET + 2602, LocationType.BONUS, + lambda state: state._sc2wol_has_competent_comp(multiworld, player) and + state._sc2wol_defense_rating(multiworld, player, True) > 6), + LocationData("Gates of Hell", "Gates of Hell: 4 Drop Pods", SC2WOL_LOC_ID_OFFSET + 2603, LocationType.BONUS, + lambda state: state._sc2wol_has_competent_comp(multiworld, player) and + state._sc2wol_defense_rating(multiworld, player, True) > 6), + LocationData("Gates of Hell", "Gates of Hell: 6 Drop Pods", SC2WOL_LOC_ID_OFFSET + 2604, LocationType.BONUS, + lambda state: state._sc2wol_has_competent_comp(multiworld, player) and + state._sc2wol_defense_rating(multiworld, player, True) > 6), + LocationData("Gates of Hell", "Gates of Hell: 8 Drop Pods", SC2WOL_LOC_ID_OFFSET + 2605, LocationType.BONUS, + lambda state: state._sc2wol_has_competent_comp(multiworld, player) and + state._sc2wol_defense_rating(multiworld, player, True) > 6), + LocationData("Belly of the Beast", "Belly of the Beast: Victory", SC2WOL_LOC_ID_OFFSET + 2700, LocationType.VICTORY), + LocationData("Belly of the Beast", "Belly of the Beast: First Charge", SC2WOL_LOC_ID_OFFSET + 2701, LocationType.MISSION_PROGRESS), + LocationData("Belly of the Beast", "Belly of the Beast: Second Charge", SC2WOL_LOC_ID_OFFSET + 2702, LocationType.MISSION_PROGRESS), + LocationData("Belly of the Beast", "Belly of the Beast: Third Charge", SC2WOL_LOC_ID_OFFSET + 2703, LocationType.MISSION_PROGRESS), + LocationData("Belly of the Beast", "Belly of the Beast: First Group Rescued", SC2WOL_LOC_ID_OFFSET + 2704, LocationType.BONUS), + LocationData("Belly of the Beast", "Belly of the Beast: Second Group Rescued", SC2WOL_LOC_ID_OFFSET + 2705, LocationType.BONUS), + LocationData("Belly of the Beast", "Belly of the Beast: Third Group Rescued", SC2WOL_LOC_ID_OFFSET + 2706, LocationType.BONUS), + LocationData("Shatter the Sky", "Shatter the Sky: Victory", SC2WOL_LOC_ID_OFFSET + 2800, LocationType.VICTORY, lambda state: state._sc2wol_has_competent_comp(multiworld, player)), - LocationData("Shatter the Sky", "Shatter the Sky: Close Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2801, + LocationData("Shatter the Sky", "Shatter the Sky: Close Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2801, LocationType.MISSION_PROGRESS, lambda state: state._sc2wol_has_competent_comp(multiworld, player)), - LocationData("Shatter the Sky", "Shatter the Sky: Northwest Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2802, + LocationData("Shatter the Sky", "Shatter the Sky: Northwest Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2802, LocationType.MISSION_PROGRESS, lambda state: state._sc2wol_has_competent_comp(multiworld, player)), - LocationData("Shatter the Sky", "Shatter the Sky: Southeast Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2803, + LocationData("Shatter the Sky", "Shatter the Sky: Southeast Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2803, LocationType.MISSION_PROGRESS, lambda state: state._sc2wol_has_competent_comp(multiworld, player)), - LocationData("Shatter the Sky", "Shatter the Sky: Southwest Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2804, + LocationData("Shatter the Sky", "Shatter the Sky: Southwest Coolant Tower", SC2WOL_LOC_ID_OFFSET + 2804, LocationType.MISSION_PROGRESS, lambda state: state._sc2wol_has_competent_comp(multiworld, player)), - LocationData("Shatter the Sky", "Shatter the Sky: Leviathan", SC2WOL_LOC_ID_OFFSET + 2805, + LocationData("Shatter the Sky", "Shatter the Sky: Leviathan", SC2WOL_LOC_ID_OFFSET + 2805, LocationType.OPTIONAL_BOSS, lambda state: state._sc2wol_has_competent_comp(multiworld, player)), - LocationData("All-In", "All-In: Victory", None, + LocationData("Shatter the Sky", "Shatter the Sky: East Hatchery", SC2WOL_LOC_ID_OFFSET + 2806, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_competent_comp(multiworld, player)), + LocationData("Shatter the Sky", "Shatter the Sky: North Hatchery", SC2WOL_LOC_ID_OFFSET + 2807, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_competent_comp(multiworld, player)), + LocationData("Shatter the Sky", "Shatter the Sky: Mid Hatchery", SC2WOL_LOC_ID_OFFSET + 2808, LocationType.MISSION_PROGRESS, + lambda state: state._sc2wol_has_competent_comp(multiworld, player)), + LocationData("All-In", "All-In: Victory", None, LocationType.VICTORY, lambda state: state._sc2wol_final_mission_requirements(multiworld, player)) ] diff --git a/worlds/sc2wol/LogicMixin.py b/worlds/sc2wol/LogicMixin.py index 8c7182e0e8..112302beb2 100644 --- a/worlds/sc2wol/LogicMixin.py +++ b/worlds/sc2wol/LogicMixin.py @@ -9,22 +9,38 @@ class SC2WoLLogic(LogicMixin): return self.has_any(get_basic_units(multiworld, player), player) def _sc2wol_has_air(self, multiworld: MultiWorld, player: int) -> bool: - return self.has_any({'Viking', 'Wraith', 'Banshee'}, player) or get_option_value(multiworld, player, 'required_tactics') > 0 \ + return self.has_any({'Viking', 'Wraith', 'Banshee', 'Battlecruiser'}, player) or get_option_value(multiworld, player, 'required_tactics') > 0 \ and self.has_any({'Hercules', 'Medivac'}, player) and self._sc2wol_has_common_unit(multiworld, player) def _sc2wol_has_air_anti_air(self, multiworld: MultiWorld, player: int) -> bool: return self.has('Viking', player) \ - or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has('Wraith', player) + or self.has_all({'Wraith', 'Advanced Laser Technology (Wraith)'}, player) \ + or self.has_all({'Battlecruiser', 'ATX Laser Battery (Battlecruiser)'}, player) \ + or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has_any({'Wraith', 'Valkyrie', 'Battlecruiser'}, player) - def _sc2wol_has_competent_anti_air(self, multiworld: MultiWorld, player: int) -> bool: + def _sc2wol_has_competent_ground_to_air(self, multiworld: MultiWorld, player: int) -> bool: return self.has('Goliath', player) \ or self.has('Marine', player) and self.has_any({'Medic', 'Medivac'}, player) \ + or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has('Cyclone', player) + + def _sc2wol_has_competent_anti_air(self, multiworld: MultiWorld, player: int) -> bool: + return self._sc2wol_has_competent_ground_to_air(multiworld, player) \ or self._sc2wol_has_air_anti_air(multiworld, player) + def _sc2wol_welcome_to_the_jungle_requirement(self, multiworld: MultiWorld, player: int) -> bool: + return ( + self._sc2wol_has_common_unit(multiworld, player) + and self._sc2wol_has_competent_ground_to_air(multiworld, player) + ) or ( + get_option_value(multiworld, player, 'required_tactics') > 0 + and self.has_any({'Marine', 'Vulture'}, player) + and self._sc2wol_has_air_anti_air(multiworld, player) + ) + def _sc2wol_has_anti_air(self, multiworld: MultiWorld, player: int) -> bool: - return self.has_any({'Missile Turret', 'Thor', 'War Pigs', 'Spartan Company', "Hel's Angel", 'Battlecruiser', 'Marine', 'Wraith'}, player) \ + return self.has_any({'Missile Turret', 'Thor', 'War Pigs', 'Spartan Company', "Hel's Angel", 'Battlecruiser', 'Marine', 'Wraith', 'Valkyrie', 'Cyclone'}, player) \ or self._sc2wol_has_competent_anti_air(multiworld, player) \ - or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has_any({'Ghost', 'Spectre'}, player) + or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has_any({'Ghost', 'Spectre', 'Widow Mine', 'Liberator'}, player) def _sc2wol_defense_rating(self, multiworld: MultiWorld, player: int, zerg_enemy: bool, air_enemy: bool = True) -> bool: defense_score = sum((defense_ratings[item] for item in defense_ratings if self.has(item, player))) @@ -32,6 +48,10 @@ class SC2WoLLogic(LogicMixin): defense_score += 3 if self.has_all({'Siege Tank', 'Maelstrom Rounds (Siege Tank)'}, player): defense_score += 2 + if self.has_all({'Siege Tank', 'Graduating Range (Siege Tank)'}, player): + defense_score += 1 + if self.has_all({'Widow Mine', 'Concealment (Widow Mine)'}, player): + defense_score += 1 if zerg_enemy: defense_score += sum((zerg_defense_ratings[item] for item in zerg_defense_ratings if self.has(item, player))) if self.has('Firebat', player) and self.has('Bunker', player): @@ -44,20 +64,27 @@ class SC2WoLLogic(LogicMixin): return defense_score def _sc2wol_has_competent_comp(self, multiworld: MultiWorld, player: int) -> bool: - return (self.has('Marine', player) or self.has('Marauder', player) and - self._sc2wol_has_competent_anti_air(multiworld, player)) and self.has_any({'Medivac', 'Medic'}, player) or \ - self.has('Thor', player) or self.has("Banshee", player) and self._sc2wol_has_competent_anti_air(multiworld, player) or \ - self.has('Battlecruiser', player) and self._sc2wol_has_common_unit(multiworld, player) or \ - self.has('Siege Tank', player) and self._sc2wol_has_competent_anti_air(multiworld, player) + return \ + ( + ( + self.has_any({'Marine', 'Marauder'}, player) and self.has_any({'Medivac', 'Medic'}, player) + or self.has_any({'Thor', 'Banshee', 'Siege Tank'}, player) + or self.has_all({'Liberator', 'Raid Artillery (Liberator)'}, player) + ) and self._sc2wol_has_competent_anti_air(multiworld, player) + ) \ + or \ + ( + self.has('Battlecruiser', player) and self._sc2wol_has_common_unit(multiworld, player) + ) def _sc2wol_has_train_killers(self, multiworld: MultiWorld, player: int) -> bool: return ( - self.has_any({'Siege Tank', 'Diamondback', 'Marauder'}, player) + self.has_any({'Siege Tank', 'Diamondback', 'Marauder', 'Cyclone'}, player) or get_option_value(multiworld, player, 'required_tactics') > 0 and ( self.has_all({'Reaper', "G-4 Clusterbomb"}, player) or self.has_all({'Spectre', 'Psionic Lash'}, player) - or self.has('Vulture', player) + or self.has_any({'Vulture', 'Liberator'}, player) ) ) @@ -66,16 +93,18 @@ class SC2WoLLogic(LogicMixin): def _sc2wol_has_protoss_common_units(self, multiworld: MultiWorld, player: int) -> bool: return self.has_any({'Zealot', 'Immortal', 'Stalker', 'Dark Templar'}, player) \ - or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has_any({'High Templar', 'Dark Templar'}, player) + or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has('High Templar', player) def _sc2wol_has_protoss_medium_units(self, multiworld: MultiWorld, player: int) -> bool: return self._sc2wol_has_protoss_common_units(multiworld, player) and \ - self.has_any({'Stalker', 'Void Ray', 'Phoenix', 'Carrier'}, player) \ - or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has_any({'High Templar', 'Dark Templar'}, player) + self.has_any({'Stalker', 'Void Ray', 'Carrier'}, player) \ + or get_option_value(multiworld, player, 'required_tactics') > 0 and self.has('Dark Templar', player) def _sc2wol_beats_protoss_deathball(self, multiworld: MultiWorld, player: int) -> bool: - return self.has_any({'Banshee', 'Battlecruiser'}, player) and self._sc2wol_has_competent_anti_air(multiworld, player) or \ - self._sc2wol_has_competent_comp(multiworld, player) and self._sc2wol_has_air_anti_air(multiworld, player) + return (self.has_any({'Banshee', 'Battlecruiser'}, player) or + self.has_all({'Liberator', 'Raid Artillery (Liberator)'}, player)) \ + and self._sc2wol_has_competent_anti_air(multiworld, player) or \ + self._sc2wol_has_competent_comp(multiworld, player) and self._sc2wol_has_air_anti_air(multiworld, player) def _sc2wol_has_mm_upgrade(self, multiworld: MultiWorld, player: int) -> bool: return self.has_any({"Combat Shield (Marine)", "Stabilizer Medpacks (Medic)"}, player) @@ -89,6 +118,17 @@ class SC2WoLLogic(LogicMixin): def _sc2wol_has_nukes(self, multiworld: MultiWorld, player: int) -> bool: return get_option_value(multiworld, player, 'required_tactics') > 0 and self.has_any({'Ghost', 'Spectre'}, player) + def _sc2wol_can_respond_to_colony_infestations(self, multiworld: MultiWorld, player: int) -> bool: + return self._sc2wol_has_common_unit(multiworld, player) \ + and self._sc2wol_has_competent_anti_air(multiworld, player) \ + and \ + ( + self._sc2wol_has_air_anti_air(multiworld, player) or + self.has_any({'Battlecruiser', 'Valkyrie'}), player + ) \ + and \ + self._sc2wol_defense_rating(multiworld, player, True) >= 3 + def _sc2wol_final_mission_requirements(self, multiworld: MultiWorld, player: int): beats_kerrigan = self.has_any({'Marine', 'Banshee', 'Ghost'}, player) or get_option_value(multiworld, player, 'required_tactics') > 0 if get_option_value(multiworld, player, 'all_in_map') == 0: @@ -101,7 +141,7 @@ class SC2WoLLogic(LogicMixin): # Air defense_rating = self._sc2wol_defense_rating(multiworld, player, True, True) return defense_rating >= 8 and beats_kerrigan \ - and self.has_any({'Viking', 'Battlecruiser'}, player) \ + and self.has_any({'Viking', 'Battlecruiser', 'Valkyrie'}, player) \ and self.has_any({'Hive Mind Emulator', 'Psi Disruptor', 'Missile Turret'}, player) def _sc2wol_cleared_missions(self, multiworld: MultiWorld, player: int, mission_count: int) -> bool: diff --git a/worlds/sc2wol/MissionTables.py b/worlds/sc2wol/MissionTables.py index 6db9354768..298cd7a978 100644 --- a/worlds/sc2wol/MissionTables.py +++ b/worlds/sc2wol/MissionTables.py @@ -49,8 +49,8 @@ vanilla_shuffle_order = [ FillMission(MissionPools.EASY, [2], "Artifact", completion_critical=True), FillMission(MissionPools.MEDIUM, [7], "Artifact", number=8, completion_critical=True), FillMission(MissionPools.HARD, [8], "Artifact", number=11, completion_critical=True), - FillMission(MissionPools.HARD, [9], "Artifact", number=14, completion_critical=True), - FillMission(MissionPools.HARD, [10], "Artifact", completion_critical=True), + FillMission(MissionPools.HARD, [9], "Artifact", number=14, completion_critical=True, removal_priority=11), + FillMission(MissionPools.HARD, [10], "Artifact", completion_critical=True, removal_priority=10), FillMission(MissionPools.MEDIUM, [2], "Covert", number=4), FillMission(MissionPools.MEDIUM, [12], "Covert"), FillMission(MissionPools.HARD, [13], "Covert", number=8, removal_priority=3), @@ -58,7 +58,7 @@ vanilla_shuffle_order = [ FillMission(MissionPools.MEDIUM, [2], "Rebellion", number=6), FillMission(MissionPools.HARD, [16], "Rebellion"), FillMission(MissionPools.HARD, [17], "Rebellion"), - FillMission(MissionPools.HARD, [18], "Rebellion"), + FillMission(MissionPools.HARD, [18], "Rebellion", removal_priority=12), FillMission(MissionPools.HARD, [19], "Rebellion", removal_priority=5), FillMission(MissionPools.MEDIUM, [8], "Prophecy", removal_priority=9), FillMission(MissionPools.HARD, [21], "Prophecy", removal_priority=8), @@ -98,6 +98,13 @@ gauntlet_order = [ FillMission(MissionPools.FINAL, [5], "Final", completion_critical=True) ] +mini_gauntlet_order = [ + FillMission(MissionPools.STARTER, [-1], "I", completion_critical=True), + FillMission(MissionPools.EASY, [0], "II", completion_critical=True), + FillMission(MissionPools.MEDIUM, [1], "III", completion_critical=True), + FillMission(MissionPools.FINAL, [2], "Final", completion_critical=True) +] + grid_order = [ FillMission(MissionPools.STARTER, [-1], "_1"), FillMission(MissionPools.EASY, [0], "_1"), @@ -129,6 +136,13 @@ mini_grid_order = [ FillMission(MissionPools.FINAL, [5, 7], "_3", or_requirements=True) ] +tiny_grid_order = [ + FillMission(MissionPools.STARTER, [-1], "_1"), + FillMission(MissionPools.MEDIUM, [0], "_1"), + FillMission(MissionPools.EASY, [0], "_2"), + FillMission(MissionPools.FINAL, [1, 2], "_2", or_requirements=True), +] + blitz_order = [ FillMission(MissionPools.STARTER, [-1], "I"), FillMission(MissionPools.EASY, [-1], "I"), @@ -144,7 +158,17 @@ blitz_order = [ FillMission(MissionPools.FINAL, [0, 1], "Final", number=5, or_requirements=True) ] -mission_orders = [vanilla_shuffle_order, vanilla_shuffle_order, mini_campaign_order, grid_order, mini_grid_order, blitz_order, gauntlet_order] +mission_orders = [ + vanilla_shuffle_order, + vanilla_shuffle_order, + mini_campaign_order, + grid_order, + mini_grid_order, + blitz_order, + gauntlet_order, + mini_gauntlet_order, + tiny_grid_order +] vanilla_mission_req_table = { @@ -190,7 +214,7 @@ starting_mission_locations = { "Whispers of Doom": "Whispers of Doom: Victory", "Belly of the Beast": "Belly of the Beast: Victory", "Zero Hour": "Zero Hour: First Group Rescued", - "Evacuation": "Evacuation: First Chysalis", + "Evacuation": "Evacuation: Reach Hanson", "Devil's Playground": "Devil's Playground: Tosh's Miners", "Smash and Grab": "Smash and Grab: First Relic", "The Great Train Robbery": "The Great Train Robbery: North Defiler" diff --git a/worlds/sc2wol/Options.py b/worlds/sc2wol/Options.py index 4f2032d662..0702e431a4 100644 --- a/worlds/sc2wol/Options.py +++ b/worlds/sc2wol/Options.py @@ -3,31 +3,49 @@ from BaseClasses import MultiWorld from Options import Choice, Option, Toggle, DefaultOnToggle, ItemSet, OptionSet, Range from .MissionTables import vanilla_mission_req_table +ORDER_VANILLA = 0 +ORDER_VANILLA_SHUFFLED = 1 class GameDifficulty(Choice): - """The difficulty of the campaign, affects enemy AI, starting units, and game speed.""" + """ + The difficulty of the campaign, affects enemy AI, starting units, and game speed. + + For those unfamiliar with the Archipelago randomizer, the recommended settings are one difficulty level + lower than the vanilla game + """ display_name = "Game Difficulty" option_casual = 0 option_normal = 1 option_hard = 2 option_brutal = 3 + default = 1 +class GameSpeed(Choice): + """Optional setting to override difficulty-based game speed.""" + display_name = "Game Speed" + option_default = 0 + option_slower = 1 + option_slow = 2 + option_normal = 3 + option_fast = 4 + option_faster = 5 + default = option_default -class UpgradeBonus(Choice): - """Determines what lab upgrade to use, whether it is Ultra-Capacitors which boost attack speed with every weapon - upgrade or Vanadium Plating which boosts life with every armor upgrade.""" - display_name = "Upgrade Bonus" - option_ultra_capacitors = 0 - option_vanadium_plating = 1 +class FinalMap(Choice): + """ + Determines if the final map and goal of the campaign. + All in: You need to beat All-in map + Random Hard: A random hard mission is selected as a goal. + Beat this mission in order to complete the game. + All-in map won't be in the campaign + Vanilla mission order always ends with All in mission! -class BunkerUpgrade(Choice): - """Determines what bunker lab upgrade to use, whether it is Shrike Turret which outfits bunkers with an automated - turret or Fortified Bunker which boosts the life of bunkers.""" - display_name = "Bunker Upgrade" - option_shrike_turret = 0 - option_fortified_bunker = 1 - + This option is short-lived. It may be changed in the future + """ + display_name = "Final Map" + option_all_in = 0 + option_random_hard = 1 class AllInMap(Choice): """Determines what version of All-In (final map) that will be generated for the campaign.""" @@ -37,14 +55,18 @@ class AllInMap(Choice): class MissionOrder(Choice): - """Determines the order the missions are played in. The last three mission orders end in a random mission. + """ + Determines the order the missions are played in. The last three mission orders end in a random mission. Vanilla (29): Keeps the standard mission order and branching from the WoL Campaign. Vanilla Shuffled (29): Keeps same branching paths from the WoL Campaign but randomizes the order of missions within. Mini Campaign (15): Shorter version of the campaign with randomized missions and optional branches. - Grid (16): A 4x4 grid of random missions. Start at the top-left and forge a path towards All-In. + Grid (16): A 4x4 grid of random missions. Start at the top-left and forge a path towards bottom-right mission to win. Mini Grid (9): A 3x3 version of Grid. Complete the bottom-right mission to win. Blitz (12): 12 random missions that open up very quickly. Complete the bottom-right mission to win. - Gauntlet (7): Linear series of 7 random missions to complete the campaign.""" + Gauntlet (7): Linear series of 7 random missions to complete the campaign. + Mini Gauntlet (4): Linear series of 4 random missions to complete the campaign. + Tiny Grid (4): A 2x2 version of Grid. Complete the bottom-right mission to win. + """ display_name = "Mission Order" option_vanilla = 0 option_vanilla_shuffled = 1 @@ -53,27 +75,53 @@ class MissionOrder(Choice): option_mini_grid = 4 option_blitz = 5 option_gauntlet = 6 + option_mini_gauntlet = 7 + option_tiny_grid = 8 + + +class PlayerColor(Choice): + """Determines in-game team color.""" + display_name = "Player Color" + option_white = 0 + option_red = 1 + option_blue = 2 + option_teal = 3 + option_purple = 4 + option_yellow = 5 + option_orange = 6 + option_green = 7 + option_light_pink = 8 + option_violet = 9 + option_light_grey = 10 + option_dark_green = 11 + option_brown = 12 + option_light_green = 13 + option_dark_grey = 14 + option_pink = 15 + option_rainbow = 16 + option_default = 17 + default = option_default class ShuffleProtoss(DefaultOnToggle): """Determines if the 3 protoss missions are included in the shuffle if Vanilla mission order is not enabled. - If turned off with Vanilla Shuffled, the 3 protoss missions will be in their normal position on the Prophecy chain - if not shuffled. - If turned off with reduced mission settings, the 3 protoss missions will not appear and Protoss units are removed - from the pool.""" + If turned off, the 3 protoss missions will not appear and Protoss units are removed from the pool.""" display_name = "Shuffle Protoss Missions" class ShuffleNoBuild(DefaultOnToggle): """Determines if the 5 no-build missions are included in the shuffle if Vanilla mission order is not enabled. - If turned off with Vanilla Shuffled, one no-build mission will be placed as the first mission and the rest will be - placed at the end of optional routes. - If turned off with reduced mission settings, the 5 no-build missions will not appear.""" + If turned off, the 5 no-build missions will not appear.""" display_name = "Shuffle No-Build Missions" class EarlyUnit(DefaultOnToggle): - """Guarantees that the first mission will contain a unit.""" + """ + Guarantees that the first mission will contain a unit. + + Each mission available to be the first mission has a pre-defined location where the unit should spawn. + This location gets overriden over any exclusion. It's guaranteed to be reachable with an empty inventory. + """ display_name = "Early Unit" @@ -91,11 +139,97 @@ class RequiredTactics(Choice): class UnitsAlwaysHaveUpgrades(DefaultOnToggle): - """If turned on, both upgrades will be present for each unit and structure in the seed. - This usually results in fewer units.""" + """ + If turned on, all upgrades will be present for each unit and structure in the seed. + This usually results in fewer units. + + See also: Max Number of Upgrades + """ display_name = "Units Always Have Upgrades" +class GenericUpgradeMissions(Range): + """Determines the percentage of missions in the mission order that must be completed before + level 1 of all weapon and armor upgrades is unlocked. Level 2 upgrades require double the amount of missions, + and level 3 requires triple the amount. The required amounts are always rounded down. + If set to 0, upgrades are instead added to the item pool and must be found to be used.""" + display_name = "Generic Upgrade Missions" + range_start = 0 + range_end = 100 + default = 0 + + +class GenericUpgradeResearch(Choice): + """Determines how weapon and armor upgrades affect missions once unlocked. + + Vanilla: Upgrades must be researched as normal. + Auto In No-Build: In No-Build missions, upgrades are automatically researched. + In all other missions, upgrades must be researched as normal. + Auto In Build: In No-Build missions, upgrades are unavailable as normal. + In all other missions, upgrades are automatically researched. + Always Auto: Upgrades are automatically researched in all missions.""" + display_name = "Generic Upgrade Research" + option_vanilla = 0 + option_auto_in_no_build = 1 + option_auto_in_build = 2 + option_always_auto = 3 + + +class GenericUpgradeItems(Choice): + """Determines how weapon and armor upgrades are split into items. All options produce 3 levels of each item. + Does nothing if upgrades are unlocked by completed mission counts. + + Individual Items: All weapon and armor upgrades are each an item, + resulting in 18 total upgrade items. + Bundle Weapon And Armor: All types of weapon upgrades are one item, + and all types of armor upgrades are one item, + resulting in 6 total items. + Bundle Unit Class: Weapon and armor upgrades are merged, + but Infantry, Vehicle, and Starship upgrades are bundled separately, + resulting in 9 total items. + Bundle All: All weapon and armor upgrades are one item, + resulting in 3 total items.""" + display_name = "Generic Upgrade Items" + option_individual_items = 0 + option_bundle_weapon_and_armor = 1 + option_bundle_unit_class = 2 + option_bundle_all = 3 + + +class NovaCovertOpsItems(Toggle): + """If turned on, the equipment upgrades from Nova Covert Ops may be present in the world.""" + display_name = "Nova Covert Ops Items" + default = Toggle.option_true + + +class BroodWarItems(Toggle): + """If turned on, returning items from StarCraft: Brood War may appear in the world.""" + display_name = "Brood War Items" + default = Toggle.option_true + + +class ExtendedItems(Toggle): + """If turned on, original items that did not appear in Campaign mode may appear in the world.""" + display_name = "Extended Items" + default = Toggle.option_true + + +class MaxNumberOfUpgrades(Range): + """ + Set a maximum to the number of upgrades a unit/structure can have. -1 is used to define unlimited. + Note that most unit have 4 or 6 upgrades. + + If used with Units Always Have Upgrades, each unit has this given amount of upgrades (if there enough upgrades exist) + + See also: Units Always Have Upgrades + """ + display_name = "Maximum number of upgrades per unit/structure" + range_start = -1 + # Do not know the maximum, but it is less than 123! + range_end = 123 + default = -1 + + class LockedItems(ItemSet): """Guarantees that these items will be unlockable""" display_name = "Locked Items" @@ -108,27 +242,114 @@ class ExcludedItems(ItemSet): class ExcludedMissions(OptionSet): """Guarantees that these missions will not appear in the campaign - Only applies on shortened mission orders. + Doesn't apply to vanilla mission order. It may be impossible to build a valid campaign if too many missions are excluded.""" display_name = "Excluded Missions" valid_keys = {mission_name for mission_name in vanilla_mission_req_table.keys() if mission_name != 'All-In'} +class LocationInclusion(Choice): + option_enabled = 0 + option_trash = 1 + option_nothing = 2 + + +class MissionProgressLocations(LocationInclusion): + """ + Enables or disables item rewards for progressing (not finishing) a mission. + Progressing a mission is usually a task of completing or progressing into a main objective. + Clearing an expansion base also counts here. + + Enabled: All locations fitting into this do their normal rewards + Trash: Forces a trash item in + Nothing: No rewards for this type of tasks, effectively disabling such locations + + Note: Individual locations subject to plando are always enabled, so the plando can be placed properly. + Warning: The generation may fail if too many locations are excluded by this way. + See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando) + """ + display_name = "Mission Progress Locations" + + +class BonusLocations(LocationInclusion): + """ + Enables or disables item rewards for completing bonus tasks. + Bonus tasks are those giving you a campaign-wide or mission-wide bonus in vanilla game: + Research, credits, bonus units or resources, etc. + + Enabled: All locations fitting into this do their normal rewards + Trash: Forces a trash item in + Nothing: No rewards for this type of tasks, effectively disabling such locations + + Note: Individual locations subject to plando are always enabled, so the plando can be placed properly. + Warning: The generation may fail if too many locations are excluded by this way. + See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando) + """ + display_name = "Bonus Locations" + + +class ChallengeLocations(LocationInclusion): + """ + Enables or disables item rewards for completing challenge tasks. + Challenges are tasks that have usually higher requirements to be completed + than to complete the mission they're in successfully. + You might be required to visit the same mission later when getting stronger in order to finish these tasks. + + Enabled: All locations fitting into this do their normal rewards + Trash: Forces a trash item in + Nothing: No rewards for this type of tasks, effectively disabling such locations + + Note: Individual locations subject to plando are always enabled, so the plando can be placed properly. + Warning: The generation may fail if too many locations are excluded by this way. + See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando) + """ + display_name = "Challenge Locations" + + +class OptionalBossLocations(LocationInclusion): + """ + Enables or disables item rewards for defeating optional bosses. + An optional boss is any boss that's not required to kill in order to finish the mission successfully. + All Brutalisks, Loki, etc. belongs here. + + Enabled: All locations fitting into this do their normal rewards + Trash: Forces a trash item in + Nothing: No rewards for this type of tasks, effectively disabling such locations + + Note: Individual locations subject to plando are always enabled, so the plando can be placed properly. + Warning: The generation may fail if too many locations are excluded by this way. + See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando) + """ + display_name = "Challenge Locations" + + # noinspection PyTypeChecker sc2wol_options: Dict[str, Option] = { "game_difficulty": GameDifficulty, - "upgrade_bonus": UpgradeBonus, - "bunker_upgrade": BunkerUpgrade, + "game_speed": GameSpeed, "all_in_map": AllInMap, + "final_map": FinalMap, "mission_order": MissionOrder, + "player_color": PlayerColor, "shuffle_protoss": ShuffleProtoss, "shuffle_no_build": ShuffleNoBuild, "early_unit": EarlyUnit, "required_tactics": RequiredTactics, "units_always_have_upgrades": UnitsAlwaysHaveUpgrades, + "max_number_of_upgrades": MaxNumberOfUpgrades, + "generic_upgrade_missions": GenericUpgradeMissions, + "generic_upgrade_research": GenericUpgradeResearch, + "generic_upgrade_items": GenericUpgradeItems, "locked_items": LockedItems, "excluded_items": ExcludedItems, - "excluded_missions": ExcludedMissions + "excluded_missions": ExcludedMissions, + "nco_items": NovaCovertOpsItems, + "bw_items": BroodWarItems, + "ext_items": ExtendedItems, + "mission_progress_locations": MissionProgressLocations, + "bonus_locations": BonusLocations, + "challenge_locations": ChallengeLocations, + "optional_boss_locations": OptionalBossLocations } diff --git a/worlds/sc2wol/PoolFilter.py b/worlds/sc2wol/PoolFilter.py index 16cc51f243..4a19e2dbb3 100644 --- a/worlds/sc2wol/PoolFilter.py +++ b/worlds/sc2wol/PoolFilter.py @@ -1,22 +1,22 @@ from typing import Callable, Dict, List, Set from BaseClasses import MultiWorld, ItemClassification, Item, Location -from .Items import item_table +from .Items import get_full_item_list, spider_mine_sources, second_pass_placeable_items, filler_items from .MissionTables import no_build_regions_list, easy_regions_list, medium_regions_list, hard_regions_list,\ mission_orders, MissionInfo, alt_final_mission_locations, MissionPools -from .Options import get_option_value +from .Options import get_option_value, MissionOrder, FinalMap, MissionProgressLocations, LocationInclusion from .LogicMixin import SC2WoLLogic # Items with associated upgrades UPGRADABLE_ITEMS = [ "Marine", "Medic", "Firebat", "Marauder", "Reaper", "Ghost", "Spectre", - "Hellion", "Vulture", "Goliath", "Diamondback", "Siege Tank", "Thor", - "Medivac", "Wraith", "Viking", "Banshee", "Battlecruiser", + "Hellion", "Vulture", "Goliath", "Diamondback", "Siege Tank", "Thor", "Predator", "Widow Mine", "Cyclone", + "Medivac", "Wraith", "Viking", "Banshee", "Battlecruiser", "Raven", "Science Vessel", "Liberator", "Valkyrie", "Bunker", "Missile Turret" ] BARRACKS_UNITS = {"Marine", "Medic", "Firebat", "Marauder", "Reaper", "Ghost", "Spectre"} -FACTORY_UNITS = {"Hellion", "Vulture", "Goliath", "Diamondback", "Siege Tank", "Thor", "Predator"} -STARPORT_UNITS = {"Medivac", "Wraith", "Viking", "Banshee", "Battlecruiser", "Hercules", "Science Vessel", "Raven"} +FACTORY_UNITS = {"Hellion", "Vulture", "Goliath", "Diamondback", "Siege Tank", "Thor", "Predator", "Widow Mine"} +STARPORT_UNITS = {"Medivac", "Wraith", "Viking", "Banshee", "Battlecruiser", "Hercules", "Science Vessel", "Raven", "Liberator", "Valkyrie"} PROTOSS_REGIONS = {"A Sinister Turn", "Echoes of the Future", "In Utter Darkness"} @@ -30,7 +30,7 @@ def filter_missions(multiworld: MultiWorld, player: int) -> Dict[int, List[str]] shuffle_no_build = get_option_value(multiworld, player, "shuffle_no_build") shuffle_protoss = get_option_value(multiworld, player, "shuffle_protoss") excluded_missions = get_option_value(multiworld, player, "excluded_missions") - mission_count = len(mission_orders[mission_order_type]) - 1 + final_map = get_option_value(multiworld, player, "final_map") mission_pools = { MissionPools.STARTER: no_build_regions_list[:], MissionPools.EASY: easy_regions_list[:], @@ -38,21 +38,18 @@ def filter_missions(multiworld: MultiWorld, player: int) -> Dict[int, List[str]] MissionPools.HARD: hard_regions_list[:], MissionPools.FINAL: [] } - if mission_order_type == 0: + if mission_order_type == MissionOrder.option_vanilla: # Vanilla uses the entire mission pool mission_pools[MissionPools.FINAL] = ['All-In'] return mission_pools - elif mission_order_type == 1: - # Vanilla Shuffled ignores the player-provided excluded missions - excluded_missions = set() # Omitting No-Build missions if not shuffling no-build if not shuffle_no_build: excluded_missions = excluded_missions.union(no_build_regions_list) # Omitting Protoss missions if not shuffling protoss if not shuffle_protoss: excluded_missions = excluded_missions.union(PROTOSS_REGIONS) - # Replacing All-In on low mission counts - if mission_count < 14: + # Replacing All-In with alternate ending depending on option + if final_map == FinalMap.option_random_hard: final_mission = multiworld.random.choice([mission for mission in alt_final_mission_locations.keys() if mission not in excluded_missions]) excluded_missions.add(final_mission) else: @@ -92,10 +89,18 @@ def get_item_upgrades(inventory: List[Item], parent_item: Item or str): item_name = parent_item.name if isinstance(parent_item, Item) else parent_item return [ inv_item for inv_item in inventory - if item_table[inv_item.name].parent_item == item_name + if get_full_item_list()[inv_item.name].parent_item == item_name ] +def get_item_quantity(item): + return get_full_item_list()[item.name].quantity + + +def copy_item(item: Item): + return Item(item.name, item.classification, item.code, item.player) + + class ValidInventory: def has(self, item: str, player: int): @@ -124,22 +129,6 @@ class ValidInventory: cascade_keys = self.cascade_removal_map.keys() units_always_have_upgrades = get_option_value(self.multiworld, self.player, "units_always_have_upgrades") - # Locking associated items for items that have already been placed when units_always_have_upgrades is on - if units_always_have_upgrades: - existing_items = self.existing_items[:] - while existing_items: - existing_item = existing_items.pop() - items_to_lock = self.cascade_removal_map.get(existing_item, [existing_item]) - for item in items_to_lock: - if item in inventory: - inventory.remove(item) - locked_items.append(item) - if item in existing_items: - existing_items.remove(item) - - if self.min_units_per_structure > 0 and self.has_units_per_structure(): - requirements.append(lambda state: state.has_units_per_structure()) - def attempt_removal(item: Item) -> bool: # If item can be removed and has associated items, remove them as well inventory.remove(item) @@ -149,9 +138,77 @@ class ValidInventory: if not all(requirement(self) for requirement in requirements): # If item cannot be removed, lock or revert self.logical_inventory.add(item.name) - locked_items.append(item) + for _ in range(get_item_quantity(item)): + locked_items.append(copy_item(item)) return False return True + + # Limit the maximum number of upgrades + maxUpgrad = get_option_value(self.multiworld, self.player, + "max_number_of_upgrades") + if maxUpgrad != -1: + unit_avail_upgrades = {} + # Needed to take into account locked/existing items + unit_nb_upgrades = {} + for item in inventory: + cItem = get_full_item_list()[item.name] + if cItem.type in UPGRADABLE_ITEMS and item.name not in unit_avail_upgrades: + unit_avail_upgrades[item.name] = [] + unit_nb_upgrades[item.name] = 0 + elif cItem.parent_item is not None: + if cItem.parent_item not in unit_avail_upgrades: + unit_avail_upgrades[cItem.parent_item] = [item] + unit_nb_upgrades[cItem.parent_item] = 1 + else: + unit_avail_upgrades[cItem.parent_item].append(item) + unit_nb_upgrades[cItem.parent_item] += 1 + # For those two categories, we count them but dont include them in removal + for item in locked_items + self.existing_items: + cItem = get_full_item_list()[item.name] + if cItem.type in UPGRADABLE_ITEMS and item.name not in unit_avail_upgrades: + unit_avail_upgrades[item.name] = [] + unit_nb_upgrades[item.name] = 0 + elif cItem.parent_item is not None: + if cItem.parent_item not in unit_avail_upgrades: + unit_nb_upgrades[cItem.parent_item] = 1 + else: + unit_nb_upgrades[cItem.parent_item] += 1 + # Making sure that the upgrades being removed is random + # Currently, only for combat shield vs Stabilizer Medpacks... + shuffled_unit_upgrade_list = list(unit_avail_upgrades.keys()) + self.multiworld.random.shuffle(shuffled_unit_upgrade_list) + for unit in shuffled_unit_upgrade_list: + while (unit_nb_upgrades[unit] > maxUpgrad) \ + and (len(unit_avail_upgrades[unit]) > 0): + itemCandidate = self.multiworld.random.choice(unit_avail_upgrades[unit]) + _ = attempt_removal(itemCandidate) + # Whatever it succeed to remove the iventory or it fails and thus + # lock it, the upgrade is no longer available for removal + unit_avail_upgrades[unit].remove(itemCandidate) + unit_nb_upgrades[unit] -= 1 + + # Locking associated items for items that have already been placed when units_always_have_upgrades is on + if units_always_have_upgrades: + existing_items = set(self.existing_items[:] + locked_items) + while existing_items: + existing_item = existing_items.pop() + items_to_lock = self.cascade_removal_map.get(existing_item, [existing_item]) + if get_full_item_list()[existing_item.name].type != "Upgrade": + # Don't process general upgrades, they may have been pre-locked per-level + for item in items_to_lock: + if item in inventory: + # Unit upgrades, lock all levels + for _ in range(inventory.count(item)): + inventory.remove(item) + if item not in locked_items: + # Lock all the associated items if not already locked + for _ in range(get_item_quantity(item)): + locked_items.append(copy_item(item)) + if item in existing_items: + existing_items.remove(item) + + if self.min_units_per_structure > 0 and self.has_units_per_structure(): + requirements.append(lambda state: state.has_units_per_structure()) # Determining if the full-size inventory can complete campaign if not all(requirement(self) for requirement in requirements): @@ -185,21 +242,47 @@ class ValidInventory: if cascade_failure: for transient_item in transient_items: if transient_item in inventory: - inventory.remove(transient_item) + for _ in range(inventory.count(transient_item)): + inventory.remove(transient_item) if transient_item not in locked_items: - locked_items.append(transient_item) + for _ in range(get_item_quantity(transient_item)): + locked_items.append(copy_item(transient_item)) if transient_item.classification in (ItemClassification.progression, ItemClassification.progression_skip_balancing): self.logical_inventory.add(transient_item.name) else: attempt_removal(item) - return inventory + locked_items + if not spider_mine_sources & self.logical_inventory: + inventory = [item for item in inventory if not item.name.endswith("(Spider Mine)")] + if not BARRACKS_UNITS & self.logical_inventory: + inventory = [item for item in inventory if + not (item.name.startswith("Progressive Infantry") or item.name == "Orbital Strike")] + if not FACTORY_UNITS & self.logical_inventory: + inventory = [item for item in inventory if not item.name.startswith("Progressive Vehicle")] + if not STARPORT_UNITS & self.logical_inventory: + inventory = [item for item in inventory if not item.name.startswith("Progressive Ship")] + + # Cull finished, adding locked items back into inventory + inventory += locked_items + + # Replacing empty space with generically useful items + replacement_items = [item for item in self.item_pool + if (item not in inventory + and item not in self.locked_items + and item.name in second_pass_placeable_items)] + self.multiworld.random.shuffle(replacement_items) + while len(inventory) < inventory_size and len(replacement_items) > 0: + item = replacement_items.pop() + inventory.append(item) + + return inventory def _read_logic(self): self._sc2wol_has_common_unit = lambda world, player: SC2WoLLogic._sc2wol_has_common_unit(self, world, player) self._sc2wol_has_air = lambda world, player: SC2WoLLogic._sc2wol_has_air(self, world, player) self._sc2wol_has_air_anti_air = lambda world, player: SC2WoLLogic._sc2wol_has_air_anti_air(self, world, player) self._sc2wol_has_competent_anti_air = lambda world, player: SC2WoLLogic._sc2wol_has_competent_anti_air(self, world, player) + self._sc2wol_has_competent_ground_to_air = lambda world, player: SC2WoLLogic._sc2wol_has_competent_ground_to_air(self, world, player) self._sc2wol_has_anti_air = lambda world, player: SC2WoLLogic._sc2wol_has_anti_air(self, world, player) self._sc2wol_defense_rating = lambda world, player, zerg_enemy, air_enemy=False: SC2WoLLogic._sc2wol_defense_rating(self, world, player, zerg_enemy, air_enemy) self._sc2wol_has_competent_comp = lambda world, player: SC2WoLLogic._sc2wol_has_competent_comp(self, world, player) @@ -210,6 +293,8 @@ class ValidInventory: self._sc2wol_has_protoss_common_units = lambda world, player: SC2WoLLogic._sc2wol_has_protoss_common_units(self, world, player) self._sc2wol_has_protoss_medium_units = lambda world, player: SC2WoLLogic._sc2wol_has_protoss_medium_units(self, world, player) self._sc2wol_has_mm_upgrade = lambda world, player: SC2WoLLogic._sc2wol_has_mm_upgrade(self, world, player) + self._sc2wol_welcome_to_the_jungle_requirement = lambda world, player: SC2WoLLogic._sc2wol_welcome_to_the_jungle_requirement(self, world, player) + self._sc2wol_can_respond_to_colony_infestations = lambda world, player: SC2WoLLogic._sc2wol_can_respond_to_colony_infestations(self, world, player) self._sc2wol_final_mission_requirements = lambda world, player: SC2WoLLogic._sc2wol_final_mission_requirements(self, world, player) def __init__(self, multiworld: MultiWorld, player: int, @@ -230,7 +315,7 @@ class ValidInventory: self.min_units_per_structure = int(mission_count / 7) min_upgrades = 1 if mission_count < 10 else 2 for item in item_pool: - item_info = item_table[item.name] + item_info = get_full_item_list()[item.name] if item_info.type == "Upgrade": # Locking upgrades based on mission duration if item.name not in item_quantities: diff --git a/worlds/sc2wol/Regions.py b/worlds/sc2wol/Regions.py index 033636662b..f588ce7e98 100644 --- a/worlds/sc2wol/Regions.py +++ b/worlds/sc2wol/Regions.py @@ -1,10 +1,14 @@ from typing import List, Set, Dict, Tuple, Optional, Callable from BaseClasses import MultiWorld, Region, Entrance, Location from .Locations import LocationData -from .Options import get_option_value -from .MissionTables import MissionInfo, mission_orders, vanilla_mission_req_table, alt_final_mission_locations, MissionPools +from .Options import get_option_value, MissionOrder +from .MissionTables import MissionInfo, mission_orders, vanilla_mission_req_table, alt_final_mission_locations, \ + MissionPools, vanilla_shuffle_order from .PoolFilter import filter_missions +PROPHECY_CHAIN_MISSION_COUNT = 4 + +VANILLA_SHUFFLED_FIRST_PROPHECY_MISSION = 21 def create_regions(multiworld: MultiWorld, player: int, locations: Tuple[LocationData, ...], location_cache: List[Location])\ -> Tuple[Dict[str, MissionInfo], int, str]: @@ -19,7 +23,7 @@ def create_regions(multiworld: MultiWorld, player: int, locations: Tuple[Locatio names: Dict[str, int] = {} - if mission_order_type == 0: + if mission_order_type == MissionOrder.option_vanilla: # Generating all regions and locations for region_name in vanilla_mission_req_table.keys(): @@ -108,12 +112,17 @@ def create_regions(multiworld: MultiWorld, player: int, locations: Tuple[Locatio removals = len(mission_order) - mission_pool_size # Removing entire Prophecy chain on vanilla shuffled when not shuffling protoss if remove_prophecy: - removals -= 4 + removals -= PROPHECY_CHAIN_MISSION_COUNT # Initial fill out of mission list and marking all-in mission for mission in mission_order: # Removing extra missions if mission pool is too small - if 0 < mission.removal_priority <= removals or mission.category == 'Prophecy' and remove_prophecy: + # Also handle lower removal priority than Prophecy + if 0 < mission.removal_priority <= removals or mission.category == 'Prophecy' and remove_prophecy \ + or (remove_prophecy and mission_order_type == MissionOrder.option_vanilla_shuffled + and mission.removal_priority > vanilla_shuffle_order[ + VANILLA_SHUFFLED_FIRST_PROPHECY_MISSION].removal_priority + and 0 < mission.removal_priority <= removals + PROPHECY_CHAIN_MISSION_COUNT): missions.append(None) elif mission.type == MissionPools.FINAL: missions.append(final_mission) @@ -191,22 +200,38 @@ def create_regions(multiworld: MultiWorld, player: int, locations: Tuple[Locatio # TODO: Handle 'and' connections mission_req_table = {} + def build_connection_rule(mission_names: List[str], missions_req: int) -> Callable: + if len(mission_names) > 1: + return lambda state: state.has_all({f"Beat {name}" for name in mission_names}, player) and \ + state._sc2wol_cleared_missions(multiworld, player, missions_req) + else: + return lambda state: state.has(f"Beat {mission_names[0]}", player) and \ + state._sc2wol_cleared_missions(multiworld, player, missions_req) + for i, mission in enumerate(missions): if mission is None: continue connections = [] + all_connections = [] + for connection in mission_order[i].connect_to: + if connection == -1: + continue + while missions[connection] is None: + connection -= 1 + all_connections.append(missions[connection]) for connection in mission_order[i].connect_to: required_mission = missions[connection] if connection == -1: connect(multiworld, player, names, "Menu", mission) - elif required_mission is None: - continue else: + if required_mission is None and not mission_order[i].completion_critical: # Drop non-critical null slots + continue + while required_mission is None: # Substituting null slot with prior slot + connection -= 1 + required_mission = missions[connection] + required_missions = [required_mission] if mission_order[i].or_requirements else all_connections connect(multiworld, player, names, required_mission, mission, - (lambda name, missions_req: (lambda state: state.has(f"Beat {name}", player) and - state._sc2wol_cleared_missions(multiworld, player, - missions_req))) - (missions[connection], mission_order[i].number)) + build_connection_rule(required_missions, mission_order[i].number)) connections.append(slot_map[connection]) mission_req_table.update({mission: MissionInfo( diff --git a/worlds/sc2wol/__init__.py b/worlds/sc2wol/__init__.py index 490524290c..93aebb7ad1 100644 --- a/worlds/sc2wol/__init__.py +++ b/worlds/sc2wol/__init__.py @@ -3,11 +3,11 @@ import typing from typing import List, Set, Tuple, Dict from BaseClasses import Item, MultiWorld, Location, Tutorial, ItemClassification from worlds.AutoWorld import WebWorld, World -from .Items import StarcraftWoLItem, item_table, filler_items, item_name_groups, get_full_item_list, \ - get_basic_units -from .Locations import get_locations +from .Items import StarcraftWoLItem, filler_items, item_name_groups, get_item_table, get_full_item_list, \ + get_basic_units, ItemData, upgrade_included_names, progressive_if_nco +from .Locations import get_locations, LocationType from .Regions import create_regions -from .Options import sc2wol_options, get_option_value +from .Options import sc2wol_options, get_option_value, LocationInclusion from .LogicMixin import SC2WoLLogic from .PoolFilter import filter_missions, filter_items, get_item_upgrades from .MissionTables import starting_mission_locations, MissionInfo @@ -36,7 +36,7 @@ class SC2WoLWorld(World): web = Starcraft2WoLWebWorld() data_version = 4 - item_name_to_id = {name: data.code for name, data in item_table.items()} + item_name_to_id = {name: data.code for name, data in get_full_item_list().items()} location_name_to_id = {location.name: location.code for location in get_locations(None, None)} option_definitions = sc2wol_options @@ -69,6 +69,8 @@ class SC2WoLWorld(World): starter_items = assign_starter_items(self.multiworld, self.player, excluded_items, self.locked_locations) + filter_locations(self.multiworld, self.player, self.locked_locations, self.location_cache) + pool = get_item_pool(self.multiworld, self.player, self.mission_req_table, starter_items, excluded_items, self.location_cache) fill_item_pool_with_dummy_items(self, self.multiworld, self.player, self.locked_locations, self.location_cache, pool) @@ -109,16 +111,6 @@ def setup_events(player: int, locked_locations: typing.List[str], location_cache def get_excluded_items(multiworld: MultiWorld, player: int) -> Set[str]: excluded_items: Set[str] = set() - if get_option_value(multiworld, player, "upgrade_bonus") == 1: - excluded_items.add("Ultra-Capacitors") - else: - excluded_items.add("Vanadium Plating") - - if get_option_value(multiworld, player, "bunker_upgrade") == 1: - excluded_items.add("Shrike Turret") - else: - excluded_items.add("Fortified Bunker") - for item in multiworld.precollected_items[player]: excluded_items.add(item.name) @@ -167,7 +159,7 @@ def assign_starter_item(multiworld: MultiWorld, player: int, excluded_items: Set def get_item_pool(multiworld: MultiWorld, player: int, mission_req_table: Dict[str, MissionInfo], - starter_items: List[str], excluded_items: Set[str], location_cache: List[Location]) -> List[Item]: + starter_items: List[Item], excluded_items: Set[str], location_cache: List[Location]) -> List[Item]: pool: List[Item] = [] # For the future: goal items like Artifact Shards go here @@ -176,17 +168,43 @@ def get_item_pool(multiworld: MultiWorld, player: int, mission_req_table: Dict[s # YAML items yaml_locked_items = get_option_value(multiworld, player, 'locked_items') - for name, data in item_table.items(): - if name not in excluded_items: - for _ in range(data.quantity): - item = create_item_with_correct_settings(player, name) - if name in yaml_locked_items: - locked_items.append(item) - else: - pool.append(item) + # Adjust generic upgrade availability based on options + include_upgrades = get_option_value(multiworld, player, 'generic_upgrade_missions') == 0 + upgrade_items = get_option_value(multiworld, player, 'generic_upgrade_items') + + # Include items from outside Wings of Liberty + item_sets = {'wol'} + if get_option_value(multiworld, player, 'nco_items'): + item_sets.add('nco') + if get_option_value(multiworld, player, 'bw_items'): + item_sets.add('bw') + if get_option_value(multiworld, player, 'ext_items'): + item_sets.add('ext') + + def allowed_quantity(name: str, data: ItemData) -> int: + if name in excluded_items \ + or data.type == "Upgrade" and (not include_upgrades or name not in upgrade_included_names[upgrade_items]) \ + or not data.origin.intersection(item_sets): + return 0 + elif name in progressive_if_nco and 'nco' not in item_sets: + return 1 + else: + return data.quantity + + for name, data in get_item_table(multiworld, player).items(): + for i in range(allowed_quantity(name, data)): + item = create_item_with_correct_settings(player, name) + if name in yaml_locked_items: + locked_items.append(item) + else: + pool.append(item) existing_items = starter_items + [item for item in multiworld.precollected_items[player]] existing_names = [item.name for item in existing_items] + + # Check the parent item integrity, exclude items + pool[:] = [item for item in pool if pool_contains_parent(item, pool + locked_items + existing_items)] + # Removing upgrades for excluded items for item_name in excluded_items: if item_name in existing_names: @@ -207,8 +225,100 @@ def fill_item_pool_with_dummy_items(self: SC2WoLWorld, multiworld: MultiWorld, p def create_item_with_correct_settings(player: int, name: str) -> Item: - data = item_table[name] + data = get_full_item_list()[name] item = Item(name, data.classification, data.code, player) return item + + +def pool_contains_parent(item: Item, pool: [Item]): + item_data = get_full_item_list().get(item.name) + if item_data.parent_item is None: + # The item has not associated parent, the item is valid + return True + parent_item = item_data.parent_item + # Check if the pool contains the parent item + return parent_item in [pool_item.name for pool_item in pool] + + +def filter_locations(multiworld: MultiWorld, player, locked_locations: List[str], location_cache: List[Location]): + """ + Filters the locations in the world using a trash or Nothing item + :param multiworld: + :param player: + :param locked_locations: + :param location_cache: + :return: + """ + open_locations = [location for location in location_cache if location.item is None] + plando_locations = get_plando_locations(multiworld, player) + mission_progress_locations = get_option_value(multiworld, player, "mission_progress_locations") + bonus_locations = get_option_value(multiworld, player, "bonus_locations") + challenge_locations = get_option_value(multiworld, player, "challenge_locations") + optional_boss_locations = get_option_value(multiworld, player, "optional_boss_locations") + location_data = get_locations(multiworld, player) + for location in open_locations: + # Go through the locations that aren't locked yet (early unit, etc) + if location.name not in plando_locations: + # The location is not plando'd + sc2_location = [sc2_location for sc2_location in location_data if sc2_location.name == location.name][0] + location_type = sc2_location.type + + if location_type == LocationType.MISSION_PROGRESS \ + and mission_progress_locations != LocationInclusion.option_enabled: + item_name = get_exclusion_item(multiworld, mission_progress_locations) + place_exclusion_item(item_name, location, locked_locations, player) + + if location_type == LocationType.BONUS \ + and bonus_locations != LocationInclusion.option_enabled: + item_name = get_exclusion_item(multiworld, bonus_locations) + place_exclusion_item(item_name, location, locked_locations, player) + + if location_type == LocationType.CHALLENGE \ + and challenge_locations != LocationInclusion.option_enabled: + item_name = get_exclusion_item(multiworld, challenge_locations) + place_exclusion_item(item_name, location, locked_locations, player) + + if location_type == LocationType.OPTIONAL_BOSS \ + and optional_boss_locations != LocationInclusion.option_enabled: + item_name = get_exclusion_item(multiworld, optional_boss_locations) + place_exclusion_item(item_name, location, locked_locations, player) + + +def place_exclusion_item(item_name, location, locked_locations, player): + item = create_item_with_correct_settings(player, item_name) + location.place_locked_item(item) + locked_locations.append(location.name) + + +def get_exclusion_item(multiworld: MultiWorld, option) -> str: + """ + Gets the exclusion item according to settings (trash/nothing) + :param multiworld: + :param option: + :return: Item used for location exclusion + """ + if option == LocationInclusion.option_nothing: + return "Nothing" + elif option == LocationInclusion.option_trash: + index = multiworld.random.randint(0, len(filler_items) - 1) + return filler_items[index] + raise Exception(f"Unsupported option type: {option}") + + +def get_plando_locations(multiworld: MultiWorld, player) -> List[str]: + """ + + :param multiworld: + :param player: + :return: A list of locations affected by a plando in a world + """ + plando_locations = [] + for plando_setting in multiworld.plando_items[player]: + plando_locations += plando_setting.get("locations", []) + plando_setting_location = plando_setting.get("location", None) + if plando_setting_location is not None: + plando_locations.append(plando_setting_location) + + return plando_locations From f05a9ecd2faec72961c7e9c0751454c210ecd8c0 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Fri, 15 Sep 2023 09:18:14 +0200 Subject: [PATCH 026/144] settings: add default=None to Group.get (#2178) This is regular dict behavior/emulation. --- settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.py b/settings.py index 4c9c1d1c30..a7dcbbf8dd 100644 --- a/settings.py +++ b/settings.py @@ -118,7 +118,7 @@ class Group: cls._type_cache = typing.get_type_hints(cls, globalns=mod_dict, localns=cls.__dict__) return cls._type_cache - def get(self, key: str, default: Any) -> Any: + def get(self, key: str, default: Any = None) -> Any: if key in self: return self[key] return default From 6d61eae52239a07d584e127123f94e24924aaa68 Mon Sep 17 00:00:00 2001 From: Sunny Bat Date: Fri, 15 Sep 2023 00:30:46 -0700 Subject: [PATCH 027/144] Raft: Fix test_collect_remove (#2109) --- worlds/raft/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/worlds/raft/__init__.py b/worlds/raft/__init__.py index d00b5faa9e..fec60c3bd5 100644 --- a/worlds/raft/__init__.py +++ b/worlds/raft/__init__.py @@ -138,6 +138,8 @@ class RaftWorld(World): return RaftItem(rpName, ItemClassification.filler, self.item_name_to_id[rpName], player=self.player) def collect_item(self, state, item, remove=False): + if item.advancement is False: + return None if item.name in progressive_item_list: prog_table = progressive_item_list[item.name] if remove: From 44f1a93d319c94cdf5882202bb004cd723a1e6ce Mon Sep 17 00:00:00 2001 From: Ziktofel Date: Fri, 15 Sep 2023 14:05:35 +0200 Subject: [PATCH 028/144] Docs: Update SC2 map/mod link --- worlds/sc2wol/docs/setup_en.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/sc2wol/docs/setup_en.md b/worlds/sc2wol/docs/setup_en.md index 419f98a733..9bfeb3d235 100644 --- a/worlds/sc2wol/docs/setup_en.md +++ b/worlds/sc2wol/docs/setup_en.md @@ -7,7 +7,7 @@ to obtain a config file for StarCraft 2. - [StarCraft 2](https://starcraft2.com/en-us/) - [The most recent Archipelago release](https://github.com/ArchipelagoMW/Archipelago/releases) -- [StarCraft 2 AP Maps and Data](https://github.com/TheCondor07/Starcraft2ArchipelagoData) +- [StarCraft 2 AP Maps and Data](https://github.com/Ziktofel/Archipelago-SC2-data/releases) ## How do I install this randomizer? From f9120c620fefb18ca273313d4ea1adc510944ced Mon Sep 17 00:00:00 2001 From: Altiami Date: Fri, 15 Sep 2023 11:18:03 -0700 Subject: [PATCH 029/144] The Legend of Zelda Conntector: Make items obtained counter in save data 16 bits. (#2117) Certain multiworld settings (bug observed with item link settings) can cause the total item count in TLoZ world to exceed 255. This causes an overflow in the loop to receive all pending items. This adds an additional byte to be used as a high byte for the items obtained counter. This approach was taken due to the surrounding bytes being occupied, preventing a direct 16-bit number from being used without moving to a different location and leaving more empty bytes in the memory block for save data. --- data/lua/connector_tloz.lua | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/data/lua/connector_tloz.lua b/data/lua/connector_tloz.lua index f48e4dfac1..4a2d2f25bf 100644 --- a/data/lua/connector_tloz.lua +++ b/data/lua/connector_tloz.lua @@ -67,6 +67,7 @@ local itemsObtained = 0x0677 local takeAnyCavesChecked = 0x0678 local localTriforce = 0x0679 local bonusItemsObtained = 0x067A +local itemsObtainedHigh = 0x067B itemAPids = { ["Boomerang"] = 7100, @@ -173,11 +174,18 @@ for key, value in pairs(itemAPids) do itemIDNames[value] = key end +local function getItemsObtained() + return bit.bor(bit.lshift(u8(itemsObtainedHigh), 8), u8(itemsObtained)) +end +local function setItemsObtained(value) + wU8(itemsObtainedHigh, bit.rshift(value, 8)) + wU8(itemsObtained, bit.band(value, 0xFF)) +end local function determineItem(array) memdomain.ram() - currentItemsObtained = u8(itemsObtained) + currentItemsObtained = getItemsObtained() end @@ -364,8 +372,8 @@ local function gotItem(item) wU8(0x505, itemCode) wU8(0x506, 128) wU8(0x602, 4) - numberObtained = u8(itemsObtained) + 1 - wU8(itemsObtained, numberObtained) + numberObtained = getItemsObtained() + 1 + setItemsObtained(numberObtained) if itemName == "Boomerang" then gotBoomerang() end if itemName == "Bow" then gotBow() end if itemName == "Magical Boomerang" then gotMagicalBoomerang() end @@ -476,7 +484,7 @@ function processBlock(block) if i > u8(bonusItemsObtained) then if u8(0x505) == 0 then gotItem(item) - wU8(itemsObtained, u8(itemsObtained) - 1) + setItemsObtained(getItemsObtained() - 1) wU8(bonusItemsObtained, u8(bonusItemsObtained) + 1) end end @@ -494,7 +502,7 @@ function processBlock(block) for i, item in ipairs(itemsBlock) do memDomain.ram() if u8(0x505) == 0 then - if i > u8(itemsObtained) then + if i > getItemsObtained() then gotItem(item) end end @@ -546,7 +554,7 @@ function receive() retTable["gameMode"] = gameMode retTable["overworldHC"] = getHCLocation() retTable["overworldPB"] = getPBLocation() - retTable["itemsObtained"] = u8(itemsObtained) + retTable["itemsObtained"] = getItemsObtained() msg = json.encode(retTable).."\n" local ret, error = zeldaSocket:send(msg) if ret == nil then @@ -606,4 +614,4 @@ function main() end end -main() \ No newline at end of file +main() From cff6c7c4da00a0e6d726afb75bac3854e9734542 Mon Sep 17 00:00:00 2001 From: Bryce Wilson Date: Sat, 16 Sep 2023 03:17:40 -0700 Subject: [PATCH 030/144] DS3: Fix health locations setting not enabling (#2147) * DS3: Fix health locations setting not enabling * DS3: Move health locations to their own table * DS3: Bump data version --- worlds/dark_souls_3/Locations.py | 14 ++++++++------ worlds/dark_souls_3/__init__.py | 7 +++++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/worlds/dark_souls_3/Locations.py b/worlds/dark_souls_3/Locations.py index 4e595ad36a..df241a5fd1 100644 --- a/worlds/dark_souls_3/Locations.py +++ b/worlds/dark_souls_3/Locations.py @@ -77,6 +77,7 @@ class DarkSouls3Location(Location): "Progressive Items 3", "Progressive Items 4", "Progressive Items DLC", + "Progressive Items Health", ] output = {} @@ -581,11 +582,7 @@ location_tables = { [DS3LocationData(f"Titanite Shard #{i + 1}", "Titanite Shard", DS3LocationCategory.PROGRESSIVE_ITEM) for i in range(26)] + [DS3LocationData(f"Large Titanite Shard #{i + 1}", "Large Titanite Shard", DS3LocationCategory.PROGRESSIVE_ITEM) for i in range(28)] + [DS3LocationData(f"Titanite Slab #{i + 1}", "Titanite Slab", DS3LocationCategory.PROGRESSIVE_ITEM) for i in range(3)] + - [DS3LocationData(f"Twinkling Titanite #{i + 1}", "Twinkling Titanite", DS3LocationCategory.PROGRESSIVE_ITEM) for i in range(15)] + - - # Healing - [DS3LocationData(f"Estus Shard #{i + 1}", "Estus Shard", DS3LocationCategory.HEALTH) for i in range(11)] + - [DS3LocationData(f"Undead Bone Shard #{i + 1}", "Undead Bone Shard", DS3LocationCategory.HEALTH) for i in range(10)], + [DS3LocationData(f"Twinkling Titanite #{i + 1}", "Twinkling Titanite", DS3LocationCategory.PROGRESSIVE_ITEM) for i in range(15)], "Progressive Items 2": [] + # Items @@ -683,7 +680,12 @@ location_tables = { [DS3LocationData(f"Dark Gem ${i + 1}", "Dark Gem", DS3LocationCategory.PROGRESSIVE_ITEM) for i in range(2)] + [DS3LocationData(f"Blood Gem ${i + 1}", "Blood Gem", DS3LocationCategory.PROGRESSIVE_ITEM) for i in range(1)] + [DS3LocationData(f"Blessed Gem ${i + 1}", "Blessed Gem", DS3LocationCategory.PROGRESSIVE_ITEM) for i in range(2)] + - [DS3LocationData(f"Hollow Gem ${i + 1}", "Hollow Gem", DS3LocationCategory.PROGRESSIVE_ITEM) for i in range(2)] + [DS3LocationData(f"Hollow Gem ${i + 1}", "Hollow Gem", DS3LocationCategory.PROGRESSIVE_ITEM) for i in range(2)], + + "Progressive Items Health": [] + + # Healing + [DS3LocationData(f"Estus Shard #{i + 1}", "Estus Shard", DS3LocationCategory.HEALTH) for i in range(11)] + + [DS3LocationData(f"Undead Bone Shard #{i + 1}", "Undead Bone Shard", DS3LocationCategory.HEALTH) for i in range(10)], } location_dictionary: Dict[str, DS3LocationData] = {} diff --git a/worlds/dark_souls_3/__init__.py b/worlds/dark_souls_3/__init__.py index 5d845e3ccc..b78ff0548a 100644 --- a/worlds/dark_souls_3/__init__.py +++ b/worlds/dark_souls_3/__init__.py @@ -46,7 +46,7 @@ class DarkSouls3World(World): option_definitions = dark_souls_options topology_present: bool = True web = DarkSouls3Web() - data_version = 7 + data_version = 8 base_id = 100000 enabled_location_categories: Set[DS3LocationCategory] required_client_version = (0, 4, 2) @@ -89,7 +89,7 @@ class DarkSouls3World(World): def create_regions(self): progressive_location_table = [] - if self.multiworld.enable_progressive_locations[self.player].value: + if self.multiworld.enable_progressive_locations[self.player]: progressive_location_table = [] + \ location_tables["Progressive Items 1"] + \ location_tables["Progressive Items 2"] + \ @@ -99,6 +99,9 @@ class DarkSouls3World(World): if self.multiworld.enable_dlc[self.player].value: progressive_location_table += location_tables["Progressive Items DLC"] + if self.multiworld.enable_health_upgrade_locations[self.player]: + progressive_location_table += location_tables["Progressive Items Health"] + # Create Vanilla Regions regions: Dict[str, Region] = {} regions["Menu"] = self.create_region("Menu", progressive_location_table) From ce8f07b347fb2a8f03de44039ef1e4df8e69ea06 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sat, 16 Sep 2023 03:32:05 +0200 Subject: [PATCH 031/144] Core: fix start_inventory_from_pool only adding one filler per item name --- Main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Main.py b/Main.py index 860be6347c..ab3a8a6668 100644 --- a/Main.py +++ b/Main.py @@ -159,7 +159,8 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No for player, items in depletion_pool.items(): player_world: AutoWorld.World = world.worlds[player] for count in items.values(): - new_items.append(player_world.create_filler()) + for _ in range(count): + new_items.append(player_world.create_filler()) target: int = sum(sum(items.values()) for items in depletion_pool.values()) for i, item in enumerate(world.itempool): if depletion_pool[item.player].get(item.name, 0): @@ -179,6 +180,7 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No if remaining_items: raise Exception(f"{world.get_player_name(player)}" f" is trying to remove items from their pool that don't exist: {remaining_items}") + assert len(world.itempool) == len(new_items), "Item Pool amounts should not change." world.itempool[:] = new_items # temporary home for item links, should be moved out of Main From 9312f14ffba59ee9c0a10faaff4d19da003ef8f1 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Fri, 15 Sep 2023 19:28:55 +0200 Subject: [PATCH 032/144] Subnautica: add extra Laser Cutter Fragment to priority filler --- worlds/subnautica/__init__.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/worlds/subnautica/__init__.py b/worlds/subnautica/__init__.py index 1684260cc1..2d4cf2faf6 100644 --- a/worlds/subnautica/__init__.py +++ b/worlds/subnautica/__init__.py @@ -116,15 +116,16 @@ class SubnauticaWorld(World): # list of high-count important fragments as priority filler [ "Cyclops Engine Fragment", - "Modification Station Fragment", - "Mobile Vehicle Bay Fragment", - "Seamoth Fragment", "Cyclops Hull Fragment", "Cyclops Bridge Fragment", + "Seamoth Fragment", "Prawn Suit Fragment", + "Mobile Vehicle Bay Fragment", + "Modification Station Fragment", "Moonpool Fragment", + "Laser Cutter Fragment", ], - k=min(extras, 8)): + k=min(extras, 9)): item = self.create_item(item_name) pool.append(item) extras -= 1 From 0e7c7bd1bf4e6061096be839ed87e90a89a1e618 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sat, 16 Sep 2023 19:23:22 +0200 Subject: [PATCH 033/144] Core: update versions (#2186) --- Utils.py | 2 +- WebHostLib/requirements.txt | 7 ++++--- requirements.txt | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Utils.py b/Utils.py index 159c6cdcb1..12517173e5 100644 --- a/Utils.py +++ b/Utils.py @@ -44,7 +44,7 @@ class Version(typing.NamedTuple): return ".".join(str(item) for item in self) -__version__ = "0.4.2" +__version__ = "0.4.3" version_tuple = tuplize_version(__version__) is_linux = sys.platform.startswith("linux") diff --git a/WebHostLib/requirements.txt b/WebHostLib/requirements.txt index a8b2865aae..a3695e3383 100644 --- a/WebHostLib/requirements.txt +++ b/WebHostLib/requirements.txt @@ -3,7 +3,8 @@ pony>=0.7.16; python_version <= '3.10' pony @ https://github.com/Berserker66/pony/releases/download/v0.7.16/pony-0.7.16-py3-none-any.whl#0.7.16 ; python_version >= '3.11' waitress>=2.1.2 Flask-Caching>=2.0.2 -Flask-Compress>=1.13 -Flask-Limiter>=3.3.0 -bokeh>=3.1.1 +Flask-Compress>=1.14 +Flask-Limiter>=3.5.0 +bokeh>=3.1.1; python_version <= '3.8' +bokeh>=3.2.2; python_version >= '3.9' markupsafe>=2.1.3 diff --git a/requirements.txt b/requirements.txt index 610892848d..abb572ad95 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,4 +9,4 @@ bsdiff4>=1.2.3 platformdirs>=3.9.1 certifi>=2023.7.22 cython>=0.29.35 -cymem>=2.0.7 +cymem>=2.0.8 From 6d6de4a98e17402213454162fae0e8bf55ad97e2 Mon Sep 17 00:00:00 2001 From: Ziktofel Date: Sat, 16 Sep 2023 21:50:50 +0200 Subject: [PATCH 034/144] SC2: Typo fix in option display name --- worlds/sc2wol/Options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/sc2wol/Options.py b/worlds/sc2wol/Options.py index 0702e431a4..13b01c42a2 100644 --- a/worlds/sc2wol/Options.py +++ b/worlds/sc2wol/Options.py @@ -320,7 +320,7 @@ class OptionalBossLocations(LocationInclusion): Warning: The generation may fail if too many locations are excluded by this way. See also: Excluded Locations, Item Plando (https://archipelago.gg/tutorial/Archipelago/plando/en#item-plando) """ - display_name = "Challenge Locations" + display_name = "Optional Boss Locations" # noinspection PyTypeChecker From b24037e9d99ed610cbaa33338f9f76d671c8abc9 Mon Sep 17 00:00:00 2001 From: Doug Hoskisson Date: Sun, 17 Sep 2023 08:55:21 -0700 Subject: [PATCH 035/144] Zillion: ensure 1st sphere not empty (#2190) --- worlds/zillion/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/zillion/requirements.txt b/worlds/zillion/requirements.txt index 626579ab11..2af057dece 100644 --- a/worlds/zillion/requirements.txt +++ b/worlds/zillion/requirements.txt @@ -1 +1 @@ -zilliandomizer @ git+https://github.com/beauxq/zilliandomizer@cd6a940ad7b585c75a560b91468d6b9eee030559#0.5.2 +zilliandomizer @ git+https://github.com/beauxq/zilliandomizer@4b27d115269db25fe73b0471b73495f41df1323c#0.5.3 From d5d13a6d4da126ef9c1c9b0237c2d7fe20110415 Mon Sep 17 00:00:00 2001 From: agilbert1412 Date: Sun, 17 Sep 2023 14:20:18 -0400 Subject: [PATCH 036/144] Stardew Valley: Fix two logic bugs with the wizard on Entrance Randomizer (#2192) * - Added a rule to vault bundles that require access to the wizard - Fixed the region required to meet the wizard * - Updated the location count in a test due to a previous coffee bean bugfix that added a location --- worlds/stardew_valley/data/villagers_data.py | 3 ++- worlds/stardew_valley/logic.py | 5 +++-- worlds/stardew_valley/test/TestGeneration.py | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/worlds/stardew_valley/data/villagers_data.py b/worlds/stardew_valley/data/villagers_data.py index 330d5eb955..e858d46f34 100644 --- a/worlds/stardew_valley/data/villagers_data.py +++ b/worlds/stardew_valley/data/villagers_data.py @@ -39,6 +39,7 @@ oasis = (Region.oasis,) sewers = (Region.sewer,) island = (Region.island_east,) secret_woods = (Region.secret_woods,) +wizard_tower = (Region.wizard_tower,) golden_pumpkin = ("Golden Pumpkin",) # magic_rock_candy = ("Magic Rock Candy",) @@ -314,7 +315,7 @@ milf = villager(NPC.robin, False, carpenter, Season.fall, universal_loves + robi sandy = villager(NPC.sandy, False, oasis, Season.fall, universal_loves + sandy_loves, False) vincent = villager(NPC.vincent, False, town, Season.spring, universal_loves + vincent_loves, True) willy = villager(NPC.willy, False, beach, Season.summer, universal_loves + willy_loves, True) -wizard = villager(NPC.wizard, False, forest, Season.winter, universal_loves + wizard_loves, True) +wizard = villager(NPC.wizard, False, wizard_tower, Season.winter, universal_loves + wizard_loves, True) # Custom NPCs alec = villager(ModNPC.alec, True, forest, Season.winter, universal_loves + trilobite, True, ModNames.alec) diff --git a/worlds/stardew_valley/logic.py b/worlds/stardew_valley/logic.py index 00b60696a9..377fa0d03b 100644 --- a/worlds/stardew_valley/logic.py +++ b/worlds/stardew_valley/logic.py @@ -1166,14 +1166,15 @@ class StardewLogic: def can_complete_bundle(self, bundle_requirements: List[BundleItem], number_required: int) -> StardewRule: item_rules = [] highest_quality_yet = 0 + can_speak_junimo = self.can_reach_region(Region.wizard_tower) for bundle_item in bundle_requirements: if bundle_item.item.item_id == -1: - return self.can_spend_money(bundle_item.amount) + return can_speak_junimo & self.can_spend_money(bundle_item.amount) else: item_rules.append(bundle_item.item.name) if bundle_item.quality > highest_quality_yet: highest_quality_yet = bundle_item.quality - return self.can_reach_region(Region.wizard_tower) & self.has(item_rules, number_required) & self.can_grow_gold_quality(highest_quality_yet) + return can_speak_junimo & self.has(item_rules, number_required) & self.can_grow_gold_quality(highest_quality_yet) def can_grow_gold_quality(self, quality: int) -> StardewRule: if quality <= 0: diff --git a/worlds/stardew_valley/test/TestGeneration.py b/worlds/stardew_valley/test/TestGeneration.py index a80f334d45..0142ad0079 100644 --- a/worlds/stardew_valley/test/TestGeneration.py +++ b/worlds/stardew_valley/test/TestGeneration.py @@ -214,7 +214,7 @@ class TestLocationAndItemCount(SVTestBase): self.assertGreaterEqual(len(valid_locations), len(multiworld.itempool)) def test_allsanity_without_mods_has_at_least_locations(self): - expected_locations = 993 + expected_locations = 994 allsanity_options = self.allsanity_options_without_mods() multiworld = setup_solo_multiworld(allsanity_options) number_locations = len(get_real_locations(self, multiworld)) @@ -227,7 +227,7 @@ class TestLocationAndItemCount(SVTestBase): f"\n\t\tActual: {number_locations}") def test_allsanity_with_mods_has_at_least_locations(self): - expected_locations = 1245 + expected_locations = 1246 allsanity_options = self.allsanity_options_with_mods() multiworld = setup_solo_multiworld(allsanity_options) number_locations = len(get_real_locations(self, multiworld)) From fa2891f7857f3d1be53284e380695759742ef214 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 17 Sep 2023 22:47:06 +0200 Subject: [PATCH 037/144] Factorio: offer error message with some more insight for lock file in use (#2187) --- worlds/factorio/Client.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/worlds/factorio/Client.py b/worlds/factorio/Client.py index 58dbb6df83..050455bb07 100644 --- a/worlds/factorio/Client.py +++ b/worlds/factorio/Client.py @@ -446,6 +446,10 @@ async def factorio_spinup_server(ctx: FactorioContext) -> bool: logger.warning("It appears your mods are loaded from Appdata, " "this can lead to problems with multiple Factorio instances. " "If this is the case, you will get a file locked error running Factorio.") + elif "Couldn't create lock file" in msg: + raise Exception(f"This Factorio (at {executable}) is either already running, " + "or a Factorio sharing data directories is already running. " + "Server could not start up.") if not rcon_client and "Starting RCON interface at IP ADDR:" in msg: rcon_client = factorio_rcon.RCONClient("localhost", rcon_port, rcon_password) if ctx.mod_version == ctx.__class__.mod_version: From 2ef05a1799b478682c5dbc4006ca4f03970cbd83 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 17 Sep 2023 22:56:59 +0200 Subject: [PATCH 038/144] kvui: remove custom DPI scaling on windows (#2177) --- kvui.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kvui.py b/kvui.py index 77b96b896f..835f0dad45 100644 --- a/kvui.py +++ b/kvui.py @@ -1,7 +1,14 @@ import os import logging +import sys import typing +if sys.platform == "win32": + import ctypes + # kivy 2.2.0 introduced DPI awareness on Windows, but it makes the UI enter an infinitely recursive re-layout + # by setting the application to not DPI Aware, Windows handles scaling the entire window on its own, ignoring kivy's + ctypes.windll.shcore.SetProcessDpiAwareness(0) + os.environ["KIVY_NO_CONSOLELOG"] = "1" os.environ["KIVY_NO_FILELOG"] = "1" os.environ["KIVY_NO_ARGS"] = "1" From 6e02a4ca3c2b0261f66ae479d8a046f6a3ee4a71 Mon Sep 17 00:00:00 2001 From: Silvris <58583688+Silvris@users.noreply.github.com> Date: Tue, 19 Sep 2023 17:43:37 -0500 Subject: [PATCH 039/144] Plando: fix overwriting outer scope (#2196) --- Fill.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Fill.py b/Fill.py index 3e0342f42c..21759eefe4 100644 --- a/Fill.py +++ b/Fill.py @@ -840,12 +840,12 @@ def distribute_planned(world: MultiWorld) -> None: if "early_locations" in locations: locations.remove("early_locations") - for player in worlds: - locations += early_locations[player] + for target_player in worlds: + locations += early_locations[target_player] if "non_early_locations" in locations: locations.remove("non_early_locations") - for player in worlds: - locations += non_early_locations[player] + for target_player in worlds: + locations += non_early_locations[target_player] block['locations'] = locations From 0012584e51219407086f8238fbbde66f9f259274 Mon Sep 17 00:00:00 2001 From: lordlou <87331798+lordlou@users.noreply.github.com> Date: Tue, 19 Sep 2023 19:26:42 -0400 Subject: [PATCH 040/144] SM: 0.4.2 percent goals fix (#2183) fixed percent items goals that can fail generation (reported here https://discord.com/channels/731205301247803413/1147318124383850516/1147318124383850516 and here https://discord.com/channels/731205301247803413/1138137515505750108/1138137515505750108) --- worlds/sm/variaRandomizer/rando/GraphBuilder.py | 2 +- worlds/sm/variaRandomizer/utils/objectives.py | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/worlds/sm/variaRandomizer/rando/GraphBuilder.py b/worlds/sm/variaRandomizer/rando/GraphBuilder.py index 3577baff08..7bee33ec82 100644 --- a/worlds/sm/variaRandomizer/rando/GraphBuilder.py +++ b/worlds/sm/variaRandomizer/rando/GraphBuilder.py @@ -150,7 +150,6 @@ class GraphBuilder(object): # update item% objectives accessibleItems = [il.Item for il in allItemLocs if ilCheck(il)] majorUpgrades = [item.Type for item in accessibleItems if item.BeamBits != 0 or item.ItemBits != 0] - sm.objectives.setItemPercentFuncs(len(accessibleItems), majorUpgrades) if split == "Scavenger": # update escape access for scav with last scav loc lastScavItemLoc = progItemLocs[-1] @@ -163,6 +162,7 @@ class GraphBuilder(object): if ilCheck(itemLoc) and (split.startswith("Full") or itemLoc.Location.isClass(split)): availLocsByArea[itemLoc.Location.GraphArea].append(itemLoc.Location.Name) self.log.debug("escapeTrigger. availLocsByArea="+str(availLocsByArea)) + sm.objectives.setItemPercentFuncs(len(accessibleItems), majorUpgrades, container) sm.objectives.setAreaFuncs({area:lambda sm,ap:SMBool(len(container.getLocs(lambda loc: loc.Name in availLocsByArea[area]))==0) for area in availLocsByArea}) self.log.debug("escapeTrigger. collect locs until G4 access") # collect all item/locations up until we can pass G4 (the escape triggers) diff --git a/worlds/sm/variaRandomizer/utils/objectives.py b/worlds/sm/variaRandomizer/utils/objectives.py index 8c886674fd..67cdb9a1c1 100644 --- a/worlds/sm/variaRandomizer/utils/objectives.py +++ b/worlds/sm/variaRandomizer/utils/objectives.py @@ -511,16 +511,18 @@ class Objectives(object): def setScavengerHuntFunc(self, scavClearFunc): self.goals["finish scavenger hunt"].clearFunc = scavClearFunc - def setItemPercentFuncs(self, totalItemsCount=None, allUpgradeTypes=None): - def getPctFunc(pct, totalItemsCount): + def setItemPercentFuncs(self, totalItemsCount=None, allUpgradeTypes=None, container=None): + def getPctFunc(total_needed, container): def f(sm, ap): - nonlocal pct, totalItemsCount - return sm.hasItemsPercent(pct, totalItemsCount) + nonlocal total_needed, container + locs_checked = len(container.getUsedLocs(lambda loc: True)) + return SMBool(locs_checked >= total_needed) return f + # AP: now based on location checks instead of local item for pct in [25,50,75,100]: goal = 'collect %d%% items' % pct - self.goals[goal].clearFunc = getPctFunc(pct, totalItemsCount) + self.goals[goal].clearFunc = getPctFunc(totalItemsCount * pct / 100, container) if allUpgradeTypes is not None: self.goals["collect all upgrades"].clearFunc = lambda sm, ap: sm.haveItems(allUpgradeTypes) From a0eea3a6501b11df3d2020a0b0753c7edc5c008a Mon Sep 17 00:00:00 2001 From: Remy Jette Date: Tue, 19 Sep 2023 16:27:49 -0700 Subject: [PATCH 041/144] WebHost: Don't count item links in the summary row completed worlds (#2193) --- WebHostLib/tracker.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WebHostLib/tracker.py b/WebHostLib/tracker.py index 4261c27e09..96a2b0fda7 100644 --- a/WebHostLib/tracker.py +++ b/WebHostLib/tracker.py @@ -9,7 +9,7 @@ from jinja2 import pass_context, runtime from werkzeug.exceptions import abort from MultiServer import Context, get_saving_second -from NetUtils import SlotType, NetworkSlot +from NetUtils import ClientStatus, SlotType, NetworkSlot from Utils import restricted_loads from worlds import lookup_any_item_id_to_name, lookup_any_location_id_to_name, network_data_package, games from worlds.alttp import Items @@ -1548,7 +1548,7 @@ def _get_multiworld_tracker_data(tracker: UUID) -> typing.Optional[typing.Dict[s for player, name in enumerate(names, 1): player_names[team, player] = name states[team, player] = multisave.get("client_game_state", {}).get((team, player), 0) - if states[team, player] == 30: # Goal Completed + if states[team, player] == ClientStatus.CLIENT_GOAL and player not in groups: completed_worlds += 1 long_player_names = player_names.copy() for (team, player), alias in multisave.get("name_aliases", {}).items(): From aff852fb4560c2a46d454b1fb1b38c696dbe3d14 Mon Sep 17 00:00:00 2001 From: Mewlif <68133186+jonloveslegos@users.noreply.github.com> Date: Wed, 20 Sep 2023 04:18:53 -0400 Subject: [PATCH 042/144] Undertale: Various Fixes (#2146) * Changed the pathing code to use os.path.join, instead of adding strings together, also fixed the savepath command using UndertaleContext instead of self.ctx (Credit to Wackerly for finding the self.ctx issue and fix) * Undertale: Fixed a debug function in the game not requiring debug to be enabled. * Undetale: Fixed a logic bug with the location "Letter Quest" --- UndertaleClient.py | 40 ++++++++++++++--------------- worlds/undertale/Rules.py | 2 +- worlds/undertale/data/patch.bsdiff | Bin 536636 -> 540868 bytes 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/UndertaleClient.py b/UndertaleClient.py index 6419707211..62fbe128bd 100644 --- a/UndertaleClient.py +++ b/UndertaleClient.py @@ -29,31 +29,31 @@ class UndertaleCommandProcessor(ClientCommandProcessor): def _cmd_patch(self): """Patch the game.""" if isinstance(self.ctx, UndertaleContext): - os.makedirs(name=os.getcwd() + "\\Undertale", exist_ok=True) + os.makedirs(name=os.path.join(os.getcwd(), "Undertale"), exist_ok=True) self.ctx.patch_game() self.output("Patched.") def _cmd_savepath(self, directory: str): """Redirect to proper save data folder. (Use before connecting!)""" if isinstance(self.ctx, UndertaleContext): - UndertaleContext.save_game_folder = directory - self.output("Changed to the following directory: " + directory) + self.ctx.save_game_folder = directory + self.output("Changed to the following directory: " + self.ctx.save_game_folder) @mark_raw def _cmd_auto_patch(self, steaminstall: typing.Optional[str] = None): """Patch the game automatically.""" if isinstance(self.ctx, UndertaleContext): - os.makedirs(name=os.getcwd() + "\\Undertale", exist_ok=True) + os.makedirs(name=os.path.join(os.getcwd(), "Undertale"), exist_ok=True) tempInstall = steaminstall if not os.path.isfile(os.path.join(tempInstall, "data.win")): tempInstall = None if tempInstall is None: tempInstall = "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Undertale" - if not os.path.exists("C:\\Program Files (x86)\\Steam\\steamapps\\common\\Undertale"): + if not os.path.exists(tempInstall): tempInstall = "C:\\Program Files\\Steam\\steamapps\\common\\Undertale" elif not os.path.exists(tempInstall): tempInstall = "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Undertale" - if not os.path.exists("C:\\Program Files (x86)\\Steam\\steamapps\\common\\Undertale"): + if not os.path.exists(tempInstall): tempInstall = "C:\\Program Files\\Steam\\steamapps\\common\\Undertale" if not os.path.exists(tempInstall) or not os.path.exists(tempInstall) or not os.path.isfile(os.path.join(tempInstall, "data.win")): self.output("ERROR: Cannot find Undertale. Please rerun the command with the correct folder." @@ -61,8 +61,8 @@ class UndertaleCommandProcessor(ClientCommandProcessor): else: for file_name in os.listdir(tempInstall): if file_name != "steam_api.dll": - shutil.copy(tempInstall+"\\"+file_name, - os.getcwd() + "\\Undertale\\" + file_name) + shutil.copy(os.path.join(tempInstall, file_name), + os.path.join(os.getcwd(), "Undertale", file_name)) self.ctx.patch_game() self.output("Patching successful!") @@ -111,13 +111,13 @@ class UndertaleContext(CommonContext): self.save_game_folder = os.path.expandvars(r"%localappdata%/UNDERTALE") def patch_game(self): - with open(os.getcwd() + "/Undertale/data.win", "rb") as f: + with open(os.path.join(os.getcwd(), "Undertale", "data.win"), "rb") as f: patchedFile = bsdiff4.patch(f.read(), undertale.data_path("patch.bsdiff")) - with open(os.getcwd() + "/Undertale/data.win", "wb") as f: + with open(os.path.join(os.getcwd(), "Undertale", "data.win"), "wb") as f: f.write(patchedFile) - os.makedirs(name=os.getcwd() + "\\Undertale\\" + "Custom Sprites", exist_ok=True) - with open(os.path.expandvars(os.getcwd() + "\\Undertale\\" + "Custom Sprites\\" + - "Which Character.txt"), "w") as f: + os.makedirs(name=os.path.join(os.getcwd(), "Undertale", "Custom Sprites"), exist_ok=True) + with open(os.path.expandvars(os.path.join(os.getcwd(), "Undertale", "Custom Sprites", + "Which Character.txt")), "w") as f: f.writelines(["// Put the folder name of the sprites you want to play as, make sure it is the only " "line other than this one.\n", "frisk"]) f.close() @@ -385,7 +385,7 @@ async def multi_watcher(ctx: UndertaleContext): for root, dirs, files in os.walk(path): for file in files: if "spots.mine" in file and "Online" in ctx.tags: - with open(root + "/" + file, "r") as mine: + with open(os.path.join(root, file), "r") as mine: this_x = mine.readline() this_y = mine.readline() this_room = mine.readline() @@ -408,7 +408,7 @@ async def game_watcher(ctx: UndertaleContext): for root, dirs, files in os.walk(path): for file in files: if ".item" in file: - os.remove(root+"/"+file) + os.remove(os.path.join(root, file)) sync_msg = [{"cmd": "Sync"}] if ctx.locations_checked: sync_msg.append({"cmd": "LocationChecks", "locations": list(ctx.locations_checked)}) @@ -424,13 +424,13 @@ async def game_watcher(ctx: UndertaleContext): for root, dirs, files in os.walk(path): for file in files: if "DontBeMad.mad" in file: - os.remove(root+"/"+file) + os.remove(os.path.join(root, file)) if "DeathLink" in ctx.tags: await ctx.send_death() if "scout" == file: sending = [] try: - with open(root+"/"+file, "r") as f: + with open(os.path.join(root, file), "r") as f: lines = f.readlines() for l in lines: if ctx.server_locations.__contains__(int(l)+12000): @@ -438,11 +438,11 @@ async def game_watcher(ctx: UndertaleContext): finally: await ctx.send_msgs([{"cmd": "LocationScouts", "locations": sending, "create_as_hint": int(2)}]) - os.remove(root+"/"+file) + os.remove(os.path.join(root, file)) if "check.spot" in file: sending = [] try: - with open(root+"/"+file, "r") as f: + with open(os.path.join(root, file), "r") as f: lines = f.readlines() for l in lines: sending = sending+[(int(l.rstrip('\n')))+12000] @@ -451,7 +451,7 @@ async def game_watcher(ctx: UndertaleContext): if "victory" in file and str(ctx.route) in file: victory = True if ".playerspot" in file and "Online" not in ctx.tags: - os.remove(root+"/"+file) + os.remove(os.path.join(root, file)) if "victory" in file: if str(ctx.route) == "all_routes": if "neutral" in file and ctx.completed_routes["neutral"] != 1: diff --git a/worlds/undertale/Rules.py b/worlds/undertale/Rules.py index 02c21f53f7..648152c504 100644 --- a/worlds/undertale/Rules.py +++ b/worlds/undertale/Rules.py @@ -113,7 +113,7 @@ def set_rules(multiworld: MultiWorld, player: int): set_rule(multiworld.get_location("Hush Trade", player), lambda state: state.can_reach("News Show", "Region", player) and state.has("Hot Dog...?", player, 1)) set_rule(multiworld.get_location("Letter Quest", player), - lambda state: state.can_reach("New Home Exit", "Entrance", player)) + lambda state: state.can_reach("New Home Exit", "Entrance", player) and state.has("Undyne Date", player)) if (not _undertale_is_route(multiworld.state, player, 2)) or _undertale_is_route(multiworld.state, player, 3): set_rule(multiworld.get_location("Nicecream Punch Card", player), lambda state: state.has("Punch Card", player, 3) and state.can_reach("Waterfall", "Region", player)) diff --git a/worlds/undertale/data/patch.bsdiff b/worlds/undertale/data/patch.bsdiff index 8d7dcbf43a96dcc254a12c719d70dbfeaa86d636..5d137537be83f69d17af64f33407f5915c52b835 100644 GIT binary patch literal 540868 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%0DC=X84r|?ak{af6uSdh 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
  • %bOLSMQ^Ipq#zX>pc5C8 zmogW^Te8VTT6}MP7avY|&FMSG25ho$9ijB9;6|k9A!ns zPMA`LX28sE#OG4Mz+IVT*GXyh&>Mn=*<)yGVNUg}qVciM=Y{>){oFs7c;Pl*x4=E^ zo7dLlJZ-b!e(wF3`HSQC_2f;Zb?z5Gj=BD1S=3))Whs=fouKmc41Ih@s)Rd)uYwV= zMhg=-X$^9+QKYr;ShqE@odDJ_DHYdhYfgLR;WeSy8x_t%;Ds!r2!cvT;gZEX$j&n| zk<)i`;(F)d4#Z;XM0}h2P}}g6EfQQtMCm9+5;JUhy+C98c>{)gcg%eMB&bcwxuOl# zBF)2X3HD)k zA$?77bQevJ-DNwt?i9(5RrraiG~k{|m^g@BPJ*K{W0~~7>e$K~)Qw%*{e#sILwYPO z`-L4?5D;c<6X0d?Aq&2^un_1P=4s1%`i;`=bhRKx?eQK2n!;43q;YLCxB5=0HieU+ znN>NS)!(O2-t@Na=T3v`J{#_pnqi1lGk$4zIe5^-nW3faAV*u*}!W}#-@_(@`1d+!~Gj!;V{3USntJqY!4 z$DpIc+BOjTBVG*}_l~~5jT7RxegZ2lx*qd1;_!B0MTzvl6qEv`-vNl8dH@7!JVlHi z#k>g8FBQ}PDCq!g_>b+SP3uaq=k;qdQ52GM6XfP}NS`w(MF;YB!42+9ZJX@0&SSJO zvG<$rD!cYsD*zKz!uCW&*%)_PB36TmkqETs@3|4aq9dZj$2mw2M&@Ck0y)Q5%gMNl zr&`D1ft;%ooMWQ?&T~4ETl2qTNTK5mo14nZQB zN?-$2fQ%JV_c@)clna`qOQH9KYHUlZEgR=z5QKX!7>n%39FH?j&(z|wRqnMTU$+Z3 zXGH89oOg8q4&nfwY^8s3o_g=>bCj$7zui~!>Rnd5hZ!H<>(j!osW-T3Lj@&vzriT- z!*`Nup?_zsM}OPXU7iJ0MWQg7tlxqnl2SA{0(A#F-V)*{cbKOsn6H5WGD6YX;x;iD zmw+CW09L3Nx9dzYIn~l4d1MLm_!nuu<;;ZJ=DQK@#(R$?oA!Oxhsr$lYUX336917j z@m`g>XKp0Y^lfjrl_NSSruXph<>2Hk3 z=6z*#uCwo^z4qV@e|fvlMYoP+@4y5TX{6nEsuO=R7u~0J zfj-Y;QVRs%OHn7M1}g!JvWE%!R{<=xR(o_k#T$JnG}t?Hwz<1Ep7vC4Y${m}>Mnkrd&`*vx(xaKm1HjPU;U^R~ zal`^eMKS($Sl!@MxLvHLkJnFZ9{gEMd`Vmmp?GR=eUp-Ng+H?#l~n4}_9R~n=_1GQ zozQ!Ve)QN5Ae`<+&@)JJE-9dKT{D zT^&-aHS0#SD=dU(?wZ=Rbw&H{nt4$3wIc+oL?1ldSj2S>-yoqTqY(HUT_SZx$QkJq zrc-BAlkBgOHIA&BF{bhVDM3Le$KftnPw=f4P6x(_Cgj6_mnmedJKNmQ=||;CUSZej zS1DASAnhP34($Dh4PoVvE{c~uy^>9R{svOy&FlK8`ggClwA*%rTT}J))_f?Rbx)dF z?b}#x_J%`^H$%W=>Te0jLy)w5JzMWNIJEM=y%Qr+2#2G=2}2Dcln9N^ox0$Sd7R zx_t%pQ&piA%(m0go1!O4rfzDJZJ`Nas`SUN7P=PET@zlebSRU$H&Hq7gW`^)+|MffZ%^IOt^&W54EIUskp|E(H#N<5>Uk<2E7w1k@*;#bAdTI}?FgiuumW4gBd$)! z0w4OQA}wo?KsFIu5y2&>-F#JBv%%94fR(Cy*NXCYREK)6@KOXj**qBvuQF-DprLKk z2j-`I4*k=sVw9c!xkmdJT;{nWUSsF|Wu&a`zOwcnpNiG!y|EbU$Z)oZoVV@kA}#3R zArWTAhR}dV(}e!ViC5%YRi>-?d4DE{U1QH|G~lYwyOy6^K6)m%rMuS?xD&mBi+=Z7 zPAs=<*1Edo0Ml3kHwLN@~6cBvVf}D~LA!(x`7_fM1!z=pF`*e<%jV>BVbXHLH~zB9=Tef+v}) z8LuZ2ZZ($%lZ7U%szQJXo!n+`uPtJ!U!xn(3J=RCsI?MSsLLiQeFiQk3Cy+TZtYbn^f{lWAt5r|#Ei{QeLfx*G18eJRQtFdU0Zd? z9`E33ypo)>l9T6u;#b!j{4mnIaWW%aLKT3X4bGhr;Q*AZ;fJc7hV8^BV^0(sam+N& zXRXi4i9mrWJD`szC*zrUMDZ-J9hDeB={pC781E44b*@?#E(xJ-wZ=(>iF$Lbw1(Zl zQD~@Anc=BT9B3Vnt zOY3~=0~iQ^+rcQ4JBGQ_4RL)|j7YXFH!gY|V|DSk6p9jL&Ru;H`WSKGzto&f=*}^y z#cGH;EO2w5ZLvff`J6cWl|RPRoT`Me?d$4Xq-*KVfDb7;h6AH^Z=>$$q?C5A7m z%j+llNgw6WuC|slPL@LxYpzS1jeV?C)tN4eGwI-mpyM;jr(soepfB zu!c@ZVrh|=Xr1cEulfxfGzQQN$z(jcp)msvolKZ9mE<^i>dY$n>T+=wws};__ zpwLJF0l-vZOgEaN3&nzFKOkSys%)DtguMP;o9Z{!Kb(tiJieIiR#N$O+9d}(S)mYz z9w5Df&x-6=n$gr;zMOon@u7fJLS!z|r5D6UD`pP0w$05z=cy9%tc>`;kUiicaHzMm z$2p<$%#4T}0}6P~;63}Bl{vOg)z~orJwU?0SHxS~U{>T5jcUO?;fk5@?=Ds*Ni%Fa zckmhtY+M^Cf>Hv`I~``RN20H{E)*5t~lGoNeFtRodq>HCB6be(F`gtJA}0Hra-~3%;UlC zaqT$+6w?Obr8!bW$Be2;rq7)1P0-=J)gHgp^P0M?tnj{(8ht3bT~4j|&(kaOt9xNq zWq&@mnIKRnio%Wu-#wHF0ua8dfCH1M(nE-><`$sK!2LM@Bp_AH6WLy+X;`IkiN8an z+I2bEiz{H&#lh0B1o<-voVISWB(Zavz8OVKmqk?@g%Ym5M@%sqCwk9LcO+1KGMq3; z2*A{6$A0+pCx|#}Ize4xpnM-(kH{!>+1T6-yLSySxlbgTUxG;&^8Q{r{T(?-v0WtZ z7t`e$e=sCMP!Fx*adDmmeEN~kTA;O?Ov>R{0{i=TBL&31Q6H9W4J%60BiA?t6HnDl z=6qfU@A~9Wg{XTZRXqN(*#BtFW$3;lfLHqjQ~!~~0JYZantr1ILyRBR48tje``0EXt z8#5^)OcmJ>2L-_Oma~;MBzd#snXsLArkLC&Y1JNMW?>ac)%COw$Iuk^j?pbkj+}wl%NT} zZpU5pdHP3J08!bP#oKXl*GFl)O1tl29_^ps;+BeD`-FcV{LJ7;Oa_R?7i)Gg+Q(*faFjPNi#Kq@`t?F{$;q$*NKk5~eb={a#aM~D7Egw*cccA?VuTD-k;+zq^RR%eUQ8` zuYURO!f`69MNnly@$|hBs5Eq;L?ic9bM7=nSH^Gapc7$*r?vBq@3YFYrd4<8%KKpO z9av&sPy0T3%M^o~L}8V;=IDf5tqD)BW|yG?S6MhybFMtigWLOQ7q0`148-+l)lu=ohS|W86>@i(2zfcgE$G*Ew!MNZ# z!v~-e&-qK*RtjBOTG~{+o%|=9wY2el# zW?8D%pX<4IHS7_mlT`vMPwKoz_Oc@)Fw_B6{I;I=?NyfceI0To^1k4WP5a3)D$;hM z%0xDcVK@)oM5cAm1(}qe_D&GOeJDx`*Wu(iK`*`KV(3OHrC|)3bt~g1y%NZ|L;d42 zJ2m#ikn#NflO{?cHR+8G;hBS)84_G=Ri@-)(#Crj-a@or|YRob!z(mVJakO z@slz8sBect!O+lmh)@nm{f=~m69q@OZD5YR;*~i09x~&wG&EDEvE28e-0X43j!4)7g}PF`nfk>;=M|mD*J7+3-!!=0MWaEk3n?B(TYlMn7#tm?DPL9Tac2zBzbx;0&t~l@V&e>una?4E8HWSl+LCmQ(U=XX# z<{}mw=(a>UZF3LBS&4#yXOl?dWRs_rWElrJ<%j-?C_~o!?uACiA&4f%VOoDq8ocv3 zP&f-RHmYe~t7xrC6M3^2fU-fjU;xVe7Mlxbk<3Hix=nsie55coet>xSi*<}fRL*Pi z1wD9BzD#r zB8lszZ=l!I_u5g;0&qP=3o~H}aE;X`il-%2tt~OZCE*gt2~pY791Q}PE_i8Bs#X`y zS7Q07|mt^Q43WL!V zVVb>p9~-2B{wgf;)T!Jn9Whdy$ScavbP@Jw!E+T-~1!7s}1Ptc)pB^-u-_sFHqJ*t&--$IZyx-i2xGnf3TEX zf~y#0`nAZG@2;doynQi-eQ$ZC)3511yTxPmUSr(NX2_`+acT~Nl=ugyOG|*L3U-MA zyMLp62yyfU8T0RgURn%jspLR2r!7Oa{Ma7?hipHv4ai&Ph(~y*04L+DB71hFVkObs z09}d6*}uGql$_*r4uUTdU{M>1?zrVLfc5a@#HLBVrO=Sc;~8dgJoDZVtC0L6l5X__ zA@Q5WIrU5lhZ^t4FY(FpLWm~(RY zUS~@T?|qvfVz`0e<8 zB*T|PTN7@1gT1%;%ip$qa!>i~>Kc$udiSvB(>v+1bcUPwi#KA_!7UduW_nziE5Yo1 zMI-iLGckmv`>L z0z6HF*f!^>5OP|+*h;(3@Ny!!vq)g;7jiTzfz=*irDS}YA`iGIepgwaOC>os#MJi*)9Mv@{iHSt4$NK)ssGW|7iQzg}gwV zwkoY_cgZnzds2qh9e1p%s`FCHqS{^2lZBWp8VE!!aafAGD9`#T(hTjJLy2aR#b_{8 z^f=#QZweD(!;z{2$Gna0YM!%+XU0kwbi71Qg2!j&47aGx9e1fgk8#VV97(T;JXL

    jaj3>R!VsRXgf8lkjoiCmdI-(+}|P-6C}e6 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 z zetsyRh&u}v``7~qX6@*nS67{9fJ}5sXUpU(gxY2B&2|vx{&kthOTH$M`eA zLJZDK(W!&?QSJ6i+aH8jXe=;vArin%>Sud22p!B1#J&qyeIcJv=5f`XE0qt*m1Z0F zujNgUE^K9obh7V&15B20uk*iZz3X&i53WjuxDY_jEx+P#-emH?9G$w(!W+PF$fus5;}GP7xwmRsjoeZU@;jM7lg)UB=LHXU;y%Pt)jR0`2i#}&0-sp zA#5dJS8T0FsaLt@pby8nCRxECX*QfVtVu(iiqJ^04|%4+K`#9HW$96W*ro{3%){%) zmWPna4bc56XP)heyNAkT7+~662fI4eFa>VYUd-&e>6y*mdNK;c^zG=uwbn&C=l!=V z#iQ%3ES8DgEn~`r$ziEGGN%#FFf=ME%8tJ1ys*+j9zuYS-EvhS4kd zL@bbLv4NyRE<&p#X)b(M!HGFRUDt zJSJ(-5x_QC$;7J>l@n_H7cvqDoxCJ7@s%lR^P7j4c15MH%w|^Vb@G~21i#+bAkdEd z+NTnM8IyBTvY9OknX{Wu=kly+qJ$`=XOl#Hdv$Vzeu>osCknC|`o8epiFIzF#2Sm$ zbj46@>?Z!Er#LtMh-!mnMq?X|<(BuJ&Yw*)nlFWIg-80jS5k0!$>5q<+>-`pt@faIg?GXU@XL5eDILW-u%eyP6-yg1Wlt_hB z-!`xlI!fdEZ$m^+9#wHHRpVv1WJhicn)9!^dZsVYd^k4iB69lyrVumh+(JN(XdWH5 zg-f`nN;9yI!jjqX-h)82I{w4$%Hz!R-X*~!xx-N8q4{&1{XFJhCv5$UkUioDZXZ+t zegr-Al0LJFCBAy!WuKprJD@IRdp~-K)ON5NoCfZv zLPDRWwU@%^zdql|EY}yYe+BVZAW;etU`b599^M66z=f2y zW}jO2Vd0%`-v8A$*lcoo2yBSEX$F~Z+ln80Dh zawekabDnUY1_gfsqW3YXp#V}CfiC3Wjpk=gJJ|){#Bzan;j3<$$Wa@S?%VuKiDeFS z?p9v(4&B;NBBxLy;Jr=#&QdnVLH-~(ESzUR3dc5EDT*0jV^ zPhJEk(TB0}R-LF0AJEI*K61B=lm>wpW|#AU^kA*r;XrrW*T*oDev54AN^zpC5QoYI;34YKQBd)$qNhRw+l0R|EPJ{09H$ql@H)8ZkB4 zPcuYtNa&$0tl1L(Y{$TB!Z+%VRPgfw=dCXTj!gdTUmQ1H2147m|q9!O$Wm z3U}*Iy_k{@&3f+%2$&b8?9d$yN-V+wlsvus*piI)R}I3*FN%ItRzpi)^lR#^vbe0h zT1Qw8ssolIs_(sZA68I)U7qOp@!T4L!0C} z(_lo5rbC9WNA{>G?6Wh*;guke#O0JW5ZELyK8rbQK8RLDV^{DK?MAR{HiNEgM{Ah5X5zMUZe?={y~F1^ge3^CG}!sjNQVvuOUF2%&7sMBGE zRUL8l&8vd#h|wh6So3lZlowD;_}Yo!)0Hhgn&e6&_q-E9$e_p}1-O3ga~)C!E$9Wg zaeD!B0G0yCQn}>HJzUFw06FSw>~|hfKHs%r`C_N9DDhMyDu|wpEb7=;uB<%z&j(WN zX}NI6p!-75+~2SULW;lc8Aw=172fdoW6a8q5P%2Qmu*xv#Ee!K9rZujVgX8YEA_H5 zJJg{2noDV`;Rp(`3PpC=VIqm+r+o3pF4o;Z%tSD?ztNkyWO03D+Vv}$6!vnQ#qIJ*HSIt$1&46b%)yr9) zvJP`|_BKbmO+yatFE;m6f23m-PsxCXq+wfr@z4Y#cZbi9zDJS z&% z@ns^wEPV9}g$cB*&*&F}iN805EQt+?O%FT2oPdA;ORoSXl%KBf{r&yKhFy^HqGcIM zlV)9<+GHy^6A%bc%G!N<5g?x8gC-js>FK5;9jYYOqgH)qvEwedQaCwMPoqNUA4u~h zYRzxg2T+MK!e!Um2Zq^4jDy}~z;{2T%oUn9u+0&%3#(mxapXul{3!gGni+AIRe1ad z*Fs^}%o3ZX%8rLrx&r0>W_9CuZz!nj-w_rtE?FSyppL;XK%uu_s3*E!J{MZDeY~=K z@|Wsf7RP3C{l@S|lv>NEBVyaK2wd5okg=lxz9Bxt5u+Pr7fo*OE)l$Q!M6(FF)e^E zB&0ri>9u9|9a*0pd^iNxO#2&1&mbc~qG!D&f>$FI+Xo zb0~2fj};KDN?igO12bR9=7@oxf;|ag!m%fZlB~! zE2o4Hucm#zoi^h&mDQgz^sUm!op8=xICroab&@} zfyuz~UmLq@kP&=eflur&LFV_zCKBMP1kMHqhvNemZHqEkKgkZV5qhVbje2ui2q-YA z!HgXsJz?4RqS?>DrGfoaj_(T!3KbM&g+6KUOqn2_zj@I~Fn7^rZjlr1!I4h617vt# z_-QCLp1x2h$&m>igBulgAs4tKdCM1v;3glku7B966>(RbFhQ}FKqX4-lqhQ|pDfx+ zXjY(yz6!UIk>FP8F%hkC)jtk+n4OU&FNn1p!31>vqLt zs>>O6pSmk{w|gsQdp8EYJdAyJOdl_E2ea7coeZ&ncj1>@JF1z?mLR0_zd32ZiYq4r z6iz(q4R0i#@bD%vi8`M<$ysFvdV_{a-){f3xBSfg>eY67HijtYVMqX^%L{x!zcQti z7G=?G9q<*fdlSvCpB61zuc(l6}Cax*UqXFeN?! zGD$8)fd>JKKtd`M{wf~2-4UfO6&Vx>QKvUytHlWi7Kp<5^4o8k3A5vNGRh* za7jT+hScx$Dad--yv+$y((XMLpB_rW4kCh=k#3(%wNA(908M3w(wxoJsXoqyvACSN zyJOjRgQlVAGSfP#F@A?IPkJO3J{1e*+r#lSRVi2V%RQiG66`4eWJU@as*XTRUzy7Q z)RqK@_Z^TQV85H2;8KgkCpddfTe_doqrDrszQ zHTwyAB@}lgp<%I`2I`tn?lMN#yA6?+!Mp&X09?jM=Z{2ZM5)xpM}|0~e~Q`O@!fKV zcGTv#JJ@}zqA$NEk&@6THX-Kx_%bbsbnV6v3?QbSi~nY)n0bJxZNVQ^JFX}1U?S1s z6otNkAKLN)jK&#)HqZ#jjEIG0P?>#5#B!47;pc&=$XOVEKys0Q*cgaOq=+CTg+1dC z81owVu-=_=0-j0=q5T-SXz^j3{33e3DFL?}Suv<@7nXn*TnBO_hH?@l_Pe<>Ie!Eo z%gFJ@Nc3X(I7Dczn`Ae<{PvpWfoI`JX7NIMAB?LvbFTzjR(AW;JU3jv-+aGB7oU1Eb?qNYIEXDe4x_)oq{b4p!5jyB@X!L42ghDMSgaZlsjPrn*aErR;!dnZROK4Ek z_K6nsI*o^oeq%&vH?Q~p_D}ItQ|s&Fr`|iauc@UhdW2lpg+;IOy_)EIC>k@-T|dFHUGa~ns$+@W2*&r5&EUN!OroM`oQek-?w zHmPPyU}Wz+$kq!sR*F#?in&8 z>Oisy-?!0$eJ8Wx0KxPx7NUk3C#%g}^xpDF%K-T<j@q3*H0pH zm={Sg*3`b_1f;0dqXzl49!pUu!S1Qyg}DRH3K8b26$zEVLc2@iYpaB)C%-SVY>Lh8 zUysR8(h+bGlbO&0B@I zB%?lSt5@+OKLlBBs#WzR9n$`on%F2z@zh4+src!7k%%;hp0a+ zUrXOWYq15**#_V1oNYZ;u)Z|@LS!2D-#5SzoxmuXgyEv0zJz3P&uqnvay4Lt3)$NC z=fZCM+#{bH_UwR%=LPgZSHzshWaNOY##(5M%@Nd~<}glX`{TBA0NP~BmhfI8q7@y@T35 z>wwcn6)>HB{a#I;^~7cCrdz|m(v?De0{}w0&S=z4{C@}7H*^|bPmd}-`^*)Vl#_{f*9bmA=ZRciTstf3< ze_crhoFKv+o}Q$L6Z7*~p-nWzbyVpAt6qY(25V7Yfpe2x1%1WGZYc(!J^-?<)QOEl z6=cYU$#;boI3=_mps#2H64@VIhCGSiNDj8JUyJzJCzp&It4=JXk75~YiJr;r*#MY z24B4D=!9_1A6F&8BQ6-px&pb0oW8}G1#(@|$^E$c?}(CTln#OD+>K-p2bz>1s5%OT zO^|?(^c)t#uTpqT(`*!#dfPZ~St%b2QJuCl-F{X)6@qylN6mWN^p zl(x2DOi&Xft*TFWh51#9{k}1~FA&SJfIUv(pp4t9Af$rN(AeG?Qdbf)RmaB1Ax=`} zzc1xMo3iAnU5C1Og0J9R?YZgHwj@^4O+eGD;;@+mObH20u5L~xK}{`N=Qb-CGZmV% zZy4ju9>4ad00$WiQs6=M;BKsm65Ihx;v1Vg?am0|9JwFAv(Tw&b#L0Fa%u}&S2@Jl zb4dW>LGNZJ@}hPp}6kMtT2~D64G(5mYPu;^#-mBX!c* zSIbdWAsg1`S78mtFlX@G`w>dj#EYVJTx`F53r7Si;K<0=$nTR7JTr42M^cr^?w=JD z^a)R@t6{`}_e?)hSA&{CU&9sA)+pXmt}$W2d0q3!E4JG`aAsUU`ML#f86mTnl;>LZ zMl(%}9yWWgf_54*W*4$MlUEl#rwMbq;P(g>^1ymElNmA|1MWKChgeKQr`T2YK<5V-wqZ%4rR>~i z<-g5-rs4h#><5?g#YgwQH~-ad`P^P@b$frjJ^fF*f8x&nO4e$&`R{*s`8Oo}U%vVe zopGo8!`V``&c8bA$#hoBm3rgtQSgn5v_{ zBP7Ns*^c_LnboPzU8w^BP_ntM2hO`K4}Wm@oYy0Ej7 zs{|MCN?)Nb9Ctk8!BsqKN|h+gM$RgU7v4C6B-eQ54wbgjvW$?nwtI!08-a-qr<|

    Kl6aLC9(izl4mQH)3Q&j?E3ibi>5P0SGZ@zl*8)k4MCpBd+R9GE-bM7K*2O z_Qm;3@mQggP`z$n2`C64dFC0Qd5FIgLKeS0euj+L;xve{_JE@_`S5b-GIrYO;1@It zU+dX4$2p#AZ)Z4A(rXM>Wo2XEa;lv^DFHYs`8@!ZD$HeYfT`=^;HXyTVCil9}#{A+Pm8*{;E zvxOm@A&Mh7mh_FkqO~1D!mQ#z$ZAB(7T+ggl)}XnA^%PT9S{@FSo-e7NY;k&OGxf3 zF>@?nGK7!mz>WSR1j*h!WUiAoE2l&a3XN^e7u_yA!0-Q5aM5bN7co>ylcPYG>> z>GzZRg=4wK#yIhe-ZCoFD^Kn_+a`Z}DzJGR3RH@s6INDGUrts(zR?0f#mt|hFZ^p4 z1RRn3tsZ|iDQ*~tpn=GMlEK5;nx-ud0G$gnkc)>&(y&DX|&YLsE|iti9?st-p6r zIokCS+#SyMbO#zF+*XM*MC4Ub)H62*fcU3DRF1RO-pc%+-(Ql(^&HT#rMSB;6S)+L324?@9|o z_Wgzl)TVccElhnzVx*K&Nfb&%M)TT^5)$3MpXKj}wb$LR8L?pL==QCMAzQ%jR=wDF zUd2-5DSgyK%cREeoZTm*)DJA)ur(q>(~6cx`m|(e5mqCuktWDmn3KYD<_-lzCl$E) z{ZDXZU{2hZ#L;1sn3(EA+t4)(l~T+f3cN^_Z7hYs$k-HyZuD|<)xK-qzqDv-;0#Z* zw`$PvElQ9_Lpow5A;?oAAXJ~Iu-N^TU~N&L{E4*9(K!?s7aJREEA%UG)h2CXv1%Wa zCCVn()5h6uU55ZXt%@-*<)3Gf8E3-aIm7!uIHF{p&9z*feD{7~!LLNxn#_ zl>x74Ni>|4IN}RZD?=z8!SzLgbs217nue_}A$BBXu+JcKk{MMlF=8>9is?mWN+1=c ziquPPcqX+OMUCPb5lHgtG?3t0l1f7P*m$*G_dbXR1OGuCYn2aZ>K|Xc; zU@_Es{vMScGS6|Fvtqaj6bY^vmxKv@? z%i^+U=wASwjfjc-8>}uy^!X_dCKn++qzJzV;GD;Yk2{ZP4NU~CyvEVzjlJ@lp(_b4IIQZt}!oZZ6G<;1)HHSuw$>Xg>{(5VKCuVkINauDU=@DW)Pc2afgWGS-W$28a99pm%+H@<@ zlQ&q(f0Hu+-)Bi~Yeq16d~-O9o58Q3a{0EtS0o-YP)F- z5;Q})J*kvx`Xggk7M=CSUcXny!_ZCl$Er`)?nj4o?CUEtQ6A{cZ=Y?qf$TuJD0MeM zmFbS;evCh-Rlr9)ds8;hR?SX^X|!Y50I@NJZv98y7}2O2+Oz&UNa&WRf`u^;I`D;6 zrkDv5tL8*bJ_=PqZ2JV;NG1m%Xv78o#cL{vkn*ZA1{2BSPr zrm~Cy3LSLONH+r5eVW{s=Ukkf^=|vtutu9yUzJ}dw&uq9C*7MI`0@AP@mdxk{x`d(k~xP`2^laPN(fSMii_zarw}PHb2D3yq4LjoksWUFD->@z~J4=c8sy zE!rdI^QbeR68!7W!8ZRx>LYm(L~!KP?AG*nHYcMqJt=^4zMYh>m@GJqG)1WcKTPkCl9qPQe zA?0x^RB_d&mJ{RepQM`tw>Qxltp)c`P7ROz2mZyF-qw%`Or-4$ z>tbC|cq-(LhDyxY&t{Vop-}RO@Y-(jgu6%VJlfxIl;+v!M0W1pr?HHv0mUcE7(jGg+qHx>JiDr31T z;VwW+zW?D`} zud|s7)CXMH@_p>?&|V_*qnL zKHO9wEj+>sDpOxAN4ylEF{4UM!nd9gK0H;n6`heJ0Wf5kk}R}&=_t6L^BFK^73;Pf zTrrUb3CW4t6Fj17C=TxZ4ABxKs*ESlyA-HIYC`uo(%YR#)rT$CO(&e_G`D_)Ag(`} zm6Jv%0wEEf=z5FU;#A<%DLZv=N_OB`2^5VUOznM|x7+-_%@=4ccQMn^vRug@=@R_j zz0O9<^-PCZ0t-&1VQ!9Wqoxk4^%AUp$@Luvb$O`qS6E;&?^=F$XKnk_K~F6o>W?>y zWb-F)p9;;RE~n$O9y%BCDDYXI!c}bpWAkq3Ks5m9C~h1WoXA2U+_#ht6x5%ize5R|XbTaQ%19o24 z$3kpnHg?=USasp_-tmiMcRYQf_=As49QKI2%5H7%Ojq@7EfPYh z2dKR7U2@4H3VNUUYA(jmFI$qrotfOo(CzFQxv%a~-pKW}HW}{V;sk>)p5s`_r2~2! zGTZ2D1=Ec7!kFE&CSTz7Cw<_@d(-zkp9GOy=sJP3@Sepdk2rGxcbBKByX5fDBahaL z^*%@G4Ws+0mtc+Wr(}VtAqv`F>TpG&>alEA86mO?8%tp8u`wJ~N8lh)J&bhGG(d}n z;dKPwcgQ%9l-@7OqD$}ljf(Y^1$^0-Cf0U=yz&=iQ)9q%Q>-Nq8@o!1Bh zbvy!m1Hc@CkVN3_SVQo-H<1l|=&N5>ZHSOxb1D|XoBA1BP$}5-a0j$oX40&Q897qbK)q$=}JEmyjUy5 zh_WfA)+xp1I$_}X`$Wo81Nh8x(A&HNw6gg!5tL-OsnA*wo?(VCGMLQ*Ey;6mY@h5v z!DJpSS0&b$D^&&r_eOajDij7q0t?OHb&35{La6r))InNK=1LU0HJzxCz)8VQRt3kreq;KZ>uhKfL{8yQCp2!Yrn zfx@CK?2ow^!Yw2WDM4L-_AqG={iRXAqA&QDjO4$@*u{`5mRX$rpdz;mE8CDrRtVKs z4j-W_Qxv-%`otZrerC2;NtqrB#@kzzGnr)$37JhS>5#4#B1Pz+){(2`Dq0Ft``yfD zCL;GE+pKc39hyD6B!T{PbG}9De>&OaYwJSmdm&>!FyFkCMBDD3E1EC|&yLONx4mkzk7g&hOpzq*8D`fxj)@UVeFd&TiG7ay9Uhy{0_ zl~=Fl?CCY}&5}8$QZX+)`d%5CeHVnQs?YNns^_uNP45q^OJ3JY6s-(iBQwNlJj6@6 zt7Xl1Wk@*Ojgo?dy5d$%hX@FqI37nyZy2zin&a^j0zd)sGS9!HNGn48@rY(pZ8&hf z3poe5u>BWUZOc;}d~gM-CC6BjSf&{<_OsTm0O$?f{9_6_FF2@^BV zJisx@dNQzhcQ(9RYu8H!KAm~r@)#T3E9zE`2-VFf=(&q9BT3{@p*TFMJeH{kxF#QF>8r1%d#c(IE|fcR2!R2M{Mx{g3B6Y*undkihINgu^Ald^Eqo( z1WmEC*oM6%P57#VU2IWF{N!lSxdANpJ3zCw6o5kwU1XRNFoF0Zd45!bG*TwTt?#sp zY;J#q9S#0AYQY(H!%kqV4ht3Ulal*N^s^0& znF2sZ5hGmKw;=SR^9>7ewliqR9v$u% z{UT2w91ziy1||w%u|Xgrdq4tx(=96w8Ny^Itvd`|c+|74HrcwyKv#iM?`03vt);&( z)m&nkNLhZ~@cc5SqXYq!@jqR+Yo26SRb}uveY1Qc%SJxDJ$&Z-{<6;(9TC*^X~C2x zKrkL6#Aoc|678_vJh=@&CpG)NBpp+09bq8K{if`5PEXT~?n-LK6oHD=!5-74f!JkP zS~iE9iE%MO$ln2B!mKu62A0&f@ZkqN^`o5wf!?lqAEgZ>`n5CH#4 zO1hZKJCpHRbnFR|J4~ALgY6eH4K`(y=3t<$!%Ktb=$&2s-%38u%JZAIS&54INzd@* zH(CXaA=YlQ;BJ(&ESBuu>OZTd^w>3>YGq$9I(F?hP8w_J*S}w?xU=nxmbGi{ngScC zO2=PCDt*SNgV*cz^AX|F&zm6y)h)&Ae(Osc-hOW6=KVePc%cWKj~zs6`!F%%3YwepD2PnNo;SnoD6u>U3u9eX<7UH(nfo)Toih4-xo<*sJ9%sgG# z#)D)~_BKeD+H~*s?nuw5(Rn!wH;;E^>-hIHs=S{UzRJ;I%_eX`J=xrSW)$Piz2uZa zQ5m5XP_OLWIZZ@v3M(dc{93_0h#G}U1>j;@HWGO>XhYX5;ej!lmFw1Uw9q>(p#+ay zu87ok9-l9_)10e*>`aU@)cuuEY(Ue9h^pM^`RegMBNJ4tDb4KRrlIU!vOx$jF;8|Hp6-_^U2v`_D zSJ9z%Nm&O9tJX@(bLv%B$yw zN>8|=Tzi!!!#WDeBmARLoxBSGomLiKlna3+iluCXF5JG|6IM(m0e_P2y}f@qLJZf9<4- zBT9NgT)`X&XlMQ;K^(-a<k2@M| z0@V-UHf_zqhkkoVBG5bkP%h|r$@j_ee-7qT;`l}EEi2$kv$dQdE3x`lC~FL%{J%Q6 z#pS2ty1w^)cLpsjnh!bL&)7Oh#d#?08Qq8(cv18Y;_Y9-*`<4(jEZ@cls?BB(}aB> zC?3kMUig4}B~bnD%RT2&Mvy)i{Kf}*Zt?o*gWPU~m|_VA?U;i~0?BBT{`x#hFRRBl zX`rv>b4W|G>(I*nZJh9J^9}60$+u}N`~?wi?O8bJKj&+7_UAg3>Bjci$f<+`estm7 z3up`!>t?L3cNW@3f?+VP*jvtR8x>mvewWt!O<0Ns0bbTHKUtOjI;r9I@7ziO;nKF6 zPxNf;mzsmdA9}KP&QAA|u+V5*tjY&1o;y;X&E_^7gjzC+Jr9~Oy7K9O1XuhAzoiDr zJ=G1$!5!TTP`jL;77Fza-m%$8z$C-<5Kh_|}{q<4iOEYOuiycBFHK z3}A#m$&#Ol&h7E6?{e3lxSK{b?wWAm+5*#97!2b{v71m?Kx`<+o8SiKMt06OOr^fb zS%%eHPF*I{$A2osv7yoaS2lR&xsTH^Af|QE95A+A-dausctGiZ zDp9qS)|yQ*5Ks!23lHgtki=4CdN%9+F9Sf{ybmS+b^Kk0%Ue%k8h*+WtI=a;y~ zXKe$BLt5BpG*y?|r#JcP%LgEx{?knuFeCOAHKqVM=Sh(aU2aSW7!TLy;aI(QUo#*; zykUFhC0EP9sJJlQ^1%=#aq&pye~@X;y(NWxN&B6=zww0KSLfNTQVtK`U;`yag7S~J z_OPwtlvW+2M~zDhtQeS7f41+*S2T`ViOI?jiuufn2j8Pia^6bE zbBwXN*81L;u?Oym@MJarOdqegGME&rIp1Q1>)3dN1Lj38C~!)OdJB5D#8#btyj%cS zwQTz$9fnchN*6)kLDq>A$7cbV208^V3nC09H21_{L5$HebOLYUqJK$y|0hrQZ?IEB z1n=f^$$+=9ANErto11#GN<4+KF~{$fiEZ`zca1A$*XkBM@w?XS{^f`7QzKJ7GY6sC zQi|xvX=wp+y4UuTlgYGGbE5o`h>aB}MBh~I=0*3nPH#Uha4}zwVXRJnY4B)wUQjB| zw4T&_VTkdM8fYNx!~_>oiII;NqQnBed9$8F2lM=@0Q6A@ zq3KBkpg-!V2*R1d0tF!nK>E^xZDj(NO4AVnkir;33TQe#V$Vtq8ANtbJ}%hABoZx( zzUz8`8zTBcWe^4sq)!TKN+2HtuG&VhexwvdM=CxGDxy0x!Zgoudz=p=CwG_G?DtC^ z+Y`or6-U*Gmk~i=OmP9(0`@>mSwXC*t>mQGfoVU~!Vq5yVv18pc?A+$Mi`=$LP~A30qY^z0By*JAPtf7 z#4q+z*izg@NT5jx&drG=K=}}k$Q~Zbq>+XoTaEsII$%k>`YQ8F#N00YTOCg3_B|%w zb%(UpuU`KQq-|~JCupD&g%VzQA>vEwNZ{EyBz4=J|?J zEo#q4inYFe*o`Ad*l2(O94G>K++R{(X8*5)0!R(=;6O0n@{HJ`Rl;epEEW=-5m(pV zt@(=x0u$c4afjl90Lx8+^2<c)apFOTW9W#7PJ771e|Vj*Ul!oo6t z-Djc!Z^Gm{DB!!W>>xE52Ta#if=Sa*6)%_f=c!|2cw+WA_5qgY$1w$u|7kmoCVOv; z$a>-&htYplPb+rf;p>?>-{(b+)YP4IO&t5FwzecEx0=DNx~P?EW(tLud>{G{z{SS!U`o$KXkdH(`7Y$S?&Gem5}NiY=AV1`m%bUwX$k@} zkC7awoJ8|c^Y^jizy<|hoe^GzTv$)2uVqV`n6D|I_Fwk|mjsdx122}4fS4*UPy`mR zK$Z}lTh^1*&;Sb|B?e%rp?eiKq-;Kmz$1ZhS1flH061j_G@$((;Y7!FCVjhWV-+${ z%sO(37C@>UcVsO<=S4{zcB_N#)C7sDh$TDVc&7N8=hjFz_vO5pS4wO@tpN}$F?T1Sxu8zXeuvO zSR1ZTR9&7PxJ`XX=s>8@PYq_$JW6?mH(gyE`xyWmh+rxEA`;4m)Zwycaj#hN9RT8KNR%O}ioFyPQ*3mlww4?S+?t26I<_$@ByrBP&I&^CDdM9(Zy!$e zUp`SrfDi)0a`lqE9z_GQz4Ei7#93~U6;t+_7hlD!|A~+SlhciYFhiulAV4CsU6T0A zsL~PHyy*U7Vh>tgXmAm!QBC!`Hf+Rsj;!mM;mAQgL&rdq^b`#8SM6i{;apHsN>tYh zxjLtZY>%%0y_)$x%;OF+ZLewc8KQxoKXK>#27RBbU43I*X->uoheb{ z!1j-Cc+hzCOYGK5$3Zh2GYBJVfk`41z;y3{lOG%2^T~UtMiL{KHC@i%ID%xKtKvcH*!iR)=e&&c50T0_|%maTwAvgk`Ec}_Yk-O`Jr)g z7+n>YDSr9Pi00~9pW9!q$TT@922L&x2=xPOHkeR5y@jBgW{p^SwKfI>AK&RxxIm&( zTxU4aw4GW#O|-;dCG?BmX9*)W#AbHY8E*aqwbg-r~v zRa7e&tXZ8^gOc2^Qn49G+I^31?kVUA4%YpZb5<$dAU_lweF}lIQAj&${G1WhZkoK>Gq1MjJ0eh~>VKhV_eQ61tS zwSv0;m%;ptSZpOA?>;psnKY5heM2&Ee4isDiADLT_67fnWCepsu3KS(e|%Um4hR$^ zjzu=qz7!P~o6oU_@4^hP+bi$;E8ZRjy?Z1tz?%;G$Z+Ydl?OZeWGs1?d()(W8UT4~ z2s2hh%dKOWSMQ77oRmFJ;%<@OU9rR&r0j;hcZ>cfo6tM7mt#61_%PW++RI zsprhl)0|nGVzS&WB*DSCuwrLi_=@|Xmnf63diHKL9ZqC7l>`b`l~7hJDxSJ>;OIWK z4G|U-O4N3k`{|xAU9fXTbE64Ks}d@s$n-QQq*7u&?B!+d%%IQv%oJ((ps-O2G*i1J z_pY=OW%yAPtD%}=KMgS*frbC_Vd&k-O{us9VR&f_0)I+fm&eORtLrDs*VNT4T${Eq)#=RANE1N>DG_e2mMtEurob6fvKnG^3UR_PPkKs3hI z5jc3meP3h5PoD!-1>2FB>Sfzpqd^DA(4YY+deano&x; zymEnK0azors@z9|1zWmuYr&DTqZua@7_6T)u*aepjpL5Gb44n{KT)`! zt1Zh2=KdvR*6JYq_SLkE+o;`fy-a)KF0|RMD^B8Y*~I>yvionx776;vbhuiRF=%_%p~7G3 zu!rk#75)$nQxl^+kf8u4W4wdV{cSRrgXnv_Q7|9`%w$f;jh|sQ;yF8Lus*@V7&LqZ zW&QYroQ1e3_&SIJtrxUD?)anJu6*+# zE(i?i#;=})R4L?gvuamB{vv0`XCkbuUy2~`I7g39l%9OH`(TH2!Fvsxjilw}B<_=b z94cteI#OeGmNu2`atERqP&FrLXgeu=;Hm-txvGNZNXV#2ueT;&w5d2gq-VX$@Gbbd zwyVz{f0yl5U-#8GnWaNnQGvZ-BC5@Co+d8ZP7xQQ240BB{1;_tw=fIs#YS+{E)B3< zZuIHCI>Rn>kK|0qhlt!2eb}Z;;OYKz=ORQ$Xioe&{w!bfBF1tHo2KJ`%%*?=oe?BQ zZ@6U@&v{?dGN?pE=Ly4$`XaFS7|kLuG#hwcZC}vNSMJnQz9BtgSNWWnP@QqaO@Tpr z@{0V@OD0?HtaiJ4>llP_%ac{|QI>6<@H6q)uW*3Uz3*KL_FX{!dOO?2=Znq`g}cC` zG7U;b#{Eoh0#HTRii-zoXvw8pvcMr`LtZ*_M;zT0{SiT(#%Ph>1}L zU_co*GG~Ms?_S4m_?h`u`sV&N55AI&#iBFtpbae|Myk6Gf00L&q{Qa8wuleVQbMa~ zknLefT8rL$qs;9yu#Mh6>LWo5?`2Mzg|Chb0|8)(Xd$;!J>DjglC9(YZY_cPpP{aY{gDzb|AX?CR^>_k@zElkS$mmV?V;H?yu4s4108(gY=a5jXqvCG1n+v(i!C;KxMrJgm?s2A6Ezcpo5+m zD0n>E5_8Vn*{tn=90X*_4hP>4kcE2qy3SG*4^bNP+3e-=Ija|E3q0W6ussdu2W3jH zcc7}riOEsK_IKEngDsdXue7K&WFw(phGGg~mmj|3>+jXJDD&5Rh=#|wr7(cPi3CQ- zb-2vZjEc$n*JwpbkOr5g6eQiSKZYZvp%_&aaOc1o@e>;?4?jWB)?+KLcxRy#*g;be zt#0UxLAy7lZd0N4*}M(DOUpKBO+GzqJ6OY4mMvJPejOM2JJTr8@*n*&u{l_zY_0#7 z9F~+zK^l|CtSVr~@V(37RM&kLg)LoqAISn{M-bBIswmh9rVoM~Kha`H-$<FP?Gnq(K!zxWw2eEmCjXTGpzj$e_U0SyGG@YYP07K(`Cj_p;PGa zItU=U@0(%d*J8_r4(Je`0LB@vJ|FunRy+U{IygKRhfl2Q84Q^@_z~d>gu}KdMd03H zF>n5!Aidpt5!HCh0QqHnY^hL!l2R@{mn1JinT7&CDlWG7zc=WDKQyt+{8KY}*}as6 zO>w)M*frnk^F$_Y%Ij*v{1wIus5N62wD@b%&BLKyU=vaw*Di8JhZfP8fLqH{eKvVX zwsil8+w1Z{(3Efl*<2!D2+x~d2Ql2mXwvIE|B}mG1GSD+LD4CmKiNPc^otNsSdNg7 z>&vN&$Ub>t@ii;Po29bncf7qA5IKj6XSv?P1*q`y5)2N^KY3&3e2k+>B|O)#UPzYg zLeUjOyy*o+d>Sg9RjfCHn>&v0I4APBS~o$odng6uiO3;8>59v=(qenrpgGo|C>}aZ z6`#NAjd}StBTbBoL2#vhezanoY&8dOU2HlvD6RC_ss=K>^YF%OopOkLHEI+j-l4BY z1sZq>*$E#ggpu5jm z6hE1)j_#E0yZ>^E!3lU>*UTz4I#Cwj@YgUch60ZS3!%N350yFZ0-Lu5>ovGy4ha<6 zdZclq^DUlxt(y&TSL2A8qI$tvt+&I`!sQ?KmsU;y74xUc#N`PfA;bXkc$;~LkSNk; zjiO3RO_Da(aDTrW7(J3q0DuZdlZyt?w_d<>=>B-jqJt01w~R=zJ{kJ7P<3)|%gpB$ zKbj&C@2tL&X7*^;gp?_jt`BVAezR$czyvA zZ@@FFb%gKwd_KA~@uqlY+$sNXWir|vL~-!X?{Eon#YQNk0GzHYb^gxS_xj!B65QE0 z+3T5e2dA@3Kj=Aw;qT{T*n<~1bzd(_p*9!m%PwV!pK85z&s@kTeJK9kn$K85t08xXgHLfSPya{Wjltc~9x9MS)Y;Qa>!dd4ix5 zjGBz@34gK@zq?Zc7Bqw(JJNytkU}8*2#urA7KRQ4&_@W40KO7Lfmt{Xqub=q5d};# z{v>uG2LWSGaWBt#K`8X-2xG`15P_-wXbeoYnsCZV5KWD9HTDjZ2QYymXNLl4B>;RW zg4e9fj=2|-7*J2~i{Q9dnJqoj40-tsJUd?7!j1*Qzs-fZ%&tM$@ew9d;!s8U76hV2 z6clm*#S~!z`bV@Bk>PX^og%_N<3R)q4kc=5gT&U{=nc;QcS& zES5V#Ycy05W9`^y)!1i0f|B_eq>Dn>ZU%8kpfNvU^1o^-vaqRCH}aK1&=UIK$%OueKWk1cJ7n5hv6!mFILutAjT6Dn1_p zE1PDYUEW_^C6l=V&)FT4J-7uQ)$?J^l>gotd$Mvf6%jNbr(DdXUujEqc?4T(pY8Fa z#BCH-ms5#`ZQ@|B?N3?RuE~c0l9{H_0Lda&|7K6U#yT~yZ4eVALQH>)@V$=ywIY?b zEoiA-i>8jJx3g)tas(OGY`Cd^XgO=?jQ1E(Qn>Nrw9ei3u*8f13+l#xmks!FJE~~s zUv+!#b;Pz=rf@(WP5;q}A3KI-Dc3=nEu!&|*lKD6ML)_S(CgLtYhD|IrI>`iABBZr zO;?StU_hs1m$B%%m&Y+wRnbdyjJ2097&|UPLP}>9r`u?$IIJ}aosmcCrVGL9dW9dj zUo;&t%Jxdm@Qx9?C z7KIA2e`jJ(1plvY2&D3Aqes~9vXTE^nTET8C)3E^=-$3hKf6m6e$A=*D}gJ>0E9>z3dgBH2#v%|re2@xQdOx-XM!MTc%Gy5LYQvYI^)Z*zjUuRKJR_UgMG za9-J>vp$;`$qhdHcTcIY$i3jLvFv^@_vT6K+AaxP3uKs7 zPJF6&Hq*jnbXYn$uo9+pCQJeD^zyXup$Y;wFPJ8Y@4sy1V|EJYmcP;IC8eqB z+$wE%+N1cT*vyCn0FDs?{jpx?tovEjwahWgy-_h(4?NuP1j!-Mu>wT~xKh6iD?zX^+Jv*$ym%aMpSLa}xZL{PHh@me1F z(i3eFl>fDOzTb5GAAFQ_wC*75B4CDy2#gJcfg_mt|RT{;oviz`<$KfV%yQ#>KSwM1&0 z--=@TvFipvx)3&VXY6p&E9;Z@y&ELxnZMIXEq~%m9%;Ev4dyy?6W6`sr*n-hUR}qp z!v1@g9|s#r#mY;r9FFIM!L5F9jDZpM?X<1+xVPIg-HZnG)CEBxMfPjrV9x0~?BAK^ zePKwpd1xRqh*B)|X-czJGU**~`ttHKpK~8)Z#;ZZtLO2yJKcTV7MF{aEL|p$>Fo&l zkpK19FjZc49r|sy8a2 zAMTqI2Z(t=KZ)(L1V9%$*A4Iev@3xIMlnUqg(D1brVlfl=smWSYLa;vp5iK`4oVR< zAX&-+K&X`9OsJ6-me5R%+bQf~e|A8!U|4$0wRW$}N$yYoO_;h7+X7)Ai2@*_tmyF* z3Se;4{LCfAD=*_0M~K+s5nR>c98xjlvqXSO1DOGEai9NCtuGRNiwG8b56Ha1!*PY4 zTsxZUZ~MsH$wbjOG1Kp@PMIjSDSo?vJys9+s6*F@bxosp-XK!lIaPHU-+@L>A1`Sw4DlX2_yx{MHplJ>Y<%i~PZV(9YPEk(7^~ zP8o0ZHkoreV|1&6L|X!>MY`;_Ft)UaPazZ9;BYK>3p;$iT78vw(ZGkg%Dxz#wUN}s zSU*ViR@$17gGmMKv8WW-f1N#YWeF!Yf*>9G=ll`W$M&NLcyXrEA`t%o_KgDsL0v$A z35@6dZ&{xUlPJup{&mTaCK4Lf|PQJ=K=Z(8uQ6|vxwSajW6@%11Fi3ad z8U2y2BhAjhFbZ82yruQN<~Egp>AOW30u6^yNtCEbK|*WcA0fLi57r$~75ES@6@L1F zMJsE{E$lJp@GB$#E&*rXzW+phYm2|{U0FLH-N}Lu&KI-3rCVk*TWhTWO)YU|&x(m4 zMg-(Y(v-sxDg_eLIlhuv>f9~>{DqlEi`=|zrgfU(qfjESzXHvV+E&6U#7;s4px$y% z^Fo=YS%8qWSA9E~$TEhMGK7NWL#2HDjAa57ZaoZ*{LO@i7*owKol=Cm%$HCR;~c^oH6%0Itahn4;w z{6`tv6-K(re3HVk`AGw}%J^eWR3RZwbX%i-(WOf`n}tw{@np6?5o8&(DWm|h7@96; zv2@^IL$(EyZ(6Ikx(wR9%{WH9!*aQVjZ2;2Ns%v7R&w0!E2cL)H3BZ9}nI-v;KidPSH>qN8>kE)K>?(jqrX}%XkGfphmyn#WhK<;m~2C&I&&-Jplf>?9a*jpYR`@HoGc&!Dvh=A&6 zo#cD?!&xh^?v)+Hdu+d1x54qJbQoL1Cz@+OXD9z20@;x3#mPo{z$eqqC|i! zm1VORf`V*9c}in#Dw0;+^92B=TJEDbU&=@Jl`dpWVd}Y=EM@lVW5`T(dHT+;sQ#4$ ziXc`v%8v(rYK0(e(C}4u(e7%Y$MsG*69Og$5~)B?i11q}*&!sJ&>4d=B>>2uF%eu< zLGe9v#o|8oEbXRZS1*c^eQ^cwh!*=F+ei>%$Q*-2DbXlC=g>kVNv04t!jywy@PR}^BjY6Q^A%4h)(By_JTi9bJHg31&LlqerbB9w2% zBgpcU3HCn=&fbQ`k>HI%@*63h6i=^pLQlvYQUGBBi3X`kA!ZAw=PG{iOGqNNM`(c2 z9uPq56ix~eAPNDbs7O8# z1a&{RO8Mbc*H9mV-{~o5VEs2x@HGl&Gx}Qz^6&=GF9rxzPtF`8duFaCZ*5_`SU`Ka zrw4$2LK{M>{P&_H2nnvC!GpyR{w7FPNK$=FR(-2IM;=z-lg0o zwnPFz8WJ{yWDN_CtT}e5Gz~X#t4C)X&v6;O1ihLAeD-x<;<+7{S+hYXGE@4a0@;Ss zrsHB&AAw-S!*dx8lHtHmH#s`*A0J-oTr&CCy(9%R+^{OAgi!0%5<0~|_LvFh=Q5>^ z6B9#<)d#f9>k%Qw_{x=%5gbgbdtR#r{x%=`%M$Y*Wdi53@Q_drrc7Gl0|a{Bo*NUZ zg>`#qQLr6|U*)hROtr8Hx#wP|EP^k+Rg;&m_$|1BK%;RNS53;zX;);VJ)mVGL~p22 znp!J5kc|efXc@o?G*TLZNPqi3K5Q-VLdE*rQ3Vg=bo<*slT~gXdX* zTL_`;R0=M0(!@zQFY`jyr_qy{HBlf+5G8~Lc{jm{%LH3v7W-ydl(J@kFjuE4H3oW9 zx@tfGN9J0o?TB9k@`&%mLoOs?&I*{BTx@oFWa)=wMK)frJgCNhMah>I3Ix%<_lt|b z92~;Hf{J3#@@uB5aRA_amn!#?*F(T%rz@_Rq3N)mk3Hk6*6Vkx*tZKWw=|$-uY2JV zU@!s=nIw__Ka%GJO#P?nzmD-oS-ko!jj8yupA$2ET?bP09EVN5b!X$KWkV;5C>o!T z?>BzDAEkNY)OdYoUJG<;HHs!XRq-@fDfMLy+TWwK#%u-gjs9S;q`qGuJ+5_!mia+(cP*7$H`7V0D>?fIY?HG7W;@O6bJ6Ux z+*CK*NP_vVZ_>9ozKaH@Q5@M&kRUWxR$q^#OpGp`5$`OzYZi=@uLbat0H>noO(6-o zrlpVabU0pqMaDbtR)Gw3;SWfFaeLTSFbCfPaJ3mwqn~;lG$M%0EcsOgR|Wu3ZCMc$ zy~|EFoWt5{w?3W9jW=Ic%*9ONWU8D}-TC$Ua9e z#@UpY-ya)FxyqVn1NP1ql93Gb;!Wwi3*Wih>8}Dwtw)v*Md_cOLQsb`+hW09JK_BnaTEID3@o;BQ(64IdX8i=!E0?GblgZSa+G&J& z6AL`qx(jyyA4*NcZfFW?NY6A>TsX+Jm`rzxtvjc>j%~4VBY$QT2{B0xiq&OD3>&fM zaFlnd61Yx2eim~k|Gv?=@Vsw7mVlt5p;XrDX;Q`IiV6~Sb<}5CbeC8(|=#xm8(;7YUI+x5YuQp1@N6mgg;I zW^m_)O$~Jm160X2D}-gp z91V5FgNQ-qOHI@vyhdz0zF?-n$AEwa2OsO_sN3g?<8wVoy6nwLYW|}zt}P|Ke=g`W zF~DpR_`Uew8&1Xl-dCkJfA1-UDIp`5Vq*VtiKz+%Y<583FReenIjN~V6YUxBNAGs& z3n0S1MvCqwH`OXR4BPx~MPLATP0QVi=C$Mct8#r|Yn;3Nr)K1jR_lBf=-f5vo6!+J zdB@y;;lBAnJ>N%)Cz;_@R+N;rEwT~&>Y*1w{&Op(=FC?M@coi668^4sX~YERnX&p7 zWP#-)tEVn{k$wzm;C!vDy}h@Gi{;FoBNJ5T?S zN|-1GFWnC%TJ@HRb^d!O3`C=3{LXv-z~V-Oo#L)v&#t?!C9{);V5=UMabtH796k4$ z%FRLqH%m+#gZ%fZc10CL&FXD;n7mgVC$g=mBmT7J+)A0qVXZetoI8v!C#S>p+I>>$ zE6C!JtMOcBeP+`%3ywDs@U3jt1L-Z>axKgo+@k8Q#vxF1T3Q|8Vt6g zpx$?Q>6|iVbRwCR0zr|VcR@+`Lx$eA^XkuKRij6{*id}2ETy+hJfB> zte$5Q5@%ntHQFz-yp8hDf5N&NS}xE+Ttj#QBo0W9K@n#le?9yyU8Jz~RvQTh$E-&- zr-7QexW!eZW{ug5mKahswGh~{^VQC@CsCxwkEV8~#X$5&d;CclWF_$@YcM0ZOm zU#-dL_6_-sQdp@(?_05l6u&#;~;l|eA=vr<*Jd6v=JotwjIvv$tdEI&(N0s9oUm7g#E_Ju|~ z4CMeoD(NJK9z>S66>?&7v^p-9H{89N3D>x$cwfrx@d?%2(MejNL3UdlX?IAKN@@2` zct+SM-eLLr{Ey$X>3=qNt5$>9ic{rC?rTW0_Qre2jf|3a#4ITA4w+PPivb6}-z?7J z>~u3Lq`D`Xf)^64(N`A!nLLJD+eMj-kKEQj{e$Apiaz}}`s#HXEdDcpY=e<}OSto~ zy)BTh+LC#Cb7y@VtD}!CyB8RIhIy2(GD)+fPl-CXj>3uQ{b{w(kQk1YN~P;bCEW3y zEw)Pp9|dnUWEPmA)2zjPN6}7h-tfj>wNxa;vmk?!9{4Qb2)0gc>|plSD1%IFrXGPc z0fy;Zsh^0@2`PXe0U3Z`FC#y)`vkck-+2T8`O07x|BBxxmYI&(jZ&iQG-<}m+uwm( zUtw^{Y^u+p0ceaS5#Ubv0A3b=slBHw_PH8v%7X?MpnM~to!99J{RjX}2OvfbK6H}$f5v4%!(wTfhl@t|&mnCX)p6!Kx{vdv zMB(@L&RcNmEr&0ngR(V}y>;mALKGQ-oI33*7}6sMQ#n3Ti4nw99&IwCMHHoIfSpo< z;88qywLm+i66|lj<@^z?IUW+^%53^_n8n5DQ8aC`e(n$JkCmg%R#zP5uHK)!nT(OhjK zk!1yrV!0xlA^;m1nxIA$0;gkCkQTLyB@D=blz8>AwxJ{l0%Sm^b-fh)qiI_=x{UKp zXl^9X_INEN!v#TfH|7L^Odw&7tbiv40P7kd7)ye@5Hz^88!|4$An6}zo!Zp^kRcM< zQ~ed?9@6%G_ca4An^g0W{*a08C^JO{7!(3PcuffN=9A2aN%Cc0qwuTGL*st;Y^%p{KE5k-fgcm<6_tiXulJ7@?vK#ft95e&(dLZDwt})`{cCI@`g7QIK>Js+ zF@Mp=_~bb=mq|hgeAx>(#9!Bf7wt~spjE$9o@7X96cbMGJ^n9^g1%M$c z4ooM58JLV7PVgk2rFmr-99QaK8tZYRb>Ew9dpESV+3Qu^lW(+b*R77ZG@T1Wuc=Wj zAPHQf{vTKOa;*rooE{C6e$u8SfiQ!xGGRiW^@a(tQl{v3t@G-RfcT#A8Vo?3rcI={ zisZ$c>1OeQWFo^VRy!KpJ}$-Y4z5}3*>Dn9IEX{|NM2RhcvY})MhIp0jMnChCjW}a zw}en?h5^>6p@)x#)8g#CW2`Vi*DZLg=h(Jw+cuwL+qP}nwr$(CZTp=2++XwM{+Tb6 zJCn)GyHe?0-KkWiS9a2=-MiMlCVVM03cVwM*!tdnIfFp_^Y>l6mK(Vo*voH^_tmo5 zoai8znODfGK)fmj(IinN*bi#WioIN3*vLqyFn+y{jQT<%MF9O?r4O!ZbNjEVdx1B6 zimzDPT(BU}@IWcAl+7HoX`DpHQ~7496W&;d0cxX41&zZ`*sALoX6>z6Saw+K(D-O4wVah2}Nsr0FzAB z1P`eCYRr0_ftPm@|M~c2_V?9<7c_YfHNWH@JrS zUvRQU*_jp=^zy~$k`r2?@+{*V6Ilc621>6n+lyv1(T^f>ds`5TZR{Fsg| z_$t&DU<;6rPBS`x*ZnfQ*gqp?pi15cxdU0|Ub9&aa;Znif713L$J0(#!eu>b>%bw< z3*gneDUJJ-4+cQu`d%7goi#aCB3iZRvK`ZPfs(h0RV=(*<)0xN4HQdQ9Ivbxq1ip<@ zT0EWi8Z_9Y$D-`g)DE%aEF4Z~L~S)_&>@qIcDZFAgNLK6mU*otcz!nHdM)p~WHh@v zN=OLH>$w6bKbBAMUr@~$CMi9IKP#+$Omvg@;upaQ{kHU@cTbw7O~FxT8&x4iu3!+c zRop|Tw6tiyonAG+$kmV*;~UNqFdtv5z{6hFBWmnr3g?n-xohjG6Po=bN(At`^yk(4 zD6>2n7YKi^q9-AvI9l?ssK)O1n~sWPHd?jV&znovgx6|Yy-Bpl8s-H>_*G?FIR)*1=DzuDYx{VIKP`rqyz#3Nh7_*N>2+(M z28k#)w`yiT$)V#O@w($L`(M{!yQ)$d%e$S;ENPneKKUZlyWwaqnXq(v%ZTKTauhTR zQ#?n>lfc1m_;5-%Y9x>;{gY1IR9%*yto!r+*^Pi!71qSlfi}~dpB(5rCo=Cu6MbH` zSjoa#mlga^>P5gMI7F2AC>Sp=3SLe1nYM;HA^>V$V4|Id3#ie@H9iw^dXS*zcLrve zDG|AQqPvWH`Yy z&fhugUC#loFdrE#m(8g6;?Rm$>?8f=FDe2xRRYek3@VAxdm*+4l2*W^bQE33!m2G$ zWNLpkqFJ6Li9i`8PI^=xd?8y~&nzUw?Xs6?Muuv56(}|6!juW`Es6h=3AVhm#F%^b z1fiSqvUBv~50^D8BhIteOXv#_jg&C&0`>)^u+O?1(QV?6dABT$qyCb@s1Yi5k`>W? zZ(?&q`4Ju#BxtnF3(tlI1T&1hBW~1VYzTKtmRc!Q-=tfUg%;=kCk$AH|Lp|$vL)rd=4YNH1a1N=FZoS zxr8;Se~t)PLXw~9a7PES&^7c6HSt-qj}FS81Tc06lYQ!~YnNT@{b@9>xB}>?__*-m z969t*xqX6!zBpO&Xc_oF-FAHzMmEOtVn(P4lLc$+_J-5oh={5V!VrSFA>^3v z$FjGX)`cj;p}E7I=^c$L^ed&Zo+rCEGOhy=p+ted-2iiM&f9kA+O@*=L;lUsTcIrqA8?g!w-54Abz5Xr^1&%x* zhfcuUY?|vb{qxG_8_Li@&U8iOSBq-ukcYN1p!mns%Ggv9KYU6?&?6p8Z{k$vP#^^< zj8~srHlIEzjw zfY`b(M)k!k8NLcDbvdd~T+B-kySflCtQ%_D&)Eb7l4m|h*- zoVtY7ymKGCIQ)gobY6W-z_2P0uLFWK`EZe1t5$zH7qtGxTMavX>54UN&&H=1)_uEa z=bz`aySaNo(BXziq+jWza)`2&ghI9A)^i#7`&%=qQCvcI)Blon*WWa87enkaP6QG> zptOLW2LpS9VZozrWr7HZgRf3bDz@jQA2eeu{o*`!%!eVQAc?RgPN}qKx0;bl|3peH zcMTZq*?k^P{8J4i_H(!J=}#L7Pb@6^YYhXc-?z$Xds#m9ngfYX-GuCI@~Wev)U}Te z>w{=o9BT)u0pz&2(?}{xefM%{M^yRKZJ-x*BEULDta<(%?7V29Xxo`M&z| z#BG8i#(E9O(F;&?1s#0);Ju1xyoN@fi$lU8kRLw>RZ^wlBtP1lH5O~<*>s)o(vj7< zg8BigmzC?MOMYTqTFyyRzyd(0o(x{7?^cWLE4O1}bIYZ1U2g)*s=-PpqZ-TU<-mI7 zlkie70pQ`)0Bs1I2g48dmX!63NR0RpmQ?*DDa;GyU|IxaIF8n z179GFziLo zDcXbQ>^A`cw<7)v0``zRFMT8kUE|tQwsQ&{y6%lqabv~O3y#-msQXOa8r~>6?y(Tk z!|!zQgbx?zUsAjd95vi!1~Pewy?Eg?NWbn?B9CfAujFr`rTORgDk3a_4DfH@vPu~% zB7lzyP1jA*Q(Te2__605eQcm%*H{A!h}XFDccm-Q;aVZktGft$R%N(8m$i3c<&saR$F^w)7ZW+36XgTTDMoZC;oek za=6Fh0R$4+KX`|Sq90THtxF*=06vch!{bS-k;FU9e!DjU1?Lu0~^FFBQf_;ZwKkUn&M+1fs% zEvIg8S@xL&2-Qpf=-*s9!OBLJIG@@!=f2`V)o;NwsGZe(3hI&gfL zh5Ug9wwOCSKZ|rQQ+j%>h$-&Ws+3_^+&I5UpwIJTf1n}(QIJ;EP*gT=+N5KtFmfAV zba$}#w`KPt`IMke#}eIX?C|VVBa0Ft>{i))BLT+}KT|@?pD~>4etwiCr5Kef0fv?5 zeFzJKS9mL)=zw>XMM~;7Ffwj^jfZxW=qk1AKcKMbnIp* zE46QBFYyX~$_zIIwZpIT<(pqrnFaClQAU&x;QFCWd{X@)PB8KIjZpr2fM=PL_$T?< ziFr{jnMwEV(PFFbhSwpjh85wa*P&nN1ryMJ*|FI86VBgM{PfXyC|gaQnHX!mY%dY^ z?8BarS!TjJNghAOu~p*BnU~+e&X@cSrYL+<`; za&EbtdE6EKk%6Fa#+>aqcEaK4aS)vfwaMzm1*R8%8v-zTWWX04mPeSw-QOFIqPwv{ z^b>}RO~S?E>}7dZM$hW{YCLVo5$vdvU-&#eMnq_D=a-zR_~7yp9@ zxokA(inHnwTgDp!sf)40pu{JJbNdidWJ3)7r_m(zUkTX4j|Bth<&0S8782bKhk>^Y zLF@%iK)ns4j2^pt%liwx_ZLr}ohq4yz`d5UmnNKCDKn>9-e&7FV9v&Jux73o&_o2-u2Y z+6ZMFgIVpPmQOT!UtG$^Sw)PX3&1#XQ6bB_TWh+XDo|kDke)TPI=@R>X4u_+pnek~g?$bh%%A{cy!eSCc~agi)`i(h>ym56r67>nUG zHSiI?iUm+Wddjq&bfw52z^U+7-K8Z2M8I^R^7JFzKNRcy1Coy+@~rqcJ*WHkCo8Ak z1Ls8fJ{7F8{}9YK;RuK~+?lo{qXSbjAL@W8_ELUxDeT~DM#$5%vx_DSk16-YWp<#j zpvMU-)NM^B@AL{ctlYI%s7CO-)C^?M`a0i_?wwIx8t)dT`wy1M+MToeTY`y$@;+KU z>BZ__l{9!_jnwR0&w`v@C#ba}M#?|(8XEHI(<*i%ehq#&aU z0deBDoJ(0``>tXX`m-UFjg4c1I!&Qqrq6bK8TDBK{<>P5-L|7ODba4>&jVw z2!2EYur2~e*1K3?F79Ddl&T5Z<)1eii)#v$U89vY9ZiWtz&uKa@&f8Yyt-WH{D@$L zx>~tnf-!Tjg2-l&Rq{^Ri_&q#GLX(P1Y|J6Sv|k^)sE-0qF*<*UK?GKl*j|4^}3xm zz+VGo04|Nm!(es^Y}_P;rl#~xuoQaOPntg=Y6b^6J$CoN*O9wdSuC6+Y@^=15=)l< z*@8kB40v(7F&>?o%GX7c6@cP(2zPAYrC2#~+_d3@<_EnO{TEEE#_21nRqY_2SpNG= zA-R-Y1bJj@S)n;Kj*~{@E4*ouadi#h2k|AB0aIo6Y;r&o4s0MbKn6hwZub&3b6D z4m8%^Oenw4lhy8Y9vxCkNeH7l)ce<9m{*ERhD6k)a9vFO&Hw`UB&EqchXku2!%U+P z&Xei;?Yu7JL>HGA6dYtfY%~{o#Y{u0L;5Nv6Ya5$4yCW$XYih={(Y2QggSRrJI)ye z-QSpwx;K4sxxtgeX0m2?io&|U2#)NbaZrwzkOl+&9Et=k6iGneUyj)?Txa{O2 zGM<{kkAnH;{>95=)Uc_M<6t5t0-p}>LZ!$86{UqFXfaCR@#^tPwy0+U%bfj{p(HgW z9#OaiEIq^kQbPg*wDuth{|p}Z)};lVPS{Yq4TZTNe1sga)l3%w+ko{LJkP(jfP6lu z)L77M3>ej>$OIy$s;oJV=HVfqO%vrHsVnYs8B8Fb>xhZR|WR2_0DGGt~W&# zfR%A}#9kALAI}~nv{7KKd@6$om9-@+N2k}vk=YX~WX^P=OOM1D%?cq4MdFn61J8Ws zuefSJK&$ojW}aL|>&au_sR+Kuud+uBszK(M-+{Wbl26@!PaZR#BrA7r5L8?puo!36 z&3XY^aV1*UMH4TaFkIy^QhLJe-i96yPJ2=K= zC!CrN7TVshom;SHcMU*vcZq&xyay9Bw{;m^8pSE|#M%|^P(FI!^y;8fros&Yea~AZZa|ZX%7bLuS$44h-&ZAYC zhuN59pMFx0hU>2<4J|_N(`Kg7z^!k(;Yfs8z^-n8Q9{BIy7JoaVQCeN#B-M9eUnpa z+b{6E^E>}}bo}vV+04I8)epiqyPKi%%sMB>&Pwt|<2|gQV{0X>=>0ru^_uSS{MGT251sy^ZE_m$-w9%S$Wiyvf`Cx6#y zL?IuDih4sJlH8dD?Rf30GrqrOd8LuBIRXd)imiKti-`O@MJ(X1tG4T~ab1kNXTutZ z#DBco?M%@j2f?t;&ukv~zw}1{RcD?Wxe|yT=<#wT^Mda>tT4>g7J?T=nx5>JEG?HLDA?yQXVi6sWZq7NHTzMb)cnuy3;B33o*| zxzBU7Snz{WIun@W9LCybefi(zJ_F}2d2CY8-Oj^kM z*bpxtL38}uZE1X0*2MGoE~wz1Bs>}1vZSd729y5Gpiz`#ep|SwY#w4NL_+TCe_{WAeh#FBkW-LU{s~ZjUQ>PL`cqqUvW7q$C4g zY;K%O~UakY}bd63p zTrcW0d&x(eH*m+v*>}T4+)k_lU8LR;G0c_fvrm>{#BB&`baZx#2D{XLY|6%j51|1B zDDVv^FBk+8xUCtp+@N3Y0VP@zx@h?daC!0Qw77=FB>HdbC;-qbNs2(Z3)QC1%l@tU zr^xom+fek9GFOG^C=p}JaSO(<8uKpi5VA$*i z-ZmaUpkM(05QHpbF-jPo6E{A;JuVTnh~8~mG%eSmetxwOx;{;A+Z5?=8lWE2`%L+N zu2;a7d!!M(wEdLq7tX$a?p373+ut*bj3sZ;{u8t?uA-ToUmHEL_)RjYUx=kz&Jp_}=wr zQNMc!*1X6zqcKFD__V42mzAeZyvMMA_|%8Fq_1c;8I3hhWx~^SMHw5lJqoT%fX$lP z#p;-)<<2kCDwac?shEw)Ie}YOOeuBCO8&fYJ?XoS?rW2_1EB*@EX@K445G~0n{fEV zcX&$Ek4Ss*f)N|p)8j#l4zRw5qLZTfQl8+`qZRDKxqAM_h`wnFc2~~tZ36!qVfw{8 zm&$QQGhg|=7%)(SK%(fX6c(ZYgao^WT$cq}fbA=al^~CmSbH{>OVz}t7QMTH|~HT-v=B%#H`Y7&2vo2t5KA z0AvW46pa;9E1ry&&$P|m43z@ztXmaF$?sRcV}p!woRP>xWP(NDBAS zclGsAtl=92+6U@Ahm$*B!WK8@42dcggg z)0|%221Cgt;E>z<*L_t0$h3<$*#?ncanG*eM|x$u8@*GJntjTNS%FG@)S{z6i*Vc-8iJ3D9QAGHsR8eaC}1W#Wf_g5>Zaw;1;_G-~pb7 zUqCYjhB1tGjC-DCXNkb&W-zUkwB9Gc`%ZbjsXZqXu>N?=$;u~UiO=bxQTZA0jAV41 z75bRCiF8lofhjfH8^vG6MG*>kK-967>tEFNz$1!J{Jlf83Kzh(09$iE^OZ=5GxotG z{Sag5j97%sTldbgNGcQ!(f8I&`$_@_J3;ynHM_iplcSp1#r$D}0H;xHj}yL^9|3|I zjT6a_)NFU0cXssIp$L6cl<5zYLEP|UAV9+5xI*6KJmBS^8@}bO!~D>dIZn!FxIi)lHo&96>PndDE5w1XT8gIMIfM%aSJdQsn)gh_WE| z0>W!RTuG#4@nI-H;!ltDd`A|kW>S`zt|Qi$2H(tJ05b+@g^TgIsXyuS`7#zKc|bXr zSRhAHZDWd;n)~C}8i_3cka5SY1*aBti=X(Yw1L8l@FDlBQXKVr(%w%;JqN4Get*4c zR)`#@oO$L}TN^CXc&DFx4gmm@U4u$aba`JS=6jO$WptQzbKAk+cP!iLUdRS+hTMm7 z7r$cPr|!hp`CXp6w)Y6W)Xp$$N6K}euigvdw2{+Zm+Nbif`T}Nx_ibL$8(}Xx6a`` zJ-LH|;1$=weP?aUz$5s6evF-5XYFH#D%iM(cCX|>_9a)W0tR&Z>%J(l_j|6;{v{pk z7-hvPv&d~r@7~WZgco~la1x{cnTp%q9SIRkZr!@hY)jZ4HhN7D1+YtX58S)=zMj^6q%T=RDP`^X?sS0Ksxe^+V4-o z*>1Qjxdhu%dm3T-Fv`R`w&0wk^*-eD@V=TT{!3G76O%8q_6sld2N)*bWP?}j z_wSV*k!mfGzp6HC`gM*0U)va~!;J9f-KFPd_C8=BXo^npD4n)NuB<{??Vb?W35(@O zr*yC$;@JciQaC(-t_&$mY*M%<7_C?17tDPQBB*fC_Ul9uLOt?4u3elu5HStSbFX#Z zRVQ4$>s58E!D~01#vhkXSUo0&s$l%a`y>%`t03x-TB`m_oh6)QW)~z+E2f20=!no% z975f%ur*I#YAr`dIaWckVTwataUjs^dVR3@3vKiIe?mddhJq?}HQ2tR+0 zmT&%@D}QsHfjJ3@R2h=zckp~3QNCc61kravSWR%PF;jGu3)pXh3Cr}}$TzZ*oegEK z%_tvoA(At&UNDO-jhslE$y^$l4&-DFE;Qc&3?dnKHhHsL6UkSwZK+MBW)DcuI=@3H z>dBB{94DW<)XIClMgOU|nOqBq1X;05ypL{^89zcGPx*dv~8J3Ki!S zC0i?ZXXXjz2$0r2teV0p8wJ$`tae(+IA#LFdCf%R^E#q8gS&sg3|n4|iTI2Oe|r23 zWyJa8{Hy~H=pT~+`3LuqV8u)X5=J~4A?5r?LrColbYNa2<(8WlYnevt0y)9}(pf;h zKar&G?9M}sJ%F92O+F}}zk1ofHTpzliPiP3oYyPi*(}*kVf$v$S$~wj9kgcQEkX;V zB^JqeT&2++T_Eqf#=Q`YrB^m5dA={VdGH;s>Pd$s1Fi$+&I@L*6B^6(P{R21g+7Vd z{|J+4av0Rx)~tqGMAYy(QK)!$1vz+H+Ps%|VTyQM815o*Uib87O;L9jJZ_VfGcc@> z@$`*j+u;nSE}URzV~l`+eqQE%IDye)p0Q*Sw8n!V;R*AP6tQM`I(E*-9}-Ns#eu8n z@m943X3R;E6h2u%F#jfZAE}&7jQ)ME!|&Z`E^^aP?wrbA{&E@qB?~YlgyZ`&<~x(K zbQP^$sX2F{1mQV2@PRMhi=%O(xh`uy2k(`-)%dCI_sdBiim5cnh+eYXcWv-djMT3D z14hCjqaXJb?n1Q4kPHqtcL~IhnI0cC&7TKGbxW?}E-JEHGHWqYhIW+sVGLKnHbH`$ z3tuU}{njjYSRhcyj7;{KkvP#aMb{Txz2dS@IY|z+&S&N-)Cma;6tDo~3M_zK8v-%==hL$*BA8`ta&)x#uV8%Y9BT4`-(sWK>zlJI87rVoA}P$W9MFT|=cnj$cn6xe+rCsO|))qM1#-Nf#EPurr3L+4fz__`8HK^0 z+R5`oBqemaX(7)s@-uW6(?q2w7?dUCwn8&obz5n)BH8#<3M^C2I~rOD85d~|Jq)Fb zXpv)}=MAJg(R;^%3hsUW=1SC~ivAeWnjob7APF@W#wVGB!A{#09DCwWqnbYt6kzS< z_=lRk{Dw*xlXHHVfCFwAYJ#3+2%6Z*O#w{>3An_8n1yZ7*}^m>PtL=^UN4(No@<_? zqbowBcrM&;+2lZ)srF*ndcNL`2eC-!$!K}f1@=+vu{?z0`|5YV=lg}d6Z=^5H?WqI zttZ@w4gX6ARHkahid%>Cx~FocX%rKxx0{eW(HHujx`jqWt2RSb^ z>62WZn}3BNX2#5|Y%zp?5r-i6)Ypr>*ca*ba;T4Zb~A{b?D10k3A@W9NNLS^&9_Ve z+p0~$ju1?XW066e_qiM<<;*~xIRAYkc=)Fn!_O7SR#c{uK|0D1*%0llHIgz|GKm=E zCZusDKthkoXP9R!zJ(dc;eHj6W9K{S^z_n6nx!QeYQ1y5(>ZwNkm%5ub@rQ|X3P^0 zo-CSVg63`fV@-ps6TKw1SY3+3hz^e3va)#`)}OHhr{@+|t?anu6?4!M(}<|m^jfGj z;dH_?xCg4I43M0r2m0>^_p4*e+}u1u{(a?Cqkxe4WpqIQAxpxzP*cJK zI}T5=&$@l3nY&_#=G>QMk%kCScUtqY1x6^+4>|isGM>HEpo7CG%UJ#EzP0 zc&b9}^&$*#N)MjlCv9I5hve#AD-ac~`63gDr!K0M&&bnP^Jwr+0YN*KP+nsLZHjZ+ z1MiJpN$%&Ov>VWYwyP-+%o#riM}KcarijRLQc0{Tu-c z2g2`cZUf~>sWI_~NlE)|zTMd$DqE-N`jv_w@}_{ToiTCH;GS1?n8}nVKYjTR<8_!2 z)n^1?)dl2opr~pskn6yZ9cZMYTd^s1NjBy;aTzcWw3B?Po+sl#$$fruf;f_L4j0C*5nn`QRWb>I3!Q6)(336Uy_%UQz{rlpye(43 zMeO#VRH3y@Etew|8XOx_ljfg6{gCYg5)5o-#qHbh<27je8VT5->~AO@Y7iNgB~{vK$ED^Kxhsd%5u zaDTE`;7NHC7J3~OKC@eC3cS+cXaix<+Iw=HX>uD&pUYk^cwXQRhK*Ufxa==en~;in z%;rG=QQ5cY3=!Ktt`&Bq`(jQ7P*}w?QYI(zQw|R@jm>;N@V9e$LS&eKC9oBi?ltYK z;2Dsd)cf+4d1WEw=P*L0_-Xyv2ImR<;Md3_f&mo8wG9dau81k;R~Y5)0hj^2xA2Qog62@lxGQIIxB=y2uiYs0tXZ4)umg{Xrge)Ksj>0g2neF!W2~e1CPK#C_Fs@VMyCuAsOb-Ql5bC z0I@uUf(rm)bNo~n42sJs8Re}y>2u7#O=3qfxWYXm#(-QHjKd(7%93lIze^5&R=4G@ zIn@i(RFV{kFA@+t^e0}DK_AXc>`C6&2rRlW={lMKe_>^8K-rAv3a-RQ%qqk~nGeA$ zPN9wUCh@@5DQ-#(T}YGVN~;3;4H%GBJ>f;TM<1*&cpFjy&5p>5f$;e@h#oTpo zmZVC{p&lJKJg!t&mlsNTyeYM{Cby=;Q(X-_r9-*KB~!CfDG)(56;uVt0Rm}x3N%#X z6lT_yRcznX^{SR`)-j&da1ysSyMtd)=6JVg@1-`DdDGV}D6Oj-5ht#AU}dn1*`VOZ zT`xLrIjcZKdGY~(wQ_G-Cz(Pfb)$Pvx7n3iSc7Bix&s}|9a%k#EJa>MIE5S1P0&>g z`fjW@ETzr?z~0*0-mn|UYglkJ`&_awuG;nsLC?438h~N^#LaEtvYT4@jX{5q3E?Q+ zk2|e@N(QoDF{+fVI5ti3cfIc6xZJx=)LO}Hcr?zfb_{b{&uz7@Uak@Zi+0Q3LVAWE z;>}B^t=m>XH&2Vs<}$22=U}Cm1Lxe6c1}oY^U=Q~_kSkRq0j`<6a`lJ$mfTCSY|RS zHl7eYDzI|XPiO)=QRsd#5!2S6b&)solq%^{1#MBOdTdd#0dme4C_a?^fV%f}On{yC zlzmmis>_QB-y5vW9d7JpH>-a7$NOOXD)TNLLf>^W;GDp~$+nDiCguUF#lC{rMP$oX zi?gTZ1R*d)R@;{GXG)D`l-Wr=a%|P70;eH`CqS8|BlHkYk1En51$>WzcGNBJSTzj=8fd(YO&EnGn!@H3mQ?Y} zK9Y^4Xo)Z`)y3D+YLGVA*_(`kWzKAN7DMkuzw0^#3&@Y_e^yu>DOrP@!ce=!T(S+U z4lJuA{6`N{a*ZFHjY_sKTZ?~F`7L(0!+m6B|B#6sv1a6X`ZknT1}P-ZpY$1kdmOs} z5`4`3BKbQmSUnUYH{7ojGc=GIGp$D>zZQGsbCwU#ocY_WSAl(&MZx^4d|*j9oV;$w zHwHxgXa&|rcIR-Gw6K;8=3Pb2#Z)Mnc3k;p9u5YfY()ZTo{$L z{rfs=zVI)+ThKZ+Lv;TssN2R-Dx}R5773#S!Fv-hy15_9j4T{4>=(N);Mv5wJFD+h zt1vk3fquZQTG>dR50~!!#?T6a2|0jltbOUdrI`UR0aZvTEx%{NgvzDWKZchn;TJzV5rB{OFZnZ%ZOltq+d!z{QQ6EhcFr4j z?-7+hSp0fS1hJ+K{6c!c0eipMeQP@3qYDLt{2L&EddPRqfy1(Hd~XlVJxK?a*jxC1 z*!nRElFxcBMBxAro<|!i%=OC3wtLp4)AvgehoJfKm`yDK1mbDbl6pbvKrE4YBxLV^ ze_M4lkLl?$TJ z{4|kN3VD`WqDUAb2RA*|NNo&QGd+Yh_?>Cx2=rObWAp4GE{j+64u0jFh9 zhK!`Yr23fGOGpobkrabMzzawgoI~AHKCXo9ER>yqRc;3N-)@5oE4ftR60rn3nkiKM zy^2Dn)y{MSby2Jp$zQv+OTHPd>6FW!sF_oy-a63kwSijg)?ToaeLIm)4+jJYhb+)- zIL9Y=J^6LpMKk2NtT2Z1c3Ck^+~_?oJzLK<4gFEy7R3nx&t`~;~`KmI^i@x;bsIYpmCBQF<8wL>1jamm(MFa_5yL`FubEAqtWo>BDIipP52=g$V&|*a!Wr|yl0M2W#<8(Yl zmiUq3%g+7Ga>Uo+-u1h3>eSqbz25)wq2c2;f=^fgAmH;6?hc*^#FZB1Le`!1a}xxB z1ior6J(!Z_`|}z(ap0G|Nu8wp^lR;`;Vkd?*~c1lBtmyHX;^=1p1|x~wZV77unC9O ziQ1PHXZ#rCk}4&j+_2#eB@8%21Vy^0P%|o6{QBEf;t{wpfVZ^ZVcut#5f9+Vc5$ z>h0`0tAInqtc{yge6jJUjK<--NEP23IZgEl8-bPTfy&VG+e!N`v0COtgv+{oi%hbe z3k?dYur}Y!(Iu1fHfL|OQmH@uh!yJN$?AsJK0UD;>fG3r(M@`hV`d-SOm-$?(5|y& zG|q>QP+mj+muV!nimswCGBs%i+?#5L<}i9*BR#0nz}pPFW_zGfRDsG)K7Ia1rd5$R zh7+kgrP@=4LM4;at0QGh;;CG*Ry_sMDy8}_{UG%J3tPRxa3m_V`hUaJ2_XCj|6$<& z_YB~nhy7nfY!Bi83St5XLHMBmT>itsfAs@w`_cbK+nxGv4g2p*HR^K>NLysY)5uEd z75jWy)W>$>B%kvo7js*)$eyTHyyzRtp2g%85Z#6hzW#CJS;5KjYEWKpWji(uwBxTc z_9Ht=wzDaNb$w}us2>&$BB@}A2%HQ&X@n902M z%n5lzJJaU>gs&L7&TrO2zc11jeyDE{7wVa2MTgLIn-LF5u=Rr>PqFk`JCP6asq!kx zdAp6)G}_@(B+|Q-9XOiPXrV_M5%~MBAV4`9G4P>ajCH~GRmZ(ut$X|A;uL+Ed5@xX zfqXg8vWw+7P{RPCvI#)Y&*{XYGcGGJ_q!$Nz?T~Gq?w#mRCr` zv7j4?IPRQJ_WbNyy!-8A4hZI~%o1iqIMg&7y4lKJTvDDJ%iyAnq`)z@C1Ix+KmcJ@ zmox-;QW>>sA)&9|u^>H<6cJaZ7h<5ii!S!CrLx<6yzj~i23&$OcBf3QC6*6M^_Y@J zUf6V~ZVe9aUxVSV+c;4Ey6xH+6(SdPox$6Pua(0pn|CafB3OT;4ECVHe zTK%`#V)=ioX<4P!YIiuEO#R<#6beP7F`3Qg|If`Y&3|v)e`BiuV@MdlfAIfp2EgC> z|8IUlz*X`8xq@%je_4m```Jwi>8Hq7GkSq3Hk-r#rqVT(wMl4;; z^r}>C@CytdnK3|B0SQ(Ra#+v~9Z8bkcZ}R>enR#4Ro@SWK&OM{uNn40v{Re{i+x$_XrZXZu=MuI?0507*aR{``C zC9Q-36rzM{#KIHD!zr6Llp`$m_NLvUwswWWbnw@+VjW$&12a8b7z!zCi(%YP&)V99 zUbXbedo{~ZBeq2tfD)hKgl^ye2^8JU7~>coIO7l!aViO)U`pXC`G!mMQT_$(7LL+3 zS|Mc1w8#bn!vD{h%jhG)J_3|8uX`q5kk+)@*;UBvQG(%fnM6K-0(;+4-9hb;>aRP<@9T$eT%q}zKgnfQcz{mzzEQvjwCS&-P0X+xN z3z|{9y0tgO%l@HcPJawB$TTkNRZW5>=T2L}H~ef*&!Vs(1L&%^EH}sho3-bs+q8Sh zO3LWPZ0HoBfnD~Nvuv&~o}jbxh3ERN_>wx4rnFF~@gq~JzZtVU`@Vhg?4@rTcR4hz zf7Ienr`_Z2HP&i`eO0R61&I+sRt4bMk{ro2tBrD%WBDphzw(6-y(6?VSF3XYRz}T5 zMA^11ObR09gOtue1t6*tjgT_l+YEUQhg}CNBi_5i0Q8#n0HCbg>E9?&P^~VXoU;|k zUTD?#{RS-)Axc8b*N-ueV~BlnHX0l=In)|NFuvrI+M+%qh03`sM%#l*0+GiA z^JUZg)ooHlC__QJLd|lguE?nxi-A7hI}pzktXNXPo$*>`b)YG$2K)H-LcIR4`T

    P8E1xr?0G6S%0luDT;ggQ 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

  • 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%0A#)#4zp3$ex3n%bgt)_jXDD0 zQJy(y)vDhz$Yls~uxk7>3Y?A~SRs4DaoKgC>X60WE z+#IxD#pY9-gNb`<VLTfu)2S1+$q z=btbA7yUL8k!=?<&2Q?#<2rTpf5D|Sb0Ez##=o(qgnVlM3^@CgL50EJ*@03n`M)64 z&m`R6H}ovjz^_X69bo=f!I0%d^(FY_DI|seW)m=2-f9cxcU$@2y&}7EwaxXzJWToFAOaFzsFn|;9w-VpxOJ_dZz3@`Sad?deeb9>8XF!*s64<CJMrn7$Rs64I*rPspyv$u;H+ zwaD)B{|@lw5>N&_8?JJWqzd~rI+-v2(-%u_Tb#ezJcdQ9_79e{NC4b_rj8BdHq7!j&N;A5_dh*GCa!vuOS}3%AOrJ%?e*6n9YBsMa*)pc!=`ze zUX#QdD(?u3|3#-m%1X<}5B!;F;Gv&?ANel=ri@Z-MoTcj4;NuG{|t93Oi96CzDo)# zh2Vy@{)cVX2j)nQW;eP-MuUIu`E8t{-wzHBUR<1uI;XGTh5jSTt1b|WrQ|3M7rV*mAfjMy}2_Oy)9_!sI=$24*(F(MMy ztb&pwpSrL2PlPP#QQxA(3VxB2FvQJn&mfRdY$3~bhCUYV%MyKSjRv?U`iX}#vUnAg zOi`gpC&Fy^No!w{j-sS+2 z21@9JRWDSJb8M!s&DX9%X%#Xy#-WKRqe&}M8^K4tCSnBoM_QUP2*7IDat431YpO>i z`F3=mH8(i#(_l2W7R~uyJojTmAa=4XBd~KVH!~u3!ezPvaUqr^RO6gZZuvJk_age| zBPy%d*WP!morualZ1N+n?7(^-BC;tg#hKUU@Z4*qFHBPkQ(?(cIC&L;A=FB|`(OM8 z$tedhD<+YAK6WIQCyp=N)=jS~ck&L8orc&jgR z-Eaah5&}uNNjqOy%LYny<{?wGoukm@^;%HyZ8@~xFML_~HS>lPtrwaNp;h@s!?$G5qpNfI5&w9Y5Z#CZ zHk7_hp(}?L+AH}RCO``#vbwB2G#3O{VC5+ zMLa#3we@IbqQ5_EUh|bL>h7R2TUa==6Jm^>3QE871U~`p+-EUf{up+3hqqE|HcG<5 z0eqOsG-rF(qDO%r`!wh^jgZ!>?_=}B1By7>;^@~HPrz@h9}|l*zQTAt?ILaAi-t{} zA^5tZVef|`Na!Z3*r)Bc*RxÐ>6i>)Du=&0Xqb&p5E#>fu1X7sE_?AE5N$FhNo~ z)1TZ2ctU6%9m0jn(fyh)RPwHEc__|G;|I}$?>n-x;fa)SG?v%bmQSQna}tv>q7Eki zI9ZMhpu9#zH%RBU04y#hb44c@KS$J2-&nWSKL*P|OZsjK{FW3UXe{vAtbu~S&)R9t zeQ_A8ZmsB^`qOxan_Dwc&NOeh@@n;BHBwhyT_ATgMT2=y0VeYC@$^oPGBGH&upcm} z-*nAbRlp=;WM+aPCptxLG4xqnjv`P5HcUoFMw&dwG`dL8sR->4g(LuHqU7DVG!v;!Z=|;N?-RM|6Hg@QUu3|M=LTWA9#Qp!l3e(pGcB9ctpw)@!&sF$40>_(BNaDV^S;SdWrk^WTt+m4ue5(1{agMrH+!u|L}Q{ za~>0XDb8A8ErKtnM~cJFHHYiru>hu}2i3i3UjA+}Nd|@wQR?XAQrTAz)G$ni3nNo0_4C4+@foWr)7ry0--7D=QWVhmu`b zIfsI4NjP4|r+D{5B+Y(Ho0w zr~XV$!NP?4#y|M#Y85_<*ZaZZb5Pke4Kt#1Kjr z`WT9TbRb5p;!bW!uA3r=QqiBjaxz{A3%zq+iwa05QT$A_Zj%8Ljm(_dKdd*_fZ z_iw#blA3@YMHz?oq%6ywt&$)U=KH1(2UNd3h^H96z9q_QTOJS+?PtbtzmqM76 zgoo=H(garVxJxiyp-!ry(V{A1o~8Iwevt1ED_20WH$c zfI5JU8_EaO5uPEb3!WIQb{o8ruk#jPzBu*z3Mz?Etw_A$Sj9yBW*t?egk`z*?Gv@W z=jzq9wrJC0&O^*_qw)_Qb!UDWYwt4FYnsix*3+{^T}sY9>^Z$#H_xBq`!qI;?9vz` zkn8p54=2%%tk=w7)j(pl9>II{p=Wc{`Fx_S7Y#`$tz+A<3TI59{=JU>D5L-E!-los zLo}jlab7rU3@yh&1Kp*1c`cN`k!2+&I4YzDV`!*($ZH_W8C5v^rTk1dsXx4<^_I}w zwNC$$FXTdm@a8BIHYkLCY?$R$jOUXa-Ly*(?RKzrE|h%?hD5Bku#c(l*siT5_Yx|A z0lAbQ)2Y*^n-@SVfN!RI`EHv~ibGr>-`V#9RILU}cgZ#!`=uV2gdmg38j@~>(7pR| zch>su$l~GR^oxPS(BUudgKIgyrKsBi+EB=D{N#~`?J|_lSlJ`|$b@cPf`ij~IS?X{ z&K<@JcJ%RETT|(BazKuW+KW^|C5ezJvF2k7JfiJYqdaGzkJbTDWlBkl<)$ZqoyVmB zBul+=HvS_y23J6tdBmW4aaVx~J^ZQWlStBhAe^}oA;E5V+rxo<_U5O+m<-;qr@8A* z3ZX&Xo}SefD!o;ejJl!vFwg5UVc9%7l;JkFUTSJw%1h-l@%9a! zlL5?h0|!_(*ZoZRPFe`>b1~?FYsSk)A$<;Rz}b_=?F;PJ%U9zRMO&i$6oZQAyIi9( z9f#G`bMYb#c;dF`O8zK%Z@6T2T0G~Lsnq54H6`&{S9S2L*nna!X+B$vDB2-h7&16U9D%lov;Yx?eZiZXaIf3jBFpe40u%!QjCH`gmq z2PP159JobNg%XsC&Y8ed-#OR?*s{RqB&qWa`oxXE9f42FC+zV^%+sCzQhc zAY6PenCiwnR(#>A4se9ERJt5{^C5~a0D5c%CA*C?TpwS#cn?IiH4ZGgZ{_%^7wSgnKTzi+4n1!27H4+I4 zGqoMi3R=QG(UyMXP;KA2^vD;m8mXMkB;a3O1)LpNfZs-UbhLA{H3sSNZdE}-f6&zN zjqVG&Yvbe068^q>zS`t`$a?uF=ut>*ZzR?1%*3ZJJi}en%-8>ADg>E1_X6N_0QSuV zAzDaoZwmP;Qaz_NIw1lxcQaRC4vkJ89uQ?jg-&PAT3T8h!ZMP&hsI1bZIkBWx!c>@ zOHAzoQ&LjWN!r@lETW5Y=6gg^*Ef~-b z%phT_*Npzk^I`3r1tt5GM%g5X&b#8Q*GT5Odt4cqT>*RehBS( zy?;ly=7odjLC?pODQG#t@iLpDSFEto%-rj_SfQK?_U+h-d+@*+bh#cc<1p~kw$RoV zT)N*A33_jKKaPGg$#glK`^`5cU3h2unG>+JO!HC??#JX`$n!LCT%YhM&~L3A(Q zl0HVg-K~b%yzQZ1k%D?H(DuHCq0O$Zc1XMWGU@bzJn*tb`neJr7ZGzH*pbESy}^-G z!N+4CUvbGBckmorrn@`fz5T@^RQK4mzws+3DsUOOf4cE~1_<3)s1TeZY#+bWZe zQeVLEYb2HBF6ra}6erH6QKpJ>vM*V^y~9;ma+`@ZRVeSu0jzhGYke&KLKAJb1oSMe zx+&gUaNeJ5bqoRZ2`6)vCy;@CA9Nn=D$#U>3Mnf#!gh`(iHvF_-?j(BjTqUyOh;}h zz|QTD)M!RDCtzQj=WTO|5Gj88hY&zw`@JsE9`MOfQZjuDT#ErqpLR}0tQXjWvewtM z(!7qXkS+v(!+aE9#3XI?yupstU>XX)$TAMf=s28fI}NXii>s}(gR@_``F0{GVGvet z7}FKcAh51b4_Q&D%@G-~^Q>7vH6j?o4%X_xYF0kx>RNNcEDP_4*XjaW|&CLs_B;c%3J5 zOjbd61v>RUA<=}NU`jxrU83~e#xjMBy}Rf(``C%v0l6zr4Z+a66$ZO@wdx=NP=CK# z$tK-xg~4@fzJbRQ(3`STxohQ-7L%d+d~=jc|BtCP-u=l+GwALP_vqAHL9bDzZ(b!2 z6dnVH8IFkm0`kF!;?>JSg=%6SI|s8v9|K?>7?a}|BoFtq8T=j%CBwH-VDGA-2O(E^ zWLF4|?F@5Jn8rvdZMV|y;@lEHBp8Fto{tubk8X1|ttW1bdYcvA3$TXUub3~_ZVKrP zPls-9daWw?T_K1*0G^tB*?q9jZ1(0N{T>6nG&OtZ=F=bT%m?}g+bddBzS8U;35QVU zy8;&fgDw!DErL1uZF}$oG-BFH<7&=&S2Q#MhPo?euB6>iqM-Hf4@h6YiX-r<%m-Vh zQ)!e7@WIe115l0CSWko33`|#v`@k^7v#Q{7)Ax<;PUa;1T97)k)X_d5mWaS75eeSq z?p(6+JahAkrLHwso?{>-X?}iy0nFg$uz040P^+UPGQPKUox`E8Nfy|%D;jOH=Ih~A zi?9e*BAr(G^i)pp!kA0hZdapJ3?8369{Vh)9;|CqA>%Ht^8ydeKC4dhTnGG}?#bw@ z+fX^+Dts5O?<(r-z$+;NLMT=QXEs3l}Hz5VUHKVLp zf_%U|`9`Ic*2Mx&zu4IXCIPL$9>68e_Ir)qg^g6`y&=Ihx zWdgLnXxrcvvRId@mUFXT*?N>&xd89Yws^l z=B#=j&cW_;sGTi$%%Ylk$Mv3DMu~uE1TV1h(tj^_;M9oEP#aSg9yWecCBUU+Y_k68S)%jvp;`fe!6) zP{WS?%RolGS}UmL_7(M5mkcY$m1c_$pRj81(pRz}>ruUEq-IFaRH58LrqIHZiFQ6X)lcxF+z}8aFAMZjbEZMg#Ia!(E+zwytnGDft!{Hvx_F@eQWhJ=_SupoA`a@ zQt+As@28PZLT`p)mZcJVlh*prW=3lu=pg2DclEJQ-+adO&v&R$-&F5(@}a?-4>(-o zu`Y`QssRDtH^)TEiBR;v?V%F0Th-*JOfb$Y>n9A{8ErA?JwIORrQWDkfu+V4;dB_Y zf4_+M4G-KOY^+O(41q{GvneLN+ta&Lw_WrPIE-Ak>?H{Xdfb*vx87fZw#vj#d^e8&7Qk&bHm;O;&derWDeH#9` zvKevP<-Hfs{#KFxqJjxQzW3&BVQB5RcGhy-YX&6fb8>dJd?LJiw$teQ{Ftsl-mlmA zwFeR^VCZrpSIgjhkJ+f63$W#1CBBo9(vM2a;(Ta_5!KX=Xi;HsAKCU%;D#SLo%h(f zSgjbm6ftS;es47AeSiJL_>qEccdYePdde3pRj&5s4VCYT8HIdXzR%AO)Atlh;MOG4 zbi-fd=W$bieFF)4TNPa(DYb&vbJ?@gEKAau930GwDQ0H&S8eU>?e!~dQmlEYY2BUe zPfpI(s^~Q-R({jlIy`@DCK2h!f}18yjf@bN`^wa$zaFCgTC{z!V*RDB55EVdoz4$5FUanJ$tlj(r||DI;PWwMc<`$+ z$W>X?boMd75oj|;5T-YUuETQ#vGlOM%PPjiyVMQJR-mcV56^Dm>I|S14?FvvHigB$ zU8w@EAYFj!j~dZ|9>j9v(w4H-XWfm$N1DSwtow?yE~|Y(G=cjKjy#9T$d7dk!hUebO z-3KnO#k`gf2!-8cCc$Y;zoYQ`PIdG`jaTnQa~d>nT*x*JGx=>9o1vkh6bq|*6$q)s zAIM?d3XqTx@YM1^;y;ih=8KXQryM?0wcJ&J-&U9u<1ql0siG>9gT$OufcNBthCv*ogoi6|#Y}*RAGF?Szf5o^^CUh;O z4@7s=&=QsJn8mSu-mQ~n!+E^FM62aK@s)ZqT*{!s(9D5xHZF#;o94zxN0$uKJ(AOzV+nN! zyZCZ+*V$}ux7dR4lk~Es<+7va{suVBthfpP?}f`^iU<3fzW0Vhn$Pwwx2sP(EwNmb zzcRLb-GyGd=Phk0#rJ;{^)^Jy+P(71`eLYPDo_`NAL>7u(_)%*)HI{&aA}`_k55 zp?$K=xON)f1Xuac>Zfm{lrm+-mIi}Suzm6t30R@<~OHRcfmZ?8qT+BS9evo69VlPh6^+Cbl zDSaP)NZ`ge9R&&(UuyO3<=PEDN6DY|p1jCY&rp!Hy&l4)V0tc*SG~jSa(oVs5ZHbk zsKW97h}Ch61VAIYyiXtyyu0_5U_AY;d0Bfs=HR+JF_3f)wM{5+s4t)g{vXlr7T`8$ zVUqz%{&8iB-Tg?f%6UPxeq> zPqx#50;Uy$3B^@aC#XD`t;Q3eo1WwarJLAeFw#1E^&Ssf(@7OUgWZ*lhx+St7xKug z2VWypSRYfW77e$9e7vvj;Bn{>CsL_ZKzbrC1qu9OB}HJ1)UL0~w*nc;%*}JnR142r zX~*BsuU+1&>XKZ*Fi>fk?B~N{8djryR!xh9>uVFjZ|RJBLR9`2J4;8VE0}m2(|1I= zTkwb(z4>MaP;=5w-?|@nO(i=Y@X@}8 zyNS0f5|%Z||K5Gxbc?bgD)bD;&+~ar2`oRMmAyZ;dmiP9xu$yIv+<#BYHA;L@=E27 zO4n>D+l{@Tn#%rs-Cr^SUi*l!BIS zLLpYc_Rm|KxwhJaIN=oFZ{o2%HA75P`MpG{EG%XR(25pm;w-1*+clbAnjb!DP-0Se zRojO!2H+V8=iV={S8K)IN8DupiVn!*rO=wVWt{*3V9=G#v)2A3$t0u#^yiXK)V6hrO%M(7AxKmotY_Gq zc-$(!pPwoKF9us38t!qGlrA>N%y?PGOK~vlPS3XEhHHRg5w8lb@d2Y=sh@Iw z@F!PZetu?nk31bR4nVndb$P}AnBi8w;>%jhhoPTI%ewU21^11tz?*{k6_w?UJmC~g zHY6jG%Ohpu`I;gGeHC|&YF%Y-^_Tn6I0@SSRMgIEk@(%uUAXF#4Vki(M+jo5u8_c2 z&Z54)4_6!R%+G?Hd_$JV<>VGD=&5Uy5){=yIjg4#ZDi}i;y;4Lr_yQvx&G~cBZ;P)kF<{YIdr?CK5g~g%2&kll7BA!SxTXPTh=4eH~k^K@S%25$w}^?m0Oqet9u~7?5QluB8^{G2;97 z%SqCOBa`Tbpl05K^RT|<>(EWQ(G5#Q$%(=yk z@Ucxo97V+*z8ilf5ZiijMT3YE`)BsYU=gMkYpn38)wPxsOyuj!{q^M4p%Vcl?me96 z%N3<%IvYV9p)-ekM&`Pxnp9sY+n0-$p)d&js@wk6_kVivjaUYJ4avPA;(NdF613;( zdQQucJM&Z0=3qSMLdCoS^|#U=(?UIDO|m{eIo&1X6PZg8z;Lc@4Tr?7M~MMZBqjOWcPnFAo*yta|?zx0lIe{L!p;T}h4tcC;9fA(e%~{>wy&}+S5#L&pa_azDWVA8XTuIg{xPbP3yxJdAiUH3<1ZP~Y51VyZo58bP zqfq=s2u5B_tk)>F@sr$;*jqt9S7Xe`hDlsZfqLY!EJwo}`n(SNr?(+`jkiGlCvSmQl+J7KMz8dt z6Z>PKjx*kei_TZZm*~Tg!bXdb4?vU6Hjt1IvS2%0m&7l9ht@Ms;}hfcO`!KPe}mQf z$@SbA6JUL_0w6>LkbWw0xwyVFwh@{_nS)mHJcf^y$-wmwx%>%0484FTUf+fG$Lr+kyc+kg^F3NYdLh-lZ|Ce#VN9-7I?H`w91-na zsvhXP$11(pp~LsLcp12V9{=*uzu3c4U0O(9v&VZ9p?@} z!;V6UFjMSPzkr{HLmk->^#!8lKFpj-G#Ceg?S5wdW|oP@FHG1Isw$_Hj#U{(SRFor zKpvA)oGxpF4#pYxI+VoM99G&m1|StZo>1$)O@U7$le|&cFoif}`1EvYi6#5HuvQ4A zf&@tq{#qjv&{IA}V9SV^bCoaBC?y#My$kX;;u}eNh&-*IB?ncH&(vq0Ytz`R8->EJ z5p1|zCiF{n!Rz*gG&RZd5cr}1jRGa&lApu9WT|$-fLC@t%1zj~+TRfC=;hRlVMFo1kpt80k{*y~tLOJ- zHZ;%AauDb#voV=Mis(7cp~w>cIvNt-{~|K!t21zvaXn1O*=Gvr&@vXt`zS)sU3{Jk zv(iOPnpID}NQjDJW(7B==cHBAVeMb^E0%=)sMe_zBzXx*G2R?ilGD=1zrLo6bnoG&#iv*i7d`S?_TE(Y-s&^E;h z@^8u6oF<_Vn+-;p0Oj7zHS+MSJMEl|l^hNq1FJ@PD;Ke*M8;vInURv@$+K*SXeyxT zE-8fzO60~+iQ4y0*s;u$TE{7@Wb&QOWbIrSsI7CdOiZOar1U;`!VYts=NZ$vuxn?L z(E%s9{Wb=QntUT*rbB^1AXuk4B_pn<3* zb~un~5-m9-KZ^fY1cB@o-iNC1^#5p1iWGfM1=iyupXCCBTJtwMDf+8sw$--pBtPVW z%}eaAxAEU++{BP2XB{>0a^;f;1bloHe0yWU- zjX|z9By6M5%EYhlqHFR}u3idIc=c}G&AC~Yf|?6T&kQb_c*N)!D9T1ft__SX)mByr z^|^jzKk*qH*02QZ1rnqD5IN6E1<-_^M@TZHM<{_A{6(+?e37iO2Iv$EU1dO^R@5H^ zy@DwJpoy_hnF6`z#D)R>$pE6O3FV7x4-T44+n$6;Ks-k_@YSF3*aEj((uZ2U`uZD|A^=GuXi-8yy%E2 zZyl>7KEnAyx8!P}Tyr^EU-v3eNR~rKk1;;ae&$HCeH6fG=X|%z=L3ia$jiPLm*{3s z20AYuIpUI$xYAm$XlwE{!jA*k|1oAs;&sl9^Kp*K7mlEN|78s3MFW7GkmCMsZOcDF zjR7a!WfD6*8E5VP4gi)P{!MCFfnVWIKkT18)`|T%&;x{J0@b@=~( z$Scfxc!$U+Kx7G291``y$oRG}86SP=*m>+jbZjkwZsq#9ohxWOvY?3=O&&UnLsq6C z1H@co_(=c-0omn3z9(AJLD~0PdGyWlbdRn7@56La-~QIQ>b;`UtP;#^5v7nEnB2T` zNfwWJt2?|g5L6c^&Wz-^c7@+Q+s(h%ylDy*dLE8G^*>Knw%NrzK2FUWwMk&lcvi?j zyW<8qmV)WWa~h#+KmM^yks?OjQn*84uIN@@(>A%=Y8-Dx8yfGf@8KtFF=BdK$JN2| z?9y68n61#0iH}2kXs&8@%W*o^f&QV-e&+XfoL|awn5}+u_kSoKNAxkxAwT zJc)PfF@Chf@?`(r9CUH`A}8>zyxE9FGV#^AR9v#VRr=IZJC)s2otC>>Hd-o{yY<#n z`3xHk7kYHf-)Ck;&o@S#$U0Fk7-W> z3o}LBY#RRMzVNfl%T8>m6sEMDSvUB{5BA&#)6tv%VxrM+UL#7jf18o*dRAF=I&~ZR zv?)KYde%Kr8Y?09eZQNBSm(-E<@j00y2A0{Q`^3n(~t3ft!XqV*Ziv4M8MRv`?vjT zrn&YPG~uYD=$*rqZ|>PEKyTRadwVqb?;s~8e{=CxJJmlG=8LO2x_Qlh#n0|CwJ-UUsp81by58$fQLmKF737|q ze{Xv;_*zGR9UIdX?p!7>%-Y&+cLU4K>^nJrhPEXs9)=<)`*E|1mrA}i$Tt7Zn-m@N z+mkkMFSqGI)493GFv$9bXMV2%a`XBT)<(|`z3MpVxOj5Ev*mb-vmB0={8_y_DK}fW z*mG{(1!^Q!{*FBwDP7f5JcyPy>3gT0ux|V26{{a%5x(oo92DHFVe%*ZS{9ZxTf2P4 z#XO%{hLvvg^^mpp;>GP4CTmpQQ#RR7Z?Oe!xpFRG5ZhK*+O~qbjx!naEJCuwSTj;` z7S#@{bgO!!8d5UbVv_v#GT3qeg<7uz0OPcrT6&hHm)fGA| z?rr2!7_EmEcIqTZkwM*ojg1sw#S@pQ*WcHi#vZy1a<5X26~6Nv*W23u1d?vgRAV1V zhx1e3wb{zbyp^((BTGv#_h$#e(b0jiuuY3sgiCY3Per$1F*cU_KJG>DA}t*YYY*vZ zHsw8=r?4(-%q{<(dI|KJ7bu}7=V5@7^YXND!o?o+hF#aUyL`QEm^#XCRN`|?Mv21|dn+o0H99PEnm5TwIIAaRECk7@R8&m9 zAE=g8TNnc~K@w!$t)$u{OQlOmvn2Lv$F7vDT5T5vWal!r`-}DWb@L-ysbMarrl!Wj zV6crEJ!ju5nCX-3cQv)H+@M>{kEWL+BUc-)jq*idZ-o}3BvKjQ+1gR$O_>94Ye|Y^ zZ7s5iO*&2F8#^bhKdqTBimH9)g*R0`cFN)+?(bl3?&NvCZCwp-BRaX15-8>>YlgLU zG!e2QM40OjGTG2S^=7YA(U+m2y^o`dw7Lo!6WDwBiWFnEH#_VnDpk@Z?zYXP$=vC+ z3{WCUII-LcL$kMS8hHMbDf?)COtP+1Z93tGbWD zl!1EqTZfdSqNkmFm#V>bD-aw zh=ldSt^I8{kv20A9@-}V+`+Y@xv#IMZd84{C;WQ5I*75ij4q$)vEkmmu^&fZ^*YUH zf*YOFrTs0`$zSkl_Tk_?*5B65K~B@d)pyZwAE4cBytD`tvX?<(J2&_FP}cLPQ5)Bc zZaeLpM(ra<8NzuxhI3fWrku^h%2HV>t*zd!T-J!3=xbF?tFv3isiH+o9=&-Qxh7Co zXD#a&CRtdrHN2fI?>a@BurF6)CQN%pR2EkDB0IuOvYPWfQl75<-o~&>MANy zC&e92Pw_;ZF5aQ)i5uI)%gfVxt_?We`}5oTmsqMwcOu4H{z_KyV=;@hj^-x+Gku)q z`a0RWI3Ihi%aUr7!$);M>RD&i5Uf??>r85v06Vn zx}(q*5}zvYM_Phi$fFcMMnOy4{J|>#ly8E zP*!R(%fNzHpyd6G;5F+Hnl?>x>VkqtE7Z1n$h%Pb?f>jI6E@IFDcBUnO=(qVcE(`S zCNW=ZFAsG=wfl4T&#U0Q-?0}>KA%UY!)f&S8_VbH*Jv5G0Y8itRs}T@F%%^oZv8T= z3Gako~K=Tuft{m4l!vbBjI85)%+#e;|AGcw#ap4Zv%DJgN}M zWwlPaOf%#Xj{)j8xk2CPhzp}g6fM$#0gE9nkVFR|>SF?D>O5g;s+fKNul3m`C9nNZ zB>OWRJ5=M+cs)Q!E& zwYvTm4_#R&S+VBXVj{k=Z~4r_zIorD$j_ZH;vjKYsBuyp{~axsuahEit+)Ps2kBF? zyO)vk%W965TU<+NceeL;5#2hru`x*``oFX&9BDi-lzd$&ntT}*SQ}g! zT5A;=v4~#RZ!`w2=j*Z&LYn1oVAq=As*azV_e~?s{nEP}vr^sWUb(&4-=#NuLE8U6 z_#5voY`FS#kMFqP6623scJ&cCD0&F74eTx$7&!GaN$a|)YQ4BzXm75m-}cuv78%ly zPswgiq4%Y4CgtY?uKn^unRN}@sOaC0pHDhG)nn6%#Y0gmdb^)8;@Ak@mz+*`$h*y$ z$g?`W$;lSh+I-QPc=KXdv+^&}kmE4|YNmr#zrD?(x<7BXCq}*H&GU^L&GP;UQ4sjd z(y6A>UhEVITAm}A4;@|CV1weu?UcK=_I{e^7|eg~Bh*t9ZrbU)jMw~H{%$<)59H0{ zs-i3 z+}w9_;f13&cA`};wUkz_ovd5m3U(?hDYYW~FK+__1CfI2hqO-3!^8eHNt;ZqV;dSn za#~!wHzyOGitLPzOWG^ft=7}aucPo|<*8F|T{38jz3I5#O0m$Pmom;u%$lmL?#)wD z$15u&&wk<0`_Fxe3*FJKFIyb+?ui49{AECQO-Pde{tDvCYKUJTkDlsi>Yj zd3A;Lm^aVQ)l$)N?Cbv}`PAsqy&QWFGczPtoT4GQjq)+}9ja%ZrDuq#lX7Wkb0|@_ z;n3@ygYa9+=~|H{SBu5zDb}ogj3nE8^A0CRDRVIe8UK^t`7n2|v-6SRIk-p6`i@5T ziUBl}!1-?bcA}@}M2mZQFD!=fk|anno?l-jNRv(F8+wVLTZ@@$YI;f*EUyOflx<}= zJ-6<#J+%rbYA^ArS*2?t-bXGD4h$yW691OrS`EkxYUor?qaddx#G=_+wQzFzwxOHD zr5T08KZOuI0wShJLm+bm{k7THUjk zk1?L}cG^2y>^1&P1XIm6t-@{Ae2(+zC$b!&TpdX+GS-vIm6(yy8}I8nqiV^RcC~d=CZ9!OyJ_XcQl};QMC|Vo zD91>VawM%6TtnQ9kslTQXJbRej*>aor1^BwqehOmr+Il_eKK2&IF$KOib!e;WZT=+ zMN_M)2Yj`2a9<_qS971NvumSxFI)vc+3Vw)@BtB9hNaHf~1ryqm3Ei))t_8>d<}j6s5=!kODcn*SeeF`F9_ zl*-%J@v#P0W^In?FxQ)Y-eV|}#KS1(W&9Z!A1Q7J z(;1(cwE!v{E9ZE zQS_$vtri)C%BcIx*4}Z1R)4(KBD{1?3M^Wrba%gXx&JXSqg3!J_()jzrqX7FVhhmz z6$9MwPmXAL1Qzp1&>hmAy$AaX(QnHu!%xUwt@ueY?Au5I1zKttNus``p^)FW&n? zx)q`yAJj|o`xXKC@@MPqC->vYhI9o*YaR-9-JxSVDfaAFfe1(zt5NXHJoks2PGV^> zblM4#9m*}x2m*k4#Zk+N7Sf+cC4r@R;f4b?_!)2AVqINqFr;JUMfCd<%$(UqbZGDcREr1sDo>q zDDWIagiS4>aO^Fnr^YJra1slGM3AWvef~PDqhDK-nCFfPZi6E${dyU?x>gwnX6j5% zs46{)ngB4x5aGZJ_q8=Qa(B`2zmHbFbo@h=69W1snZwVE@K8RDFrD;|GuK;#mp7xE zq-zF(RHAHN)5XOOs4lHmxrzetTCfA0;_A3o6} z(7@5vA?ps?T<3BAo|e(7kZ;2G4e2JT3gL;FxYXdg6<Pt;EkJu+GyeM+$k5QzPh(dfD(+R%dQ; znQMvuO18GlHpa2W%j8&n-|ewj-Xy8kI4>ypl&GmHvNcz6vsrdvX)c=L)}$XOBj1~D z+Zt)J@vbEwH>fbPH6oueULIa~;-4q;dP+54Vqe}c=d9;qcJ4N`u@V^7^oG`w)8%WwkwE!L+IqU!0H-k9&E5Z*09&ih-48T$uZz}}d) zaI|oBN#~TCQe<4WkE-5_dV|2y!L#Tmr<202t<`^p_U_2t<(>94a_*{sg>m20;S@;G z{|mf*E5^(VX4~5Qf4ydY#54bg$(Py3tApd;Y@mFtgn5(Dxs#7r>n_(EDcym@;(srTyv}@xer29c#>7TM_adA;R3x z&%Dj4FFf{i_ZI5gqk?;5qbAQz4i4VVRqLN8$J67!x0P%5uGinw>+8F|?wntVOyESh zuaf%vI0GuW1Qj!|>&V);Zy?*NtWsj|?&wl!d*o+xa@DQW+zi4mwS9eyJ9k|M#5`>t zT#3pNPt|Nx3NbMiV!J1wyNrd+?lqv>+r+)GGcX!!VLX$oyP2oryt~A&i{$b;^*uUA zuWw{ssY5R>rfggM9jkROQ(8gaw4XY~_4K@2INXi1{XI;(Xgau_<~3zr?SD!{OT=+B z;p}H)V_dt^uZxR|i;$MKrpa=$vQ9w@BPSkqMnkpT8k?LIcQ><+&P-f{gyiAQ+vC9B zz2vKppWDD^5^f$=R84LBC*Wh_&RcG^YUna@RxFf#9%H+eX<26Gs@8ChMuUdl2?m#-D^qA%*-nq zvd`q`XTVCS#`gC1K_;o$Z!bqBrEQeF=o4n5v^eMHP(Vu}vkxyvM0PehF|l%1a`@&U zq?_#TXBnRrqumPbDKIZajfDQqU2P664!=hJ)^l|V73laP&X)I?lLsp%p+e7vA43oBJZ8%k)X{OXvcAf-d&^iYo1f9DyGk8YD{*lo^4k13I5-ohWaPHkK2(^vv$3Ph zy4zBk?*kjt%x$+q_V)JnE%o*;&VCNkG)R|tbMV%9+Us3hTpWjP>gsLw&PFLRI)jf+ZJ$cG(+TYGXQ`Po@L9Ch&i1bLY+=*NuTuT!HUS4NTov`f8PZh7tO zXSj_l>&!0^^Vd--GUJJlje-%Eb$XIC^l*|rkJwtt&y%m6X)f;W@3J!2E-pQ9P`nvg zRQmOEQ%|o?Re1fIF0Q+-*Z24D@-j5`{sYIWDZ}Enk%idPcZZ*xiu%O66Et_wXKG@W z6|1(G`1t#3F0Qa}b#o6gx!$ISEv@yd9L|?!Q+s8`4b<^%%+ojdhbF3xf{wbvfCy%WAK3a&mF@)hbh6E#@{h zG@O2Ps8GQwe22OD>ox@pxU3ZCR>nMew{0p|mT|4-Fj#_CHd*B=Q>LQ24+^DNv!_6t z!@`Xo?iE|*2O`($ql;KAB1L~a4)*e^IHvI9L2A%ZyOojJ^sG4sg3=8Dx+L?8I8*PG4t#@uesHcQ62whK?74GJTy&G`9@Vx zOOS-?2sXkDCxDRtv$By}A0kiO-`kkhhF^r&PZ9fYvDmg7WvPcI1VOZtIGka(x)$16Ssv5X^GORES-JmP^)Kn>U47a$=dZ1eGA^#C zi-(8Cbvb?cSBpc<>6qOo{Tc1A79yAwyWW( z95-&3M3ei~QPrn)gJru0h#S1&U>r>(PhwKz>2uG1S=2w7h?QQ-r0Bqr`=&^-zCz#j zw(P{qW$Dnn(vSwU6x-A7Y|T<87`BjU-9i&hXbPqc*FEjD)EHNIrVXv(Q7LgcW&tvT zTGp-wgLlqR1?`Gv!j8N*D0S9^78eCpXk~qK@Fw(Emoih9vd26rt_rAPNeA%}Or@3653vl)xFg@#X?2x&ci;5$50w1qrZV z1QsW_W>+g|?D`UX7^nOye6(>}ZTN&4=H5QS$cS^x`KUIx25(V_Ohqa^JVuy0Oy6DI zD-hGaAqW9Ji!tr~F7srt9@gewx2zES{OOHz-C>8tyj$x(%lwhQzOkVvw)r^!JwMAl zp|vRLqHx#V#kgLj+d25G(!#UCeAHe)o%Nkh;UFc+1{~WEiUnx}UU$meP)YEmOHFx0 zD27Mdlt7O80)*Xi^Ee0vlZ8hA)0I%1nEA2p5b&+_P^p#bo7cVesf$qJZ;z$p8vJnh zMvH6Ra+&>7N#>dZsdO^un2XgvQKa_v?$cHzIx$xR6lQVZrY<|z#@MJ6T@zd2``WmJ zD~Nv^pBK>jI%PixZ!zg7dPD3>_B4+o*SP?25^zd#Wh&&dP5uG$`meIKH&lAX?R;Ia zcnWwH`=2fi)wTMY27*qG^pjiJ5MwOWf-VYW`Uj`+_@ zbdYm(<7{rMKjmWo)AKpsxZi%iOMPGP@EUI}S3apZ{+Yht^V=U~|JNBn+RlE$8E00z zPww}~(*KmTi!FMf!1R}aA6-Dcy9|re`NNmJ&hnr7yQJI?Gj++4eF18z$5D2$&c8z- zz|9`8%Hti@cG=f!;&r9Rp|#sF-}|_7=lA?z)&o{_t!HOr;_zg2GN*TIV?*#)gZ|Iz zzNUUEs-tRnxUgd6?Na4U+XKl@y^Bfrx!2jEmWn#&vaGzaVO_=3xJ=LDVZPR4`R{pP z*l_z+<@7`nemhb+#7n;tJjGpK&2i!0sQwp16UslfA=bZxpTYNC&49sAf6WJpyxSR^ z*zmn;eB_h=TRAi3 zpJhL-;ufTjr1PP_$7bl^V0U7g>SSgum#}!jhsk9S*?%_iirLN#TJ{^??<22k)BQI? zyxYY+p8aQB?%AHymXg{BWe*Ftx<@0i=o&Il5QtqfIMhz>Pwwz^8n}2>pM2SPTDEqe zA+hnqyxqX#t+%JS;L)_*|8sqp9*1{V{&Nsjv6leH0pNM=2%sn<+^_0c{@My+d$6C4;E_2 z_Go;*>Q3pC?sjsv+hx*E#-T*G-@yLnV+~iEx63yWq==R*&&A8pZ)b(o{dN)am}6}|q@o_OXFLZFT{Sh+a+B{kW4xyFf)q9sR`-n!@U3a%Ug0LOZ30SX z^f313Jl5Q?Zj|H3>n|GR8w)}oN4Jm1+#lOFcH%2`in8XrRCp1J*%p7V>Ix#H8mNiiKo<=3nhje>)i`9E2h@z(m?h4NL655 z;b6qsv2yqGmrix}kwfb7cQQW0Wpp-l6vUUcwWBSz4hsA$`)IXyGVZo4;9zoK)xyfZ z&&blmmhU?UUhaAe{rlV1ofYtrqq-_>|Gh=U$KFbX50h6wZQky#t6p+K3G0EDG8O&a zl3tD{UM7wzk4@p>;{D#~cR`~bR`*@c77_-%`v((k-}5h%*+;(oW&mKRQjrV9xZFEf zS$q5Fwbo%I!q-`4EF66%Iz2cz7P7aTSHQH77d=h=_RI8=ZKOlNKg59^`)i$@j^-`G zO?f#%g+CNvNG~=AL#f`tLM6sem7eOzjJf> z>)NxU;P6)5JF|s#wU6NTwk*tvTT5#Tx;?Je7vohw6A+Z~ZQHom;w*IYVXD^WIAdb? z>uI`lr+0T#EW5PtaA4QVw=V;wrR7PwyEm3>E4-4ABd%Wu#yMug7{_Rf!0){r7>AIKGt#;#VS4hu?rDooK ze16?-$77A%)zS1(=H_EF=+y7lI*#9TIvPU9#<@Gy-`}58NbUU$+|-$|KHZy~e145{ zv@Y)S{CzaHHyKdOzrRSw_|i*%WiA!%?haOTybrdr9`>!4uL|1IYIPKI@7-NsU}A16 z*WPE1ySs&z)3978+jJ@yX0kn-Y~teV*1mzMhlhKa)3t?!hV}{+G>;y2tFv>lt*vnD zA?j(pz2-V;iHh~%zA`d`DvC-g7qb2`A_InV8cU7)rP;Eg&P$EOTXN2#lbP-|Zti~1 zB8yj&H1JIUn06u_dfGU7ywl0c>n_H|+VwJMrm4HBI@|3-$}urEwyw0h+!@$7 zBxKvXqkYZ}1_hg%N$+O)JeRz-@YcGxRin*ECGvZ#tD1~}&D3y5_Q7Gm+)VY} z#lOF}y7u%vy}kB3J3B#3^3d4UKtOnkf}S3`tBCvs1i0%>OGr1&8sAOA($L@2It(72 zD&i(sofU84X1%)`WrqyzdzG*Ko6pAKgOJh7Vxsv$Rs}=#`eMi3CQ}a3&mK10R!~8Rg@;`CzIOxHwhK1k(+5*tjMW2V^lkc;h_Y^ntO(bBYed=#1-6O?=mT&9VcQtx04Ubjku)5ToF@Zdo z+I&_D8ouZ~ejxa54vEm5t;Iv+HxIPZQ80G4S)yS7^e@)}a4^_S<%PQM=7(*ypwQ0?&@Ia0>jH4@yT&Sb=@h(_`6u!dWU33oOJH*PivL+ zEBp-)uPb5whXDB;K#5m_Tn#N0j42tx@UBctLseZ13>weo445#)<_}ooL#!zN8C0v*n@L%%Wbt;)R`OQV z)!CHHcOuuuJs{VinaWyg@|~7A3|S_B;$Of(0}~8UA@IXgF8uZ~2^QY0Auskfs<8|T ztsKn|ADo&>m%%|o1uR(g9DBe+C-*b@;pHQ|fPo9?AgX`3xD*0kDcgU$CoUOGUR%K< zSopL3k%lEYbps`1e+!+~)_WTPpj??;O>m#}w6?d8NE(2AC`4q9M@lJXe<0EZ>m&*V zKCK3!p)zD%yZyB!7_*g188nK-0G7W^-EuKeCYE$ClN#yt z)yr+T7~Q;9azpDKEhQ=wp@Hm<4b3C&)TjMB`F*-xQP2>_TL)3D^d0^RuN=R!Su9Uc z^nY1jFA5v{iw4)l5)*wZ=bc2k0}j=Dcb~;2D|gKL|03BFK&_b-{QW-Vtp2|&stKBF z@^XoE4p?q9Nl2D$pUharnMe=URUZSLM9gFdJ0^Op>t%jMa|0xb?RyKfMH~gLJL_Hs z)4m?{OdGpX^qY2v8}2@cn%2~CbXnZHTu2gjRvwFq5gbjn&c+=LrENL?&pTIXN}4n+ zG5h~@qc4?~;~swBpY=CP8+{<$BT<^L}{7`;!S^Q3-varysW+Vvn09c6s&lW3|1py1JTYEd_!1-x@FU&Rz56 z>`6aQ^Ju+lnX-LA;@t3(a>MU$?8DOytotuZc7EUwFt|*rx3FisJNkNkp3#l_m7Lf1 z6&||3hSo2S?Uv40Y7N{E2JUUX1%L5DSMm?)y8k_RQ1I_o&>MwMc{GrB{qVE=84hiG z9ePy|R-5VL+6*<2xu#nl4eOmo;#>GM*gjmZ?Xv`Hr{A6bi?6jkqQkGP#y$t<11Y!t z%3Nt2dmZ*1Pw9S+ZKv2fJubejzRnBpM;6T4cPmB9yhW$tafa_(w*9^;@u zn_l}D7NX6Hq==(`cZQ_e+}i18Rm9RdZal62JVfK+$2r+5VASBptKe*&Q@os~ z3$moO%gg-P^%5?3W^PV`RlK>^(xArA&(AFsmXg)ubGL+DLRY`P$&pp(Huqw$CSFu+ z-hB&SSR?s#^!Ze4X;(Wt4mPA|J2zRuzEo>I?rv%v?ZkNbezpe1x75s3 zXPr3;Y6dK8p_|}!n>!yL9HrKuZBQBOe~o)vVzhI zpBfY>Qd4K}@|Hr*EZu!*(OoIuQ)t}nPK7FLv0~YDjbD4Tc}hr(S}{b#`-@asu-H<{ zuNK0VB83e#YDLzzG4t!)Ng+_SMMp<$dU>Y3v!aiCdwY3HZUjm|1S3T1F9`(wL{D=(#J@WwM~W^O^`D}hozgNW96Vgfcg3yrj(SmwY9a0 z%3+a`Yw&PsD^{6ak-vjOUV5f1V`69VZ$D(;QwLL0GZpr8^YiR2TC8TLyk@&kPmwNS z*7lt2x$M}Mmo-wUQ-VtiT3R|fIx~vCp1&NUsz=Yx&fD8d+U)>xV~E#abiHgQ~S(qT?)J4=#UUR+s0OJ7|Y?i%N)%pRQ`9Sr>UO&8f%RYxSKMXZhI)InEU zSt?VRGRs-2=^rj$m!M&rsdZ`@(<87}yn6ng;IxR}M%wjy*|s&X^@*?bHLQ}0Do323 zK6j+4P}g9=uFZ{|ot7-CHmz>FZ2x777fFJ;@+@_|8-I}QR&6<%478@8L1jr-ravhAgo>3>TQ6F_ z3YXJrB6#QN(z&EbX6*0OCPJ0h=sj(nKKVJhN~n>c^kk@larH^Yj&>kbvnkw z&2ycv(T#bRQ>QX^-riP4{7yT3O|r7Gtzo3J#U95NHZ^(MJ37UdW2*7#1X)^KXJ=tk=j8JkAV^{j{nOIDn;}g!f&SHjU+`th|5Ey|Arty>#5(GFfsG z4)$aue$7QccBq%a5%kDk7f)o_uzq;zgYJ0bY7KWPyP_SM&Q4_x(g+yH$jHdY5ho|M zc%6>}1W5z5x1Kvde2R8RN}jJBg=_0T`o7MadCFD!6xPC4$QSAQ)Cfrel(0}!E?%1K z(#>jh@xNHK4It$hsD(tT)0ANAnM@l%ZO9(FGVJk61N{L)Qwj)z(WajUwSF;cgz&DY zfiEHJx}&e2R2(vZOl`^#4)H*pm&|BqF`%ij1RxZ%LMWO7KL^55CT4-&P%19f6MjNa zh?O6~ORet2%78bb9xMK~Bw+)GFNR`OaVnE!mRpgSgP{LHq01-@d?6qVqF62);VN7_&cBP+sh%2Dune`ZNR zu?PCvdtm3o_bWV^FDvmgD)Yvw{^{hCi7}?5x09>WW7fMw>sGqBUaFL^G9Et z?8yw^1vus?km(>`4iK(~Yk_X=nguKx_05EYXL`jCDf)*o5D2nP5}eU7#2~D_1air?CMAlIA~+yU9RZt! z4-;bC?#gKJ@|76)6;pgy|5d%swQE~R`fm5mw3zN+V)wn|^}P>%tVCQ@^ZvrZz~}P0 z_9@Pgn-UU&K$i&5Ehln^S8Qx;7CB$}%>4P`)#?>8R($EM>-+tll?|qb6^05YlDlk` zJ@oyfoHK5ubb0!ZhP8cv#_B^?{jXzY}?*8K| z=qc#oTiDC+>QKi!k5l#z;lGqdIJckI@0SI|HqzX~we;Y+GWWfKCi4tWXz|cZ#~WqY z8?mV;1CO5{EgSsWIh;AXy&S0OZK(g9xM@ty5AM?0bZ6~6m@XC;hCns)jZlxekh zlX&I|c+GrkE6M6)T9^+qx*Q(Kw?B^g{ReMF687O&9r}M?>vKn8+)B4&#nmq(vvW;v z4n}#J;U?S@his7yS0vuACr=@-!NBG&dSCB9Wv<;PVtaqlfKREii2Dtm|98tx_~5_1 zqxUZmf628xVCb0pm+H$>ElYQw=rZDBW^6aO>8UmOD6gYxu6=JME{ge*8b0p7%=w=_ zj^=$Cy~@|4pL_JQ>f_U+lbvIWHxn?N8I~Es{z~Mx?XlL9c_Q~e>3i49wV?;-#f!7? zXM3{9qt}3Yxx~tu8z4E>1k+4v3J)?|L64nUq9@^ z$~#Y^@Y>JhO{+IEGjHhPGI;Fh<8S2sO!sqoxxRs}>6L_NICk^2k!fGi-~F9Dtv$T| z>sT20_~V_O=Ie~-8Z;(V{j2hKoag7;IjJV?YGw0(zPMG@ z=PB&HN-!l*(?gS4j16q6aA;#=V?$lghO86XJo=W2p{7)cFf_gXJxz?gO*)!-a(p^9 zv>s_`PhH8O0^QbbgUWpR8hQM=lKc6Z6!dg09har0XG5`1Lq9))%8cCEO3lWc6y@Ir5Xl#PH2UL|4SQ`avbrNa};Y9;Py9GJAuvLGN#lN?t}st76+b zw?0!luP#E&w$4^;yI7=4uO5_Y$`GO5HM;aw(NulWwY9aPhQbFKS_kW9=~ep_ZRb4A zfuo%TCbktUeO#n=Mii_hq_^)ki>$OBWu!iBk}kQ4FZyCr2GUMXVp3a<3I|()Vq!KD z=1O-vZ?t&v?Q&=m!4MxkMvhLMqvzr1Xdcnm)=jUivpn(Q+TON>3Gh64vris8dd*qi z0t5&X%dC9){C!PJzc}{3mX^Gb+(3U<|6$I~)h|i}5A_gmp-Hg|&+RZieruE#;vmAT zg$IO{5AQBRTHemfJ*6vow#|s=67dcO>J7cTKicNnm1vurnL4_&&coc?^S3uQ5h6jR ze`o%i7SLb7ud~9xK>l7P(j4vWt)w+2WS3^v?PmMC8?;nxZS1bxa4~mF_jfkX*V@`Z zp}yKFJ0(1L_SB>}X%p=1Th?z;79N5vZB0!nw#rAih&6Rm3HpUle^~adyH&l5TBs5g z4Vs7$(&@yvf6e-%ZKCzR@gAB}`pM64>Xin%a5U^cr=e|cJO3q>D$lL%;D9YtEq{pCpSwDtjzKTAtX0B1m$zdlwc1lO9HQ(WOr8G!kK1!^_aA;LX$1(b0mVl)9Rl zW2tpcmZT-?yDEwsQIO3B z2+&50&7sh>mpG#GX=UUohxJ2p2n&k5gMV4?m>{S2n)7TS6F#`-?%a|1weU$;OaKsHmu@sm=xa1IJMF zh+17CUxDB_xx&zjWPU5K*n!-iwrQj0YWcqhgSt@2p?4?0)i)$hCH|zb5Vz5vMI_Gdy1hr(mi2EM@agT@y z4*;CmH$@N|BH)v=%2cC)Kgx}w7~QJIIJ#7$qeeGC2M`3J3J4?=oG6-XAXBe!9V8%Yog(u4CSC?S4huyd3l68mVv`TQ7MG*YO1m&|0+37!{5VeexL6) z;Zi4n<*UnWEpYqs=uf;c2Y~S1-*SCibV>?cwHyS952m3bW((m!K?vS0d3kAsVPMW) zX>CXo3;p`62r~;GQZ=ZY0A`&|(Rbc&*Aky9MtR~{2-6s;k*$-Di2r|dl1EPla(deu z&OlNCoZ)hEncyOe{HHr!j7OgrNPO?+{84tTB(9b$Yk_j}Lb zpyWnIaxwc(Cf7dEVnBaFKJE85jiYCht1XeLyY@vq-tl>PowDVe_+#UIKiv}Z*2eNU z+xyn))j8*TFe188{YEk`2K>`-?w)hJdR4ke^fYw+WXpz+&jd;erw?zzvc;dP=8tdu z?X^PYU$;Zwi?nA+s*B~lfyLBdYnA=wE3GRr=k7CGFxb&fzC`g_=f_Z(+U)GyOJBa~ zJipgvy*OHpX!d?gb z<)p^!b8Es%pfa}=bL8hKH2I^YzQboav#=3oB)4Ay{O2a?#^VfdeqHRm3 zO+;hZ5LoWXpZ^WMGc&#qgY@nwTBqn@HzD3muTRDCX%?R^QpxSky}qdp>3pqciIsP= z#=oz>YoY&Or`lo3eHZmEXIx>wdUZ=kooVzQ{4T&yw%O98LBuy&4K-hayZYqIF{6G-g3i5=3>Ov&_qN%U7GJtS34Phhr@Msb>hl}FTvlL8^PYI zBKH+Ww{yANg?i(?6=>b$xwyQUvqs|L7n8=y<0?;&KPv;rk7q?HlKjiei`92owT*vYF{AuRJx$l@cv2 zKNfa&dq`)Np4%(k`xq@2jqA6TizdA~HkbyMW^Aga#)f_=M{MmzhIV$x)@RPfhKm-* zc9xc#m3C?VpL8CR<4*HC#4jsrY=z6q%@ck_S8Rst3+5YhZrfW{%UfNWTU-m{V`F9$ z>%{6z`*wrl{_}4ubDa~69(NA^GxmdK+ecqRMng|4Q&&S@J7d#R&702OWm9xcD~&5t zQ$FLfHhZ%Z6ANckpEJ|MB{x6&3ptOCk18dS?snsT;(e8-Ws*)OHYJ&*m6rzv*7>VB z*4uJJeX*ajrcMF_9;dFR)$&UznVVtu)s>S4rR|l8i=B<*g^{9B-3x2Ap`oFoP2c76 z>3I|OVSK%dfT7c|w9MJoG#7WbyZ!!-ZwjEe@%Y$x^7|Z^c-UOM?(-@e-Rcz^rJ$g+ zaylF%d7PeJa9w)%IUYDR-6A+0Iv!k}9NZuuo4TD|TlHKVA{(2q8{fFwo11g1TK4F0 zXrJHX_1mlO^ICM;vbU=l;AEMa;-UAbYns_~U|FJM8QU8h8d_T)v0@tII_t|Q~)qnrr&^wfQjD+hA}x!KpJ!m9)`3 zQ+_Po;nsV+(XhM@d`Fr@@J7(Y(8MQ)C6|s*hZ9-CyQ{AchgHePjor;c0{=fBo!!C1 zeZKR9vSpqFX`dq(CJH7Et(~9gu@j8Xe$lqmcGUe`*5QQJveyyW*VNs_^PnyE40pi?Ew`&S$XzY9WYRh4B-gIkw~B0nDl7= z6$#4EEj};jWPoiWK59M^5sb>?DATV+dBJ5fQYi#mNcrz`tRMLLe5+v zQ`QH1>8Il&+<-82b@o8I4!RT=+Jsp10Ut7_zav4MZCq!w@iG|FGyp%wDh~yj??JV+ zp*>AUW!lydivd+zLnl#@QsZ+sV8 z*?YV!i5=0}8=ddN$C)PWP~WiBoFlo6a>WE6?eQRue<#>CUw@p5XKal!&r?R|0ttYK&CVtEL<;{IY zU&3@o?K@WIu+zC@SZ8Z>zP09x{<8J9EUBd8Ot{x9cFDZkx~W#S++fsWJ}xJz+h9CJ z)>P2`=H1=!-z=pYS=-|zqdHm`X7*%o_g(Y2nk3)lEE}Mtb#(KcZE4m)y4oCFRO>Z( z?AmvmJ36j*=DiI*EwAg&w#+JjwZXsqbML_9c3AU$Fyi~e^_xrO<`~ZESo}8s57&0{ z28ZkFH=pahm-e%-yX1+(@WpaW|5M8ROZ`HA7~iwa|78+AhCY~E!yJ&$WgA$$OlI-;(Z-i328oUtWJpq znF&HRcYg;6KHE`II-E!U-aqM|9E@bTj9*vH3!)>4&dbNUGqFuTXyEQ`KfTRU&85+! z4RtBcA<(4m-}LP2_`AFG^B>`hJF?75^;Xfh^2ljR|G)3NJG`U22P!uvEnH8V*ZH@& ze38G8K98|*lgVd{m^={BgmtaINpX=x?mtsPZ4f69VUt2(ON@rNzki z(#PXkcb$z1pHAnytWafhFEU=wHMLvMUTQ?>@coz;x$@-);=1WsaBSU7>>eN8h#YOT zy7H1}=;Ts`wZY`|6XfP@>0#rS$+yuWa80LiCX93@cwH{Mfu`_dH8$y0Yohw{hnLBod6040*B+crvlk@hnvb8X< zaBz8BEwuLT9$rq4wk9SfCl>qcSh=(>vT`y$Stz^+kh{f2O$7_5XG(0S5 zr=hvc8ck)*_NwRS=dszUg&bPhoVCkFQJUJhc~O{Y-+U{zV^h`Q{Ezkb?Il@xrHP`z zSg_mtbul^Dvr&nMXAyLX6|`7HC8~I=3#+xP&mD$9@iSbxA=R>SW0@(VM;(n#yzK1k z<}6TpnK?N*IVxJQ&i6Pg0soBoKr~CanLRd6lAV1un-01%ALO6H!mz1i5kCm+ZEMuhy@xq|Ji+rHc=0p*@z_a+9^R7W$>k;;WN&Vb1QIqAy;)ar}f?9SWuNu1~eE zoi{qu*Oc}1;^=#8tRByN!p<2_Z|3GhK(+OqjeyW)sIzY+e$TvJ=NS^YVajsnf_ph0 zx*8h!*163tTg+9-ptZ8XX&BCy~rI_oRca`X#ql1Hk6@fCtdp|1e9bNq$9UrTb zexxTUtd5<+dK;FB7RyrbXn1@n7SfDHg5H%Cua{>C-AzwPJrYg*q1- zTG|~2#*m)RM>eu8l~#U5__(s8)a6!so;6;KJs%aR>lSUl!^6X=QCn!ccuFwWue@G* z{(`l78XDT%TKQP&Z#+$N4`Qg$ab`B=91h~WPEMVf*m!D`=jXn{M)_8q_I$iay7}6c z>L5ls2bri)nO4pzVtuIpD)byQvGMAyPiZx79*7H&c-X7F$U8}jH0;5aJQ;fUMwT0HSw|`Lv?jp78%|@DrGQ9tn2_#Jj1ty0^ zCMF*oJeYV`N_lnjU)I~3(ZLOpE9irzaXkI~l}fX+uWw?g&(F`i@6Aw@Z%ci&lR-i4 z-3_f2wWX(7MTG_{@?Y)I$}jA+XtP${p04(+&(}sLsDiGBf*ELDALUlLqTYTPs}X+| z_DOPA_k4&fq0vJND=RA2Qp5^%@>{K2iLtS(bC(dq=k<1$$6Jq&E1x%&P7S4+`i08M z?kh~SQyuMnsk!;)y3x^L#WQbii^>(%>`~C;J5;e|&1bnjB|NqCWcpYb8gA1FDD7God;os6dqa|}DS~=qxmToQcg_KtC=4N1)GR&!X_ez(US!RL@el;6E zp4L%v?B1?iyH8Hs;#69q@aK3c{^f=q_;G` ze~f%DWLfX(fsLr5=~s4WmI;QPsFUoUuiK9lEB>#9;uu%n25kF;0*+5<>u4qyI7ePW zn)CJ|)KE`IsS)?Y-f9g?3K|l}$Ie&<5E-U%g06hcWuNj^0VpA&1BPU|MED_x zTNDGi4!AqfC~Bk|7Gq14A;6M7jD8ldb?{CpnNr&kz3N>Ng@ zrn|49c_p2sqL;3pxa!3%w|QKvv{Os3;G&(W-sK_v9vFl>K$aFu(&N9osslWP)x|!4e$n z^db#pzbv;TVvSiw?w~S)s@uoMv-X0^Yc+i=kQ;gDQ^}-@1gsU zCjOaJ@RMzC7-h8E#FW!<_N#r{f82VWcfPdUI!#i&R$SPzi$QbuLcZI8A+XwXxwaOX`(-RY9men6wteqq4dU-xFk1y^DPQN%i#dox=y%*QLif(@^PG3)E z+#J=kLOd+ny#!nD6)n4@H7{%v@=lCJ6%gdj#5P9vePV2be1F?;z2{m5Yo2#?K{pdpsf0m89 zxml*i-rnNRG~K3j^tN_TDN{RJ$Hg#pDl}-=>9k;QF4}X7B*9xWb7sULMVpdnGt%%G< zvLe02YSi%P1}A-*T(#yXzCmv!Is;eos$&bKFQubdtSmbDQEO&q_6ELI{Fq%QShF5zMh7@N~~-8 zO||(xL;c(At#&#E?e3|( z)5}R=F0Q=g3M=U(F45_}7ATsdwu64UEs4?T>UgxwF1~T)3z&}5IhmaqvbE&xq_cgr z%9pnH+{~p_AZTxkR9`L5&S2^Vt!%YPHwu+q=4G7}%%~ps zkY+_uLukc`P;K9B>E};$VxN+C1%sMsqTghXI-u(APZbF(t=8z{VyeoODI?YB+~Vj< zl$MmF3fsTyusYtaH#*GgOta`PO{uZ)tF~^orsGujTij?{v$b1&j;5yVeVwydvDSj9 zmNoU>o=(n{Jxcb?w7%Cr22w(-raLcIT1>Hhyq&G82+<;2YwtBr&HCp%alTp)6?D05 z*}`?L-q_)>S&6Jt%noB&8pm^MX++A63td_5w=O!h-@nvNPmI~nUNmcv<2N>O5@fh! zt&(h;(`|Aow3lNzt5Iy&WXnILBK7w6jmx~<(CJf&MJ-$Jkn^?`aK%$5OcpLWZpuH# z{+hen+}l{GRyk_Rb4>Fdf|d`(jcUss-(@zAg_@<`q_G~Bl2pj|^R<)P*5bX*)9Bi* zZt-4{ZDl-C?CxmdVe8s)p`qchho2{hXl-h%M3Uasr7CJ^>6YK~Y&jjtTc>Oyftj|a za)N5w^H_JVd9rTLk$To$iC7oDC%x4hIBAmv?pb%fhf6KnbNFiDW)e2mwMqe{uvPaN z9^{;FKD^WT_VzBWv$0Sl_SRx*X{ETlKc%x(=86kAdb#@kD8FwOssEX&Li2Lb{L`^> zb5G|~%&r*n)g8}8je36X436Jw8Y&btYY{BZ;KQYE^I-qEgOf659`^O>dOl_5^^MJp zJJD74H&Ly=yi1UM8BZQPQ$7Uq$x^3Fxw)o)pJzT!)M~LhWz5%ttx9F9RoloueG+(; zRPj<;YIV#FhI#y81m0>Yi|1N1T$;BQbP-k-7kl#^ zJhnD8xt*IGc{daH9ell?KrDsvhYSy3SpZLlQ(4Cka1;QmYr==3ocT!rFkB|}#RO&D zws4J)t7VdCUCMR}WQ>uz5G2YDp#suIGc}}VEQEYC7&W^>uII%Qy{^lgxta#n&EJu| zUbys%o$2UM2nU$2Bw0wOYHJJwRzW$=L(f^N9RCq7r#28%N&xqPn_A)Tc7&X}6mkII^3IQ%7DnOdN1qcTSMDf@HNVm?ce6@~pscI1~aO?kwN9xXXh!TE4 zu8^NY`}uzpii2S$_XX{p(6G;GZ4C)Tr{h#_2-y*GC^|TW7Gea)Zle@LIYi{@?d<&f zR0#r~uG{5ppjAMZ$0*7WGgFlb>-NJi0zgjbPe-Li3tFIj$_caNme@3t@)K4-(~vm_ zDQMEM9grP+Z&0{O%bueoU*?L?4`+CLIaL_qlZ!Ox`BxGWa1T;%R!9F)U;&^|ic(>LY{;X$ zwaxVOTU|7}(+_}%DMTBqc}rVr%p4iG>Lovot+m;vE>TEL=^mnk#t05*`jacAH+Q(w z_bV?%$q+V&$Pt>kAj#pKP0Q`<@N!gCqTkQ*B4%5nWPplm|E`fnL$@6j;U z3_Xt5)=&H(;a(~0qdxreT6Wq|dWV8Q69*Pv;tFWHros6NNwG&WKo^d@9mcktG zakLhhx5{Se841_FPTLC_xo)plFxSw`3O(0;3>>btMmH?w(XAptA( zN%lkn_N+8c4d8HDwkmXpSqt1+aruYl>{w`_ZqXD{dQK--V_vh(nx*uLbO`hBhK%#c?W zC*|yUTXMY=Uc;+^`C94v3HVwEz&(Jbt0poaG=>T9<5r@>vg zc^P0SEywa~H2vs3<=^CO<}2`W!D;tOEBUbU?T$H1?e*n+iwCWj^;&#cN=H?ExA|sl z>O66c**iP9A6uI(E^VHj$0$Bp3<37`Cfr=i=6!h@ZHD(G-r>h@JCC%!`ma$jz2vqrN>cbpU)XLYG9jY# zH#TmuB=oy&)^*|K?a{~yyr)d=XttXhW3!K9Z#+u;&M6*-eeZWqlXd@BCNbMSh+aG;S;v`+F{Qge-7v%)}{`ohn$Kv=Z z_2zVOZEbCTA9g>3mV`>%bSN0s_SZ(^-+o;;H#^(CyLI-vpB;NiX<81W&TiYji1iUu zs@sysYvA)q#o@!$bMJh_9oqBB6%*`Eor)tzU^X@@bXAy*7wpCDb-1d|82bZ%*16}e z-_wfHze+TJ)cGX&eLjC_eY?cUDnp8$J>P@D8-)GD%xLB1N;4`U=cRguuU`Mc7hV=V z8nR^W_ESFOk3KB!=W_$Q-I;-ESCE$9?u>4Ab#W>c*6qIVw5xim9q%KxueQ)_;Pt(F zw!R(`%bpLb^U;9*6+NBr$KdWlhbw+>v7;&3&b^$O1 z=K39)-3P6vO%vmYNWBT?!s`{+qWZTYZy|mC8%!+j^}A0C=$DF~{_V4-T1DDt;kUD0 z&n&i!g~=I}EAua(tDsdL+NNhiA_wXZei zyFE9&JT|^9Tslp3oA;q!@E!WrmaOomW?gz*FEJ(AJiL{iG`2X$Bf)0bDyBK#a<-?s zTW@tSi9f)GCFUm%nD+i=UAs7U zENb|13huP0o*xVL9m~(?FqY*oz3u7)hSooJlLvceW+C||1&{Ff{7;j2EABg`B2;SM zBvpA`-hTDIg?9Yewp4BQ(tXu^T-tHzzC-0;OWK&THZ{5-EbdFHiM(A|2eCF#_B(#- zRM2E>>PPQd*!Qn-k3p_GCu%&+wc7Y=Z6yEiTUx-hx*3OmMMz(d$a~-S&Y}JlkM+0e zWxA~5seS6x=b=OF?H)ZIk| zfdVagZZqXQ&iN=!W7h3QpSe~YTUUL@HizBhM0H~Euk~*CllFc9$n9|2n^J`|X!rWv zzLL=3+}zyj_2ym2ue&FElBLaQyi}5t(1t|BVU=H|eGmgCG9T<@ZjlV(0XHf4nQv^pWAudV>+Om)J~$_EERuAj~dcPuX`Q z`pQyrHE4``V%vaT7sDB0Y7ks}>=8z!c82UEGHlj^sOL=?LpiOamG^kqjEszojBOIp zWL^~TFS|_%Yo2c0l5{^8wv4fOr5{QSuPwx3I_xd|nB#?i#Pyn&&{x1?sTLLp!{Gf< zyW2+V1t07oL=G+gTWQR=F9#qg=WoeGMT#SzP`$^(Xfb>gSGYwpK>!bAQb6(fOq$P| zapC~LDv2aHO)+IuK!ZjA%}n5pAxwTzK%nsatW5of;J_Wb9j(Jd)P3*m)WT&b z1BUA6QmAtL>bog6{!=dov{LeayGXZ+!3_W+0YQbdbs3hl?^Q81K{{tn@gl*<3T6)Z zAxJ8n9k)Yt%@JjSxLJk3VQ@~bHH(XW!PL>W5FUX`5GfG`Tw9?*h`nR0uA0`v^@!tR zGT&Imj66DAw{;J;j*oG}*0_LubLg>FCO|0C>Spfzn4o~5I?t3>A`WFW+P>e7{)0Hj zN7=i-eoMf?`nlK$2U_cgmEr$eWBp&gw#BG!FEN!|?;eB8J%dhXPj1nb=L%L3$4p?g zNwHd^)ZHI$_fl5$HC-vl%{?mO?;#)zvFN|t0#-7bh%OS~1@A%ChpYfm3!5R-W#PTn zGD4qFMi(`;W5>bKf(d2m|n<#2{I3x--_93>B(}qa`ftg{?5-{zr7}#@L?rJr)-e& zAA3y~Al(_Tyo4N?-z_*R(?ahq=^a@c>k>1bn?hu{oGK|-M}}FI*S;tgN+x9@974A) z2J?mTvd;mPI7m5@&byVyl6pu~o_N&3>DhEtcP2>X{er* z+~|EA<=3<1ro*w1%ic@&x4JbuoUA;LegAvJiHe@yFBn1PnTefKHuAnz22&hH%JYBe z&f>g{bv3_a9R*qk1iV&!qka6Q4)t~{p4s0_rEs0n+5CPwso!sHjluxVtK6;3Kc8JP zxt3xleQfvF46ky}17(kWDwb&Vr~jYsf9z%AkLq(cYXXO5S!ZWA-RE_0J*Wlvm zTABJUJ>a;Ux%vU)`yHe z6(iL+qWODXlU4rY;?k~WdRgV&xK4GP=*e}wja_W8h4*&uv?q_I^mQ)LJ%VwAZcm01 zPlfr~Nm(jFA{LyV$npO+w!1&Xz)0!!|KBY*tv*Hd#VC$h3<<;tm_pfz3214T;pgUc z58-*;YbkXUN#wB$)W3zW$MKg*-Xbo491C*RUvl-kS!ZfPq54v#+4=Vkv^_LA8NJ&) z*8Re&>SPm&3yLaI?|0PgYOU$RxAoF+>**55;_BB(g-3yE?`sYx>%Z1>%R77NoWFk4 z6*x3@HooEOqj)8p&;%r?;XR4>9&zQb91)g6TR_a4F&+MWJ8DXDk=FSRFE!TsKq zl?P=g%R-Cww@=K=fxq0~9xu~z_fU2z{Jr%i?rDym-ljYXHr-LD(65SW4b1J&k5^%v zoiDZU>0OsIvkL&n*f;Yu`I!AoZ$GxXo6n0gQ{YOs4KA1MVr^0OKD-A|9m3et+_|q# z!}bY~>*nkBd645G=56UzS>Qso1)j4{H|@-gYm@d_&d1HvxPwHpka>u*(oJ^Eb9R5S zyT`h8bfWsk&}{4K>!aH6Jk`oxOM}OCHWge|^s4e#Vjx&$oP-f8Xl5O!W_XivNUc-YmUE z0-18qAbj~`wJnuy{(#9|t-R)6)xk}=16loKe)oIR{}v40)Ec-eYo}@4zdOe*vF)v{ zw`TwMe+7Ctd3t^+7Vc>4Hb%N$m-0Dt#+ohLU4A~h$og7U{Y_F$`HMZC$~gq7X<~lvvM_zCGXRq1B`kT*h{5ljtPCwesqqkne50uQ^Cp)*^rhgA!O%|SB z_V#U*hdNr(f)(YsS(&33*VyjC-FWkURZ1V@-lns%jYeC?j$P+|Ta>(OX*2lyEx#4? znVZu3+WP&!A19wq${&JnOyP^ItZxP~l{>F&Z`X@uf^+bAAkRern%dWU2kVk5_qLuce9dURCDc z$k&7EI&xu~sPvppIWxJEsrEkVM=X#ziQnv{qV(TC&hT7kD#cX>eLrnGMC<%jI5Lu& zjQ&2{doG zE#ubv$!%onYIo9^rpWi{W%B6uTtDD&rB8E)uU0=sA0zivYWTL%eYwwl!&LWky`Y@$tqaO#hs1q@UPWdM=;-taMizynS;E&gpc{5?h!P zGd0tx&=-8xU2Ej)>%4=df1a1d>wAtQgc_dXZv1~s$j4z@kI97wM#3hg{Oc3O>4{2b zj`a5)JDwVyn>eW)rGH<`oaXn>TGfd3IJk~`Fdhbu*wIt{62YP7oCJB z*DILOVV<#_h57e&Y)rYT-K1&WhQ~LX{L(j8%|}vkH?m+iJQsI|;i{bLWldbc7ZxN` z2IE-Zv+sJk#)W&Wj$j!mrHp#sF&$;E@)q{$rS-jBvaL^l8P{o#1D|fKG%JwnK%}Za z5vky)nOj1EZOQ!DJ|4slCF>C6dWwuaN_yX*B#R2ME+6 zl`t9XYfDeG(r;l}^p_%_rpB-m{GHqv}8yXjT6d*9QnA=~6u zDB_5JpseOmb|ZU;w#hb?pKteRn(HAZPU=>fYXsu5vobb@N1u6Q_<^PDTtgH5I&v!; zO>+h4i>R6?`cME~MH5j+iRw7+Vig_%$<>$4MM|oBOc^pK7RNQce#pmBFYf^&NYt1@ z2YmnP6}hwt?jSY@(XS4G2=Ph^qju>YEQAim)aXuR#NCv9#KD8yz^L|UCO>75K#WWv ztaCo|5rH)$H|#0nPpQ%vkXN*ca{CZMuGz*IVqS9h!iWMNe33kH#QI?_ocl8HZR#EK zE2Q!_bqVd0=5si=A_LqhG6E*}ASjD$fhi-RZeI+L5u$wQU%n4VmEHl7PwriPBv+lU z>)4#^AJPC8^&d$M_wM_<{QKxWvG5g);5oi7yvhSI1z6o)V?3s!A}Sz+ioDs6j3$GB z@3il8H4_DSKZ~&7iyPn&g;V1E(eA7jh%3(MaJu6VGr{wnH(JE_TD#JYH*)z-J5Kz= zg#&#gjH_(>X?I&&7`I9Hj?$!4A&DBFazJ7&yeI~BPh_q*YSB+ZmG|Vxj|f${?}NvD zIE4lfn`e2AcM4RsNzf1|;GUH0p10C2J^$?~81XCn8ZJww{2c%3NJc0P6DR)b(R+7! z0)?Fe!NiJ%b};vCg58)Sdhz^;?@c_cjATmQa{ZibcxsjG*o)gwyKasr{1J+on$oIf22a0?9m5%Spz8oe#__Krk=3vp`_I`#iiuV$y zMo3o0Gxj5$owa@sQ+J#>>OV}|0R`ZOFZlvB`}bp8Yi?+w*~ECU|JM*- zOZT2V%+=3s-p+5w9QXXv+x7E(X0KtfUg8v&erTU$t87=PsYm%0N=8MW<*(@xO_TAL z9%!n$>VM33OSgKur`a9r2; zY3lKxZMbV)ZgPWCtk30XN7UyT!=-!Uo#gYnzi=D=pt}xBx8c3~{QkdQ<;4#BPX_#F zwb`eyry4h{t83R=_NkqfW5Z|k0h-kaDTYsJ^y z;P_oHP5po5{`MWlOTgbH(51c5#hZ_N-eo;78ln-j|UP)Qo%k}lS@@CQ5QtgeN{@!Y$|Zi_$pa9_$kECf;#X24ZEg%TL&vvlB84r`^d~!f5}2w z`Hsdi)t_fr-3pXE6zkPrR>tRq#TiN4LrFO@rAf*jdbnR$CVD7$>3Np^AGGq$f@Y_z z;@d5s_S>QQ=zrmj7v^!<{XZr2-3Wi#WiPQ|*S9{5zqww29o0{2gEvmRstNe<^{QKW zxj!k%iOg)PTs+)LdxNyUZDPf(4kq(n?$(6wN!oV~>a2R%Jm*G^H{to~9zV-%-`#Eh zmeRQ9X|u6`N5O+l4*e?~pS5fKkt!2k^eJkek>D{+lKi3sl5%~4 z93T%50j5ra0}T`~MniL~=TiURv9_Zb?7g;|ADZEDI5qCI(bsVFn0&*Zlr)k%M%lam zB>0d$M^F))hH~}O3;R^k5jQplz{X#|!oorKX^cJYkb?0S`Uk8Qm8XJr=1h$)q!{?8 zFxP~TzhTmBn=`Z!SL z8L|skGM1(F5{QQ92BS7}U8R$t>on6w=24t$+f)|e5r9jNY7d2={@WRutNS)z-1@=N zkfkJ(wCmTE*}7@m{M7gEoeShjA`cFor}d{H4^4 zo>Eub7sl;EYf2i5J7Z)-1mI&>I(GoH(@T}&u<2GZ{?n%h8;Prc!&Ix9_l*UpRv%l^}1CW)<`^6v_)Jj+GVj#?U`%+ zHd`B($>p+mR)5o-2!dYu2o7cd5sG`guj)0O$Rim$>tGthpLUCxwmm^QzW9}4>DEyu z8X(R;gv5AQJENhgYf1(@@w0>-7zM4xtqfL=2)0Gz8oQbv_aZ1!u%_Y5KDd}Fm0SP11De@fR^8vDc;UoRE8H^$3 z$$Gv6XGt3{dMODDkyd56AD2Yt`SEmvT@0;VY8wF@Iv?rsCD?-!5b)l5xI*L;UPLt? zzt8f1oVqe+pl*MUl-Yu;C^@bqZj4h#`S#3^8_@8&oIuMmokDb=(rTk~B`Dg(F%9NT$Ju#s{s>#^+#CO$Olw9IHw1@u*3_Vw z?2}v>{)s?sFIx6aXg)=9H-(|LOhgD@sRN9Ge*}5v@<=_Wa3^iFmZ_%&O_N5lacmG1Ch1j{IC|E4P#nnHHDJ&=%UY1Y7O1 z&b>Tp9`(C7qU~Mk5A@*VO!73&menox=iZ2e^{GVjYx!}rYqPCVCpR`-r(Z<}CpF98 zy!?Bd{69m27M~*!s@K802cE>}^A;+^{`{8;{th@l#GBRoK2)xE9^KE{|4;E+GEetI zN5g%y0-b|!Od-Jg|)lgfLT?o+=D@yn>tE55Gj z%R=Vcbavh9nAQ4Hmi3yM@$V#<@!2v&PX*MZKF01J@_)k1PKVdnKlD2U z{Q9fxZ3>MfKTSRK)DGuLLEC>`uVa0j(jOmU1wGT&Is)tY#1%aCJ2Tc1Pgdqr;Uis# zHi^}i%0=&Q+QG>CKOUDFc(-nP>{IG+{EK;&JH;oK;iWHwmW9vY+}CSnPuIaWb`KQn z2Hq{+raEihDr)EcjDw}>XN`47_ zC2rU0hwCw-ZBG|CLoafLU6j^~^lfo{znX-dGa-5s?Q%Al-Y3M3{K`MviRK^6-1De3 zi;Gc}o+$sysIKO{9}o9&9La`9Z6@w$aGoWprYa1db_-%U-YG)Zq1QYPs{tw zr`+4bXPqmp`@w%7`|pMN^0!$L+4Yr6vFy%5prrl?qB3q<`O}yfL9i^BdpyeUG=hW2 zPgF#%l3&Z^NkRvLiEJpJ0BS&$zv%#52_gLYN(CTb!IR5%rBoiiVw1S?+V1--uNnKU ze+45$yayeq7lprQ6floP8AUXp=pQj`fITD+(E@N%B!u{&HsS)G836VT3;vCxd>vrr z%7bD4szVSmWg98kDMcw?nrhj+&iEK6$Mbksz2c>?zsZUzA=5(zHhsWIcm99Fw2zOE z&`3yN-2f)#BE+3lh?Xp}!;nEAvJAma0YFqC6M-;PfFN^?0Mw00xxG-7ALT?b55^ZZ zvLMTf0i{2{uc!lhVd?q_n@9!dqBL;iYDmu%i$49r(*YdvofC99=*}S5GzXx=kwrDa zG2~T_pn-BarsoV2pOqC*W#(KOX=Zyy|B@)ByK;nCvY>WYP}@{xrfLP?lb*UL0pTm^ z2XLdwK}9H(*S&iqN^K~Lg1$wlru?V$c=$~u1>TC{B-9k02qFTJM^GmWSTcj=ss?(@ zp*`Ovs@(KUK#wsB(Mu6X8TyVCg2)JfwYl&VLlC-7*}{kiK>-XQCS$hVb(=X=bK3{o z;4-#HV6asxFjxduF1RK=K?1U0i1w6%BMXF4C_{xYByh<3^6${QsqU{m)#$vYPki(K zO)#G9=AwqOUXb__?MydBMiG?Ay0r=k^$GxNn2%cimxTl$#iXmXB&gutS{xw?u)S+a z++#dcsy1BR7Yz_#!Eh!1FyS)Q4`$FCETqN+0&^u#%wMQj`S{R)wz{NijuLSeOt13U z+d!jWcp~sidhtDNk+`bs?~}vfH5K+G0K60WSOX!75e=U%Uhu(=WYx)viZhxYD|!sC zn<;p{?6HpXM?yfZN^97e*v}rpq_};c*e2c`n|YH>e8%LPQjcikcP&^b|avFV6LOf61+Vt8qZQrym>lVzRKnn{?K-UCk{?mfa}NFZpb1vizDCbiDl4 z9=VF!XkK}GS7gQ0XC#k9Qr3=0y=N>aTNF4<#(H>+6Vv+oJx;54_Z;84Obi`UZYAzV z{mkEyFT0x2v8=m!uYXHw>F@P!UCy^iZPUJ7*V=X&oUfM5DaLHK+jnnsC{8Y*tF|ZD z5;zC)b-1NeD4hF5X2=o{+gGThLx0B$x%D2@i+9!7>|NUK8`y99rp7OZj`CTaL*Z~P zZnv^0&6uCX-olB>Xt1=1s-Y{p~I5-c+H%4>h*+2cp}3&zjJQNPEfeu7uulM&!by`*1?aFVyjRa1QPa9JMq zpl5r}|H#$s+1bAY6m0nJHPRJDpA=g{k5LD=n{K4yyK*f-H3?zgWa7u|zIr^ntNQo* z$~>{Ynv1?ursN~`7CFBbzjCK%`Lv|i+^^Bwgu=qBDG|z+pFWVZ-+ON#_4ciHzE-?f z+VFC}O!Ue1p0KO8N?JqFB>ZxtieMOf>mLd~pHd>6_6>i(sTPVAH|C(o$x)S40XgjG zbih-y(C6xlV}XRnOyFUD-^X@zLdMwO>PeX8!Cs$0Zjg@V#QMJ9bI4}eAKr1|vnOZv zPydSUC+XH)=CWzKX&GH;dFbjP(iQ2E@$%9lD87x6(`Oxa&$f@PDK*$x8JSmgHZPN( ze2LTw&xM{wtlwSF1kC=kg%%4c#JkE;Z$@n$I?SsYe9!8GcEVOV&jM#3m8Y${YjLHz zflBuVMYj_t@g*fYMPn?}kDha1I-i!;%lOY(SfF6fi)%#5(^}FyTEP8UEB)p-d8B{S zMixAohilAIGyC)wI9xY-Y0I2;Ke<}F@89w*5tZi4M-llVe`T=)10DnHjn7r^czT=k zHoszL>YZ8ta4abAsQGs=b`y|mle*3@C~xi%xzjJdMrm^A(#V`Qj1&_q64kNaMi1*- zE^~^!V@AKe$#-Lo_u`tleV?Dg)jo%%!0QZ~9)m+MksoM-6m|VR(v0l<*Tza|&(cyf zyFKeSr01{xP8I1=h$TChpdT-f4fw6OfEm(%#`v!h~ulN50}TE<-a`)B{k*z*8vG^0{$(B|f_ zp?+ZNH0#qJRMPhmb({2XQ)sd2cWm_(^0#TVWBj_B3Ojph_u5L;70}4{$^SebEzbqL z1{hzHm4A0q#x#9zGyA1^blmED_1Cdi&)uc>Ia7UKDJ5FzeedXpLZbhZtPKYgr>=hy zIij{jAo0PYX5%xL;QO}`!^xJ3?C<5@X)ZiA`}7|j@x7Ty>x^5geBT<*bBFb$ZmPeV zwgc<7o^dSM3C1D_dHtKOi=kC1t*2g?IZq0n!~{M=uR-}!`!_#sbHvqnfd82CFmxk% z42eCQ6&nk)%fD8#cZr8WCGsoB>eg^O@EkiE#d;NGr32Ld{_$d_aGh^s)KN|pq{G>4 zVpOJfDvLrVV{>K0BBD7s9&6HNbJtCb#%}ZbtYal5lF4MUSuA<(5o34>6j6&#Q8_pI zQi`!0Zo>T!#EEPk1)OKsy%k%#DCm0%Y%Ns%vtQZ0?$t6FYL*8(y>bF_xx>&RyhrqM zvegu?<$r?>H>yhgE&pcCmQg#^;UM>vhZ}dxVg#0%GHsg;3N$&H5A}jxdgO9U_K7-U z-Cb-a*k)u&P9_W+A}h5X8v+j@o1XwFb1BaSIeQz-wV)cmsN#`(r9n2;h||lX4v0iA z1^Iw*4K&)$42k`cZ?T|Icl|pAZ3kNl$q80VZU(772Lg@@1A)M?arKl@@WyBaL|9_= zfvPYDgaIMO)?w0HcH;5;eYxG%a#2MmTcqFHTkYvKha`OYV+Q}WRK^Gq0u4iFk3Ze^ zDQxRp{UakIIDPsWpd<&YRpWZyvI(>0k~pa~f6L-jS5+7fhE=H^;oiM2I(XnWd%e*d z(J@GcL%%J5m)eDd1W;vCi~UbSO4KjM0TT9q5ZN7sk}>3tB_o?CP@ach=ZPMHBY0>N zg2P%M9*76F1IgMU6WoLy?f;TWPE=RJQ{Ddvpms4@EP0}HtQuQqt~o5uJS?`C@*P3i z#5)U}_|Pc{0wWUBEohJ?QQZenU4q6COsrzUxTs`5*19tfkDY>FF!sh7XEHWhf*ktm zA#9D^u9SwhrXjiElG8T+h^Y?nB`~--sx^Ou`@0&liWjXYC>*>_hBJ&}fV$uMLH2Wc zn2YVf;DA{9y)pV{#e#uqG^U815CbMup9|JT+SO{gU*zsh>l1<7`B%FCb1t4(lC>)R z&8pq+`QIjbg5o<#yyg@0zPiWt-a|S5=Cyn;I+M|!nW1Q_Z;p|f`DdpLPokN=r~7ps_3Xw*%-)@cL&D#<$@%Qi zRiatvC8zAgJAZV2zwXm$3~zsz8fVALOR>7gZJ)~4&E@Tt?m;xVBF3b8bT*{lc{?v9 z`H-#)H<>#eShaer`+ReFS?--9DeGrlD;3TdkMO<9G&z}r9r}N+_mf?{RE#lBQQF2( ze8Smz4EnlBrh5tTtfc8v2C5#_`@Ef()xVGK4`1U>C6#%5vgVEx3qdM4?=qGg_D<^RGuoio5FM-<9+AWJ*v@ zoj>I<@Hafoaog_OJ;&s5|6Z$B;vp6++#+6At-E0|PnGcN+dF+%)lW7{E=}%Be);6A zeg1l-t_ez63JHlz3x_QB*eR%bQ`Yaa1k&V{$Jg+Dn*Hj#BQoF14+HwQI3H1{PWNYj z-N1;p#i(CDw*%Jgu~dJbZzfHRxDssJ_AskpacM0_SZ#A@WpU109ywF+sp}v*dth7B zxjArl?jy3-DQ=Q+XS+8ZJ-apdj#QcHxjx3BJB&RWZ;}VS7A-xCF`(d?D8J2@8=v); z^2@kS=z9!38C^cTiOF7jHor9~{<^&t_#f3Biupf(>P?#7W`343 z<$T**Ntj~^w2}Wj_(}=-kIwgZc%m(bwf!$3-l z45+5p;=D0_3)TA8^;&lF#pX88N%Cjv5LB#>yX%VYSZ=onSQ(GWMDtp(M@B>4xflHH zOPw_}7QD+U@-Yk=c&RuEq<$oBL%)6QcW5FDu@SN$ zVqFPJi$ONTP;o1ZJ##?hPK2%Hw3Q+ztXf`lvgF2NWj=)lKQ>5v-ur6AWA^F8)0ha% z+q4g!nlTB$Zq@>Qhv?4qS%Z(yVA)%UP$`;Kq#9fz=!bN-;lf7;PJ~_hR>ZOtB`80= z`y+u9l9+Cq$CS>O%oY`b=w@Dp5J02Es!4!*uzY3;5TdFM=pbI4CBgWRX;U8@U@NX- zoV&(P_OYcA40M~p5mYZm??+Yiz$--Gf%Q78!(1(X!#TuQVKO@yM)*=E3A7MNPhvTa zIZ{Z#Y|a8R5X$siZc}mT4x2^=krn2(j;|#*$X~elujR=2BqcAMJH8itkCR2uaPi#% zAmA;Fj`jL`H;BbpG;KWazm}WI_UAV!HP^B!Y@{U#EpXKl6DM-^ws1vdxA~z+ob^A!p`F1#@=JG6H+rRk1 zcro5Wq7D%C@8A02s>>k7hSzpL9^*}#yy%jP#Sm)Iq^N++i( zCBNLjz(^pz$6jL}IgauimiAu{jW~h z<}me;l~AAnVd^6M0Y0Lmw^c{X0e3-XdZV^B$wpJ0#mb1ue*sP3&#kuLgqO< z(#e^ZoK}j!>-Bl$tea+e$EWbUTC)6_dA5-F{Ao>wOPo{?-v%D5o>SgzNlM2Q{6P->i1uG?6L(V%3 z6(Wc7AW}ZGG|_x=dzI%PJdU?cYpwTKMj&H9Xkq5^rt?m$PtO(+*;MUb&kS#Bpo&D^#x1(k`K}2Me`iYd=f|pIK3$Dk)}6Le z*^?vN{l(mil)%R{KK65ZpsH1WI1trrvy(SYyN?d**FAWe>)j{&?X!KiZwom4avzuJ zBaaH^#zs|=X0Ohfdy|d^ne7t|g`;WFA=2Zng>L44vMEZwT;75} zNN`uKW_)jHG;}unkXA|5QP`YFHkR)7+qHIHYkpN`o-QtU94GuQrZ+ZjKGoM(%=P&k z)c%MPJ*jCa$zlDe)`Xr!g-g^p~E`3QIWKd-u$J=Mc8LbMxZV%$~X;V<=iv@v(dK*94 zuhEMIeR}Y|a~`E{B4?&X`8CwYrN8U?{n_ztompw#m(A>QgO!@U)4s9$DCT*yr*Uv? z_gVWwdX)J8zh5`ku9m3@>m%1TW{R)+%&LwK)hz9oKgnHi%!8$=_PDXD_38O_ymx23 zw4BeRypXPQ+CP3I@29`-5}FzvJ(tKMR-^Z)19&q2eE3%`MZEY(v+Tp93**#Zc}6Go^a9Lf-I z+ExdGg8xprvP@2jnyUW(lOg;y+#Piui9a+)->dbJyZ8U=O*!*12eq$Y<#Nj_u?fc> zy`_?4Z>~H1U-nGT5pDmkz3yZyk>q?2zT>0Jw3Ot*u57?gR9iYG-*suTrrQXc=YPvA z%fE77ew}Jc<{B1iMBERiMX=@ftpT{whbVoX<(umT7eO0ppxWSA$Qy%RwB{o~_e`d> zB!LjZt;3ShLw#&9wGb-Yos%$63!YH-?4B8upihGQo% z*yg$h%=Y_Fxcgt72?Hvj|8MON;)J+^s6G9TA|E8((17u7%dkP3;S$mkm$SUnvE4sJ zb-#4-l9!!pB_otr4QXdw!xlakMVZ93P;=7Z*jBWH3w@VZTNc(^D~f*n>{md z@SAK^6>gT~iWZmaiO=P$cQTWy=o z9!i>$mtzMO{|`fy4vQvZv_}}c61t$ZD57|E5QPM8ac=IUt=@|QU7c8W(=%MGPxE7s zK1O{cd;F4f+V^TTzj+hHG>^Ke7Qs^U@Gb&;*U`tWmh@N)*K>SHN9X72LqVA%L!u(F zAvaB1*4GE9)G_~-nn1bWG&}=K9d&}QzMxL;7S3=Ux9etZTWEvEwANxNm=+D19 zeZ$AYb}{o!G{8J3>%}$4hJiP|63+179P3Gpoy)0z4OOGw+H;lAwPXwQjtrpO=K1pG z2;Ys`v9;%l)2qEbrxX0uN83|_WYL@eRh1+U)&?e3efQ`4@ncU*d`tfKBoj|ev*I~4 z&{U#?r|+?uHV$`Uf6k_0fFuP?w{L^Ks|oh+kmY@dk79j(a<93$Kkm*y4UcOsq=ZP* zTY00j6u_w340=r+iHeR8r2MEbYF1v} zVLEFHNurnjr_K57Em$9XStgeLle>tB5_7M3cT~(*|LKS-7iwu6hb||y_XR5L*Bffg zM)q5{ScRYSwT2^Vo;&7zEda(omqrhI@(N>f^ZVP-GW7g^=HvQNmmHDbLu7t}0-#{M zMdmKDbUU$jzk?|^x8`Eg!gSDSOJ^R;hsWS=oS(ejvzM^6Yq4{lqQ|1d!SUXD^K8}+ zb~JhTkZ2CSJSDOy(Xz|?wKpyOuG1NGIl-ivuR1P}N9c10>^_D3E>G#t_bL~Q{DEqh z;{SB_=6`SBhw$<4pgC|S9dTSqZSeGIuebl+dY!a?WotpFrM+AIslCs=`Ek-;-C)=0 z*``xIuw%%NlI1Vt-QVWl>SpU{f1G$wSUAm9lDTM5Ip+FuEq|-YTzPtV8|BK;J5|_n zw46^G`QRjah`LRGtQywOPajxj%%2h&dERnzfEUjY*Hm3OXCuv%vsN+nA8XOwH`cnr zBOLRcogTB%{`GoNYTMgkmttQ$^?15A(2>=y4U24i7hhIohHrNi7fUx*E5r(x42x$2 zLp4&*vnJ+CC_hfuXS?0;qFe6f;mHTTkN0tQIDU@?KM(L7Yn->XvP*e}X)RFoH17`f z>%;ypCclD$DDyi9SjqM~d#aYJH`rr@PSo9|P=8a3i0jnMbyt!;iU*%0(ojvU8PXINUCMJ(@imvL8&?+AiK+ME3Zjw9QvScUE@^Hh0tIcNMsU&;Bf@M&}}?B1nc%t0;SX=}Vr zbT?8y^geDg@|<$o$dq{i)D$2>T=Hu10G}+N4uc3oL?lG@eMWb!ML;7X6g(lF5EJ=2 zQi%LhBv<7|VoXYFt16nZgE4>e)}&+U4uJp1ctxbx>K+S^k0CU5OCSh1O>@lI&o4{J z@IPfH+^a(%ib#=eC^ZMtN7SHbiKeYm&6shYd8&><8}oAheU$stX#8?}&*O;D)W}Fk zNJvOJ5QtEaDC7*$6(Wc!nNE7-ngbt8RJ2SKK$HQZfNhY36Yov3={ao^+;)dRPz`Ra zMu_(a7sI!~s7lpCkuYfzWYo_~adY^ayAOpg<`|&*?UewUZdIW&^!Sg5KS?x>=+=3&1X7$`&gb?#b=Nm%`*D0;FPCO~byZ|<@5y8_`JNU=>xc4MOX(1I4rSlFD*`>$iB_^%0<}3O zKYekY<`K7~4f8NemE49}F)4xO#ThO4^tGc*&OXXxxYiZ46#sAP}G1 zqk#~CY4qrW+079eDZtbFRR*ELaZLyYfM%S(_!x#j7g|!RkuBNfB`#exvW&>_JAR-j zTq>7s33gE6;rnBYo;L_s3=h_uSR`KkNC%DeL!k$AX8cxI7d80hu@Lk(tDz0!ru$BH z{$TFKFS2oAG`UZ-7aol~_jqzq&-|`xr#Oesg7ABT07a-^I0XR2pv3rcrjl*@DGQzl zJom28G2sR461u#UKY5`(_nCi>`_&0&skG6xA^EY2zWb-3Q=i;M{U2v^RoF7) zTt1L0ZJ7F3bE^~f7fT6IE%}c&86U{X?g{pNwb?pTeojUGH?S?7x{v*p=YH1XAB){Z z+K-=<(`l;OxZS#$NGN4#S-$$O@M?WdEpN@MoPVpjbWG&dV(q^EjV9;+(#|d{t;L~D zo#OQJ>gD~v^F4h{-_@;w#Knqsfsu*5!M}~8&o!L&v80Xj+4#!n=$bhpGjh1>=JgXv&QlXBOioyQn_qxmau(zCgRwa0LmSilJKFF*mtx(w3f1{g8 z$E=EshTc&|vddPp5rx%X>3J|a{+U$d5q@-qoU3^a$n!zsBn>5Z+@(;vTIafi#H=fR zmr8v_#%TMd&#Tv69&}qj<8=RFfavAC`dbD1FMQbj4koPBQ%h|I*ktKbf@N_b!OSAx~MHTe2K^1-<|Y&EOZ1#c8a+LZ)>J<*mrw=$=Wu4UqJ6RA6k~r z&#eNbD|ozmb3d`i=4CJPP3E(}9)f4A_h0HiW=ekk?%S+I-CE;>SKj}JS1xm_LjRwi zi?b1{r%wePZ`cH|DTYv(BL=X9el_fxjy3+3G`cqbW`@j1x0#qCgLcq+xy{Q#6q=`s z=TvMB2i7xU)QK3v1UI!}xf=-2f4~9 zVnyNsdAe0hiVNx?Ae9w?Z+&2gdb@2ljm;-$zc@d)xuHNip0CGKU$iN$^k;Ls`_Pn> zl!=t;xSg_sUUMPu{|faDlE*rQH7JO4Sycp&-4$}ea`G4h$*QGAchcp6!Y^JD&Gu?< zR@!w0LwZ__skVhKqw9LKHWHai7G?^OSQ5)`jG{B)=Gnf|9y3$m4v>Z#!Lv+G=%195)QfqmO|k*0X1Ga zanXcsNDRor^>r6~JSWAYL48N}i}o&&dvi{SNyn^zsiD%3uMAoGer2qzzt`p+d)H41 zL;r)x6bfewNih5y#iEY5{(aqd|5euyhtUrErpIC2*&Z4igN6w%iVARKXi%7*ZRU$i zL!n?Q>C-oN{C4=J%goPI;RiiQD1o?M2a;}YIcE@pH)RWmi+lDj5#NXiz|?|PN$6zwCwuNT;I*DFqXd6d}|H<7qY zFBQ*r)wtIioNYE}ZPho2(Ic|cyZ>#zE~|MM0yYV+C|@$J)l0^wvo)$Jo(P#(r{np2V#?;$;Y<$+!e~#UFu|^7TO{L(KGLk zh0(85tx|l&riEXdg&VwzIHjJy+ijKAzcqRO$k?~-%fynf#Lw_^bYt9_Dl!diJOnQobr>fpzz*>5Q2-2RmOx^^MtclSC@cQ?4sciiY7 zJEG;`((ZO2dPpo4?X7f0UoEV{2HRaN(}210#q&=^B`UQzZ@}vIMAdt*c#_*AFS(}p zCV8IMPrARqlGD$T`e)U;zbnfs*5K*W0i$PlS^|331VY5m79)H zcx$nd`MwWH(0ubhp>`uxruocVILsotSeG2Hem&=3fug^h@Ypg-Tjf!!rr+f{%8o>p z)OtTblj$2(o^Vz?nzah7q5TeTZgubKt-rU=Goqt^l3P9(zY!08qk9u5Y4_j#s`LTF!q*SDRC)4)i`2C{z4?9lxdcQZK z;+{X`xVxL4>KA1FW|65L_^)!1q32!C;{AIna!X0gfw~_?Q9aM2T80%LWup#`iWWZ{ z`SI*=@^9Wq>Hp98NXCWE+F!Bu2VX4$H&J(34Sq0SN&S4@cWe~ADgoyO~|Hjj;ED>^(phkm32yp z)?$5lm%Vut{7ypUQ*SX&_-Z+4_4}#k@J}J5D?7TLH~Go8P?jhXaTVqav})t}7AqUn zy}&G{h)l_5^3M$|uXGxN>qJHI)|Do7XX=YV&@<}N!@F7J;@Zu5iZ;5ZjgApiKYFEc zi2brqnyJ#yv)9b+8MNC>Tz|Y!T=xPoe4Tzi1-<3f^%57eR5e6^c!e!@QQk2_^~018 z8x5gUT+Sgxf7%jn($CBc2uQJz!Q^7z;9YOT=~C&9nzIUZ8Ch#Icq&ifp)8h#u1fx_ z6tm?yyIP5}+|M)cjx%@KB$?+VB!>$cNCUnL4#gkOXL7TJ}c6;ma zN(c@If4w86q1eC53^LyhHWj;$W6MR&YGMe#D@^sc0}ryW93moIl|i6)zTBmMvP!zh ziK$A@aYQ@iBhvg!I#N}`B9dh}1*hpSxpxjfru*F`CwnO#-LHRCD@l+w5FDnT#YzJe6jpo5U~+GBCIop;@Ty4{!I zZ;ZwDT4|rmF7FRw8;|SyowCh6P*Wb^2Y(^Z+##wKI3nxpBAvzn7sD$2zbM6uego#K zv2OWH`d`-C1YHR9k#wuBo5F=Lvf3FG_XRFFM~w9`i(QiZ-0Oee!L#fADQA%CI_+O) z(kPW(_xqh*KJ*x~R#oy!-|na!|MSCx^>n}LyWw2eLed~imcfuxF`6Dtfl2t7#hFp%PwaljSpSM-p{|Q^6jj}=Q?^t zk~$w}Q7(dx99^54HeXrSZpM4oD|47<>mlK@j*#fQCRVk!tyb1KJb1O8Hawry4E|={ zWxE}=bIuwP|1{a!wsZI1?V6!xEs(e1V9?OoLA z;odleNhgVSSvRQDz+3C-cv(Q2*YhJZJ8|<0>x7M`+}FXm{T}Zod^X!Larapin7ewq zIMIr$on0CE>Q!All}znBPo(UURGh8K&RzRI#r|AC?Oi#G?k4Xo45~j-cVFzL?^*S) zi*IG`uCEi~CHJ}N+Q)fo$<3l>UQ_bDOr}^`ixi#lucUcXw*Q^_=My$mFJ@x*8TI9# z`zdAI7N<|~UCtPfH2K|-mtmy7^!nOYm>Qn@eu>$h)`97m8`odk{d6@iZI|&U-$y@X zCQ8&jTsk7vH;n7=F-$IqKK;@r{3dJK`UD19z0DiSzr!K*(l6(~de`v%d{@#=`V~D7 zze^>a?&DgmUtCVDJM4Tk>*H;CUgTDWV>*S;Bg66geVpV&^_<$8Z_W6*R$U!W*!N{p z@koigyUQPSrA=??;q2;8Wzy+($1W3(Vn5N3)Pc{H+~<|`KV6ONXg@{iG&7NQ8dmA) zrSSZ_ydQE5Z|>u&+C;S+9q(7`+(-_9V@?VCFSTlS{^s{%&hAs{dzAk76Uv!-n98-P$7`ee0b*8%^-E1Jo@X_EDnBM& z8Z_2;DaJ0L?5jI*Zq~=mVP@rDaq>V#uJ7_|)@z{6*hR>kr{x>zpz_EqB-8u`} zq2d}t0nu4Pa6(^dE4}t)bRGh``)#prAGi97dlo#3Zs@k6L7 zkG1lX2VzEvl%*Y)2g3oy7BJS{hS49e+uv^hRhqlVc!-@S5yz}L79tBuY#YIff9rWb z7{l8Cafp!*Zs)`?9V3Gz9Pk8yw*L36lS72$MUJMo4A46fbOGSNZ=avx+Dju=e8&oh zzodN^`gs{@Y-26DiClYBeG1A7DuJ<=IL|2)Tr%GWy+ttNsapnP%yt^!%>CN7$7ovK zZQrMF4u8O=mGgrGMr=!JOVKqW=BHSL^Fq8E+a8e|4!5hjO2G1UutfqV5B+0;jQ{{N z0XI_5_Hzc~T`Nn!(T2e`bm|}&w9u}qgj!=|Y!Ij+wxd367Mf0*k4?l*Ug`#TdtiaG zxU9#26Cw86{;#yiWpuIS$EfqCy!qp`oKlsiLLv*$p?E0+f>xj~6B+|6Acj^0PpG;q zBFLa#;>IOnP$GKT+C2}R_!5rOXpEBBV!NC_kvxBULM@wFyNvBsz0(H5%+tmTo7NsS z!9FKQDp?UOiDiDaMDo>05;o~~m&@Qa_{hM)5|NoH-C7m~o!0`oa|w^=z)R4152W2} z84Xs^u!j$sV;Xox;X@BrLQNDvmaF**ePX~udF+Bqea(xnpnIN%3FJEZVU&bSk?;Q5 zhY!j4hLB5j`X1j!H0eAy#EQl6DP%yD4+KHXPM`Z?t@vcDG;DDECV6fJPK z_x6Sga9ci;xuaTvMt z+GH$vIu84E-gPg<#kfE9^Q`qM0NNQUT=yu!ABvMjXm8#()`K|4-*_D?1cUT5G%w&m zf5~gKN1Q8fTqW8KrA)$?aM7uAQ1Y$*haP)Iv1UFe*so>BFG-ssXQ7_=oub-X760WL?z~4WH6M!ZkGm@CL*1jY&80ts zMx|;JQ%!=`^0N=~zg!p8p8E?;#+n!|3%!fwdI^2XG#N^M3y)7Q`Sbp6-ckN@ zy>FeA>Kyi8DPs~N9|M68u=i89d$sAUlZj7vcZ_xpQafwG{^~iIzTAXX>gCm!ziD9E z^x$}U6yxu9jaITJA-v3om zjh?iZ_p7&40c(%p6NTB$ZQB8b+tT@a5%*Zr?awUpSaBMjNe)>N&d?Rwa|RG^obp!;j|YiFHR<)+$UUB8=C-_ExAtM>1ve>B6Cq>oKz z%e;~07Hj>bwO#JzKK~2kA%>~FGk>4*{#Qq`w=|S`Nqdeg8qZty;cK?Ra1UnX)9Aoj z<@@=$y3=|#dDi`GN_+ehQYmAr?Q-k8cg@pbN}-tIgbi4nyL?m8WN}FMX3LdsUf1W! z(XB4qM-6&(&!!iYf2vpZyo*tDck>#bmFhm`!;e^tVd%f;__#l+cN-PI^wm9z;?26> z+uK9nas88d{wbEaBYLwZ@EE?Y_@(3f^dk9RC-pjz6*msqqWVuXM9k!U-wtJZ^`!nf z%G}w9PfJ&6y7f@U?R*c%GFKu;C_j=DRNinPl~;a#d;gR_ezd-Bg?5=s@JNA)UzdU| zgzL?^J*twIq^H*9YwtFlzUk-qd+SwlPD@_sR6R*$@QTw4@8-06T~3*y-f?i3>KrfK;MAfPP;0AL8I@{|qf36>x5 z@i8T&B@;cKU_pE@B|gLfq@!hh+df4z?<}Twskq-i{^x&E=ozNvQZS6fWVa&F6A~!i zn6`b2I*fo|9YZz9swPa2Hm^Im`Drko2WTc_c}%>4eeJ40ie~EI*6ic=3++E@=D)eZobFp_d|a&m4Z2+oL4ELw``0DSOksv>$&Sm#Ovtbu_d zv_Qcdkr4!9YAOY6fvf+^@BJv3_Ld?oMR24Xe}}t_cj{PX2WTOxObm%kKU7(=?DLTxqA62?e z=yu*LEp9>T{eJ4{GHvAvJSUhFed!dXYWY$po@Hd-wRu8Wpt~<@WC8$^6ea_IP@+^} zK|hwG5cAnK!dDp^&))ti)13fQaCq~c$I@@1^!I)} zT(d!CrSRa|$uJgq3K%-~4$XNzlPJZ`A#uVw3!5R+UOuS+3IUxY{#V;kAOd_K^sm?` zkOCi}{y(B(Rep`8ly-{ewDdp$ZXQg>;3NcAX(89~wL^`zZV%tJv&c&A6b8jwu=QZh z08nfII323(&p1Z~1DW!6v_K#HBgS(+%f7i`p)HXt%Tzr$Pxmr;`cg%wVH9jp8NYE_#ccAKlP+U|Bb17ofKKb-D?!AjFf ze)bOW8(f{QLu8~p^B+XIn6F}2j;^t+QKXws)`Q03w!jn$;bt;D7ug>_oBHf2p<1M= z_h&7fr|{+jC$tC@}qCkE?r+#ZEC!DqPwoC1;-~k-LGmYXpb&_PG0Z*enk(L z!{Vszcg^d+t73uP^aFj-so(FFUsyIn|fH+Pd-6g5&c zq#jx8jmjB&?wzHbQ#8$9%)BZLqh{M)EPBF!md7CWQ1E*dEGTWC!bT%=xk3-0?kmEz-s_@ElUHQj5B!9$;60;mTm=3e;}o+=Y5&yd^X_wPdvgBqFYT6!#;Ymfd~2(#ISz(n zfBw8X=uy-2y}$Q+nwV#}_4vA`w=TWW={%sf?*qUvV;G&Rn0I_8r=Y zOnW`y=Zg|Iv1tOAP+*4)6xj{gq z5w2ZnN|ymgkf8s>dvWyM;~h4GwM%x^+t}d5o_WQcmbd=7TWKI%vQr6EK3aa&_M0Jz zYS8_8U-5w13#l>OkR`vz>^D`TCJS0l*jzCd)MtC7Zg3{{f%;|u&p(x*ky({|AVqlt zVaQ8f676{AQ}9>aq>Pi`jEszojBE)SLZKt_b7$#DR2`Xq0U0YJJ6ch=bXBg}GuG8>#Ew!Q2p9DT# zldUAFft9;MxIrV5b<(XRIf2AHGJ}>!E9{F>1PtTYeWqcX89Ty+lewy3@-&V(e8#!d zKi}zgVqQ6dt1)k0W#mg?lu`~nf#M-6@GD6ZM%aiXyHij6Kf>SSr1Z!`y|Uc(l5MTX zi21T#(^q&*B`)!u!AM18=sF8;@<2z9yvaBZyo#UfeCJ4V5MU&G3NQqMMc~U=NRZ({%UQGN5EJcp zhe>c$RLq#BIWy^Kfk=lL%>68>$sE_n<1DC2AqQwITiVnlh~2#t2g}o_MZ}BYY#W0I zYsc4u#=%Ckkn+%L42eIWPK%0k^1Fa-fLn(TxsB&nU5>qj8BU>G02CLtF_WN)!If?N z(OdWOz(2hCK)m`I{3olx)92Huy#O#8tu=XAWZggfVmxmeJ{{_)O_|@Lz968sfMPH~ zkNfR;yCswqp0XSI#q-WrgcL%{nB zU>161kQIs36FLQLph+VVAYmF!j4#vA#Nd1FG#G0AbWB7(ZxDlC-?+v57v)x>CXySO zUOm#u#;H_i=^LykFwK~%vuP2u`OQCwl?N`F*5&9Q@;(&hCi1GfMgdIn&ej$urR%Ij zxk6u!Hhw66QZ)6y?uSmnkFBch;cubixmyiA%FOSd2Y>cze8Gj9%xCI$+++UkS6>k6 zTG6xNS}R9en4eO1FZp)*XUMani6q{$@8`5_l_=Qu6bx>k#IpAu^4>zaiig=XTZQ&d zzTZPE=}*4ckZw$wqqMj6-Ua&);8F0?-t7eu+M9X##B(fclRU@d*5|%tPw4h)Z2#6< z{`E1CDUrC{S-Y8dycwC;>VHob?+nMjH
    8o-8(v19kK+K9BxE4if!m&kc5B&x16 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
    {^bU&J!sBW1PcXAK3eT{v571mJtfN43jT+ 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 literal 536636 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 xdOkeh-Q6V@GX|D7StP5! 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�vLT?3x37f5c^~@r?>Ig2h%?u(L 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%0Ato?0*$%9Xu84Ki>?R8{Dwr8i1`l{il z9=DhIoO`Z$likQ>W8-NJi+pYZGhJsGVkDUc=uUsmYM$ZAPfHqhy*7ysmbkR&p*Lim=di5N|L2c-0Wzf2lh|gRtlUE^?0aV zRYmRno?gz?GYvZE>ScgKcBM|xx&GO5$22%{qImM*5*b1wh?0LogZ@CKL@uO>F+@jI z=A8VPR><~rSy#RF{ZmUsn#O>4XNJyHT83$QjPr#@gn{=90^epA-bOX{K<#Z*VCRgz zWF#j$wcsSoObH53hp~GOrnzj+^WI1QmE~n4aKvsx4){dX3Z65S?lCH}Bn*;3DOi|9 zHhVRV-Y~QE2v~4vkAg+RBqH+_VdV$`QZ<{ZWA4p}4ZETfYm57|^Vq!?DR;~PlfKpX zv;Q?{JC3h_i4@Wm>)yC}??|sJsm3f7L~JH3fpx&k)A9)b`Q0pu?0TCef?Ag&5S-7i zz{iG^(nvi0p{2^RMec&rR@Q?_#P#mY6d}*6P}zJ0I7k+M0h-TT`1Za*3EV&#BN{W? znY?4dFf|=bVsv@=&WSbAjK3OHwp3pzJtFL=33Uz}svO zze?pJ9@Q&ODo(P`g+{cG&@N4;U6U7vj~CbmAic*jAkPK961m7r-4H6UZGY`1^#v>m zZ?dlR#9``uQu)o=lvMx{7om>*XY!4yfb-pn_mw0SU-=VKPu0>U1%pcC;@VWE5UE;7 zgTbKZtWgziwGRm@Mk*9)Ib7)4H3m?+PTLVF`M*NED7--Kp(rWsVvLriv5H`Xt^@cTzl6Ts<9XRzCVPaC|3c-eG| z08@(m6ff}eHM<3xT0k!WuIi@El{+slF(VdZtl%vsb!95wKNUfS~I z(8J*km&d9~DIA&!HMmyaE#lTRRDKRgg5i_C=tyjJR%{UtNkV)%g?;N4H_EYMZ5l=F zJpA)F08dQU*-E>SMe6CsIANu8zd>D?7Q#%+%IneqR-=g%D>GB{v>cPej~4qk&IvM^ zAZGUs<>PBu5PfGRBnBS@RN=bva4UcQcm@*s{N3Rc!pJdk>)MD2gz1NZ`Jr_^=q?g! zF-CbAD3&=ExZ+&!%*Ep+a$2ZgKr%uXqyi34YSfggxDsX%s3jHbVtYvvx z#)d2Sc5D1*%uX|a5SFe&KSb?;P z3{7ZEJ#3oRKI=4YGxku~tzT8Pe2_+EhI6^Hh%a(~$;_D!Ea%F-*j9QJof*?y?t3g- z#wZwF8yuqbPaIRpwlXyegqmmssG4q}`4WcO<@j2ZMm`tnv&ZXU|9sXFNM;Iu?X;_4 zXy=Ix|GovBIe{GX%7Q(G`PSrWVapqw!Ntny84>bCp8hbiK??aXL9f=&NR5kC5#9z8 zt=T9W)6Tv_m%ZtvqhIG6{TKHfpg)}&9&n(N;kSZUUXL_RMHtl%H_d|#N_}4fQgfB% zo@YZ>Q`@GGv|#NUOf013SPvc9%q-fLH>CI#BP+)IdAR%KGpLLt@)1So-1PEypfE$< z<`M1A$KUd(uA%_gLcaB7(*A4G3%X;l_{AbAh3hfv_5gXu+_eGCsWK#NM<%#9Qw4p( zPejnN0us|QuV$AY6dkRaGQo$m=^+a$b!m~=j`0;~)hy0kE~MU#zqtiCVX`6S_e3Q~ zyB9^FfKy0J!dzdln9rF}SW|pBfO8V`KvI5667bAggg#OjzCI?tcR|2+2U;v<6>3cg zi&^<$T3b73lcI&A2!l9$1wbSHu`~qu~a6b=DU* zvnVE-3`TrXdhNoFMrw>nErl;@m`}u4GYJyDjK!P4u~(exJobFUxEci#wzi}vWwHZN z;>Dh;7*7B;cGmy%FsHwU)44YIF0F;`sgy_eK*)@)Kg0o%4+pq^A38QlimoCN6eq!E zonQ{l^+u|?(M@!}LEOAql`N-KPo<5G6k61stI z@7vxuQ7n3|XYz4oYe#D z1$>;9%SzvDxVMJ0haa%GvdFAgnIHeGENdQB&US8gCE~noB-7@pgVNUBd3oq$6cggT4(nvyh(Xk2Y-eo2 zP@!V>Ddz<}CdWrR3x|_x?d~U4gyL#eaC$wNVejV-X?;SD=g2ORV@=zjb3IQ z`qKt%=Upb&8OcozT>28NDO38yp*@1(7=EaEfV#OgUZl3C)0ENfjMfi(TnRk8H{zfJ z%zGiaQXa+kx;U1qo}dZ-ke%wZ$U=AQ{c#nLEYtp>UOI+0`d5&SWRh|Hr3}TMO}*XK z6#OsrNXvMFCw)>Gf^dzy?83@O=nM~ zx=$Sc@fi3yo~5`(-M2Eu)dVmp~)48Y9AWr!#@JaZknguZBO1h8v zJFUO#j+v`%xZmfO?`xx2E^!Jb8v`#108SZ^wfn6d-a57uvUe-wy5)^zHpG#Z25a_` zq&0i3lOd*J%UnPOFnUC@+vk2S5;m=jjFfq0Kas1|!`t;G<;-{^#=SuCiO$`Xfy2Ta zb88|5B4#1uM?^sHamDqECDHKkC}FUFf07 zllJz)SBp*@fKe}j4L~?&BwU+ibrS@i0{8r=$7Hq29_x(k^E@m_nL{9eWX*e`f0LON zBEM#{jg!e4Iedm95df*tSTLj~*UnvMMDP=JJbkNb90}<0Tv;5+kW1fMx_y5eJYA6rK`@@RU1OI)@cx0l`RCTtIx{%4-K)>^ER=y{OFB$5yJA3QB~j zc9Vf?)t}VDINZ+2vyxVx;{TXj+J@K!5swNlVc%4tVn^|+2U_ULG{B?ha_f@P0aM>U zt|J`BCj-8q5EY|Zl7qk3L&#BEj3R-X4DiqYzc%FgjP|zG@;GQ+{G_O`vD_A53xBTb z*~LCZHR-!z`0G^JP+S}dR9S@*W3%!1_UQ!$PwzGiR=&PRn-x}>%Lp(9JSFFqtFvym zGgkYV@~rf+Bo9FHZhY`_B+uHH*K4c#1OiUSqHknY^YP=lq;28sbb66Ku0}yjE#{QI z9zGNDYG)l~@m@?JFcyPeFUFqy!kiif4itlyIis-cwilaKjdv+f0RI|MfeUd*{aB>o zf^I?m-FUS^AHmA(C2;tCWR^@x96ZpzR#Rp;r_Zq4F|cSWrM%~9s$22aN;9l41qOeC zB&tv&fq}?*RWt+f&=u$1E=f^I*UXV!t~-sH}19}y7yXP>SwM1J{ zPzmbnL?4l+A4eajqG2%NJ$@E&x6_MCLgrC4tKybhgq9j0<|#nQr;-MV6-l+iiiHac z(2Bty*B0($iP{+5;~oV`$s?I~;;6Bvy=Hqd{rGHLmp%3w72^)O*rR2BU7O~jQg;YA zRd|hm+Gh@jheCGcEUakohOc`3jd%U#=!A6GUaL2(jjmxc!Uz@w2qQ{ZiG$7+D=Ozp zGiBiz?w;hRY$mSYV2f(k`Jmxx(c0FgIdx0~WW08ZxoZ7U;s#TLMzOcO{G596_Br9v zUkaK(U1vrm>a;(w7E!9{@uu_wb$jbC-xkj1YCBR50qK{ofk5+fU(E-mKbL>WaK@UP z@$JOr*6N>I)@`od?90RUw9(G}trB-eff$2QI zY?*aQEjI0&oIOU*Y3w)K3TO;oJHCb!n7Hyu3GW9i5Ra)<=4g%IWTOeJ`Coy|#W=dO?+5@LX9L0n>PG z?(ABX^~^UI{5fcs2C?yOWqJ2(OdG=>t1_l`sGD9eOVs;H-PG-^M4)OI4m1*JT1Nu< zvPr_5E>-yrbO8sWLvjc4MlxKGC_h<&W9GVW6=Y8MJ#Q?jTJ7!}?V@5VKKF4}gflSM zj)67ZWxy!gf&>@pVp$`%zs8)yc@ysmbiG^Z!QM&V7ZFrcUW;u_qIwN#hiquf41F`l zz%|AWyh}i+PMSe@HVG{U!j;shjvdJI`E211FdvV`sA08a$($}ts?|`etcOKJz98=Z zG=ALjt}4W!qP*ykU_WPf|# zX#Y)fJfy6HE;EiB{g+fo-nG@S4}~*@33Sx1&k1`OF#4~t%_uW;u;+ zfv(zK1|dn+nTHI8&@e6w6Xz)Pj}|q&>PXq*-U?X3=|aHwYUW^)JVH9LoQct{ z`O2kLtVs3Cs*eNYy#|BIQFt*e^l+>iTnXL(XrYa??*~%6N?}W=N;bN$OTvr8hx#Ng zV#RfXuh7CSS;<=(B{W1Bo3`M_wpN1W-504R&YDEm@Y)epd$RSH2)%`rFxk)+p6Pf- zc}$@;7!HwOG*ahA zjX&M4M2sf~57L`ItM~9)-r0*X@z)SiB^r!V&VE2HHiHde>Iza%*NmSHMa;ZWujCux zrh(0P3lBfFZ*2EqXxn4hGdi&|&@bVAgiNRTjdvQ~s4I+~DqBaSld^1q) z$}zF9yArz{v%162#2JPLUFp@znXiuaq@y`YZ? z>sWunpIaH0=n!N|mD+l9S-!!xZ%_zp+SLP6$i`$fSMyt-Dl6@D4yn)ik{`I8ay%&} zc`BTwfFiP$!d}oeALT~e@{xT`v(JU`Vjk1X)E)PsWlG@h{@a`d6n7h6mwua~tIhY= z59qMpdt(*So;^?;cbJSv2QTDy;#R9#k5yD@4;b*cN`rW;oj=8>)xXM6e_Za>N)Fri7cBY#_L1uZ*H3RGD|R38)j&dS%{)l+Y5CTUFvXm4c} zMeKRP^l&fSR+Gz>l7OzhOjh)GG_12p3>5=cvf>gFb2G@&Qrd!V1^##-7$_$BSY5SA z$7ZMLj#65|sl?~!$ky4Ru)WLq%zd7cWb?r3XTgsBL^llGj*_U^7-VTWu%k{OZP2GV zF0B^|NZc8LUllFp`W+U?75fm{)23v!`64{?(fhO(XfvhH=r}7R9|DkzZB74nWin6i z7}9Qf(@UL-mSulFtV}PF(N;WQ>!xh3AzA*Uia`?Oh5OZp742cnf?2=y9>FpO?%M4y zoD)~Al})_HqoC(EbY3yHN&5gt--%*8M-p5ABpTNY&nEPFF@d0&GfKX8t!_x|){So& zI&(a9nW4*|3c4ExQ=PMsaL^v}FnO_IrcdUlP5-979WKK5vxOwI^cMH4UwX&HMF&C9 z@JaLBV7GyD79|%DC*}W4Il=FBX!KJ+I4I@oLX2WdHu-Y*PuS$zIKA-sz3P$gOic&= zBQ*sv!yCR(L$)fPuMr024#@ozU@Iimz_oIh3(+;x*5#?Svhx}v|KpqXnLCr4nnTy~Rj!|V# zfLvc=aS^DeYW#W@Xc(FK@KYCWT>5t`XXr>#hWLF6d4~uHm;#R(>NP9_*9!!X(r?RxO#W z5iQ(xA3_>t>=1RgR1hVS5a*4-5TT&a(DTYJ$XKY~jO`m;3i{KL5=n744avv+FckiB zZ$-eSXeja^(4P)apYRoXu!4IT)Yc9*&Y#b|HLy-(Ljgs^4~r0?yO13t`3BA3f=`uq z(3}($3P8E%2foh3&aUwPz(oRb=sNWWvz$?0j|)o~JH3D=TbY(C@?&ajUO{0M1TQJ; zi)U#B$Z!-TiDj_pG$}0_mnKnLua2r5c>)@TXPrY~LoN7k*Rm`iF zS^6w*)E1rDAyuamS@btEg;+3ReI#dT+~7`y=4@3~t#s>KV=(mDn>W?r-ZC*)>y}<2 zynOVb*Lw8R*@>FidD-JwG@+Ta$FyDj*pUm^E%4^gXx0hby*yyA9kVb1-sz5^I!CX~for zOSK?+<2vlj>G>(vq@PPT>TTz`)oYxGtOqZ}D$Z|L)8t}E z=msHkd>*m7W;Z!4VWf`t>}Lr2!d|*w%lL-maj?h{~KWMf4KiU9t8G=OHKclKZ|Al9yTYG z`T6--rav$Lo8f6ZZnvAg;n@E_8Bc`_>)!`Ibo{l+e-E=I3dM)N17AQI+~{OQ(hIo~ zS=9ikW!SGNix_sopC3!db0C=AC;9@xp8!~J&LOxRwwOyCP_NrEI{0FBSpu~?O>{Mc z60gn;8C8sw6Z?Wk`~~OFWb%PfD^Z&0(qI(F zk5UrR4xU^k%HvYf>vE7BO`@I)>jfXA&k~@P3Ka@?zo$I)u=F?} zZ4TOC@ zObNg;utss*hoWYXu}ip*qh!2tAbcN*ghK@U&PU5#fJ?YMSX^L&162_R{N*oc%4X#_ zBw)~IF<3*;yw7W)B47djV;*qV7>6W3ypnwYNdRsyL3axY9JeU*XrbpU1T+}rFwqWW zcN2ydnR(#tIxM|8nEB@957@$GG+hb44R2dEW9gSEz8_RFR+1!vzoIzsWm&HRmSFu) zswkng2`>`#!d)URdo`eJD8QRPmipaZh6GZ4nI239fZKLCZ=m=B?zy+LOPD|vZhgC( z>e#M3D!!$K*&=;c9Qbs{(3U>(MYBEH?7y7;&Vt|xX?wwhGdgnY zWciSIXo}n7_)IUA3AKZ?b9TK}eV|4jMDDtkp-Oqg?WnU?u@!EF$+2js_blDaTLybE5DI>f-UNMvF**B9w|COS#E8fge#yr)5g-hRFj0%XVL*Au;;w zMLl6!ei(o1K4TAYJsG`$59$8iTx)J-ya|F^52bm~Ei6PMs;(gzPBqYj?;lVZct44N zt*pEv^?Am)p^r~tulx>;+qb?)HUZy_DkkD_T|m$|;Aiu@GGZ^YV}#)n-`8xf#jI5| zk;gZ7w)gcBUm*F;l*&Y2ovYc!Qu;up`i;5@Gx^Zhlc(i%a4Osp7wabwSV@c)QI?dq742yNB(+Od*e7dXb;33$sXir*wc1I@*av(1gydI%-`hZSrm zZ$tmlU4gAgP~X9w{;H{wndQ6&GHR9mQ2(8{=bO&!b^eH2S^}~{5zW@&R4GX_4Bn+vHwg*(Sr*ko<$J50sbSxvn0zV|c zoVQZ{yL{R_wX0+RBlrq`3aVYigQ*g@qN%S-)0)$xx>~7+$u5vUsNE~SIRujEuVi>k zf@#U3W1x%gs4z{vBcVPD?1i+A;al!PZQnczlx@d=-OwC*E7GdWKX{};@)R?~wwMcn zL`!J|OnQn5!ep=u5EW?m^}S9X1y!M~N5c5EksLy+ zbt0*4qQBtWvpn#^r1_jb(H?y@d4epubPk@sLC(rUi}OR*eOz{Wt=ulr{i2S7RoDxX-nQF%_YWY>17m>*>BO4e*?rIHk>E@pb$-(6DOZk3$1f|8#~OvD46X-&~8W zJ=F}3?RjCkgSBg7)W>N|q9|A9EOvwLv_X(rA5YkJ);8-JS&ax^3YM<|spk44Ky;_I z*w5&}*I>RyTy^4h-%LpdwQcA)Fu~`x8~?NuUkN{HM`E|3t;xEsVIi~2QH+m^BDc89 z+;xF57ZyJM$%D_4U1dOv;MyfU9)cuxQ{0|Sfu{n%|E`1FHFh|w2qZFB#7OgNl~@@V zXI;VlJ0AwO2DbOwu^J}Y`55FB1EI!84M+p8RgFNtHB?tcSN`d`$XU5WTNOE#CE7)|uvm(EArdwy{ zs=g%fKKE`;JtcpF2^bhdK^&IhW-rUhU3?t!=9=2(&lVY<&uZhS9Wr5t8gno=yx9Bc z?C?&#GyJ0Wy@)@9bK&(!F6G_i{O|qFmNc9A7Rr-eykB7768l6s$|8X(+-$LLF#iI{@n+$q#iTry!(NsYFl8 zbMC|^UroH@`tjF}hUxZcgF)`-{H?)v{^(`a2oaXwO7oidRH;NMJVC%sFXoH0v$@GJ zvJl*{$38YF1J5Z_mgwWSL}X3o`ZI^D zI+CzsyBl28E>fSs_fj36fLfYVHEQs6&GCfQPQ`LmY*MVAjKwA0#z_VT>c1Vv#|airddr_12%?gHW24*fZ{iG-(;Zo|H;-Pz2c^ee307 zrN4;vr2lXhP^_Wpn+ZR?W{!JP|B^%CS#@aQ#c;E6=#0C)#{dVZKksWcRg}*?N%vZZ zGanZG*+5bbX4?F)_jMp;F8v%j^mKqD(RP`!oMM=0Q5w-$D}oJaY}q`_hn_?Fdm~bC zPV^SelwY20ktz*t7*F5=_fXsBg+;Cnql#@b$NV7L|I7cWy)pM8-YRVpg}la$_7oL# zQq3R=eOukm@n$^gPE69wgI1{zSbVJy%-P^0V0W zc>dNsV9*i@f(R#g-WRKCNoEfPZ8K*0AwG9~(qxw5`CzRHM+&T-CZtP0?ut(cn{wdhYj<8L63Zt(c5ktR_3&f`sy%`<5wZn^JfElH zH%nHWmlN#twwZyV6L)XLSrnwzN`!yf5eAUacr;edR`mHn@zyaA6uJgvunLf^1)`qY{ZN=J4?ipqf1e$qIl-mbtHcoIq z=?)GSU0cF^DmZQ}XzJ```5~mRbZZU~$iT9iI|kEcflDkumSq_$gA9n|1q~xoaeKiadcx@y!`>iV zA?C?J;R=~g@10xQJkB;?X>IUvFzgFS|73nMWutv3Ph4!t`7LpCku;tmfti>`n0=kc zi-&~}WEr6uc7&s&8{2G-^zKhb5knw^BurPJS1(FZQfO6REL~aaa>JRn>CDlmKm9D| zGx;;G4WloCC9VL%l_PV~==f6J>P7A9%$3|;`K$Wwm6Cr31KWg0Vf|0aL=<%xRHmV9N-0T5XAz^Ru1~2rFRpcMG8BXV* zu=w2E{)=W#{^QsZAdX|2>|QVlMDrFUb&$hxWh`X0mKDZDeymH*MTo@Emf{kLH}c@` zdHw`o8@SqC!~UI)~r~hWqsVv>yK#ZK;XSxAM2B;ymUc7I4r82x3&zp zFHB;Eca@do z%78!k!VFVlx5Bzk8UTaY;y~g!APBWiK6Lq2+kx3MBP>bw48s?e2uE{ien<#{3KyzM zQ=3&j%=kFCs0So$1>7^nPA>s=j+)$CKWF%u#ivFwh{wy5`q?bk>f?vbyG573SgNuX zM0@%nJjM7wPDjNvza6_2UlL=`{;^cVdqG7=W|@d|&>)TY;M?pTPle{x<_8k0j!25u z0m^3>vO`j$0k6=V&M6W9_ED@?$#|WIj_PI(p;nD?aO-9je|1KYF%Z3f;dOv{^9N1o;=Qc}-TX7g-HWYn!`!N_f%GLRC{$Xl z4k^4YIA{xS&t6^Y!iPx>&Qteo>Bdk4gZMXr0zgV;;~tY@nB9?;?lxm_q3{HFCARqu zdJ;{04di(x6Wx)RdG-@K*)yE)_?%wV(#QWP&*J&vk2{|*JVB_oT(eC6MtlaNcJ#ft z+TOzwhTwAUqJs_gDO^d|c})N9uYjz8KxAX61i;sMmS7|EkWigb#q}DhOhUxyw?EG} zxQrSZYJwh^DKO>Bli!sb4=)}y*;sxjkJ~3-kFfG}kIIZ=pvEDVIpuK2qx&X)#H`U> z$R@SZ=`WK95tbsaLmFRKSJ#k6p2vPAu(?=Zw<0$xFk~0A#J2-QCk&y1({H_V-0-%q z&G76>v#}6?-f{#9EhyMa0DG9Xj;ZPZWpav?1PsFin@%zXLpKuPND2~#{4}d+IDuJG zE7%#Q5A0(n4oDfs-@?u9#dS$)pJw(I|6>K&sa>$k4&m>siY+eXJ`$E?`4ZQHhOn;qMB(y^U4 z*LBXh-{&`K>>7L3{#YOOTK~D`B7Y$w<)N z8hvG0Yo`*j;Z55zqMZ)-u(aetz_)o=nUH#Si7~1=SKre)qMz9*ke84CdA9Q_lvwkE=ZNPD%^*-rA-r!^yL{wPd zsPL$;xT@n5WjW|lFGU0XfvnRCgO>ca-n`j)uNV??NXy_IInSgAgg#w_5q#b9BFLkA9b)4ON(%)!m< zA+$_L)AZS=TQ~oeFzj#oZXvTqM=S_=60N0xQEwGcWM*Y9){}U6OdL!sJiy z#Zska0uMTd402|r*sQf=`3Ij76a$lDQ(26`F-j%lv4R~-qA|le_)g?d|6{6R7 zJyC5P`gbiJMWgL7yxn2=$1sS0w#cC;2?7lHI1Pe~sM)j9s_iOW8e^gu@%?q%SR8nd zs=vRBhwb2=MWVP~H>l2mjj!-LUcud6eXZq~n8(t!AxGK~G**0xXN)0Lk!MsgxdZ6i zk{kVHZY1PmuI}gq5{jeJ!}Kz<(q;X-=P{m1faZ&C_ohk5a1{bt`F+>Z#v#azx%y1v zy`=)nCd`IbiRFPgREKEuqEkBKGP-l>%Br*$z^KIFrP z-Bo~-KWHLyy==-B)T`c#`GrL`JBOw z0H9r7WubPWoVM=mmL)L@!GE_pJ}TjHU8Pb|Ldz(AI==x96XZi%(9T5O`NCNj)NiB` z&ULLmqQJt;!dt7kW$bF;vCNbYtviKX9^443a&%IbzH3}%lyYdv}F zTa4qS^_TM%UT9~7FQ2(YBwNoZ3XS1eIyIlu&Mz_Q29sy#wD*Z_)9=qQ^oCsPA!hpFAZE1CSqqNQs5TfK;glr#A_= zc4^|tnGs$5A$r+|+>TK-WuR5XS%%dgVDLiiHh)}2 zO+ZI}{;Dw&UE0gL>(tW5+PV{b4!FVm2rb~f0n#jU>RTl)y>0xy1=>Afp9pO{mUNlv zk>?tnA9{hhbNg18Jc@ykXi8N>|I^YwQQJTI90mr`Tc+EiDECb@(e6%J8c*z1H!hW8 z3yUt|Tj&4R-En-N39)3tPY3+)wvK3iB7DJ0%6GRMo{g6O#^mN)EI}n1|C({lieMUD z#QZV0z?6S;25n)!hbDE`%;&t>_bNa5uU2ixq+<5Rlb%LubHW_yVMjG~;Y{VXFK{1F zAvaI-Nb1$ZD8Z_iq)}_;SaR%)Oz`7OEA4Qq6Ya!YLTvj0Ry3VRR{AD_3uRo_hFM*_ zNi?DlWyD;LVp5K4hh~Dsb4{SRPD2;8Z5xIdzN#R35~8R=gjLbu^cF@DG(Mx~p z`Km&K9IUw2hAm0ya6$(W)`Btn+0bt5ik3U#P&;9`=^Rt3^E{e`Pca|G+2!Wg*qIAldupyB0WKH*9S@IFC+E-lvP$`|hHkU&c$K+CK2Q&5 zd`DcithbBCTQ;w6TND7#SSOIH9+#%68Xp%0e+@n=4sI)dZ|OStpo+m-4jQepU?TPC z2d(VN&u{0NpGo6i7A3+{>&`bckUkD@2hDVsfVQN=&uUPD8(Ykgr*yh%2nYyX6fp!a zg4{x;tC*OyX!vE3oWSoYMB@9$h{3Zl=E}}I!O%MlmfCOpZqC2)(OaRka^@SR+RwUIto zZPlYK`b4rzxn#IdSn;XLNuwY@-e5NOQl}Im+PCPFsz&RIMDe%Bz~ z<-UoCWqW`vglBc8oe~8nBfE$=)n%#^&Ah$CjSm9 zXn~U)1sFn{Fd7J0uU{E2r+sb<>RVh*x;G|?q^egf_-td``-(Pd!Q8KI^EmBl?_`%${|H{;!u88&n{0%=u3oqD1sZ7SRM8oK(+4jl zh@<<98f{sZ$Tu593+sYC;QXOfvJ<%8=W$|$eMdqsDlSxBx-I`r|yq$qfKEWYSFii7Cr|_zqkA;XkHi-lhkHc-1tWiN@B8y#cK^o}(RU^PKj`c0{inRO zd;j}JUX!ErKNPK$n*NW}|DQBznqU8em*U>g@Y9DcXZrZ=<^9t~eQ)TRuj2ur#W9{m zoNQxYBc(6?&h9Oml~aE%aL7^r?yPeRiaaBEb)Ty*E~fUKr(WaU(eAd$#J4yRbvb`b z0GU_cQA2X$Y(eF=P(eqvU4-hwTIC*%!?SFH;u>YoNlG~%{TK50(2%|b4D3UG`Os+T z!g*#L1vq)yc!d_*5a>c@X2{b@n)o=fhylRGdj7pOoEu1=?6d_C8MUpH? zu}V07yJxCFmP?JbGSi3bkmG}m<|FJ5k>7|k@@GEFz-xzdx^o=n5g81Y5E@%G*f5`8 zlrtPX{I})y=TPlIH9QrMdEACzi(j{4Q}(<#2jYA zvK*n5uEHGQPld*dKZYC)5ZwGg(z}DvA7wvNFxqxrY@B4gfsMd~A(2mV>A6+H;iHI1 zCWS@F2~t!N{*H1P%CeKM*?h_?G}ayKQ&I}Jac<9QyK^h;Wb+x}5A3^s8+M|`!^$>M z-KSp-)eUt6!T>e&83~~9Onjm1Wc`IxaSo&@OZc6ptq?TF6M=cie^sw zc>itE8)R%?5=8n4nLklej+ShZR?x1?k)6*Gj|9fOzYxVXDf9R-V)zyPzH-i6xGat>dAN}igoTh%9raLHE-6&b@?4Opr zaAE$RX8bvAVdh`9j48|hp{Q^vUoh=^TJ#TxyVFU3C3op$pI=Ozbpab5Fonf8Zq$>0 z#vDkSjdeUCKrPOU*Q;#3w*0eVD;QwW_MsT9BgDDZe0vX?Y7|cG_nN=~j$|7HB|$3@ zJU264>Z-O^TFFF$uPv8yPNTjAa+A3qkAHcIk6}tVRgCU&B}Tb+gyfi)o){m8Q1&=p zG+=2B* zE{BftFXVzZTn+?)exy=j1wc0S$0()0FQX5NC-5q_BGRIv*$#zJHk@DTol*kqgPdNidC&O9u!*@*r~MN z0Zcm_viX0m6^n-~lI5mL&s7+J*DpB3=kDK7swP)gEh^%GX)w8+N>6roWyGtwMB8^m z1H|lo*fFfTi^Jp1oq^Y@kz}gNBcQ=WkkPP>_o~ zA!IxzAWz_JlqeMlvJs)d62sq4IK#@knsGid6ohKKq`YvgXRjxlVEnQXb`}4ugb9e2 z^9*++RfaIlElC-ub@=ibKNrlLK|YqsuN~dZ@5d4=PZA84#7k|vccy0!KRSl_R~P}8 zib|rNMRP==BmEND6X_zpBdt@6jB02c-S09(UDt~czg}PA2dur2Uanz^HG5@3sVmo z3&FjoGiZ<>)LN+FcTVv!okN9jZ0v4po9-^fF!o569aFSy=tG%p-Wsf?)*n&o(a|?L zMh!n&%c6217F~XXF zbo{JM^uOwYn9hW-m0JO{%RbJS36<^}(49WmJ|M8!@Urnj>hi;}WA<% zm`_!TP3CJZ6=K7Oix~K_e!55(Q>yN5-$%y@>D$LmXi<0Sl(Zun3*4ibI^K{3Vr7b) zPr^O3iV097yUpU02TIrb)VAtTVr_vjhHncEdY+;NymAmCtIGuiR*^%(m-26F5Iy9J zzKgmMJ0--k4BfyZ#hVyr<;>8$LBZoLwG@#dVs4ML56zS3u)mM&E|S@Gp(2+u74FXg z5oCv|uOphmZ!3J6(&ZD1puat83M=mJdYHx^o)$v*^w`9uh{Y&3n8S-_&)DzXvAYr= zNu;x&Ot-fZUlRVDU6tIyrI#vy`&l2wPEFHkybI)_l@-|?R(R(%@-c_qrr0}WzO4r# zqL8A>V>c1-yw3wt?EBh=I?Wfd7TV2IL@h^fUz_vk8-+n+UI@wPm&4Tp}D zGdlG5E)?$QG2$(ZHVj#zUjgN5Z`J3}h%VOe2cg|RwlGEHB>C9}<85`A1t4>QDX4cy zSilo;4IDfCiM(?x!eq7~b0|*T@?gP1wSVd$P6AUCfpjLI*jghAjC!NBSru!AJwdv! zRs1s6?&&9U8xv(Ym#V#em-f1LR#^NobDv|$ep@Ke`e(Q*zv8lVd*swl<3LB;hUO!B zmT*Hyg)i)Yg;0>QJ)kN0lSG)|^YiylCmT$iFw$$H2_%DYQVYrbI+r67$3$jSMoS_b z82U`T1^RT$^dluDc_D@0CGFXZ{1G2fCN>A!L&Hgo(M+n z?^GIXAr%QUa-)&7d?B7y=J@jk!F-~mokW}f9{-+(GEgbv$F^3Vu)rV%q-X93PxZk( z)DIpMrt&?p%aK9b6FeplfGk+lGyZay0F%YVKV+usjglVlEafhb?tVc4vz$7Q~N6)hj-2i zFZI23IW4<-bcl^s+|rkCSEKX#dY8biSc3vTK>qx_+Q9yHqC3B5s_)8n$vlr1ObPG_ zo#~s}R)RF{m6s*(o`n%Y?az4*{z<5?$6RjJe%3*At5k}Yz0ZBwv{GO@`~%vR3sFU-FN987YIL6S@mmQ=%TP^jra{T5nU3u3{#XFc*8L95q_ zj&VN6WUGw0)P9)6XkCxJ72;l+GB>asH^F8M8onjTaL2|Wph}Z)@B2l)Pj@DB9bl)k z{Biy4@NwQ2i~X10qTLG;gw&%D^sazFDJW3B-v0p{OV^qR_%iOUS|2_M(weTB1B~PM23Tsq& zpuyV~S87o`F6x%-(OhQghKXNeaO4Qfwl|H1Au&we5+wttDyfX9Wp)N$dX{zHG1Kzt z-28L^@43K>k6unUkME+Hw0=4Xq-yo!s&N*ZnK0HR$ali<+L|oyVB0Fkk}xu`u|~M> zZROLg9Nvn~Z5ezruwIiltz&p|;M&`pZUqn@UAer?@i();2I^--_lif4yAWYGI7p|*ipZ@=UW1Mwlhz^q*RN4Gu+iM4mv`Fq)Idgz?trPZ@-OIVvbOo zlG5vI_LqMv9Mi&PGNA1(W)#&)@Po?{UCDG83$Q1oqSRWrFE#YO%`1`n3PhV)fs)Cd z!no>~LU73$ITU(WaA9Q$`okJ^+{Z}7q$Ny;J1>ydnx(P@X^QPlc*A{d4To)3cwn&f z7yb_qq=WEsB(FGiJ&^d={OlnlOCS1eXx z#B8bMR%7H*PeaC|&EE-~E)fT<^46S3SF>McJQ=#YsKL?cj3nadsjI;{xPCtz4>_kL zY2U7jV86^az%J8l;=JED7&se9T!Btho9;~gpQ#XlN!ADuQLf18O_PsBnV86!7}q#Q zm55IGO5t$T2;hn)zY05#x;wGoTAlls4KTT@7jrp<623zj5DvIv!bLZTAi;# zdJimcrSDz5D#EK76Xes{APWN*eISo4CssSnj{(QR#m+8DISYsUHk6vp(s*+ z>hHj49EDp}FB2wNQTY~3CpbQoB1D+U8r!e>yW$Sc87C6bNWSz6_UNQz=%-!_Y*YWi zRbT>x&+;dGskh!{`5u$g9!+m2l>+deMmDUgc{!7Hbm$YxhK%)9u`Zgy;J*cr5T&f- z=Jq8Y%~SBns066~fhH}2v?X~D_}hV2?xSq!KxS{VAWSFLi#t!`9oq9?P;3~9YNN*u z9W}8&b>(MQ0}NOyKynQ-pC}Tcr%pxW(Jc%ipuhA~ysZ;eX$Xh{LBJ@%)9_VaBgO2H z_-3Ar$32}~D@8y@1mv)Fwy7%~T~Fb;$h6{yv{*nGX+Kgin)W%U0VLIZQ&8E|Q@yt8 zeYTy<6H|(2dpNf*TE-=OLK|Z(N6VT%a8jVHT9-E{mt5pp;5V6{*{$&pu8=vc0K;H3 zkushM=oR}#JZHho)>%p0jCn8@dYpZFZ0HgvCG17N^^tui~v{&iV{|k4bhO<_ zsbieHSgsiBR;;!~2bl8Ry0zKS5xLBOw#FzLGc*4Z*mN4f*gtiEzOQv&Lxem)512PF zz)*l`G)eNVB(KD{P$Im}toiVD(YY0lV%|;i1*1*jBjlM3+Jwe8xYTzM3jTCc0=gl) z{D|a%6)=>Q=FS;56Glo#%5fObH+)b@2G-?>Zes}NznV!7+!;VjD>OJTAlW?yjMTie^&+r?eX5mu~WLU9jk zCbY!%I3M`;P&;-1k8OZwFNl0(+AngjaR~?)!JNDFFNZPq%a2l6VKDul26D01XB%p^ z7S-vdji`Q@hmAkL*ec)%fK=fHLzrp3SW^uS{rXiN;Ikyvv^r^B%e2_u739w)oS40p zk;LGPaHv+JfF}rfX#~=E1!$;)g&=k-skQxizyX9212>$bOgs44{^IknLKT^hRu^}k z^yfo(#S~QfUa%f*hV@mjby!mQyG520Ic+fbxxyz9aCZ6BCx!t*k6op>{$xqgjRn6) z$d)z6wQg(xVIjz?0F|r{ly6aqu5AXl>>?^oi=ptA>Vk>Jv^{P_Yv{?@#xf5A&!gHmBqfEZ;lZrmbuNC>1r@a`aPM?u7JXI*`IdggkN)Y`0OuFa&jk1<%2(ZI9WO^K2uD>FXub7)dEB7*Lv22fhQ2jjq zXR;G~#2i6YsmK`o>nYye!a7e?W?O9;3}v z;_sf!pWwezm8JGOnfDT)h5su2*;u~2aQmF-T;9wm(FX=HVii;A;4ve91Rp>uHuo2$ z&&60J0~#-o4xI z)ntUO08!LWLun0^34Hl6_0ek(^a6!AXS)rTk;AdKsCvnCYe2AXBku3xYYKfy%xWIw zd$ZWqtA@t#V9mqtBjL8~?ddNEU5BMZnZF?qrQ=+bK6Ftfe>k?0aLy#Znx`LHliJ3ranx%J;hH-`QB>>zcet$Q5ksyzdn_y1UkmcpMkXd z_}K%Y0SbNu{F_Mi6p{Pe>egWL)8a3WN8A3{G=j+T?ijostKVM@NwvpVhD;j zkm`oZjmJnF_6nhh3lGIQ51z^sUBfm;SQ%~o4RAO7^P98gsC@lSDbks-q4MSONt4U$ zbX%WZ7GC>w94a&to{zoJs=dX9FnF^SnJ$WtFyR^64B`~}kq(qbN2LxquZRR-RN zZMm%`B7Ei8N+snAVe8{VQK4CpU-vpX+3u@_bP4~Bd2(#KS$z?q{D|B!OQM<*g?1G` zd}-#u*?WwEqmF9>Z(K{X4O-zxA5RYYGsxYcNN{Sxg{8p)i zs=Yj7V5`o>QwLcwD1;3o7FX;7>txq&JmP~Of?`G|^Se4AER9B#85O=nm@5+oxt1fi zi%S7h78e-m*!`<$JddmxDAX)~g!kJ>?s}QzZ34+_vvE{A8K+h3Mp``jfz&4RBp@G5N0k`enPR2c|Ra~0J0;K z3Ca>rwE=fnM0H%hUYd=<(ZFUFoiDz5CU6&ZM|>wU6w(rri$y!Bv&IohOv+5)k*J=Y~JSOMNS z+RN1Ayiw)SfUWa=__+7e!RfYj=Vg~qFHMgqRYx`-CWuHdUg(T!A10;(^H&SE4Xd>@ z0un9sn2tVZ-Xk+xw&PFVM~L8VQFEsdndAASqu4cB`rYZKKIr@dS5-QD&|8xcMx06q z9MOQp)5cCS+H%ii)V22Md;Q8UY#(fz%;vMaykh`^4glqkt^%>}x7KB$%l)-mwHiwa zFK|1o?7vLpWI9s}yD!vu?kqcP;ox{StVJWXJ?j=dCYr^*rUHoMKAIKwe=uDLWMc-y zL-K?h5Y&qfGeGAD4s4GT*|j$nn=qq+p;wlzdy1=t?6O~2@VqKOyWBLiH|v)7MFLNo zp1xzVgCjFJ;XkUUGkNsEqb7!zk5@G^GNP9Jy!A@#o4cSQd)Q8wUn(tLi2!fYU!v(q zI6lYWboCw?vWTFLIAF55+kT3!7*sZ}0WOw5pplH&7bM5ruU+b){On39Y|$VL+14N3t~`q2;?Uuxl7T;v zY+#!%Ba&c7KPA_Wni2 zEl9h7c??f#K*Z*O6Sm5t9(upL>|!Lp=H?iBH9L(e0dz)XLs#07jKR#)KS%p z%WRv7e)^Y{SgCljWs;HIn+rj$(mA8&czSjgz=w%`jfcIUYqP1jWQugrM6b?u+p&qx zuG`B7^hUL?%PzFm$|+Ldk#dn+|JMg~vw?4cn6}PPQyYDjo)>(<*F=V#wc5NKMwLMb zLN)iK;m+VtD+PI*VL?`6lfXnx>EF-rK391a_J~QdR60w3Bg7J#m{*H4W2o>aGIi_r zUxBue1lxF}=P?AZoyt9lGpYQ}tx;y3J;UPY%-e2lgU)H&L@R`FH zTxcLJZo#!}y&PV}zmM#ridjl9m!`-KkTKKYNGd=pikPjzmkXvNvN{Y7UyD_Ne@@hY z8%mM4W*xU(JDEi8ck2YA;e{)on8(6ZqnkE_Dc>7lT~^|v24lFzi_2fcSLC|HGk(G+NzuF4yK#jx?!8I>vm8cn)Re{p-rOnoka1R zcB1eFZ(!EOf$S)yBge<3KaB#On;TjZ={vJE(mQBAEkz_qs!Rf;DmOOh(H4eM1~EBM z)O~e>o7gAAdHa?DnPI>E9WU3i&Jz8L`~KDE>X3XYzRmTu>-X@ulp$s^(n!vl^geOm zHhMx#YiFXR1R{KJwFpxL)^D=D7h{nFz1V(WBqp-j!`V`ixz@`PCsdH)QpAKK; zJ}+?&pI+J%yaQMa<7>JsYKMEb_v4nkolmaF_Wgnc~?qW5r&0E=fF+kY*z+pmA*i)3~FK2|?wsq%x*Hy;1mM&vThArR;I9 z_|aN|9Gefeh|!d}6Fy_r)hAKDZab(aOxvnG9Kysdh)5zhDWK-oXD26X`K_r#2#oi2 zDkj@2+-w(5I+M6%ih`u5N%`syASU-QRn-p)vAchi%{{J&OTmB6d9O7wKg^S*@fa

    O%9PJ!zfp(9bIjhVg4uQ%V~ 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�d9mwzR}cK(J2JH% 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 ESd4{4MF0Q* From 794959e1822081dabc733957e0bc73c3aa26ba8e Mon Sep 17 00:00:00 2001 From: Remy Jette Date: Wed, 20 Sep 2023 01:20:03 -0700 Subject: [PATCH 043/144] WebHost: Fix KeyError in alttp multitracker (#2194) --- WebHostLib/tracker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WebHostLib/tracker.py b/WebHostLib/tracker.py index 96a2b0fda7..0d9ead7951 100644 --- a/WebHostLib/tracker.py +++ b/WebHostLib/tracker.py @@ -1683,7 +1683,7 @@ def get_LttP_multiworld_tracker(tracker: UUID): for item_id in precollected: attribute_item(team, player, item_id) for location in locations_checked: - if location not in player_locations or location not in player_location_to_area[player]: + if location not in player_locations or location not in player_location_to_area.get(player, {}): continue item, recipient, flags = player_locations[location] recipients = groups.get(recipient, [recipient]) From 4a27fae1abc6ab0c6822622cd4c8a03b984a138d Mon Sep 17 00:00:00 2001 From: CaitSith2 Date: Wed, 20 Sep 2023 02:22:01 -0700 Subject: [PATCH 044/144] Core: Allow any valid priority location in yaml even when they are not used in a given game. (#2128) * Allow any valid priority location in yaml. For some games, the use location group name "Everywhere", results in the generator failing no matter what, as only a subset of the location names will actually be present. A good example of that is Zillion. It has 21 location names per room, of which, only at most 2 is ever used. Co-authored-by: Fabian Dill --- Main.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Main.py b/Main.py index ab3a8a6668..48b37764a9 100644 --- a/Main.py +++ b/Main.py @@ -139,7 +139,13 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No exclusion_rules(world, player, world.exclude_locations[player].value) world.priority_locations[player].value -= world.exclude_locations[player].value for location_name in world.priority_locations[player].value: - world.get_location(location_name, player).progress_type = LocationProgressType.PRIORITY + try: + location = world.get_location(location_name, player) + except KeyError as e: # failed to find the given location. Check if it's a legitimate location + if location_name not in world.worlds[player].location_name_to_id: + raise Exception(f"Unable to prioritize location {location_name} in player {player}'s world.") from e + else: + location.progress_type = LocationProgressType.PRIORITY # Set local and non-local item rules. if world.players > 1: From d471dcc0676852cc3005e8e7e6bb77b40c03b258 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Wed, 20 Sep 2023 16:05:56 +0200 Subject: [PATCH 045/144] Core, WebHost: lazy-load worlds in unpickler, WebHost and WebHostLib (#2156) * Core: lazy-load worlds in unpickler this should hopefully fix customserver's memory consumption * WebHost: move imports around to save memory in MP * MultiServer: prefer loading _speedups without pyximport This saves ~15MB per MP and speeds up module import if it was built in-place. * Tests: fix tests for changed WebHost imports * CustomServer: run GC after setup * CustomServer: cleanup exception handling --- NetUtils.py | 24 +++++++++----- Utils.py | 6 +++- WebHost.py | 18 +++++------ WebHostLib/autolauncher.py | 52 +----------------------------- WebHostLib/customserver.py | 11 ++++--- WebHostLib/locker.py | 51 +++++++++++++++++++++++++++++ test/webhost/TestAPIGenerate.py | 3 +- test/webhost/TestFileGeneration.py | 3 +- 8 files changed, 93 insertions(+), 75 deletions(-) create mode 100644 WebHostLib/locker.py diff --git a/NetUtils.py b/NetUtils.py index c31aa69510..ee11c28f33 100644 --- a/NetUtils.py +++ b/NetUtils.py @@ -407,14 +407,22 @@ class _LocationStore(dict, typing.MutableMapping[int, typing.Dict[int, typing.Tu if typing.TYPE_CHECKING: # type-check with pure python implementation until we have a typing stub LocationStore = _LocationStore else: - try: - import pyximport - pyximport.install() - except ImportError: - pyximport = None try: from _speedups import LocationStore + import _speedups + import os.path + if os.path.getctime(_speedups.__file__) < os.path.getctime("_speedups.pyx"): + warnings.warn(f"{_speedups.__file__} outdated! " + f"Please rebuild with `cythonize -b -i _speedups.pyx` or delete it!") except ImportError: - warnings.warn("_speedups not available. Falling back to pure python LocationStore. " - "Install a matching C++ compiler for your platform to compile _speedups.") - LocationStore = _LocationStore + try: + import pyximport + pyximport.install() + except ImportError: + pyximport = None + try: + from _speedups import LocationStore + except ImportError: + warnings.warn("_speedups not available. Falling back to pure python LocationStore. " + "Install a matching C++ compiler for your platform to compile _speedups.") + LocationStore = _LocationStore diff --git a/Utils.py b/Utils.py index 12517173e5..1556a764f2 100644 --- a/Utils.py +++ b/Utils.py @@ -359,11 +359,13 @@ safe_builtins = frozenset(( class RestrictedUnpickler(pickle.Unpickler): + generic_properties_module: Optional[object] + def __init__(self, *args, **kwargs): super(RestrictedUnpickler, self).__init__(*args, **kwargs) self.options_module = importlib.import_module("Options") self.net_utils_module = importlib.import_module("NetUtils") - self.generic_properties_module = importlib.import_module("worlds.generic") + self.generic_properties_module = None def find_class(self, module, name): if module == "builtins" and name in safe_builtins: @@ -373,6 +375,8 @@ class RestrictedUnpickler(pickle.Unpickler): return getattr(self.net_utils_module, name) # Options and Plando are unpickled by WebHost -> Generate if module == "worlds.generic" and name in {"PlandoItem", "PlandoConnection"}: + if not self.generic_properties_module: + self.generic_properties_module = importlib.import_module("worlds.generic") return getattr(self.generic_properties_module, name) # pep 8 specifies that modules should have "all-lowercase names" (options, not Options) if module.lower().endswith("options"): diff --git a/WebHost.py b/WebHost.py index 36645ad27d..8595fa7a27 100644 --- a/WebHost.py +++ b/WebHost.py @@ -13,15 +13,6 @@ import Utils import settings Utils.local_path.cached_path = os.path.dirname(__file__) or "." # py3.8 is not abs. remove "." when dropping 3.8 - -from WebHostLib import register, cache, app as raw_app -from waitress import serve - -from WebHostLib.models import db -from WebHostLib.autolauncher import autohost, autogen -from WebHostLib.lttpsprites import update_sprites_lttp -from WebHostLib.options import create as create_options_files - settings.no_gui = True configpath = os.path.abspath("config.yaml") if not os.path.exists(configpath): # fall back to config.yaml in home @@ -29,6 +20,9 @@ if not os.path.exists(configpath): # fall back to config.yaml in home def get_app(): + from WebHostLib import register, cache, app as raw_app + from WebHostLib.models import db + register() app = raw_app if os.path.exists(configpath) and not app.config["TESTING"]: @@ -121,6 +115,11 @@ if __name__ == "__main__": multiprocessing.freeze_support() multiprocessing.set_start_method('spawn') logging.basicConfig(format='[%(asctime)s] %(message)s', level=logging.INFO) + + from WebHostLib.lttpsprites import update_sprites_lttp + from WebHostLib.autolauncher import autohost, autogen + from WebHostLib.options import create as create_options_files + try: update_sprites_lttp() except Exception as e: @@ -137,4 +136,5 @@ if __name__ == "__main__": if app.config["DEBUG"]: app.run(debug=True, port=app.config["PORT"]) else: + from waitress import serve serve(app, port=app.config["PORT"], threads=app.config["WAITRESS_THREADS"]) diff --git a/WebHostLib/autolauncher.py b/WebHostLib/autolauncher.py index 0475a63297..9083867120 100644 --- a/WebHostLib/autolauncher.py +++ b/WebHostLib/autolauncher.py @@ -3,8 +3,6 @@ from __future__ import annotations import json import logging import multiprocessing -import os -import sys import threading import time import typing @@ -13,55 +11,7 @@ from datetime import timedelta, datetime from pony.orm import db_session, select, commit from Utils import restricted_loads - - -class CommonLocker(): - """Uses a file lock to signal that something is already running""" - lock_folder = "file_locks" - - def __init__(self, lockname: str, folder=None): - if folder: - self.lock_folder = folder - os.makedirs(self.lock_folder, exist_ok=True) - self.lockname = lockname - self.lockfile = os.path.join(self.lock_folder, f"{self.lockname}.lck") - - -class AlreadyRunningException(Exception): - pass - - -if sys.platform == 'win32': - class Locker(CommonLocker): - def __enter__(self): - try: - if os.path.exists(self.lockfile): - os.unlink(self.lockfile) - self.fp = os.open( - self.lockfile, os.O_CREAT | os.O_EXCL | os.O_RDWR) - except OSError as e: - raise AlreadyRunningException() from e - - def __exit__(self, _type, value, tb): - fp = getattr(self, "fp", None) - if fp: - os.close(self.fp) - os.unlink(self.lockfile) -else: # unix - import fcntl - - - class Locker(CommonLocker): - def __enter__(self): - try: - self.fp = open(self.lockfile, "wb") - fcntl.flock(self.fp.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) - except OSError as e: - raise AlreadyRunningException() from e - - def __exit__(self, _type, value, tb): - fcntl.flock(self.fp.fileno(), fcntl.LOCK_UN) - self.fp.close() +from .locker import Locker, AlreadyRunningException def launch_room(room: Room, config: dict): diff --git a/WebHostLib/customserver.py b/WebHostLib/customserver.py index 8fbf692dec..16f8c8b261 100644 --- a/WebHostLib/customserver.py +++ b/WebHostLib/customserver.py @@ -19,6 +19,7 @@ import Utils from MultiServer import Context, server, auto_shutdown, ServerCommandProcessor, ClientMessageProcessor, load_server_cert from Utils import restricted_loads, cache_argsless +from .locker import Locker from .models import Command, GameDataPackage, Room, db @@ -163,16 +164,19 @@ def run_server_process(room_id, ponyconfig: dict, static_server_data: dict, db.generate_mapping(check_tables=False) async def main(): + import gc + Utils.init_logging(str(room_id), write_mode="a") ctx = WebHostContext(static_server_data) ctx.load(room_id) ctx.init_save() ssl_context = load_server_cert(cert_file, cert_key_file) if cert_file else None + gc.collect() # free intermediate objects used during setup try: ctx.server = websockets.serve(functools.partial(server, ctx=ctx), ctx.host, ctx.port, ssl=ssl_context) await ctx.server - except Exception: # likely port in use - in windows this is OSError, but I didn't check the others + except OSError: # likely port in use ctx.server = websockets.serve(functools.partial(server, ctx=ctx), ctx.host, 0, ssl=ssl_context) await ctx.server @@ -198,16 +202,15 @@ def run_server_process(room_id, ponyconfig: dict, static_server_data: dict, await ctx.shutdown_task logging.info("Shutting down") - from .autolauncher import Locker with Locker(room_id): try: asyncio.run(main()) - except KeyboardInterrupt: + except (KeyboardInterrupt, SystemExit): with db_session: room = Room.get(id=room_id) # ensure the Room does not spin up again on its own, minute of safety buffer room.last_activity = datetime.datetime.utcnow() - datetime.timedelta(minutes=1, seconds=room.timeout) - except: + except Exception: with db_session: room = Room.get(id=room_id) room.last_port = -1 diff --git a/WebHostLib/locker.py b/WebHostLib/locker.py new file mode 100644 index 0000000000..5293352887 --- /dev/null +++ b/WebHostLib/locker.py @@ -0,0 +1,51 @@ +import os +import sys + + +class CommonLocker: + """Uses a file lock to signal that something is already running""" + lock_folder = "file_locks" + + def __init__(self, lockname: str, folder=None): + if folder: + self.lock_folder = folder + os.makedirs(self.lock_folder, exist_ok=True) + self.lockname = lockname + self.lockfile = os.path.join(self.lock_folder, f"{self.lockname}.lck") + + +class AlreadyRunningException(Exception): + pass + + +if sys.platform == 'win32': + class Locker(CommonLocker): + def __enter__(self): + try: + if os.path.exists(self.lockfile): + os.unlink(self.lockfile) + self.fp = os.open( + self.lockfile, os.O_CREAT | os.O_EXCL | os.O_RDWR) + except OSError as e: + raise AlreadyRunningException() from e + + def __exit__(self, _type, value, tb): + fp = getattr(self, "fp", None) + if fp: + os.close(self.fp) + os.unlink(self.lockfile) +else: # unix + import fcntl + + + class Locker(CommonLocker): + def __enter__(self): + try: + self.fp = open(self.lockfile, "wb") + fcntl.flock(self.fp.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) + except OSError as e: + raise AlreadyRunningException() from e + + def __exit__(self, _type, value, tb): + fcntl.flock(self.fp.fileno(), fcntl.LOCK_UN) + self.fp.close() diff --git a/test/webhost/TestAPIGenerate.py b/test/webhost/TestAPIGenerate.py index 5c14373a90..8ea78f27f9 100644 --- a/test/webhost/TestAPIGenerate.py +++ b/test/webhost/TestAPIGenerate.py @@ -5,7 +5,8 @@ import json class TestDocs(unittest.TestCase): @classmethod def setUpClass(cls) -> None: - from WebHost import get_app, raw_app + from WebHostLib import app as raw_app + from WebHost import get_app raw_app.config["PONY"] = { "provider": "sqlite", "filename": ":memory:", diff --git a/test/webhost/TestFileGeneration.py b/test/webhost/TestFileGeneration.py index 6010202c41..f01b70e14f 100644 --- a/test/webhost/TestFileGeneration.py +++ b/test/webhost/TestFileGeneration.py @@ -14,7 +14,8 @@ class TestFileGeneration(unittest.TestCase): cls.incorrect_path = os.path.join(os.path.split(os.path.dirname(__file__))[0], "WebHostLib") def testOptions(self): - WebHost.create_options_files() + from WebHostLib.options import create as create_options_files + create_options_files() target = os.path.join(self.correct_path, "static", "generated", "configs") self.assertTrue(os.path.exists(target)) self.assertFalse(os.path.exists(os.path.join(self.incorrect_path, "static", "generated", "configs"))) From 638d6807dbfe642a2c149d2875a59964ded67143 Mon Sep 17 00:00:00 2001 From: kindasneaki Date: Wed, 20 Sep 2023 14:53:00 -0600 Subject: [PATCH 046/144] add invisible to locations div (#2191) --- WebHostLib/static/assets/weighted-settings.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WebHostLib/static/assets/weighted-settings.js b/WebHostLib/static/assets/weighted-settings.js index 07157cb579..fb7d3a349b 100644 --- a/WebHostLib/static/assets/weighted-settings.js +++ b/WebHostLib/static/assets/weighted-settings.js @@ -160,6 +160,7 @@ const buildUI = (settingData) => { weightedSettingsDiv.classList.add('invisible'); itemPoolDiv.classList.add('invisible'); hintsDiv.classList.add('invisible'); + locationsDiv.classList.add('invisible'); expandButton.classList.remove('invisible'); }); @@ -168,6 +169,7 @@ const buildUI = (settingData) => { weightedSettingsDiv.classList.remove('invisible'); itemPoolDiv.classList.remove('invisible'); hintsDiv.classList.remove('invisible'); + locationsDiv.classList.remove('invisible'); expandButton.classList.add('invisible'); }); }); From 5e46967b7d96c135f2a9d37b6104b8e93abe461c Mon Sep 17 00:00:00 2001 From: lordlou <87331798+lordlou@users.noreply.github.com> Date: Thu, 21 Sep 2023 20:49:27 -0400 Subject: [PATCH 047/144] SM: 0.4.2 broken quick save and reload fix (#2204) --- .../multiworld-basepatch.ips | Bin 19144 -> 19156 bytes .../data/SMBasepatch_prebuilt/multiworld.sym | 245 +++++++++--------- .../sm-basepatch-symbols.json | 4 +- worlds/sm/docs/en_Super Metroid.md | 3 +- worlds/sm/variaRandomizer/rom/rompatcher.py | 10 +- 5 files changed, 136 insertions(+), 126 deletions(-) diff --git a/worlds/sm/data/SMBasepatch_prebuilt/multiworld-basepatch.ips b/worlds/sm/data/SMBasepatch_prebuilt/multiworld-basepatch.ips index 67863bb9f00228aee09d1c8257bf41cbd17e8a90..7854ca3332874a330fd15ea22c1b69326ec799df 100644 GIT binary patch delta 109 zcmX>xmGR0{#t9P`A5NT@pvb`0Dzs|9c4K2^7~`r84^4*uujex{6l7@rXPD2xP@tqW znV(U`WGO_=ss#)T`+=eosUoc+FJ7EDaUh26!nx|=0g#XZ)sc~49tx_zD}jM&RspJLNGSmS>z@JuaA+z20G|K=a3Uv@ z5& Date: Thu, 21 Sep 2023 20:21:35 +0200 Subject: [PATCH 048/144] Witness: Expert Doors Logic Fix Expert Swamp Maze currently thinks it needs Red Underwater 4 instead of the door. This could lead to an unbeatable seed in door shuffle, although it's very unlikely. --- worlds/witness/WitnessLogicExpert.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/witness/WitnessLogicExpert.txt b/worlds/witness/WitnessLogicExpert.txt index b373c7417c..581167cc45 100644 --- a/worlds/witness/WitnessLogicExpert.txt +++ b/worlds/witness/WitnessLogicExpert.txt @@ -703,7 +703,7 @@ Swamp Between Bridges Far (Swamp) - Swamp Red Underwater - 0x183F2 - Swamp Rotat 158322 - 0x0000A (Between Bridges Far Row 4) - 0x00009 - Rotated Shapers & Shapers & Dots & Full Dots Door - 0x183F2 (Red Water Pump) - 0x00596 -Swamp Red Underwater (Swamp) - Swamp Maze - 0x014D1: +Swamp Red Underwater (Swamp) - Swamp Maze - 0x305D5: 158323 - 0x00001 (Red Underwater 1) - True - Shapers & Negative Shapers & Dots & Full Dots 158324 - 0x014D2 (Red Underwater 2) - True - Shapers & Negative Shapers & Dots & Full Dots 158325 - 0x014D4 (Red Underwater 3) - True - Shapers & Negative Shapers & Dots & Full Dots From 9931605f94322ddc82856b8da02a88a04a0f9ba0 Mon Sep 17 00:00:00 2001 From: Ziktofel Date: Fri, 22 Sep 2023 14:33:10 +0200 Subject: [PATCH 049/144] SC2 Client: Fix processing metadata from Github Releases for `/download_data` --- worlds/sc2wol/Client.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/worlds/sc2wol/Client.py b/worlds/sc2wol/Client.py index c544cf0c55..a9bb826b74 100644 --- a/worlds/sc2wol/Client.py +++ b/worlds/sc2wol/Client.py @@ -3,6 +3,7 @@ from __future__ import annotations import asyncio import copy import ctypes +import json import logging import multiprocessing import os.path @@ -1146,7 +1147,9 @@ def download_latest_release_zip(owner: str, repo: str, api_version: str, metadat r1 = requests.get(url, headers=headers) if r1.status_code == 200: - latest_metadata = str(r1.json()) + latest_metadata = r1.json() + cleanup_downloaded_metadata(latest_metadata) + latest_metadata = str(latest_metadata) # sc2_logger.info(f"Latest version: {latest_metadata}.") else: sc2_logger.warning(f"Status code: {r1.status_code}") @@ -1174,6 +1177,11 @@ def download_latest_release_zip(owner: str, repo: str, api_version: str, metadat return "", metadata +def cleanup_downloaded_metadata(medatada_json): + for asset in medatada_json['assets']: + del asset['download_count'] + + def is_mod_update_available(owner: str, repo: str, api_version: str, metadata: str) -> bool: import requests @@ -1182,7 +1190,9 @@ def is_mod_update_available(owner: str, repo: str, api_version: str, metadata: s r1 = requests.get(url, headers=headers) if r1.status_code == 200: - latest_metadata = str(r1.json()) + latest_metadata = r1.json() + cleanup_downloaded_metadata(latest_metadata) + latest_metadata = str(latest_metadata) if metadata != latest_metadata: return True else: From 39a50da55c18f86752e919daf0e6dada46535225 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Fri, 22 Sep 2023 21:32:03 +0200 Subject: [PATCH 050/144] Factorio: fix world generation in spoiler (#2209) This used a set operation previously, resulting in random order of dict items. --- worlds/factorio/Options.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/factorio/Options.py b/worlds/factorio/Options.py index 2b579658fc..18eee67e03 100644 --- a/worlds/factorio/Options.py +++ b/worlds/factorio/Options.py @@ -390,8 +390,8 @@ class FactorioWorldGen(OptionDict): def __init__(self, value: typing.Dict[str, typing.Any]): advanced = {"pollution", "enemy_evolution", "enemy_expansion"} self.value = { - "basic": {key: value[key] for key in value.keys() - advanced}, - "advanced": {key: value[key] for key in value.keys() & advanced} + "basic": {k: v for k, v in value.items() if k not in advanced}, + "advanced": {k: v for k, v in value.items() if k in advanced} } # verify min_values <= max_values From b4b8426defbdff1b876de59f760221e95c986b09 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Fri, 22 Sep 2023 21:34:40 +0200 Subject: [PATCH 051/144] Core: update jellyfish --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index abb572ad95..bfc637a80a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ colorama>=0.4.5 websockets>=11.0.3 PyYAML>=6.0.1 -jellyfish>=1.0.0 +jellyfish>=1.0.1 jinja2>=3.1.2 schema>=0.7.5 kivy>=2.2.0 From ea799c494e3064b5145e51f8612fdd39e190ffdc Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Fri, 22 Sep 2023 23:05:04 +0200 Subject: [PATCH 052/144] Speedups: fix file date check when frozen (#2211) --- NetUtils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NetUtils.py b/NetUtils.py index ee11c28f33..a2db6a2ac5 100644 --- a/NetUtils.py +++ b/NetUtils.py @@ -411,7 +411,7 @@ else: from _speedups import LocationStore import _speedups import os.path - if os.path.getctime(_speedups.__file__) < os.path.getctime("_speedups.pyx"): + if os.path.isfile("_speedups.pyx") and os.path.getctime(_speedups.__file__) < os.path.getctime("_speedups.pyx"): warnings.warn(f"{_speedups.__file__} outdated! " f"Please rebuild with `cythonize -b -i _speedups.pyx` or delete it!") except ImportError: From b40fba0840d5e3ab623f3d697f1af3b6770fdafd Mon Sep 17 00:00:00 2001 From: eudaimonistic <94811100+eudaimonistic@users.noreply.github.com> Date: Fri, 22 Sep 2023 17:08:27 -0400 Subject: [PATCH 053/144] Subnautica: Update en_Subnautica.md (#2207) Added reference to the four goal options, and a small grammatical change. --- worlds/subnautica/docs/en_Subnautica.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/subnautica/docs/en_Subnautica.md b/worlds/subnautica/docs/en_Subnautica.md index 9a112aa596..5e99208b5f 100644 --- a/worlds/subnautica/docs/en_Subnautica.md +++ b/worlds/subnautica/docs/en_Subnautica.md @@ -12,7 +12,7 @@ awarded from scanning those items have been shuffled into location checks throug ## What is the goal of Subnautica when randomized? -The goal remains unchanged. Cure the plague, build the Neptune Escape Rocket, and escape into space. +There are four goals currently available. The Launch goal has you leave the planet. The Free goal has you cure the plague. Infected is achieved at maximum infection level. Drive asks you to repair the Aurora Drive Core. ## What items and locations get shuffled? @@ -34,5 +34,5 @@ player's world. ## When the player receives a technology, what happens? -When the player receives a technology, the chat log displays a notification the technology has been received. +When the player receives a technology, the chat log displays a notification that the technology has been received. From db7c0c9db96515e9ad2cbc6f00c54c6e82581278 Mon Sep 17 00:00:00 2001 From: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> Date: Fri, 22 Sep 2023 20:45:52 -0400 Subject: [PATCH 054/144] Docs: Clarify Documentation Information for Undertale, Terraria, DOOM 1993 (#2149) * Cleaned up Undertale documentation Standardized file names * Outlined Terraria installation more clearly Other minor edits to setup guide * Minor edits to DOOM 1993 set-up guide * Update worlds/terraria/docs/setup_en.md Co-authored-by: kindasneaki * Suggested changes from @Seldom-SE Co-authored-by: Seldom <38388947+seldom-se@users.noreply.github.com> * Code block to quotation change from code review Co-authored-by: Seldom <38388947+seldom-se@users.noreply.github.com> Co-authored-by: Chris Wilson * Code review from @LegendaryLinux Co-authored-by: Chris Wilson --------- Co-authored-by: kindasneaki Co-authored-by: Seldom <38388947+seldom-se@users.noreply.github.com> Co-authored-by: Chris Wilson --- worlds/doom_1993/docs/setup_en.md | 4 +- worlds/terraria/docs/setup_en.md | 42 ++++++++++------- worlds/undertale/__init__.py | 4 +- worlds/undertale/docs/en_Undertale.md | 33 ++++++++------ worlds/undertale/docs/setup_en.md | 65 +++++++++++++++++++++++++++ worlds/undertale/docs/undertale_en.md | 59 ------------------------ 6 files changed, 114 insertions(+), 93 deletions(-) create mode 100644 worlds/undertale/docs/setup_en.md delete mode 100644 worlds/undertale/docs/undertale_en.md diff --git a/worlds/doom_1993/docs/setup_en.md b/worlds/doom_1993/docs/setup_en.md index 5afe6d3e26..cfd97f623a 100644 --- a/worlds/doom_1993/docs/setup_en.md +++ b/worlds/doom_1993/docs/setup_en.md @@ -11,9 +11,9 @@ ## Installing AP Doom 1. Download [APDOOM.zip](https://github.com/Daivuk/apdoom/releases) and extract it. -2. Copy DOOM.WAD from your steam install into the extracted folder. +2. Copy `DOOM.WAD` from your game's installation directory into the newly extracted folder. You can find the folder in steam by finding the game in your library, - right clicking it and choosing *Manage→Browse Local Files*. + right-clicking it and choosing **Manage -> Browse Local Files**. ## Joining a MultiWorld Game diff --git a/worlds/terraria/docs/setup_en.md b/worlds/terraria/docs/setup_en.md index d0833b7484..84744a4a33 100644 --- a/worlds/terraria/docs/setup_en.md +++ b/worlds/terraria/docs/setup_en.md @@ -3,11 +3,23 @@ ## Required Software Download and install [Terraria](https://store.steampowered.com/app/105600/Terraria/) -and [TModLoader](https://store.steampowered.com/app/1281930/tModLoader/) on Steam +and [tModLoader](https://store.steampowered.com/app/1281930/tModLoader/) on Steam ## Installing the Archipelago Mod -Subscribe to [the mod](https://steamcommunity.com/sharedfiles/filedetails/?id=2922217554) on Steam. +1. Subscribe to [the mod](https://steamcommunity.com/sharedfiles/filedetails/?id=2922217554) on Steam +2. Open tModLoader +3. Go to **Workshop -> Manage Mods** and enable the Archipelago mod + - If tModLoader states that you need version 1.4.3, follow the following steps + 1. Close tModLoader + 2. Right-Click tModLoader in Steam and select **Properties** + 3. Navigate to **Betas -> Beta Participation** + 4. Select **1.4.3-legacy - Legacy - Stable tModLoader for Terraria 1.4.3** + 5. Update tModLoader through Steam + 6. Open tModLoader and navigate back to the **Manage Mods** menu +4. tModLoader will say that it needs to refresh; exit this menu, and it will do this automatically +5. Once tModLoader finishes loading, the Archipelago mod is finished installing; you can now +[connect to an Archipelago game](#joining-an-archipelago-game-in-terraria). This mod might not work with mods that significantly alter progression or vanilla features. It is highly recommended to use utility mods and features to speed up gameplay, such as: @@ -16,7 +28,7 @@ highly recommended to use utility mods and features to speed up gameplay, such a - Ore Excavator - Magic Storage - Alchemist NPC Lite - - (May be used to break progression) + - (Can be used to break progression) - Reduced Grinding - Upgraded Research @@ -24,8 +36,8 @@ highly recommended to use utility mods and features to speed up gameplay, such a ### What is a YAML and why do I need one? -You can see the [basic multiworld setup guide](/tutorial/Archipelago/setup/en) here -on the Archipelago website to learn about why Archipelago uses YAML files and what they're for. +The [basic multiworld setup guide](/tutorial/Archipelago/setup/en) can be found on Archipelago's website. Among other things, it explains what .yaml +files are, and how they are used. ### Where do I get a YAML? @@ -34,17 +46,15 @@ on the Archipelago website to generate a YAML using a graphical interface. ## Joining an Archipelago Game in Terraria -1. Launch TModLoader -2. In Workshop > Manage Mods, edit Archipelago Randomizer's settings - - "Name" should be the player name you set when creating your YAML file - - "Port" should be the port number associated with the Archipelago server. It will be a 4 or 5 - digit number. - - If you're not hosting your game on the Archipelago website, change "Address" to the server's - URL or IP address -3. Create a new character and world as normal (or use an existing one if you prefer). Terraria is -usually significantly more difficult with this mod, so it is recommended to choose a lower -difficulty than you normally would. -4. Open the world in single player or multiplayer +1. Launch tModLoader +2. In **Workshop > Manage Mods**, edit Archipelago Randomizer's settings + - **Name** should be the player name you set when creating your YAML file. + - **Port** should be the port number associated with the Archipelago server. It will be a 4 or 5-digit number. + - If you're not hosting your game on the Archipelago website, change **Address** to the server's URL or IP address +3. Create a new character and world as normal (or use an existing one if you prefer). Terraria usually becomes +significantly more difficult with this mod, so it is recommended to choose a lower difficulty than you normally would +play on. +4. Open the world in single player or multiplayer. 5. When you're ready, open chat, and enter `/apstart` to start the game. ## Commands diff --git a/worlds/undertale/__init__.py b/worlds/undertale/__init__.py index 3a34a162c4..5e36344703 100644 --- a/worlds/undertale/__init__.py +++ b/worlds/undertale/__init__.py @@ -33,8 +33,8 @@ class UndertaleWeb(WebWorld): "A guide to setting up the Archipelago Undertale software on your computer. This guide covers " "single-player, multiworld, and related software.", "English", - "undertale_en.md", - "undertale/en", + "setup_en.md", + "setup/en", ["Mewlif"] )] diff --git a/worlds/undertale/docs/en_Undertale.md b/worlds/undertale/docs/en_Undertale.md index 79ca21681e..3905d3bc3e 100644 --- a/worlds/undertale/docs/en_Undertale.md +++ b/worlds/undertale/docs/en_Undertale.md @@ -24,24 +24,29 @@ every major route in the game, those being `Pacifist`, `Neutral`, and `Genocide` There are some major differences between vanilla and the randomizer. -There are now doors to every major area in the underground located in the flower room (The first room of the game), those being Ruins, Snowdin, Waterfall, Hotland, and Core. +There are now doors to every major area in the underground located in the flower room (the first room of the game.) +These doors lead to Ruins, Snowdin, Waterfall, Hotland, and Core from left to right. Each door needs their respective key from the pool to enter. -You start with one key for a random door. (Core will never be given to start with.) -The rest of the keys will be in the item pool. +You start with one key for a random door and the rest of the keys will be in the item pool to be found by other players. +(Core will never be given to start with, unless otherwise specified.) -Genocide works a little differently in terms of the requirements. -You now only need to get through Core and fight Mettaton NEO, and then beat Sans, to win. -If you choose to fight other major bosses, you will still need to grind out the area before fighting them like normal. -Pacifist is mostly the same, except you are not required to go to the Ruins to spare Toriel, -you only need to spare Papyrus, Undyne, and Mettaton EX. Although you still cannot kill anyone. -You are also still required to do the date/hangout with Papyrus, the hangout with Undyne, and the date with Alphys, -in that order, before entering the True Lab. +**Genocide** works a little differently in terms of the requirements. -You now require custom items to Hangout with Papyrus, Undyne, to enter the True Lab, and to fight Mettaton EX/NEO. -Those being `Complete Skeleton`, `Fish`, `DT Extractor`, and `Mettaton Plush`. +In order to win with the genocide route, you only need to get through Core, fight Mettaton NEO, and beat Sans to win. +If you choose to fight other major bosses, you will still need to progress the area like normal before fighting them. -The Riverperson will only take you to locations you have actually seen the Riverperson at. -Meaning they will only take you to, for example, Waterfall, if you have seen the Riverperson at Waterfall at least once. +**Pacifist** remains mostly the same. + +In the Pacifist run, you are not required to go to the Ruins to spare Toriel. The only necessary spares are Papyrus, +Undyne, and Mettaton EX. Just as it is in the vanilla game, you cannot kill anyone. You are also required to complete +the date/hangout with Papyrus, Undyne, and Alphys, in that order, before entering the True Lab. + +Additionally, custom items are required to hang out with Papyrus, Undyne, to enter the True Lab, and to fight +Mettaton EX/NEO. The respective items for each interaction are `Complete Skeleton`, `Fish`, `DT Extractor`, +and `Mettaton Plush`. + +The Riverperson will only take you to locations you have seen them at, meaning they will only take you to +Waterfall if you have seen them at Waterfall at least once. If you press `W` while in the save menu, you will teleport back to the flower room, for quick access to the other areas. \ No newline at end of file diff --git a/worlds/undertale/docs/setup_en.md b/worlds/undertale/docs/setup_en.md new file mode 100644 index 0000000000..f82105c269 --- /dev/null +++ b/worlds/undertale/docs/setup_en.md @@ -0,0 +1,65 @@ +# Undertale Randomizer Setup Guide + +### Required Software + +- Undertale from the [Steam page](https://store.steampowered.com/app/391540) +- Archipelago from the [Archipelago Releases Page](https://github.com/ArchipelagoMW/Archipelago/releases) + - (select `Undertale Client` during installation.) + +### First time setup + +Start the Undertale client from your Archipelago folder and input `/auto_patch ` at the bottom. + +This directory is usually located at `C:\Program Files\Steam\steamapps\Undertale`, but it can be different depending on +your installation. You can easily find the directory by opening the Undertale directory through Steam by right-clicking +Undertale in your library and selecting `Manage -> Browse local files`. Then, on Windows you can see the directory that +you need at the top of the window that opens. + +After using the `/auto_patch` command, **Archipelago will make an Undertale folder within the Archipelago install +location.** That folder contains the version of Undertale you will use for Archipelago. (If you update Archipelago, +you will need to redo this set-up.) + +**Linux Users**: The Linux installation is mostly similar, however, Undertale will be installed on Steam as the Linux +variant. Since this randomizer only supports the Windows version, we must fix this, by right-click the game in Steam, +going to `Properties -> Compatibility`, and checking `Force the use of a specific Steam Play compatibility tool`. This +downloads the Windows version of Undertale to use instead of the Linux version. If the play button is greyed out in +Steam, be sure to go to `Settings -> Compatibility` and toggle `Enable Steam Play for all other titles`. + +### Connect to the MultiServer + +Make sure both Undertale **from the Archipelago folder** and its client are running. (Undertale will ask for a save slot +to play on. Archipelago Undertale does not overwrite vanilla saves, but you may want to back up your save as a precaution.) + +In the top text box of the client, type the `IP Address` (or `Hostname`) and `Port` separated with a `:` symbol. +(Ex. `archipelago.gg:38281`) + +The client will then ask for the slot name, input your slot name chosen during YAML creation in the text box at the +bottom of the client. + +**Linux Users**: When you start the client, it is likely that the save data path is incorrect, and how the game +is played depends on where the save data folder is located. + +**On Steam (via Proton)**: This assumes the game is in a Steam Library folder. Right-click Undertale, go to `Manage -> +Browse Local Files`. Go up the directories to the `steamapps` folder, open `compatdata/391540` (391540 is the "magic number" for +Undertale in Steam). Save data from here is at `/pfx/drive_c/users/steamuser/AppData/Local/UNDERTALE`. + +**Through WINE directly**: This depends on the prefix used. If it is default, then the save data is located at +`/home/USERNAME/.wine/drive_c/users/USERNAME/AppData/Local/UNDERTALE`. + +Once the save data folder is located, run the `/savepath` command to redirect the client to the correct save data folder +before connecting. + +### Play the game + +When the console tells you that you have joined the room, you're all set. Congratulations on successfully joining a +multi-world game! + +### PLEASE READ! + +Please read this page in its entirety before asking questions! Most importantly, there is a list of +gameplay differences at the bottom. +[Undertale Game Info Page](/games/Undertale/info/en) + +### Where do I get a YAML file? + +You can customize your settings by visiting the [Undertale Player Settings Page](/games/Undertale/player-settings) diff --git a/worlds/undertale/docs/undertale_en.md b/worlds/undertale/docs/undertale_en.md deleted file mode 100644 index a2f3d2579a..0000000000 --- a/worlds/undertale/docs/undertale_en.md +++ /dev/null @@ -1,59 +0,0 @@ -# Undertale Randomizer Setup Guide - -### Required Software - -- Undertale from the [Steam page](https://store.steampowered.com/app/391540) -- Archipelago from the [Archipelago Releases Page](https://github.com/ArchipelagoMW/Archipelago/releases) - - (select `Undertale Client` during installation.) - -### First time setup - -Start the Undertale client, and in the bottom text box, input `/auto_patch (Input your Undertale install directory here)` (It is usually located at `C:\Program Files\Steam\steamapps\Undertale`, but it can be different, you can more easily find the directory -by opening the Undertale directory through Steam), it will then make an Undertale folder that will be created in the -Archipelago install location. That contains the version of Undertale you will use for Archipelago. (You will need to -redo this step when updating Archipelago.) - -**Linux Users**: This guide is mostly similar; however, when Undertale is installed on Steam, it defaults to a Linux -supported variant; this randomizer only supports the Windows version. To fix this, right-click the game in Steam, go to -Properties -> Compatibility, and check "Force the use of a specific Steam Play compatibility tool". This -downloads the Windows version instead. If the play button is greyed out in Steam, be sure to go to -Settings -> Compatibility and toggle "Enable Steam Play for all other titles". - -### Connect to the MultiServer - -Make sure both Undertale and its client are running. (Undertale will ask for a saveslot, it can be 1 through 99, none -of the slots will overwrite your vanilla save, although you may want to make a backup just in case.) - -In the top text box of the client, type the -`Ip Address` (or `Hostname`) and `Port` separated with a `:` symbol. (Ex. `archipelago.gg:38281`) - -The client will then ask for the slot name, input that in the text box at the bottom of the client. - -**Linux Users**: When you start the client, it is likely that the save data path is incorrect, and how the game -is played depends on where the save data folder is located. - -*On Steam (via Proton)*: This assumes the game is in a Steam Library folder. Right-click Undertale, go to Manage -> -Browse Local Files. Move back to the steamapps folder, open compatdata/391540 (391540 is the "magic number" for -Undertale in Steam and can be confirmed by visiting its store page and looking at the URL). Save data from here is at -/pfx/drive_c/users/steamuser/AppData/Local/UNDERTALE. - -*Through WINE directly*: This depends on the prefix used. If it is default, then the save data is located at -/home/USERNAME/.wine/drive_c/users/USERNAME/AppData/Local/UNDERTALE. - -Once the save data folder is located, run the /savepath command to redirect the client to the correct save data folder -before connecting. - -### Play the game - -When the console tells you that you have joined the room, you're all set. Congratulations on successfully joining a -multiworld game! - -### PLEASE READ! - -Please read this page in its entirety before asking questions! Most importantly, there is a list of -gameplay differences at the bottom. -[Undertale Game Info Page](/games/Undertale/info/en) - -### Where do I get a YAML file? - -You can customize your settings by visiting the [Undertale Player Settings Page](/games/Undertale/player-settings) From f147f9e5a01b7fc35c528c978b6e97daa43a2e0e Mon Sep 17 00:00:00 2001 From: Bicoloursnake <60069210+Bicoloursnake@users.noreply.github.com> Date: Sat, 23 Sep 2023 00:40:47 -0400 Subject: [PATCH 055/144] Docs: DKC3, Lufia2AC, SM, SMW, SMZ3: Updating documentation with the current location of SNI Connector.lua (#2203) * Update SC2 setup guide Removed a sentence that made sense when I included sudo in the command in the previous sentence, but does not make sense otherwise. * Update en_Super Mario 64.md It turns out castle has a lowercase l in it. * Docs: SMW: Updated SNIClient Connector Lua Directory * Docs: DKC3: Updated SNIClient Connector Lua Directory * Docs: Lufia2AC: Updated SNIClient Connector Lua Directory * Docs: SM: Updated SNIClient Connector Lua Directory * Docs: SMZ3: Updated SNIClient Connector Lua Directory --- worlds/dkc3/docs/setup_en.md | 6 ++---- worlds/lufia2ac/docs/setup_en.md | 8 +++----- worlds/sm/docs/multiworld_en.md | 6 ++---- worlds/smw/docs/setup_en.md | 6 ++---- worlds/smz3/docs/multiworld_en.md | 6 ++---- 5 files changed, 11 insertions(+), 21 deletions(-) diff --git a/worlds/dkc3/docs/setup_en.md b/worlds/dkc3/docs/setup_en.md index 56ef80d4a5..bb10756300 100644 --- a/worlds/dkc3/docs/setup_en.md +++ b/worlds/dkc3/docs/setup_en.md @@ -87,8 +87,7 @@ first time launching, you may be prompted to allow it to communicate through the 3. Click on **New Lua Script Window...** 4. In the new window, click **Browse...** 5. Select the connector lua file included with your client - - Look in the Archipelago folder for `/SNI/lua/x64` or `/SNI/lua/x86` depending on if the - emulator is 64-bit or 32-bit. + - Look in the Archipelago folder for `/SNI/lua/Connector.lua`. 6. If you see an error while loading the script that states `socket.dll missing` or similar, navigate to the folder of the lua you are using in your file explorer and copy the `socket.dll` to the base folder of your snes9x install. @@ -100,8 +99,7 @@ the lua you are using in your file explorer and copy the `socket.dll` to the bas 2. Load your ROM file if it hasn't already been loaded. If you changed your core preference after loading the ROM, don't forget to reload it (default hotkey: Ctrl+R). 3. Drag+drop the `Connector.lua` file included with your client onto the main EmuHawk window. - - Look in the Archipelago folder for `/SNI/lua/x64` or `/SNI/lua/x86` depending on if the - emulator is 64-bit or 32-bit. Please note the most recent versions of BizHawk are 64-bit only. + - Look in the Archipelago folder for `/SNI/lua/Connector.lua`. - You could instead open the Lua Console manually, click `Script` 〉 `Open Script`, and navigate to `Connector.lua` with the file picker. diff --git a/worlds/lufia2ac/docs/setup_en.md b/worlds/lufia2ac/docs/setup_en.md index f9e0d3725c..4236c26e8a 100644 --- a/worlds/lufia2ac/docs/setup_en.md +++ b/worlds/lufia2ac/docs/setup_en.md @@ -82,8 +82,7 @@ first time launching, you may be prompted to allow it to communicate through the 3. Click on **New Lua Script Window...** 4. In the new window, click **Browse...** 5. Select the connector lua file included with your client - - Look in the Archipelago folder for `/SNI/lua/x64` or `/SNI/lua/x86` depending on if the - emulator is 64-bit or 32-bit. + - Look in the Archipelago folder for `/SNI/lua/Connector.lua`. 6. If you see an error while loading the script that states `socket.dll missing` or similar, navigate to the folder of the lua you are using in your file explorer and copy the `socket.dll` to the base folder of your snes9x install. @@ -94,9 +93,8 @@ the lua you are using in your file explorer and copy the `socket.dll` to the bas - (≥ 2.9) `Config` 〉 `Preferred Cores` 〉 `SNES` 〉 `BSNESv115+` 2. Load your ROM file if it hasn't already been loaded. If you changed your core preference after loading the ROM, don't forget to reload it (default hotkey: Ctrl+R). -3. Drag+drop the `Connector.lua` file that you downloaded above onto the main EmuHawk window. - - Look in the Archipelago folder for `/SNI/lua/x64` or `/SNI/lua/x86` depending on if the - emulator is 64-bit or 32-bit. Please note the most recent versions of BizHawk are 64-bit only. +3. Drag+drop the `Connector.lua` file included with your client onto the main EmuHawk window. + - Look in the Archipelago folder for `/SNI/lua/Connector.lua`. - You could instead open the Lua Console manually, click `Script` 〉 `Open Script`, and navigate to `Connector.lua` with the file picker. diff --git a/worlds/sm/docs/multiworld_en.md b/worlds/sm/docs/multiworld_en.md index 20c055bc91..ce91e7a7e4 100644 --- a/worlds/sm/docs/multiworld_en.md +++ b/worlds/sm/docs/multiworld_en.md @@ -87,8 +87,7 @@ first time launching, you may be prompted to allow it to communicate through the 3. Click on **New Lua Script Window...** 4. In the new window, click **Browse...** 5. Select the connector lua file included with your client - - Look in the Archipelago folder for `/SNI/lua/x64` or `/SNI/lua/x86` depending on if the - emulator is 64-bit or 32-bit. + - Look in the Archipelago folder for `/SNI/lua/Connector.lua`. 6. If you see an error while loading the script that states `socket.dll missing` or similar, navigate to the folder of the lua you are using in your file explorer and copy the `socket.dll` to the base folder of your snes9x install. @@ -100,8 +99,7 @@ the lua you are using in your file explorer and copy the `socket.dll` to the bas 2. Load your ROM file if it hasn't already been loaded. If you changed your core preference after loading the ROM, don't forget to reload it (default hotkey: Ctrl+R). 3. Drag+drop the `Connector.lua` file included with your client onto the main EmuHawk window. - - Look in the Archipelago folder for `/SNI/lua/x64` or `/SNI/lua/x86` depending on if the - emulator is 64-bit or 32-bit. Please note the most recent versions of BizHawk are 64-bit only. + - Look in the Archipelago folder for `/SNI/lua/Connector.lua`. - You could instead open the Lua Console manually, click `Script` 〉 `Open Script`, and navigate to `Connector.lua` with the file picker. diff --git a/worlds/smw/docs/setup_en.md b/worlds/smw/docs/setup_en.md index 2a9435c95d..9ca8bdf58a 100644 --- a/worlds/smw/docs/setup_en.md +++ b/worlds/smw/docs/setup_en.md @@ -77,8 +77,7 @@ first time launching, you may be prompted to allow it to communicate through the 3. Click on **New Lua Script Window...** 4. In the new window, click **Browse...** 5. Select the connector lua file included with your client - - Look in the Archipelago folder for `/SNI/lua/x64` or `/SNI/lua/x86` depending on if the - emulator is 64-bit or 32-bit. + - Look in the Archipelago folder for `/SNI/lua/Connector.lua`. 6. If you see an error while loading the script that states `socket.dll missing` or similar, navigate to the folder of the lua you are using in your file explorer and copy the `socket.dll` to the base folder of your snes9x install. @@ -90,8 +89,7 @@ the lua you are using in your file explorer and copy the `socket.dll` to the bas 2. Load your ROM file if it hasn't already been loaded. If you changed your core preference after loading the ROM, don't forget to reload it (default hotkey: Ctrl+R). 3. Drag+drop the `Connector.lua` file included with your client onto the main EmuHawk window. - - Look in the Archipelago folder for `/SNI/lua/x64` or `/SNI/lua/x86` depending on if the - emulator is 64-bit or 32-bit. Please note the most recent versions of BizHawk are 64-bit only. + - Look in the Archipelago folder for `/SNI/lua/Connector.lua`. - You could instead open the Lua Console manually, click `Script` 〉 `Open Script`, and navigate to `Connector.lua` with the file picker. diff --git a/worlds/smz3/docs/multiworld_en.md b/worlds/smz3/docs/multiworld_en.md index 27c8a507e3..da6e29ab69 100644 --- a/worlds/smz3/docs/multiworld_en.md +++ b/worlds/smz3/docs/multiworld_en.md @@ -85,8 +85,7 @@ first time launching, you may be prompted to allow it to communicate through the 3. Click on **New Lua Script Window...** 4. In the new window, click **Browse...** 5. Select the connector lua file included with your client - - Look in the Archipelago folder for `/SNI/lua/x64` or `/SNI/lua/x86` depending on if the - emulator is 64-bit or 32-bit. + - Look in the Archipelago folder for `/SNI/lua/Connector.lua`. 6. If you see an error while loading the script that states `socket.dll missing` or similar, navigate to the folder of the lua you are using in your file explorer and copy the `socket.dll` to the base folder of your snes9x install. @@ -98,8 +97,7 @@ the lua you are using in your file explorer and copy the `socket.dll` to the bas 2. Load your ROM file if it hasn't already been loaded. If you changed your core preference after loading the ROM, don't forget to reload it (default hotkey: Ctrl+R). 3. Drag+drop the `Connector.lua` file included with your client onto the main EmuHawk window. - - Look in the Archipelago folder for `/SNI/lua/x64` or `/SNI/lua/x86` depending on if the - emulator is 64-bit or 32-bit. Please note the most recent versions of BizHawk are 64-bit only. + - Look in the Archipelago folder for `/SNI/lua/Connector.lua`. - You could instead open the Lua Console manually, click `Script` 〉 `Open Script`, and navigate to `Connector.lua` with the file picker. From 2b69820619846e76fbb272c7cdf2b15ccb3e8f40 Mon Sep 17 00:00:00 2001 From: Shiny <36184001+ShinyNT@users.noreply.github.com> Date: Sat, 23 Sep 2023 01:45:46 -0300 Subject: [PATCH 056/144] =?UTF-8?q?Pok=C3=A9mon=20Red=20and=20Blue:=20Addi?= =?UTF-8?q?ng=20Spanish=20Setup=20Guide=20(#2205)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * added setup_es.md setup_en 100% translated (with a bit of adaptation to spanish linguistics) * Update __init__.py add reference to the spanish tutorial * Update setup_es.md removed temporary "wip translation" header * Update setup_es.md formatting cleanup * Update setup_es.md translated "alias for" on lines 73 and 74, which I just forgot to --- worlds/pokemon_rb/__init__.py | 17 ++++- worlds/pokemon_rb/docs/setup_es.md | 110 +++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 worlds/pokemon_rb/docs/setup_es.md diff --git a/worlds/pokemon_rb/__init__.py b/worlds/pokemon_rb/__init__.py index 2c70f28416..11aa737e0f 100644 --- a/worlds/pokemon_rb/__init__.py +++ b/worlds/pokemon_rb/__init__.py @@ -49,14 +49,25 @@ class PokemonSettings(settings.Group): class PokemonWebWorld(WebWorld): - tutorials = [Tutorial( + setup_en = Tutorial( "Multiworld Setup Guide", "A guide to playing Pokemon Red and Blue with Archipelago.", "English", "setup_en.md", "setup/en", ["Alchav"] - )] + ) + + setup_es = Tutorial( + setup_en.tutorial_name, + setup_en.description, + "Español", + "setup_es.md", + "setup/es", + ["Shiny"] + ) + + tutorials = [setup_en, setup_es] class PokemonRedBlueWorld(World): @@ -739,4 +750,4 @@ class PokemonRBItem(Item): name, item_data.classification, item_data.id, player - ) \ No newline at end of file + ) diff --git a/worlds/pokemon_rb/docs/setup_es.md b/worlds/pokemon_rb/docs/setup_es.md new file mode 100644 index 0000000000..4073103775 --- /dev/null +++ b/worlds/pokemon_rb/docs/setup_es.md @@ -0,0 +1,110 @@ +# Guía de instalación para Pokémon Red and Blue: Archipelago + +## Importante + +Al usar BizHawk, esta guía solo es aplicable en los sistemas de Windows y Linux. + +## Software Requerido + +- BizHawk: [BizHawk Releases en TASVideos](https://tasvideos.org/BizHawk/ReleaseHistory) + - La versión 2.3.1 y posteriores son soportadas. Se recomienda la versión 2.7 para estabilidad. + - Instrucciones de instalación detalladas para BizHawk se pueden encontrar en el enlace de arriba. + - Los usuarios de Windows deben ejecutar el instalador de prerrequisitos (prereq installer) primero, que también se + encuentra en el enlace de arriba. +- El cliente incorporado de Archipelago, que se puede encontrar [aquí](https://github.com/ArchipelagoMW/Archipelago/releases) + (selecciona `Pokemon Client` durante la instalación). +- Los ROMs originales de Pokémon Red y/o Blue. La comunidad de Archipelago no puede proveerlos. + +## Software Opcional + +- [Tracker de mapa para Pokémon Red and Blue Archipelago](https://github.com/j-imbo/pkmnrb_jim/releases/latest), para usar con [PopTracker](https://github.com/black-sliver/PopTracker/releases) + + +## Configurando BizHawk + +Una vez que Bizhawk se haya instalado, abre Emuhawk y cambia las siguientes configuraciones: + +- (≤ 2.8) Abrir EmuHawk e ir a Config > Customize. Abrir la pestaña Advanced, y en la opción de Lua Core cambiar desde + "NLua+KopiLua" a "Lua+LuaInterface". Luego reinicia EmuHawk. Esto es fundamental para que el script de Lua funcione + correctamente. + **NOTA: Incluso si "Lua+LuaInterface" ya estaba seleccionado, cambia entre las opciones y vuelvelo a seleccionar. + **Algunas instalaciones de versiones nuevas de EmuHawk tienen una tendencia a mostrar "Lua+LuaInterface" por defecto + **pero siguen cargando "NLua+KopiLua" hasta completar este paso.** +- Aun en la pestaña Advanced, asegurate que la casilla de AutoSaveRAM este marcada, y selecciona también la casilla 5s. + Esto reduce la posibilidad de que se pierdan datos guardados en el caso de que el emulador deje de funcionar (crash). +- En Config > Customize, pestaña General, marcar la casilla "Run in background". Esto evitará que te desconectes del + cliente mientras EmuHawk se esta ejecutando en segundo plano. + +Es muy recomendado asociar los archivos GB (\*.gb) al emulador EmuHawk que se acaba de instalar. +Para hacerlo, simplemente busca uno de los ROMs de gameboy, presiona con el click derecho sobre el y selecciona +"Abrir con...", despliega la lista que aparece y selecciona la opción al final de la lista "Buscar otra aplicación en" +"el equipo", luego navega a la carpeta de Bizhawk y selecciona EmuHawk.exe. + +## Configura tu archivo YAML + +### Que es un archivo YAML y por qué necesito uno? + +Tu archivo YAML contiene un número de opciones que proveen al generador con información sobre como debe generar tu +juego. Cada jugador de un multiworld entregara su propio archivo YAML. Esto permite que cada jugador disfrute de una +experiencia personalizada a su manera, y que diferentes jugadores dentro del mismo multiworld pueden tener diferentes +opciones. + +### Donde puedo obtener un archivo YAML? + +Puedes generar un archivo YAML or descargar su plantilla en la [pagina de configuración de jugador de Pokemon Red and Blue](/games/Pokemon%20Red%20and%20Blue/player-settings) + +Es importante tener en cuenta que la opción `game_version` determina el ROM que será parcheado. +Tanto el jugador como la persona que genera (si está generando localmente) necesitarán el archivo del ROM +correspondiente. + +Para las opciones `trainer_name` y `rival_name`, los siguientes caracteres normales son permitidos: + +* `‘’“”·… ABCDEFGHIJKLMNOPQRSTUVWXYZ():;[]abcdefghijklmnopqrstuvwxyzé'-?!.♂$×/,♀0123456789` + +Y los siguientes caracteres especiales (cada uno ocupa un caracter): +* `<'d>` +* `<'l>` +* `<'t>` +* `<'v>` +* `<'r>` +* `<'m>` +* `` +* `` +* `` alias para `♂` +* `` alias para `♀` + +## Unirse a un juego MultiWorld + +### Obtener tu parche de Pokémon + +Cuando te unes a un juego multiworld, se te pedirá que entregues tu archivo YAML a quien lo este organizando. +Una vez que la generación acabe, el anfitrión te dará un enlace a tu archivo, o un .zip con los archivos de +todos. Tu archivo tiene una extensión `.apred` o `.apblue`. + +Haz doble click en tu archivo `.apred` o `.apblue` para que se ejecute el cliente y realize el parcheado de la ROM. +Una vez acabe ese proceso (esto puede tardar un poco), el cliente y el emulador se abrirán automaticamente (si es que se +ha asociado la extensión al emulador tal como fue recomendado) + +### Conectarse al multiserver + +Una vez ejecutado tanto el cliente como el emulador, hay que conectarlos. Abre la carpeta de instalación de Archipelago, +luego abre `data/lua`, y simplemente arrastra el archivo `connector_pkmn_rb.lua` a la ventana principal de Emuhawk. +(Alternativamente, puedes abrir la consola de Lua manualmente. En Emuhawk ir a Tools > Lua Console, luego ir al menú +`Script` 〉 `Open Script`, navegar a la ubicación de `connector_pkmn_rb.lua` y seleccionarlo.) + +Para conectar el cliente con el servidor, simplemente pon `:` en la caja de texto superior y presiona +enter (si el servidor tiene contraseña, en la caja de texto inferior escribir `/connect : [contraseña]`) + +Ahora ya estás listo para tu aventura en Kanto. + +## Auto-Tracking + +Pokémon Red and Blue tiene un mapa completamente funcional que soporta seguimiento automático. + +1. Descarga el [Tracker de mapa para Pokémon Red and Blue Archipelago](https://github.com/j-imbo/pkmnrb_jim/releases/latest) y [PopTracker](https://github.com/black-sliver/PopTracker/releases). +2. Abre PopTracker, y carga el pack de Pokémon Red and Blue. +3. Haz click en el símbolo "AP" en la parte superior. +4. Ingresa la dirección de AP, nombre del slot y contraseña (si es que hay). + +Y ya, el resto debería hacerse solo! Los items y checks seran marcados automaticamente, e incluso reconocerá tus +configuraciones - Ocultará checks y ajustará la logica segun corresponda. From 124113f3d3034f73b793992c22692d747faf0291 Mon Sep 17 00:00:00 2001 From: Felix R <50271878+FelicitusNeko@users.noreply.github.com> Date: Sat, 23 Sep 2023 01:54:21 -0300 Subject: [PATCH 057/144] bumpstik: update docs (#2198) --- worlds/bumpstik/docs/setup_en.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/worlds/bumpstik/docs/setup_en.md b/worlds/bumpstik/docs/setup_en.md index 51334aa277..e64a6e9f29 100644 --- a/worlds/bumpstik/docs/setup_en.md +++ b/worlds/bumpstik/docs/setup_en.md @@ -1,21 +1,19 @@ ## Required Software -Download the game from the [Bumper Stickers GitHub releases page](https://github.com/FelicitusNeko/FlixelBumpStik/releases). - -*A web version will be made available on itch.io at a later time.* +Download the game from the [Bumper Stickers GitHub releases page](https://github.com/FelicitusNeko/FlixelBumpStik/releases), or from the [Bumper Stickers AP Itch page](https://kewliomzx.itch.io/bumpstik-ap), where you can also play it in your browser. ## Installation Procedures Simply download the latest version of Bumper Stickers from the link above, and extract it wherever you like. -- ⚠️ Do not extract Bumper Stickers to Program Files, as this will cause file access issues. +- ⚠️ It is not recommended to copy this game, or any files, directly into your Program Files folder under Windows. ## Joining a Multiworld Game -1. Run `BumpStik-AP.exe`. +1. Run `BumpStikAP.exe`. 2. Select "Archipelago Mode". 3. Enter your server details in the fields provided, and click "Start". - - ※ If you are connecting to a WSS server (such as archipelago.gg), specify `wss://` in the host name. Otherwise, the game will assume `ws://`. + - The game will attempt to automatically detect whether to connect via normal (WS) or secure (WSS) server, but you can specify `ws://` or `wss://` to prioritise one or the other. ## How to play Bumper Stickers (Classic) From b41a1e69b4cac009c9935ce635a90c047a3a86d0 Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Sun, 24 Sep 2023 01:48:20 +0200 Subject: [PATCH 058/144] The Witness: Fix Itemlinks --- worlds/witness/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/worlds/witness/__init__.py b/worlds/witness/__init__.py index f05202751f..faaafd598b 100644 --- a/worlds/witness/__init__.py +++ b/worlds/witness/__init__.py @@ -248,6 +248,9 @@ class WitnessWorld(World): return WitnessItem(item_name, item_data.classification, item_data.ap_code, player=self.player) + def get_filler_item_name(self) -> str: + return "Speed Boost" + class WitnessLocation(Location): """ From 5af47425b061349a47c215c3e639c11adcd1ce73 Mon Sep 17 00:00:00 2001 From: Trevor L <80716066+TRPG0@users.noreply.github.com> Date: Sun, 24 Sep 2023 00:08:40 -0600 Subject: [PATCH 059/144] Hylics 2: Add more missing locations (#2219) Adds two missing locations in Sage Labyrinth and their items --- worlds/hylics2/Items.py | 2 +- worlds/hylics2/Locations.py | 4 ++++ worlds/hylics2/__init__.py | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/worlds/hylics2/Items.py b/worlds/hylics2/Items.py index e09144c6cc..4556a4a674 100644 --- a/worlds/hylics2/Items.py +++ b/worlds/hylics2/Items.py @@ -98,7 +98,7 @@ item_table: Dict[int, ItemDict] = { 'count': 4, 'name': 'MULTI-JUICE'}, 200651: {'classification': ItemClassification.filler, - 'count': 1, + 'count': 3, 'name': 'MULTI STEM CELL'}, 200652: {'classification': ItemClassification.filler, 'count': 6, diff --git a/worlds/hylics2/Locations.py b/worlds/hylics2/Locations.py index 80e02b1c71..053bfbd585 100644 --- a/worlds/hylics2/Locations.py +++ b/worlds/hylics2/Locations.py @@ -220,6 +220,10 @@ location_table: Dict[int, LocationDict] = { 'region': 15}, 200754: {'name': "Sage Labyrinth: 2F Sarcophagus", 'region': 15}, + 200786: {'name': "Sage Labyrinth: Boss Secret Chest 1", + 'region': 15}, + 200787: {'name': "Sage Labyrinth: Boss Secret Chest 2", + 'region': 15}, 200725: {'name': "Sage Labyrinth: Motor Hunter Sarcophagus", 'region': 15}, 200726: {'name': "Sage Labyrinth: Sage Item 1", diff --git a/worlds/hylics2/__init__.py b/worlds/hylics2/__init__.py index 11401b1137..f721fb4749 100644 --- a/worlds/hylics2/__init__.py +++ b/worlds/hylics2/__init__.py @@ -36,7 +36,7 @@ class Hylics2World(World): topology_present: bool = True - data_version = 2 + data_version = 3 start_location = "Waynehouse" From 93c18cd9a7d9646d59616d4e4148c22b0ccabc0f Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Sun, 24 Sep 2023 10:30:33 +0200 Subject: [PATCH 060/144] Core/GUI: Better fileselection error (#2216) * Core: better error if GUI is unavailable * Core: enable open_directory kdialog and zenity The native dialog helpers were disabled because there was odd behavior. This is now fixed and was tested with latest zenity and kdialog. * Core: fix open_filename suggestion for zenity --- Utils.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/Utils.py b/Utils.py index 1556a764f2..9ceba48299 100644 --- a/Utils.py +++ b/Utils.py @@ -576,7 +576,7 @@ def open_filename(title: str, filetypes: typing.Sequence[typing.Tuple[str, typin zenity = which("zenity") if zenity: z_filters = (f'--file-filter={text} ({", ".join(ext)}) | *{" *".join(ext)}' for (text, ext) in filetypes) - selection = (f'--filename="{suggest}',) if suggest else () + selection = (f"--filename={suggest}",) if suggest else () return run(zenity, f"--title={title}", "--file-selection", *z_filters, *selection) # fall back to tk @@ -588,7 +588,10 @@ def open_filename(title: str, filetypes: typing.Sequence[typing.Tuple[str, typin f'This attempt was made because open_filename was used for "{title}".') raise e else: - root = tkinter.Tk() + try: + root = tkinter.Tk() + except tkinter.TclError: + return None # GUI not available. None is the same as a user clicking "cancel" root.withdraw() return tkinter.filedialog.askopenfilename(title=title, filetypes=((t[0], ' '.join(t[1])) for t in filetypes), initialfile=suggest or None) @@ -601,13 +604,14 @@ def open_directory(title: str, suggest: str = "") -> typing.Optional[str]: if is_linux: # prefer native dialog from shutil import which - kdialog = None#which("kdialog") + kdialog = which("kdialog") if kdialog: - return run(kdialog, f"--title={title}", "--getexistingdirectory", suggest or ".") - zenity = None#which("zenity") + return run(kdialog, f"--title={title}", "--getexistingdirectory", + os.path.abspath(suggest) if suggest else ".") + zenity = which("zenity") if zenity: z_filters = ("--directory",) - selection = (f'--filename="{suggest}',) if suggest else () + selection = (f"--filename={os.path.abspath(suggest)}/",) if suggest else () return run(zenity, f"--title={title}", "--file-selection", *z_filters, *selection) # fall back to tk @@ -619,7 +623,10 @@ def open_directory(title: str, suggest: str = "") -> typing.Optional[str]: f'This attempt was made because open_filename was used for "{title}".') raise e else: - root = tkinter.Tk() + try: + root = tkinter.Tk() + except tkinter.TclError: + return None # GUI not available. None is the same as a user clicking "cancel" root.withdraw() return tkinter.filedialog.askdirectory(title=title, mustexist=True, initialdir=suggest or None) From 4141a50d8c2adb4bd16eb6cf2fa4dd4cc8ecfb88 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Mon, 25 Sep 2023 20:27:02 +0200 Subject: [PATCH 061/144] Terraria: remove unused data --- worlds/terraria/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/worlds/terraria/__init__.py b/worlds/terraria/__init__.py index a8c823bcb8..306a65ef91 100644 --- a/worlds/terraria/__init__.py +++ b/worlds/terraria/__init__.py @@ -339,6 +339,5 @@ class TerrariaWorld(World): return { "goal": list(self.goal_locations), "achievements": self.multiworld.achievements[self.player].value, - "fill_extra_checks_with": self.multiworld.fill_extra_checks_with[self.player].value, "deathlink": bool(self.multiworld.death_link[self.player]), } From 98d61b32af6ebe53c6c44e7d7f8365753e5cdf67 Mon Sep 17 00:00:00 2001 From: Daivuk Date: Mon, 25 Sep 2023 18:18:20 -0400 Subject: [PATCH 062/144] DOOM 1993: Logic fixes --- worlds/doom_1993/Locations.py | 17 ++++++++------- worlds/doom_1993/Regions.py | 41 +++++++++++++++++++++++++++++------ worlds/doom_1993/Rules.py | 13 +++++------ 3 files changed, 49 insertions(+), 22 deletions(-) diff --git a/worlds/doom_1993/Locations.py b/worlds/doom_1993/Locations.py index 942c7d2a42..778efb4661 100644 --- a/worlds/doom_1993/Locations.py +++ b/worlds/doom_1993/Locations.py @@ -1794,13 +1794,13 @@ location_table: Dict[int, LocationDict] = { 'map': 7, 'index': 65, 'doom_type': 2004, - 'region': "Limbo (E3M7) Red"}, + 'region': "Limbo (E3M7) Green"}, 351297: {'name': 'Limbo (E3M7) - Armor', 'episode': 3, 'map': 7, 'index': 67, 'doom_type': 2018, - 'region': "Limbo (E3M7) Red"}, + 'region': "Limbo (E3M7) Green"}, 351298: {'name': 'Limbo (E3M7) - Yellow skull key', 'episode': 3, 'map': 7, @@ -2496,19 +2496,19 @@ location_table: Dict[int, LocationDict] = { 'map': 6, 'index': 77, 'doom_type': 38, - 'region': "Against Thee Wickedly (E4M6) Yellow"}, + 'region': "Against Thee Wickedly (E4M6) Magenta"}, 351414: {'name': 'Against Thee Wickedly (E4M6) - Invulnerability', 'episode': 4, 'map': 6, 'index': 78, 'doom_type': 2022, - 'region': "Against Thee Wickedly (E4M6) Red"}, + 'region': "Against Thee Wickedly (E4M6) Pink"}, 351415: {'name': 'Against Thee Wickedly (E4M6) - Invulnerability 2', 'episode': 4, 'map': 6, 'index': 89, 'doom_type': 2022, - 'region': "Against Thee Wickedly (E4M6) Red"}, + 'region': "Against Thee Wickedly (E4M6) Magenta"}, 351416: {'name': 'Against Thee Wickedly (E4M6) - BFG9000', 'episode': 4, 'map': 6, @@ -2520,7 +2520,7 @@ location_table: Dict[int, LocationDict] = { 'map': 6, 'index': 102, 'doom_type': 8, - 'region': "Against Thee Wickedly (E4M6) Red"}, + 'region': "Against Thee Wickedly (E4M6) Pink"}, 351418: {'name': 'Against Thee Wickedly (E4M6) - Berserk', 'episode': 4, 'map': 6, @@ -2550,7 +2550,7 @@ location_table: Dict[int, LocationDict] = { 'map': 6, 'index': -1, 'doom_type': -1, - 'region': "Against Thee Wickedly (E4M6) Red"}, + 'region': "Against Thee Wickedly (E4M6) Magenta"}, 351423: {'name': 'And Hell Followed (E4M7) - Shotgun', 'episode': 4, 'map': 7, @@ -2628,7 +2628,7 @@ location_table: Dict[int, LocationDict] = { 'map': 7, 'index': 182, 'doom_type': 39, - 'region': "And Hell Followed (E4M7) Main"}, + 'region': "And Hell Followed (E4M7) Blue"}, 351436: {'name': 'And Hell Followed (E4M7) - Red skull key', 'episode': 4, 'map': 7, @@ -3414,6 +3414,7 @@ death_logic_locations = [ "Command Control (E1M4) - Supercharge", "Command Control (E1M4) - Mega Armor", "Containment Area (E2M2) - Supercharge", + "Containment Area (E2M2) - Plasma gun", "Pandemonium (E3M3) - Mega Armor", "House of Pain (E3M4) - Chaingun", "House of Pain (E3M4) - Invulnerability", diff --git a/worlds/doom_1993/Regions.py b/worlds/doom_1993/Regions.py index 58626e62ae..602c29f5bd 100644 --- a/worlds/doom_1993/Regions.py +++ b/worlds/doom_1993/Regions.py @@ -394,7 +394,8 @@ regions:List[RegionDict] = [ "episode":3, "connections":[ "Limbo (E3M7) Red", - "Limbo (E3M7) Blue"]}, + "Limbo (E3M7) Blue", + "Limbo (E3M7) Pink"]}, {"name":"Limbo (E3M7) Blue", "connects_to_hub":False, "episode":3, @@ -404,11 +405,24 @@ regions:List[RegionDict] = [ "episode":3, "connections":[ "Limbo (E3M7) Main", - "Limbo (E3M7) Yellow"]}, + "Limbo (E3M7) Yellow", + "Limbo (E3M7) Green"]}, {"name":"Limbo (E3M7) Yellow", "connects_to_hub":False, "episode":3, "connections":["Limbo (E3M7) Red"]}, + {"name":"Limbo (E3M7) Pink", + "connects_to_hub":False, + "episode":3, + "connections":[ + "Limbo (E3M7) Green", + "Limbo (E3M7) Main"]}, + {"name":"Limbo (E3M7) Green", + "connects_to_hub":False, + "episode":3, + "connections":[ + "Limbo (E3M7) Pink", + "Limbo (E3M7) Red"]}, # Dis (E3M8) {"name":"Dis (E3M8) Main", @@ -529,19 +543,32 @@ regions:List[RegionDict] = [ {"name":"Against Thee Wickedly (E4M6) Main", "connects_to_hub":True, "episode":4, - "connections":[ - "Against Thee Wickedly (E4M6) Blue", - "Against Thee Wickedly (E4M6) Yellow", - "Against Thee Wickedly (E4M6) Red"]}, + "connections":["Against Thee Wickedly (E4M6) Blue"]}, {"name":"Against Thee Wickedly (E4M6) Red", "connects_to_hub":False, "episode":4, - "connections":["Against Thee Wickedly (E4M6) Main"]}, + "connections":[ + "Against Thee Wickedly (E4M6) Blue", + "Against Thee Wickedly (E4M6) Pink", + "Against Thee Wickedly (E4M6) Main"]}, {"name":"Against Thee Wickedly (E4M6) Blue", + "connects_to_hub":False, + "episode":4, + "connections":[ + "Against Thee Wickedly (E4M6) Main", + "Against Thee Wickedly (E4M6) Yellow", + "Against Thee Wickedly (E4M6) Red"]}, + {"name":"Against Thee Wickedly (E4M6) Magenta", "connects_to_hub":False, "episode":4, "connections":["Against Thee Wickedly (E4M6) Main"]}, {"name":"Against Thee Wickedly (E4M6) Yellow", + "connects_to_hub":False, + "episode":4, + "connections":[ + "Against Thee Wickedly (E4M6) Blue", + "Against Thee Wickedly (E4M6) Magenta"]}, + {"name":"Against Thee Wickedly (E4M6) Pink", "connects_to_hub":False, "episode":4, "connections":["Against Thee Wickedly (E4M6) Main"]}, diff --git a/worlds/doom_1993/Rules.py b/worlds/doom_1993/Rules.py index 6f24112cbe..6e13a8af34 100644 --- a/worlds/doom_1993/Rules.py +++ b/worlds/doom_1993/Rules.py @@ -50,9 +50,6 @@ def set_episode1_rules(player, world): set_rule(world.get_entrance("Command Control (E1M4) Blue -> Command Control (E1M4) Main", player), lambda state: state.has("Command Control (E1M4) - Yellow keycard", player, 1) or state.has("Command Control (E1M4) - Blue keycard", player, 1)) - set_rule(world.get_entrance("Command Control (E1M4) Yellow -> Command Control (E1M4) Main", player), lambda state: - state.has("Command Control (E1M4) - Yellow keycard", player, 1) or - state.has("Command Control (E1M4) - Blue keycard", player, 1)) # Phobos Lab (E1M5) set_rule(world.get_entrance("Hub -> Phobos Lab (E1M5) Main", player), lambda state: @@ -354,8 +351,12 @@ def set_episode3_rules(player, world): state.has("Limbo (E3M7) - Red skull key", player, 1)) set_rule(world.get_entrance("Limbo (E3M7) Main -> Limbo (E3M7) Blue", player), lambda state: state.has("Limbo (E3M7) - Blue skull key", player, 1)) + set_rule(world.get_entrance("Limbo (E3M7) Main -> Limbo (E3M7) Pink", player), lambda state: + state.has("Limbo (E3M7) - Blue skull key", player, 1)) set_rule(world.get_entrance("Limbo (E3M7) Red -> Limbo (E3M7) Yellow", player), lambda state: state.has("Limbo (E3M7) - Yellow skull key", player, 1)) + set_rule(world.get_entrance("Limbo (E3M7) Pink -> Limbo (E3M7) Green", player), lambda state: + state.has("Limbo (E3M7) - Red skull key", player, 1)) # Dis (E3M8) set_rule(world.get_entrance("Hub -> Dis (E3M8) Main", player), lambda state: @@ -466,12 +467,10 @@ def set_episode4_rules(player, world): state.has("BFG9000", player, 1))) set_rule(world.get_entrance("Against Thee Wickedly (E4M6) Main -> Against Thee Wickedly (E4M6) Blue", player), lambda state: state.has("Against Thee Wickedly (E4M6) - Blue skull key", player, 1)) - set_rule(world.get_entrance("Against Thee Wickedly (E4M6) Main -> Against Thee Wickedly (E4M6) Yellow", player), lambda state: + set_rule(world.get_entrance("Against Thee Wickedly (E4M6) Blue -> Against Thee Wickedly (E4M6) Yellow", player), lambda state: state.has("Against Thee Wickedly (E4M6) - Yellow skull key", player, 1)) - set_rule(world.get_entrance("Against Thee Wickedly (E4M6) Main -> Against Thee Wickedly (E4M6) Red", player), lambda state: + set_rule(world.get_entrance("Against Thee Wickedly (E4M6) Blue -> Against Thee Wickedly (E4M6) Red", player), lambda state: state.has("Against Thee Wickedly (E4M6) - Red skull key", player, 1)) - set_rule(world.get_entrance("Against Thee Wickedly (E4M6) Blue -> Against Thee Wickedly (E4M6) Main", player), lambda state: - state.has("Against Thee Wickedly (E4M6) - Blue skull key", player, 1)) # And Hell Followed (E4M7) set_rule(world.get_entrance("Hub -> And Hell Followed (E4M7) Main", player), lambda state: From 974bab2b2490eb8f9854ff5c8668bb66c6d1f305 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 25 Sep 2023 22:15:00 -0400 Subject: [PATCH 063/144] [WebHost] Add search filter and collapse button to Supported Games page (#2215) * Add search filter and collapse button to Supported Games page * Autofocus search input, fix bug with arrow display when searching * Add "Expand All" and "Collapse All" buttons. Buttons respect visible games. --- WebHostLib/static/assets/supportedGames.js | 83 +++++++++++++++++++++ WebHostLib/static/styles/supportedGames.css | 20 +++++ WebHostLib/templates/supportedGames.html | 15 +++- 3 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 WebHostLib/static/assets/supportedGames.js diff --git a/WebHostLib/static/assets/supportedGames.js b/WebHostLib/static/assets/supportedGames.js new file mode 100644 index 0000000000..1acf0e0cc5 --- /dev/null +++ b/WebHostLib/static/assets/supportedGames.js @@ -0,0 +1,83 @@ +window.addEventListener('load', () => { + const gameHeaders = document.getElementsByClassName('collapse-toggle'); + Array.from(gameHeaders).forEach((header) => { + const gameName = header.getAttribute('data-game'); + header.addEventListener('click', () => { + const gameArrow = document.getElementById(`${gameName}-arrow`); + const gameInfo = document.getElementById(gameName); + if (gameInfo.classList.contains('collapsed')) { + gameArrow.innerText = '▼'; + gameInfo.classList.remove('collapsed'); + } else { + gameArrow.innerText = '▶'; + gameInfo.classList.add('collapsed'); + } + }); + }); + + // Handle game filter input + const gameSearch = document.getElementById('game-search'); + gameSearch.value = ''; + + gameSearch.addEventListener('input', (evt) => { + if (!evt.target.value.trim()) { + // If input is empty, display all collapsed games + return Array.from(gameHeaders).forEach((header) => { + header.style.display = null; + const gameName = header.getAttribute('data-game'); + document.getElementById(`${gameName}-arrow`).innerText = '▶'; + document.getElementById(gameName).classList.add('collapsed'); + }); + } + + // Loop over all the games + Array.from(gameHeaders).forEach((header) => { + const gameName = header.getAttribute('data-game'); + const gameArrow = document.getElementById(`${gameName}-arrow`); + const gameInfo = document.getElementById(gameName); + + // If the game name includes the search string, display the game. If not, hide it + if (gameName.toLowerCase().includes(evt.target.value.toLowerCase())) { + header.style.display = null; + gameArrow.innerText = '▼'; + gameInfo.classList.remove('collapsed'); + } else { + console.log(header); + header.style.display = 'none'; + gameArrow.innerText = '▶'; + gameInfo.classList.add('collapsed'); + } + }); + }); + + document.getElementById('expand-all').addEventListener('click', expandAll); + document.getElementById('collapse-all').addEventListener('click', collapseAll); +}); + +const expandAll = () => { + const gameHeaders = document.getElementsByClassName('collapse-toggle'); + // Loop over all the games + Array.from(gameHeaders).forEach((header) => { + const gameName = header.getAttribute('data-game'); + const gameArrow = document.getElementById(`${gameName}-arrow`); + const gameInfo = document.getElementById(gameName); + + if (header.style.display === 'none') { return; } + gameArrow.innerText = '▼'; + gameInfo.classList.remove('collapsed'); + }); +}; + +const collapseAll = () => { + const gameHeaders = document.getElementsByClassName('collapse-toggle'); + // Loop over all the games + Array.from(gameHeaders).forEach((header) => { + const gameName = header.getAttribute('data-game'); + const gameArrow = document.getElementById(`${gameName}-arrow`); + const gameInfo = document.getElementById(gameName); + + if (header.style.display === 'none') { return; } + gameArrow.innerText = '▶'; + gameInfo.classList.add('collapsed'); + }); +}; diff --git a/WebHostLib/static/styles/supportedGames.css b/WebHostLib/static/styles/supportedGames.css index f86ab581ca..1e9a98c17a 100644 --- a/WebHostLib/static/styles/supportedGames.css +++ b/WebHostLib/static/styles/supportedGames.css @@ -18,6 +18,16 @@ margin-bottom: 2px; } +#games h2 .collapse-arrow{ + font-size: 20px; + vertical-align: middle; + cursor: pointer; +} + +#games p.collapsed{ + display: none; +} + #games a{ font-size: 16px; } @@ -31,3 +41,13 @@ line-height: 25px; margin-bottom: 7px; } + +#games #page-controls{ + display: flex; + flex-direction: row; + margin-top: 0.25rem; +} + +#games #page-controls button{ + margin-left: 0.5rem; +} diff --git a/WebHostLib/templates/supportedGames.html b/WebHostLib/templates/supportedGames.html index 82f6348db2..63b70216d7 100644 --- a/WebHostLib/templates/supportedGames.html +++ b/WebHostLib/templates/supportedGames.html @@ -4,16 +4,27 @@ Supported Games + {% endblock %} {% block body %} {% include 'header/oceanHeader.html' %}

    Currently Supported Games

    +
    +
    +
    + + + +
    +
    {% for game_name in worlds | title_sorted %} {% set world = worlds[game_name] %} -

    {{ game_name }}

    -

    +

    +  {{ game_name }} +

    +

    hm4qqX-4ua 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ˀ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

    - - - - - - +{% endmacro -%} +{#- call the macro to build the table header -#} +{%- for name, internal_name, img_src in science_packs %} + {{ make_header(name, img_src) }} +{% endfor -%} {% endblock %} {% block custom_table_row scoped %} {% if games[player] == "Factorio" %} -{% set player_inventory = named_inventory[team][player] %} -{% set prog_science = player_inventory["progressive-science-pack"] %} - - - - - - + {%- set player_inventory = named_inventory[team][player] -%} + {%- set prog_science = player_inventory["progressive-science-pack"] -%} + {%- for name, internal_name, img_src in science_packs %} + + {% endfor -%} {% else %} - - - - - - + {%- for _ in science_packs %} + + {% endfor -%} {% endif %} {% endblock%} From fd93f6e7221a85cc51afa5051a5d690732733f30 Mon Sep 17 00:00:00 2001 From: Aaron Wagener Date: Sun, 8 Oct 2023 04:46:30 -0500 Subject: [PATCH 101/144] Tests: add can_reach_region method to WorldTestBase (#2274) --- test/TestBase.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/TestBase.py b/test/TestBase.py index 4df6b80769..b710194294 100644 --- a/test/TestBase.py +++ b/test/TestBase.py @@ -189,12 +189,16 @@ class WorldTestBase(unittest.TestCase): self.multiworld.state.remove(item) def can_reach_location(self, location: str) -> bool: - """Determines if the current state can reach the provide location name""" + """Determines if the current state can reach the provided location name""" return self.multiworld.state.can_reach(location, "Location", 1) def can_reach_entrance(self, entrance: str) -> bool: """Determines if the current state can reach the provided entrance name""" return self.multiworld.state.can_reach(entrance, "Entrance", 1) + + def can_reach_region(self, region: str) -> bool: + """Determines if the current state can reach the provided region name""" + return self.multiworld.state.can_reach(region, "Region", 1) def count(self, item_name: str) -> int: """Returns the amount of an item currently in state""" From 5eeaf834cb8ef42a1722068f4ce0eb5c2e90012f Mon Sep 17 00:00:00 2001 From: Aaron Wagener Date: Sun, 8 Oct 2023 05:08:47 -0500 Subject: [PATCH 102/144] Tests: Add a test for fill to WorldTestBase (#2049) * Tests: Add a test for fill to WorldTestBase * test items and minimal accessibility, only bailing out when no reachable locations exist. * put egg shard max/goal at sane values 114 locations - 35 always-present progression items - 25 excluded locations from settings <= 74 egg shards past me can't do arithmetic * f * i'm bad at git * make fill import local to prevent circular imports --------- Co-authored-by: espeon65536 --- test/TestBase.py | 36 +++++++++++++++++++++++++++++++++++- worlds/minecraft/Options.py | 4 ++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/test/TestBase.py b/test/TestBase.py index b710194294..61088e581c 100644 --- a/test/TestBase.py +++ b/test/TestBase.py @@ -6,7 +6,7 @@ from test.general import gen_steps from worlds import AutoWorld from worlds.AutoWorld import call_all -from BaseClasses import MultiWorld, CollectionState, ItemClassification, Item +from BaseClasses import Location, MultiWorld, CollectionState, ItemClassification, Item from worlds.alttp.Items import ItemFactory @@ -275,3 +275,37 @@ class WorldTestBase(unittest.TestCase): locations = self.multiworld.get_reachable_locations(state, 1) self.assertGreater(len(locations), 0, "Need to be able to reach at least one location to get started.") + + def testFill(self): + """Generates a multiworld and validates placements with the defined options""" + # don't run this test if accessibility is set manually + if not (self.run_default_tests and self.constructed): + return + from Fill import distribute_items_restrictive + + # basically a shortened reimplementation of this method from core, in order to force the check is done + def fulfills_accessibility(): + locations = self.multiworld.get_locations(1).copy() + state = CollectionState(self.multiworld) + while locations: + sphere: typing.List[Location] = [] + for n in range(len(locations) - 1, -1, -1): + if locations[n].can_reach(state): + sphere.append(locations.pop(n)) + self.assertTrue(sphere or self.multiworld.accessibility[1] == "minimal", + f"Unreachable locations: {locations}") + if not sphere: + break + for location in sphere: + if location.item: + state.collect(location.item, True, location) + + return self.multiworld.has_beaten_game(state, 1) + + with self.subTest("Game", game=self.game): + distribute_items_restrictive(self.multiworld) + call_all(self.multiworld, "post_fill") + self.assertTrue(fulfills_accessibility(), "Collected all locations, but can't beat the game.") + placed_items = [loc.item for loc in self.multiworld.get_locations() if loc.item and loc.item.code] + self.assertLessEqual(len(self.multiworld.itempool), len(placed_items), + "Unplaced Items remaining in itempool") diff --git a/worlds/minecraft/Options.py b/worlds/minecraft/Options.py index 084a611e44..cdb5bf303f 100644 --- a/worlds/minecraft/Options.py +++ b/worlds/minecraft/Options.py @@ -14,7 +14,7 @@ class EggShardsRequired(Range): """Number of dragon egg shards to collect to spawn bosses.""" display_name = "Egg Shards Required" range_start = 0 - range_end = 74 + range_end = 50 default = 0 @@ -22,7 +22,7 @@ class EggShardsAvailable(Range): """Number of dragon egg shards available to collect.""" display_name = "Egg Shards Available" range_start = 0 - range_end = 74 + range_end = 50 default = 0 From cc2247bfa012fe8c8278c13f18c0429888937e77 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 8 Oct 2023 13:26:14 +0200 Subject: [PATCH 103/144] CommonClient: fix json prints not being logged in UI mode (#2253) --- CommonClient.py | 14 ++++++++++---- Utils.py | 16 +++++++++++++--- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/CommonClient.py b/CommonClient.py index 61fad65897..154b61b1d5 100644 --- a/CommonClient.py +++ b/CommonClient.py @@ -1,4 +1,6 @@ from __future__ import annotations + +import copy import logging import asyncio import urllib.parse @@ -242,6 +244,7 @@ class CommonContext: self.watcher_event = asyncio.Event() self.jsontotextparser = JSONtoTextParser(self) + self.rawjsontotextparser = RawJSONtoTextParser(self) self.update_data_package(network_data_package) # execution @@ -377,10 +380,13 @@ class CommonContext: def on_print_json(self, args: dict): if self.ui: - self.ui.print_json(args["data"]) - else: - text = self.jsontotextparser(args["data"]) - logger.info(text) + # send copy to UI + self.ui.print_json(copy.deepcopy(args["data"])) + + logging.getLogger("FileLog").info(self.rawjsontotextparser(copy.deepcopy(args["data"])), + extra={"NoStream": True}) + logging.getLogger("StreamLog").info(self.jsontotextparser(copy.deepcopy(args["data"])), + extra={"NoFile": True}) def on_package(self, cmd: str, args: dict): """For custom package handling in subclasses.""" diff --git a/Utils.py b/Utils.py index 91584f55d6..d48b8ea9cc 100644 --- a/Utils.py +++ b/Utils.py @@ -459,11 +459,21 @@ def init_logging(name: str, loglevel: typing.Union[str, int] = logging.INFO, wri write_mode, encoding="utf-8-sig") file_handler.setFormatter(logging.Formatter(log_format)) + + class Filter(logging.Filter): + def __init__(self, filter_name, condition): + super().__init__(filter_name) + self.condition = condition + + def filter(self, record: logging.LogRecord) -> bool: + return self.condition(record) + + file_handler.addFilter(Filter("NoStream", lambda record: not getattr(record, "NoFile", False))) root_logger.addHandler(file_handler) if sys.stdout: - root_logger.addHandler( - logging.StreamHandler(sys.stdout) - ) + stream_handler = logging.StreamHandler(sys.stdout) + stream_handler.addFilter(Filter("NoFile", lambda record: not getattr(record, "NoStream", False))) + root_logger.addHandler(stream_handler) # Relay unhandled exceptions to logger. if not getattr(sys.excepthook, "_wrapped", False): # skip if already modified From 6f9484f3750ce05113044baab8fe0f53fc28cc59 Mon Sep 17 00:00:00 2001 From: Aaron Wagener Date: Sun, 8 Oct 2023 07:33:39 -0500 Subject: [PATCH 104/144] The Messenger: Make modules and tests PEP8 (#2276) * The Messenger: PEP8 module and test names * fix dumb * whitespace --- worlds/messenger/__init__.py | 30 ++++++++----------- .../messenger/{Constants.py => constants.py} | 2 +- worlds/messenger/{Options.py => options.py} | 0 worlds/messenger/{Regions.py => regions.py} | 0 worlds/messenger/{Rules.py => rules.py} | 8 ++--- worlds/messenger/{Shop.py => shop.py} | 0 .../{SubClasses.py => subclasses.py} | 8 ++--- .../test/{TestAccess.py => test_access.py} | 24 +++++++-------- .../{TestLocations.py => test_locations.py} | 6 ++-- .../test/{TestLogic.py => test_logic.py} | 12 ++++---- .../test/{TestNotes.py => test_notes.py} | 10 +++---- .../test/{TestShop.py => test_shop.py} | 24 +++++++-------- .../{TestShopChest.py => test_shop_chest.py} | 12 ++++---- 13 files changed, 65 insertions(+), 71 deletions(-) rename worlds/messenger/{Constants.py => constants.py} (98%) rename worlds/messenger/{Options.py => options.py} (100%) rename worlds/messenger/{Regions.py => regions.py} (100%) rename worlds/messenger/{Rules.py => rules.py} (99%) rename worlds/messenger/{Shop.py => shop.py} (100%) rename worlds/messenger/{SubClasses.py => subclasses.py} (95%) rename worlds/messenger/test/{TestAccess.py => test_access.py} (95%) rename worlds/messenger/test/{TestLocations.py => test_locations.py} (81%) rename worlds/messenger/test/{TestLogic.py => test_logic.py} (96%) rename worlds/messenger/test/{TestNotes.py => test_notes.py} (80%) rename worlds/messenger/test/{TestShop.py => test_shop.py} (89%) rename worlds/messenger/test/{TestShopChest.py => test_shop_chest.py} (94%) diff --git a/worlds/messenger/__init__.py b/worlds/messenger/__init__.py index b37f23749d..c7fd253632 100644 --- a/worlds/messenger/__init__.py +++ b/worlds/messenger/__init__.py @@ -1,14 +1,14 @@ import logging -from typing import Dict, Any, List, Optional +from typing import Any, Dict, List, Optional -from BaseClasses import Tutorial, ItemClassification, CollectionState, Item, MultiWorld -from worlds.AutoWorld import World, WebWorld -from .Constants import NOTES, PHOBEKINS, ALL_ITEMS, ALWAYS_LOCATIONS, BOSS_LOCATIONS, FILLER -from .Options import messenger_options, NotesNeeded, Goal, PowerSeals, Logic -from .Regions import REGIONS, REGION_CONNECTIONS, SEALS, MEGA_SHARDS -from .Shop import SHOP_ITEMS, shuffle_shop_prices, FIGURINES -from .SubClasses import MessengerRegion, MessengerItem -from . import Rules +from BaseClasses import CollectionState, Item, ItemClassification, Tutorial +from worlds.AutoWorld import WebWorld, World +from .constants import ALL_ITEMS, ALWAYS_LOCATIONS, BOSS_LOCATIONS, FILLER, NOTES, PHOBEKINS +from .options import Goal, Logic, NotesNeeded, PowerSeals, messenger_options +from .regions import MEGA_SHARDS, REGIONS, REGION_CONNECTIONS, SEALS +from .rules import MessengerHardRules, MessengerOOBRules, MessengerRules +from .shop import FIGURINES, SHOP_ITEMS, shuffle_shop_prices +from .subclasses import MessengerItem, MessengerRegion class MessengerWeb(WebWorld): @@ -68,15 +68,11 @@ class MessengerWorld(World): total_seals: int = 0 required_seals: int = 0 - total_shards: int + total_shards: int = 0 shop_prices: Dict[str, int] figurine_prices: Dict[str, int] _filler_items: List[str] - def __init__(self, multiworld: MultiWorld, player: int): - super().__init__(multiworld, player) - self.total_shards = 0 - def generate_early(self) -> None: if self.multiworld.goal[self.player] == Goal.option_power_seal_hunt: self.multiworld.shuffle_seals[self.player].value = PowerSeals.option_true @@ -144,11 +140,11 @@ class MessengerWorld(World): def set_rules(self) -> None: logic = self.multiworld.logic_level[self.player] if logic == Logic.option_normal: - Rules.MessengerRules(self).set_messenger_rules() + MessengerRules(self).set_messenger_rules() elif logic == Logic.option_hard: - Rules.MessengerHardRules(self).set_messenger_rules() + MessengerHardRules(self).set_messenger_rules() else: - Rules.MessengerOOBRules(self).set_messenger_rules() + MessengerOOBRules(self).set_messenger_rules() def fill_slot_data(self) -> Dict[str, Any]: shop_prices = {SHOP_ITEMS[item].internal_name: price for item, price in self.shop_prices.items()} diff --git a/worlds/messenger/Constants.py b/worlds/messenger/constants.py similarity index 98% rename from worlds/messenger/Constants.py rename to worlds/messenger/constants.py index 121584da05..e6608be043 100644 --- a/worlds/messenger/Constants.py +++ b/worlds/messenger/constants.py @@ -1,6 +1,6 @@ # items # listing individual groups first for easy lookup -from .Shop import SHOP_ITEMS, FIGURINES +from .shop import SHOP_ITEMS, FIGURINES NOTES = [ "Key of Hope", diff --git a/worlds/messenger/Options.py b/worlds/messenger/options.py similarity index 100% rename from worlds/messenger/Options.py rename to worlds/messenger/options.py diff --git a/worlds/messenger/Regions.py b/worlds/messenger/regions.py similarity index 100% rename from worlds/messenger/Regions.py rename to worlds/messenger/regions.py diff --git a/worlds/messenger/Rules.py b/worlds/messenger/rules.py similarity index 99% rename from worlds/messenger/Rules.py rename to worlds/messenger/rules.py index 664bf5f6d7..65a99627f2 100644 --- a/worlds/messenger/Rules.py +++ b/worlds/messenger/rules.py @@ -2,9 +2,9 @@ from typing import Dict, Callable, TYPE_CHECKING from BaseClasses import CollectionState, MultiWorld from worlds.generic.Rules import set_rule, allow_self_locking_items, add_rule -from .Options import MessengerAccessibility, Goal -from .Constants import NOTES, PHOBEKINS -from .SubClasses import MessengerShopLocation +from .options import MessengerAccessibility, Goal +from .constants import NOTES, PHOBEKINS +from .subclasses import MessengerShopLocation if TYPE_CHECKING: from . import MessengerWorld @@ -119,7 +119,7 @@ class MessengerRules: def can_dboost(self, state: CollectionState) -> bool: return state.has_any({"Path of Resilience", "Meditation"}, self.player) and \ state.has("Second Wind", self.player) - + def is_aerobatic(self, state: CollectionState) -> bool: return self.has_wingsuit(state) and state.has("Aerobatics Warrior", self.player) diff --git a/worlds/messenger/Shop.py b/worlds/messenger/shop.py similarity index 100% rename from worlds/messenger/Shop.py rename to worlds/messenger/shop.py diff --git a/worlds/messenger/SubClasses.py b/worlds/messenger/subclasses.py similarity index 95% rename from worlds/messenger/SubClasses.py rename to worlds/messenger/subclasses.py index 717b389878..e6a18b2e4e 100644 --- a/worlds/messenger/SubClasses.py +++ b/worlds/messenger/subclasses.py @@ -2,10 +2,10 @@ from functools import cached_property from typing import Optional, TYPE_CHECKING, cast from BaseClasses import CollectionState, Item, ItemClassification, Location, Region -from .Constants import NOTES, PHOBEKINS, PROG_ITEMS, USEFUL_ITEMS -from .Options import Goal -from .Regions import MEGA_SHARDS, REGIONS, SEALS -from .Shop import FIGURINES, PROG_SHOP_ITEMS, SHOP_ITEMS, USEFUL_SHOP_ITEMS +from .constants import NOTES, PHOBEKINS, PROG_ITEMS, USEFUL_ITEMS +from .options import Goal +from .regions import MEGA_SHARDS, REGIONS, SEALS +from .shop import FIGURINES, PROG_SHOP_ITEMS, SHOP_ITEMS, USEFUL_SHOP_ITEMS if TYPE_CHECKING: from . import MessengerWorld diff --git a/worlds/messenger/test/TestAccess.py b/worlds/messenger/test/test_access.py similarity index 95% rename from worlds/messenger/test/TestAccess.py rename to worlds/messenger/test/test_access.py index e95a81c5c1..7a77a9b066 100644 --- a/worlds/messenger/test/TestAccess.py +++ b/worlds/messenger/test/test_access.py @@ -1,5 +1,5 @@ from . import MessengerTestBase -from ..Constants import NOTES, PHOBEKINS +from ..constants import NOTES, PHOBEKINS class AccessTest(MessengerTestBase): @@ -7,7 +7,7 @@ class AccessTest(MessengerTestBase): "shuffle_shards": "true", } - def testTabi(self) -> None: + def test_tabi(self) -> None: """locations that hard require the Lightfoot Tabi""" locations = [ "Searing Crags - Pyro", "Underworld - Key of Chaos", "Underworld Seal - Sharp and Windy Climb", @@ -19,7 +19,7 @@ class AccessTest(MessengerTestBase): items = [["Lightfoot Tabi"]] self.assertAccessDependency(locations, items) - def testDart(self) -> None: + def test_dart(self) -> None: """locations that hard require the Rope Dart""" locations = [ "Ninja Village Seal - Tree House", "Autumn Hills - Key of Hope", "Howling Grotto Seal - Crushing Pits", @@ -31,7 +31,7 @@ class AccessTest(MessengerTestBase): items = [["Rope Dart"]] self.assertAccessDependency(locations, items) - def testWingsuit(self) -> None: + def test_wingsuit(self) -> None: """locations that hard require the Wingsuit""" locations = [ "Ninja Village - Candle", "Ninja Village Seal - Tree House", "Autumn Hills - Climbing Claws", @@ -57,7 +57,7 @@ class AccessTest(MessengerTestBase): items = [["Wingsuit"]] self.assertAccessDependency(locations, items) - def testVertical(self) -> None: + def test_vertical(self) -> None: """locations that require either the Rope Dart or the Wingsuit""" locations = [ "Ninja Village Seal - Tree House", "Howling Grotto Seal - Crushing Pits", @@ -92,7 +92,7 @@ class AccessTest(MessengerTestBase): items = [["Wingsuit", "Rope Dart"]] self.assertAccessDependency(locations, items) - def testAmulet(self) -> None: + def test_amulet(self) -> None: """Locations that require Ruxxtin's Amulet""" locations = [ "Cloud Ruins - Acro", "Cloud Ruins Seal - Ghost Pit", "Cloud Ruins Seal - Toothbrush Alley", @@ -103,7 +103,7 @@ class AccessTest(MessengerTestBase): items = [["Ruxxtin's Amulet"]] self.assertAccessDependency(locations, items) - def testFirefly(self) -> None: + def test_firefly(self) -> None: """Elemental Skylands and Corrupted Future require the Magic Firefly""" locations = [ "Elemental Skylands - Key of Symbiosis", "Elemental Skylands Seal - Air", "Elemental Skylands Seal - Fire", @@ -113,7 +113,7 @@ class AccessTest(MessengerTestBase): items = [["Magic Firefly"]] self.assertAccessDependency(locations, items) - def testCrests(self) -> None: + def test_crests(self) -> None: """Test Key of Love nonsense""" locations = ["Sunken Shrine - Key of Love"] items = [["Sun Crest", "Moon Crest"]] @@ -124,19 +124,19 @@ class AccessTest(MessengerTestBase): self.collect_by_name("Sun Crest") self.assertEqual(self.can_reach_location("Sunken Shrine - Key of Love"), False) - def testThistle(self) -> None: + def test_thistle(self) -> None: """I'm a chuckster!""" locations = ["Searing Crags - Key of Strength"] items = [["Power Thistle"]] self.assertAccessDependency(locations, items) - def testCrown(self) -> None: + def test_crown(self) -> None: """Crocomire but not""" locations = ["Corrupted Future - Key of Courage"] items = [["Demon King Crown"]] self.assertAccessDependency(locations, items) - def testGoal(self) -> None: + def test_goal(self) -> None: """Test some different states to verify goal requires the correct items""" self.collect_all_but([*NOTES, "Rescue Phantom"]) self.assertEqual(self.can_reach_location("Rescue Phantom"), False) @@ -153,7 +153,7 @@ class ItemsAccessTest(MessengerTestBase): "accessibility": "items", } - def testSelfLockingItems(self) -> None: + def test_self_locking_items(self) -> None: """Force items that can be self locked to ensure it's valid placement.""" location_lock_pairs = { "Searing Crags - Key of Strength": ["Power Thistle"], diff --git a/worlds/messenger/test/TestLocations.py b/worlds/messenger/test/test_locations.py similarity index 81% rename from worlds/messenger/test/TestLocations.py rename to worlds/messenger/test/test_locations.py index ccb358568c..0c330be4bd 100644 --- a/worlds/messenger/test/TestLocations.py +++ b/worlds/messenger/test/test_locations.py @@ -1,5 +1,5 @@ from . import MessengerTestBase -from ..SubClasses import MessengerLocation +from ..subclasses import MessengerLocation class LocationsTest(MessengerTestBase): @@ -10,7 +10,7 @@ class LocationsTest(MessengerTestBase): @property def run_default_tests(self) -> bool: return False - - def testLocationsExist(self): + + def test_locations_exist(self) -> None: for location in self.multiworld.worlds[1].location_name_to_id: self.assertIsInstance(self.multiworld.get_location(location, self.player), MessengerLocation) diff --git a/worlds/messenger/test/TestLogic.py b/worlds/messenger/test/test_logic.py similarity index 96% rename from worlds/messenger/test/TestLogic.py rename to worlds/messenger/test/test_logic.py index 932bc13867..53ea929922 100644 --- a/worlds/messenger/test/TestLogic.py +++ b/worlds/messenger/test/test_logic.py @@ -1,5 +1,3 @@ -from typing import Iterable, List - from BaseClasses import ItemClassification from . import MessengerTestBase @@ -10,7 +8,7 @@ class HardLogicTest(MessengerTestBase): "shuffle_shards": "true", } - def testVertical(self) -> None: + def test_vertical(self) -> None: """Test the locations that still require wingsuit or rope dart.""" locations = [ # tower of time @@ -54,7 +52,7 @@ class HardLogicTest(MessengerTestBase): items = [["Wingsuit", "Rope Dart"]] self.assertAccessDependency(locations, items) - def testWindmill(self) -> None: + def test_windmill(self) -> None: """Windmill Shuriken isn't progression on normal difficulty, so test it's marked correctly and required.""" self.assertEqual(ItemClassification.progression, self.get_item_by_name("Windmill Shuriken").classification) windmill_locs = [ @@ -81,8 +79,8 @@ class HardLogicTest(MessengerTestBase): item = self.get_item_by_name("Rope Dart") self.collect(item) self.assertTrue(self.can_reach_location(special_loc)) - - def testGlacial(self) -> None: + + def test_glacial(self) -> None: """Test Glacial Peak locations.""" self.assertAccessDependency(["Glacial Peak Seal - Ice Climbers"], [["Second Wind", "Meditation"], ["Rope Dart"], ["Wingsuit"]], @@ -100,7 +98,7 @@ class NoLogicTest(MessengerTestBase): "logic_level": "oob", } - def testAccess(self) -> None: + def test_access(self) -> None: """Test the locations with rules still require things.""" all_locations = [ "Bamboo Creek - Claustro", "Searing Crags - Key of Strength", "Elemental Skylands - Key of Symbiosis", diff --git a/worlds/messenger/test/TestNotes.py b/worlds/messenger/test/test_notes.py similarity index 80% rename from worlds/messenger/test/TestNotes.py rename to worlds/messenger/test/test_notes.py index c4292e4900..46cec5f3c8 100644 --- a/worlds/messenger/test/TestNotes.py +++ b/worlds/messenger/test/test_notes.py @@ -1,5 +1,5 @@ from . import MessengerTestBase -from ..Constants import NOTES +from ..constants import NOTES class TwoNoteGoalTest(MessengerTestBase): @@ -7,7 +7,7 @@ class TwoNoteGoalTest(MessengerTestBase): "notes_needed": 2, } - def testPrecollectedNotes(self) -> None: + def test_precollected_notes(self) -> None: self.assertEqual(self.multiworld.state.count_group("Notes", self.player), 4) @@ -16,15 +16,15 @@ class FourNoteGoalTest(MessengerTestBase): "notes_needed": 4, } - def testPrecollectedNotes(self) -> None: + def test_precollected_notes(self) -> None: self.assertEqual(self.multiworld.state.count_group("Notes", self.player), 2) class DefaultGoalTest(MessengerTestBase): - def testPrecollectedNotes(self) -> None: + def test_precollected_notes(self) -> None: self.assertEqual(self.multiworld.state.count_group("Notes", self.player), 0) - def testGoal(self) -> None: + def test_goal(self) -> None: self.assertBeatable(False) self.collect_by_name(NOTES) rope_dart = self.get_item_by_name("Rope Dart") diff --git a/worlds/messenger/test/TestShop.py b/worlds/messenger/test/test_shop.py similarity index 89% rename from worlds/messenger/test/TestShop.py rename to worlds/messenger/test/test_shop.py index 89ea93624d..bfd3b417a8 100644 --- a/worlds/messenger/test/TestShop.py +++ b/worlds/messenger/test/test_shop.py @@ -1,7 +1,7 @@ from typing import Dict from . import MessengerTestBase -from ..Shop import SHOP_ITEMS, FIGURINES +from ..shop import SHOP_ITEMS, FIGURINES class ShopCostTest(MessengerTestBase): @@ -10,13 +10,13 @@ class ShopCostTest(MessengerTestBase): "shuffle_shards": "true", } - def testShopRules(self) -> None: + def test_shop_rules(self) -> None: for loc in SHOP_ITEMS: loc = f"The Shop - {loc}" with self.subTest("has cost", loc=loc): self.assertFalse(self.can_reach_location(loc)) - def testShopPrices(self) -> None: + def test_shop_prices(self) -> None: prices: Dict[str, int] = self.multiworld.worlds[self.player].shop_prices for loc, price in prices.items(): with self.subTest("prices", loc=loc): @@ -24,7 +24,7 @@ class ShopCostTest(MessengerTestBase): self.assertTrue(loc in SHOP_ITEMS) self.assertEqual(len(prices), len(SHOP_ITEMS)) - def testDBoost(self) -> None: + def test_dboost(self) -> None: locations = [ "Riviere Turquoise Seal - Bounces and Balls", "Forlorn Temple - Demon King", "Forlorn Temple Seal - Rocket Maze", "Forlorn Temple Seal - Rocket Sunset", @@ -33,10 +33,10 @@ class ShopCostTest(MessengerTestBase): items = [["Path of Resilience", "Meditation", "Second Wind"]] self.assertAccessDependency(locations, items) - def testCurrents(self) -> None: + def test_currents(self) -> None: self.assertAccessDependency(["Elemental Skylands Seal - Water"], [["Currents Master"]]) - def testStrike(self) -> None: + def test_strike(self) -> None: locations = [ "Glacial Peak Seal - Projectile Spike Pit", "Elemental Skylands Seal - Fire", ] @@ -50,22 +50,22 @@ class ShopCostMinTest(ShopCostTest): "shuffle_seals": "false", } - def testShopRules(self) -> None: + def test_shop_rules(self) -> None: if self.multiworld.worlds[self.player].total_shards: - super().testShopRules() + super().test_shop_rules() else: for loc in SHOP_ITEMS: loc = f"The Shop - {loc}" with self.subTest("has cost", loc=loc): self.assertTrue(self.can_reach_location(loc)) - def testDBoost(self) -> None: + def test_dboost(self) -> None: pass - def testCurrents(self) -> None: + def test_currents(self) -> None: pass - def testStrike(self) -> None: + def test_strike(self) -> None: pass @@ -79,7 +79,7 @@ class PlandoTest(MessengerTestBase): }, } - def testCosts(self) -> None: + def test_costs(self) -> None: for loc in SHOP_ITEMS: loc = f"The Shop - {loc}" with self.subTest("has cost", loc=loc): diff --git a/worlds/messenger/test/TestShopChest.py b/worlds/messenger/test/test_shop_chest.py similarity index 94% rename from worlds/messenger/test/TestShopChest.py rename to worlds/messenger/test/test_shop_chest.py index ad4178fbd7..058a200447 100644 --- a/worlds/messenger/test/TestShopChest.py +++ b/worlds/messenger/test/test_shop_chest.py @@ -8,11 +8,11 @@ class AllSealsRequired(MessengerTestBase): "goal": "power_seal_hunt", } - def testSealsShuffled(self) -> None: + def test_seals_shuffled(self) -> None: """Shuffle seals should be forced on when shop chest is the goal so test it.""" self.assertTrue(self.multiworld.shuffle_seals[self.player]) - def testChestAccess(self) -> None: + def test_chest_access(self) -> None: """Defaults to a total of 45 power seals in the pool and required.""" with self.subTest("Access Dependency"): self.assertEqual(len([seal for seal in self.multiworld.itempool if seal.name == "Power Seal"]), @@ -38,7 +38,7 @@ class HalfSealsRequired(MessengerTestBase): "percent_seals_required": 50, } - def testSealsAmount(self) -> None: + def test_seals_amount(self) -> None: """Should have 45 power seals in the item pool and half that required""" self.assertEqual(self.multiworld.total_seals[self.player], 45) self.assertEqual(self.multiworld.worlds[self.player].total_seals, 45) @@ -57,7 +57,7 @@ class ThirtyThirtySeals(MessengerTestBase): "percent_seals_required": 34, } - def testSealsAmount(self) -> None: + def test_seals_amount(self) -> None: """Should have 30 power seals in the pool and 33 percent of that required.""" self.assertEqual(self.multiworld.total_seals[self.player], 30) self.assertEqual(self.multiworld.worlds[self.player].total_seals, 30) @@ -75,7 +75,7 @@ class MaxSealsNoShards(MessengerTestBase): "total_seals": 85, } - def testSealsAmount(self) -> None: + def test_seals_amount(self) -> None: """Should set total seals to 70 since shards aren't shuffled.""" self.assertEqual(self.multiworld.total_seals[self.player], 85) self.assertEqual(self.multiworld.worlds[self.player].total_seals, 70) @@ -88,7 +88,7 @@ class MaxSealsWithShards(MessengerTestBase): "shuffle_shards": "true", } - def testSealsAmount(self) -> None: + def test_seals_amount(self) -> None: """Should have 85 seals in the pool with all required and be a valid seed.""" self.assertEqual(self.multiworld.total_seals[self.player], 85) self.assertEqual(self.multiworld.worlds[self.player].total_seals, 85) From 0d8a868ed972d610fbbbad7fd014a80352951e00 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 8 Oct 2023 22:14:28 +0200 Subject: [PATCH 105/144] Utils: support messagebox on windows without dependencies (#2224) --- Utils.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Utils.py b/Utils.py index d48b8ea9cc..5fb037a173 100644 --- a/Utils.py +++ b/Utils.py @@ -680,6 +680,11 @@ def messagebox(title: str, text: str, error: bool = False) -> None: if zenity: return run(zenity, f"--title={title}", f"--text={text}", "--error" if error else "--info") + elif is_windows: + import ctypes + style = 0x10 if error else 0x0 + return ctypes.windll.user32.MessageBoxW(0, text, title, style) + # fall back to tk try: import tkinter From a7b4914bb73546d46662b39f11ab2c30f715cabd Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Mon, 9 Oct 2023 10:18:41 +0200 Subject: [PATCH 106/144] WebHost: update flask (#2250) * WebHost: update flask * WebHost: update flask-caching --------- Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> --- WebHostLib/__init__.py | 1 - WebHostLib/requirements.txt | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/WebHostLib/__init__.py b/WebHostLib/__init__.py index 441f3272fd..43ca89f0b3 100644 --- a/WebHostLib/__init__.py +++ b/WebHostLib/__init__.py @@ -50,7 +50,6 @@ app.config["PONY"] = { } app.config["MAX_ROLL"] = 20 app.config["CACHE_TYPE"] = "SimpleCache" -app.config["JSON_AS_ASCII"] = False app.config["HOST_ADDRESS"] = "" cache = Cache() diff --git a/WebHostLib/requirements.txt b/WebHostLib/requirements.txt index 55669f1018..654104252c 100644 --- a/WebHostLib/requirements.txt +++ b/WebHostLib/requirements.txt @@ -1,7 +1,7 @@ -flask>=2.2.3 +flask>=3.0.0 pony>=0.7.17 waitress>=2.1.2 -Flask-Caching>=2.0.2 +Flask-Caching>=2.1.0 Flask-Compress>=1.14 Flask-Limiter>=3.5.0 bokeh>=3.1.1; python_version <= '3.8' From 7193182294f854479c8c3815346a5db1c3ce56c6 Mon Sep 17 00:00:00 2001 From: Aaron Wagener Date: Tue, 10 Oct 2023 15:30:20 -0500 Subject: [PATCH 107/144] Core: move option results to the World class instead of MultiWorld (#993) :crossed_fingers: * map option objects to a `World.options` dict * convert RoR2 to options dict system for testing * add temp behavior for lttp with notes * copy/paste bad * convert `set_default_common_options` to a namespace property * reorganize test call order * have fill_restrictive use the new options system * update world api * update soe tests * fix world api * core: auto initialize a dataclass on the World class with the option results * core: auto initialize a dataclass on the World class with the option results: small tying improvement * add `as_dict` method to the options dataclass * fix namespace issues with tests * have current option updates use `.value` instead of changing the option * update ror2 to use the new options system again * revert the junk pool dict since it's cased differently * fix begin_with_loop typo * write new and old options to spoiler * change factorio option behavior back * fix comparisons * move common and per_game_common options to new system * core: automatically create missing options_dataclass from legacy option_definitions * remove spoiler special casing and add back the Factorio option changing but in new system * give ArchipIDLE the default options_dataclass so its options get generated and spoilered properly * reimplement `inspect.get_annotations` * move option info generation for webhost to new system * need to include Common and PerGame common since __annotations__ doesn't include super * use get_type_hints for the options dictionary * typing.get_type_hints returns the bases too. * forgot to sweep through generate * sweep through all the tests * swap to a metaclass property * move remaining usages from get_type_hints to metaclass property * move remaining usages from __annotations__ to metaclass property * move remaining usages from legacy dictionaries to metaclass property * remove legacy dictionaries * cache the metaclass property * clarify inheritance in world api * move the messenger to new options system * add an assert for my dumb * update the doc * rename o to options * missed a spot * update new messenger options * comment spacing Co-authored-by: Doug Hoskisson * fix tests * fix missing import * make the documentation definition more accurate * use options system for loc creation * type cast MessengerWorld * fix typo and use quotes for cast * LTTP: set random seed in tests * ArchipIdle: remove change here as it's default on AutoWorld * Stardew: Need to set state because `set_default_common_options` used to * The Messenger: update shop rando and helpers to new system; optimize imports * Add a kwarg to `as_dict` to do the casing for you * RoR2: use new kwarg for less code * RoR2: revert some accidental reverts * The Messenger: remove an unnecessary variable * remove TypeVar that isn't used * CommonOptions not abstract * Docs: fix mistake in options api.md Co-authored-by: Doug Hoskisson * create options for item link worlds * revert accidental doc removals * Item Links: set default options on group * change Zillion to new options dataclass * remove unused parameter to function * use TypeGuard for Literal narrowing * move dlc quest to new api * move overcooked 2 to new api * fixed some missed code in oc2 * - Tried to be compliant with 993 (WIP?) * - I think it all works now * - Removed last trace of me touching core * typo * It now passes all tests! * Improve options, fix all issues I hope * - Fixed init options * dlcquest: fix bad imports * missed a file * - Reduce code duplication * add as_dict documentation * - Use .items(), get option name more directly, fix slot data content * - Remove generic options from the slot data * improve slot data documentation * remove `CommonOptions.get_value` (#21) * better slot data description Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> --------- Co-authored-by: el-u <109771707+el-u@users.noreply.github.com> Co-authored-by: Doug Hoskisson Co-authored-by: Doug Hoskisson Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> Co-authored-by: Alex Gilbert --- BaseClasses.py | 40 +- Fill.py | 12 +- Generate.py | 18 +- Main.py | 22 +- Options.py | 88 ++- WebHostLib/options.py | 5 +- docs/options api.md | 33 +- docs/world api.md | 80 ++- test/TestBase.py | 4 +- test/general/TestFill.py | 17 +- test/general/TestHelpers.py | 2 +- test/general/TestOptions.py | 2 +- test/general/__init__.py | 6 +- worlds/AutoWorld.py | 32 +- worlds/alttp/test/__init__.py | 16 + worlds/alttp/test/dungeons/TestDungeon.py | 19 +- worlds/alttp/test/inverted/TestInverted.py | 16 +- .../test/inverted/TestInvertedBombRules.py | 16 +- .../TestInvertedMinor.py | 19 +- .../test/inverted_owg/TestInvertedOWG.py | 18 +- worlds/alttp/test/minor_glitches/TestMinor.py | 20 +- worlds/alttp/test/owg/TestVanillaOWG.py | 15 +- worlds/alttp/test/vanilla/TestVanilla.py | 16 +- worlds/dlcquest/Items.py | 32 +- worlds/dlcquest/Locations.py | 3 +- worlds/dlcquest/Options.py | 56 +- worlds/dlcquest/Regions.py | 49 +- worlds/dlcquest/Rules.py | 128 ++-- worlds/dlcquest/__init__.py | 42 +- worlds/messenger/__init__.py | 40 +- worlds/messenger/constants.py | 4 +- worlds/messenger/options.py | 38 +- worlds/messenger/regions.py | 2 +- worlds/messenger/rules.py | 26 +- worlds/messenger/shop.py | 4 +- worlds/messenger/subclasses.py | 18 +- worlds/oot/__init__.py | 4 +- worlds/overcooked2/Options.py | 49 +- worlds/overcooked2/__init__.py | 71 +-- worlds/ror2/Options.py | 75 +-- worlds/ror2/__init__.py | 112 ++-- worlds/sm/__init__.py | 2 +- worlds/stardew_valley/__init__.py | 94 +-- worlds/stardew_valley/bundles.py | 2 +- worlds/stardew_valley/items.py | 188 +++--- worlds/stardew_valley/locations.py | 160 ++--- worlds/stardew_valley/logic.py | 107 ++-- worlds/stardew_valley/mods/logic/deepwoods.py | 4 +- worlds/stardew_valley/mods/logic/magic.py | 16 +- worlds/stardew_valley/mods/logic/skills.py | 14 +- .../mods/logic/skullcavernelevator.py | 4 +- worlds/stardew_valley/options.py | 113 ++-- worlds/stardew_valley/regions.py | 33 +- worlds/stardew_valley/rules.py | 589 +++++++++--------- .../test/TestLogicSimplification.py | 3 +- worlds/stardew_valley/test/TestOptions.py | 109 ++-- worlds/stardew_valley/test/TestRegions.py | 17 +- worlds/stardew_valley/test/__init__.py | 83 +-- .../stardew_valley/test/checks/goal_checks.py | 6 +- .../test/checks/option_checks.py | 36 +- .../stardew_valley/test/long/TestModsLong.py | 63 ++ .../test/long/TestOptionsLong.py | 1 - .../test/long/TestRandomWorlds.py | 3 +- .../stardew_valley/test/long/option_names.py | 9 +- worlds/stardew_valley/test/mods/TestMods.py | 104 ++-- worlds/zillion/__init__.py | 14 +- worlds/zillion/options.py | 136 ++-- worlds/zillion/requirements.txt | 1 + worlds/zillion/test/TestOptions.py | 10 +- 69 files changed, 1587 insertions(+), 1603 deletions(-) create mode 100644 worlds/stardew_valley/test/long/TestModsLong.py diff --git a/BaseClasses.py b/BaseClasses.py index 45190ac7b9..1b6677dd19 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -226,25 +226,24 @@ class MultiWorld(): range(1, self.players + 1)} def set_options(self, args: Namespace) -> None: - for option_key in Options.common_options: - setattr(self, option_key, getattr(args, option_key, {})) - for option_key in Options.per_game_common_options: - setattr(self, option_key, getattr(args, option_key, {})) - for player in self.player_ids: self.custom_data[player] = {} world_type = AutoWorld.AutoWorldRegister.world_types[self.game[player]] - for option_key in world_type.option_definitions: - setattr(self, option_key, getattr(args, option_key, {})) - self.worlds[player] = world_type(self, player) self.worlds[player].random = self.per_slot_randoms[player] + for option_key in world_type.options_dataclass.type_hints: + option_values = getattr(args, option_key, {}) + setattr(self, option_key, option_values) + # TODO - remove this loop once all worlds use options dataclasses + options_dataclass: typing.Type[Options.PerGameCommonOptions] = self.worlds[player].options_dataclass + self.worlds[player].options = options_dataclass(**{option_key: getattr(args, option_key)[player] + for option_key in options_dataclass.type_hints}) def set_item_links(self): item_links = {} replacement_prio = [False, True, None] for player in self.player_ids: - for item_link in self.item_links[player].value: + for item_link in self.worlds[player].options.item_links.value: if item_link["name"] in item_links: if item_links[item_link["name"]]["game"] != self.game[player]: raise Exception(f"Cannot ItemLink across games. Link: {item_link['name']}") @@ -299,14 +298,6 @@ class MultiWorld(): group["non_local_items"] = item_link["non_local_items"] group["link_replacement"] = replacement_prio[item_link["link_replacement"]] - # intended for unittests - def set_default_common_options(self): - for option_key, option in Options.common_options.items(): - setattr(self, option_key, {player_id: option(option.default) for player_id in self.player_ids}) - for option_key, option in Options.per_game_common_options.items(): - setattr(self, option_key, {player_id: option(option.default) for player_id in self.player_ids}) - self.state = CollectionState(self) - def secure(self): self.random = ThreadBarrierProxy(secrets.SystemRandom()) self.is_race = True @@ -863,19 +854,19 @@ class Region: """ Adds locations to the Region object, where location_type is your Location class and locations is a dict of location names to address. - + :param locations: dictionary of locations to be created and added to this Region `{name: ID}` :param location_type: Location class to be used to create the locations with""" if location_type is None: location_type = Location for location, address in locations.items(): self.locations.append(location_type(self.player, location, address, self)) - + def connect(self, connecting_region: Region, name: Optional[str] = None, rule: Optional[Callable[[CollectionState], bool]] = None) -> None: """ Connects this Region to another Region, placing the provided rule on the connection. - + :param connecting_region: Region object to connect to path is `self -> exiting_region` :param name: name of the connection being created :param rule: callable to determine access of this connection to go from self to the exiting_region""" @@ -883,11 +874,11 @@ class Region: if rule: exit_.access_rule = rule exit_.connect(connecting_region) - + def create_exit(self, name: str) -> Entrance: """ Creates and returns an Entrance object as an exit of this region. - + :param name: name of the Entrance being created """ exit_ = self.entrance_type(self.player, name, self) @@ -1257,7 +1248,7 @@ class Spoiler: def to_file(self, filename: str) -> None: def write_option(option_key: str, option_obj: Options.AssembleOptions) -> None: - res = getattr(self.multiworld, option_key)[player] + res = getattr(self.multiworld.worlds[player].options, option_key) display_name = getattr(option_obj, "display_name", option_key) outfile.write(f"{display_name + ':':33}{res.current_option_name}\n") @@ -1275,8 +1266,7 @@ class Spoiler: outfile.write('\nPlayer %d: %s\n' % (player, self.multiworld.get_player_name(player))) outfile.write('Game: %s\n' % self.multiworld.game[player]) - options = ChainMap(Options.per_game_common_options, self.multiworld.worlds[player].option_definitions) - for f_option, option in options.items(): + for f_option, option in self.multiworld.worlds[player].options_dataclass.type_hints.items(): write_option(f_option, option) AutoWorld.call_single(self.multiworld, "write_spoiler_header", player, outfile) diff --git a/Fill.py b/Fill.py index 7c81aed7ba..600d18ef2a 100644 --- a/Fill.py +++ b/Fill.py @@ -5,6 +5,8 @@ import typing from collections import Counter, deque from BaseClasses import CollectionState, Item, Location, LocationProgressType, MultiWorld +from Options import Accessibility + from worlds.AutoWorld import call_all from worlds.generic.Rules import add_item_rule @@ -70,7 +72,7 @@ def fill_restrictive(world: MultiWorld, base_state: CollectionState, locations: spot_to_fill: typing.Optional[Location] = None # if minimal accessibility, only check whether location is reachable if game not beatable - if world.accessibility[item_to_place.player] == 'minimal': + if world.worlds[item_to_place.player].options.accessibility == Accessibility.option_minimal: perform_access_check = not world.has_beaten_game(maximum_exploration_state, item_to_place.player) \ if single_player_placement else not has_beaten_game @@ -265,7 +267,7 @@ def fast_fill(world: MultiWorld, def accessibility_corrections(world: MultiWorld, state: CollectionState, locations, pool=[]): maximum_exploration_state = sweep_from_pool(state, pool) - minimal_players = {player for player in world.player_ids if world.accessibility[player] == "minimal"} + minimal_players = {player for player in world.player_ids if world.worlds[player].options.accessibility == "minimal"} unreachable_locations = [location for location in world.get_locations() if location.player in minimal_players and not location.can_reach(maximum_exploration_state)] for location in unreachable_locations: @@ -288,7 +290,7 @@ def inaccessible_location_rules(world: MultiWorld, state: CollectionState, locat unreachable_locations = [location for location in locations if not location.can_reach(maximum_exploration_state)] if unreachable_locations: def forbid_important_item_rule(item: Item): - return not ((item.classification & 0b0011) and world.accessibility[item.player] != 'minimal') + return not ((item.classification & 0b0011) and world.worlds[item.player].options.accessibility != 'minimal') for location in unreachable_locations: add_item_rule(location, forbid_important_item_rule) @@ -531,9 +533,9 @@ def balance_multiworld_progression(world: MultiWorld) -> None: # If other players are below the threshold value, swap progression in this sphere into earlier spheres, # which gives more locations available by this sphere. balanceable_players: typing.Dict[int, float] = { - player: world.progression_balancing[player] / 100 + player: world.worlds[player].options.progression_balancing / 100 for player in world.player_ids - if world.progression_balancing[player] > 0 + if world.worlds[player].options.progression_balancing > 0 } if not balanceable_players: logging.info('Skipping multiworld progression balancing.') diff --git a/Generate.py b/Generate.py index 5d44a1db45..08fe2b9083 100644 --- a/Generate.py +++ b/Generate.py @@ -157,7 +157,8 @@ def main(args=None, callback=ERmain): for yaml in weights_cache[path]: if category_name is None: for category in yaml: - if category in AutoWorldRegister.world_types and key in Options.common_options: + if category in AutoWorldRegister.world_types and \ + key in Options.CommonOptions.type_hints: yaml[category][key] = option elif category_name not in yaml: logging.warning(f"Meta: Category {category_name} is not present in {path}.") @@ -340,7 +341,7 @@ def roll_meta_option(option_key, game: str, category_dict: Dict) -> Any: return get_choice(option_key, category_dict) if game in AutoWorldRegister.world_types: game_world = AutoWorldRegister.world_types[game] - options = ChainMap(game_world.option_definitions, Options.per_game_common_options) + options = game_world.options_dataclass.type_hints if option_key in options: if options[option_key].supports_weighting: return get_choice(option_key, category_dict) @@ -445,8 +446,8 @@ def roll_settings(weights: dict, plando_options: PlandoOptions = PlandoOptions.b f"which is not enabled.") ret = argparse.Namespace() - for option_key in Options.per_game_common_options: - if option_key in weights and option_key not in Options.common_options: + for option_key in Options.PerGameCommonOptions.type_hints: + if option_key in weights and option_key not in Options.CommonOptions.type_hints: raise Exception(f"Option {option_key} has to be in a game's section, not on its own.") ret.game = get_choice("game", weights) @@ -466,16 +467,11 @@ def roll_settings(weights: dict, plando_options: PlandoOptions = PlandoOptions.b game_weights = weights[ret.game] ret.name = get_choice('name', weights) - for option_key, option in Options.common_options.items(): + for option_key, option in Options.CommonOptions.type_hints.items(): setattr(ret, option_key, option.from_any(get_choice(option_key, weights, option.default))) - for option_key, option in world_type.option_definitions.items(): + for option_key, option in world_type.options_dataclass.type_hints.items(): handle_option(ret, game_weights, option_key, option, plando_options) - for option_key, option in Options.per_game_common_options.items(): - # skip setting this option if already set from common_options, defaulting to root option - if option_key not in world_type.option_definitions and \ - (option_key not in Options.common_options or option_key in game_weights): - handle_option(ret, game_weights, option_key, option, plando_options) if PlandoOptions.items in plando_options: ret.plando_items = game_weights.get("plando_items", []) if ret.game == "Minecraft" or ret.game == "Ocarina of Time": diff --git a/Main.py b/Main.py index 48b37764a9..dfc4ed1793 100644 --- a/Main.py +++ b/Main.py @@ -108,7 +108,7 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No logger.info('') for player in world.player_ids: - for item_name, count in world.start_inventory[player].value.items(): + for item_name, count in world.worlds[player].options.start_inventory.value.items(): for _ in range(count): world.push_precollected(world.create_item(item_name, player)) @@ -130,15 +130,15 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No for player in world.player_ids: # items can't be both local and non-local, prefer local - world.non_local_items[player].value -= world.local_items[player].value - world.non_local_items[player].value -= set(world.local_early_items[player]) + world.worlds[player].options.non_local_items.value -= world.worlds[player].options.local_items.value + world.worlds[player].options.non_local_items.value -= set(world.local_early_items[player]) AutoWorld.call_all(world, "set_rules") for player in world.player_ids: - exclusion_rules(world, player, world.exclude_locations[player].value) - world.priority_locations[player].value -= world.exclude_locations[player].value - for location_name in world.priority_locations[player].value: + exclusion_rules(world, player, world.worlds[player].options.exclude_locations.value) + world.worlds[player].options.priority_locations.value -= world.worlds[player].options.exclude_locations.value + for location_name in world.worlds[player].options.priority_locations.value: try: location = world.get_location(location_name, player) except KeyError as e: # failed to find the given location. Check if it's a legitimate location @@ -151,8 +151,8 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No if world.players > 1: locality_rules(world) else: - world.non_local_items[1].value = set() - world.local_items[1].value = set() + world.worlds[1].options.non_local_items.value = set() + world.worlds[1].options.local_items.value = set() AutoWorld.call_all(world, "generate_basic") @@ -360,11 +360,11 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No f" {location}" locations_data[location.player][location.address] = \ location.item.code, location.item.player, location.item.flags - if location.name in world.start_location_hints[location.player]: + if location.name in world.worlds[location.player].options.start_location_hints: precollect_hint(location) - elif location.item.name in world.start_hints[location.item.player]: + elif location.item.name in world.worlds[location.item.player].options.start_hints: precollect_hint(location) - elif any([location.item.name in world.start_hints[player] + elif any([location.item.name in world.worlds[player].options.start_hints for player in world.groups.get(location.item.player, {}).get("players", [])]): precollect_hint(location) diff --git a/Options.py b/Options.py index 960e6c19d1..d9ddfc2e2f 100644 --- a/Options.py +++ b/Options.py @@ -2,6 +2,9 @@ from __future__ import annotations import abc import logging +from copy import deepcopy +from dataclasses import dataclass +import functools import math import numbers import random @@ -211,6 +214,12 @@ class NumericOption(Option[int], numbers.Integral, abc.ABC): else: return self.value > other + def __ge__(self, other: typing.Union[int, NumericOption]) -> bool: + if isinstance(other, NumericOption): + return self.value >= other.value + else: + return self.value >= other + def __bool__(self) -> bool: return bool(self.value) @@ -896,10 +905,55 @@ class ProgressionBalancing(SpecialRange): } -common_options = { - "progression_balancing": ProgressionBalancing, - "accessibility": Accessibility -} +class OptionsMetaProperty(type): + def __new__(mcs, + name: str, + bases: typing.Tuple[type, ...], + attrs: typing.Dict[str, typing.Any]) -> "OptionsMetaProperty": + for attr_type in attrs.values(): + assert not isinstance(attr_type, AssembleOptions),\ + f"Options for {name} should be type hinted on the class, not assigned" + return super().__new__(mcs, name, bases, attrs) + + @property + @functools.lru_cache(maxsize=None) + def type_hints(cls) -> typing.Dict[str, typing.Type[Option[typing.Any]]]: + """Returns type hints of the class as a dictionary.""" + return typing.get_type_hints(cls) + + +@dataclass +class CommonOptions(metaclass=OptionsMetaProperty): + progression_balancing: ProgressionBalancing + accessibility: Accessibility + + def as_dict(self, *option_names: str, casing: str = "snake") -> typing.Dict[str, typing.Any]: + """ + Returns a dictionary of [str, Option.value] + + :param option_names: names of the options to return + :param casing: case of the keys to return. Supports `snake`, `camel`, `pascal`, `kebab` + """ + option_results = {} + for option_name in option_names: + if option_name in type(self).type_hints: + if casing == "snake": + display_name = option_name + elif casing == "camel": + split_name = [name.title() for name in option_name.split("_")] + split_name[0] = split_name[0].lower() + display_name = "".join(split_name) + elif casing == "pascal": + display_name = "".join([name.title() for name in option_name.split("_")]) + elif casing == "kebab": + display_name = option_name.replace("_", "-") + else: + raise ValueError(f"{casing} is invalid casing for as_dict. " + "Valid names are 'snake', 'camel', 'pascal', 'kebab'.") + option_results[display_name] = getattr(self, option_name).value + else: + raise ValueError(f"{option_name} not found in {tuple(type(self).type_hints)}") + return option_results class LocalItems(ItemSet): @@ -1020,17 +1074,16 @@ class ItemLinks(OptionList): link.setdefault("link_replacement", None) -per_game_common_options = { - **common_options, # can be overwritten per-game - "local_items": LocalItems, - "non_local_items": NonLocalItems, - "start_inventory": StartInventory, - "start_hints": StartHints, - "start_location_hints": StartLocationHints, - "exclude_locations": ExcludeLocations, - "priority_locations": PriorityLocations, - "item_links": ItemLinks -} +@dataclass +class PerGameCommonOptions(CommonOptions): + local_items: LocalItems + non_local_items: NonLocalItems + start_inventory: StartInventory + start_hints: StartHints + start_location_hints: StartLocationHints + exclude_locations: ExcludeLocations + priority_locations: PriorityLocations + item_links: ItemLinks def generate_yaml_templates(target_folder: typing.Union[str, "pathlib.Path"], generate_hidden: bool = True): @@ -1071,10 +1124,7 @@ def generate_yaml_templates(target_folder: typing.Union[str, "pathlib.Path"], ge for game_name, world in AutoWorldRegister.world_types.items(): if not world.hidden or generate_hidden: - all_options: typing.Dict[str, AssembleOptions] = { - **per_game_common_options, - **world.option_definitions - } + all_options: typing.Dict[str, AssembleOptions] = world.options_dataclass.type_hints with open(local_path("data", "options.yaml")) as f: file_data = f.read() diff --git a/WebHostLib/options.py b/WebHostLib/options.py index fca01407e0..18a28045ee 100644 --- a/WebHostLib/options.py +++ b/WebHostLib/options.py @@ -36,10 +36,7 @@ def create(): for game_name, world in AutoWorldRegister.world_types.items(): - all_options: typing.Dict[str, Options.AssembleOptions] = { - **Options.per_game_common_options, - **world.option_definitions - } + all_options: typing.Dict[str, Options.AssembleOptions] = world.options_dataclass.type_hints # Generate JSON files for player-settings pages player_settings = { diff --git a/docs/options api.md b/docs/options api.md index fdabd9facd..2c86833800 100644 --- a/docs/options api.md +++ b/docs/options api.md @@ -28,19 +28,23 @@ Choice, and defining `alias_true = option_full`. and is reserved by AP. You can set this as your default value, but you cannot define your own `option_random`. As an example, suppose we want an option that lets the user start their game with a sword in their inventory. Let's -create our option class (with a docstring), give it a `display_name`, and add it to a dictionary that keeps track of our -options: +create our option class (with a docstring), give it a `display_name`, and add it to our game's options dataclass: ```python # Options.py +from dataclasses import dataclass + +from Options import Toggle, PerGameCommonOptions + + class StartingSword(Toggle): """Adds a sword to your starting inventory.""" display_name = "Start With Sword" -example_options = { - "starting_sword": StartingSword -} +@dataclass +class ExampleGameOptions(PerGameCommonOptions): + starting_sword: StartingSword ``` This will create a `Toggle` option, internally called `starting_sword`. To then submit this to the multiworld, we add it @@ -48,27 +52,30 @@ to our world's `__init__.py`: ```python from worlds.AutoWorld import World -from .Options import options +from .Options import ExampleGameOptions class ExampleWorld(World): - option_definitions = options + # this gives the generator all the definitions for our options + options_dataclass = ExampleGameOptions + # this gives us typing hints for all the options we defined + options: ExampleGameOptions ``` ### Option Checking Options are parsed by `Generate.py` before the worlds are created, and then the option classes are created shortly after world instantiation. These are created as attributes on the MultiWorld and can be accessed with -`self.multiworld.my_option_name[self.player]`. This is the option class, which supports direct comparison methods to +`self.options.my_option_name`. This is an instance of the option class, which supports direct comparison methods to relevant objects (like comparing a Toggle class to a `bool`). If you need to access the option result directly, this is the option class's `value` attribute. For our example above we can do a simple check: ```python -if self.multiworld.starting_sword[self.player]: +if self.options.starting_sword: do_some_things() ``` or if I need a boolean object, such as in my slot_data I can access it as: ```python -start_with_sword = bool(self.multiworld.starting_sword[self.player].value) +start_with_sword = bool(self.options.starting_sword.value) ``` ## Generic Option Classes @@ -120,7 +127,7 @@ Like Toggle, but 1 (true) is the default value. A numeric option allowing you to define different sub options. Values are stored as integers, but you can also do comparison methods with the class and strings, so if you have an `option_early_sword`, this can be compared with: ```python -if self.multiworld.sword_availability[self.player] == "early_sword": +if self.options.sword_availability == "early_sword": do_early_sword_things() ``` @@ -128,7 +135,7 @@ or: ```python from .Options import SwordAvailability -if self.multiworld.sword_availability[self.player] == SwordAvailability.option_early_sword: +if self.options.sword_availability == SwordAvailability.option_early_sword: do_early_sword_things() ``` @@ -160,7 +167,7 @@ within the world. Like choice allows you to predetermine options and has all of the same comparison methods and handling. Also accepts any user defined string as a valid option, so will either need to be validated by adding a validation step to the option class or within world, if necessary. Value for this class is `Union[str, int]` so if you need the value at a specified -point, `self.multiworld.my_option[self.player].current_key` will always return a string. +point, `self.options.my_option.current_key` will always return a string. ### PlandoBosses An option specifically built for handling boss rando, if your game can use it. Is a subclass of TextChoice so supports diff --git a/docs/world api.md b/docs/world api.md index 05b9e09399..6fb5b3ac9c 100644 --- a/docs/world api.md +++ b/docs/world api.md @@ -86,9 +86,11 @@ inside a `World` object. ### Player Options Players provide customized settings for their World in the form of yamls. -Those are accessible through `self.multiworld.[self.player]`. A dict -of valid options has to be provided in `self.option_definitions`. Options are automatically -added to the `World` object for easy access. +A `dataclass` of valid options definitions has to be provided in `self.options_dataclass`. +(It must be a subclass of `PerGameCommonOptions`.) +Option results are automatically added to the `World` object for easy access. +Those are accessible through `self.options.`, and you can get a dictionary of the option values via +`self.options.as_dict()`, passing the desired options as strings. ### World Settings @@ -221,11 +223,11 @@ See [pip documentation](https://pip.pypa.io/en/stable/cli/pip_install/#requireme AP will only import the `__init__.py`. Depending on code size it makes sense to use multiple files and use relative imports to access them. -e.g. `from .Options import mygame_options` from your `__init__.py` will load -`worlds//Options.py` and make its `mygame_options` accessible. +e.g. `from .Options import MyGameOptions` from your `__init__.py` will load +`world/[world_name]/Options.py` and make its `MyGameOptions` accessible. When imported names pile up it may be easier to use `from . import Options` -and access the variable as `Options.mygame_options`. +and access the variable as `Options.MyGameOptions`. Imports from directories outside your world should use absolute imports. Correct use of relative / absolute imports is required for zipped worlds to @@ -273,8 +275,9 @@ Each option has its own class, inherits from a base option type, has a docstring to describe it and a `display_name` property for display on the website and in spoiler logs. -The actual name as used in the yaml is defined in a `Dict[str, AssembleOptions]`, that is -assigned to the world under `self.option_definitions`. +The actual name as used in the yaml is defined via the field names of a `dataclass` that is +assigned to the world under `self.options_dataclass`. By convention, the strings +that define your option names should be in `snake_case`. Common option types are `Toggle`, `DefaultOnToggle`, `Choice`, `Range`. For more see `Options.py` in AP's base directory. @@ -309,8 +312,8 @@ default = 0 ```python # Options.py -from Options import Toggle, Range, Choice, Option -import typing +from dataclasses import dataclass +from Options import Toggle, Range, Choice, PerGameCommonOptions class Difficulty(Choice): """Sets overall game difficulty.""" @@ -333,23 +336,27 @@ class FixXYZGlitch(Toggle): """Fixes ABC when you do XYZ""" display_name = "Fix XYZ Glitch" -# By convention we call the options dict variable `_options`. -mygame_options: typing.Dict[str, AssembleOptions] = { - "difficulty": Difficulty, - "final_boss_hp": FinalBossHP, - "fix_xyz_glitch": FixXYZGlitch, -} +# By convention, we call the options dataclass `Options`. +# It has to be derived from 'PerGameCommonOptions'. +@dataclass +class MyGameOptions(PerGameCommonOptions): + difficulty: Difficulty + final_boss_hp: FinalBossHP + fix_xyz_glitch: FixXYZGlitch ``` + ```python # __init__.py from worlds.AutoWorld import World -from .Options import mygame_options # import the options dict +from .Options import MyGameOptions # import the options dataclass + class MyGameWorld(World): - #... - option_definitions = mygame_options # assign the options dict to the world - #... + # ... + options_dataclass = MyGameOptions # assign the options dataclass to the world + options: MyGameOptions # typing for option results + # ... ``` ### A World Class Skeleton @@ -359,13 +366,14 @@ class MyGameWorld(World): import settings import typing -from .Options import mygame_options # the options we defined earlier +from .Options import MyGameOptions # the options we defined earlier from .Items import mygame_items # data used below to add items to the World from .Locations import mygame_locations # same as above from worlds.AutoWorld import World from BaseClasses import Region, Location, Entrance, Item, RegionType, ItemClassification + class MyGameItem(Item): # or from Items import MyGameItem game = "My Game" # name of the game/world this item is from @@ -374,6 +382,7 @@ class MyGameLocation(Location): # or from Locations import MyGameLocation game = "My Game" # name of the game/world this location is in + class MyGameSettings(settings.Group): class RomFile(settings.SNESRomPath): """Insert help text for host.yaml here.""" @@ -384,7 +393,8 @@ class MyGameSettings(settings.Group): class MyGameWorld(World): """Insert description of the world/game here.""" game = "My Game" # name of the game/world - option_definitions = mygame_options # options the player can set + options_dataclass = MyGameOptions # options the player can set + options: MyGameOptions # typing hints for option results settings: typing.ClassVar[MyGameSettings] # will be automatically assigned from type hint topology_present = True # show path to required location checks in spoiler @@ -460,7 +470,7 @@ In addition, the following methods can be implemented and are called in this ord ```python def generate_early(self) -> None: # read player settings to world instance - self.final_boss_hp = self.multiworld.final_boss_hp[self.player].value + self.final_boss_hp = self.options.final_boss_hp.value ``` #### create_item @@ -687,9 +697,9 @@ def generate_output(self, output_directory: str): in self.multiworld.precollected_items[self.player]], "final_boss_hp": self.final_boss_hp, # store option name "easy", "normal" or "hard" for difficuly - "difficulty": self.multiworld.difficulty[self.player].current_key, + "difficulty": self.options.difficulty.current_key, # store option value True or False for fixing a glitch - "fix_xyz_glitch": self.multiworld.fix_xyz_glitch[self.player].value, + "fix_xyz_glitch": self.options.fix_xyz_glitch.value, } # point to a ROM specified by the installation src = self.settings.rom_file @@ -702,6 +712,26 @@ def generate_output(self, output_directory: str): generate_mod(src, out_file, data) ``` +### Slot Data + +If the game client needs to know information about the generated seed, a preferred method of transferring the data +is through the slot data. This can be filled from the `fill_slot_data` method of your world by returning a `Dict[str, Any]`, +but should be limited to data that is absolutely necessary to not waste resources. Slot data is sent to your client once +it has successfully [connected](network%20protocol.md#connected). +If you need to know information about locations in your world, instead +of propagating the slot data, it is preferable to use [LocationScouts](network%20protocol.md#locationscouts) since that +data already exists on the server. The most common usage of slot data is to send option results that the client needs +to be aware of. + +```python +def fill_slot_data(self): + # in order for our game client to handle the generated seed correctly we need to know what the user selected + # for their difficulty and final boss HP + # a dictionary returned from this method gets set as the slot_data and will be sent to the client after connecting + # the options dataclass has a method to return a `Dict[str, Any]` of each option name provided and the option's value + return self.options.as_dict("difficulty", "final_boss_hp") +``` + ### Documentation Each world implementation should have a tutorial and a game info page. These are both rendered on the website by reading diff --git a/test/TestBase.py b/test/TestBase.py index 61088e581c..e6fbafd95a 100644 --- a/test/TestBase.py +++ b/test/TestBase.py @@ -125,13 +125,13 @@ class WorldTestBase(unittest.TestCase): self.multiworld.game[1] = self.game self.multiworld.player_name = {1: "Tester"} self.multiworld.set_seed(seed) + self.multiworld.state = CollectionState(self.multiworld) args = Namespace() - for name, option in AutoWorld.AutoWorldRegister.world_types[self.game].option_definitions.items(): + for name, option in AutoWorld.AutoWorldRegister.world_types[self.game].options_dataclass.type_hints.items(): setattr(args, name, { 1: option.from_any(self.options.get(name, getattr(option, "default"))) }) self.multiworld.set_options(args) - self.multiworld.set_default_common_options() for step in gen_steps: call_all(self.multiworld, step) diff --git a/test/general/TestFill.py b/test/general/TestFill.py index 99f48cd0c7..0933603dfd 100644 --- a/test/general/TestFill.py +++ b/test/general/TestFill.py @@ -1,16 +1,20 @@ from typing import List, Iterable import unittest + +import Options +from Options import Accessibility from worlds.AutoWorld import World from Fill import FillError, balance_multiworld_progression, fill_restrictive, \ distribute_early_items, distribute_items_restrictive from BaseClasses import Entrance, LocationProgressType, MultiWorld, Region, Item, Location, \ - ItemClassification + ItemClassification, CollectionState from worlds.generic.Rules import CollectionRule, add_item_rule, locality_rules, set_rule def generate_multi_world(players: int = 1) -> MultiWorld: multi_world = MultiWorld(players) multi_world.player_name = {} + multi_world.state = CollectionState(multi_world) for i in range(players): player_id = i+1 world = World(multi_world, player_id) @@ -19,9 +23,16 @@ def generate_multi_world(players: int = 1) -> MultiWorld: multi_world.player_name[player_id] = "Test Player " + str(player_id) region = Region("Menu", player_id, multi_world, "Menu Region Hint") multi_world.regions.append(region) + for option_key, option in Options.PerGameCommonOptions.type_hints.items(): + if hasattr(multi_world, option_key): + getattr(multi_world, option_key).setdefault(player_id, option.from_any(getattr(option, "default"))) + else: + setattr(multi_world, option_key, {player_id: option.from_any(getattr(option, "default"))}) + # TODO - remove this loop once all worlds use options dataclasses + world.options = world.options_dataclass(**{option_key: getattr(multi_world, option_key)[player_id] + for option_key in world.options_dataclass.type_hints}) multi_world.set_seed(0) - multi_world.set_default_common_options() return multi_world @@ -186,7 +197,7 @@ class TestFillRestrictive(unittest.TestCase): items = player1.prog_items locations = player1.locations - multi_world.accessibility[player1.id].value = multi_world.accessibility[player1.id].option_minimal + multi_world.worlds[player1.id].options.accessibility = Accessibility.from_any(Accessibility.option_minimal) multi_world.completion_condition[player1.id] = lambda state: state.has( items[1].name, player1.id) set_rule(locations[1], lambda state: state.has( diff --git a/test/general/TestHelpers.py b/test/general/TestHelpers.py index c0b560c7e4..17fdce653c 100644 --- a/test/general/TestHelpers.py +++ b/test/general/TestHelpers.py @@ -1,3 +1,4 @@ +from argparse import Namespace from typing import Dict, Optional, Callable from BaseClasses import MultiWorld, CollectionState, Region @@ -13,7 +14,6 @@ class TestHelpers(unittest.TestCase): self.multiworld.game[self.player] = "helper_test_game" self.multiworld.player_name = {1: "Tester"} self.multiworld.set_seed() - self.multiworld.set_default_common_options() def testRegionHelpers(self) -> None: regions: Dict[str, str] = { diff --git a/test/general/TestOptions.py b/test/general/TestOptions.py index b7058183e0..4a3bd0b02a 100644 --- a/test/general/TestOptions.py +++ b/test/general/TestOptions.py @@ -6,6 +6,6 @@ class TestOptions(unittest.TestCase): def testOptionsHaveDocString(self): for gamename, world_type in AutoWorldRegister.world_types.items(): if not world_type.hidden: - for option_key, option in world_type.option_definitions.items(): + for option_key, option in world_type.options_dataclass.type_hints.items(): with self.subTest(game=gamename, option=option_key): self.assertTrue(option.__doc__) diff --git a/test/general/__init__.py b/test/general/__init__.py index b0fb7ca32e..d7ecc95749 100644 --- a/test/general/__init__.py +++ b/test/general/__init__.py @@ -1,7 +1,7 @@ from argparse import Namespace from typing import Type, Tuple -from BaseClasses import MultiWorld +from BaseClasses import MultiWorld, CollectionState from worlds.AutoWorld import call_all, World gen_steps = ("generate_early", "create_regions", "create_items", "set_rules", "generate_basic", "pre_fill") @@ -12,11 +12,11 @@ def setup_solo_multiworld(world_type: Type[World], steps: Tuple[str, ...] = gen_ multiworld.game[1] = world_type.game multiworld.player_name = {1: "Tester"} multiworld.set_seed() + multiworld.state = CollectionState(multiworld) args = Namespace() - for name, option in world_type.option_definitions.items(): + for name, option in world_type.options_dataclass.type_hints.items(): setattr(args, name, {1: option.from_any(option.default)}) multiworld.set_options(args) - multiworld.set_default_common_options() for step in steps: call_all(multiworld, step) return multiworld diff --git a/worlds/AutoWorld.py b/worlds/AutoWorld.py index e2fda16b87..9a8b6a56ef 100644 --- a/worlds/AutoWorld.py +++ b/worlds/AutoWorld.py @@ -4,11 +4,12 @@ import hashlib import logging import pathlib import sys -from typing import Any, Callable, ClassVar, Dict, FrozenSet, List, Optional, Set, TYPE_CHECKING, TextIO, Tuple, Type, \ +from dataclasses import make_dataclass +from typing import Any, Callable, ClassVar, Dict, Set, Tuple, FrozenSet, List, Optional, TYPE_CHECKING, TextIO, Type, \ Union +from Options import PerGameCommonOptions from BaseClasses import CollectionState -from Options import AssembleOptions if TYPE_CHECKING: import random @@ -63,6 +64,12 @@ class AutoWorldRegister(type): dct["required_client_version"] = max(dct["required_client_version"], base.__dict__["required_client_version"]) + # create missing options_dataclass from legacy option_definitions + # TODO - remove this once all worlds use options dataclasses + if "options_dataclass" not in dct and "option_definitions" in dct: + dct["options_dataclass"] = make_dataclass(f"{name}Options", dct["option_definitions"].items(), + bases=(PerGameCommonOptions,)) + # construct class new_class = super().__new__(mcs, name, bases, dct) if "game" in dct: @@ -163,8 +170,11 @@ class World(metaclass=AutoWorldRegister): """A World object encompasses a game's Items, Locations, Rules and additional data or functionality required. A Game should have its own subclass of World in which it defines the required data structures.""" - option_definitions: ClassVar[Dict[str, AssembleOptions]] = {} + options_dataclass: ClassVar[Type[PerGameCommonOptions]] = PerGameCommonOptions """link your Options mapping""" + options: PerGameCommonOptions + """resulting options for the player of this world""" + game: ClassVar[str] """name the game""" topology_present: ClassVar[bool] = False @@ -362,16 +372,14 @@ class World(metaclass=AutoWorldRegister): def create_group(cls, multiworld: "MultiWorld", new_player_id: int, players: Set[int]) -> World: """Creates a group, which is an instance of World that is responsible for multiple others. An example case is ItemLinks creating these.""" - import Options + # TODO remove loop when worlds use options dataclass + for option_key, option in cls.options_dataclass.type_hints.items(): + getattr(multiworld, option_key)[new_player_id] = option(option.default) + group = cls(multiworld, new_player_id) + group.options = cls.options_dataclass(**{option_key: option(option.default) + for option_key, option in cls.options_dataclass.type_hints.items()}) - for option_key, option in cls.option_definitions.items(): - getattr(multiworld, option_key)[new_player_id] = option(option.default) - for option_key, option in Options.common_options.items(): - getattr(multiworld, option_key)[new_player_id] = option(option.default) - for option_key, option in Options.per_game_common_options.items(): - getattr(multiworld, option_key)[new_player_id] = option(option.default) - - return cls(multiworld, new_player_id) + return group # decent place to implement progressive items, in most cases can stay as-is def collect_item(self, state: "CollectionState", item: "Item", remove: bool = False) -> Optional[str]: diff --git a/worlds/alttp/test/__init__.py b/worlds/alttp/test/__init__.py index e69de29bb2..5baaa7e88e 100644 --- a/worlds/alttp/test/__init__.py +++ b/worlds/alttp/test/__init__.py @@ -0,0 +1,16 @@ +import unittest +from argparse import Namespace + +from BaseClasses import MultiWorld, CollectionState +from worlds import AutoWorldRegister + + +class LTTPTestBase(unittest.TestCase): + def world_setup(self): + self.multiworld = MultiWorld(1) + self.multiworld.state = CollectionState(self.multiworld) + self.multiworld.set_seed(None) + args = Namespace() + for name, option in AutoWorldRegister.world_types["A Link to the Past"].options_dataclass.type_hints.items(): + setattr(args, name, {1: option.from_any(getattr(option, "default"))}) + self.multiworld.set_options(args) diff --git a/worlds/alttp/test/dungeons/TestDungeon.py b/worlds/alttp/test/dungeons/TestDungeon.py index 81085ab10a..94c30c3493 100644 --- a/worlds/alttp/test/dungeons/TestDungeon.py +++ b/worlds/alttp/test/dungeons/TestDungeon.py @@ -1,25 +1,16 @@ -import unittest -from argparse import Namespace - -from BaseClasses import MultiWorld, CollectionState, ItemClassification -from worlds.alttp.Dungeons import get_dungeon_item_pool +from BaseClasses import CollectionState, ItemClassification +from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool from worlds.alttp.EntranceShuffle import mandatory_connections, connect_simple from worlds.alttp.ItemPool import difficulties from worlds.alttp.Items import ItemFactory from worlds.alttp.Regions import create_regions from worlds.alttp.Shops import create_shops -from worlds import AutoWorld +from worlds.alttp.test import LTTPTestBase -class TestDungeon(unittest.TestCase): +class TestDungeon(LTTPTestBase): def setUp(self): - self.multiworld = MultiWorld(1) - self.multiworld.set_seed(None) - args = Namespace() - for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): - setattr(args, name, {1: option.from_any(option.default)}) - self.multiworld.set_options(args) - self.multiworld.set_default_common_options() + self.world_setup() self.starting_regions = [] # Where to start exploring self.remove_exits = [] # Block dungeon exits self.multiworld.difficulty_requirements[1] = difficulties['normal'] diff --git a/worlds/alttp/test/inverted/TestInverted.py b/worlds/alttp/test/inverted/TestInverted.py index ad7458202e..f5608ba07b 100644 --- a/worlds/alttp/test/inverted/TestInverted.py +++ b/worlds/alttp/test/inverted/TestInverted.py @@ -1,6 +1,3 @@ -from argparse import Namespace - -from BaseClasses import MultiWorld from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool from worlds.alttp.EntranceShuffle import link_inverted_entrances from worlds.alttp.InvertedRegions import create_inverted_regions @@ -10,17 +7,12 @@ from worlds.alttp.Regions import mark_light_world_regions from worlds.alttp.Shops import create_shops from test.TestBase import TestBase -from worlds import AutoWorld +from worlds.alttp.test import LTTPTestBase -class TestInverted(TestBase): + +class TestInverted(TestBase, LTTPTestBase): def setUp(self): - self.multiworld = MultiWorld(1) - self.multiworld.set_seed(None) - args = Namespace() - for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): - setattr(args, name, {1: option.from_any(option.default)}) - self.multiworld.set_options(args) - self.multiworld.set_default_common_options() + self.world_setup() self.multiworld.difficulty_requirements[1] = difficulties['normal'] self.multiworld.mode[1] = "inverted" create_inverted_regions(self.multiworld, 1) diff --git a/worlds/alttp/test/inverted/TestInvertedBombRules.py b/worlds/alttp/test/inverted/TestInvertedBombRules.py index 89c5d78603..d9eacb5ad9 100644 --- a/worlds/alttp/test/inverted/TestInvertedBombRules.py +++ b/worlds/alttp/test/inverted/TestInvertedBombRules.py @@ -1,27 +1,17 @@ -import unittest -from argparse import Namespace - -from BaseClasses import MultiWorld from worlds.alttp.Dungeons import create_dungeons from worlds.alttp.EntranceShuffle import connect_entrance, Inverted_LW_Entrances, Inverted_LW_Dungeon_Entrances, Inverted_LW_Single_Cave_Doors, Inverted_Old_Man_Entrances, Inverted_DW_Entrances, Inverted_DW_Dungeon_Entrances, Inverted_DW_Single_Cave_Doors, \ Inverted_LW_Entrances_Must_Exit, Inverted_LW_Dungeon_Entrances_Must_Exit, Inverted_Bomb_Shop_Multi_Cave_Doors, Inverted_Bomb_Shop_Single_Cave_Doors, Blacksmith_Single_Cave_Doors, Inverted_Blacksmith_Multi_Cave_Doors from worlds.alttp.InvertedRegions import create_inverted_regions from worlds.alttp.ItemPool import difficulties from worlds.alttp.Rules import set_inverted_big_bomb_rules -from worlds import AutoWorld +from worlds.alttp.test import LTTPTestBase -class TestInvertedBombRules(unittest.TestCase): +class TestInvertedBombRules(LTTPTestBase): def setUp(self): - self.multiworld = MultiWorld(1) - self.multiworld.set_seed(None) + self.world_setup() self.multiworld.mode[1] = "inverted" - args = Namespace - for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): - setattr(args, name, {1: option.from_any(option.default)}) - self.multiworld.set_options(args) - self.multiworld.set_default_common_options() self.multiworld.difficulty_requirements[1] = difficulties['normal'] create_inverted_regions(self.multiworld, 1) self.multiworld.worlds[1].create_dungeons() diff --git a/worlds/alttp/test/inverted_minor_glitches/TestInvertedMinor.py b/worlds/alttp/test/inverted_minor_glitches/TestInvertedMinor.py index 72049e1774..33e5822981 100644 --- a/worlds/alttp/test/inverted_minor_glitches/TestInvertedMinor.py +++ b/worlds/alttp/test/inverted_minor_glitches/TestInvertedMinor.py @@ -1,27 +1,18 @@ -from argparse import Namespace - -from BaseClasses import MultiWorld from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool from worlds.alttp.EntranceShuffle import link_inverted_entrances from worlds.alttp.InvertedRegions import create_inverted_regions -from worlds.alttp.ItemPool import generate_itempool, difficulties +from worlds.alttp.ItemPool import difficulties from worlds.alttp.Items import ItemFactory from worlds.alttp.Regions import mark_light_world_regions from worlds.alttp.Shops import create_shops -from worlds.alttp.Rules import set_rules from test.TestBase import TestBase -from worlds import AutoWorld +from worlds.alttp.test import LTTPTestBase -class TestInvertedMinor(TestBase): + +class TestInvertedMinor(TestBase, LTTPTestBase): def setUp(self): - self.multiworld = MultiWorld(1) - self.multiworld.set_seed(None) - args = Namespace() - for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): - setattr(args, name, {1: option.from_any(option.default)}) - self.multiworld.set_options(args) - self.multiworld.set_default_common_options() + self.world_setup() self.multiworld.mode[1] = "inverted" self.multiworld.logic[1] = "minorglitches" self.multiworld.difficulty_requirements[1] = difficulties['normal'] diff --git a/worlds/alttp/test/inverted_owg/TestInvertedOWG.py b/worlds/alttp/test/inverted_owg/TestInvertedOWG.py index 77a551db6f..a4e84fce9b 100644 --- a/worlds/alttp/test/inverted_owg/TestInvertedOWG.py +++ b/worlds/alttp/test/inverted_owg/TestInvertedOWG.py @@ -1,28 +1,18 @@ -from argparse import Namespace - -from BaseClasses import MultiWorld from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool from worlds.alttp.EntranceShuffle import link_inverted_entrances from worlds.alttp.InvertedRegions import create_inverted_regions -from worlds.alttp.ItemPool import generate_itempool, difficulties +from worlds.alttp.ItemPool import difficulties from worlds.alttp.Items import ItemFactory from worlds.alttp.Regions import mark_light_world_regions from worlds.alttp.Shops import create_shops -from worlds.alttp.Rules import set_rules from test.TestBase import TestBase -from worlds import AutoWorld +from worlds.alttp.test import LTTPTestBase -class TestInvertedOWG(TestBase): +class TestInvertedOWG(TestBase, LTTPTestBase): def setUp(self): - self.multiworld = MultiWorld(1) - self.multiworld.set_seed(None) - args = Namespace() - for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): - setattr(args, name, {1: option.from_any(option.default)}) - self.multiworld.set_options(args) - self.multiworld.set_default_common_options() + self.world_setup() self.multiworld.logic[1] = "owglitches" self.multiworld.mode[1] = "inverted" self.multiworld.difficulty_requirements[1] = difficulties['normal'] diff --git a/worlds/alttp/test/minor_glitches/TestMinor.py b/worlds/alttp/test/minor_glitches/TestMinor.py index fdf626fe9d..d5cfd3095b 100644 --- a/worlds/alttp/test/minor_glitches/TestMinor.py +++ b/worlds/alttp/test/minor_glitches/TestMinor.py @@ -1,27 +1,15 @@ -from argparse import Namespace - -from BaseClasses import MultiWorld -from worlds.alttp.Dungeons import create_dungeons, get_dungeon_item_pool -from worlds.alttp.EntranceShuffle import link_entrances +from worlds.alttp.Dungeons import get_dungeon_item_pool from worlds.alttp.InvertedRegions import mark_dark_world_regions from worlds.alttp.ItemPool import difficulties from worlds.alttp.Items import ItemFactory -from worlds.alttp.Regions import create_regions -from worlds.alttp.Shops import create_shops from test.TestBase import TestBase -from worlds import AutoWorld +from worlds.alttp.test import LTTPTestBase -class TestMinor(TestBase): +class TestMinor(TestBase, LTTPTestBase): def setUp(self): - self.multiworld = MultiWorld(1) - self.multiworld.set_seed(None) - args = Namespace() - for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): - setattr(args, name, {1: option.from_any(option.default)}) - self.multiworld.set_options(args) - self.multiworld.set_default_common_options() + self.world_setup() self.multiworld.logic[1] = "minorglitches" self.multiworld.difficulty_requirements[1] = difficulties['normal'] self.multiworld.worlds[1].er_seed = 0 diff --git a/worlds/alttp/test/owg/TestVanillaOWG.py b/worlds/alttp/test/owg/TestVanillaOWG.py index c0888aa32f..37b0b6ccb8 100644 --- a/worlds/alttp/test/owg/TestVanillaOWG.py +++ b/worlds/alttp/test/owg/TestVanillaOWG.py @@ -1,24 +1,15 @@ -from argparse import Namespace - -from BaseClasses import MultiWorld from worlds.alttp.Dungeons import get_dungeon_item_pool from worlds.alttp.InvertedRegions import mark_dark_world_regions from worlds.alttp.ItemPool import difficulties from worlds.alttp.Items import ItemFactory from test.TestBase import TestBase -from worlds import AutoWorld +from worlds.alttp.test import LTTPTestBase -class TestVanillaOWG(TestBase): +class TestVanillaOWG(TestBase, LTTPTestBase): def setUp(self): - self.multiworld = MultiWorld(1) - self.multiworld.set_seed(None) - args = Namespace() - for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): - setattr(args, name, {1: option.from_any(option.default)}) - self.multiworld.set_options(args) - self.multiworld.set_default_common_options() + self.world_setup() self.multiworld.difficulty_requirements[1] = difficulties['normal'] self.multiworld.logic[1] = "owglitches" self.multiworld.worlds[1].er_seed = 0 diff --git a/worlds/alttp/test/vanilla/TestVanilla.py b/worlds/alttp/test/vanilla/TestVanilla.py index e338410df2..3c983e9850 100644 --- a/worlds/alttp/test/vanilla/TestVanilla.py +++ b/worlds/alttp/test/vanilla/TestVanilla.py @@ -1,22 +1,14 @@ -from argparse import Namespace - -from BaseClasses import MultiWorld from worlds.alttp.Dungeons import get_dungeon_item_pool from worlds.alttp.InvertedRegions import mark_dark_world_regions from worlds.alttp.ItemPool import difficulties from worlds.alttp.Items import ItemFactory from test.TestBase import TestBase -from worlds import AutoWorld +from worlds.alttp.test import LTTPTestBase -class TestVanilla(TestBase): + +class TestVanilla(TestBase, LTTPTestBase): def setUp(self): - self.multiworld = MultiWorld(1) - self.multiworld.set_seed(None) - args = Namespace() - for name, option in AutoWorld.AutoWorldRegister.world_types["A Link to the Past"].option_definitions.items(): - setattr(args, name, {1: option.from_any(option.default)}) - self.multiworld.set_options(args) - self.multiworld.set_default_common_options() + self.world_setup() self.multiworld.logic[1] = "noglitches" self.multiworld.difficulty_requirements[1] = difficulties['normal'] self.multiworld.worlds[1].er_seed = 0 diff --git a/worlds/dlcquest/Items.py b/worlds/dlcquest/Items.py index 61f4cd30fa..61d1be54cb 100644 --- a/worlds/dlcquest/Items.py +++ b/worlds/dlcquest/Items.py @@ -1,11 +1,12 @@ import csv import enum import math -from typing import Protocol, Union, Dict, List, Set -from BaseClasses import Item, ItemClassification -from . import Options, data from dataclasses import dataclass, field from random import Random +from typing import Dict, List, Set + +from BaseClasses import Item, ItemClassification +from . import Options, data class DLCQuestItem(Item): @@ -93,38 +94,35 @@ def create_trap_items(world, World_Options: Options.DLCQuestOptions, trap_needed def create_items(world, World_Options: Options.DLCQuestOptions, locations_count: int, random: Random): created_items = [] - if World_Options[Options.Campaign] == Options.Campaign.option_basic or World_Options[ - Options.Campaign] == Options.Campaign.option_both: + if World_Options.campaign == Options.Campaign.option_basic or World_Options.campaign == Options.Campaign.option_both: for item in items_by_group[Group.DLCQuest]: if item.has_any_group(Group.DLC): created_items.append(world.create_item(item)) - if item.has_any_group(Group.Item) and World_Options[ - Options.ItemShuffle] == Options.ItemShuffle.option_shuffled: + if item.has_any_group(Group.Item) and World_Options.item_shuffle == Options.ItemShuffle.option_shuffled: created_items.append(world.create_item(item)) - if World_Options[Options.CoinSanity] == Options.CoinSanity.option_coin: - coin_bundle_needed = math.floor(825 / World_Options[Options.CoinSanityRange]) + if World_Options.coinsanity == Options.CoinSanity.option_coin: + coin_bundle_needed = math.floor(825 / World_Options.coinbundlequantity) for item in items_by_group[Group.DLCQuest]: if item.has_any_group(Group.Coin): for i in range(coin_bundle_needed): created_items.append(world.create_item(item)) - if 825 % World_Options[Options.CoinSanityRange] != 0: + if 825 % World_Options.coinbundlequantity != 0: created_items.append(world.create_item(item)) - if World_Options[Options.Campaign] == Options.Campaign.option_live_freemium_or_die or World_Options[ - Options.Campaign] == Options.Campaign.option_both: + if (World_Options.campaign == Options.Campaign.option_live_freemium_or_die or + World_Options.campaign == Options.Campaign.option_both): for item in items_by_group[Group.Freemium]: if item.has_any_group(Group.DLC): created_items.append(world.create_item(item)) - if item.has_any_group(Group.Item) and World_Options[ - Options.ItemShuffle] == Options.ItemShuffle.option_shuffled: + if item.has_any_group(Group.Item) and World_Options.item_shuffle == Options.ItemShuffle.option_shuffled: created_items.append(world.create_item(item)) - if World_Options[Options.CoinSanity] == Options.CoinSanity.option_coin: - coin_bundle_needed = math.floor(889 / World_Options[Options.CoinSanityRange]) + if World_Options.coinsanity == Options.CoinSanity.option_coin: + coin_bundle_needed = math.floor(889 / World_Options.coinbundlequantity) for item in items_by_group[Group.Freemium]: if item.has_any_group(Group.Coin): for i in range(coin_bundle_needed): created_items.append(world.create_item(item)) - if 889 % World_Options[Options.CoinSanityRange] != 0: + if 889 % World_Options.coinbundlequantity != 0: created_items.append(world.create_item(item)) trap_items = create_trap_items(world, World_Options, locations_count - len(created_items), random) diff --git a/worlds/dlcquest/Locations.py b/worlds/dlcquest/Locations.py index 08d37e7812..a9fdd00a20 100644 --- a/worlds/dlcquest/Locations.py +++ b/worlds/dlcquest/Locations.py @@ -1,5 +1,4 @@ -from BaseClasses import Location, MultiWorld -from . import Options +from BaseClasses import Location class DLCQuestLocation(Location): diff --git a/worlds/dlcquest/Options.py b/worlds/dlcquest/Options.py index a1674a4d5a..ce728b4e92 100644 --- a/worlds/dlcquest/Options.py +++ b/worlds/dlcquest/Options.py @@ -1,22 +1,6 @@ -from typing import Union, Dict, runtime_checkable, Protocol -from Options import Option, DeathLink, Choice, Toggle, SpecialRange from dataclasses import dataclass - -@runtime_checkable -class DLCQuestOption(Protocol): - internal_name: str - - -@dataclass -class DLCQuestOptions: - options: Dict[str, Union[bool, int]] - - def __getitem__(self, item: Union[str, DLCQuestOption]) -> Union[bool, int]: - if isinstance(item, DLCQuestOption): - item = item.internal_name - - return self.options.get(item, None) +from Options import Choice, DeathLink, PerGameCommonOptions, SpecialRange class DoubleJumpGlitch(Choice): @@ -94,31 +78,13 @@ class ItemShuffle(Choice): default = 0 -DLCQuest_options: Dict[str, type(Option)] = { - option.internal_name: option - for option in [ - DoubleJumpGlitch, - CoinSanity, - CoinSanityRange, - TimeIsMoney, - EndingChoice, - Campaign, - ItemShuffle, - ] -} -default_options = {option.internal_name: option.default for option in DLCQuest_options.values()} -DLCQuest_options["death_link"] = DeathLink - - -def fetch_options(world, player: int) -> DLCQuestOptions: - return DLCQuestOptions({option: get_option_value(world, player, option) for option in DLCQuest_options}) - - -def get_option_value(world, player: int, name: str) -> Union[bool, int]: - assert name in DLCQuest_options, f"{name} is not a valid option for DLC Quest." - - value = getattr(world, name) - - if issubclass(DLCQuest_options[name], Toggle): - return bool(value[player].value) - return value[player].value +@dataclass +class DLCQuestOptions(PerGameCommonOptions): + double_jump_glitch: DoubleJumpGlitch + coinsanity: CoinSanity + coinbundlequantity: CoinSanityRange + time_is_money: TimeIsMoney + ending_choice: EndingChoice + campaign: Campaign + item_shuffle: ItemShuffle + death_link: DeathLink diff --git a/worlds/dlcquest/Regions.py b/worlds/dlcquest/Regions.py index 8135a1c362..dfb5f6c021 100644 --- a/worlds/dlcquest/Regions.py +++ b/worlds/dlcquest/Regions.py @@ -1,8 +1,9 @@ import math -from BaseClasses import MultiWorld, Region, Location, Entrance, ItemClassification + +from BaseClasses import Entrance, MultiWorld, Region +from . import Options from .Locations import DLCQuestLocation, location_table from .Rules import create_event -from . import Options DLCQuestRegion = ["Movement Pack", "Behind Tree", "Psychological Warfare", "Double Jump Left", "Double Jump Behind the Tree", "The Forest", "Final Room"] @@ -26,16 +27,16 @@ def add_coin_dlcquest(region: Region, Coin: int, player: int): def create_regions(world: MultiWorld, player: int, World_Options: Options.DLCQuestOptions): Regmenu = Region("Menu", player, world) - if World_Options[Options.Campaign] == Options.Campaign.option_basic or World_Options[ - Options.Campaign] == Options.Campaign.option_both: + if (World_Options.campaign == Options.Campaign.option_basic or World_Options.campaign + == Options.Campaign.option_both): Regmenu.exits += [Entrance(player, "DLC Quest Basic", Regmenu)] - if World_Options[Options.Campaign] == Options.Campaign.option_live_freemium_or_die or World_Options[ - Options.Campaign] == Options.Campaign.option_both: + if (World_Options.campaign == Options.Campaign.option_live_freemium_or_die or World_Options.campaign + == Options.Campaign.option_both): Regmenu.exits += [Entrance(player, "Live Freemium or Die", Regmenu)] world.regions.append(Regmenu) - if World_Options[Options.Campaign] == Options.Campaign.option_basic or World_Options[ - Options.Campaign] == Options.Campaign.option_both: + if (World_Options.campaign == Options.Campaign.option_basic or World_Options.campaign + == Options.Campaign.option_both): Regmoveright = Region("Move Right", player, world, "Start of the basic game") Locmoveright_name = ["Movement Pack", "Animation Pack", "Audio Pack", "Pause Menu Pack"] @@ -43,13 +44,13 @@ def create_regions(world: MultiWorld, player: int, World_Options: Options.DLCQue Regmoveright.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regmoveright) for loc_name in Locmoveright_name] add_coin_dlcquest(Regmoveright, 4, player) - if World_Options[Options.CoinSanity] == Options.CoinSanity.option_coin: - coin_bundle_needed = math.floor(825 / World_Options[Options.CoinSanityRange]) + if World_Options.coinsanity == Options.CoinSanity.option_coin: + coin_bundle_needed = math.floor(825 / World_Options.coinbundlequantity) for i in range(coin_bundle_needed): - item_coin = f"DLC Quest: {World_Options[Options.CoinSanityRange] * (i + 1)} Coin" + item_coin = f"DLC Quest: {World_Options.coinbundlequantity * (i + 1)} Coin" Regmoveright.locations += [ DLCQuestLocation(player, item_coin, location_table[item_coin], Regmoveright)] - if 825 % World_Options[Options.CoinSanityRange] != 0: + if 825 % World_Options.coinbundlequantity != 0: Regmoveright.locations += [ DLCQuestLocation(player, "DLC Quest: 825 Coin", location_table["DLC Quest: 825 Coin"], Regmoveright)] @@ -58,7 +59,7 @@ def create_regions(world: MultiWorld, player: int, World_Options: Options.DLCQue Regmovpack = Region("Movement Pack", player, world) Locmovpack_name = ["Time is Money Pack", "Psychological Warfare Pack", "Armor for your Horse Pack", "Shepherd Sheep"] - if World_Options[Options.ItemShuffle] == Options.ItemShuffle.option_shuffled: + if World_Options.item_shuffle == Options.ItemShuffle.option_shuffled: Locmovpack_name += ["Sword"] Regmovpack.exits = [Entrance(player, "Tree", Regmovpack), Entrance(player, "Cloud", Regmovpack)] Regmovpack.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regmovpack) for loc_name @@ -68,7 +69,7 @@ def create_regions(world: MultiWorld, player: int, World_Options: Options.DLCQue Regbtree = Region("Behind Tree", player, world) Locbtree_name = ["Double Jump Pack", "Map Pack", "Between Trees Sheep", "Hole in the Wall Sheep"] - if World_Options[Options.ItemShuffle] == Options.ItemShuffle.option_shuffled: + if World_Options.item_shuffle == Options.ItemShuffle.option_shuffled: Locbtree_name += ["Gun"] Regbtree.exits = [Entrance(player, "Behind Tree Double Jump", Regbtree), Entrance(player, "Forest Entrance", Regbtree)] @@ -191,27 +192,27 @@ def create_regions(world: MultiWorld, player: int, World_Options: Options.DLCQue world.get_entrance("True Double Jump", player).connect(world.get_region("True Double Jump Behind Tree", player)) - if World_Options[Options.Campaign] == Options.Campaign.option_live_freemium_or_die or World_Options[ - Options.Campaign] == Options.Campaign.option_both: + if (World_Options.campaign == Options.Campaign.option_live_freemium_or_die or World_Options.campaign + == Options.Campaign.option_both): Regfreemiumstart = Region("Freemium Start", player, world) Locfreemiumstart_name = ["Particles Pack", "Day One Patch Pack", "Checkpoint Pack", "Incredibly Important Pack", "Nice Try", "Story is Important", "I Get That Reference!"] - if World_Options[Options.ItemShuffle] == Options.ItemShuffle.option_shuffled: + if World_Options.item_shuffle == Options.ItemShuffle.option_shuffled: Locfreemiumstart_name += ["Wooden Sword"] Regfreemiumstart.exits = [Entrance(player, "Vines", Regfreemiumstart)] Regfreemiumstart.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regfreemiumstart) for loc_name in Locfreemiumstart_name] add_coin_freemium(Regfreemiumstart, 50, player) - if World_Options[Options.CoinSanity] == Options.CoinSanity.option_coin: - coin_bundle_needed = math.floor(889 / World_Options[Options.CoinSanityRange]) + if World_Options.coinsanity == Options.CoinSanity.option_coin: + coin_bundle_needed = math.floor(889 / World_Options.coinbundlequantity) for i in range(coin_bundle_needed): - item_coin_freemium = f"Live Freemium or Die: {World_Options[Options.CoinSanityRange] * (i + 1)} Coin" + item_coin_freemium = f"Live Freemium or Die: {World_Options.coinbundlequantity * (i + 1)} Coin" Regfreemiumstart.locations += [ DLCQuestLocation(player, item_coin_freemium, location_table[item_coin_freemium], Regfreemiumstart)] - if 889 % World_Options[Options.CoinSanityRange] != 0: + if 889 % World_Options.coinbundlequantity != 0: Regfreemiumstart.locations += [ DLCQuestLocation(player, "Live Freemium or Die: 889 Coin", location_table["Live Freemium or Die: 889 Coin"], @@ -220,7 +221,7 @@ def create_regions(world: MultiWorld, player: int, World_Options: Options.DLCQue Regbehindvine = Region("Behind the Vines", player, world) Locbehindvine_name = ["Wall Jump Pack", "Health Bar Pack", "Parallax Pack"] - if World_Options[Options.ItemShuffle] == Options.ItemShuffle.option_shuffled: + if World_Options.item_shuffle == Options.ItemShuffle.option_shuffled: Locbehindvine_name += ["Pickaxe"] Regbehindvine.exits = [Entrance(player, "Wall Jump Entrance", Regbehindvine)] Regbehindvine.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regbehindvine) for @@ -260,7 +261,7 @@ def create_regions(world: MultiWorld, player: int, World_Options: Options.DLCQue Regcutcontent = Region("Cut Content", player, world) Loccutcontent_name = [] - if World_Options[Options.ItemShuffle] == Options.ItemShuffle.option_shuffled: + if World_Options.item_shuffle == Options.ItemShuffle.option_shuffled: Loccutcontent_name += ["Humble Indie Bindle"] Regcutcontent.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regcutcontent) for loc_name in Loccutcontent_name] @@ -269,7 +270,7 @@ def create_regions(world: MultiWorld, player: int, World_Options: Options.DLCQue Regnamechange = Region("Name Change", player, world) Locnamechange_name = [] - if World_Options[Options.ItemShuffle] == Options.ItemShuffle.option_shuffled: + if World_Options.item_shuffle == Options.ItemShuffle.option_shuffled: Locnamechange_name += ["Box of Various Supplies"] Regnamechange.exits = [Entrance(player, "Behind Rocks", Regnamechange)] Regnamechange.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regnamechange) for diff --git a/worlds/dlcquest/Rules.py b/worlds/dlcquest/Rules.py index d2495121f4..c5fdfe8282 100644 --- a/worlds/dlcquest/Rules.py +++ b/worlds/dlcquest/Rules.py @@ -1,10 +1,10 @@ import math import re -from .Locations import DLCQuestLocation -from ..generic.Rules import add_rule, set_rule, item_name_in_locations -from .Items import DLCQuestItem + from BaseClasses import ItemClassification +from worlds.generic.Rules import add_rule, item_name_in_locations, set_rule from . import Options +from .Items import DLCQuestItem def create_event(player, event: str): @@ -42,7 +42,7 @@ def set_rules(world, player, World_Options: Options.DLCQuestOptions): def set_basic_rules(World_Options, has_enough_coin, player, world): - if World_Options[Options.Campaign] == Options.Campaign.option_live_freemium_or_die: + if World_Options.campaign == Options.Campaign.option_live_freemium_or_die: return set_basic_entrance_rules(player, world) set_basic_self_obtained_items_rules(World_Options, player, world) @@ -66,12 +66,12 @@ def set_basic_entrance_rules(player, world): def set_basic_self_obtained_items_rules(World_Options, player, world): - if World_Options[Options.ItemShuffle] != Options.ItemShuffle.option_disabled: + if World_Options.item_shuffle != Options.ItemShuffle.option_disabled: return set_rule(world.get_entrance("Behind Ogre", player), lambda state: state.has("Gun Pack", player)) - if World_Options[Options.TimeIsMoney] == Options.TimeIsMoney.option_required: + if World_Options.time_is_money == Options.TimeIsMoney.option_required: set_rule(world.get_entrance("Tree", player), lambda state: state.has("Time is Money Pack", player)) set_rule(world.get_entrance("Cave Tree", player), @@ -87,7 +87,7 @@ def set_basic_self_obtained_items_rules(World_Options, player, world): def set_basic_shuffled_items_rules(World_Options, player, world): - if World_Options[Options.ItemShuffle] != Options.ItemShuffle.option_shuffled: + if World_Options.item_shuffle != Options.ItemShuffle.option_shuffled: return set_rule(world.get_entrance("Behind Ogre", player), lambda state: state.has("Gun", player)) @@ -108,13 +108,13 @@ def set_basic_shuffled_items_rules(World_Options, player, world): set_rule(world.get_location("Gun", player), lambda state: state.has("Gun Pack", player)) - if World_Options[Options.TimeIsMoney] == Options.TimeIsMoney.option_required: + if World_Options.time_is_money == Options.TimeIsMoney.option_required: set_rule(world.get_location("Sword", player), lambda state: state.has("Time is Money Pack", player)) def set_double_jump_glitchless_rules(World_Options, player, world): - if World_Options[Options.DoubleJumpGlitch] != Options.DoubleJumpGlitch.option_none: + if World_Options.double_jump_glitch != Options.DoubleJumpGlitch.option_none: return set_rule(world.get_entrance("Cloud Double Jump", player), lambda state: state.has("Double Jump Pack", player)) @@ -123,7 +123,7 @@ def set_double_jump_glitchless_rules(World_Options, player, world): def set_easy_double_jump_glitch_rules(World_Options, player, world): - if World_Options[Options.DoubleJumpGlitch] == Options.DoubleJumpGlitch.option_all: + if World_Options.double_jump_glitch == Options.DoubleJumpGlitch.option_all: return set_rule(world.get_entrance("Behind Tree Double Jump", player), lambda state: state.has("Double Jump Pack", player)) @@ -132,70 +132,70 @@ def set_easy_double_jump_glitch_rules(World_Options, player, world): def self_basic_coinsanity_funded_purchase_rules(World_Options, has_enough_coin, player, world): - if World_Options[Options.CoinSanity] != Options.CoinSanity.option_coin: + if World_Options.coinsanity != Options.CoinSanity.option_coin: return - number_of_bundle = math.floor(825 / World_Options[Options.CoinSanityRange]) + number_of_bundle = math.floor(825 / World_Options.coinbundlequantity) for i in range(number_of_bundle): - item_coin = f"DLC Quest: {World_Options[Options.CoinSanityRange] * (i + 1)} Coin" + item_coin = f"DLC Quest: {World_Options.coinbundlequantity * (i + 1)} Coin" set_rule(world.get_location(item_coin, player), - has_enough_coin(player, World_Options[Options.CoinSanityRange] * (i + 1))) - if 825 % World_Options[Options.CoinSanityRange] != 0: + has_enough_coin(player, World_Options.coinbundlequantity * (i + 1))) + if 825 % World_Options.coinbundlequantity != 0: set_rule(world.get_location("DLC Quest: 825 Coin", player), has_enough_coin(player, 825)) set_rule(world.get_location("Movement Pack", player), lambda state: state.has("DLC Quest: Coin Bundle", player, - math.ceil(4 / World_Options[Options.CoinSanityRange]))) + math.ceil(4 / World_Options.coinbundlequantity))) set_rule(world.get_location("Animation Pack", player), lambda state: state.has("DLC Quest: Coin Bundle", player, - math.ceil(5 / World_Options[Options.CoinSanityRange]))) + math.ceil(5 / World_Options.coinbundlequantity))) set_rule(world.get_location("Audio Pack", player), lambda state: state.has("DLC Quest: Coin Bundle", player, - math.ceil(5 / World_Options[Options.CoinSanityRange]))) + math.ceil(5 / World_Options.coinbundlequantity))) set_rule(world.get_location("Pause Menu Pack", player), lambda state: state.has("DLC Quest: Coin Bundle", player, - math.ceil(5 / World_Options[Options.CoinSanityRange]))) + math.ceil(5 / World_Options.coinbundlequantity))) set_rule(world.get_location("Time is Money Pack", player), lambda state: state.has("DLC Quest: Coin Bundle", player, - math.ceil(20 / World_Options[Options.CoinSanityRange]))) + math.ceil(20 / World_Options.coinbundlequantity))) set_rule(world.get_location("Double Jump Pack", player), lambda state: state.has("DLC Quest: Coin Bundle", player, - math.ceil(100 / World_Options[Options.CoinSanityRange]))) + math.ceil(100 / World_Options.coinbundlequantity))) set_rule(world.get_location("Pet Pack", player), lambda state: state.has("DLC Quest: Coin Bundle", player, - math.ceil(5 / World_Options[Options.CoinSanityRange]))) + math.ceil(5 / World_Options.coinbundlequantity))) set_rule(world.get_location("Sexy Outfits Pack", player), lambda state: state.has("DLC Quest: Coin Bundle", player, - math.ceil(5 / World_Options[Options.CoinSanityRange]))) + math.ceil(5 / World_Options.coinbundlequantity))) set_rule(world.get_location("Top Hat Pack", player), lambda state: state.has("DLC Quest: Coin Bundle", player, - math.ceil(5 / World_Options[Options.CoinSanityRange]))) + math.ceil(5 / World_Options.coinbundlequantity))) set_rule(world.get_location("Map Pack", player), lambda state: state.has("DLC Quest: Coin Bundle", player, - math.ceil(140 / World_Options[Options.CoinSanityRange]))) + math.ceil(140 / World_Options.coinbundlequantity))) set_rule(world.get_location("Gun Pack", player), lambda state: state.has("DLC Quest: Coin Bundle", player, - math.ceil(75 / World_Options[Options.CoinSanityRange]))) + math.ceil(75 / World_Options.coinbundlequantity))) set_rule(world.get_location("The Zombie Pack", player), lambda state: state.has("DLC Quest: Coin Bundle", player, - math.ceil(5 / World_Options[Options.CoinSanityRange]))) + math.ceil(5 / World_Options.coinbundlequantity))) set_rule(world.get_location("Night Map Pack", player), lambda state: state.has("DLC Quest: Coin Bundle", player, - math.ceil(75 / World_Options[Options.CoinSanityRange]))) + math.ceil(75 / World_Options.coinbundlequantity))) set_rule(world.get_location("Psychological Warfare Pack", player), lambda state: state.has("DLC Quest: Coin Bundle", player, - math.ceil(50 / World_Options[Options.CoinSanityRange]))) + math.ceil(50 / World_Options.coinbundlequantity))) set_rule(world.get_location("Armor for your Horse Pack", player), lambda state: state.has("DLC Quest: Coin Bundle", player, - math.ceil(250 / World_Options[Options.CoinSanityRange]))) + math.ceil(250 / World_Options.coinbundlequantity))) set_rule(world.get_location("Finish the Fight Pack", player), lambda state: state.has("DLC Quest: Coin Bundle", player, - math.ceil(5 / World_Options[Options.CoinSanityRange]))) + math.ceil(5 / World_Options.coinbundlequantity))) def set_basic_self_funded_purchase_rules(World_Options, has_enough_coin, player, world): - if World_Options[Options.CoinSanity] != Options.CoinSanity.option_none: + if World_Options.coinsanity != Options.CoinSanity.option_none: return set_rule(world.get_location("Movement Pack", player), has_enough_coin(player, 4)) @@ -232,17 +232,17 @@ def set_basic_self_funded_purchase_rules(World_Options, has_enough_coin, player, def self_basic_win_condition(World_Options, player, world): - if World_Options[Options.EndingChoice] == Options.EndingChoice.option_any: + if World_Options.ending_choice == Options.EndingChoice.option_any: set_rule(world.get_location("Winning Basic", player), lambda state: state.has("Finish the Fight Pack", player)) - if World_Options[Options.EndingChoice] == Options.EndingChoice.option_true: + if World_Options.ending_choice == Options.EndingChoice.option_true: set_rule(world.get_location("Winning Basic", player), lambda state: state.has("Armor for your Horse Pack", player) and state.has("Finish the Fight Pack", player)) def set_lfod_rules(World_Options, has_enough_coin_freemium, player, world): - if World_Options[Options.Campaign] == Options.Campaign.option_basic: + if World_Options.campaign == Options.Campaign.option_basic: return set_lfod_entrance_rules(player, world) set_boss_door_requirements_rules(player, world) @@ -297,7 +297,7 @@ def set_boss_door_requirements_rules(player, world): def set_lfod_self_obtained_items_rules(World_Options, player, world): - if World_Options[Options.ItemShuffle] != Options.ItemShuffle.option_disabled: + if World_Options.item_shuffle != Options.ItemShuffle.option_disabled: return set_rule(world.get_entrance("Vines", player), lambda state: state.has("Incredibly Important Pack", player)) @@ -309,7 +309,7 @@ def set_lfod_self_obtained_items_rules(World_Options, player, world): def set_lfod_shuffled_items_rules(World_Options, player, world): - if World_Options[Options.ItemShuffle] != Options.ItemShuffle.option_shuffled: + if World_Options.item_shuffle != Options.ItemShuffle.option_shuffled: return set_rule(world.get_entrance("Vines", player), lambda state: state.has("Wooden Sword", player) or state.has("Pickaxe", player)) @@ -328,79 +328,79 @@ def set_lfod_shuffled_items_rules(World_Options, player, world): def self_lfod_coinsanity_funded_purchase_rules(World_Options, has_enough_coin_freemium, player, world): - if World_Options[Options.CoinSanity] != Options.CoinSanity.option_coin: + if World_Options.coinsanity != Options.CoinSanity.option_coin: return - number_of_bundle = math.floor(889 / World_Options[Options.CoinSanityRange]) + number_of_bundle = math.floor(889 / World_Options.coinbundlequantity) for i in range(number_of_bundle): item_coin_freemium = "Live Freemium or Die: number Coin" - item_coin_loc_freemium = re.sub("number", str(World_Options[Options.CoinSanityRange] * (i + 1)), + item_coin_loc_freemium = re.sub("number", str(World_Options.coinbundlequantity * (i + 1)), item_coin_freemium) set_rule(world.get_location(item_coin_loc_freemium, player), - has_enough_coin_freemium(player, World_Options[Options.CoinSanityRange] * (i + 1))) - if 889 % World_Options[Options.CoinSanityRange] != 0: + has_enough_coin_freemium(player, World_Options.coinbundlequantity * (i + 1))) + if 889 % World_Options.coinbundlequantity != 0: set_rule(world.get_location("Live Freemium or Die: 889 Coin", player), has_enough_coin_freemium(player, 889)) add_rule(world.get_entrance("Boss Door", player), lambda state: state.has("Live Freemium or Die: Coin Bundle", player, - math.ceil(889 / World_Options[Options.CoinSanityRange]))) + math.ceil(889 / World_Options.coinbundlequantity))) set_rule(world.get_location("Particles Pack", player), lambda state: state.has("Live Freemium or Die: Coin Bundle", player, - math.ceil(5 / World_Options[Options.CoinSanityRange]))) + math.ceil(5 / World_Options.coinbundlequantity))) set_rule(world.get_location("Day One Patch Pack", player), lambda state: state.has("Live Freemium or Die: Coin Bundle", player, - math.ceil(5 / World_Options[Options.CoinSanityRange]))) + math.ceil(5 / World_Options.coinbundlequantity))) set_rule(world.get_location("Checkpoint Pack", player), lambda state: state.has("Live Freemium or Die: Coin Bundle", player, - math.ceil(5 / World_Options[Options.CoinSanityRange]))) + math.ceil(5 / World_Options.coinbundlequantity))) set_rule(world.get_location("Incredibly Important Pack", player), lambda state: state.has("Live Freemium or Die: Coin Bundle", player, - math.ceil(15 / World_Options[Options.CoinSanityRange]))) + math.ceil(15 / World_Options.coinbundlequantity))) set_rule(world.get_location("Wall Jump Pack", player), lambda state: state.has("Live Freemium or Die: Coin Bundle", player, - math.ceil(35 / World_Options[Options.CoinSanityRange]))) + math.ceil(35 / World_Options.coinbundlequantity))) set_rule(world.get_location("Health Bar Pack", player), lambda state: state.has("Live Freemium or Die: Coin Bundle", player, - math.ceil(5 / World_Options[Options.CoinSanityRange]))) + math.ceil(5 / World_Options.coinbundlequantity))) set_rule(world.get_location("Parallax Pack", player), lambda state: state.has("Live Freemium or Die: Coin Bundle", player, - math.ceil(5 / World_Options[Options.CoinSanityRange]))) + math.ceil(5 / World_Options.coinbundlequantity))) set_rule(world.get_location("Harmless Plants Pack", player), lambda state: state.has("Live Freemium or Die: Coin Bundle", player, - math.ceil(130 / World_Options[Options.CoinSanityRange]))) + math.ceil(130 / World_Options.coinbundlequantity))) set_rule(world.get_location("Death of Comedy Pack", player), lambda state: state.has("Live Freemium or Die: Coin Bundle", player, - math.ceil(15 / World_Options[Options.CoinSanityRange]))) + math.ceil(15 / World_Options.coinbundlequantity))) set_rule(world.get_location("Canadian Dialog Pack", player), lambda state: state.has("Live Freemium or Die: Coin Bundle", player, - math.ceil(10 / World_Options[Options.CoinSanityRange]))) + math.ceil(10 / World_Options.coinbundlequantity))) set_rule(world.get_location("DLC NPC Pack", player), lambda state: state.has("Live Freemium or Die: Coin Bundle", player, - math.ceil(15 / World_Options[Options.CoinSanityRange]))) + math.ceil(15 / World_Options.coinbundlequantity))) set_rule(world.get_location("Cut Content Pack", player), lambda state: state.has("Live Freemium or Die: Coin Bundle", player, - math.ceil(40 / World_Options[Options.CoinSanityRange]))) + math.ceil(40 / World_Options.coinbundlequantity))) set_rule(world.get_location("Name Change Pack", player), lambda state: state.has("Live Freemium or Die: Coin Bundle", player, - math.ceil(150 / World_Options[Options.CoinSanityRange]))) + math.ceil(150 / World_Options.coinbundlequantity))) set_rule(world.get_location("Season Pass", player), lambda state: state.has("Live Freemium or Die: Coin Bundle", player, - math.ceil(199 / World_Options[Options.CoinSanityRange]))) + math.ceil(199 / World_Options.coinbundlequantity))) set_rule(world.get_location("High Definition Next Gen Pack", player), lambda state: state.has("Live Freemium or Die: Coin Bundle", player, - math.ceil(20 / World_Options[Options.CoinSanityRange]))) + math.ceil(20 / World_Options.coinbundlequantity))) set_rule(world.get_location("Increased HP Pack", player), lambda state: state.has("Live Freemium or Die: Coin Bundle", player, - math.ceil(10 / World_Options[Options.CoinSanityRange]))) + math.ceil(10 / World_Options.coinbundlequantity))) set_rule(world.get_location("Remove Ads Pack", player), lambda state: state.has("Live Freemium or Die: Coin Bundle", player, - math.ceil(25 / World_Options[Options.CoinSanityRange]))) + math.ceil(25 / World_Options.coinbundlequantity))) def set_lfod_self_funded_purchase_rules(World_Options, has_enough_coin_freemium, player, world): - if World_Options[Options.CoinSanity] != Options.CoinSanity.option_none: + if World_Options.coinsanity != Options.CoinSanity.option_none: return add_rule(world.get_entrance("Boss Door", player), has_enough_coin_freemium(player, 889)) @@ -442,10 +442,10 @@ def set_lfod_self_funded_purchase_rules(World_Options, has_enough_coin_freemium, def set_completion_condition(World_Options, player, world): - if World_Options[Options.Campaign] == Options.Campaign.option_basic: + if World_Options.campaign == Options.Campaign.option_basic: world.completion_condition[player] = lambda state: state.has("Victory Basic", player) - if World_Options[Options.Campaign] == Options.Campaign.option_live_freemium_or_die: + if World_Options.campaign == Options.Campaign.option_live_freemium_or_die: world.completion_condition[player] = lambda state: state.has("Victory Freemium", player) - if World_Options[Options.Campaign] == Options.Campaign.option_both: + if World_Options.campaign == Options.Campaign.option_both: world.completion_condition[player] = lambda state: state.has("Victory Basic", player) and state.has( "Victory Freemium", player) diff --git a/worlds/dlcquest/__init__.py b/worlds/dlcquest/__init__.py index 9569d0efcc..392eac7796 100644 --- a/worlds/dlcquest/__init__.py +++ b/worlds/dlcquest/__init__.py @@ -1,12 +1,13 @@ -from typing import Dict, Any, Iterable, Optional, Union +from typing import Union + from BaseClasses import Tutorial -from worlds.AutoWorld import World, WebWorld -from .Items import DLCQuestItem, item_table, ItemData, create_items -from .Locations import location_table, DLCQuestLocation -from .Options import DLCQuest_options, DLCQuestOptions, fetch_options -from .Rules import set_rules -from .Regions import create_regions +from worlds.AutoWorld import WebWorld, World from . import Options +from .Items import DLCQuestItem, ItemData, create_items, item_table +from .Locations import DLCQuestLocation, location_table +from .Options import DLCQuestOptions +from .Regions import create_regions +from .Rules import set_rules client_version = 0 @@ -35,10 +36,8 @@ class DLCqworld(World): data_version = 1 - option_definitions = DLCQuest_options - - def generate_early(self): - self.options = fetch_options(self.multiworld, self.player) + options_dataclass = DLCQuestOptions + options: DLCQuestOptions def create_regions(self): create_regions(self.multiworld, self.player, self.options) @@ -68,8 +67,8 @@ class DLCqworld(World): self.multiworld.itempool.remove(item) def precollect_coinsanity(self): - if self.options[Options.Campaign] == Options.Campaign.option_basic: - if self.options[Options.CoinSanity] == Options.CoinSanity.option_coin and self.options[Options.CoinSanityRange] >= 5: + if self.options.campaign == Options.Campaign.option_basic: + if self.options.coinsanity == Options.CoinSanity.option_coin and self.options.coinbundlequantity >= 5: self.multiworld.push_precollected(self.create_item("Movement Pack")) @@ -80,12 +79,11 @@ class DLCqworld(World): return DLCQuestItem(item.name, item.classification, item.code, self.player) def fill_slot_data(self): - return { - "death_link": self.multiworld.death_link[self.player].value, - "ending_choice": self.multiworld.ending_choice[self.player].value, - "campaign": self.multiworld.campaign[self.player].value, - "coinsanity": self.multiworld.coinsanity[self.player].value, - "coinbundlerange": self.multiworld.coinbundlequantity[self.player].value, - "item_shuffle": self.multiworld.item_shuffle[self.player].value, - "seed": self.multiworld.per_slot_randoms[self.player].randrange(99999999) - } + options_dict = self.options.as_dict( + "death_link", "ending_choice", "campaign", "coinsanity", "item_shuffle" + ) + options_dict.update({ + "coinbundlerange": self.options.coinbundlequantity.value, + "seed": self.random.randrange(99999999) + }) + return options_dict diff --git a/worlds/messenger/__init__.py b/worlds/messenger/__init__.py index c7fd253632..4be699e9cf 100644 --- a/worlds/messenger/__init__.py +++ b/worlds/messenger/__init__.py @@ -4,7 +4,7 @@ from typing import Any, Dict, List, Optional from BaseClasses import CollectionState, Item, ItemClassification, Tutorial from worlds.AutoWorld import WebWorld, World from .constants import ALL_ITEMS, ALWAYS_LOCATIONS, BOSS_LOCATIONS, FILLER, NOTES, PHOBEKINS -from .options import Goal, Logic, NotesNeeded, PowerSeals, messenger_options +from .options import Goal, Logic, MessengerOptions, NotesNeeded, PowerSeals from .regions import MEGA_SHARDS, REGIONS, REGION_CONNECTIONS, SEALS from .rules import MessengerHardRules, MessengerOOBRules, MessengerRules from .shop import FIGURINES, SHOP_ITEMS, shuffle_shop_prices @@ -44,7 +44,8 @@ class MessengerWorld(World): "Phobekin": set(PHOBEKINS), } - option_definitions = messenger_options + options_dataclass = MessengerOptions + options: MessengerOptions base_offset = 0xADD_000 item_name_to_id = {item: item_id @@ -74,9 +75,9 @@ class MessengerWorld(World): _filler_items: List[str] def generate_early(self) -> None: - if self.multiworld.goal[self.player] == Goal.option_power_seal_hunt: - self.multiworld.shuffle_seals[self.player].value = PowerSeals.option_true - self.total_seals = self.multiworld.total_seals[self.player].value + if self.options.goal == Goal.option_power_seal_hunt: + self.options.shuffle_seals.value = PowerSeals.option_true + self.total_seals = self.options.total_seals.value self.shop_prices, self.figurine_prices = shuffle_shop_prices(self) @@ -87,7 +88,7 @@ class MessengerWorld(World): def create_items(self) -> None: # create items that are always in the item pool - itempool = [ + itempool: List[MessengerItem] = [ self.create_item(item) for item in self.item_name_to_id if item not in @@ -97,13 +98,13 @@ class MessengerWorld(World): } and "Time Shard" not in item ] - if self.multiworld.goal[self.player] == Goal.option_open_music_box: + if self.options.goal == Goal.option_open_music_box: # make a list of all notes except those in the player's defined starting inventory, and adjust the # amount we need to put in the itempool and precollect based on that notes = [note for note in NOTES if note not in self.multiworld.precollected_items[self.player]] self.random.shuffle(notes) precollected_notes_amount = NotesNeeded.range_end - \ - self.multiworld.notes_needed[self.player] - \ + self.options.notes_needed - \ (len(NOTES) - len(notes)) if precollected_notes_amount: for note in notes[:precollected_notes_amount]: @@ -111,15 +112,14 @@ class MessengerWorld(World): notes = notes[precollected_notes_amount:] itempool += [self.create_item(note) for note in notes] - elif self.multiworld.goal[self.player] == Goal.option_power_seal_hunt: + elif self.options.goal == Goal.option_power_seal_hunt: total_seals = min(len(self.multiworld.get_unfilled_locations(self.player)) - len(itempool), - self.multiworld.total_seals[self.player].value) + self.options.total_seals.value) if total_seals < self.total_seals: logging.warning(f"Not enough locations for total seals setting " - f"({self.multiworld.total_seals[self.player].value}). Adjusting to {total_seals}") + f"({self.options.total_seals}). Adjusting to {total_seals}") self.total_seals = total_seals - self.required_seals =\ - int(self.multiworld.percent_seals_required[self.player].value / 100 * self.total_seals) + self.required_seals = int(self.options.percent_seals_required.value / 100 * self.total_seals) seals = [self.create_item("Power Seal") for _ in range(self.total_seals)] for i in range(self.required_seals): @@ -138,7 +138,7 @@ class MessengerWorld(World): self.multiworld.itempool += itempool def set_rules(self) -> None: - logic = self.multiworld.logic_level[self.player] + logic = self.options.logic_level if logic == Logic.option_normal: MessengerRules(self).set_messenger_rules() elif logic == Logic.option_hard: @@ -151,12 +151,12 @@ class MessengerWorld(World): figure_prices = {FIGURINES[item].internal_name: price for item, price in self.figurine_prices.items()} return { - "deathlink": self.multiworld.death_link[self.player].value, - "goal": self.multiworld.goal[self.player].current_key, - "music_box": self.multiworld.music_box[self.player].value, + "deathlink": self.options.death_link.value, + "goal": self.options.goal.current_key, + "music_box": self.options.music_box.value, "required_seals": self.required_seals, - "mega_shards": self.multiworld.shuffle_shards[self.player].value, - "logic": self.multiworld.logic_level[self.player].current_key, + "mega_shards": self.options.shuffle_shards.value, + "logic": self.options.logic_level.current_key, "shop": shop_prices, "figures": figure_prices, "max_price": self.total_shards, @@ -175,7 +175,7 @@ class MessengerWorld(World): item_id: Optional[int] = self.item_name_to_id.get(name, None) override_prog = getattr(self, "multiworld") is not None and \ name in {"Windmill Shuriken"} and \ - self.multiworld.logic_level[self.player] > Logic.option_normal + self.options.logic_level > Logic.option_normal count = 0 if "Time Shard " in name: count = int(name.strip("Time Shard ()")) diff --git a/worlds/messenger/constants.py b/worlds/messenger/constants.py index e6608be043..f05d276cea 100644 --- a/worlds/messenger/constants.py +++ b/worlds/messenger/constants.py @@ -1,7 +1,7 @@ +from .shop import FIGURINES, SHOP_ITEMS + # items # listing individual groups first for easy lookup -from .shop import SHOP_ITEMS, FIGURINES - NOTES = [ "Key of Hope", "Key of Chaos", diff --git a/worlds/messenger/options.py b/worlds/messenger/options.py index 8e8b61a204..1da544bee7 100644 --- a/worlds/messenger/options.py +++ b/worlds/messenger/options.py @@ -1,7 +1,10 @@ +from dataclasses import dataclass from typing import Dict -from schema import Schema, Or, And, Optional -from Options import DefaultOnToggle, DeathLink, Range, Accessibility, Choice, Toggle, OptionDict, StartInventoryPool +from schema import And, Optional, Or, Schema + +from Options import Accessibility, Choice, DeathLink, DefaultOnToggle, OptionDict, PerGameCommonOptions, Range, \ + StartInventoryPool, Toggle class MessengerAccessibility(Accessibility): @@ -129,18 +132,19 @@ class PlannedShopPrices(OptionDict): }) -messenger_options = { - "accessibility": MessengerAccessibility, - "start_inventory": StartInventoryPool, - "logic_level": Logic, - "shuffle_seals": PowerSeals, - "shuffle_shards": MegaShards, - "goal": Goal, - "music_box": MusicBox, - "notes_needed": NotesNeeded, - "total_seals": AmountSeals, - "percent_seals_required": RequiredSeals, - "shop_price": ShopPrices, - "shop_price_plan": PlannedShopPrices, - "death_link": DeathLink, -} +@dataclass +class MessengerOptions(PerGameCommonOptions): + accessibility: MessengerAccessibility + start_inventory: StartInventoryPool + logic_level: Logic + shuffle_seals: PowerSeals + shuffle_shards: MegaShards + goal: Goal + music_box: MusicBox + notes_needed: NotesNeeded + total_seals: AmountSeals + percent_seals_required: RequiredSeals + shop_price: ShopPrices + shop_price_plan: PlannedShopPrices + death_link: DeathLink + diff --git a/worlds/messenger/regions.py b/worlds/messenger/regions.py index 88579a9704..28750b949e 100644 --- a/worlds/messenger/regions.py +++ b/worlds/messenger/regions.py @@ -1,4 +1,4 @@ -from typing import Dict, Set, List +from typing import Dict, List, Set REGIONS: Dict[str, List[str]] = { "Menu": [], diff --git a/worlds/messenger/rules.py b/worlds/messenger/rules.py index 65a99627f2..c9bd9b8625 100644 --- a/worlds/messenger/rules.py +++ b/worlds/messenger/rules.py @@ -1,9 +1,9 @@ -from typing import Dict, Callable, TYPE_CHECKING +from typing import Callable, Dict, TYPE_CHECKING -from BaseClasses import CollectionState, MultiWorld -from worlds.generic.Rules import set_rule, allow_self_locking_items, add_rule -from .options import MessengerAccessibility, Goal +from BaseClasses import CollectionState +from worlds.generic.Rules import add_rule, allow_self_locking_items, set_rule from .constants import NOTES, PHOBEKINS +from .options import Goal, MessengerAccessibility from .subclasses import MessengerShopLocation if TYPE_CHECKING: @@ -145,13 +145,13 @@ class MessengerRules: if region.name == "The Shop": for loc in [location for location in region.locations if isinstance(location, MessengerShopLocation)]: loc.access_rule = loc.can_afford - if multiworld.goal[self.player] == Goal.option_power_seal_hunt: + if self.world.options.goal == Goal.option_power_seal_hunt: set_rule(multiworld.get_entrance("Tower HQ -> Music Box", self.player), lambda state: state.has("Shop Chest", self.player)) multiworld.completion_condition[self.player] = lambda state: state.has("Rescue Phantom", self.player) if multiworld.accessibility[self.player] > MessengerAccessibility.option_locations: - set_self_locking_items(multiworld, self.player) + set_self_locking_items(self.world, self.player) class MessengerHardRules(MessengerRules): @@ -212,9 +212,9 @@ class MessengerHardRules(MessengerRules): def set_messenger_rules(self) -> None: super().set_messenger_rules() for loc, rule in self.extra_rules.items(): - if not self.world.multiworld.shuffle_seals[self.player] and "Seal" in loc: + if not self.world.options.shuffle_seals and "Seal" in loc: continue - if not self.world.multiworld.shuffle_shards[self.player] and "Shard" in loc: + if not self.world.options.shuffle_shards and "Shard" in loc: continue add_rule(self.world.multiworld.get_location(loc, self.player), rule, "or") @@ -249,20 +249,22 @@ class MessengerOOBRules(MessengerRules): def set_messenger_rules(self) -> None: super().set_messenger_rules() self.world.multiworld.completion_condition[self.player] = lambda state: True - self.world.multiworld.accessibility[self.player].value = MessengerAccessibility.option_minimal + self.world.options.accessibility.value = MessengerAccessibility.option_minimal -def set_self_locking_items(multiworld: MultiWorld, player: int) -> None: +def set_self_locking_items(world: MessengerWorld, player: int) -> None: + multiworld = world.multiworld + # do the ones for seal shuffle on and off first allow_self_locking_items(multiworld.get_location("Searing Crags - Key of Strength", player), "Power Thistle") allow_self_locking_items(multiworld.get_location("Sunken Shrine - Key of Love", player), "Sun Crest", "Moon Crest") allow_self_locking_items(multiworld.get_location("Corrupted Future - Key of Courage", player), "Demon King Crown") # add these locations when seals are shuffled - if multiworld.shuffle_seals[player]: + if world.options.shuffle_seals: allow_self_locking_items(multiworld.get_location("Elemental Skylands Seal - Water", player), "Currents Master") # add these locations when seals and shards aren't shuffled - elif not multiworld.shuffle_shards[player]: + elif not world.options.shuffle_shards: for entrance in multiworld.get_region("Cloud Ruins", player).entrances: entrance.access_rule = lambda state: state.has("Wingsuit", player) or state.has("Rope Dart", player) allow_self_locking_items(multiworld.get_region("Forlorn Temple", player), *PHOBEKINS) diff --git a/worlds/messenger/shop.py b/worlds/messenger/shop.py index f0915f5d02..3c8c7bf6f2 100644 --- a/worlds/messenger/shop.py +++ b/worlds/messenger/shop.py @@ -74,8 +74,8 @@ FIGURINES: Dict[str, ShopData] = { def shuffle_shop_prices(world: MessengerWorld) -> Tuple[Dict[str, int], Dict[str, int]]: - shop_price_mod = world.multiworld.shop_price[world.player].value - shop_price_planned = world.multiworld.shop_price_plan[world.player] + shop_price_mod = world.options.shop_price.value + shop_price_planned = world.options.shop_price_plan shop_prices: Dict[str, int] = {} figurine_prices: Dict[str, int] = {} diff --git a/worlds/messenger/subclasses.py b/worlds/messenger/subclasses.py index e6a18b2e4e..c5d90e00c8 100644 --- a/worlds/messenger/subclasses.py +++ b/worlds/messenger/subclasses.py @@ -9,16 +9,15 @@ from .shop import FIGURINES, PROG_SHOP_ITEMS, SHOP_ITEMS, USEFUL_SHOP_ITEMS if TYPE_CHECKING: from . import MessengerWorld -else: - MessengerWorld = object class MessengerRegion(Region): - def __init__(self, name: str, world: MessengerWorld) -> None: + + def __init__(self, name: str, world: "MessengerWorld") -> None: super().__init__(name, world.player, world.multiworld) locations = [loc for loc in REGIONS[self.name]] if self.name == "The Shop": - if self.multiworld.goal[self.player] > Goal.option_open_music_box: + if world.options.goal > Goal.option_open_music_box: locations.append("Shop Chest") shop_locations = {f"The Shop - {shop_loc}": world.location_name_to_id[f"The Shop - {shop_loc}"] for shop_loc in SHOP_ITEMS} @@ -26,9 +25,9 @@ class MessengerRegion(Region): self.add_locations(shop_locations, MessengerShopLocation) elif self.name == "Tower HQ": locations.append("Money Wrench") - if self.multiworld.shuffle_seals[self.player] and self.name in SEALS: + if world.options.shuffle_seals and self.name in SEALS: locations += [seal_loc for seal_loc in SEALS[self.name]] - if self.multiworld.shuffle_shards[self.player] and self.name in MEGA_SHARDS: + if world.options.shuffle_shards and self.name in MEGA_SHARDS: locations += [shard for shard in MEGA_SHARDS[self.name]] loc_dict = {loc: world.location_name_to_id[loc] if loc in world.location_name_to_id else None for loc in locations} @@ -49,7 +48,7 @@ class MessengerShopLocation(MessengerLocation): @cached_property def cost(self) -> int: name = self.name.replace("The Shop - ", "") # TODO use `remove_prefix` when 3.8 finally gets dropped - world: MessengerWorld = self.parent_region.multiworld.worlds[self.player] + world = cast("MessengerWorld", self.parent_region.multiworld.worlds[self.player]) # short circuit figurines which all require demon's bane be purchased, but nothing else if "Figurine" in name: return world.figurine_prices[name] +\ @@ -70,9 +69,8 @@ class MessengerShopLocation(MessengerLocation): return world.shop_prices[name] def can_afford(self, state: CollectionState) -> bool: - world: MessengerWorld = state.multiworld.worlds[self.player] - cost = self.cost - can_afford = state.has("Shards", self.player, min(cost, world.total_shards)) + world = cast("MessengerWorld", state.multiworld.worlds[self.player]) + can_afford = state.has("Shards", self.player, min(self.cost, world.total_shards)) if "Figurine" in self.name: can_afford = state.has("Money Wrench", self.player) and can_afford\ and state.can_reach("Money Wrench", "Location", self.player) diff --git a/worlds/oot/__init__.py b/worlds/oot/__init__.py index 655f4989b2..539abd9674 100644 --- a/worlds/oot/__init__.py +++ b/worlds/oot/__init__.py @@ -32,7 +32,7 @@ from .Cosmetics import patch_cosmetics from Utils import get_options from BaseClasses import MultiWorld, CollectionState, Tutorial, LocationProgressType -from Options import Range, Toggle, VerifyKeys +from Options import Range, Toggle, VerifyKeys, Accessibility from Fill import fill_restrictive, fast_fill, FillError from worlds.generic.Rules import exclusion_rules, add_item_rule from ..AutoWorld import World, AutoLogicRegister, WebWorld @@ -286,7 +286,7 @@ class OOTWorld(World): # No Logic forces all tricks on, prog balancing off and beatable-only elif self.logic_rules == 'no_logic': self.multiworld.progression_balancing[self.player].value = False - self.multiworld.accessibility[self.player] = self.multiworld.accessibility[self.player].from_text("minimal") + self.multiworld.accessibility[self.player].value = Accessibility.option_minimal for trick in normalized_name_tricks.values(): setattr(self, trick['name'], True) diff --git a/worlds/overcooked2/Options.py b/worlds/overcooked2/Options.py index 9ddcf5e85f..18a2c18ed4 100644 --- a/worlds/overcooked2/Options.py +++ b/worlds/overcooked2/Options.py @@ -1,6 +1,7 @@ +from dataclasses import dataclass from enum import IntEnum from typing import TypedDict -from Options import DefaultOnToggle, Toggle, Range, Choice, OptionSet +from Options import DefaultOnToggle, PerGameCommonOptions, Toggle, Range, Choice, OptionSet from .Overcooked2Levels import Overcooked2Dlc class LocationBalancingMode(IntEnum): @@ -167,32 +168,30 @@ class StarThresholdScale(Range): default = 35 -overcooked_options = { +@dataclass +class OC2Options(PerGameCommonOptions): # generator options - "location_balancing": LocationBalancing, - "ramp_tricks": RampTricks, - + location_balancing: LocationBalancing + ramp_tricks: RampTricks + # deathlink - "deathlink": DeathLink, - + deathlink: DeathLink + # randomization options - "shuffle_level_order": ShuffleLevelOrder, - "include_dlcs": DLCOptionSet, - "include_horde_levels": IncludeHordeLevels, - "prep_levels": PrepLevels, - "kevin_levels": KevinLevels, - + shuffle_level_order: ShuffleLevelOrder + include_dlcs: DLCOptionSet + include_horde_levels: IncludeHordeLevels + prep_levels: PrepLevels + kevin_levels: KevinLevels + # quality of life options - "fix_bugs": FixBugs, - "shorter_level_duration": ShorterLevelDuration, - "short_horde_levels": ShortHordeLevels, - "always_preserve_cooking_progress": AlwaysPreserveCookingProgress, - "always_serve_oldest_order": AlwaysServeOldestOrder, - "display_leaderboard_scores": DisplayLeaderboardScores, - + fix_bugs: FixBugs + shorter_level_duration: ShorterLevelDuration + short_horde_levels: ShortHordeLevels + always_preserve_cooking_progress: AlwaysPreserveCookingProgress + always_serve_oldest_order: AlwaysServeOldestOrder + display_leaderboard_scores: DisplayLeaderboardScores + # difficulty settings - "stars_to_win": StarsToWin, - "star_threshold_scale": StarThresholdScale, -} - -OC2Options = TypedDict("OC2Options", {option.__name__: option for option in overcooked_options.values()}) + stars_to_win: StarsToWin + star_threshold_scale: StarThresholdScale diff --git a/worlds/overcooked2/__init__.py b/worlds/overcooked2/__init__.py index afffa744fa..2bf523b347 100644 --- a/worlds/overcooked2/__init__.py +++ b/worlds/overcooked2/__init__.py @@ -6,7 +6,7 @@ from worlds.AutoWorld import World, WebWorld from .Overcooked2Levels import Overcooked2Dlc, Overcooked2Level, Overcooked2GenericLevel from .Locations import Overcooked2Location, oc2_location_name_to_id, oc2_location_id_to_name -from .Options import overcooked_options, OC2Options, OC2OnToggle, LocationBalancingMode, DeathLinkMode +from .Options import OC2Options, OC2OnToggle, LocationBalancingMode, DeathLinkMode from .Items import item_table, Overcooked2Item, item_name_to_id, item_id_to_name, item_to_unlock_event, item_frequencies, dlc_exclusives from .Logic import has_requirements_for_level_star, has_requirements_for_level_access, level_shuffle_factory, is_item_progression, is_useful @@ -47,7 +47,6 @@ class Overcooked2World(World): game = "Overcooked! 2" web = Overcooked2Web() required_client_version = (0, 3, 8) - option_definitions = overcooked_options topology_present: bool = False data_version = 3 @@ -57,13 +56,14 @@ class Overcooked2World(World): location_id_to_name = oc2_location_id_to_name location_name_to_id = oc2_location_name_to_id - options: Dict[str, Any] + options_dataclass = OC2Options + options: OC2Options itempool: List[Overcooked2Item] # Helper Functions def is_level_horde(self, level_id: int) -> bool: - return self.options["IncludeHordeLevels"] and \ + return self.options.include_horde_levels and \ (self.level_mapping is not None) and \ level_id in self.level_mapping.keys() and \ self.level_mapping[level_id].is_horde @@ -145,11 +145,6 @@ class Overcooked2World(World): location ) - def get_options(self) -> Dict[str, Any]: - return OC2Options({option.__name__: getattr(self.multiworld, name)[self.player].result - if issubclass(option, OC2OnToggle) else getattr(self.multiworld, name)[self.player].value - for name, option in overcooked_options.items()}) - def get_n_random_locations(self, n: int) -> List[int]: """Return a list of n random non-repeating level locations""" levels = list() @@ -160,7 +155,7 @@ class Overcooked2World(World): for level in Overcooked2Level(): if level.level_id == 36: continue - elif not self.options["KevinLevels"] and level.level_id > 36: + elif not self.options.kevin_levels and level.level_id > 36: break levels.append(level.level_id) @@ -231,26 +226,25 @@ class Overcooked2World(World): def generate_early(self): self.player_name = self.multiworld.player_name[self.player] - self.options = self.get_options() # 0.0 to 1.0 where 1.0 is World Record - self.star_threshold_scale = self.options["StarThresholdScale"] / 100.0 + self.star_threshold_scale = self.options.star_threshold_scale / 100.0 # Parse DLCOptionSet back into enums - self.enabled_dlc = {Overcooked2Dlc(x) for x in self.options["DLCOptionSet"]} + self.enabled_dlc = {Overcooked2Dlc(x) for x in self.options.include_dlcs.value} # Generate level unlock requirements such that the levels get harder to unlock # the further the game has progressed, and levels progress radially rather than linearly - self.level_unlock_counts = level_unlock_requirement_factory(self.options["StarsToWin"]) + self.level_unlock_counts = level_unlock_requirement_factory(self.options.stars_to_win.value) # Assign new kitchens to each spot on the overworld using pure random chance and nothing else - if self.options["ShuffleLevelOrder"]: + if self.options.shuffle_level_order: self.level_mapping = \ level_shuffle_factory( self.multiworld.random, - self.options["PrepLevels"] != PrepLevelMode.excluded, - self.options["IncludeHordeLevels"], - self.options["KevinLevels"], + self.options.prep_levels != PrepLevelMode.excluded, + self.options.include_horde_levels.result, + self.options.kevin_levels.result, self.enabled_dlc, self.player_name, ) @@ -277,7 +271,7 @@ class Overcooked2World(World): # Create and populate "regions" (a.k.a. levels) for level in Overcooked2Level(): - if not self.options["KevinLevels"] and level.level_id > 36: + if not self.options.kevin_levels and level.level_id > 36: break # Create Region (e.g. "1-1") @@ -336,7 +330,7 @@ class Overcooked2World(World): level_access_rule: Callable[[CollectionState], bool] = \ lambda state, level_name=level.level_name, previous_level_completed_event_name=previous_level_completed_event_name, required_star_count=required_star_count: \ - has_requirements_for_level_access(state, level_name, previous_level_completed_event_name, required_star_count, self.options["RampTricks"], self.player) + has_requirements_for_level_access(state, level_name, previous_level_completed_event_name, required_star_count, self.options.ramp_tricks.result, self.player) self.connect_regions("Overworld", level.level_name, level_access_rule) # Level --> Overworld @@ -369,11 +363,11 @@ class Overcooked2World(World): # Item is always useless with these settings continue - if not self.options["IncludeHordeLevels"] and item_name in ["Calmer Unbread", "Coin Purse"]: + if not self.options.include_horde_levels and item_name in ["Calmer Unbread", "Coin Purse"]: # skip horde-specific items if no horde levels continue - if not self.options["KevinLevels"]: + if not self.options.kevin_levels: if item_name.startswith("Kevin"): # skip kevin items if no kevin levels continue @@ -382,7 +376,7 @@ class Overcooked2World(World): # skip dark green ramp if there's no Kevin-1 to reveal it continue - if is_item_progression(item_name, self.level_mapping, self.options["KevinLevels"]): + if is_item_progression(item_name, self.level_mapping, self.options.kevin_levels): # progression.append(item_name) classification = ItemClassification.progression else: @@ -404,7 +398,7 @@ class Overcooked2World(World): # Fill any free space with filler pool_count = len(oc2_location_name_to_id) - if not self.options["KevinLevels"]: + if not self.options.kevin_levels: pool_count -= 8 while len(self.itempool) < pool_count: @@ -416,7 +410,7 @@ class Overcooked2World(World): def place_events(self): # Add Events (Star Acquisition) for level in Overcooked2Level(): - if not self.options["KevinLevels"] and level.level_id > 36: + if not self.options.kevin_levels and level.level_id > 36: break if level.level_id != 36: @@ -449,7 +443,7 @@ class Overcooked2World(World): # Serialize Level Order story_level_order = dict() - if self.options["ShuffleLevelOrder"]: + if self.options.shuffle_level_order: for level_id in self.level_mapping: level: Overcooked2GenericLevel = self.level_mapping[level_id] story_level_order[str(level_id)] = { @@ -481,7 +475,7 @@ class Overcooked2World(World): level_unlock_requirements[str(level_id)] = level_id - 1 # Set Kevin Unlock Requirements - if self.options["KevinLevels"]: + if self.options.kevin_levels: def kevin_level_to_keyholder_level_id(level_id: int) -> Optional[int]: location = self.multiworld.find_item(f"Kevin-{level_id-36}", self.player) if location.player != self.player: @@ -506,7 +500,7 @@ class Overcooked2World(World): on_level_completed[level_id] = [item_to_unlock_event(location.item.name)] # Put it all together - star_threshold_scale = self.options["StarThresholdScale"] / 100 + star_threshold_scale = self.options.star_threshold_scale / 100 base_data = { # Changes Inherent to rando @@ -528,13 +522,13 @@ class Overcooked2World(World): "SaveFolderName": mod_name, "CustomOrderTimeoutPenalty": 10, "LevelForceHide": [37, 38, 39, 40, 41, 42, 43, 44], - "LocalDeathLink": self.options["DeathLink"] != DeathLinkMode.disabled, - "BurnTriggersDeath": self.options["DeathLink"] == DeathLinkMode.death_and_overcook, + "LocalDeathLink": self.options.deathlink != DeathLinkMode.disabled, + "BurnTriggersDeath": self.options.deathlink == DeathLinkMode.death_and_overcook, # Game Modifications "LevelPurchaseRequirements": level_purchase_requirements, "Custom66TimerScale": max(0.4, 0.25 + (1.0 - star_threshold_scale)*0.6), - "ShortHordeLevels": self.options["ShortHordeLevels"], + "ShortHordeLevels": self.options.short_horde_levels, "CustomLevelOrder": custom_level_order, # Items (Starting Inventory) @@ -580,28 +574,27 @@ class Overcooked2World(World): # Set remaining data in the options dict bugs = ["FixDoubleServing", "FixSinkBug", "FixControlStickThrowBug", "FixEmptyBurnerThrow"] for bug in bugs: - self.options[bug] = self.options["FixBugs"] - self.options["PreserveCookingProgress"] = self.options["AlwaysPreserveCookingProgress"] - self.options["TimerAlwaysStarts"] = self.options["PrepLevels"] == PrepLevelMode.ayce - self.options["LevelTimerScale"] = 0.666 if self.options["ShorterLevelDuration"] else 1.0 - self.options["LeaderboardScoreScale"] = { + base_data[bug] = self.options.fix_bugs.result + base_data["PreserveCookingProgress"] = self.options.always_preserve_cooking_progress.result + base_data["TimerAlwaysStarts"] = self.options.prep_levels == PrepLevelMode.ayce + base_data["LevelTimerScale"] = 0.666 if self.options.shorter_level_duration else 1.0 + base_data["LeaderboardScoreScale"] = { "FourStars": 1.0, "ThreeStars": star_threshold_scale, "TwoStars": star_threshold_scale * 0.75, "OneStar": star_threshold_scale * 0.35, } - base_data.update(self.options) return base_data def fill_slot_data(self) -> Dict[str, Any]: return self.fill_json_data() def write_spoiler(self, spoiler_handle: TextIO) -> None: - if not self.options["ShuffleLevelOrder"]: + if not self.options.shuffle_level_order: return - world: Overcooked2World = self.multiworld.worlds[self.player] + world: Overcooked2World = self spoiler_handle.write(f"\n\n{self.player_name}'s Level Order:\n\n") for overworld_id in world.level_mapping: overworld_name = Overcooked2GenericLevel(overworld_id).shortname.split("Story ")[1] diff --git a/worlds/ror2/Options.py b/worlds/ror2/Options.py index cdd548d33f..79739e85ef 100644 --- a/worlds/ror2/Options.py +++ b/worlds/ror2/Options.py @@ -1,5 +1,5 @@ -from typing import Dict -from Options import Option, Toggle, DefaultOnToggle, DeathLink, Range, Choice +from dataclasses import dataclass +from Options import Toggle, DefaultOnToggle, DeathLink, Range, Choice, PerGameCommonOptions # NOTE be aware that since the range of item ids that RoR2 uses is based off of the maximums of checks @@ -274,39 +274,40 @@ class ItemWeights(Choice): option_void = 9 -# define a dictionary for the weights of the generated item pool. -ror2_weights: Dict[str, type(Option)] = { - "green_scrap": GreenScrap, - "red_scrap": RedScrap, - "yellow_scrap": YellowScrap, - "white_scrap": WhiteScrap, - "common_item": CommonItem, - "uncommon_item": UncommonItem, - "legendary_item": LegendaryItem, - "boss_item": BossItem, - "lunar_item": LunarItem, - "void_item": VoidItem, - "equipment": Equipment -} -ror2_options: Dict[str, type(Option)] = { - "goal": Goal, - "total_locations": TotalLocations, - "chests_per_stage": ChestsPerEnvironment, - "shrines_per_stage": ShrinesPerEnvironment, - "scavengers_per_stage": ScavengersPerEnvironment, - "scanner_per_stage": ScannersPerEnvironment, - "altars_per_stage": AltarsPerEnvironment, - "total_revivals": TotalRevivals, - "start_with_revive": StartWithRevive, - "final_stage_death": FinalStageDeath, - "begin_with_loop": BeginWithLoop, - "dlc_sotv": DLC_SOTV, - "death_link": DeathLink, - "item_pickup_step": ItemPickupStep, - "shrine_use_step": ShrineUseStep, - "enable_lunar": AllowLunarItems, - "item_weights": ItemWeights, - "item_pool_presets": ItemPoolPresetToggle, - **ror2_weights -} + +# define a class for the weights of the generated item pool. +@dataclass +class ROR2Weights: + green_scrap: GreenScrap + red_scrap: RedScrap + yellow_scrap: YellowScrap + white_scrap: WhiteScrap + common_item: CommonItem + uncommon_item: UncommonItem + legendary_item: LegendaryItem + boss_item: BossItem + lunar_item: LunarItem + void_item: VoidItem + equipment: Equipment + +@dataclass +class ROR2Options(PerGameCommonOptions, ROR2Weights): + goal: Goal + total_locations: TotalLocations + chests_per_stage: ChestsPerEnvironment + shrines_per_stage: ShrinesPerEnvironment + scavengers_per_stage: ScavengersPerEnvironment + scanner_per_stage: ScannersPerEnvironment + altars_per_stage: AltarsPerEnvironment + total_revivals: TotalRevivals + start_with_revive: StartWithRevive + final_stage_death: FinalStageDeath + begin_with_loop: BeginWithLoop + dlc_sotv: DLC_SOTV + death_link: DeathLink + item_pickup_step: ItemPickupStep + shrine_use_step: ShrineUseStep + enable_lunar: AllowLunarItems + item_weights: ItemWeights + item_pool_presets: ItemPoolPresetToggle \ No newline at end of file diff --git a/worlds/ror2/__init__.py b/worlds/ror2/__init__.py index 7c638f50b3..22c65dd9de 100644 --- a/worlds/ror2/__init__.py +++ b/worlds/ror2/__init__.py @@ -6,7 +6,7 @@ from .Rules import set_rules from .RoR2Environments import * from BaseClasses import Region, Entrance, Item, ItemClassification, MultiWorld, Tutorial -from .Options import ror2_options, ItemWeights +from .Options import ItemWeights, ROR2Options from worlds.AutoWorld import World, WebWorld from .Regions import create_regions @@ -28,8 +28,9 @@ class RiskOfRainWorld(World): Combine loot in surprising ways and master each character until you become the havoc you feared upon your first crash landing. """ - game: str = "Risk of Rain 2" - option_definitions = ror2_options + game = "Risk of Rain 2" + options_dataclass = ROR2Options + options: ROR2Options topology_present = False item_name_to_id = item_table @@ -46,45 +47,44 @@ class RiskOfRainWorld(World): def generate_early(self) -> None: # figure out how many revivals should exist in the pool - if self.multiworld.goal[self.player] == "classic": - total_locations = self.multiworld.total_locations[self.player].value + if self.options.goal == "classic": + total_locations = self.options.total_locations.value else: total_locations = len( orderedstage_location.get_locations( - chests=self.multiworld.chests_per_stage[self.player].value, - shrines=self.multiworld.shrines_per_stage[self.player].value, - scavengers=self.multiworld.scavengers_per_stage[self.player].value, - scanners=self.multiworld.scanner_per_stage[self.player].value, - altars=self.multiworld.altars_per_stage[self.player].value, - dlc_sotv=self.multiworld.dlc_sotv[self.player].value + chests=self.options.chests_per_stage.value, + shrines=self.options.shrines_per_stage.value, + scavengers=self.options.scavengers_per_stage.value, + scanners=self.options.scanner_per_stage.value, + altars=self.options.altars_per_stage.value, + dlc_sotv=self.options.dlc_sotv.value ) ) - self.total_revivals = int(self.multiworld.total_revivals[self.player].value / 100 * + self.total_revivals = int(self.options.total_revivals.value / 100 * total_locations) - # self.total_revivals = self.multiworld.total_revivals[self.player].value - if self.multiworld.start_with_revive[self.player].value: + if self.options.start_with_revive: self.total_revivals -= 1 def create_items(self) -> None: # shortcut for starting_inventory... The start_with_revive option lets you start with a Dio's Best Friend - if self.multiworld.start_with_revive[self.player]: + if self.options.start_with_revive: self.multiworld.push_precollected(self.multiworld.create_item("Dio's Best Friend", self.player)) environments_pool = {} # only mess with the environments if they are set as items - if self.multiworld.goal[self.player] == "explore": + if self.options.goal == "explore": # figure out all available ordered stages for each tier environment_available_orderedstages_table = environment_vanilla_orderedstages_table - if self.multiworld.dlc_sotv[self.player]: + if self.options.dlc_sotv: environment_available_orderedstages_table = collapse_dict_list_vertical(environment_available_orderedstages_table, environment_sotv_orderedstages_table) environments_pool = shift_by_offset(environment_vanilla_table, environment_offest) - if self.multiworld.dlc_sotv[self.player]: + if self.options.dlc_sotv: environment_offset_table = shift_by_offset(environment_sotv_table, environment_offest) environments_pool = {**environments_pool, **environment_offset_table} - environments_to_precollect = 5 if self.multiworld.begin_with_loop[self.player].value else 1 + environments_to_precollect = 5 if self.options.begin_with_loop else 1 # percollect environments for each stage (or just stage 1) for i in range(environments_to_precollect): unlock = self.multiworld.random.choices(list(environment_available_orderedstages_table[i].keys()), k=1) @@ -100,19 +100,19 @@ class RiskOfRainWorld(World): for env_name, _ in environments_pool.items(): itempool += [env_name] - if self.multiworld.goal[self.player] == "classic": + if self.options.goal == "classic": # classic mode - total_locations = self.multiworld.total_locations[self.player].value + total_locations = self.options.total_locations.value else: # explore mode total_locations = len( orderedstage_location.get_locations( - chests=self.multiworld.chests_per_stage[self.player].value, - shrines=self.multiworld.shrines_per_stage[self.player].value, - scavengers=self.multiworld.scavengers_per_stage[self.player].value, - scanners=self.multiworld.scanner_per_stage[self.player].value, - altars=self.multiworld.altars_per_stage[self.player].value, - dlc_sotv=self.multiworld.dlc_sotv[self.player].value + chests=self.options.chests_per_stage.value, + shrines=self.options.shrines_per_stage.value, + scavengers=self.options.scavengers_per_stage.value, + scanners=self.options.scanner_per_stage.value, + altars=self.options.altars_per_stage.value, + dlc_sotv=self.options.dlc_sotv.value ) ) # Create junk items @@ -138,9 +138,9 @@ class RiskOfRainWorld(World): def create_junk_pool(self) -> Dict: # if presets are enabled generate junk_pool from the selected preset - pool_option = self.multiworld.item_weights[self.player].value + pool_option = self.options.item_weights.value junk_pool: Dict[str, int] = {} - if self.multiworld.item_pool_presets[self.player]: + if self.options.item_pool_presets: # generate chaos weights if the preset is chosen if pool_option == ItemWeights.option_chaos: for name, max_value in item_pool_weights[pool_option].items(): @@ -149,31 +149,31 @@ class RiskOfRainWorld(World): junk_pool = item_pool_weights[pool_option].copy() else: # generate junk pool from user created presets junk_pool = { - "Item Scrap, Green": self.multiworld.green_scrap[self.player].value, - "Item Scrap, Red": self.multiworld.red_scrap[self.player].value, - "Item Scrap, Yellow": self.multiworld.yellow_scrap[self.player].value, - "Item Scrap, White": self.multiworld.white_scrap[self.player].value, - "Common Item": self.multiworld.common_item[self.player].value, - "Uncommon Item": self.multiworld.uncommon_item[self.player].value, - "Legendary Item": self.multiworld.legendary_item[self.player].value, - "Boss Item": self.multiworld.boss_item[self.player].value, - "Lunar Item": self.multiworld.lunar_item[self.player].value, - "Void Item": self.multiworld.void_item[self.player].value, - "Equipment": self.multiworld.equipment[self.player].value + "Item Scrap, Green": self.options.green_scrap.value, + "Item Scrap, Red": self.options.red_scrap.value, + "Item Scrap, Yellow": self.options.yellow_scrap.value, + "Item Scrap, White": self.options.white_scrap.value, + "Common Item": self.options.common_item.value, + "Uncommon Item": self.options.uncommon_item.value, + "Legendary Item": self.options.legendary_item.value, + "Boss Item": self.options.boss_item.value, + "Lunar Item": self.options.lunar_item.value, + "Void Item": self.options.void_item.value, + "Equipment": self.options.equipment.value } # remove lunar items from the pool if they're disabled in the yaml unless lunartic is rolled - if not (self.multiworld.enable_lunar[self.player] or pool_option == ItemWeights.option_lunartic): + if not (self.options.enable_lunar or pool_option == ItemWeights.option_lunartic): junk_pool.pop("Lunar Item") # remove void items from the pool - if not (self.multiworld.dlc_sotv[self.player] or pool_option == ItemWeights.option_void): + if not (self.options.dlc_sotv or pool_option == ItemWeights.option_void): junk_pool.pop("Void Item") return junk_pool def create_regions(self) -> None: - if self.multiworld.goal[self.player] == "classic": + if self.options.goal == "classic": # classic mode menu = create_region(self.multiworld, self.player, "Menu") self.multiworld.regions.append(menu) @@ -182,7 +182,7 @@ class RiskOfRainWorld(World): victory_region = create_region(self.multiworld, self.player, "Victory") self.multiworld.regions.append(victory_region) petrichor = create_region(self.multiworld, self.player, "Petrichor V", - get_classic_item_pickups(self.multiworld.total_locations[self.player].value)) + get_classic_item_pickups(self.options.total_locations.value)) self.multiworld.regions.append(petrichor) # classic mode can get to victory from the beginning of the game @@ -200,21 +200,13 @@ class RiskOfRainWorld(World): create_events(self.multiworld, self.player) def fill_slot_data(self): + options_dict = self.options.as_dict("item_pickup_step", "shrine_use_step", "goal", "total_locations", + "chests_per_stage", "shrines_per_stage", "scavengers_per_stage", + "scanner_per_stage", "altars_per_stage", "total_revivals", "start_with_revive", + "final_stage_death", "death_link", casing="camel") return { - "itemPickupStep": self.multiworld.item_pickup_step[self.player].value, - "shrineUseStep": self.multiworld.shrine_use_step[self.player].value, - "goal": self.multiworld.goal[self.player].value, + **options_dict, "seed": "".join(self.multiworld.per_slot_randoms[self.player].choice(string.digits) for _ in range(16)), - "totalLocations": self.multiworld.total_locations[self.player].value, - "chestsPerStage": self.multiworld.chests_per_stage[self.player].value, - "shrinesPerStage": self.multiworld.shrines_per_stage[self.player].value, - "scavengersPerStage": self.multiworld.scavengers_per_stage[self.player].value, - "scannerPerStage": self.multiworld.scanner_per_stage[self.player].value, - "altarsPerStage": self.multiworld.altars_per_stage[self.player].value, - "totalRevivals": self.multiworld.total_revivals[self.player].value, - "startWithDio": self.multiworld.start_with_revive[self.player].value, - "finalStageDeath": self.multiworld.final_stage_death[self.player].value, - "deathLink": self.multiworld.death_link[self.player].value, } def create_item(self, name: str) -> Item: @@ -241,12 +233,12 @@ class RiskOfRainWorld(World): def create_events(world: MultiWorld, player: int) -> None: - total_locations = world.total_locations[player].value + total_locations = world.worlds[player].options.total_locations.value num_of_events = total_locations // 25 if total_locations / 25 == num_of_events: num_of_events -= 1 world_region = world.get_region("Petrichor V", player) - if world.goal[player] == "classic": + if world.worlds[player].options.goal == "classic": # only setup Pickups when using classic_mode for i in range(num_of_events): event_loc = RiskOfRainLocation(player, f"Pickup{(i + 1) * 25}", None, world_region) @@ -254,7 +246,7 @@ def create_events(world: MultiWorld, player: int) -> None: event_loc.access_rule = \ lambda state, i=i: state.can_reach(f"ItemPickup{((i + 1) * 25) - 1}", "Location", player) world_region.locations.append(event_loc) - elif world.goal[player] == "explore": + elif world.worlds[player].options.goal == "explore": for n in range(1, 6): event_region = world.get_region(f"OrderedStage_{n}", player) diff --git a/worlds/sm/__init__.py b/worlds/sm/__init__.py index 21e29868ed..9d6f28607e 100644 --- a/worlds/sm/__init__.py +++ b/worlds/sm/__init__.py @@ -148,7 +148,7 @@ class SMWorld(World): self.remote_items = self.multiworld.remote_items[self.player] if (len(self.variaRando.randoExec.setup.restrictedLocs) > 0): - self.multiworld.accessibility[self.player] = self.multiworld.accessibility[self.player].from_text("minimal") + self.multiworld.accessibility[self.player].value = Accessibility.option_minimal logger.warning(f"accessibility forced to 'minimal' for player {self.multiworld.get_player_name(self.player)} because of 'fun' settings") def create_items(self): diff --git a/worlds/stardew_valley/__init__.py b/worlds/stardew_valley/__init__.py index 150e3589b0..1f46eb79d7 100644 --- a/worlds/stardew_valley/__init__.py +++ b/worlds/stardew_valley/__init__.py @@ -1,18 +1,20 @@ import logging -from typing import Dict, Any, Iterable, Optional, Union, Set +from typing import Dict, Any, Iterable, Optional, Union, Set, List from BaseClasses import Region, Entrance, Location, Item, Tutorial, CollectionState, ItemClassification, MultiWorld +from Options import PerGameCommonOptions from worlds.AutoWorld import World, WebWorld -from . import rules, logic, options +from . import rules from .bundles import get_all_bundles, Bundle from .items import item_table, create_items, ItemData, Group, items_by_group from .locations import location_table, create_locations, LocationData from .logic import StardewLogic, StardewRule, True_, MAX_MONTHS -from .options import stardew_valley_options, StardewOptions, fetch_options +from .options import StardewValleyOptions, SeasonRandomization, Goal, BundleRandomization, BundlePrice, NumberOfLuckBuffs, NumberOfMovementBuffs, \ + BackpackProgression, BuildingProgression, ExcludeGingerIsland from .regions import create_regions from .rules import set_rules from worlds.generic.Rules import set_rule -from .strings.goal_names import Goal +from .strings.goal_names import Goal as GoalName client_version = 0 @@ -50,7 +52,6 @@ class StardewValleyWorld(World): befriend villagers, and uncover dark secrets. """ game = "Stardew Valley" - option_definitions = stardew_valley_options topology_present = False item_name_to_id = {name: data.code for name, data in item_table.items()} @@ -59,7 +60,8 @@ class StardewValleyWorld(World): data_version = 3 required_client_version = (0, 4, 0) - options: StardewOptions + options_dataclass = StardewValleyOptions + options: StardewValleyOptions logic: StardewLogic web = StardewWebWorld() @@ -72,25 +74,24 @@ class StardewValleyWorld(World): self.all_progression_items = set() def generate_early(self): - self.options = fetch_options(self.multiworld, self.player) self.force_change_options_if_incompatible() self.logic = StardewLogic(self.player, self.options) self.modified_bundles = get_all_bundles(self.multiworld.random, self.logic, - self.options[options.BundleRandomization], - self.options[options.BundlePrice]) + self.options.bundle_randomization, + self.options.bundle_price) def force_change_options_if_incompatible(self): - goal_is_walnut_hunter = self.options[options.Goal] == options.Goal.option_greatest_walnut_hunter - goal_is_perfection = self.options[options.Goal] == options.Goal.option_perfection + goal_is_walnut_hunter = self.options.goal == Goal.option_greatest_walnut_hunter + goal_is_perfection = self.options.goal == Goal.option_perfection goal_is_island_related = goal_is_walnut_hunter or goal_is_perfection - exclude_ginger_island = self.options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true + exclude_ginger_island = self.options.exclude_ginger_island == ExcludeGingerIsland.option_true if goal_is_island_related and exclude_ginger_island: - self.options[options.ExcludeGingerIsland] = options.ExcludeGingerIsland.option_false - goal = options.Goal.name_lookup[self.options[options.Goal]] + self.options.exclude_ginger_island.value = ExcludeGingerIsland.option_false + goal_name = self.options.goal.current_key player_name = self.multiworld.player_name[self.player] - logging.warning(f"Goal '{goal}' requires Ginger Island. Exclude Ginger Island setting forced to 'False' for player {self.player} ({player_name})") + logging.warning(f"Goal '{goal_name}' requires Ginger Island. Exclude Ginger Island setting forced to 'False' for player {self.player} ({player_name})") def create_regions(self): def create_region(name: str, exits: Iterable[str]) -> Region: @@ -116,7 +117,7 @@ class StardewValleyWorld(World): if not item_table[excluded_items.name].has_any_group(Group.RESOURCE_PACK, Group.FRIENDSHIP_PACK)] - if self.options[options.SeasonRandomization] == options.SeasonRandomization.option_disabled: + if self.options.season_randomization == SeasonRandomization.option_disabled: items_to_exclude = [item for item in items_to_exclude if item_table[item.name] not in items_by_group[Group.SEASON]] @@ -134,12 +135,12 @@ class StardewValleyWorld(World): self.setup_victory() def precollect_starting_season(self) -> Optional[StardewItem]: - if self.options[options.SeasonRandomization] == options.SeasonRandomization.option_progressive: + if self.options.season_randomization == SeasonRandomization.option_progressive: return season_pool = items_by_group[Group.SEASON] - if self.options[options.SeasonRandomization] == options.SeasonRandomization.option_disabled: + if self.options.season_randomization == SeasonRandomization.option_disabled: for season in season_pool: self.multiworld.push_precollected(self.create_item(season)) return @@ -148,18 +149,18 @@ class StardewValleyWorld(World): if item.name in {season.name for season in items_by_group[Group.SEASON]}]: return - if self.options[options.SeasonRandomization] == options.SeasonRandomization.option_randomized_not_winter: + if self.options.season_randomization == SeasonRandomization.option_randomized_not_winter: season_pool = [season for season in season_pool if season.name != "Winter"] starting_season = self.create_item(self.multiworld.random.choice(season_pool)) self.multiworld.push_precollected(starting_season) def setup_early_items(self): - if (self.options[options.BuildingProgression] == - options.BuildingProgression.option_progressive_early_shipping_bin): + if (self.options.building_progression == + BuildingProgression.option_progressive_early_shipping_bin): self.multiworld.early_items[self.player]["Shipping Bin"] = 1 - if self.options[options.BackpackProgression] == options.BackpackProgression.option_early_progressive: + if self.options.backpack_progression == BackpackProgression.option_early_progressive: self.multiworld.early_items[self.player]["Progressive Backpack"] = 1 def setup_month_events(self): @@ -172,40 +173,40 @@ class StardewValleyWorld(World): self.create_event_location(month_end, self.logic.received("Month End", i).simplify(), "Month End") def setup_victory(self): - if self.options[options.Goal] == options.Goal.option_community_center: - self.create_event_location(location_table[Goal.community_center], + if self.options.goal == Goal.option_community_center: + self.create_event_location(location_table[GoalName.community_center], self.logic.can_complete_community_center().simplify(), "Victory") - elif self.options[options.Goal] == options.Goal.option_grandpa_evaluation: - self.create_event_location(location_table[Goal.grandpa_evaluation], + elif self.options.goal == Goal.option_grandpa_evaluation: + self.create_event_location(location_table[GoalName.grandpa_evaluation], self.logic.can_finish_grandpa_evaluation().simplify(), "Victory") - elif self.options[options.Goal] == options.Goal.option_bottom_of_the_mines: - self.create_event_location(location_table[Goal.bottom_of_the_mines], + elif self.options.goal == Goal.option_bottom_of_the_mines: + self.create_event_location(location_table[GoalName.bottom_of_the_mines], self.logic.can_mine_to_floor(120).simplify(), "Victory") - elif self.options[options.Goal] == options.Goal.option_cryptic_note: - self.create_event_location(location_table[Goal.cryptic_note], + elif self.options.goal == Goal.option_cryptic_note: + self.create_event_location(location_table[GoalName.cryptic_note], self.logic.can_complete_quest("Cryptic Note").simplify(), "Victory") - elif self.options[options.Goal] == options.Goal.option_master_angler: - self.create_event_location(location_table[Goal.master_angler], + elif self.options.goal == Goal.option_master_angler: + self.create_event_location(location_table[GoalName.master_angler], self.logic.can_catch_every_fish().simplify(), "Victory") - elif self.options[options.Goal] == options.Goal.option_complete_collection: - self.create_event_location(location_table[Goal.complete_museum], + elif self.options.goal == Goal.option_complete_collection: + self.create_event_location(location_table[GoalName.complete_museum], self.logic.can_complete_museum().simplify(), "Victory") - elif self.options[options.Goal] == options.Goal.option_full_house: - self.create_event_location(location_table[Goal.full_house], + elif self.options.goal == Goal.option_full_house: + self.create_event_location(location_table[GoalName.full_house], (self.logic.has_children(2) & self.logic.can_reproduce()).simplify(), "Victory") - elif self.options[options.Goal] == options.Goal.option_greatest_walnut_hunter: - self.create_event_location(location_table[Goal.greatest_walnut_hunter], + elif self.options.goal == Goal.option_greatest_walnut_hunter: + self.create_event_location(location_table[GoalName.greatest_walnut_hunter], self.logic.has_walnut(130).simplify(), "Victory") - elif self.options[options.Goal] == options.Goal.option_perfection: - self.create_event_location(location_table[Goal.perfection], + elif self.options.goal == Goal.option_perfection: + self.create_event_location(location_table[GoalName.perfection], self.logic.has_everything(self.all_progression_items).simplify(), "Victory") @@ -230,7 +231,7 @@ class StardewValleyWorld(World): location.place_locked_item(self.create_item(item)) def set_rules(self): - set_rules(self.multiworld, self.player, self.options, self.logic, self.modified_bundles) + set_rules(self) self.force_first_month_once_all_early_items_are_found() def force_first_month_once_all_early_items_are_found(self): @@ -276,11 +277,12 @@ class StardewValleyWorld(World): key, value = self.modified_bundles[bundle_key].to_pair() modified_bundles[key] = value - excluded_options = [options.BundleRandomization, options.BundlePrice, - options.NumberOfMovementBuffs, options.NumberOfLuckBuffs] - slot_data = dict(self.options.options) - for option in excluded_options: - slot_data.pop(option.internal_name) + excluded_options = [BundleRandomization, BundlePrice, NumberOfMovementBuffs, NumberOfLuckBuffs] + excluded_option_names = [option.internal_name for option in excluded_options] + generic_option_names = [option_name for option_name in PerGameCommonOptions.type_hints] + excluded_option_names.extend(generic_option_names) + included_option_names: List[str] = [option_name for option_name in self.options_dataclass.type_hints if option_name not in excluded_option_names] + slot_data = self.options.as_dict(*included_option_names) slot_data.update({ "seed": self.multiworld.per_slot_randoms[self.player].randrange(1000000000), # Seed should be max 9 digits "randomized_entrances": self.randomized_entrances, diff --git a/worlds/stardew_valley/bundles.py b/worlds/stardew_valley/bundles.py index 7cbb139237..4af21542a4 100644 --- a/worlds/stardew_valley/bundles.py +++ b/worlds/stardew_valley/bundles.py @@ -152,7 +152,7 @@ class Bundle: # shuffle_vault_amongst_themselves(random, bundles) -def get_all_bundles(random: Random, logic: StardewLogic, randomization: int, price: int) -> Dict[str, Bundle]: +def get_all_bundles(random: Random, logic: StardewLogic, randomization: BundleRandomization, price: BundlePrice) -> Dict[str, Bundle]: bundles = {} for bundle_key in vanilla_bundles: bundle_value = vanilla_bundles[bundle_key] diff --git a/worlds/stardew_valley/items.py b/worlds/stardew_valley/items.py index 1035997034..d5c71dae46 100644 --- a/worlds/stardew_valley/items.py +++ b/worlds/stardew_valley/items.py @@ -7,10 +7,11 @@ from random import Random from typing import Dict, List, Protocol, Union, Set, Optional from BaseClasses import Item, ItemClassification -from . import options, data +from . import data from .data.villagers_data import all_villagers from .mods.mod_data import ModNames -from .options import StardewOptions +from .options import StardewValleyOptions, TrapItems, FestivalLocations, ExcludeGingerIsland, SpecialOrderLocations, SeasonRandomization, Cropsanity, Friendsanity, Museumsanity, \ + Fishsanity, BuildingProgression, SkillProgression, ToolProgression, ElevatorProgression, BackpackProgression, ArcadeMachineLocations from .strings.ap_names.buff_names import Buff ITEM_CODE_OFFSET = 717000 @@ -138,10 +139,9 @@ initialize_groups() def create_items(item_factory: StardewItemFactory, locations_count: int, items_to_exclude: List[Item], - world_options: StardewOptions, - random: Random) -> List[Item]: + options: StardewValleyOptions, random: Random) -> List[Item]: items = [] - unique_items = create_unique_items(item_factory, world_options, random) + unique_items = create_unique_items(item_factory, options, random) for item in items_to_exclude: if item in unique_items: @@ -151,58 +151,58 @@ def create_items(item_factory: StardewItemFactory, locations_count: int, items_t items += unique_items logger.debug(f"Created {len(unique_items)} unique items") - unique_filler_items = create_unique_filler_items(item_factory, world_options, random, locations_count - len(items)) + unique_filler_items = create_unique_filler_items(item_factory, options, random, locations_count - len(items)) items += unique_filler_items logger.debug(f"Created {len(unique_filler_items)} unique filler items") - resource_pack_items = fill_with_resource_packs_and_traps(item_factory, world_options, random, items, locations_count) + resource_pack_items = fill_with_resource_packs_and_traps(item_factory, options, random, items, locations_count) items += resource_pack_items logger.debug(f"Created {len(resource_pack_items)} resource packs") return items -def create_unique_items(item_factory: StardewItemFactory, world_options: StardewOptions, random: Random) -> List[Item]: +def create_unique_items(item_factory: StardewItemFactory, options: StardewValleyOptions, random: Random) -> List[Item]: items = [] items.extend(item_factory(item) for item in items_by_group[Group.COMMUNITY_REWARD]) - create_backpack_items(item_factory, world_options, items) + create_backpack_items(item_factory, options, items) create_mine_rewards(item_factory, items, random) - create_elevators(item_factory, world_options, items) - create_tools(item_factory, world_options, items) - create_skills(item_factory, world_options, items) - create_wizard_buildings(item_factory, world_options, items) - create_carpenter_buildings(item_factory, world_options, items) + create_elevators(item_factory, options, items) + create_tools(item_factory, options, items) + create_skills(item_factory, options, items) + create_wizard_buildings(item_factory, options, items) + create_carpenter_buildings(item_factory, options, items) items.append(item_factory("Beach Bridge")) items.append(item_factory("Dark Talisman")) create_tv_channels(item_factory, items) create_special_quest_rewards(item_factory, items) - create_stardrops(item_factory, world_options, items) - create_museum_items(item_factory, world_options, items) - create_arcade_machine_items(item_factory, world_options, items) + create_stardrops(item_factory, options, items) + create_museum_items(item_factory, options, items) + create_arcade_machine_items(item_factory, options, items) items.append(item_factory(random.choice(items_by_group[Group.GALAXY_WEAPONS]))) - create_player_buffs(item_factory, world_options, items) + create_player_buffs(item_factory, options, items) create_traveling_merchant_items(item_factory, items) items.append(item_factory("Return Scepter")) - create_seasons(item_factory, world_options, items) - create_seeds(item_factory, world_options, items) - create_friendsanity_items(item_factory, world_options, items) - create_festival_rewards(item_factory, world_options, items) + create_seasons(item_factory, options, items) + create_seeds(item_factory, options, items) + create_friendsanity_items(item_factory, options, items) + create_festival_rewards(item_factory, options, items) create_babies(item_factory, items, random) - create_special_order_board_rewards(item_factory, world_options, items) - create_special_order_qi_rewards(item_factory, world_options, items) - create_walnut_purchase_rewards(item_factory, world_options, items) - create_magic_mod_spells(item_factory, world_options, items) + create_special_order_board_rewards(item_factory, options, items) + create_special_order_qi_rewards(item_factory, options, items) + create_walnut_purchase_rewards(item_factory, options, items) + create_magic_mod_spells(item_factory, options, items) return items -def create_backpack_items(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]): - if (world_options[options.BackpackProgression] == options.BackpackProgression.option_progressive or - world_options[options.BackpackProgression] == options.BackpackProgression.option_early_progressive): +def create_backpack_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): + if (options.backpack_progression == BackpackProgression.option_progressive or + options.backpack_progression == BackpackProgression.option_early_progressive): items.extend(item_factory(item) for item in ["Progressive Backpack"] * 2) - if ModNames.big_backpack in world_options[options.Mods]: + if ModNames.big_backpack in options.mods: items.append(item_factory("Progressive Backpack")) @@ -220,46 +220,46 @@ def create_mine_rewards(item_factory: StardewItemFactory, items: List[Item], ran items.append(item_factory("Skull Key")) -def create_elevators(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]): - if world_options[options.ElevatorProgression] == options.ElevatorProgression.option_vanilla: +def create_elevators(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): + if options.elevator_progression == ElevatorProgression.option_vanilla: return items.extend([item_factory(item) for item in ["Progressive Mine Elevator"] * 24]) - if ModNames.deepwoods in world_options[options.Mods]: + if ModNames.deepwoods in options.mods: items.extend([item_factory(item) for item in ["Progressive Woods Obelisk Sigils"] * 10]) - if ModNames.skull_cavern_elevator in world_options[options.Mods]: + if ModNames.skull_cavern_elevator in options.mods: items.extend([item_factory(item) for item in ["Progressive Skull Cavern Elevator"] * 8]) -def create_tools(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]): - if world_options[options.ToolProgression] == options.ToolProgression.option_progressive: +def create_tools(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): + if options.tool_progression == ToolProgression.option_progressive: items.extend(item_factory(item) for item in items_by_group[Group.PROGRESSIVE_TOOLS] * 4) items.append(item_factory("Golden Scythe")) -def create_skills(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]): - if world_options[options.SkillProgression] == options.SkillProgression.option_progressive: +def create_skills(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): + if options.skill_progression == SkillProgression.option_progressive: for item in items_by_group[Group.SKILL_LEVEL_UP]: - if item.mod_name not in world_options[options.Mods] and item.mod_name is not None: + if item.mod_name not in options.mods and item.mod_name is not None: continue items.extend(item_factory(item) for item in [item.name] * 10) -def create_wizard_buildings(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]): +def create_wizard_buildings(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): items.append(item_factory("Earth Obelisk")) items.append(item_factory("Water Obelisk")) items.append(item_factory("Desert Obelisk")) items.append(item_factory("Junimo Hut")) items.append(item_factory("Gold Clock")) - if world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_false: + if options.exclude_ginger_island == ExcludeGingerIsland.option_false: items.append(item_factory("Island Obelisk")) - if ModNames.deepwoods in world_options[options.Mods]: + if ModNames.deepwoods in options.mods: items.append(item_factory("Woods Obelisk")) -def create_carpenter_buildings(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]): - if world_options[options.BuildingProgression] in {options.BuildingProgression.option_progressive, - options.BuildingProgression.option_progressive_early_shipping_bin}: +def create_carpenter_buildings(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): + if options.building_progression in {BuildingProgression.option_progressive, + BuildingProgression.option_progressive_early_shipping_bin}: items.append(item_factory("Progressive Coop")) items.append(item_factory("Progressive Coop")) items.append(item_factory("Progressive Coop")) @@ -278,7 +278,7 @@ def create_carpenter_buildings(item_factory: StardewItemFactory, world_options: items.append(item_factory("Progressive House")) items.append(item_factory("Progressive House")) items.append(item_factory("Progressive House")) - if ModNames.tractor in world_options[options.Mods]: + if ModNames.tractor in options.mods: items.append(item_factory("Tractor Garage")) @@ -290,17 +290,17 @@ def create_special_quest_rewards(item_factory: StardewItemFactory, items: List[I items.append(item_factory("Iridium Snake Milk")) -def create_stardrops(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]): +def create_stardrops(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): items.append(item_factory("Stardrop")) # The Mines level 100 items.append(item_factory("Stardrop")) # Old Master Cannoli - if world_options[options.Fishsanity] != options.Fishsanity.option_none: + if options.fishsanity != Fishsanity.option_none: items.append(item_factory("Stardrop")) #Master Angler Stardrop - if ModNames.deepwoods in world_options[options.Mods]: + if ModNames.deepwoods in options.mods: items.append(item_factory("Stardrop")) # Petting the Unicorn -def create_museum_items(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]): - if world_options[options.Museumsanity] == options.Museumsanity.option_none: +def create_museum_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): + if options.museumsanity == Museumsanity.option_none: return items.extend(item_factory(item) for item in ["Magic Rock Candy"] * 5) items.extend(item_factory(item) for item in ["Ancient Seeds"] * 5) @@ -311,17 +311,17 @@ def create_museum_items(item_factory: StardewItemFactory, world_options: Stardew items.append(item_factory("Dwarvish Translation Guide")) -def create_friendsanity_items(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]): - if world_options[options.Friendsanity] == options.Friendsanity.option_none: +def create_friendsanity_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): + if options.friendsanity == Friendsanity.option_none: return - exclude_non_bachelors = world_options[options.Friendsanity] == options.Friendsanity.option_bachelors - exclude_locked_villagers = world_options[options.Friendsanity] == options.Friendsanity.option_starting_npcs or \ - world_options[options.Friendsanity] == options.Friendsanity.option_bachelors - include_post_marriage_hearts = world_options[options.Friendsanity] == options.Friendsanity.option_all_with_marriage - exclude_ginger_island = world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true - heart_size = world_options[options.FriendsanityHeartSize] + exclude_non_bachelors = options.friendsanity == Friendsanity.option_bachelors + exclude_locked_villagers = options.friendsanity == Friendsanity.option_starting_npcs or \ + options.friendsanity == Friendsanity.option_bachelors + include_post_marriage_hearts = options.friendsanity == Friendsanity.option_all_with_marriage + exclude_ginger_island = options.exclude_ginger_island == ExcludeGingerIsland.option_true + heart_size = options.friendsanity_heart_size for villager in all_villagers: - if villager.mod_name not in world_options[options.Mods] and villager.mod_name is not None: + if villager.mod_name not in options.mods and villager.mod_name is not None: continue if not villager.available and exclude_locked_villagers: continue @@ -350,8 +350,8 @@ def create_babies(item_factory: StardewItemFactory, items: List[Item], random: R items.append(item_factory(chosen_baby)) -def create_arcade_machine_items(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]): - if world_options[options.ArcadeMachineLocations] == options.ArcadeMachineLocations.option_full_shuffling: +def create_arcade_machine_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): + if options.arcade_machine_locations == ArcadeMachineLocations.option_full_shuffling: items.append(item_factory("JotPK: Progressive Boots")) items.append(item_factory("JotPK: Progressive Boots")) items.append(item_factory("JotPK: Progressive Gun")) @@ -367,11 +367,9 @@ def create_arcade_machine_items(item_factory: StardewItemFactory, world_options: items.extend(item_factory(item) for item in ["Junimo Kart: Extra Life"] * 8) -def create_player_buffs(item_factory: StardewItemFactory, world_options: options.StardewOptions, items: List[Item]): - number_of_movement_buffs: int = world_options[options.NumberOfMovementBuffs] - number_of_luck_buffs: int = world_options[options.NumberOfLuckBuffs] - items.extend(item_factory(item) for item in [Buff.movement] * number_of_movement_buffs) - items.extend(item_factory(item) for item in [Buff.luck] * number_of_luck_buffs) +def create_player_buffs(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): + items.extend(item_factory(item) for item in [Buff.movement] * options.number_of_movement_buffs.value) + items.extend(item_factory(item) for item in [Buff.luck] * options.number_of_luck_buffs.value) def create_traveling_merchant_items(item_factory: StardewItemFactory, items: List[Item]): @@ -380,36 +378,36 @@ def create_traveling_merchant_items(item_factory: StardewItemFactory, items: Lis *(item_factory(item) for item in ["Traveling Merchant Discount"] * 8)]) -def create_seasons(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]): - if world_options[options.SeasonRandomization] == options.SeasonRandomization.option_disabled: +def create_seasons(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): + if options.season_randomization == SeasonRandomization.option_disabled: return - if world_options[options.SeasonRandomization] == options.SeasonRandomization.option_progressive: + if options.season_randomization == SeasonRandomization.option_progressive: items.extend([item_factory(item) for item in ["Progressive Season"] * 3]) return items.extend([item_factory(item) for item in items_by_group[Group.SEASON]]) -def create_seeds(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]): - if world_options[options.Cropsanity] == options.Cropsanity.option_disabled: +def create_seeds(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): + if options.cropsanity == Cropsanity.option_disabled: return - include_ginger_island = world_options[options.ExcludeGingerIsland] != options.ExcludeGingerIsland.option_true + include_ginger_island = options.exclude_ginger_island != ExcludeGingerIsland.option_true seed_items = [item_factory(item) for item in items_by_group[Group.CROPSANITY] if include_ginger_island or Group.GINGER_ISLAND not in item.groups] items.extend(seed_items) -def create_festival_rewards(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]): - if world_options[options.FestivalLocations] == options.FestivalLocations.option_disabled: +def create_festival_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): + if options.festival_locations == FestivalLocations.option_disabled: return items.extend([*[item_factory(item) for item in items_by_group[Group.FESTIVAL] if item.classification != ItemClassification.filler], item_factory("Stardrop")]) -def create_walnut_purchase_rewards(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]): - if world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true: +def create_walnut_purchase_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): + if options.exclude_ginger_island == ExcludeGingerIsland.option_true: return items.extend([item_factory("Boat Repair"), @@ -420,16 +418,16 @@ def create_walnut_purchase_rewards(item_factory: StardewItemFactory, world_optio -def create_special_order_board_rewards(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]): - if world_options[options.SpecialOrderLocations] == options.SpecialOrderLocations.option_disabled: +def create_special_order_board_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): + if options.special_order_locations == SpecialOrderLocations.option_disabled: return items.extend([item_factory(item) for item in items_by_group[Group.SPECIAL_ORDER_BOARD]]) -def create_special_order_qi_rewards(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]): - if (world_options[options.SpecialOrderLocations] != options.SpecialOrderLocations.option_board_qi or - world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true): +def create_special_order_qi_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): + if (options.special_order_locations != SpecialOrderLocations.option_board_qi or + options.exclude_ginger_island == ExcludeGingerIsland.option_true): return qi_gem_rewards = ["100 Qi Gems", "10 Qi Gems", "40 Qi Gems", "25 Qi Gems", "25 Qi Gems", "40 Qi Gems", "20 Qi Gems", "50 Qi Gems", "40 Qi Gems", "35 Qi Gems"] @@ -441,35 +439,35 @@ def create_tv_channels(item_factory: StardewItemFactory, items: List[Item]): items.extend([item_factory(item) for item in items_by_group[Group.TV_CHANNEL]]) -def create_filler_festival_rewards(item_factory: StardewItemFactory, world_options: StardewOptions) -> List[Item]: - if world_options[options.FestivalLocations] == options.FestivalLocations.option_disabled: +def create_filler_festival_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions) -> List[Item]: + if options.festival_locations == FestivalLocations.option_disabled: return [] return [item_factory(item) for item in items_by_group[Group.FESTIVAL] if item.classification == ItemClassification.filler] -def create_magic_mod_spells(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]): - if ModNames.magic not in world_options[options.Mods]: +def create_magic_mod_spells(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): + if ModNames.magic not in options.mods: return [] items.extend([item_factory(item) for item in items_by_group[Group.MAGIC_SPELL]]) -def create_unique_filler_items(item_factory: StardewItemFactory, world_options: options.StardewOptions, random: Random, +def create_unique_filler_items(item_factory: StardewItemFactory, options: StardewValleyOptions, random: Random, available_item_slots: int) -> List[Item]: items = [] - items.extend(create_filler_festival_rewards(item_factory, world_options)) + items.extend(create_filler_festival_rewards(item_factory, options)) if len(items) > available_item_slots: items = random.sample(items, available_item_slots) return items -def fill_with_resource_packs_and_traps(item_factory: StardewItemFactory, world_options: options.StardewOptions, random: Random, +def fill_with_resource_packs_and_traps(item_factory: StardewItemFactory, options: StardewValleyOptions, random: Random, items_already_added: List[Item], number_locations: int) -> List[Item]: - include_traps = world_options[options.TrapItems] != options.TrapItems.option_no_traps + include_traps = options.trap_items != TrapItems.option_no_traps all_filler_packs = [pack for pack in items_by_group[Group.RESOURCE_PACK]] all_filler_packs.extend(items_by_group[Group.TRASH]) if include_traps: @@ -479,15 +477,15 @@ def fill_with_resource_packs_and_traps(item_factory: StardewItemFactory, world_o if pack.name not in items_already_added_names] trap_items = [pack for pack in items_by_group[Group.TRAP] if pack.name not in items_already_added_names and - (pack.mod_name is None or pack.mod_name in world_options[options.Mods])] + (pack.mod_name is None or pack.mod_name in options.mods)] priority_filler_items = [] priority_filler_items.extend(useful_resource_packs) if include_traps: priority_filler_items.extend(trap_items) - all_filler_packs = remove_excluded_packs(all_filler_packs, world_options) - priority_filler_items = remove_excluded_packs(priority_filler_items, world_options) + all_filler_packs = remove_excluded_packs(all_filler_packs, options) + priority_filler_items = remove_excluded_packs(priority_filler_items, options) number_priority_items = len(priority_filler_items) required_resource_pack = number_locations - len(items_already_added) @@ -521,8 +519,8 @@ def fill_with_resource_packs_and_traps(item_factory: StardewItemFactory, world_o return items -def remove_excluded_packs(packs, world_options): +def remove_excluded_packs(packs, options: StardewValleyOptions): included_packs = [pack for pack in packs if Group.DEPRECATED not in pack.groups] - if world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true: + if options.exclude_ginger_island == ExcludeGingerIsland.option_true: included_packs = [pack for pack in included_packs if Group.GINGER_ISLAND not in pack.groups] return included_packs diff --git a/worlds/stardew_valley/locations.py b/worlds/stardew_valley/locations.py index 67bffa1396..345796b031 100644 --- a/worlds/stardew_valley/locations.py +++ b/worlds/stardew_valley/locations.py @@ -4,10 +4,12 @@ from dataclasses import dataclass from random import Random from typing import Optional, Dict, Protocol, List, FrozenSet -from . import options, data +from . import data +from .options import StardewValleyOptions from .data.fish_data import legendary_fish, special_fish, all_fish from .data.museum_data import all_museum_items from .data.villagers_data import all_villagers +from .options import ExcludeGingerIsland, Friendsanity, ArcadeMachineLocations, SpecialOrderLocations, Cropsanity, Fishsanity, Museumsanity, FestivalLocations, SkillProgression, BuildingProgression, ToolProgression, ElevatorProgression, BackpackProgression from .strings.goal_names import Goal from .strings.region_names import Region @@ -133,12 +135,12 @@ def initialize_groups(): initialize_groups() -def extend_cropsanity_locations(randomized_locations: List[LocationData], world_options): - if world_options[options.Cropsanity] == options.Cropsanity.option_disabled: +def extend_cropsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions): + if options.cropsanity == Cropsanity.option_disabled: return cropsanity_locations = locations_by_tag[LocationTags.CROPSANITY] - cropsanity_locations = filter_ginger_island(world_options, cropsanity_locations) + cropsanity_locations = filter_ginger_island(options, cropsanity_locations) randomized_locations.extend(cropsanity_locations) @@ -157,56 +159,56 @@ def extend_help_wanted_quests(randomized_locations: List[LocationData], desired_ randomized_locations.append(location_table[f"Help Wanted: Gathering {batch + 1}"]) -def extend_fishsanity_locations(randomized_locations: List[LocationData], world_options, random: Random): +def extend_fishsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, random: Random): prefix = "Fishsanity: " - if world_options[options.Fishsanity] == options.Fishsanity.option_none: + if options.fishsanity == Fishsanity.option_none: return - elif world_options[options.Fishsanity] == options.Fishsanity.option_legendaries: + elif options.fishsanity == Fishsanity.option_legendaries: randomized_locations.extend(location_table[f"{prefix}{legendary.name}"] for legendary in legendary_fish) - elif world_options[options.Fishsanity] == options.Fishsanity.option_special: + elif options.fishsanity == Fishsanity.option_special: randomized_locations.extend(location_table[f"{prefix}{special.name}"] for special in special_fish) - elif world_options[options.Fishsanity] == options.Fishsanity.option_randomized: + elif options.fishsanity == Fishsanity.option_randomized: fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish if random.random() < 0.4] - randomized_locations.extend(filter_ginger_island(world_options, fish_locations)) - elif world_options[options.Fishsanity] == options.Fishsanity.option_all: + randomized_locations.extend(filter_ginger_island(options, fish_locations)) + elif options.fishsanity == Fishsanity.option_all: fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish] - randomized_locations.extend(filter_ginger_island(world_options, fish_locations)) - elif world_options[options.Fishsanity] == options.Fishsanity.option_exclude_legendaries: + randomized_locations.extend(filter_ginger_island(options, fish_locations)) + elif options.fishsanity == Fishsanity.option_exclude_legendaries: fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish if fish not in legendary_fish] - randomized_locations.extend(filter_ginger_island(world_options, fish_locations)) - elif world_options[options.Fishsanity] == options.Fishsanity.option_exclude_hard_fish: + randomized_locations.extend(filter_ginger_island(options, fish_locations)) + elif options.fishsanity == Fishsanity.option_exclude_hard_fish: fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish if fish.difficulty < 80] - randomized_locations.extend(filter_ginger_island(world_options, fish_locations)) - elif world_options[options.Fishsanity] == options.Fishsanity.option_only_easy_fish: + randomized_locations.extend(filter_ginger_island(options, fish_locations)) + elif options.fishsanity == Fishsanity.option_only_easy_fish: fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish if fish.difficulty < 50] - randomized_locations.extend(filter_ginger_island(world_options, fish_locations)) + randomized_locations.extend(filter_ginger_island(options, fish_locations)) -def extend_museumsanity_locations(randomized_locations: List[LocationData], museumsanity: int, random: Random): +def extend_museumsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, random: Random): prefix = "Museumsanity: " - if museumsanity == options.Museumsanity.option_none: + if options.museumsanity == Museumsanity.option_none: return - elif museumsanity == options.Museumsanity.option_milestones: + elif options.museumsanity == Museumsanity.option_milestones: randomized_locations.extend(locations_by_tag[LocationTags.MUSEUM_MILESTONES]) - elif museumsanity == options.Museumsanity.option_randomized: + elif options.museumsanity == Museumsanity.option_randomized: randomized_locations.extend(location_table[f"{prefix}{museum_item.name}"] for museum_item in all_museum_items if random.random() < 0.4) - elif museumsanity == options.Museumsanity.option_all: + elif options.museumsanity == Museumsanity.option_all: randomized_locations.extend(location_table[f"{prefix}{museum_item.name}"] for museum_item in all_museum_items) -def extend_friendsanity_locations(randomized_locations: List[LocationData], world_options: options.StardewOptions): - if world_options[options.Friendsanity] == options.Friendsanity.option_none: +def extend_friendsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions): + if options.friendsanity == Friendsanity.option_none: return - exclude_leo = world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true - exclude_non_bachelors = world_options[options.Friendsanity] == options.Friendsanity.option_bachelors - exclude_locked_villagers = world_options[options.Friendsanity] == options.Friendsanity.option_starting_npcs or \ - world_options[options.Friendsanity] == options.Friendsanity.option_bachelors - include_post_marriage_hearts = world_options[options.Friendsanity] == options.Friendsanity.option_all_with_marriage - heart_size = world_options[options.FriendsanityHeartSize] + exclude_leo = options.exclude_ginger_island == ExcludeGingerIsland.option_true + exclude_non_bachelors = options.friendsanity == Friendsanity.option_bachelors + exclude_locked_villagers = options.friendsanity == Friendsanity.option_starting_npcs or \ + options.friendsanity == Friendsanity.option_bachelors + include_post_marriage_hearts = options.friendsanity == Friendsanity.option_all_with_marriage + heart_size = options.friendsanity_heart_size for villager in all_villagers: - if villager.mod_name not in world_options[options.Mods] and villager.mod_name is not None: + if villager.mod_name not in options.mods and villager.mod_name is not None: continue if not villager.available and exclude_locked_villagers: continue @@ -228,38 +230,38 @@ def extend_friendsanity_locations(randomized_locations: List[LocationData], worl randomized_locations.append(location_table[f"Friendsanity: Pet {heart} <3"]) -def extend_festival_locations(randomized_locations: List[LocationData], festival_option: int): - if festival_option == options.FestivalLocations.option_disabled: +def extend_festival_locations(randomized_locations: List[LocationData], options: StardewValleyOptions): + if options.festival_locations == FestivalLocations.option_disabled: return festival_locations = locations_by_tag[LocationTags.FESTIVAL] randomized_locations.extend(festival_locations) - extend_hard_festival_locations(randomized_locations, festival_option) + extend_hard_festival_locations(randomized_locations, options) -def extend_hard_festival_locations(randomized_locations, festival_option: int): - if festival_option != options.FestivalLocations.option_hard: +def extend_hard_festival_locations(randomized_locations, options: StardewValleyOptions): + if options.festival_locations != FestivalLocations.option_hard: return hard_festival_locations = locations_by_tag[LocationTags.FESTIVAL_HARD] randomized_locations.extend(hard_festival_locations) -def extend_special_order_locations(randomized_locations: List[LocationData], world_options): - if world_options[options.SpecialOrderLocations] == options.SpecialOrderLocations.option_disabled: +def extend_special_order_locations(randomized_locations: List[LocationData], options: StardewValleyOptions): + if options.special_order_locations == SpecialOrderLocations.option_disabled: return - include_island = world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_false - board_locations = filter_disabled_locations(world_options, locations_by_tag[LocationTags.SPECIAL_ORDER_BOARD]) + include_island = options.exclude_ginger_island == ExcludeGingerIsland.option_false + board_locations = filter_disabled_locations(options, locations_by_tag[LocationTags.SPECIAL_ORDER_BOARD]) randomized_locations.extend(board_locations) - if world_options[options.SpecialOrderLocations] == options.SpecialOrderLocations.option_board_qi and include_island: - include_arcade = world_options[options.ArcadeMachineLocations] != options.ArcadeMachineLocations.option_disabled + if options.special_order_locations == SpecialOrderLocations.option_board_qi and include_island: + include_arcade = options.arcade_machine_locations != ArcadeMachineLocations.option_disabled qi_orders = [location for location in locations_by_tag[LocationTags.SPECIAL_ORDER_QI] if include_arcade or LocationTags.JUNIMO_KART not in location.tags] randomized_locations.extend(qi_orders) -def extend_walnut_purchase_locations(randomized_locations: List[LocationData], world_options): - if world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true: +def extend_walnut_purchase_locations(randomized_locations: List[LocationData], options: StardewValleyOptions): + if options.exclude_ginger_island == ExcludeGingerIsland.option_true: return randomized_locations.append(location_table["Repair Ticket Machine"]) randomized_locations.append(location_table["Repair Boat Hull"]) @@ -269,82 +271,82 @@ def extend_walnut_purchase_locations(randomized_locations: List[LocationData], w randomized_locations.extend(locations_by_tag[LocationTags.WALNUT_PURCHASE]) -def extend_mandatory_locations(randomized_locations: List[LocationData], world_options): +def extend_mandatory_locations(randomized_locations: List[LocationData], options): mandatory_locations = [location for location in locations_by_tag[LocationTags.MANDATORY]] - filtered_mandatory_locations = filter_disabled_locations(world_options, mandatory_locations) + filtered_mandatory_locations = filter_disabled_locations(options, mandatory_locations) randomized_locations.extend(filtered_mandatory_locations) -def extend_backpack_locations(randomized_locations: List[LocationData], world_options): - if world_options[options.BackpackProgression] == options.BackpackProgression.option_vanilla: +def extend_backpack_locations(randomized_locations: List[LocationData], options: StardewValleyOptions): + if options.backpack_progression == BackpackProgression.option_vanilla: return backpack_locations = [location for location in locations_by_tag[LocationTags.BACKPACK]] - filtered_backpack_locations = filter_modded_locations(world_options, backpack_locations) + filtered_backpack_locations = filter_modded_locations(options, backpack_locations) randomized_locations.extend(filtered_backpack_locations) -def extend_elevator_locations(randomized_locations: List[LocationData], world_options): - if world_options[options.ElevatorProgression] == options.ElevatorProgression.option_vanilla: +def extend_elevator_locations(randomized_locations: List[LocationData], options: StardewValleyOptions): + if options.elevator_progression == ElevatorProgression.option_vanilla: return elevator_locations = [location for location in locations_by_tag[LocationTags.ELEVATOR]] - filtered_elevator_locations = filter_modded_locations(world_options, elevator_locations) + filtered_elevator_locations = filter_modded_locations(options, elevator_locations) randomized_locations.extend(filtered_elevator_locations) def create_locations(location_collector: StardewLocationCollector, - world_options: options.StardewOptions, + options: StardewValleyOptions, random: Random): randomized_locations = [] - extend_mandatory_locations(randomized_locations, world_options) - extend_backpack_locations(randomized_locations, world_options) + extend_mandatory_locations(randomized_locations, options) + extend_backpack_locations(randomized_locations, options) - if not world_options[options.ToolProgression] == options.ToolProgression.option_vanilla: + if not options.tool_progression == ToolProgression.option_vanilla: randomized_locations.extend(locations_by_tag[LocationTags.TOOL_UPGRADE]) - extend_elevator_locations(randomized_locations, world_options) + extend_elevator_locations(randomized_locations, options) - if not world_options[options.SkillProgression] == options.SkillProgression.option_vanilla: + if not options.skill_progression == SkillProgression.option_vanilla: for location in locations_by_tag[LocationTags.SKILL_LEVEL]: - if location.mod_name is None or location.mod_name in world_options[options.Mods]: + if location.mod_name is None or location.mod_name in options.mods: randomized_locations.append(location_table[location.name]) - if not world_options[options.BuildingProgression] == options.BuildingProgression.option_vanilla: + if not options.building_progression == BuildingProgression.option_vanilla: for location in locations_by_tag[LocationTags.BUILDING_BLUEPRINT]: - if location.mod_name is None or location.mod_name in world_options[options.Mods]: + if location.mod_name is None or location.mod_name in options.mods: randomized_locations.append(location_table[location.name]) - if world_options[options.ArcadeMachineLocations] != options.ArcadeMachineLocations.option_disabled: + if options.arcade_machine_locations != ArcadeMachineLocations.option_disabled: randomized_locations.extend(locations_by_tag[LocationTags.ARCADE_MACHINE_VICTORY]) - if world_options[options.ArcadeMachineLocations] == options.ArcadeMachineLocations.option_full_shuffling: + if options.arcade_machine_locations == ArcadeMachineLocations.option_full_shuffling: randomized_locations.extend(locations_by_tag[LocationTags.ARCADE_MACHINE]) - extend_cropsanity_locations(randomized_locations, world_options) - extend_help_wanted_quests(randomized_locations, world_options[options.HelpWantedLocations]) - extend_fishsanity_locations(randomized_locations, world_options, random) - extend_museumsanity_locations(randomized_locations, world_options[options.Museumsanity], random) - extend_friendsanity_locations(randomized_locations, world_options) + extend_cropsanity_locations(randomized_locations, options) + extend_help_wanted_quests(randomized_locations, options.help_wanted_locations.value) + extend_fishsanity_locations(randomized_locations, options, random) + extend_museumsanity_locations(randomized_locations, options, random) + extend_friendsanity_locations(randomized_locations, options) - extend_festival_locations(randomized_locations, world_options[options.FestivalLocations]) - extend_special_order_locations(randomized_locations, world_options) - extend_walnut_purchase_locations(randomized_locations, world_options) + extend_festival_locations(randomized_locations, options) + extend_special_order_locations(randomized_locations, options) + extend_walnut_purchase_locations(randomized_locations, options) for location_data in randomized_locations: location_collector(location_data.name, location_data.code, location_data.region) -def filter_ginger_island(world_options: options.StardewOptions, locations: List[LocationData]) -> List[LocationData]: - include_island = world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_false +def filter_ginger_island(options: StardewValleyOptions, locations: List[LocationData]) -> List[LocationData]: + include_island = options.exclude_ginger_island == ExcludeGingerIsland.option_false return [location for location in locations if include_island or LocationTags.GINGER_ISLAND not in location.tags] -def filter_modded_locations(world_options: options.StardewOptions, locations: List[LocationData]) -> List[LocationData]: - current_mod_names = world_options[options.Mods] +def filter_modded_locations(options: StardewValleyOptions, locations: List[LocationData]) -> List[LocationData]: + current_mod_names = options.mods return [location for location in locations if location.mod_name is None or location.mod_name in current_mod_names] -def filter_disabled_locations(world_options: options.StardewOptions, locations: List[LocationData]) -> List[LocationData]: - locations_first_pass = filter_ginger_island(world_options, locations) - locations_second_pass = filter_modded_locations(world_options, locations_first_pass) +def filter_disabled_locations(options: StardewValleyOptions, locations: List[LocationData]) -> List[LocationData]: + locations_first_pass = filter_ginger_island(options, locations) + locations_second_pass = filter_modded_locations(options, locations_first_pass) return locations_second_pass diff --git a/worlds/stardew_valley/logic.py b/worlds/stardew_valley/logic.py index 377fa0d03b..b2841d1566 100644 --- a/worlds/stardew_valley/logic.py +++ b/worlds/stardew_valley/logic.py @@ -4,7 +4,6 @@ import math from dataclasses import dataclass, field from typing import Dict, Union, Optional, Iterable, Sized, List, Set -from . import options from .data import all_fish, FishItem, all_purchasable_seeds, SeedItem, all_crops, CropItem from .data.bundle_data import BundleItem from .data.crops_data import crops_by_name @@ -20,7 +19,8 @@ from .mods.logic.special_orders import get_modded_special_orders_rules from .mods.logic.skullcavernelevator import has_skull_cavern_elevator_to_floor from .mods.mod_data import ModNames from .mods.logic import magic, skills -from .options import StardewOptions +from .options import Museumsanity, SeasonRandomization, StardewValleyOptions, BuildingProgression, SkillProgression, ToolProgression, Friendsanity, Cropsanity, \ + ExcludeGingerIsland, ElevatorProgression, ArcadeMachineLocations, FestivalLocations, SpecialOrderLocations from .regions import vanilla_regions from .stardew_rule import False_, Reach, Or, True_, Received, Count, And, Has, TotalReceived, StardewRule from .strings.animal_names import Animal, coop_animals, barn_animals @@ -81,10 +81,11 @@ tool_upgrade_prices = { fishing_regions = [Region.beach, Region.town, Region.forest, Region.mountain, Region.island_south, Region.island_west] + @dataclass(frozen=True, repr=False) class StardewLogic: player: int - options: StardewOptions + options: StardewValleyOptions item_rules: Dict[str, StardewRule] = field(default_factory=dict) sapling_rules: Dict[str, StardewRule] = field(default_factory=dict) @@ -398,7 +399,7 @@ class StardewLogic: Building.cellar: self.can_spend_money_at(Region.carpenter, 100000) & self.has_house(2), }) - self.building_rules.update(get_modded_building_rules(self, self.options[options.Mods])) + self.building_rules.update(get_modded_building_rules(self, self.options.mods)) self.quest_rules.update({ Quest.introductions: self.can_reach_region(Region.town), @@ -455,7 +456,7 @@ class StardewLogic: self.can_meet(NPC.wizard) & self.can_meet(NPC.willy), }) - self.quest_rules.update(get_modded_quest_rules(self, self.options[options.Mods])) + self.quest_rules.update(get_modded_quest_rules(self, self.options.mods)) self.festival_rules.update({ FestivalCheck.egg_hunt: self.has_season(Season.spring) & self.can_reach_region(Region.town) & self.can_win_egg_hunt(), @@ -539,7 +540,7 @@ class StardewLogic: self.can_spend_money(80000), # I need this extra rule because money rules aren't additive... }) - self.special_order_rules.update(get_modded_special_orders_rules(self, self.options[options.Mods])) + self.special_order_rules.update(get_modded_special_orders_rules(self, self.options.mods)) def has(self, items: Union[str, (Iterable[str], Sized)], count: Optional[int] = None) -> StardewRule: if isinstance(items, str): @@ -596,7 +597,7 @@ class StardewLogic: return self.has_lived_months(min(8, amount // MONEY_PER_MONTH)) def can_spend_money(self, amount: int) -> StardewRule: - if self.options[options.StartingMoney] == -1: + if self.options.starting_money == -1: return True_() return self.has_lived_months(min(8, amount // (MONEY_PER_MONTH // 5))) @@ -607,7 +608,7 @@ class StardewLogic: if material == ToolMaterial.basic or tool == Tool.scythe: return True_() - if self.options[options.ToolProgression] == options.ToolProgression.option_progressive: + if self.options.tool_progression == ToolProgression.option_progressive: return self.received(f"Progressive {tool}", count=tool_materials[material]) return self.has(f"{material} Bar") & self.can_spend_money(tool_upgrade_prices[material]) @@ -644,7 +645,7 @@ class StardewLogic: if level <= 0: return True_() - if self.options[options.SkillProgression] == options.SkillProgression.option_progressive: + if self.options.skill_progression == SkillProgression.option_progressive: return self.received(f"{skill} Level", count=level) return self.can_earn_skill_level(skill, level) @@ -656,7 +657,7 @@ class StardewLogic: if level <= 0: return True_() - if self.options[options.SkillProgression] == options.SkillProgression.option_progressive: + if self.options.skill_progression == SkillProgression.option_progressive: skills_items = ["Farming Level", "Mining Level", "Foraging Level", "Fishing Level", "Combat Level"] if allow_modded_skills: @@ -672,7 +673,7 @@ class StardewLogic: def has_building(self, building: str) -> StardewRule: carpenter_rule = self.can_reach_region(Region.carpenter) - if not self.options[options.BuildingProgression] == options.BuildingProgression.option_vanilla: + if not self.options.building_progression == BuildingProgression.option_vanilla: count = 1 if building in [Building.coop, Building.barn, Building.shed]: building = f"Progressive {building}" @@ -693,7 +694,7 @@ class StardewLogic: if upgrade_level > 3: return False_() - if not self.options[options.BuildingProgression] == options.BuildingProgression.option_vanilla: + if not self.options.building_progression == BuildingProgression.option_vanilla: return self.received(f"Progressive House", upgrade_level) & self.can_reach_region(Region.carpenter) if upgrade_level == 1: @@ -734,7 +735,7 @@ class StardewLogic: return tool_rule & enemy_rule def can_get_fishing_xp(self) -> StardewRule: - if self.options[options.SkillProgression] == options.SkillProgression.option_progressive: + if self.options.skill_progression == SkillProgression.option_progressive: return self.can_fish() | self.can_crab_pot() return self.can_fish() @@ -746,7 +747,7 @@ class StardewLogic: skill_rule = self.has_skill_level(Skill.fishing, skill_required) region_rule = self.can_reach_any_region(fishing_regions) number_fishing_rod_required = 1 if difficulty < 50 else 2 - if self.options[options.ToolProgression] == options.ToolProgression.option_progressive: + if self.options.tool_progression == ToolProgression.option_progressive: return self.received("Progressive Fishing Rod", number_fishing_rod_required) & skill_rule & region_rule return skill_rule & region_rule @@ -763,7 +764,7 @@ class StardewLogic: return self.has_max_fishing_rod() & skill_rule def can_buy_seed(self, seed: SeedItem) -> StardewRule: - if self.options[options.Cropsanity] == options.Cropsanity.option_disabled: + if self.options.cropsanity == Cropsanity.option_disabled: item_rule = True_() else: item_rule = self.received(seed.name) @@ -781,7 +782,7 @@ class StardewLogic: Fruit.peach: 6000, Fruit.pomegranate: 6000, Fruit.banana: 0, Fruit.mango: 0} received_sapling = self.received(f"{fruit} Sapling") - if self.options[options.Cropsanity] == options.Cropsanity.option_disabled: + if self.options.cropsanity == Cropsanity.option_disabled: allowed_buy_sapling = True_() else: allowed_buy_sapling = received_sapling @@ -824,14 +825,14 @@ class StardewLogic: def can_catch_every_fish(self) -> StardewRule: rules = [self.has_skill_level(Skill.fishing, 10), self.has_max_fishing_rod()] for fish in all_fish: - if self.options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true and \ + if self.options.exclude_ginger_island == ExcludeGingerIsland.option_true and \ fish in island_fish: continue rules.append(self.can_catch_fish(fish)) return And(rules) def has_max_fishing_rod(self) -> StardewRule: - if self.options[options.ToolProgression] == options.ToolProgression.option_progressive: + if self.options.tool_progression == ToolProgression.option_progressive: return self.received(APTool.fishing_rod, 4) return self.can_get_fishing_xp() @@ -875,7 +876,7 @@ class StardewLogic: def can_crab_pot(self, region: str = Generic.any) -> StardewRule: crab_pot_rule = self.has(Craftable.bait) - if self.options[options.SkillProgression] == options.SkillProgression.option_progressive: + if self.options.skill_progression == SkillProgression.option_progressive: crab_pot_rule = crab_pot_rule & self.has(Machine.crab_pot) else: crab_pot_rule = crab_pot_rule & self.can_get_fishing_xp() @@ -926,9 +927,7 @@ class StardewLogic: return region_rule & ((tool_rule & foraging_rule) | magic_rule) def has_max_buffs(self) -> StardewRule: - number_of_movement_buffs: int = self.options[options.NumberOfMovementBuffs] - number_of_luck_buffs: int = self.options[options.NumberOfLuckBuffs] - return self.received(Buff.movement, number_of_movement_buffs) & self.received(Buff.luck, number_of_luck_buffs) + return self.received(Buff.movement, self.options.number_of_movement_buffs.value) & self.received(Buff.luck, self.options.number_of_luck_buffs.value) def get_weapon_rule_for_floor_tier(self, tier: int): if tier >= 4: @@ -946,9 +945,9 @@ class StardewLogic: rules = [] weapon_rule = self.get_weapon_rule_for_floor_tier(tier) rules.append(weapon_rule) - if self.options[options.ToolProgression] == options.ToolProgression.option_progressive: + if self.options.tool_progression == ToolProgression.option_progressive: rules.append(self.has_tool(Tool.pickaxe, ToolMaterial.tiers[tier])) - if self.options[options.SkillProgression] == options.SkillProgression.option_progressive: + if self.options.skill_progression == SkillProgression.option_progressive: combat_tier = min(10, max(0, tier * 2)) rules.append(self.has_skill_level(Skill.combat, combat_tier)) return And(rules) @@ -958,15 +957,15 @@ class StardewLogic: rules = [] weapon_rule = self.get_weapon_rule_for_floor_tier(tier) rules.append(weapon_rule) - if self.options[options.ToolProgression] == options.ToolProgression.option_progressive: + if self.options.tool_progression == ToolProgression.option_progressive: rules.append(self.has_tool(Tool.pickaxe, ToolMaterial.tiers[tier])) - if self.options[options.SkillProgression] == options.SkillProgression.option_progressive: + if self.options.skill_progression == SkillProgression.option_progressive: combat_tier = min(10, max(0, tier * 2)) rules.append(self.has_skill_level(Skill.combat, combat_tier)) return And(rules) def has_mine_elevator_to_floor(self, floor: int) -> StardewRule: - if self.options[options.ElevatorProgression] != options.ElevatorProgression.option_vanilla: + if self.options.elevator_progression != ElevatorProgression.option_vanilla: return self.received("Progressive Mine Elevator", count=int(floor / 5)) return True_() @@ -984,9 +983,9 @@ class StardewLogic: weapon_rule = self.has_great_weapon() rules.append(weapon_rule) rules.append(self.can_cook()) - if self.options[options.ToolProgression] == options.ToolProgression.option_progressive: + if self.options.tool_progression == ToolProgression.option_progressive: rules.append(self.received("Progressive Pickaxe", min(4, max(0, tier + 2)))) - if self.options[options.SkillProgression] == options.SkillProgression.option_progressive: + if self.options.skill_progression == SkillProgression.option_progressive: skill_tier = min(10, max(0, tier * 2 + 6)) rules.extend({self.has_skill_level(Skill.combat, skill_tier), self.has_skill_level(Skill.mining, skill_tier)}) @@ -1005,20 +1004,20 @@ class StardewLogic: self.can_progress_easily_in_the_skull_cavern_from_floor(previous_previous_elevator))) & has_mine_elevator def has_jotpk_power_level(self, power_level: int) -> StardewRule: - if self.options[options.ArcadeMachineLocations] != options.ArcadeMachineLocations.option_full_shuffling: + if self.options.arcade_machine_locations != ArcadeMachineLocations.option_full_shuffling: return True_() jotpk_buffs = ["JotPK: Progressive Boots", "JotPK: Progressive Gun", "JotPK: Progressive Ammo", "JotPK: Extra Life", "JotPK: Increased Drop Rate"] return self.received(jotpk_buffs, power_level) def has_junimo_kart_power_level(self, power_level: int) -> StardewRule: - if self.options[options.ArcadeMachineLocations] != options.ArcadeMachineLocations.option_full_shuffling: + if self.options.arcade_machine_locations != ArcadeMachineLocations.option_full_shuffling: return True_() return self.received("Junimo Kart: Extra Life", power_level) def has_junimo_kart_max_level(self) -> StardewRule: play_rule = self.can_reach_region(Region.junimo_kart_3) - if self.options[options.ArcadeMachineLocations] != options.ArcadeMachineLocations.option_full_shuffling: + if self.options.arcade_machine_locations != ArcadeMachineLocations.option_full_shuffling: return play_rule return self.has_junimo_kart_power_level(8) @@ -1043,12 +1042,12 @@ class StardewLogic: def has_relationship(self, npc: str, hearts: int = 1) -> StardewRule: if hearts <= 0: return True_() - friendsanity = self.options[options.Friendsanity] - if friendsanity == options.Friendsanity.option_none: + friendsanity = self.options.friendsanity + if friendsanity == Friendsanity.option_none: return self.can_earn_relationship(npc, hearts) if npc not in all_villagers_by_name: if npc == NPC.pet: - if friendsanity == options.Friendsanity.option_bachelors: + if friendsanity == Friendsanity.option_bachelors: return self.can_befriend_pet(hearts) return self.received_hearts(NPC.pet, hearts) if npc == Generic.any or npc == Generic.bachelor: @@ -1078,11 +1077,11 @@ class StardewLogic: if not self.npc_is_in_current_slot(npc): return True_() villager = all_villagers_by_name[npc] - if friendsanity == options.Friendsanity.option_bachelors and not villager.bachelor: + if friendsanity == Friendsanity.option_bachelors and not villager.bachelor: return self.can_earn_relationship(npc, hearts) - if friendsanity == options.Friendsanity.option_starting_npcs and not villager.available: + if friendsanity == Friendsanity.option_starting_npcs and not villager.available: return self.can_earn_relationship(npc, hearts) - is_capped_at_8 = villager.bachelor and friendsanity != options.Friendsanity.option_all_with_marriage + is_capped_at_8 = villager.bachelor and friendsanity != Friendsanity.option_all_with_marriage if is_capped_at_8 and hearts > 8: return self.received_hearts(villager, 8) & self.can_earn_relationship(npc, hearts) return self.received_hearts(villager, hearts) @@ -1090,7 +1089,7 @@ class StardewLogic: def received_hearts(self, npc: Union[str, Villager], hearts: int) -> StardewRule: if isinstance(npc, Villager): return self.received_hearts(npc.name, hearts) - heart_size: int = self.options[options.FriendsanityHeartSize] + heart_size = self.options.friendsanity_heart_size.value return self.received(self.heart(npc), math.ceil(hearts / heart_size)) def can_meet(self, npc: str) -> StardewRule: @@ -1122,13 +1121,13 @@ class StardewLogic: if hearts <= 0: return True_() - heart_size: int = self.options[options.FriendsanityHeartSize] + heart_size = self.options.friendsanity_heart_size.value previous_heart = hearts - heart_size previous_heart_rule = self.has_relationship(npc, previous_heart) if npc == NPC.pet: earn_rule = self.can_befriend_pet(hearts) - elif npc == NPC.wizard and ModNames.magic in self.options[options.Mods]: + elif npc == NPC.wizard and ModNames.magic in self.options.mods: earn_rule = self.can_meet(npc) & self.has_lived_months(hearts) elif npc in all_villagers_by_name: if not self.npc_is_in_current_slot(npc): @@ -1284,7 +1283,7 @@ class StardewLogic: return self.has_lived_months(8) def can_speak_dwarf(self) -> StardewRule: - if self.options[options.Museumsanity] == options.Museumsanity.option_none: + if self.options.museumsanity == Museumsanity.option_none: return And([self.can_donate_museum_item(item) for item in dwarf_scrolls]) return self.received("Dwarvish Translation Guide") @@ -1334,7 +1333,7 @@ class StardewLogic: def can_complete_museum(self) -> StardewRule: rules = [self.can_reach_region(Region.museum), self.can_mine_perfectly()] - if self.options[options.Museumsanity] != options.Museumsanity.option_none: + if self.options.museumsanity != Museumsanity.option_none: rules.append(self.received("Traveling Merchant Metal Detector", 4)) for donation in all_museum_items: @@ -1345,9 +1344,9 @@ class StardewLogic: if season == Generic.any: return True_() seasons_order = [Season.spring, Season.summer, Season.fall, Season.winter] - if self.options[options.SeasonRandomization] == options.SeasonRandomization.option_progressive: + if self.options.season_randomization == SeasonRandomization.option_progressive: return self.received(Season.progressive, seasons_order.index(season)) - if self.options[options.SeasonRandomization] == options.SeasonRandomization.option_disabled: + if self.options.season_randomization == SeasonRandomization.option_disabled: if season == Season.spring: return True_() return self.has_lived_months(1) @@ -1371,19 +1370,19 @@ class StardewLogic: return self.received("Month End", number) def has_rusty_key(self) -> StardewRule: - if self.options[options.Museumsanity] == options.Museumsanity.option_none: + if self.options.museumsanity == Museumsanity.option_none: required_donations = 80 # It's 60, but without a metal detector I'd rather overshoot so players don't get screwed by RNG return self.has([item.name for item in all_museum_items], required_donations) & self.can_reach_region(Region.museum) return self.received(Wallet.rusty_key) def can_win_egg_hunt(self) -> StardewRule: - number_of_movement_buffs: int = self.options[options.NumberOfMovementBuffs] - if self.options[options.FestivalLocations] == options.FestivalLocations.option_hard or number_of_movement_buffs < 2: + number_of_movement_buffs = self.options.number_of_movement_buffs.value + if self.options.festival_locations == FestivalLocations.option_hard or number_of_movement_buffs < 2: return True_() return self.received(Buff.movement, number_of_movement_buffs // 2) def can_succeed_luau_soup(self) -> StardewRule: - if self.options[options.FestivalLocations] != options.FestivalLocations.option_hard: + if self.options.festival_locations != FestivalLocations.option_hard: return True_() eligible_fish = [Fish.blobfish, Fish.crimsonfish, "Ice Pip", Fish.lava_eel, Fish.legend, Fish.angler, Fish.catfish, Fish.glacierfish, Fish.mutant_carp, Fish.spookfish, Fish.stingray, Fish.sturgeon, "Super Cucumber"] @@ -1398,7 +1397,7 @@ class StardewLogic: return Or(fish_rule) | Or(aged_rule) def can_succeed_grange_display(self) -> StardewRule: - if self.options[options.FestivalLocations] != options.FestivalLocations.option_hard: + if self.options.festival_locations != FestivalLocations.option_hard: return True_() animal_rule = self.has_animal(Generic.any) artisan_rule = self.can_keg(Generic.any) | self.can_preserves_jar(Generic.any) @@ -1527,12 +1526,12 @@ class StardewLogic: return blacksmith_access & self.has(geode) def has_island_trader(self) -> StardewRule: - if self.options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true: + if self.options.exclude_ginger_island == ExcludeGingerIsland.option_true: return False_() return self.can_reach_region(Region.island_trader) def has_walnut(self, number: int) -> StardewRule: - if self.options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true: + if self.options.exclude_ginger_island == ExcludeGingerIsland.option_true: return False_() if number <= 0: return True_() @@ -1592,7 +1591,7 @@ class StardewLogic: def npc_is_in_current_slot(self, name: str) -> bool: npc = all_villagers_by_name[name] mod = npc.mod_name - return mod is None or mod in self.options[options.Mods] + return mod is None or mod in self.options.mods def can_do_combat_at_level(self, level: str) -> StardewRule: if level == Performance.basic: @@ -1612,7 +1611,7 @@ class StardewLogic: return tool_rule | spell_rule def has_prismatic_jelly_reward_access(self) -> StardewRule: - if self.options[options.SpecialOrderLocations] == options.SpecialOrderLocations.option_disabled: + if self.options.special_order_locations == SpecialOrderLocations.option_disabled: return self.can_complete_special_order("Prismatic Jelly") return self.received("Monster Musk Recipe") diff --git a/worlds/stardew_valley/mods/logic/deepwoods.py b/worlds/stardew_valley/mods/logic/deepwoods.py index f6ecd6d828..2aa90e5b76 100644 --- a/worlds/stardew_valley/mods/logic/deepwoods.py +++ b/worlds/stardew_valley/mods/logic/deepwoods.py @@ -17,14 +17,14 @@ def can_reach_woods_depth(vanilla_logic, depth: int) -> StardewRule: if depth > 50: rules.append(vanilla_logic.can_do_combat_at_level(Performance.great) & vanilla_logic.can_cook() & vanilla_logic.received(ModTransportation.woods_obelisk)) - if vanilla_logic.options[options.SkillProgression] == options.SkillProgression.option_progressive: + if vanilla_logic.options.skill_progression == options.SkillProgression.option_progressive: combat_tier = min(10, max(0, tier + 5)) rules.append(vanilla_logic.has_skill_level(Skill.combat, combat_tier)) return And(rules) def has_woods_rune_to_depth(vanilla_logic, floor: int) -> StardewRule: - if vanilla_logic.options[options.ElevatorProgression] == options.ElevatorProgression.option_vanilla: + if vanilla_logic.options.elevator_progression == options.ElevatorProgression.option_vanilla: return True_() return vanilla_logic.received("Progressive Woods Obelisk Sigils", count=int(floor / 10)) diff --git a/worlds/stardew_valley/mods/logic/magic.py b/worlds/stardew_valley/mods/logic/magic.py index a084c6aa91..709376399c 100644 --- a/worlds/stardew_valley/mods/logic/magic.py +++ b/worlds/stardew_valley/mods/logic/magic.py @@ -7,19 +7,19 @@ from ... import options def can_use_clear_debris_instead_of_tool_level(vanilla_logic, level: int) -> StardewRule: - if ModNames.magic not in vanilla_logic.options[options.Mods]: + if ModNames.magic not in vanilla_logic.options.mods: return False_() return vanilla_logic.received(MagicSpell.clear_debris) & can_use_altar(vanilla_logic) & vanilla_logic.received(ModSkillLevel.magic_level, level) def can_use_altar(vanilla_logic) -> StardewRule: - if ModNames.magic not in vanilla_logic.options[options.Mods]: + if ModNames.magic not in vanilla_logic.options.mods: return False_() return vanilla_logic.can_reach_region(MagicRegion.altar) def has_any_spell(vanilla_logic) -> StardewRule: - if ModNames.magic not in vanilla_logic.options[options.Mods]: + if ModNames.magic not in vanilla_logic.options.mods: return False_() return can_use_altar(vanilla_logic) @@ -40,7 +40,7 @@ def has_support_spell_count(vanilla_logic, count: int) -> StardewRule: def has_decent_spells(vanilla_logic) -> StardewRule: - if ModNames.magic not in vanilla_logic.options[options.Mods]: + if ModNames.magic not in vanilla_logic.options.mods: return False_() magic_resource_rule = can_use_altar(vanilla_logic) & vanilla_logic.received(ModSkillLevel.magic_level, 2) magic_attack_options_rule = has_attack_spell_count(vanilla_logic, 1) @@ -48,7 +48,7 @@ def has_decent_spells(vanilla_logic) -> StardewRule: def has_good_spells(vanilla_logic) -> StardewRule: - if ModNames.magic not in vanilla_logic.options[options.Mods]: + if ModNames.magic not in vanilla_logic.options.mods: return False_() magic_resource_rule = can_use_altar(vanilla_logic) & vanilla_logic.received(ModSkillLevel.magic_level, 4) magic_attack_options_rule = has_attack_spell_count(vanilla_logic, 2) @@ -57,7 +57,7 @@ def has_good_spells(vanilla_logic) -> StardewRule: def has_great_spells(vanilla_logic) -> StardewRule: - if ModNames.magic not in vanilla_logic.options[options.Mods]: + if ModNames.magic not in vanilla_logic.options.mods: return False_() magic_resource_rule = can_use_altar(vanilla_logic) & vanilla_logic.received(ModSkillLevel.magic_level, 6) magic_attack_options_rule = has_attack_spell_count(vanilla_logic, 3) @@ -66,7 +66,7 @@ def has_great_spells(vanilla_logic) -> StardewRule: def has_amazing_spells(vanilla_logic) -> StardewRule: - if ModNames.magic not in vanilla_logic.options[options.Mods]: + if ModNames.magic not in vanilla_logic.options.mods: return False_() magic_resource_rule = can_use_altar(vanilla_logic) & vanilla_logic.received(ModSkillLevel.magic_level, 8) magic_attack_options_rule = has_attack_spell_count(vanilla_logic, 4) @@ -75,6 +75,6 @@ def has_amazing_spells(vanilla_logic) -> StardewRule: def can_blink(vanilla_logic) -> StardewRule: - if ModNames.magic not in vanilla_logic.options[options.Mods]: + if ModNames.magic not in vanilla_logic.options.mods: return False_() return vanilla_logic.received(MagicSpell.blink) & can_use_altar(vanilla_logic) diff --git a/worlds/stardew_valley/mods/logic/skills.py b/worlds/stardew_valley/mods/logic/skills.py index 05b4c623e1..24402a088b 100644 --- a/worlds/stardew_valley/mods/logic/skills.py +++ b/worlds/stardew_valley/mods/logic/skills.py @@ -29,17 +29,17 @@ def append_mod_skill_level(skills_items: List[str], active_mods): def can_earn_mod_skill_level(logic, skill: str, level: int) -> StardewRule: - if ModNames.luck_skill in logic.options[options.Mods] and skill == ModSkill.luck: + if ModNames.luck_skill in logic.options.mods and skill == ModSkill.luck: return can_earn_luck_skill_level(logic, level) - if ModNames.magic in logic.options[options.Mods] and skill == ModSkill.magic: + if ModNames.magic in logic.options.mods and skill == ModSkill.magic: return can_earn_magic_skill_level(logic, level) - if ModNames.socializing_skill in logic.options[options.Mods] and skill == ModSkill.socializing: + if ModNames.socializing_skill in logic.options.mods and skill == ModSkill.socializing: return can_earn_socializing_skill_level(logic, level) - if ModNames.archaeology in logic.options[options.Mods] and skill == ModSkill.archaeology: + if ModNames.archaeology in logic.options.mods and skill == ModSkill.archaeology: return can_earn_archaeology_skill_level(logic, level) - if ModNames.cooking_skill in logic.options[options.Mods] and skill == ModSkill.cooking: + if ModNames.cooking_skill in logic.options.mods and skill == ModSkill.cooking: return can_earn_cooking_skill_level(logic, level) - if ModNames.binning_skill in logic.options[options.Mods] and skill == ModSkill.binning: + if ModNames.binning_skill in logic.options.mods and skill == ModSkill.binning: return can_earn_binning_skill_level(logic, level) return False_() @@ -65,7 +65,7 @@ def can_earn_magic_skill_level(vanilla_logic, level: int) -> StardewRule: def can_earn_socializing_skill_level(vanilla_logic, level: int) -> StardewRule: villager_count = [] for villager in all_villagers: - if villager.mod_name in vanilla_logic.options[options.Mods] or villager.mod_name is None: + if villager.mod_name in vanilla_logic.options.mods or villager.mod_name is None: villager_count.append(vanilla_logic.can_earn_relationship(villager.name, level)) return Count(level * 2, villager_count) diff --git a/worlds/stardew_valley/mods/logic/skullcavernelevator.py b/worlds/stardew_valley/mods/logic/skullcavernelevator.py index 74db86d89a..9a5140ae39 100644 --- a/worlds/stardew_valley/mods/logic/skullcavernelevator.py +++ b/worlds/stardew_valley/mods/logic/skullcavernelevator.py @@ -4,7 +4,7 @@ from ... import options def has_skull_cavern_elevator_to_floor(self, floor: int) -> StardewRule: - if self.options[options.ElevatorProgression] != options.ElevatorProgression.option_vanilla and \ - ModNames.skull_cavern_elevator in self.options[options.Mods]: + if self.options.elevator_progression != options.ElevatorProgression.option_vanilla and \ + ModNames.skull_cavern_elevator in self.options.mods: return self.received("Progressive Skull Cavern Elevator", floor // 25) return True_() diff --git a/worlds/stardew_valley/options.py b/worlds/stardew_valley/options.py index 78de9e8dbb..75573359a5 100644 --- a/worlds/stardew_valley/options.py +++ b/worlds/stardew_valley/options.py @@ -1,29 +1,9 @@ from dataclasses import dataclass -from typing import Dict, Union, Protocol, runtime_checkable, ClassVar +from typing import Dict -from Options import Option, Range, DeathLink, SpecialRange, Toggle, Choice, OptionSet +from Options import Range, SpecialRange, Toggle, Choice, OptionSet, PerGameCommonOptions, DeathLink, Option from .mods.mod_data import ModNames -@runtime_checkable -class StardewOption(Protocol): - internal_name: ClassVar[str] - - -@dataclass -class StardewOptions: - options: Dict[str, Union[bool, int, str]] - - def __getitem__(self, item: Union[str, StardewOption]) -> Union[bool, int, str]: - if isinstance(item, StardewOption): - item = item.internal_name - - return self.options.get(item, None) - - def __setitem__(self, key: Union[str, StardewOption], value: Union[bool, int, str]): - if isinstance(key, StardewOption): - key = key.internal_name - self.options[key] = value - class Goal(Choice): """What's your goal with this play-through? @@ -553,56 +533,39 @@ class Mods(OptionSet): } -stardew_valley_option_classes = [ - Goal, - StartingMoney, - ProfitMargin, - BundleRandomization, - BundlePrice, - EntranceRandomization, - SeasonRandomization, - Cropsanity, - BackpackProgression, - ToolProgression, - SkillProgression, - BuildingProgression, - FestivalLocations, - ElevatorProgression, - ArcadeMachineLocations, - SpecialOrderLocations, - HelpWantedLocations, - Fishsanity, - Museumsanity, - Friendsanity, - FriendsanityHeartSize, - NumberOfMovementBuffs, - NumberOfLuckBuffs, - ExcludeGingerIsland, - TrapItems, - MultipleDaySleepEnabled, - MultipleDaySleepCost, - ExperienceMultiplier, - FriendshipMultiplier, - DebrisMultiplier, - QuickStart, - Gifting, - Mods, -] -stardew_valley_options: Dict[str, type(Option)] = {option.internal_name: option for option in - stardew_valley_option_classes} -default_options = {option.internal_name: option.default for option in stardew_valley_options.values()} -stardew_valley_options["death_link"] = DeathLink - - -def fetch_options(world, player: int) -> StardewOptions: - return StardewOptions({option: get_option_value(world, player, option) for option in stardew_valley_options}) - - -def get_option_value(world, player: int, name: str) -> Union[bool, int]: - assert name in stardew_valley_options, f"{name} is not a valid option for Stardew Valley." - - value = getattr(world, name) - - if issubclass(stardew_valley_options[name], Toggle): - return bool(value[player].value) - return value[player].value +@dataclass +class StardewValleyOptions(PerGameCommonOptions): + goal: Goal + starting_money: StartingMoney + profit_margin: ProfitMargin + bundle_randomization: BundleRandomization + bundle_price: BundlePrice + entrance_randomization: EntranceRandomization + season_randomization: SeasonRandomization + cropsanity: Cropsanity + backpack_progression: BackpackProgression + tool_progression: ToolProgression + skill_progression: SkillProgression + building_progression: BuildingProgression + festival_locations: FestivalLocations + elevator_progression: ElevatorProgression + arcade_machine_locations: ArcadeMachineLocations + special_order_locations: SpecialOrderLocations + help_wanted_locations: HelpWantedLocations + fishsanity: Fishsanity + museumsanity: Museumsanity + friendsanity: Friendsanity + friendsanity_heart_size: FriendsanityHeartSize + number_of_movement_buffs: NumberOfMovementBuffs + number_of_luck_buffs: NumberOfLuckBuffs + exclude_ginger_island: ExcludeGingerIsland + trap_items: TrapItems + multiple_day_sleep_enabled: MultipleDaySleepEnabled + multiple_day_sleep_cost: MultipleDaySleepCost + experience_multiplier: ExperienceMultiplier + friendship_multiplier: FriendshipMultiplier + debris_multiplier: DebrisMultiplier + quick_start: QuickStart + gifting: Gifting + mods: Mods + death_link: DeathLink diff --git a/worlds/stardew_valley/regions.py b/worlds/stardew_valley/regions.py index 60cad4c136..e8daa772d8 100644 --- a/worlds/stardew_valley/regions.py +++ b/worlds/stardew_valley/regions.py @@ -2,11 +2,10 @@ from random import Random from typing import Iterable, Dict, Protocol, List, Tuple, Set from BaseClasses import Region, Entrance -from . import options +from .options import EntranceRandomization, ExcludeGingerIsland, Museumsanity from .strings.entrance_names import Entrance from .strings.region_names import Region from .region_classes import RegionData, ConnectionData, RandomizationFlag -from .options import StardewOptions from .mods.mod_regions import ModDataList @@ -397,12 +396,12 @@ vanilla_connections = [ ] -def create_final_regions(world_options: StardewOptions) -> List[RegionData]: +def create_final_regions(world_options) -> List[RegionData]: final_regions = [] final_regions.extend(vanilla_regions) - if world_options[options.Mods] is None: + if world_options.mods is None: return final_regions - for mod in world_options[options.Mods]: + for mod in world_options.mods.value: if mod not in ModDataList: continue for mod_region in ModDataList[mod].regions: @@ -417,19 +416,19 @@ def create_final_regions(world_options: StardewOptions) -> List[RegionData]: return final_regions -def create_final_connections(world_options: StardewOptions) -> List[ConnectionData]: +def create_final_connections(world_options) -> List[ConnectionData]: final_connections = [] final_connections.extend(vanilla_connections) - if world_options[options.Mods] is None: + if world_options.mods is None: return final_connections - for mod in world_options[options.Mods]: + for mod in world_options.mods.value: if mod not in ModDataList: continue final_connections.extend(ModDataList[mod].connections) return final_connections -def create_regions(region_factory: RegionFactory, random: Random, world_options: StardewOptions) -> Tuple[ +def create_regions(region_factory: RegionFactory, random: Random, world_options) -> Tuple[ Iterable[Region], Dict[str, str]]: final_regions = create_final_regions(world_options) regions: Dict[str: Region] = {region.name: region_factory(region.name, region.exits) for region in @@ -448,21 +447,21 @@ def create_regions(region_factory: RegionFactory, random: Random, world_options: return regions.values(), randomized_data -def randomize_connections(random: Random, world_options: StardewOptions, regions_by_name) -> Tuple[ +def randomize_connections(random: Random, world_options, regions_by_name) -> Tuple[ List[ConnectionData], Dict[str, str]]: connections_to_randomize = [] final_connections = create_final_connections(world_options) connections_by_name: Dict[str, ConnectionData] = {connection.name: connection for connection in final_connections} - if world_options[options.EntranceRandomization] == options.EntranceRandomization.option_pelican_town: + if world_options.entrance_randomization == EntranceRandomization.option_pelican_town: connections_to_randomize = [connection for connection in final_connections if RandomizationFlag.PELICAN_TOWN in connection.flag] - elif world_options[options.EntranceRandomization] == options.EntranceRandomization.option_non_progression: + elif world_options.entrance_randomization == EntranceRandomization.option_non_progression: connections_to_randomize = [connection for connection in final_connections if RandomizationFlag.NON_PROGRESSION in connection.flag] - elif world_options[options.EntranceRandomization] == options.EntranceRandomization.option_buildings: + elif world_options.entrance_randomization == EntranceRandomization.option_buildings: connections_to_randomize = [connection for connection in final_connections if RandomizationFlag.BUILDINGS in connection.flag] - elif world_options[options.EntranceRandomization] == options.EntranceRandomization.option_chaos: + elif world_options.entrance_randomization == EntranceRandomization.option_chaos: connections_to_randomize = [connection for connection in final_connections if RandomizationFlag.BUILDINGS in connection.flag] connections_to_randomize = exclude_island_if_necessary(connections_to_randomize, world_options) @@ -491,8 +490,8 @@ def randomize_connections(random: Random, world_options: StardewOptions, regions def remove_excluded_entrances(connections_to_randomize, world_options): - exclude_island = world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true - exclude_sewers = world_options[options.Museumsanity] == options.Museumsanity.option_none + exclude_island = world_options.exclude_ginger_island == ExcludeGingerIsland.option_true + exclude_sewers = world_options.museumsanity == Museumsanity.option_none if exclude_island: connections_to_randomize = [connection for connection in connections_to_randomize if RandomizationFlag.GINGER_ISLAND not in connection.flag] if exclude_sewers: @@ -502,7 +501,7 @@ def remove_excluded_entrances(connections_to_randomize, world_options): def exclude_island_if_necessary(connections_to_randomize: List[ConnectionData], world_options) -> List[ConnectionData]: - exclude_island = world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true + exclude_island = world_options.exclude_ginger_island == ExcludeGingerIsland.option_true if exclude_island: connections_to_randomize = [connection for connection in connections_to_randomize if RandomizationFlag.GINGER_ISLAND not in connection.flag] diff --git a/worlds/stardew_valley/rules.py b/worlds/stardew_valley/rules.py index 34ee1f807d..f56dec39a1 100644 --- a/worlds/stardew_valley/rules.py +++ b/worlds/stardew_valley/rules.py @@ -1,10 +1,10 @@ import itertools -from typing import Dict, List +from typing import List from BaseClasses import MultiWorld from worlds.generic import Rules as MultiWorldRules -from . import options, locations -from .bundles import Bundle +from .options import StardewValleyOptions, ToolProgression, BuildingProgression, SkillProgression, ExcludeGingerIsland, Cropsanity, SpecialOrderLocations, Museumsanity, \ + BackpackProgression, ArcadeMachineLocations from .strings.entrance_names import dig_to_mines_floor, dig_to_skull_floor, Entrance, move_to_woods_depth, \ DeepWoodsEntrance, AlecEntrance, MagicEntrance from .data.museum_data import all_museum_items, all_museum_minerals, all_museum_artifacts, \ @@ -13,9 +13,8 @@ from .data.museum_data import all_museum_items, all_museum_minerals, all_museum_ from .strings.region_names import Region from .mods.mod_data import ModNames from .mods.logic import magic, deepwoods -from .locations import LocationTags +from .locations import LocationTags, locations_by_tag from .logic import StardewLogic, And, tool_upgrade_prices -from .options import StardewOptions from .strings.ap_names.transport_names import Transportation from .strings.artisan_good_names import ArtisanGood from .strings.calendar_names import Weekday @@ -28,251 +27,256 @@ from .strings.villager_names import NPC, ModNPC from .strings.wallet_item_names import Wallet -def set_rules(multi_world: MultiWorld, player: int, world_options: StardewOptions, logic: StardewLogic, - current_bundles: Dict[str, Bundle]): - all_location_names = list(location.name for location in multi_world.get_locations(player)) +def set_rules(world): + multiworld = world.multiworld + world_options = world.options + player = world.player + logic = world.logic + current_bundles = world.modified_bundles + + all_location_names = list(location.name for location in multiworld.get_locations(player)) - set_entrance_rules(logic, multi_world, player, world_options) + set_entrance_rules(logic, multiworld, player, world_options) - set_ginger_island_rules(logic, multi_world, player, world_options) + set_ginger_island_rules(logic, multiworld, player, world_options) # Those checks do not exist if ToolProgression is vanilla - if world_options[options.ToolProgression] != options.ToolProgression.option_vanilla: - MultiWorldRules.add_rule(multi_world.get_location("Purchase Fiberglass Rod", player), + if world_options.tool_progression != ToolProgression.option_vanilla: + MultiWorldRules.add_rule(multiworld.get_location("Purchase Fiberglass Rod", player), (logic.has_skill_level(Skill.fishing, 2) & logic.can_spend_money(1800)).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Purchase Iridium Rod", player), + MultiWorldRules.add_rule(multiworld.get_location("Purchase Iridium Rod", player), (logic.has_skill_level(Skill.fishing, 6) & logic.can_spend_money(7500)).simplify()) materials = [None, "Copper", "Iron", "Gold", "Iridium"] tool = [Tool.hoe, Tool.pickaxe, Tool.axe, Tool.watering_can, Tool.watering_can, Tool.trash_can] for (previous, material), tool in itertools.product(zip(materials[:4], materials[1:]), tool): if previous is None: - MultiWorldRules.add_rule(multi_world.get_location(f"{material} {tool} Upgrade", player), + MultiWorldRules.add_rule(multiworld.get_location(f"{material} {tool} Upgrade", player), (logic.has(f"{material} Ore") & logic.can_spend_money(tool_upgrade_prices[material])).simplify()) else: - MultiWorldRules.add_rule(multi_world.get_location(f"{material} {tool} Upgrade", player), + MultiWorldRules.add_rule(multiworld.get_location(f"{material} {tool} Upgrade", player), (logic.has(f"{material} Ore") & logic.has_tool(tool, previous) & logic.can_spend_money(tool_upgrade_prices[material])).simplify()) - set_skills_rules(logic, multi_world, player, world_options) + set_skills_rules(logic, multiworld, player, world_options) # Bundles for bundle in current_bundles.values(): - location = multi_world.get_location(bundle.get_name_with_bundle(), player) + location = multiworld.get_location(bundle.get_name_with_bundle(), player) rules = logic.can_complete_bundle(bundle.requirements, bundle.number_required) simplified_rules = rules.simplify() MultiWorldRules.set_rule(location, simplified_rules) - MultiWorldRules.add_rule(multi_world.get_location("Complete Crafts Room", player), + MultiWorldRules.add_rule(multiworld.get_location("Complete Crafts Room", player), And(logic.can_reach_location(bundle.name) - for bundle in locations.locations_by_tag[LocationTags.CRAFTS_ROOM_BUNDLE]).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Complete Pantry", player), + for bundle in locations_by_tag[LocationTags.CRAFTS_ROOM_BUNDLE]).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Complete Pantry", player), And(logic.can_reach_location(bundle.name) - for bundle in locations.locations_by_tag[LocationTags.PANTRY_BUNDLE]).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Complete Fish Tank", player), + for bundle in locations_by_tag[LocationTags.PANTRY_BUNDLE]).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Complete Fish Tank", player), And(logic.can_reach_location(bundle.name) - for bundle in locations.locations_by_tag[LocationTags.FISH_TANK_BUNDLE]).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Complete Boiler Room", player), + for bundle in locations_by_tag[LocationTags.FISH_TANK_BUNDLE]).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Complete Boiler Room", player), And(logic.can_reach_location(bundle.name) - for bundle in locations.locations_by_tag[LocationTags.BOILER_ROOM_BUNDLE]).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Complete Bulletin Board", player), + for bundle in locations_by_tag[LocationTags.BOILER_ROOM_BUNDLE]).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Complete Bulletin Board", player), And(logic.can_reach_location(bundle.name) for bundle - in locations.locations_by_tag[LocationTags.BULLETIN_BOARD_BUNDLE]).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Complete Vault", player), + in locations_by_tag[LocationTags.BULLETIN_BOARD_BUNDLE]).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Complete Vault", player), And(logic.can_reach_location(bundle.name) - for bundle in locations.locations_by_tag[LocationTags.VAULT_BUNDLE]).simplify()) + for bundle in locations_by_tag[LocationTags.VAULT_BUNDLE]).simplify()) # Buildings - if world_options[options.BuildingProgression] != options.BuildingProgression.option_vanilla: - for building in locations.locations_by_tag[LocationTags.BUILDING_BLUEPRINT]: - if building.mod_name is not None and building.mod_name not in world_options[options.Mods]: + if world_options.building_progression != BuildingProgression.option_vanilla: + for building in locations_by_tag[LocationTags.BUILDING_BLUEPRINT]: + if building.mod_name is not None and building.mod_name not in world_options.mods: continue - MultiWorldRules.set_rule(multi_world.get_location(building.name, player), + MultiWorldRules.set_rule(multiworld.get_location(building.name, player), logic.building_rules[building.name.replace(" Blueprint", "")].simplify()) - set_cropsanity_rules(all_location_names, logic, multi_world, player, world_options) - set_story_quests_rules(all_location_names, logic, multi_world, player, world_options) - set_special_order_rules(all_location_names, logic, multi_world, player, world_options) - set_help_wanted_quests_rules(logic, multi_world, player, world_options) - set_fishsanity_rules(all_location_names, logic, multi_world, player) - set_museumsanity_rules(all_location_names, logic, multi_world, player, world_options) - set_friendsanity_rules(all_location_names, logic, multi_world, player) - set_backpack_rules(logic, multi_world, player, world_options) - set_festival_rules(all_location_names, logic, multi_world, player) + set_cropsanity_rules(all_location_names, logic, multiworld, player, world_options) + set_story_quests_rules(all_location_names, logic, multiworld, player, world_options) + set_special_order_rules(all_location_names, logic, multiworld, player, world_options) + set_help_wanted_quests_rules(logic, multiworld, player, world_options) + set_fishsanity_rules(all_location_names, logic, multiworld, player) + set_museumsanity_rules(all_location_names, logic, multiworld, player, world_options) + set_friendsanity_rules(all_location_names, logic, multiworld, player) + set_backpack_rules(logic, multiworld, player, world_options) + set_festival_rules(all_location_names, logic, multiworld, player) - MultiWorldRules.add_rule(multi_world.get_location("Old Master Cannoli", player), + MultiWorldRules.add_rule(multiworld.get_location("Old Master Cannoli", player), logic.has("Sweet Gem Berry").simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Galaxy Sword Shrine", player), + MultiWorldRules.add_rule(multiworld.get_location("Galaxy Sword Shrine", player), logic.has("Prismatic Shard").simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Have a Baby", player), + MultiWorldRules.add_rule(multiworld.get_location("Have a Baby", player), logic.can_reproduce(1).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Have Another Baby", player), + MultiWorldRules.add_rule(multiworld.get_location("Have Another Baby", player), logic.can_reproduce(2).simplify()) - set_traveling_merchant_rules(logic, multi_world, player) - set_arcade_machine_rules(logic, multi_world, player, world_options) - set_deepwoods_rules(logic, multi_world, player, world_options) - set_magic_spell_rules(logic, multi_world, player, world_options) + set_traveling_merchant_rules(logic, multiworld, player) + set_arcade_machine_rules(logic, multiworld, player, world_options) + set_deepwoods_rules(logic, multiworld, player, world_options) + set_magic_spell_rules(logic, multiworld, player, world_options) -def set_skills_rules(logic, multi_world, player, world_options): +def set_skills_rules(logic, multiworld, player, world_options): # Skills - if world_options[options.SkillProgression] != options.SkillProgression.option_vanilla: + if world_options.skill_progression != SkillProgression.option_vanilla: for i in range(1, 11): - set_skill_rule(logic, multi_world, player, Skill.farming, i) - set_skill_rule(logic, multi_world, player, Skill.fishing, i) - set_skill_rule(logic, multi_world, player, Skill.foraging, i) - set_skill_rule(logic, multi_world, player, Skill.mining, i) - set_skill_rule(logic, multi_world, player, Skill.combat, i) + set_skill_rule(logic, multiworld, player, Skill.farming, i) + set_skill_rule(logic, multiworld, player, Skill.fishing, i) + set_skill_rule(logic, multiworld, player, Skill.foraging, i) + set_skill_rule(logic, multiworld, player, Skill.mining, i) + set_skill_rule(logic, multiworld, player, Skill.combat, i) # Modded Skills - if ModNames.luck_skill in world_options[options.Mods]: - set_skill_rule(logic, multi_world, player, ModSkill.luck, i) - if ModNames.magic in world_options[options.Mods]: - set_skill_rule(logic, multi_world, player, ModSkill.magic, i) - if ModNames.binning_skill in world_options[options.Mods]: - set_skill_rule(logic, multi_world, player, ModSkill.binning, i) - if ModNames.cooking_skill in world_options[options.Mods]: - set_skill_rule(logic, multi_world, player, ModSkill.cooking, i) - if ModNames.socializing_skill in world_options[options.Mods]: - set_skill_rule(logic, multi_world, player, ModSkill.socializing, i) - if ModNames.archaeology in world_options[options.Mods]: - set_skill_rule(logic, multi_world, player, ModSkill.archaeology, i) + if ModNames.luck_skill in world_options.mods: + set_skill_rule(logic, multiworld, player, ModSkill.luck, i) + if ModNames.magic in world_options.mods: + set_skill_rule(logic, multiworld, player, ModSkill.magic, i) + if ModNames.binning_skill in world_options.mods: + set_skill_rule(logic, multiworld, player, ModSkill.binning, i) + if ModNames.cooking_skill in world_options.mods: + set_skill_rule(logic, multiworld, player, ModSkill.cooking, i) + if ModNames.socializing_skill in world_options.mods: + set_skill_rule(logic, multiworld, player, ModSkill.socializing, i) + if ModNames.archaeology in world_options.mods: + set_skill_rule(logic, multiworld, player, ModSkill.archaeology, i) -def set_skill_rule(logic, multi_world, player, skill: str, level: int): +def set_skill_rule(logic, multiworld, player, skill: str, level: int): location_name = f"Level {level} {skill}" - location = multi_world.get_location(location_name, player) + location = multiworld.get_location(location_name, player) rule = logic.can_earn_skill_level(skill, level).simplify() MultiWorldRules.set_rule(location, rule) -def set_entrance_rules(logic, multi_world, player, world_options: StardewOptions): +def set_entrance_rules(logic, multiworld, player, world_options: StardewValleyOptions): for floor in range(5, 120 + 5, 5): - MultiWorldRules.set_rule(multi_world.get_entrance(dig_to_mines_floor(floor), player), + MultiWorldRules.set_rule(multiworld.get_entrance(dig_to_mines_floor(floor), player), logic.can_mine_to_floor(floor).simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_tide_pools, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_tide_pools, player), logic.received("Beach Bridge") | (magic.can_blink(logic)).simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_quarry, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_quarry, player), logic.received("Bridge Repair") | (magic.can_blink(logic)).simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_secret_woods, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_secret_woods, player), logic.has_tool(Tool.axe, "Iron") | (magic.can_blink(logic)).simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.forest_to_sewer, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.forest_to_sewer, player), logic.has_rusty_key().simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.town_to_sewer, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.town_to_sewer, player), logic.has_rusty_key().simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.take_bus_to_desert, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.take_bus_to_desert, player), logic.received("Bus Repair").simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_skull_cavern, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_skull_cavern, player), logic.received(Wallet.skull_key).simplify()) for floor in range(25, 200 + 25, 25): - MultiWorldRules.set_rule(multi_world.get_entrance(dig_to_skull_floor(floor), player), + MultiWorldRules.set_rule(multiworld.get_entrance(dig_to_skull_floor(floor), player), logic.can_mine_to_skull_cavern_floor(floor).simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.talk_to_mines_dwarf, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.talk_to_mines_dwarf, player), logic.can_speak_dwarf() & logic.has_tool(Tool.pickaxe, ToolMaterial.iron)) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.use_desert_obelisk, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.use_desert_obelisk, player), logic.received(Transportation.desert_obelisk).simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.use_island_obelisk, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.use_island_obelisk, player), logic.received(Transportation.island_obelisk).simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.use_farm_obelisk, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.use_farm_obelisk, player), logic.received(Transportation.farm_obelisk).simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.buy_from_traveling_merchant, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.buy_from_traveling_merchant, player), logic.has_traveling_merchant()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_greenhouse, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_greenhouse, player), logic.received("Greenhouse")) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.mountain_to_adventurer_guild, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.mountain_to_adventurer_guild, player), logic.received("Adventurer's Guild")) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.mountain_to_railroad, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.mountain_to_railroad, player), logic.has_lived_months(2)) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_witch_warp_cave, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_witch_warp_cave, player), logic.received(Wallet.dark_talisman) | (magic.can_blink(logic)).simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_witch_hut, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_witch_hut, player), (logic.has(ArtisanGood.void_mayonnaise) | magic.can_blink(logic)).simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_mutant_bug_lair, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_mutant_bug_lair, player), ((logic.has_rusty_key() & logic.can_reach_region(Region.railroad) & logic.can_meet(NPC.krobus) | magic.can_blink(logic)).simplify())) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_harvey_room, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_harvey_room, player), logic.has_relationship(NPC.harvey, 2)) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.mountain_to_maru_room, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.mountain_to_maru_room, player), logic.has_relationship(NPC.maru, 2)) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_sebastian_room, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_sebastian_room, player), (logic.has_relationship(NPC.sebastian, 2) | magic.can_blink(logic)).simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.forest_to_leah_cottage, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.forest_to_leah_cottage, player), logic.has_relationship(NPC.leah, 2)) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_elliott_house, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_elliott_house, player), logic.has_relationship(NPC.elliott, 2)) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_sunroom, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_sunroom, player), logic.has_relationship(NPC.caroline, 2)) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_wizard_basement, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.enter_wizard_basement, player), logic.has_relationship(NPC.wizard, 4)) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.mountain_to_leo_treehouse, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.mountain_to_leo_treehouse, player), logic.received("Treehouse")) - if ModNames.alec in world_options[options.Mods]: - MultiWorldRules.set_rule(multi_world.get_entrance(AlecEntrance.petshop_to_bedroom, player), + if ModNames.alec in world_options.mods: + MultiWorldRules.set_rule(multiworld.get_entrance(AlecEntrance.petshop_to_bedroom, player), (logic.has_relationship(ModNPC.alec, 2) | magic.can_blink(logic)).simplify()) -def set_ginger_island_rules(logic: StardewLogic, multi_world, player, world_options: StardewOptions): - set_island_entrances_rules(logic, multi_world, player) - if world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true: +def set_ginger_island_rules(logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions): + set_island_entrances_rules(logic, multiworld, player) + if world_options.exclude_ginger_island == ExcludeGingerIsland.option_true: return - set_boat_repair_rules(logic, multi_world, player) - set_island_parrot_rules(logic, multi_world, player) - MultiWorldRules.add_rule(multi_world.get_location("Open Professor Snail Cave", player), + set_boat_repair_rules(logic, multiworld, player) + set_island_parrot_rules(logic, multiworld, player) + MultiWorldRules.add_rule(multiworld.get_location("Open Professor Snail Cave", player), logic.has(Craftable.cherry_bomb).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Complete Island Field Office", player), + MultiWorldRules.add_rule(multiworld.get_location("Complete Island Field Office", player), logic.can_complete_field_office().simplify()) -def set_boat_repair_rules(logic: StardewLogic, multi_world, player): - MultiWorldRules.add_rule(multi_world.get_location("Repair Boat Hull", player), +def set_boat_repair_rules(logic: StardewLogic, multiworld, player): + MultiWorldRules.add_rule(multiworld.get_location("Repair Boat Hull", player), logic.has(Material.hardwood).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Repair Boat Anchor", player), + MultiWorldRules.add_rule(multiworld.get_location("Repair Boat Anchor", player), logic.has(MetalBar.iridium).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Repair Ticket Machine", player), + MultiWorldRules.add_rule(multiworld.get_location("Repair Ticket Machine", player), logic.has(ArtisanGood.battery_pack).simplify()) -def set_island_entrances_rules(logic: StardewLogic, multi_world, player): +def set_island_entrances_rules(logic: StardewLogic, multiworld, player): boat_repaired = logic.received(Transportation.boat_repair).simplify() - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.fish_shop_to_boat_tunnel, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.fish_shop_to_boat_tunnel, player), boat_repaired) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.boat_to_ginger_island, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.boat_to_ginger_island, player), boat_repaired) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.island_south_to_west, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.island_south_to_west, player), logic.received("Island West Turtle").simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.island_south_to_north, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.island_south_to_north, player), logic.received("Island North Turtle").simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.island_west_to_islandfarmhouse, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.island_west_to_islandfarmhouse, player), logic.received("Island Farmhouse").simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.island_west_to_gourmand_cave, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.island_west_to_gourmand_cave, player), logic.received("Island Farmhouse").simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.island_north_to_dig_site, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.island_north_to_dig_site, player), logic.received("Dig Site Bridge").simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.dig_site_to_professor_snail_cave, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.dig_site_to_professor_snail_cave, player), logic.received("Open Professor Snail Cave").simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.talk_to_island_trader, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.talk_to_island_trader, player), logic.received("Island Trader").simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.island_south_to_southeast, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.island_south_to_southeast, player), logic.received("Island Resort").simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.use_island_resort, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.use_island_resort, player), logic.received("Island Resort").simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.island_west_to_qi_walnut_room, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.island_west_to_qi_walnut_room, player), logic.received("Qi Walnut Room").simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.island_north_to_volcano, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.island_north_to_volcano, player), (logic.can_water(0) | logic.received("Volcano Bridge") | magic.can_blink(logic)).simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.volcano_to_secret_beach, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.volcano_to_secret_beach, player), logic.can_water(2).simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.climb_to_volcano_5, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.climb_to_volcano_5, player), (logic.can_mine_perfectly() & logic.can_water(1)).simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.talk_to_volcano_dwarf, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.talk_to_volcano_dwarf, player), logic.can_speak_dwarf()) - MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.climb_to_volcano_10, player), + MultiWorldRules.set_rule(multiworld.get_entrance(Entrance.climb_to_volcano_10, player), (logic.can_mine_perfectly() & logic.can_water(1) & logic.received("Volcano Exit Shortcut")).simplify()) parrots = [Entrance.parrot_express_docks_to_volcano, Entrance.parrot_express_jungle_to_volcano, Entrance.parrot_express_dig_site_to_volcano, Entrance.parrot_express_docks_to_dig_site, @@ -281,78 +285,78 @@ def set_island_entrances_rules(logic: StardewLogic, multi_world, player): Entrance.parrot_express_volcano_to_jungle, Entrance.parrot_express_jungle_to_docks, Entrance.parrot_express_dig_site_to_docks, Entrance.parrot_express_volcano_to_docks] for parrot in parrots: - MultiWorldRules.set_rule(multi_world.get_entrance(parrot, player), logic.received(Transportation.parrot_express).simplify()) + MultiWorldRules.set_rule(multiworld.get_entrance(parrot, player), logic.received(Transportation.parrot_express).simplify()) -def set_island_parrot_rules(logic: StardewLogic, multi_world, player): +def set_island_parrot_rules(logic: StardewLogic, multiworld, player): has_walnut = logic.has_walnut(1).simplify() has_5_walnut = logic.has_walnut(5).simplify() has_10_walnut = logic.has_walnut(10).simplify() has_20_walnut = logic.has_walnut(20).simplify() - MultiWorldRules.add_rule(multi_world.get_location("Leo's Parrot", player), + MultiWorldRules.add_rule(multiworld.get_location("Leo's Parrot", player), has_walnut) - MultiWorldRules.add_rule(multi_world.get_location("Island West Turtle", player), + MultiWorldRules.add_rule(multiworld.get_location("Island West Turtle", player), has_10_walnut & logic.received("Island North Turtle")) - MultiWorldRules.add_rule(multi_world.get_location("Island Farmhouse", player), + MultiWorldRules.add_rule(multiworld.get_location("Island Farmhouse", player), has_20_walnut) - MultiWorldRules.add_rule(multi_world.get_location("Island Mailbox", player), + MultiWorldRules.add_rule(multiworld.get_location("Island Mailbox", player), has_5_walnut & logic.received("Island Farmhouse")) - MultiWorldRules.add_rule(multi_world.get_location(Transportation.farm_obelisk, player), + MultiWorldRules.add_rule(multiworld.get_location(Transportation.farm_obelisk, player), has_20_walnut & logic.received("Island Mailbox")) - MultiWorldRules.add_rule(multi_world.get_location("Dig Site Bridge", player), + MultiWorldRules.add_rule(multiworld.get_location("Dig Site Bridge", player), has_10_walnut & logic.received("Island West Turtle")) - MultiWorldRules.add_rule(multi_world.get_location("Island Trader", player), + MultiWorldRules.add_rule(multiworld.get_location("Island Trader", player), has_10_walnut & logic.received("Island Farmhouse")) - MultiWorldRules.add_rule(multi_world.get_location("Volcano Bridge", player), + MultiWorldRules.add_rule(multiworld.get_location("Volcano Bridge", player), has_5_walnut & logic.received("Island West Turtle") & logic.can_reach_region(Region.volcano_floor_10)) - MultiWorldRules.add_rule(multi_world.get_location("Volcano Exit Shortcut", player), + MultiWorldRules.add_rule(multiworld.get_location("Volcano Exit Shortcut", player), has_5_walnut & logic.received("Island West Turtle")) - MultiWorldRules.add_rule(multi_world.get_location("Island Resort", player), + MultiWorldRules.add_rule(multiworld.get_location("Island Resort", player), has_20_walnut & logic.received("Island Farmhouse")) - MultiWorldRules.add_rule(multi_world.get_location(Transportation.parrot_express, player), + MultiWorldRules.add_rule(multiworld.get_location(Transportation.parrot_express, player), has_10_walnut) -def set_cropsanity_rules(all_location_names: List[str], logic, multi_world, player, world_options: StardewOptions): - if world_options[options.Cropsanity] == options.Cropsanity.option_disabled: +def set_cropsanity_rules(all_location_names: List[str], logic, multiworld, player, world_options: StardewValleyOptions): + if world_options.cropsanity == Cropsanity.option_disabled: return harvest_prefix = "Harvest " harvest_prefix_length = len(harvest_prefix) - for harvest_location in locations.locations_by_tag[LocationTags.CROPSANITY]: - if harvest_location.name in all_location_names and (harvest_location.mod_name is None or harvest_location.mod_name in world_options[options.Mods]): + for harvest_location in locations_by_tag[LocationTags.CROPSANITY]: + if harvest_location.name in all_location_names and (harvest_location.mod_name is None or harvest_location.mod_name in world_options.mods): crop_name = harvest_location.name[harvest_prefix_length:] - MultiWorldRules.set_rule(multi_world.get_location(harvest_location.name, player), + MultiWorldRules.set_rule(multiworld.get_location(harvest_location.name, player), logic.has(crop_name).simplify()) -def set_story_quests_rules(all_location_names: List[str], logic, multi_world, player, world_options: StardewOptions): - for quest in locations.locations_by_tag[LocationTags.QUEST]: - if quest.name in all_location_names and (quest.mod_name is None or quest.mod_name in world_options[options.Mods]): - MultiWorldRules.set_rule(multi_world.get_location(quest.name, player), +def set_story_quests_rules(all_location_names: List[str], logic, multiworld, player, world_options: StardewValleyOptions): + for quest in locations_by_tag[LocationTags.QUEST]: + if quest.name in all_location_names and (quest.mod_name is None or quest.mod_name in world_options.mods): + MultiWorldRules.set_rule(multiworld.get_location(quest.name, player), logic.quest_rules[quest.name].simplify()) -def set_special_order_rules(all_location_names: List[str], logic: StardewLogic, multi_world, player, - world_options: StardewOptions): - if world_options[options.SpecialOrderLocations] == options.SpecialOrderLocations.option_disabled: +def set_special_order_rules(all_location_names: List[str], logic: StardewLogic, multiworld, player, + world_options: StardewValleyOptions): + if world_options.special_order_locations == SpecialOrderLocations.option_disabled: return board_rule = logic.received("Special Order Board") & logic.has_lived_months(4) - for board_order in locations.locations_by_tag[LocationTags.SPECIAL_ORDER_BOARD]: + for board_order in locations_by_tag[LocationTags.SPECIAL_ORDER_BOARD]: if board_order.name in all_location_names: order_rule = board_rule & logic.special_order_rules[board_order.name] - MultiWorldRules.set_rule(multi_world.get_location(board_order.name, player), order_rule.simplify()) + MultiWorldRules.set_rule(multiworld.get_location(board_order.name, player), order_rule.simplify()) - if world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true: + if world_options.exclude_ginger_island == ExcludeGingerIsland.option_true: return - if world_options[options.SpecialOrderLocations] == options.SpecialOrderLocations.option_board_only: + if world_options.special_order_locations == SpecialOrderLocations.option_board_only: return qi_rule = logic.can_reach_region(Region.qi_walnut_room) & logic.has_lived_months(8) - for qi_order in locations.locations_by_tag[LocationTags.SPECIAL_ORDER_QI]: + for qi_order in locations_by_tag[LocationTags.SPECIAL_ORDER_QI]: if qi_order.name in all_location_names: order_rule = qi_rule & logic.special_order_rules[qi_order.name] - MultiWorldRules.set_rule(multi_world.get_location(qi_order.name, player), order_rule.simplify()) + MultiWorldRules.set_rule(multiworld.get_location(qi_order.name, player), order_rule.simplify()) help_wanted_prefix = "Help Wanted:" @@ -362,8 +366,8 @@ fishing = "Fishing" slay_monsters = "Slay Monsters" -def set_help_wanted_quests_rules(logic: StardewLogic, multi_world, player, world_options): - help_wanted_number = world_options[options.HelpWantedLocations] +def set_help_wanted_quests_rules(logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions): + help_wanted_number = world_options.help_wanted_locations for i in range(0, help_wanted_number): set_number = i // 7 month_rule = logic.has_lived_months(set_number).simplify() @@ -371,58 +375,58 @@ def set_help_wanted_quests_rules(logic: StardewLogic, multi_world, player, world quest_number_in_set = i % 7 if quest_number_in_set < 4: quest_number = set_number * 4 + quest_number_in_set + 1 - set_help_wanted_delivery_rule(multi_world, player, month_rule, quest_number) + set_help_wanted_delivery_rule(multiworld, player, month_rule, quest_number) elif quest_number_in_set == 4: - set_help_wanted_fishing_rule(logic, multi_world, player, month_rule, quest_number) + set_help_wanted_fishing_rule(logic, multiworld, player, month_rule, quest_number) elif quest_number_in_set == 5: - set_help_wanted_slay_monsters_rule(logic, multi_world, player, month_rule, quest_number) + set_help_wanted_slay_monsters_rule(logic, multiworld, player, month_rule, quest_number) elif quest_number_in_set == 6: - set_help_wanted_gathering_rule(multi_world, player, month_rule, quest_number) + set_help_wanted_gathering_rule(multiworld, player, month_rule, quest_number) -def set_help_wanted_delivery_rule(multi_world, player, month_rule, quest_number): +def set_help_wanted_delivery_rule(multiworld, player, month_rule, quest_number): location_name = f"{help_wanted_prefix} {item_delivery} {quest_number}" - MultiWorldRules.set_rule(multi_world.get_location(location_name, player), month_rule) + MultiWorldRules.set_rule(multiworld.get_location(location_name, player), month_rule) -def set_help_wanted_gathering_rule(multi_world, player, month_rule, quest_number): +def set_help_wanted_gathering_rule(multiworld, player, month_rule, quest_number): location_name = f"{help_wanted_prefix} {gathering} {quest_number}" - MultiWorldRules.set_rule(multi_world.get_location(location_name, player), month_rule) + MultiWorldRules.set_rule(multiworld.get_location(location_name, player), month_rule) -def set_help_wanted_fishing_rule(logic: StardewLogic, multi_world, player, month_rule, quest_number): +def set_help_wanted_fishing_rule(logic: StardewLogic, multiworld, player, month_rule, quest_number): location_name = f"{help_wanted_prefix} {fishing} {quest_number}" fishing_rule = month_rule & logic.can_fish() - MultiWorldRules.set_rule(multi_world.get_location(location_name, player), fishing_rule.simplify()) + MultiWorldRules.set_rule(multiworld.get_location(location_name, player), fishing_rule.simplify()) -def set_help_wanted_slay_monsters_rule(logic: StardewLogic, multi_world, player, month_rule, quest_number): +def set_help_wanted_slay_monsters_rule(logic: StardewLogic, multiworld, player, month_rule, quest_number): location_name = f"{help_wanted_prefix} {slay_monsters} {quest_number}" slay_rule = month_rule & logic.can_do_combat_at_level("Basic") - MultiWorldRules.set_rule(multi_world.get_location(location_name, player), slay_rule.simplify()) + MultiWorldRules.set_rule(multiworld.get_location(location_name, player), slay_rule.simplify()) -def set_fishsanity_rules(all_location_names: List[str], logic: StardewLogic, multi_world: MultiWorld, player: int): +def set_fishsanity_rules(all_location_names: List[str], logic: StardewLogic, multiworld: MultiWorld, player: int): fish_prefix = "Fishsanity: " - for fish_location in locations.locations_by_tag[LocationTags.FISHSANITY]: + for fish_location in locations_by_tag[LocationTags.FISHSANITY]: if fish_location.name in all_location_names: fish_name = fish_location.name[len(fish_prefix):] - MultiWorldRules.set_rule(multi_world.get_location(fish_location.name, player), + MultiWorldRules.set_rule(multiworld.get_location(fish_location.name, player), logic.has(fish_name).simplify()) -def set_museumsanity_rules(all_location_names: List[str], logic: StardewLogic, multi_world: MultiWorld, player: int, - world_options: StardewOptions): +def set_museumsanity_rules(all_location_names: List[str], logic: StardewLogic, multiworld: MultiWorld, player: int, + world_options: StardewValleyOptions): museum_prefix = "Museumsanity: " - if world_options[options.Museumsanity] == options.Museumsanity.option_milestones: - for museum_milestone in locations.locations_by_tag[LocationTags.MUSEUM_MILESTONES]: - set_museum_milestone_rule(logic, multi_world, museum_milestone, museum_prefix, player) - elif world_options[options.Museumsanity] != options.Museumsanity.option_none: - set_museum_individual_donations_rules(all_location_names, logic, multi_world, museum_prefix, player) + if world_options.museumsanity == Museumsanity.option_milestones: + for museum_milestone in locations_by_tag[LocationTags.MUSEUM_MILESTONES]: + set_museum_milestone_rule(logic, multiworld, museum_milestone, museum_prefix, player) + elif world_options.museumsanity != Museumsanity.option_none: + set_museum_individual_donations_rules(all_location_names, logic, multiworld, museum_prefix, player) -def set_museum_individual_donations_rules(all_location_names, logic: StardewLogic, multi_world, museum_prefix, player): - all_donations = sorted(locations.locations_by_tag[LocationTags.MUSEUM_DONATIONS], +def set_museum_individual_donations_rules(all_location_names, logic: StardewLogic, multiworld, museum_prefix, player): + all_donations = sorted(locations_by_tag[LocationTags.MUSEUM_DONATIONS], key=lambda x: all_museum_items_by_name[x.name[len(museum_prefix):]].difficulty, reverse=True) counter = 0 number_donations = len(all_donations) @@ -430,13 +434,14 @@ def set_museum_individual_donations_rules(all_location_names, logic: StardewLogi if museum_location.name in all_location_names: donation_name = museum_location.name[len(museum_prefix):] required_detectors = counter * 5 // number_donations - rule = logic.can_donate_museum_item(all_museum_items_by_name[donation_name]) & logic.received("Traveling Merchant Metal Detector", required_detectors) - MultiWorldRules.set_rule(multi_world.get_location(museum_location.name, player), + rule = logic.can_donate_museum_item(all_museum_items_by_name[donation_name]) & logic.received("Traveling Merchant Metal Detector", + required_detectors) + MultiWorldRules.set_rule(multiworld.get_location(museum_location.name, player), rule.simplify()) counter += 1 -def set_museum_milestone_rule(logic: StardewLogic, multi_world: MultiWorld, museum_milestone, museum_prefix: str, +def set_museum_milestone_rule(logic: StardewLogic, multiworld: MultiWorld, museum_milestone, museum_prefix: str, player: int): milestone_name = museum_milestone.name[len(museum_prefix):] donations_suffix = " Donations" @@ -462,7 +467,7 @@ def set_museum_milestone_rule(logic: StardewLogic, multi_world: MultiWorld, muse rule = logic.can_donate_museum_item(Artifact.ancient_seed) & logic.received(metal_detector, 4) if rule is None: return - MultiWorldRules.set_rule(multi_world.get_location(museum_milestone.name, player), rule.simplify()) + MultiWorldRules.set_rule(multiworld.get_location(museum_milestone.name, player), rule.simplify()) def get_museum_item_count_rule(logic: StardewLogic, suffix, milestone_name, accepted_items, donation_func): @@ -473,156 +478,156 @@ def get_museum_item_count_rule(logic: StardewLogic, suffix, milestone_name, acce return rule -def set_backpack_rules(logic: StardewLogic, multi_world: MultiWorld, player: int, world_options): - if world_options[options.BackpackProgression] != options.BackpackProgression.option_vanilla: - MultiWorldRules.set_rule(multi_world.get_location("Large Pack", player), +def set_backpack_rules(logic: StardewLogic, multiworld: MultiWorld, player: int, world_options: StardewValleyOptions): + if world_options.backpack_progression != BackpackProgression.option_vanilla: + MultiWorldRules.set_rule(multiworld.get_location("Large Pack", player), logic.can_spend_money(2000).simplify()) - MultiWorldRules.set_rule(multi_world.get_location("Deluxe Pack", player), + MultiWorldRules.set_rule(multiworld.get_location("Deluxe Pack", player), (logic.can_spend_money(10000) & logic.received("Progressive Backpack")).simplify()) - if ModNames.big_backpack in world_options[options.Mods]: - MultiWorldRules.set_rule(multi_world.get_location("Premium Pack", player), + if ModNames.big_backpack in world_options.mods: + MultiWorldRules.set_rule(multiworld.get_location("Premium Pack", player), (logic.can_spend_money(150000) & logic.received("Progressive Backpack", 2)).simplify()) -def set_festival_rules(all_location_names: List[str], logic: StardewLogic, multi_world, player): +def set_festival_rules(all_location_names: List[str], logic: StardewLogic, multiworld, player): festival_locations = [] - festival_locations.extend(locations.locations_by_tag[LocationTags.FESTIVAL]) - festival_locations.extend(locations.locations_by_tag[LocationTags.FESTIVAL_HARD]) + festival_locations.extend(locations_by_tag[LocationTags.FESTIVAL]) + festival_locations.extend(locations_by_tag[LocationTags.FESTIVAL_HARD]) for festival in festival_locations: if festival.name in all_location_names: - MultiWorldRules.set_rule(multi_world.get_location(festival.name, player), + MultiWorldRules.set_rule(multiworld.get_location(festival.name, player), logic.festival_rules[festival.name].simplify()) -def set_traveling_merchant_rules(logic: StardewLogic, multi_world: MultiWorld, player: int): +def set_traveling_merchant_rules(logic: StardewLogic, multiworld: MultiWorld, player: int): for day in Weekday.all_days: item_for_day = f"Traveling Merchant: {day}" for i in range(1, 4): location_name = f"Traveling Merchant {day} Item {i}" - MultiWorldRules.set_rule(multi_world.get_location(location_name, player), + MultiWorldRules.set_rule(multiworld.get_location(location_name, player), logic.received(item_for_day)) -def set_arcade_machine_rules(logic: StardewLogic, multi_world: MultiWorld, player: int, world_options): - MultiWorldRules.add_rule(multi_world.get_entrance(Entrance.play_junimo_kart, player), +def set_arcade_machine_rules(logic: StardewLogic, multiworld: MultiWorld, player: int, world_options: StardewValleyOptions): + MultiWorldRules.add_rule(multiworld.get_entrance(Entrance.play_junimo_kart, player), logic.received(Wallet.skull_key).simplify()) - if world_options[options.ArcadeMachineLocations] != options.ArcadeMachineLocations.option_full_shuffling: + if world_options.arcade_machine_locations != ArcadeMachineLocations.option_full_shuffling: return - MultiWorldRules.add_rule(multi_world.get_entrance(Entrance.play_junimo_kart, player), + MultiWorldRules.add_rule(multiworld.get_entrance(Entrance.play_junimo_kart, player), logic.has("Junimo Kart Small Buff").simplify()) - MultiWorldRules.add_rule(multi_world.get_entrance(Entrance.reach_junimo_kart_2, player), + MultiWorldRules.add_rule(multiworld.get_entrance(Entrance.reach_junimo_kart_2, player), logic.has("Junimo Kart Medium Buff").simplify()) - MultiWorldRules.add_rule(multi_world.get_entrance(Entrance.reach_junimo_kart_3, player), + MultiWorldRules.add_rule(multiworld.get_entrance(Entrance.reach_junimo_kart_3, player), logic.has("Junimo Kart Big Buff").simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Junimo Kart: Sunset Speedway (Victory)", player), + MultiWorldRules.add_rule(multiworld.get_location("Junimo Kart: Sunset Speedway (Victory)", player), logic.has("Junimo Kart Max Buff").simplify()) - MultiWorldRules.add_rule(multi_world.get_entrance(Entrance.play_journey_of_the_prairie_king, player), + MultiWorldRules.add_rule(multiworld.get_entrance(Entrance.play_journey_of_the_prairie_king, player), logic.has("JotPK Small Buff").simplify()) - MultiWorldRules.add_rule(multi_world.get_entrance(Entrance.reach_jotpk_world_2, player), + MultiWorldRules.add_rule(multiworld.get_entrance(Entrance.reach_jotpk_world_2, player), logic.has("JotPK Medium Buff").simplify()) - MultiWorldRules.add_rule(multi_world.get_entrance(Entrance.reach_jotpk_world_3, player), + MultiWorldRules.add_rule(multiworld.get_entrance(Entrance.reach_jotpk_world_3, player), logic.has("JotPK Big Buff").simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Journey of the Prairie King Victory", player), + MultiWorldRules.add_rule(multiworld.get_location("Journey of the Prairie King Victory", player), logic.has("JotPK Max Buff").simplify()) -def set_friendsanity_rules(all_location_names: List[str], logic: StardewLogic, multi_world: MultiWorld, player: int): +def set_friendsanity_rules(all_location_names: List[str], logic: StardewLogic, multiworld: MultiWorld, player: int): friend_prefix = "Friendsanity: " friend_suffix = " <3" - for friend_location in locations.locations_by_tag[LocationTags.FRIENDSANITY]: - if not friend_location.name in all_location_names: + for friend_location in locations_by_tag[LocationTags.FRIENDSANITY]: + if friend_location.name not in all_location_names: continue friend_location_without_prefix = friend_location.name[len(friend_prefix):] friend_location_trimmed = friend_location_without_prefix[:friend_location_without_prefix.index(friend_suffix)] split_index = friend_location_trimmed.rindex(" ") friend_name = friend_location_trimmed[:split_index] num_hearts = int(friend_location_trimmed[split_index + 1:]) - MultiWorldRules.set_rule(multi_world.get_location(friend_location.name, player), + MultiWorldRules.set_rule(multiworld.get_location(friend_location.name, player), logic.can_earn_relationship(friend_name, num_hearts).simplify()) -def set_deepwoods_rules(logic: StardewLogic, multi_world: MultiWorld, player: int, world_options: StardewOptions): - if ModNames.deepwoods in world_options[options.Mods]: - MultiWorldRules.add_rule(multi_world.get_location("Breaking Up Deep Woods Gingerbread House", player), +def set_deepwoods_rules(logic: StardewLogic, multiworld: MultiWorld, player: int, world_options: StardewValleyOptions): + if ModNames.deepwoods in world_options.mods: + MultiWorldRules.add_rule(multiworld.get_location("Breaking Up Deep Woods Gingerbread House", player), logic.has_tool(Tool.axe, "Gold") & deepwoods.can_reach_woods_depth(logic, 50).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Chop Down a Deep Woods Iridium Tree", player), + MultiWorldRules.add_rule(multiworld.get_location("Chop Down a Deep Woods Iridium Tree", player), logic.has_tool(Tool.axe, "Iridium").simplify()) - MultiWorldRules.set_rule(multi_world.get_entrance(DeepWoodsEntrance.use_woods_obelisk, player), + MultiWorldRules.set_rule(multiworld.get_entrance(DeepWoodsEntrance.use_woods_obelisk, player), logic.received("Woods Obelisk").simplify()) for depth in range(10, 100 + 10, 10): - MultiWorldRules.set_rule(multi_world.get_entrance(move_to_woods_depth(depth), player), + MultiWorldRules.set_rule(multiworld.get_entrance(move_to_woods_depth(depth), player), deepwoods.can_chop_to_depth(logic, depth).simplify()) -def set_magic_spell_rules(logic: StardewLogic, multi_world: MultiWorld, player: int, world_options: StardewOptions): - if ModNames.magic not in world_options[options.Mods]: +def set_magic_spell_rules(logic: StardewLogic, multiworld: MultiWorld, player: int, world_options: StardewValleyOptions): + if ModNames.magic not in world_options.mods: return - MultiWorldRules.set_rule(multi_world.get_entrance(MagicEntrance.store_to_altar, player), - (logic.has_relationship(NPC.wizard, 3) & - logic.can_reach_region(Region.wizard_tower)).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Analyze: Clear Debris", player), - ((logic.has_tool("Axe", "Basic") | logic.has_tool("Pickaxe", "Basic")) - & magic.can_use_altar(logic)).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Analyze: Till", player), - (logic.has_tool("Hoe", "Basic") & magic.can_use_altar(logic)).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Analyze: Water", player), - (logic.has_tool("Watering Can", "Basic") & magic.can_use_altar(logic)).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Analyze All Toil School Locations", player), - (logic.has_tool("Watering Can", "Basic") & logic.has_tool("Hoe", "Basic") - & (logic.has_tool("Axe", "Basic") | logic.has_tool("Pickaxe", "Basic")) - & magic.can_use_altar(logic)).simplify()) + MultiWorldRules.set_rule(multiworld.get_entrance(MagicEntrance.store_to_altar, player), + (logic.has_relationship(NPC.wizard, 3) & + logic.can_reach_region(Region.wizard_tower)).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Analyze: Clear Debris", player), + ((logic.has_tool("Axe", "Basic") | logic.has_tool("Pickaxe", "Basic")) + & magic.can_use_altar(logic)).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Analyze: Till", player), + (logic.has_tool("Hoe", "Basic") & magic.can_use_altar(logic)).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Analyze: Water", player), + (logic.has_tool("Watering Can", "Basic") & magic.can_use_altar(logic)).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Analyze All Toil School Locations", player), + (logic.has_tool("Watering Can", "Basic") & logic.has_tool("Hoe", "Basic") + & (logic.has_tool("Axe", "Basic") | logic.has_tool("Pickaxe", "Basic")) + & magic.can_use_altar(logic)).simplify()) # Do I *want* to add boots into logic when you get them even in vanilla without effort? idk - MultiWorldRules.add_rule(multi_world.get_location("Analyze: Evac", player), - (logic.can_mine_perfectly() & magic.can_use_altar(logic)).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Analyze: Haste", player), - (logic.has("Coffee") & magic.can_use_altar(logic)).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Analyze: Heal", player), - (logic.has("Life Elixir") & magic.can_use_altar(logic)).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Analyze All Life School Locations", player), - (logic.has("Coffee") & logic.has("Life Elixir") - & logic.can_mine_perfectly() & magic.can_use_altar(logic)).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Analyze: Descend", player), - (logic.can_reach_region(Region.mines) & magic.can_use_altar(logic)).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Analyze: Fireball", player), - (logic.has("Fire Quartz") & magic.can_use_altar(logic)).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Analyze: Frostbite", player), - (logic.can_mine_to_floor(70) & logic.can_fish(85) & magic.can_use_altar(logic)).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Analyze All Elemental School Locations", player), - (logic.can_reach_region(Region.mines) & logic.has("Fire Quartz") - & logic.can_reach_region(Region.mines_floor_70) & logic.can_fish(85) & - magic.can_use_altar(logic)).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Analyze: Lantern", player), - magic.can_use_altar(logic).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Analyze: Tendrils", player), - (logic.can_reach_region(Region.farm) & magic.can_use_altar(logic)).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Analyze: Shockwave", player), - (logic.has("Earth Crystal") & magic.can_use_altar(logic)).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Analyze All Nature School Locations", player), - (logic.has("Earth Crystal") & logic.can_reach_region("Farm") & - magic.can_use_altar(logic)).simplify()), - MultiWorldRules.add_rule(multi_world.get_location("Analyze: Meteor", player), - (logic.can_reach_region(Region.farm) & logic.has_lived_months(12) - & magic.can_use_altar(logic)).simplify()), - MultiWorldRules.add_rule(multi_world.get_location("Analyze: Lucksteal", player), - (logic.can_reach_region(Region.witch_hut) & magic.can_use_altar(logic)).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Analyze: Bloodmana", player), - (logic.can_reach_region(Region.mines_floor_100) & magic.can_use_altar(logic)).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Analyze All Eldritch School Locations", player), - (logic.can_reach_region(Region.witch_hut) & - logic.can_reach_region(Region.mines_floor_100) & - logic.can_reach_region(Region.farm) & logic.has_lived_months(12) & - magic.can_use_altar(logic)).simplify()) - MultiWorldRules.add_rule(multi_world.get_location("Analyze Every Magic School Location", player), - (logic.has_tool("Watering Can", "Basic") & logic.has_tool("Hoe", "Basic") - & (logic.has_tool("Axe", "Basic") | logic.has_tool("Pickaxe", "Basic")) & - logic.has("Coffee") & logic.has("Life Elixir") - & logic.can_mine_perfectly() & logic.has("Earth Crystal") & - logic.can_reach_region(Region.mines) & - logic.has("Fire Quartz") & logic.can_fish(85) & - logic.can_reach_region(Region.witch_hut) & - logic.can_reach_region(Region.mines_floor_100) & - logic.can_reach_region(Region.farm) & logic.has_lived_months(12) & - magic.can_use_altar(logic)).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Analyze: Evac", player), + (logic.can_mine_perfectly() & magic.can_use_altar(logic)).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Analyze: Haste", player), + (logic.has("Coffee") & magic.can_use_altar(logic)).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Analyze: Heal", player), + (logic.has("Life Elixir") & magic.can_use_altar(logic)).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Analyze All Life School Locations", player), + (logic.has("Coffee") & logic.has("Life Elixir") + & logic.can_mine_perfectly() & magic.can_use_altar(logic)).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Analyze: Descend", player), + (logic.can_reach_region(Region.mines) & magic.can_use_altar(logic)).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Analyze: Fireball", player), + (logic.has("Fire Quartz") & magic.can_use_altar(logic)).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Analyze: Frostbite", player), + (logic.can_mine_to_floor(70) & logic.can_fish(85) & magic.can_use_altar(logic)).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Analyze All Elemental School Locations", player), + (logic.can_reach_region(Region.mines) & logic.has("Fire Quartz") + & logic.can_reach_region(Region.mines_floor_70) & logic.can_fish(85) & + magic.can_use_altar(logic)).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Analyze: Lantern", player), + magic.can_use_altar(logic).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Analyze: Tendrils", player), + (logic.can_reach_region(Region.farm) & magic.can_use_altar(logic)).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Analyze: Shockwave", player), + (logic.has("Earth Crystal") & magic.can_use_altar(logic)).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Analyze All Nature School Locations", player), + (logic.has("Earth Crystal") & logic.can_reach_region("Farm") & + magic.can_use_altar(logic)).simplify()), + MultiWorldRules.add_rule(multiworld.get_location("Analyze: Meteor", player), + (logic.can_reach_region(Region.farm) & logic.has_lived_months(12) + & magic.can_use_altar(logic)).simplify()), + MultiWorldRules.add_rule(multiworld.get_location("Analyze: Lucksteal", player), + (logic.can_reach_region(Region.witch_hut) & magic.can_use_altar(logic)).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Analyze: Bloodmana", player), + (logic.can_reach_region(Region.mines_floor_100) & magic.can_use_altar(logic)).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Analyze All Eldritch School Locations", player), + (logic.can_reach_region(Region.witch_hut) & + logic.can_reach_region(Region.mines_floor_100) & + logic.can_reach_region(Region.farm) & logic.has_lived_months(12) & + magic.can_use_altar(logic)).simplify()) + MultiWorldRules.add_rule(multiworld.get_location("Analyze Every Magic School Location", player), + (logic.has_tool("Watering Can", "Basic") & logic.has_tool("Hoe", "Basic") + & (logic.has_tool("Axe", "Basic") | logic.has_tool("Pickaxe", "Basic")) & + logic.has("Coffee") & logic.has("Life Elixir") + & logic.can_mine_perfectly() & logic.has("Earth Crystal") & + logic.can_reach_region(Region.mines) & + logic.has("Fire Quartz") & logic.can_fish(85) & + logic.can_reach_region(Region.witch_hut) & + logic.can_reach_region(Region.mines_floor_100) & + logic.can_reach_region(Region.farm) & logic.has_lived_months(12) & + magic.can_use_altar(logic)).simplify()) diff --git a/worlds/stardew_valley/test/TestLogicSimplification.py b/worlds/stardew_valley/test/TestLogicSimplification.py index 83d779ce90..33b2428098 100644 --- a/worlds/stardew_valley/test/TestLogicSimplification.py +++ b/worlds/stardew_valley/test/TestLogicSimplification.py @@ -1,6 +1,5 @@ from .. import True_ -from ..logic import Received, Has, False_, And, Or, StardewLogic -from ..options import default_options, StardewOptions +from ..logic import Received, Has, False_, And, Or def test_simplify_true_in_and(): diff --git a/worlds/stardew_valley/test/TestOptions.py b/worlds/stardew_valley/test/TestOptions.py index 1cd17ada1f..712aa300d5 100644 --- a/worlds/stardew_valley/test/TestOptions.py +++ b/worlds/stardew_valley/test/TestOptions.py @@ -1,15 +1,14 @@ import itertools -import unittest from random import random from typing import Dict from BaseClasses import ItemClassification, MultiWorld -from Options import SpecialRange, OptionSet +from Options import SpecialRange from . import setup_solo_multiworld, SVTestBase -from .. import StardewItem, options, items_by_group, Group +from .. import StardewItem, items_by_group, Group, StardewValleyWorld from ..locations import locations_by_tag, LocationTags, location_table -from ..options import StardewOption, stardew_valley_option_classes, Mods -from ..strings.goal_names import Goal +from ..options import ExcludeGingerIsland, ToolProgression, Goal, SeasonRandomization, TrapItems, SpecialOrderLocations, ArcadeMachineLocations +from ..strings.goal_names import Goal as GoalName from ..strings.season_names import Season from ..strings.special_order_names import SpecialOrder from ..strings.tool_names import ToolMaterial, Tool @@ -51,39 +50,41 @@ def get_option_choices(option) -> Dict[str, int]: class TestGenerateDynamicOptions(SVTestBase): def test_given_special_range_when_generate_then_basic_checks(self): - for option in stardew_valley_option_classes: - if not issubclass(option, SpecialRange): + options = self.world.options_dataclass.type_hints + for option_name, option in options.items(): + if not isinstance(option, SpecialRange): continue for value in option.special_range_names: - with self.subTest(f"{option.internal_name}: {value}"): - choices = {option.internal_name: option.special_range_names[value]} + with self.subTest(f"{option_name}: {value}"): + choices = {option_name: option.special_range_names[value]} multiworld = setup_solo_multiworld(choices) basic_checks(self, multiworld) def test_given_choice_when_generate_then_basic_checks(self): seed = int(random() * pow(10, 18) - 1) - for option in stardew_valley_option_classes: + options = self.world.options_dataclass.type_hints + for option_name, option in options.items(): if not option.options: continue for value in option.options: - with self.subTest(f"{option.internal_name}: {value} [Seed: {seed}]"): - world_options = {option.internal_name: option.options[value]} + with self.subTest(f"{option_name}: {value} [Seed: {seed}]"): + world_options = {option_name: option.options[value]} multiworld = setup_solo_multiworld(world_options, seed) basic_checks(self, multiworld) class TestGoal(SVTestBase): def test_given_goal_when_generate_then_victory_is_in_correct_location(self): - for goal, location in [("community_center", Goal.community_center), - ("grandpa_evaluation", Goal.grandpa_evaluation), - ("bottom_of_the_mines", Goal.bottom_of_the_mines), - ("cryptic_note", Goal.cryptic_note), - ("master_angler", Goal.master_angler), - ("complete_collection", Goal.complete_museum), - ("full_house", Goal.full_house), - ("perfection", Goal.perfection)]: + for goal, location in [("community_center", GoalName.community_center), + ("grandpa_evaluation", GoalName.grandpa_evaluation), + ("bottom_of_the_mines", GoalName.bottom_of_the_mines), + ("cryptic_note", GoalName.cryptic_note), + ("master_angler", GoalName.master_angler), + ("complete_collection", GoalName.complete_museum), + ("full_house", GoalName.full_house), + ("perfection", GoalName.perfection)]: with self.subTest(msg=f"Goal: {goal}, Location: {location}"): - world_options = {options.Goal.internal_name: options.Goal.options[goal]} + world_options = {Goal.internal_name: Goal.options[goal]} multi_world = setup_solo_multiworld(world_options) victory = multi_world.find_item("Victory", 1) self.assertEqual(victory.name, location) @@ -91,14 +92,14 @@ class TestGoal(SVTestBase): class TestSeasonRandomization(SVTestBase): def test_given_disabled_when_generate_then_all_seasons_are_precollected(self): - world_options = {options.SeasonRandomization.internal_name: options.SeasonRandomization.option_disabled} + world_options = {SeasonRandomization.internal_name: SeasonRandomization.option_disabled} multi_world = setup_solo_multiworld(world_options) precollected_items = {item.name for item in multi_world.precollected_items[1]} self.assertTrue(all([season in precollected_items for season in SEASONS])) def test_given_randomized_when_generate_then_all_seasons_are_in_the_pool_or_precollected(self): - world_options = {options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized} + world_options = {SeasonRandomization.internal_name: SeasonRandomization.option_randomized} multi_world = setup_solo_multiworld(world_options) precollected_items = {item.name for item in multi_world.precollected_items[1]} items = {item.name for item in multi_world.get_items()} | precollected_items @@ -106,7 +107,7 @@ class TestSeasonRandomization(SVTestBase): self.assertEqual(len(SEASONS.intersection(precollected_items)), 1) def test_given_progressive_when_generate_then_3_progressive_seasons_are_in_the_pool(self): - world_options = {options.SeasonRandomization.internal_name: options.SeasonRandomization.option_progressive} + world_options = {SeasonRandomization.internal_name: SeasonRandomization.option_progressive} multi_world = setup_solo_multiworld(world_options) items = [item.name for item in multi_world.get_items()] @@ -115,7 +116,7 @@ class TestSeasonRandomization(SVTestBase): class TestToolProgression(SVTestBase): def test_given_vanilla_when_generate_then_no_tool_in_pool(self): - world_options = {options.ToolProgression.internal_name: options.ToolProgression.option_vanilla} + world_options = {ToolProgression.internal_name: ToolProgression.option_vanilla} multi_world = setup_solo_multiworld(world_options) items = {item.name for item in multi_world.get_items()} @@ -123,7 +124,7 @@ class TestToolProgression(SVTestBase): self.assertNotIn(tool, items) def test_given_progressive_when_generate_then_progressive_tool_of_each_is_in_pool_four_times(self): - world_options = {options.ToolProgression.internal_name: options.ToolProgression.option_progressive} + world_options = {ToolProgression.internal_name: ToolProgression.option_progressive} multi_world = setup_solo_multiworld(world_options) items = [item.name for item in multi_world.get_items()] @@ -131,7 +132,7 @@ class TestToolProgression(SVTestBase): self.assertEqual(items.count("Progressive " + tool), 4) def test_given_progressive_when_generate_then_tool_upgrades_are_locations(self): - world_options = {options.ToolProgression.internal_name: options.ToolProgression.option_progressive} + world_options = {ToolProgression.internal_name: ToolProgression.option_progressive} multi_world = setup_solo_multiworld(world_options) locations = {locations.name for locations in multi_world.get_locations(1)} @@ -148,50 +149,52 @@ class TestToolProgression(SVTestBase): class TestGenerateAllOptionsWithExcludeGingerIsland(SVTestBase): def test_given_special_range_when_generate_exclude_ginger_island(self): - for option in stardew_valley_option_classes: - if not issubclass(option, - SpecialRange) or option.internal_name == options.ExcludeGingerIsland.internal_name: + options = self.world.options_dataclass.type_hints + for option_name, option in options.items(): + if not isinstance(option, SpecialRange) or option_name == ExcludeGingerIsland.internal_name: continue for value in option.special_range_names: - with self.subTest(f"{option.internal_name}: {value}"): + with self.subTest(f"{option_name}: {value}"): multiworld = setup_solo_multiworld( - {options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true, - option.internal_name: option.special_range_names[value]}) + {ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true, + option_name: option.special_range_names[value]}) check_no_ginger_island(self, multiworld) def test_given_choice_when_generate_exclude_ginger_island(self): seed = int(random() * pow(10, 18) - 1) - island_option = options.ExcludeGingerIsland - for option in stardew_valley_option_classes: - if not option.options or option.internal_name == island_option.internal_name: + options = self.world.options_dataclass.type_hints + for option_name, option in options.items(): + if not option.options or option_name == ExcludeGingerIsland.internal_name: continue for value in option.options: - with self.subTest(f"{option.internal_name}: {value} [Seed: {seed}]"): + with self.subTest(f"{option_name}: {value} [Seed: {seed}]"): multiworld = setup_solo_multiworld( - {island_option.internal_name: island_option.option_true, - option.internal_name: option.options[value]}, seed) - if multiworld.worlds[self.player].options[island_option.internal_name] != island_option.option_true: + {ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true, + option_name: option.options[value]}, seed) + stardew_world: StardewValleyWorld = multiworld.worlds[self.player] + if stardew_world.options.exclude_ginger_island != ExcludeGingerIsland.option_true: continue basic_checks(self, multiworld) check_no_ginger_island(self, multiworld) def test_given_island_related_goal_then_override_exclude_ginger_island(self): - island_goals = [value for value in options.Goal.options if value in ["walnut_hunter", "perfection"]] - island_option = options.ExcludeGingerIsland + island_goals = [value for value in Goal.options if value in ["walnut_hunter", "perfection"]] + island_option = ExcludeGingerIsland for goal in island_goals: for value in island_option.options: with self.subTest(f"Goal: {goal}, {island_option.internal_name}: {value}"): multiworld = setup_solo_multiworld( - {options.Goal.internal_name: options.Goal.options[goal], + {Goal.internal_name: Goal.options[goal], island_option.internal_name: island_option.options[value]}) - self.assertEqual(multiworld.worlds[self.player].options[island_option.internal_name], island_option.option_false) + stardew_world: StardewValleyWorld = multiworld.worlds[self.player] + self.assertEqual(stardew_world.options.exclude_ginger_island, island_option.option_false) basic_checks(self, multiworld) class TestTraps(SVTestBase): def test_given_no_traps_when_generate_then_no_trap_in_pool(self): world_options = self.allsanity_options_without_mods() - world_options.update({options.TrapItems.internal_name: options.TrapItems.option_no_traps}) + world_options.update({TrapItems.internal_name: TrapItems.option_no_traps}) multi_world = setup_solo_multiworld(world_options) trap_items = [item_data.name for item_data in items_by_group[Group.TRAP]] @@ -202,12 +205,12 @@ class TestTraps(SVTestBase): self.assertNotIn(item, multiworld_items) def test_given_traps_when_generate_then_all_traps_in_pool(self): - trap_option = options.TrapItems + trap_option = TrapItems for value in trap_option.options: if value == "no_traps": continue world_options = self.allsanity_options_with_mods() - world_options.update({options.TrapItems.internal_name: trap_option.options[value]}) + world_options.update({TrapItems.internal_name: trap_option.options[value]}) multi_world = setup_solo_multiworld(world_options) trap_items = [item_data.name for item_data in items_by_group[Group.TRAP] if Group.DEPRECATED not in item_data.groups and item_data.mod_name is None] multiworld_items = [item.name for item in multi_world.get_items()] @@ -218,7 +221,7 @@ class TestTraps(SVTestBase): class TestSpecialOrders(SVTestBase): def test_given_disabled_then_no_order_in_pool(self): - world_options = {options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_disabled} + world_options = {SpecialOrderLocations.internal_name: SpecialOrderLocations.option_disabled} multi_world = setup_solo_multiworld(world_options) locations_in_pool = {location.name for location in multi_world.get_locations() if location.name in location_table} @@ -228,7 +231,7 @@ class TestSpecialOrders(SVTestBase): self.assertNotIn(LocationTags.SPECIAL_ORDER_QI, location.tags) def test_given_board_only_then_no_qi_order_in_pool(self): - world_options = {options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_only} + world_options = {SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_only} multi_world = setup_solo_multiworld(world_options) locations_in_pool = {location.name for location in multi_world.get_locations() if location.name in location_table} @@ -242,8 +245,8 @@ class TestSpecialOrders(SVTestBase): self.assertIn(board_location.name, locations_in_pool) def test_given_board_and_qi_then_all_orders_in_pool(self): - world_options = {options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_qi, - options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_victories} + world_options = {SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_qi, + ArcadeMachineLocations.internal_name: ArcadeMachineLocations.option_victories} multi_world = setup_solo_multiworld(world_options) locations_in_pool = {location.name for location in multi_world.get_locations()} @@ -258,8 +261,8 @@ class TestSpecialOrders(SVTestBase): self.assertIn(board_location.name, locations_in_pool) def test_given_board_and_qi_without_arcade_machines_then_lets_play_a_game_not_in_pool(self): - world_options = {options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_qi, - options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_disabled} + world_options = {SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_qi, + ArcadeMachineLocations.internal_name: ArcadeMachineLocations.option_disabled} multi_world = setup_solo_multiworld(world_options) locations_in_pool = {location.name for location in multi_world.get_locations()} diff --git a/worlds/stardew_valley/test/TestRegions.py b/worlds/stardew_valley/test/TestRegions.py index 293ce72d07..2347ca33db 100644 --- a/worlds/stardew_valley/test/TestRegions.py +++ b/worlds/stardew_valley/test/TestRegions.py @@ -3,7 +3,8 @@ import sys import unittest from . import SVTestBase, setup_solo_multiworld -from .. import StardewOptions, options, StardewValleyWorld +from .. import options, StardewValleyWorld, StardewValleyOptions +from ..options import EntranceRandomization, ExcludeGingerIsland from ..regions import vanilla_regions, vanilla_connections, randomize_connections, RandomizationFlag connections_by_name = {connection.name for connection in vanilla_connections} @@ -37,11 +38,12 @@ class TestEntranceRando(unittest.TestCase): seed = random.randrange(sys.maxsize) with self.subTest(flag=flag, msg=f"Seed: {seed}"): rand = random.Random(seed) - world_options = StardewOptions({options.EntranceRandomization.internal_name: option, - options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false}) + world_options = {EntranceRandomization.internal_name: option, + ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false} + multiworld = setup_solo_multiworld(world_options) regions_by_name = {region.name: region for region in vanilla_regions} - _, randomized_connections = randomize_connections(rand, world_options, regions_by_name) + _, randomized_connections = randomize_connections(rand, multiworld.worlds[1].options, regions_by_name) for connection in vanilla_connections: if flag in connection.flag: @@ -62,11 +64,12 @@ class TestEntranceRando(unittest.TestCase): with self.subTest(option=option, flag=flag): seed = random.randrange(sys.maxsize) rand = random.Random(seed) - world_options = StardewOptions({options.EntranceRandomization.internal_name: option, - options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true}) + world_options = {EntranceRandomization.internal_name: option, + ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true} + multiworld = setup_solo_multiworld(world_options) regions_by_name = {region.name: region for region in vanilla_regions} - _, randomized_connections = randomize_connections(rand, world_options, regions_by_name) + _, randomized_connections = randomize_connections(rand, multiworld.worlds[1].options, regions_by_name) for connection in vanilla_connections: if flag in connection.flag: diff --git a/worlds/stardew_valley/test/__init__.py b/worlds/stardew_valley/test/__init__.py index 59da64c1d7..53181154d3 100644 --- a/worlds/stardew_valley/test/__init__.py +++ b/worlds/stardew_valley/test/__init__.py @@ -5,9 +5,12 @@ from typing import Dict, FrozenSet, Tuple, Any, ClassVar from BaseClasses import MultiWorld from test.TestBase import WorldTestBase from test.general import gen_steps, setup_solo_multiworld as setup_base_solo_multiworld -from .. import StardewValleyWorld, options +from .. import StardewValleyWorld from ..mods.mod_data import ModNames from worlds.AutoWorld import call_all +from ..options import Cropsanity, SkillProgression, SpecialOrderLocations, Friendsanity, NumberOfLuckBuffs, SeasonRandomization, ToolProgression, \ + ElevatorProgression, Museumsanity, BackpackProgression, BuildingProgression, ArcadeMachineLocations, HelpWantedLocations, Fishsanity, NumberOfMovementBuffs, \ + BundleRandomization, BundlePrice, FestivalLocations, FriendsanityHeartSize, ExcludeGingerIsland, TrapItems, Goal, Mods class SVTestBase(WorldTestBase): @@ -33,48 +36,48 @@ class SVTestBase(WorldTestBase): def minimal_locations_maximal_items(self): min_max_options = { - options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized, - options.Cropsanity.internal_name: options.Cropsanity.option_shuffled, - options.BackpackProgression.internal_name: options.BackpackProgression.option_vanilla, - options.ToolProgression.internal_name: options.ToolProgression.option_vanilla, - options.SkillProgression.internal_name: options.SkillProgression.option_vanilla, - options.BuildingProgression.internal_name: options.BuildingProgression.option_vanilla, - options.ElevatorProgression.internal_name: options.ElevatorProgression.option_vanilla, - options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_disabled, - options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_disabled, - options.HelpWantedLocations.internal_name: 0, - options.Fishsanity.internal_name: options.Fishsanity.option_none, - options.Museumsanity.internal_name: options.Museumsanity.option_none, - options.Friendsanity.internal_name: options.Friendsanity.option_none, - options.NumberOfMovementBuffs.internal_name: 12, - options.NumberOfLuckBuffs.internal_name: 12, + SeasonRandomization.internal_name: SeasonRandomization.option_randomized, + Cropsanity.internal_name: Cropsanity.option_shuffled, + BackpackProgression.internal_name: BackpackProgression.option_vanilla, + ToolProgression.internal_name: ToolProgression.option_vanilla, + SkillProgression.internal_name: SkillProgression.option_vanilla, + BuildingProgression.internal_name: BuildingProgression.option_vanilla, + ElevatorProgression.internal_name: ElevatorProgression.option_vanilla, + ArcadeMachineLocations.internal_name: ArcadeMachineLocations.option_disabled, + SpecialOrderLocations.internal_name: SpecialOrderLocations.option_disabled, + HelpWantedLocations.internal_name: 0, + Fishsanity.internal_name: Fishsanity.option_none, + Museumsanity.internal_name: Museumsanity.option_none, + Friendsanity.internal_name: Friendsanity.option_none, + NumberOfMovementBuffs.internal_name: 12, + NumberOfLuckBuffs.internal_name: 12, } return min_max_options def allsanity_options_without_mods(self): allsanity = { - options.Goal.internal_name: options.Goal.option_perfection, - options.BundleRandomization.internal_name: options.BundleRandomization.option_shuffled, - options.BundlePrice.internal_name: options.BundlePrice.option_expensive, - options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized, - options.Cropsanity.internal_name: options.Cropsanity.option_shuffled, - options.BackpackProgression.internal_name: options.BackpackProgression.option_progressive, - options.ToolProgression.internal_name: options.ToolProgression.option_progressive, - options.SkillProgression.internal_name: options.SkillProgression.option_progressive, - options.BuildingProgression.internal_name: options.BuildingProgression.option_progressive, - options.FestivalLocations.internal_name: options.FestivalLocations.option_hard, - options.ElevatorProgression.internal_name: options.ElevatorProgression.option_progressive, - options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_full_shuffling, - options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_qi, - options.HelpWantedLocations.internal_name: 56, - options.Fishsanity.internal_name: options.Fishsanity.option_all, - options.Museumsanity.internal_name: options.Museumsanity.option_all, - options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage, - options.FriendsanityHeartSize.internal_name: 1, - options.NumberOfMovementBuffs.internal_name: 12, - options.NumberOfLuckBuffs.internal_name: 12, - options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false, - options.TrapItems.internal_name: options.TrapItems.option_nightmare, + Goal.internal_name: Goal.option_perfection, + BundleRandomization.internal_name: BundleRandomization.option_shuffled, + BundlePrice.internal_name: BundlePrice.option_expensive, + SeasonRandomization.internal_name: SeasonRandomization.option_randomized, + Cropsanity.internal_name: Cropsanity.option_shuffled, + BackpackProgression.internal_name: BackpackProgression.option_progressive, + ToolProgression.internal_name: ToolProgression.option_progressive, + SkillProgression.internal_name: SkillProgression.option_progressive, + BuildingProgression.internal_name: BuildingProgression.option_progressive, + FestivalLocations.internal_name: FestivalLocations.option_hard, + ElevatorProgression.internal_name: ElevatorProgression.option_progressive, + ArcadeMachineLocations.internal_name: ArcadeMachineLocations.option_full_shuffling, + SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_qi, + HelpWantedLocations.internal_name: 56, + Fishsanity.internal_name: Fishsanity.option_all, + Museumsanity.internal_name: Museumsanity.option_all, + Friendsanity.internal_name: Friendsanity.option_all_with_marriage, + FriendsanityHeartSize.internal_name: 1, + NumberOfMovementBuffs.internal_name: 12, + NumberOfLuckBuffs.internal_name: 12, + ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_false, + TrapItems.internal_name: TrapItems.option_nightmare, } return allsanity @@ -89,7 +92,7 @@ class SVTestBase(WorldTestBase): ModNames.wellwick, ModNames.ginger, ModNames.shiko, ModNames.delores, ModNames.ayeisha, ModNames.riley, ModNames.skull_cavern_elevator ) - allsanity.update({options.Mods.internal_name: all_mods}) + allsanity.update({Mods.internal_name: all_mods}) return allsanity pre_generated_worlds = {} @@ -110,7 +113,7 @@ def setup_solo_multiworld(test_options=None, seed=None, multiworld.set_seed(seed) # print(f"Seed: {multiworld.seed}") # Uncomment to print the seed for every test args = Namespace() - for name, option in StardewValleyWorld.option_definitions.items(): + for name, option in StardewValleyWorld.options_dataclass.type_hints.items(): value = option(test_options[name]) if name in test_options else option.from_any(option.default) setattr(args, name, {1: value}) multiworld.set_options(args) diff --git a/worlds/stardew_valley/test/checks/goal_checks.py b/worlds/stardew_valley/test/checks/goal_checks.py index e1059fe2d6..d0f06a6caa 100644 --- a/worlds/stardew_valley/test/checks/goal_checks.py +++ b/worlds/stardew_valley/test/checks/goal_checks.py @@ -1,11 +1,11 @@ from BaseClasses import MultiWorld -from .option_checks import is_setting, assert_is_setting +from .option_checks import get_stardew_options from ... import options from .. import SVTestBase def is_goal(multiworld: MultiWorld, goal: int) -> bool: - return is_setting(multiworld, options.Goal.internal_name, goal) + return get_stardew_options(multiworld).goal.value == goal def is_bottom_mines(multiworld: MultiWorld) -> bool: @@ -33,7 +33,7 @@ def is_not_perfection(multiworld: MultiWorld) -> bool: def assert_ginger_island_is_included(tester: SVTestBase, multiworld: MultiWorld): - assert_is_setting(tester, multiworld, options.ExcludeGingerIsland.internal_name, options.ExcludeGingerIsland.option_false) + tester.assertEqual(get_stardew_options(multiworld).exclude_ginger_island, options.ExcludeGingerIsland.option_false) def assert_walnut_hunter_world_is_valid(tester: SVTestBase, multiworld: MultiWorld): diff --git a/worlds/stardew_valley/test/checks/option_checks.py b/worlds/stardew_valley/test/checks/option_checks.py index e6bced5b1c..ce8e552461 100644 --- a/worlds/stardew_valley/test/checks/option_checks.py +++ b/worlds/stardew_valley/test/checks/option_checks.py @@ -1,5 +1,3 @@ -from typing import Union - from BaseClasses import MultiWorld from .world_checks import get_all_item_names, get_all_location_names from .. import SVTestBase @@ -8,32 +6,16 @@ from ...locations import LocationTags from ...strings.ap_names.transport_names import Transportation -def get_stardew_world(multiworld: MultiWorld) -> Union[StardewValleyWorld, None]: +def get_stardew_world(multiworld: MultiWorld) -> StardewValleyWorld: for world_key in multiworld.worlds: world = multiworld.worlds[world_key] if isinstance(world, StardewValleyWorld): return world - return None + raise ValueError("no stardew world in this multiworld") -def is_setting(multiworld: MultiWorld, setting_name: str, setting_value: int) -> bool: - stardew_world = get_stardew_world(multiworld) - if not stardew_world: - return False - current_value = stardew_world.options[setting_name] - return current_value == setting_value - - -def is_not_setting(multiworld: MultiWorld, setting_name: str, setting_value: int) -> bool: - return not is_setting(multiworld, setting_name, setting_value) - - -def assert_is_setting(tester: SVTestBase, multiworld: MultiWorld, setting_name: str, setting_value: int) -> bool: - stardew_world = get_stardew_world(multiworld) - if not stardew_world: - return False - current_value = stardew_world.options[setting_name] - tester.assertEqual(current_value, setting_value) +def get_stardew_options(multiworld: MultiWorld) -> options.StardewValleyOptions: + return get_stardew_world(multiworld).options def assert_can_reach_island(tester: SVTestBase, multiworld: MultiWorld): @@ -49,7 +31,8 @@ def assert_cannot_reach_island(tester: SVTestBase, multiworld: MultiWorld): def assert_can_reach_island_if_should(tester: SVTestBase, multiworld: MultiWorld): - include_island = is_setting(multiworld, options.ExcludeGingerIsland.internal_name, options.ExcludeGingerIsland.option_false) + stardew_options = get_stardew_options(multiworld) + include_island = stardew_options.exclude_ginger_island.value == options.ExcludeGingerIsland.option_false if include_island: assert_can_reach_island(tester, multiworld) else: @@ -57,7 +40,7 @@ def assert_can_reach_island_if_should(tester: SVTestBase, multiworld: MultiWorld def assert_cropsanity_same_number_items_and_locations(tester: SVTestBase, multiworld: MultiWorld): - is_cropsanity = is_setting(multiworld, options.Cropsanity.internal_name, options.Cropsanity.option_shuffled) + is_cropsanity = get_stardew_options(multiworld).cropsanity.value == options.Cropsanity.option_shuffled if not is_cropsanity: return @@ -80,11 +63,10 @@ def assert_has_deluxe_scarecrow_recipe(tester: SVTestBase, multiworld: MultiWorl def assert_festivals_give_access_to_deluxe_scarecrow(tester: SVTestBase, multiworld: MultiWorld): - has_festivals = is_not_setting(multiworld, options.FestivalLocations.internal_name, options.FestivalLocations.option_disabled) + stardew_options = get_stardew_options(multiworld) + has_festivals = stardew_options.festival_locations.value != options.FestivalLocations.option_disabled if not has_festivals: return assert_all_rarecrows_exist(tester, multiworld) assert_has_deluxe_scarecrow_recipe(tester, multiworld) - - diff --git a/worlds/stardew_valley/test/long/TestModsLong.py b/worlds/stardew_valley/test/long/TestModsLong.py new file mode 100644 index 0000000000..b3ec6f1420 --- /dev/null +++ b/worlds/stardew_valley/test/long/TestModsLong.py @@ -0,0 +1,63 @@ +from typing import List, Union + +from BaseClasses import MultiWorld +from worlds.stardew_valley.mods.mod_data import ModNames +from worlds.stardew_valley.test import setup_solo_multiworld +from worlds.stardew_valley.test.TestOptions import basic_checks, SVTestBase +from worlds.stardew_valley.items import item_table +from worlds.stardew_valley.locations import location_table +from worlds.stardew_valley.options import Mods +from .option_names import options_to_include + +all_mods = frozenset({ModNames.deepwoods, ModNames.tractor, ModNames.big_backpack, + ModNames.luck_skill, ModNames.magic, ModNames.socializing_skill, ModNames.archaeology, + ModNames.cooking_skill, ModNames.binning_skill, ModNames.juna, + ModNames.jasper, ModNames.alec, ModNames.yoba, ModNames.eugene, + ModNames.wellwick, ModNames.ginger, ModNames.shiko, ModNames.delores, + ModNames.ayeisha, ModNames.riley, ModNames.skull_cavern_elevator}) + + +def check_stray_mod_items(chosen_mods: Union[List[str], str], tester: SVTestBase, multiworld: MultiWorld): + if isinstance(chosen_mods, str): + chosen_mods = [chosen_mods] + for multiworld_item in multiworld.get_items(): + item = item_table[multiworld_item.name] + tester.assertTrue(item.mod_name is None or item.mod_name in chosen_mods) + for multiworld_location in multiworld.get_locations(): + if multiworld_location.event: + continue + location = location_table[multiworld_location.name] + tester.assertTrue(location.mod_name is None or location.mod_name in chosen_mods) + + +class TestGenerateModsOptions(SVTestBase): + + def test_given_mod_pairs_when_generate_then_basic_checks(self): + if self.skip_long_tests: + return + mods = list(all_mods) + num_mods = len(mods) + for mod1_index in range(0, num_mods): + for mod2_index in range(mod1_index + 1, num_mods): + mod1 = mods[mod1_index] + mod2 = mods[mod2_index] + mod_pair = (mod1, mod2) + with self.subTest(f"Mods: {mod_pair}"): + multiworld = setup_solo_multiworld({Mods: mod_pair}) + basic_checks(self, multiworld) + check_stray_mod_items(list(mod_pair), self, multiworld) + + def test_given_mod_names_when_generate_paired_with_other_options_then_basic_checks(self): + if self.skip_long_tests: + return + num_options = len(options_to_include) + for option_index in range(0, num_options): + option = options_to_include[option_index] + if not option.options: + continue + for value in option.options: + for mod in all_mods: + with self.subTest(f"{option.internal_name}: {value}, Mod: {mod}"): + multiworld = setup_solo_multiworld({option.internal_name: option.options[value], Mods: mod}) + basic_checks(self, multiworld) + check_stray_mod_items(mod, self, multiworld) \ No newline at end of file diff --git a/worlds/stardew_valley/test/long/TestOptionsLong.py b/worlds/stardew_valley/test/long/TestOptionsLong.py index c614ddcc36..23ac6125e6 100644 --- a/worlds/stardew_valley/test/long/TestOptionsLong.py +++ b/worlds/stardew_valley/test/long/TestOptionsLong.py @@ -24,7 +24,6 @@ class TestGenerateDynamicOptions(SVTestBase): def test_given_option_pair_when_generate_then_basic_checks(self): if self.skip_long_tests: return - num_options = len(options_to_include) for option1_index in range(0, num_options): for option2_index in range(option1_index + 1, num_options): diff --git a/worlds/stardew_valley/test/long/TestRandomWorlds.py b/worlds/stardew_valley/test/long/TestRandomWorlds.py index 6ba814aba2..0145f471d1 100644 --- a/worlds/stardew_valley/test/long/TestRandomWorlds.py +++ b/worlds/stardew_valley/test/long/TestRandomWorlds.py @@ -1,4 +1,4 @@ -from typing import Dict, List +from typing import Dict import random from BaseClasses import MultiWorld @@ -9,7 +9,6 @@ from ..checks.goal_checks import assert_perfection_world_is_valid, assert_goal_w from ..checks.option_checks import assert_can_reach_island_if_should, assert_cropsanity_same_number_items_and_locations, \ assert_festivals_give_access_to_deluxe_scarecrow from ..checks.world_checks import assert_same_number_items_locations, assert_victory_exists -from ... import options def get_option_choices(option) -> Dict[str, int]: diff --git a/worlds/stardew_valley/test/long/option_names.py b/worlds/stardew_valley/test/long/option_names.py index 9bb950d3a6..649d0da5b3 100644 --- a/worlds/stardew_valley/test/long/option_names.py +++ b/worlds/stardew_valley/test/long/option_names.py @@ -1,7 +1,8 @@ -from worlds.stardew_valley.options import stardew_valley_option_classes +from ... import StardewValleyWorld options_to_exclude = ["profit_margin", "starting_money", "multiple_day_sleep_enabled", "multiple_day_sleep_cost", "experience_multiplier", "friendship_multiplier", "debris_multiplier", - "quick_start", "gifting", "gift_tax"] -options_to_include = [option_to_include for option_to_include in stardew_valley_option_classes - if option_to_include.internal_name not in options_to_exclude] + "quick_start", "gifting", "gift_tax", "progression_balancing", "accessibility", "start_inventory", "start_hints", "death_link"] + +options_to_include = [option for option_name, option in StardewValleyWorld.options_dataclass.type_hints.items() + if option_name not in options_to_exclude] diff --git a/worlds/stardew_valley/test/mods/TestMods.py b/worlds/stardew_valley/test/mods/TestMods.py index a3198e4d2a..02fd30a6b1 100644 --- a/worlds/stardew_valley/test/mods/TestMods.py +++ b/worlds/stardew_valley/test/mods/TestMods.py @@ -4,21 +4,21 @@ import random import sys from BaseClasses import MultiWorld -from worlds.stardew_valley.test import setup_solo_multiworld -from worlds.stardew_valley.test.TestOptions import basic_checks, SVTestBase -from worlds.stardew_valley import options, locations, items, Group, ItemClassification, StardewOptions -from worlds.stardew_valley.mods.mod_data import ModNames -from worlds.stardew_valley.regions import RandomizationFlag, create_final_connections, randomize_connections, create_final_regions -from worlds.stardew_valley.items import item_table, items_by_group -from worlds.stardew_valley.locations import location_table, LocationTags -from worlds.stardew_valley.options import stardew_valley_option_classes, Mods, EntranceRandomization +from ...mods.mod_data import ModNames +from .. import setup_solo_multiworld +from ..TestOptions import basic_checks, SVTestBase +from ... import items, Group, ItemClassification +from ...regions import RandomizationFlag, create_final_connections, randomize_connections, create_final_regions +from ...items import item_table, items_by_group +from ...locations import location_table +from ...options import Mods, EntranceRandomization, Friendsanity, SeasonRandomization, SpecialOrderLocations, ExcludeGingerIsland, TrapItems -mod_list = ["DeepWoods", "Tractor Mod", "Bigger Backpack", - "Luck Skill", "Magic", "Socializing Skill", "Archaeology", - "Cooking Skill", "Binning Skill", "Juna - Roommate NPC", - "Professor Jasper Thomas", "Alec Revisited", "Custom NPC - Yoba", "Custom NPC Eugene", - "'Prophet' Wellwick", "Mister Ginger (cat npc)", "Shiko - New Custom NPC", "Delores - Custom NPC", - "Ayeisha - The Postal Worker (Custom NPC)", "Custom NPC - Riley", "Skull Cavern Elevator"] +all_mods = frozenset({ModNames.deepwoods, ModNames.tractor, ModNames.big_backpack, + ModNames.luck_skill, ModNames.magic, ModNames.socializing_skill, ModNames.archaeology, + ModNames.cooking_skill, ModNames.binning_skill, ModNames.juna, + ModNames.jasper, ModNames.alec, ModNames.yoba, ModNames.eugene, + ModNames.wellwick, ModNames.ginger, ModNames.shiko, ModNames.delores, + ModNames.ayeisha, ModNames.riley, ModNames.skull_cavern_elevator}) def check_stray_mod_items(chosen_mods: Union[List[str], str], tester: SVTestBase, multiworld: MultiWorld): @@ -37,54 +37,27 @@ def check_stray_mod_items(chosen_mods: Union[List[str], str], tester: SVTestBase class TestGenerateModsOptions(SVTestBase): def test_given_single_mods_when_generate_then_basic_checks(self): - for mod in mod_list: + for mod in all_mods: with self.subTest(f"Mod: {mod}"): multi_world = setup_solo_multiworld({Mods: mod}) basic_checks(self, multi_world) check_stray_mod_items(mod, self, multi_world) - def test_given_mod_pairs_when_generate_then_basic_checks(self): - if self.skip_long_tests: - return - num_mods = len(mod_list) - for mod1_index in range(0, num_mods): - for mod2_index in range(mod1_index + 1, num_mods): - mod1 = mod_list[mod1_index] - mod2 = mod_list[mod2_index] - mods = (mod1, mod2) - with self.subTest(f"Mods: {mods}"): - multiworld = setup_solo_multiworld({Mods: mods}) - basic_checks(self, multiworld) - check_stray_mod_items(list(mods), self, multiworld) - def test_given_mod_names_when_generate_paired_with_entrance_randomizer_then_basic_checks(self): for option in EntranceRandomization.options: - for mod in mod_list: + for mod in all_mods: with self.subTest(f"entrance_randomization: {option}, Mod: {mod}"): multiworld = setup_solo_multiworld({EntranceRandomization.internal_name: option, Mods: mod}) basic_checks(self, multiworld) check_stray_mod_items(mod, self, multiworld) - def test_given_mod_names_when_generate_paired_with_other_options_then_basic_checks(self): - if self.skip_long_tests: - return - for option in stardew_valley_option_classes: - if not option.options: - continue - for value in option.options: - for mod in mod_list: - with self.subTest(f"{option.internal_name}: {value}, Mod: {mod}"): - multiworld = setup_solo_multiworld({option.internal_name: option.options[value], Mods: mod}) - basic_checks(self, multiworld) - check_stray_mod_items(mod, self, multiworld) - class TestBaseItemGeneration(SVTestBase): options = { - options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage, - options.SeasonRandomization.internal_name: options.SeasonRandomization.option_progressive, - options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_qi, - options.Mods.internal_name: mod_list + Friendsanity.internal_name: Friendsanity.option_all_with_marriage, + SeasonRandomization.internal_name: SeasonRandomization.option_progressive, + SpecialOrderLocations.internal_name: SpecialOrderLocations.option_board_qi, + Mods.internal_name: all_mods } def test_all_progression_items_are_added_to_the_pool(self): @@ -105,10 +78,10 @@ class TestBaseItemGeneration(SVTestBase): class TestNoGingerIslandModItemGeneration(SVTestBase): options = { - options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage, - options.SeasonRandomization.internal_name: options.SeasonRandomization.option_progressive, - options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true, - options.Mods.internal_name: mod_list + Friendsanity.internal_name: Friendsanity.option_all_with_marriage, + SeasonRandomization.internal_name: SeasonRandomization.option_progressive, + ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true, + Mods.internal_name: all_mods } def test_all_progression_items_except_island_are_added_to_the_pool(self): @@ -134,29 +107,31 @@ class TestModEntranceRando(unittest.TestCase): def test_mod_entrance_randomization(self): - for option, flag in [(options.EntranceRandomization.option_pelican_town, RandomizationFlag.PELICAN_TOWN), - (options.EntranceRandomization.option_non_progression, RandomizationFlag.NON_PROGRESSION), - (options.EntranceRandomization.option_buildings, RandomizationFlag.BUILDINGS)]: + for option, flag in [(EntranceRandomization.option_pelican_town, RandomizationFlag.PELICAN_TOWN), + (EntranceRandomization.option_non_progression, RandomizationFlag.NON_PROGRESSION), + (EntranceRandomization.option_buildings, RandomizationFlag.BUILDINGS)]: with self.subTest(option=option, flag=flag): seed = random.randrange(sys.maxsize) rand = random.Random(seed) - world_options = StardewOptions({options.EntranceRandomization.internal_name: option, - options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false, - options.Mods.internal_name: mod_list}) - final_regions = create_final_regions(world_options) - final_connections = create_final_connections(world_options) + world_options = {EntranceRandomization.internal_name: option, + ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_false, + Mods.internal_name: all_mods} + multiworld = setup_solo_multiworld(world_options) + world = multiworld.worlds[1] + final_regions = create_final_regions(world.options) + final_connections = create_final_connections(world.options) regions_by_name = {region.name: region for region in final_regions} - _, randomized_connections = randomize_connections(rand, world_options, regions_by_name) + _, randomized_connections = randomize_connections(rand, world.options, regions_by_name) for connection in final_connections: if flag in connection.flag: connection_in_randomized = connection.name in randomized_connections reverse_in_randomized = connection.reverse in randomized_connections self.assertTrue(connection_in_randomized, - f"Connection {connection.name} should be randomized but it is not in the output. Seed = {seed}") + f"Connection {connection.name} should be randomized but it is not in the output. Seed = {seed}") self.assertTrue(reverse_in_randomized, - f"Connection {connection.reverse} should be randomized but it is not in the output. Seed = {seed}") + f"Connection {connection.reverse} should be randomized but it is not in the output. Seed = {seed}") self.assertEqual(len(set(randomized_connections.values())), len(randomized_connections.values()), f"Connections are duplicated in randomization. Seed = {seed}") @@ -164,12 +139,11 @@ class TestModEntranceRando(unittest.TestCase): class TestModTraps(SVTestBase): def test_given_traps_when_generate_then_all_traps_in_pool(self): - trap_option = options.TrapItems - for value in trap_option.options: + for value in TrapItems.options: if value == "no_traps": continue world_options = self.allsanity_options_without_mods() - world_options.update({options.TrapItems.internal_name: trap_option.options[value], Mods: "Magic"}) + world_options.update({TrapItems.internal_name: TrapItems.options[value], Mods: "Magic"}) multi_world = setup_solo_multiworld(world_options) trap_items = [item_data.name for item_data in items_by_group[Group.TRAP] if Group.DEPRECATED not in item_data.groups] multiworld_items = [item.name for item in multi_world.get_items()] diff --git a/worlds/zillion/__init__.py b/worlds/zillion/__init__.py index f5e04b4ebc..0d8fac3337 100644 --- a/worlds/zillion/__init__.py +++ b/worlds/zillion/__init__.py @@ -12,7 +12,7 @@ from BaseClasses import ItemClassification, LocationProgressType, \ MultiWorld, Item, CollectionState, Entrance, Tutorial from .logic import cs_to_zz_locs from .region import ZillionLocation, ZillionRegion -from .options import ZillionStartChar, zillion_options, validate +from .options import ZillionOptions, ZillionStartChar, 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 @@ -70,7 +70,9 @@ class ZillionWorld(World): game = "Zillion" web = ZillionWebWorld() - option_definitions = zillion_options + options_dataclass = ZillionOptions + options: ZillionOptions + settings: typing.ClassVar[ZillionSettings] topology_present = True # indicate if world type has any meaningful layout/pathing @@ -142,7 +144,10 @@ class ZillionWorld(World): if not hasattr(self.multiworld, "zillion_logic_cache"): setattr(self.multiworld, "zillion_logic_cache", {}) - zz_op, item_counts = validate(self.multiworld, self.player) + zz_op, item_counts = validate(self.options) + + if zz_op.early_scope: + self.multiworld.early_items[self.player]["Scope"] = 1 self._item_counts = item_counts @@ -299,7 +304,8 @@ class ZillionWorld(World): elif start_char_counts["Champ"] > start_char_counts["Apple"]: to_stay = "Champ" else: # equal - to_stay = multiworld.random.choice(("Apple", "Champ")) + choices: Tuple[Literal['Apple', 'Champ', 'JJ'], ...] = ("Apple", "Champ") + to_stay = multiworld.random.choice(choices) for p, sc in players_start_chars: if sc != to_stay: diff --git a/worlds/zillion/options.py b/worlds/zillion/options.py index 6aa88f5b22..80f9469ec8 100644 --- a/worlds/zillion/options.py +++ b/worlds/zillion/options.py @@ -1,13 +1,14 @@ from collections import Counter -# import logging -from typing import TYPE_CHECKING, Any, Dict, Tuple, cast -from Options import AssembleOptions, DefaultOnToggle, Range, SpecialRange, Toggle, Choice +from dataclasses import dataclass +from typing import Dict, Tuple +from typing_extensions import TypeGuard # remove when Python >= 3.10 + +from Options import DefaultOnToggle, PerGameCommonOptions, Range, SpecialRange, Toggle, Choice + from zilliandomizer.options import \ Options as ZzOptions, char_to_gun, char_to_jump, ID, \ VBLR as ZzVBLR, chars, Chars, ItemCounts as ZzItemCounts from zilliandomizer.options.parsing import validate as zz_validate -if TYPE_CHECKING: - from BaseClasses import MultiWorld class ZillionContinues(SpecialRange): @@ -41,6 +42,19 @@ class VBLR(Choice): option_restrictive = 3 default = 1 + def to_zz_vblr(self) -> ZzVBLR: + def is_vblr(o: str) -> TypeGuard[ZzVBLR]: + """ + This function is because mypy doesn't support narrowing with `in`, + https://github.com/python/mypy/issues/12535 + so this is the only way I see to get type narrowing to `Literal`. + """ + return o in ("vanilla", "balanced", "low", "restrictive") + + key = self.current_key + assert is_vblr(key), f"{key=}" + return key + class ZillionGunLevels(VBLR): """ @@ -225,27 +239,27 @@ class ZillionRoomGen(Toggle): display_name = "room generation" -zillion_options: Dict[str, AssembleOptions] = { - "continues": ZillionContinues, - "floppy_req": ZillionFloppyReq, - "gun_levels": ZillionGunLevels, - "jump_levels": ZillionJumpLevels, - "randomize_alarms": ZillionRandomizeAlarms, - "max_level": ZillionMaxLevel, - "start_char": ZillionStartChar, - "opas_per_level": ZillionOpasPerLevel, - "id_card_count": ZillionIDCardCount, - "bread_count": ZillionBreadCount, - "opa_opa_count": ZillionOpaOpaCount, - "zillion_count": ZillionZillionCount, - "floppy_disk_count": ZillionFloppyDiskCount, - "scope_count": ZillionScopeCount, - "red_id_card_count": ZillionRedIDCardCount, - "early_scope": ZillionEarlyScope, - "skill": ZillionSkill, - "starting_cards": ZillionStartingCards, - "room_gen": ZillionRoomGen, -} +@dataclass +class ZillionOptions(PerGameCommonOptions): + continues: ZillionContinues + floppy_req: ZillionFloppyReq + gun_levels: ZillionGunLevels + jump_levels: ZillionJumpLevels + randomize_alarms: ZillionRandomizeAlarms + max_level: ZillionMaxLevel + start_char: ZillionStartChar + opas_per_level: ZillionOpasPerLevel + id_card_count: ZillionIDCardCount + bread_count: ZillionBreadCount + opa_opa_count: ZillionOpaOpaCount + zillion_count: ZillionZillionCount + floppy_disk_count: ZillionFloppyDiskCount + scope_count: ZillionScopeCount + red_id_card_count: ZillionRedIDCardCount + early_scope: ZillionEarlyScope + skill: ZillionSkill + starting_cards: ZillionStartingCards + room_gen: ZillionRoomGen def convert_item_counts(ic: "Counter[str]") -> ZzItemCounts: @@ -262,47 +276,34 @@ def convert_item_counts(ic: "Counter[str]") -> ZzItemCounts: return tr -def validate(world: "MultiWorld", p: int) -> "Tuple[ZzOptions, Counter[str]]": +def validate(options: ZillionOptions) -> "Tuple[ZzOptions, Counter[str]]": """ adjusts options to make game completion possible - `world` parameter is MultiWorld object that has my options on it - `p` is my player id + `options` parameter is ZillionOptions object that was put on my world by the core """ - for option_name in zillion_options: - assert hasattr(world, option_name), f"Zillion option {option_name} didn't get put in world object" - wo = cast(Any, world) # so I don't need getattr on all the options - skill = wo.skill[p].value + skill = options.skill.value - jump_levels = cast(ZillionJumpLevels, wo.jump_levels[p]) - jump_option = jump_levels.current_key - required_level = char_to_jump["Apple"][cast(ZzVBLR, jump_option)].index(3) + 1 + jump_option = options.jump_levels.to_zz_vblr() + required_level = char_to_jump["Apple"][jump_option].index(3) + 1 if skill == 0: # because of hp logic on final boss required_level = 8 - gun_levels = cast(ZillionGunLevels, wo.gun_levels[p]) - gun_option = gun_levels.current_key - guns_required = char_to_gun["Champ"][cast(ZzVBLR, gun_option)].index(3) + gun_option = options.gun_levels.to_zz_vblr() + guns_required = char_to_gun["Champ"][gun_option].index(3) - floppy_req = cast(ZillionFloppyReq, wo.floppy_req[p]) + floppy_req = options.floppy_req - card = cast(ZillionIDCardCount, wo.id_card_count[p]) - bread = cast(ZillionBreadCount, wo.bread_count[p]) - opa = cast(ZillionOpaOpaCount, wo.opa_opa_count[p]) - gun = cast(ZillionZillionCount, wo.zillion_count[p]) - floppy = cast(ZillionFloppyDiskCount, wo.floppy_disk_count[p]) - scope = cast(ZillionScopeCount, wo.scope_count[p]) - red = cast(ZillionRedIDCardCount, wo.red_id_card_count[p]) item_counts = Counter({ - "ID Card": card, - "Bread": bread, - "Opa-Opa": opa, - "Zillion": gun, - "Floppy Disk": floppy, - "Scope": scope, - "Red ID Card": red + "ID Card": options.id_card_count, + "Bread": options.bread_count, + "Opa-Opa": options.opa_opa_count, + "Zillion": options.zillion_count, + "Floppy Disk": options.floppy_disk_count, + "Scope": options.scope_count, + "Red ID Card": options.red_id_card_count }) minimums = Counter({ "ID Card": 0, @@ -335,10 +336,10 @@ def validate(world: "MultiWorld", p: int) -> "Tuple[ZzOptions, Counter[str]]": item_counts["Empty"] += diff assert sum(item_counts.values()) == 144 - max_level = cast(ZillionMaxLevel, wo.max_level[p]) + max_level = options.max_level max_level.value = max(required_level, max_level.value) - opas_per_level = cast(ZillionOpasPerLevel, wo.opas_per_level[p]) + opas_per_level = options.opas_per_level while (opas_per_level.value > 1) and (1 + item_counts["Opa-Opa"] // opas_per_level.value < max_level.value): # logging.warning( # "zillion options validate: option opas_per_level incompatible with options max_level and opa_opa_count" @@ -347,39 +348,34 @@ def validate(world: "MultiWorld", p: int) -> "Tuple[ZzOptions, Counter[str]]": # that should be all of the level requirements met - name_capitalization = { + name_capitalization: Dict[str, Chars] = { "jj": "JJ", "apple": "Apple", "champ": "Champ", } - start_char = cast(ZillionStartChar, wo.start_char[p]) + start_char = options.start_char start_char_name = name_capitalization[start_char.current_key] assert start_char_name in chars - start_char_name = cast(Chars, start_char_name) - starting_cards = cast(ZillionStartingCards, wo.starting_cards[p]) + starting_cards = options.starting_cards - room_gen = cast(ZillionRoomGen, wo.room_gen[p]) - - early_scope = cast(ZillionEarlyScope, wo.early_scope[p]) - if early_scope: - world.early_items[p]["Scope"] = 1 + room_gen = options.room_gen zz_item_counts = convert_item_counts(item_counts) zz_op = ZzOptions( zz_item_counts, - cast(ZzVBLR, jump_option), - cast(ZzVBLR, gun_option), + jump_option, + gun_option, opas_per_level.value, max_level.value, False, # tutorial skill, start_char_name, floppy_req.value, - wo.continues[p].value, - wo.randomize_alarms[p].value, - False, # early scope is done with AP early_items API + options.continues.value, + bool(options.randomize_alarms.value), + bool(options.early_scope.value), True, # balance defense starting_cards.value, bool(room_gen.value) diff --git a/worlds/zillion/requirements.txt b/worlds/zillion/requirements.txt index 4858ef3153..93d2dbc1a6 100644 --- a/worlds/zillion/requirements.txt +++ b/worlds/zillion/requirements.txt @@ -1 +1,2 @@ zilliandomizer @ git+https://github.com/beauxq/zilliandomizer@d7122bcbeda40da5db26d60fad06246a1331706f#0.5.4 +typing-extensions>=4.7, <5 diff --git a/worlds/zillion/test/TestOptions.py b/worlds/zillion/test/TestOptions.py index 1ec186dae5..c4f02d4bd3 100644 --- a/worlds/zillion/test/TestOptions.py +++ b/worlds/zillion/test/TestOptions.py @@ -1,6 +1,6 @@ from . import ZillionTestBase -from worlds.zillion.options import ZillionJumpLevels, ZillionGunLevels, validate +from worlds.zillion.options import ZillionJumpLevels, ZillionGunLevels, ZillionOptions, validate from zilliandomizer.options import VBLR_CHOICES @@ -9,7 +9,9 @@ class OptionsTest(ZillionTestBase): def test_validate_default(self) -> None: self.world_setup() - validate(self.multiworld, 1) + options = self.multiworld.worlds[1].options + assert isinstance(options, ZillionOptions) + validate(options) def test_vblr_ap_to_zz(self) -> None: """ all of the valid values for the AP options map to valid values for ZZ options """ @@ -20,7 +22,9 @@ class OptionsTest(ZillionTestBase): for value in vblr_class.name_lookup.values(): self.options = {option_name: value} self.world_setup() - zz_options, _item_counts = validate(self.multiworld, 1) + options = self.multiworld.worlds[1].options + assert isinstance(options, ZillionOptions) + zz_options, _item_counts = validate(options) assert getattr(zz_options, option_name) in VBLR_CHOICES # TODO: test validate with invalid combinations of options From d7475ddd73699fcf290d956e1a31f9aecc1051dc Mon Sep 17 00:00:00 2001 From: Doug Hoskisson Date: Tue, 10 Oct 2023 14:08:19 -0700 Subject: [PATCH 108/144] Zillion: remove test detection hack (#2287) --- worlds/zillion/__init__.py | 15 ++++++++------- worlds/zillion/requirements.txt | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/worlds/zillion/__init__.py b/worlds/zillion/__init__.py index 0d8fac3337..1e79f4f133 100644 --- a/worlds/zillion/__init__.py +++ b/worlds/zillion/__init__.py @@ -24,7 +24,6 @@ from zilliandomizer.system import System from zilliandomizer.logic_components.items import RESCUE, items as zz_items, Item as ZzItem from zilliandomizer.logic_components.locations import Location as ZzLocation, Req from zilliandomizer.options import Chars -from zilliandomizer.patch import detect_test from ..AutoWorld import World, WebWorld @@ -151,9 +150,7 @@ class ZillionWorld(World): self._item_counts = item_counts - rom_dir_name = "" if detect_test() else os.path.dirname(get_base_rom_path()) with redirect_stdout(self.lsi): # type: ignore - self.zz_system.make_patcher(rom_dir_name) self.zz_system.make_randomizer(zz_op) self.zz_system.seed(self.multiworld.seed) @@ -323,6 +320,8 @@ class ZillionWorld(World): """ sync zilliandomizer item locations with AP item locations """ + rom_dir_name = os.path.dirname(get_base_rom_path()) + self.zz_system.make_patcher(rom_dir_name) assert self.zz_system.randomizer and self.zz_system.patcher, "generate_early hasn't been called" zz_options = self.zz_system.randomizer.options @@ -373,7 +372,9 @@ class ZillionWorld(World): zz_options.start_char, self.zz_system.randomizer.loc_name_2_pretty) self.slot_data_ready.set() - zz_patcher.all_fixes_and_options(zz_options) + rm = self.zz_system.resource_managers + assert rm, "missing resource_managers from generate_early" + zz_patcher.all_fixes_and_options(zz_options, rm) zz_patcher.set_external_item_interface(zz_options.start_char, zz_options.max_level) zz_patcher.set_multiworld_items(multi_items) game_id = self.multiworld.player_name[self.player].encode() + b'\x00' + self.multiworld.seed_name[-6:].encode() @@ -384,7 +385,7 @@ class ZillionWorld(World): If you need any last-second randomization, use MultiWorld.per_slot_randoms[slot] instead.""" self.finalize_item_locations() - assert self.zz_system.patcher, "didn't get patcher from generate_early" + assert self.zz_system.patcher, "didn't get patcher from finalize_item_locations" # original_rom_bytes = self.zz_patcher.rom patched_rom_bytes = self.zz_system.patcher.get_patched_bytes() @@ -415,12 +416,12 @@ class ZillionWorld(World): # TODO: tell client which canisters are keywords # so it can open and get those when restoring doors - zz_patcher = self.zz_system.patcher - assert zz_patcher, "didn't get patcher from generate_early" assert self.zz_system.randomizer, "didn't get randomizer from generate_early" rescues: Dict[str, Any] = {} self.slot_data_ready.wait() + zz_patcher = self.zz_system.patcher + assert zz_patcher, "didn't get patcher from generate_output" for i in (0, 1): if i in zz_patcher.rescue_locations: ri = zz_patcher.rescue_locations[i] diff --git a/worlds/zillion/requirements.txt b/worlds/zillion/requirements.txt index 93d2dbc1a6..c8944925ac 100644 --- a/worlds/zillion/requirements.txt +++ b/worlds/zillion/requirements.txt @@ -1,2 +1,2 @@ -zilliandomizer @ git+https://github.com/beauxq/zilliandomizer@d7122bcbeda40da5db26d60fad06246a1331706f#0.5.4 +zilliandomizer @ git+https://github.com/beauxq/zilliandomizer@ae00a4b186be897c7cfaf429a0e0ff83c4ecf28c#0.6.0 typing-extensions>=4.7, <5 From 88dfbd4087ffe52d28635f37567ca61e4daba7fa Mon Sep 17 00:00:00 2001 From: Remy Jette Date: Tue, 10 Oct 2023 14:20:08 -0700 Subject: [PATCH 109/144] WebHost: Show error instead of 500 for unexpected files in multidata zip (#2260) * WebHost: Show error instead of 500 for unexpected files in multidata zip * Add filename to error message * Apply suggestions from code review Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> --------- Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> --- WebHostLib/upload.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/WebHostLib/upload.py b/WebHostLib/upload.py index 89a839cfa3..e7ac033913 100644 --- a/WebHostLib/upload.py +++ b/WebHostLib/upload.py @@ -104,13 +104,21 @@ def upload_zip_to_db(zfile: zipfile.ZipFile, owner=None, meta={"race": False}, s # Factorio elif file.filename.endswith(".zip"): - _, _, slot_id, *_ = file.filename.split('_')[0].split('-', 3) + try: + _, _, slot_id, *_ = file.filename.split('_')[0].split('-', 3) + except ValueError: + flash("Error: Unexpected file found in .zip: " + file.filename) + return data = zfile.open(file, "r").read() files[int(slot_id[1:])] = data # All other files using the standard MultiWorld.get_out_file_name_base method else: - _, _, slot_id, *_ = file.filename.split('.')[0].split('_', 3) + try: + _, _, slot_id, *_ = file.filename.split('.')[0].split('_', 3) + except ValueError: + flash("Error: Unexpected file found in .zip: " + file.filename) + return data = zfile.open(file, "r").read() files[int(slot_id[1:])] = data From e1ee08a599442b668f8b14b81e71eefdf67afe9f Mon Sep 17 00:00:00 2001 From: Aaron Wagener Date: Tue, 10 Oct 2023 18:51:13 -0500 Subject: [PATCH 110/144] FFR: create items in create_items (#2291) --- worlds/ff1/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/ff1/__init__.py b/worlds/ff1/__init__.py index 56b41d62d0..432467399e 100644 --- a/worlds/ff1/__init__.py +++ b/worlds/ff1/__init__.py @@ -91,7 +91,7 @@ class FF1World(World): def set_rules(self): self.multiworld.completion_condition[self.player] = lambda state: state.has(CHAOS_TERMINATED_EVENT, self.player) - def generate_basic(self): + def create_items(self): items = get_options(self.multiworld, 'items', self.player) if FF1_BRIDGE in items.keys(): self._place_locked_item_in_sphere0(FF1_BRIDGE) From 1ef3bc78dc0875d8e2973b8660b531c39404af14 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Wed, 11 Oct 2023 19:21:02 +0200 Subject: [PATCH 111/144] CommonClient: inherit Context tags (#2283) --- CommonClient.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CommonClient.py b/CommonClient.py index 154b61b1d5..a5e9b4553a 100644 --- a/CommonClient.py +++ b/CommonClient.py @@ -882,7 +882,7 @@ def get_base_parser(description: typing.Optional[str] = None): def run_as_textclient(): class TextContext(CommonContext): # Text Mode to use !hint and such with games that have no text entry - tags = {"AP", "TextOnly"} + tags = CommonContext.tags | {"TextOnly"} game = "" # empty matches any game since 0.3.2 items_handling = 0b111 # receive all items for /received want_slot_data = False # Can't use game specific slot_data From 19d649f92b8d012575936afe4758396999458259 Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Fri, 13 Oct 2023 01:46:16 +0200 Subject: [PATCH 112/144] The Witness: Update docs (outdated information) (#2294) * Update Witness Game Page * Update outdated Witness Setup Guide * Incorporate suggestions --- worlds/witness/docs/en_The Witness.md | 7 ++++--- worlds/witness/docs/setup_en.md | 8 +++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/worlds/witness/docs/en_The Witness.md b/worlds/witness/docs/en_The Witness.md index 2ae478bed0..4d00ecaae4 100644 --- a/worlds/witness/docs/en_The Witness.md +++ b/worlds/witness/docs/en_The Witness.md @@ -10,12 +10,13 @@ config file. Puzzles are randomly generated using the popular [Sigma Rando](https://github.com/sigma144/witness-randomizer). They are made to be similar to the original game, but with different solutions. -Ontop of that each puzzle symbol (Squares, Stars, Dots, etc.) is now an item. +On top of that, each puzzle symbol (Squares, Stars, Dots, etc.) is now an item. Panels with puzzle symbols on them are now locked initially. ## What is a "check" in The Witness? Solving the last panel in a row of panels or an important standalone panel will count as a check, and send out an item. +It is also possible to add Environmental Puzzles into the location pool via the "Shuffle Environmental Puzzles" setting. ## What "items" can you unlock in The Witness? @@ -32,7 +33,7 @@ By default, the audio logs scattered around the world will have 10 hints for you Example: "Shipwreck Vault contains Triangles". -## The Jungle, Orchard, Forest and Color House aren't randomized. What gives? +## The Jungle, Orchard, Forest and Color Bunker aren't randomized. What gives? There are limitations to what can currently be randomized in The Witness. There is an option to turn these non-randomized panels off, called "disable_non_randomized" in your yaml file. This will also slightly change the activation requirement of certain panels, detailed [here](https://github.com/sigma144/witness-randomizer/wiki/Activation-Triggers). @@ -46,4 +47,4 @@ In this case, the generator will make its best attempt to adjust logic according One of the use cases of this could be to pre-open a specific door or pre-activate a single laser. In "shuffle_EPs: obelisk_sides", any Environmental Puzzles in exclude_locations will be pre-completed and not considered for their Obelisk Side. -If every Environmental Puzzle on an Obelisk Side is pre-completed, that side disappears from the location pool entirely. \ No newline at end of file +If every Environmental Puzzle on an Obelisk Side is pre-completed, that side disappears from the location pool entirely. diff --git a/worlds/witness/docs/setup_en.md b/worlds/witness/docs/setup_en.md index 94a50846f9..daa9b8b9b5 100644 --- a/worlds/witness/docs/setup_en.md +++ b/worlds/witness/docs/setup_en.md @@ -29,8 +29,10 @@ To continue an earlier game: ## Archipelago Text Client -It is recommended to have Archipelago's Text Client open on the side to keep track of what items you receive and send, as The Witness has no in-game messages. -
    Or use the Auto-Tracker! +It is recommended to have Archipelago's Text Client open on the side to keep track of what items you receive and send. +
    The Witness does display received and sent items in-game, but only for a short time, and the messages are easy to miss while playing. + +

    Of course, you can also use the Auto-Tracker! ## Auto-Tracking @@ -41,4 +43,4 @@ The Witness has a fully functional map tracker that supports auto-tracking. 3. Click on the "AP" symbol at the top. 4. Enter the AP address, slot name and password. -The rest should take care of itself! Items and checks will be marked automatically, and it even knows your settings - It will hide checks & adjust logic accordingly. Note that the tracker may be out of date. \ No newline at end of file +The rest should take care of itself! Items and checks will be marked automatically, and it even knows your settings - It will hide checks & adjust logic accordingly. From 8fc304269e90c1cc2fbce2d9d0806ddb51f657a4 Mon Sep 17 00:00:00 2001 From: Shiny <36184001+ShinyNT@users.noreply.github.com> Date: Thu, 12 Oct 2023 20:51:10 -0300 Subject: [PATCH 113/144] Docs: add Spanish guide for Muse Dash (#2297) * adding setup_es * Update setup_es.md * Update setup_es.md * Update __init__.py referencing setup_es on init.py * Update __init__.py fixing a space --- worlds/musedash/__init__.py | 11 +++++++- worlds/musedash/docs/setup_es.md | 48 ++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 worlds/musedash/docs/setup_es.md diff --git a/worlds/musedash/__init__.py b/worlds/musedash/__init__.py index 78b9c253d5..754d2352e0 100644 --- a/worlds/musedash/__init__.py +++ b/worlds/musedash/__init__.py @@ -23,7 +23,16 @@ class MuseDashWebWorld(WebWorld): ["DeamonHunter"] ) - tutorials = [setup_en] + setup_es = Tutorial( + setup_en.tutorial_name, + setup_en.description, + "Español", + "setup_es.md", + "setup/es", + ["Shiny"] + ) + + tutorials = [setup_en, setup_es] class MuseDashWorld(World): diff --git a/worlds/musedash/docs/setup_es.md b/worlds/musedash/docs/setup_es.md new file mode 100644 index 0000000000..21fc69e7eb --- /dev/null +++ b/worlds/musedash/docs/setup_es.md @@ -0,0 +1,48 @@ +# Guía de instalación para Muse Dash: Archipelago + +## Enlaces rápidos +- [Página Principal](../../../../games/Muse%20Dash/info/en) +- [Página de Configuraciones](../../../../games/Muse%20Dash/player-settings) + +## Software Requerido + +- Windows 8 o más reciente. +- Muse Dash: [Disponible en Steam](https://store.steampowered.com/app/774171/Muse_Dash/) + - \[Opcional\] [Just as Planned] DLC: [tambien disponible on Steam](https://store.steampowered.com/app/1055810/Muse_Dash__Just_as_planned/) +- Melon Loader: [GitHub](https://github.com/LavaGang/MelonLoader/releases/latest) + - .Net Framework 4.8 podría ser necesario para el instalador: [Descarga](https://dotnet.microsoft.com/en-us/download/dotnet-framework/net48) +- .Net 6.0 (si aún no está instalado): [Descarga](https://dotnet.microsoft.com/en-us/download/dotnet/6.0#runtime-6.0.15) +- Muse Dash Archipelago Mod: [GitHub](https://github.com/DeamonHunter/ArchipelagoMuseDash/releases/latest) + +## Instalar el mod de Archipelago en Muse Dash + +1. Descarga [MelonLoader.Installer.exe](https://github.com/LavaGang/MelonLoader/releases/latest) y ejecutalo. +2. Elije la pestaña "automated", haz clic en el botón "select" y busca tu `MuseDash.exe`. Luego haz clic en "install". + - Puedes encontrar la carpeta en Steam buscando el juego en tu biblioteca, haciendo clic derecho sobre el y elegir *Administrar→Ver archivos locales*. + - Si haces clic en la barra superior que te indica la carpeta en la que estas, te dará la dirección de ésta para que puedas copiarla. Al pegar esa dirección en la ventana que **MelonLoader** abre, irá automaticamente a esa carpeta. +3. Ejecuta el juego una vez, y espera hasta que aparezca la pantalla de inicio de Muse Dash antes de cerrarlo. +4. Descarga la última version de [Muse Dash Archipelago Mod](https://github.com/DeamonHunter/ArchipelagoMuseDash/releases/latest) y extraelo en la nueva carpeta creada llamada `/Mods/`, localizada en la carpeta de instalación de Muse Dash. + - Todos los archivos deben ir directamente en la carpeta `/Mods/`, y NO en una subcarpeta dentro de la carpeta `/Mods/` + +Si todo fue instalado correctamente, un botón aparecerá en la parte inferior derecha del juego una vez abierto, que te permitirá conectarte al servidor de Archipelago. + +## Generar un juego MultiWorld +1. Entra a la página de [configuraciones de jugador](/games/Muse%20Dash/player-settings) y configura las opciones del juego a tu gusto. +2. Genera tu archivo YAML y úsalo para generar un juego nuevo en el radomizer + - (Instrucciones sobre como generar un juego en Archipelago disponibles en la [guía web de Archipelago en Inglés](/tutorial/Archipelago/setup/en)) + +## Unirse a un juego MultiWorld + +1. Ejecuta Muse Dash y pasa por la pantalla de introducción. Haz clic en el botón de la esquina inferior derecha. +2. Ingresa los detalles de la sesión de archipelago, como la dirección del servidor con el puerto (por ejemplo, archipelago.gg:38381), nombre de usuario y contraseña. +3. Si todo se ingresó correctamente, el pop-up debería desaparecer y se mostrará el menú principal habitual. Al ingresar a la selección de canciones, deberías ver una cantidad limitada de canciones. + +## Solución de problemas + +### No Support Module Loaded + +Este error ocurre cuando Melon Loader no puede encontrar los archivos necesarios para ejecutar mods. Generalmente, hay dos razones principales de este error: una falla al generar los archivos cuando el juego se ejecutó por primera vez con Melon Loader, o un antivirus que elimina los archivos después de la generación. + +Para solucionar este problema, primero debes eliminar Melon Loader de Muse Dash. Puedes hacer esto eliminando la carpeta Melon Loader dentro de la carpeta de Muse Dash. Luego, seguir los pasos de instalación nuevamente. + +Si continúas teniendo problemas y estás utilizando un antivirus, es posible que tengas que desactivarlo temporalmente cuando se ejecute Muse Dash por primera vez, o excluir la carpeta Muse Dash de ser escaneada. From fffbe68428e6ac6b562af9f5e2be7e653b1eea44 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 15 Oct 2023 04:51:52 +0200 Subject: [PATCH 114/144] Subnautica: cleanup pass (#2293) --- worlds/subnautica/__init__.py | 60 +++++++++---------- .../subnautica/{Creatures.py => creatures.py} | 0 worlds/subnautica/{Exports.py => exports.py} | 4 +- worlds/subnautica/{Items.py => items.py} | 0 .../subnautica/{Locations.py => locations.py} | 0 worlds/subnautica/{Options.py => options.py} | 4 +- worlds/subnautica/{Rules.py => rules.py} | 31 +++++----- worlds/subnautica/test/__init__.py | 12 ++-- 8 files changed, 55 insertions(+), 56 deletions(-) rename worlds/subnautica/{Creatures.py => creatures.py} (100%) rename worlds/subnautica/{Exports.py => exports.py} (95%) rename worlds/subnautica/{Items.py => items.py} (100%) rename worlds/subnautica/{Locations.py => locations.py} (100%) rename worlds/subnautica/{Options.py => options.py} (98%) rename worlds/subnautica/{Rules.py => rules.py} (92%) diff --git a/worlds/subnautica/__init__.py b/worlds/subnautica/__init__.py index 2d4cf2faf6..7b25b61c81 100644 --- a/worlds/subnautica/__init__.py +++ b/worlds/subnautica/__init__.py @@ -6,12 +6,12 @@ from typing import List, Dict, Any, cast from BaseClasses import Region, Entrance, Location, Item, Tutorial, ItemClassification from worlds.AutoWorld import World, WebWorld -from . import Items -from . import Locations -from . import Creatures -from . import Options -from .Items import item_table, group_items, items_by_type, ItemType -from .Rules import set_rules +from . import items +from . import locations +from . import creatures +from . import options +from .items import item_table, group_items, items_by_type, ItemType +from .rules import set_rules logger = logging.getLogger("Subnautica") @@ -27,8 +27,8 @@ class SubnaticaWeb(WebWorld): )] -all_locations = {data["name"]: loc_id for loc_id, data in Locations.location_table.items()} -all_locations.update(Creatures.creature_locations) +all_locations = {data["name"]: loc_id for loc_id, data in locations.location_table.items()} +all_locations.update(creatures.creature_locations) class SubnauticaWorld(World): @@ -40,9 +40,9 @@ class SubnauticaWorld(World): game = "Subnautica" web = SubnaticaWeb() - item_name_to_id = {data.name: item_id for item_id, data in Items.item_table.items()} + item_name_to_id = {data.name: item_id for item_id, data in items.item_table.items()} location_name_to_id = all_locations - option_definitions = Options.options + option_definitions = options.option_definitions data_version = 10 required_client_version = (0, 4, 1) @@ -50,37 +50,37 @@ class SubnauticaWorld(World): creatures_to_scan: List[str] def generate_early(self) -> None: - if self.multiworld.early_seaglide[self.player]: + if self.options.early_seaglide: self.multiworld.local_early_items[self.player]["Seaglide Fragment"] = 2 - scan_option: Options.AggressiveScanLogic = self.multiworld.creature_scan_logic[self.player] + scan_option: options.AggressiveScanLogic = self.options.creature_scan_logic creature_pool = scan_option.get_pool() - self.multiworld.creature_scans[self.player].value = min( + self.options.creature_scans.value = min( len(creature_pool), - self.multiworld.creature_scans[self.player].value + self.options.creature_scans.value ) - self.creatures_to_scan = self.multiworld.random.sample( - creature_pool, self.multiworld.creature_scans[self.player].value) + self.creatures_to_scan = self.random.sample( + creature_pool, self.options.creature_scans.value) def create_regions(self): self.multiworld.regions += [ self.create_region("Menu", None, ["Lifepod 5"]), self.create_region("Planet 4546B", - Locations.events + - [location["name"] for location in Locations.location_table.values()] + - [creature+Creatures.suffix for creature in self.creatures_to_scan]) + locations.events + + [location["name"] for location in locations.location_table.values()] + + [creature + creatures.suffix for creature in self.creatures_to_scan]) ] # Link regions self.multiworld.get_entrance("Lifepod 5", self.player).connect(self.multiworld.get_region("Planet 4546B", self.player)) - for event in Locations.events: + for event in locations.events: self.multiworld.get_location(event, self.player).place_locked_item( SubnauticaItem(event, ItemClassification.progression, None, player=self.player)) # make the goal event the victory "item" - self.multiworld.get_location(self.multiworld.goal[self.player].get_event_name(), self.player).item.name = "Victory" + self.multiworld.get_location(self.options.goal.get_event_name(), self.player).item.name = "Victory" # refer to Rules.py set_rules = set_rules @@ -88,7 +88,7 @@ class SubnauticaWorld(World): def create_items(self): # Generate item pool pool: List[SubnauticaItem] = [] - extras = self.multiworld.creature_scans[self.player].value + extras = self.options.creature_scans.value grouped = set(itertools.chain.from_iterable(group_items.values())) @@ -139,17 +139,15 @@ class SubnauticaWorld(World): self.multiworld.itempool += pool def fill_slot_data(self) -> Dict[str, Any]: - goal: Options.Goal = self.multiworld.goal[self.player] - swim_rule: Options.SwimRule = self.multiworld.swim_rule[self.player] vanilla_tech: List[str] = [] slot_data: Dict[str, Any] = { - "goal": goal.current_key, - "swim_rule": swim_rule.current_key, + "goal": self.options.goal.current_key, + "swim_rule": self.options.swim_rule.current_key, "vanilla_tech": vanilla_tech, "creatures_to_scan": self.creatures_to_scan, - "death_link": self.multiworld.death_link[self.player].value, - "free_samples": self.multiworld.free_samples[self.player].value, + "death_link": self.options.death_link.value, + "free_samples": self.options.free_samples.value, } return slot_data @@ -161,10 +159,10 @@ class SubnauticaWorld(World): item_table[item_id].classification, item_id, player=self.player) - def create_region(self, name: str, locations=None, exits=None): + def create_region(self, name: str, region_locations=None, exits=None): ret = Region(name, self.player, self.multiworld) - if locations: - for location in locations: + if region_locations: + for location in region_locations: loc_id = self.location_name_to_id.get(location, None) location = SubnauticaLocation(self.player, location, loc_id, ret) ret.locations.append(location) diff --git a/worlds/subnautica/Creatures.py b/worlds/subnautica/creatures.py similarity index 100% rename from worlds/subnautica/Creatures.py rename to worlds/subnautica/creatures.py diff --git a/worlds/subnautica/Exports.py b/worlds/subnautica/exports.py similarity index 95% rename from worlds/subnautica/Exports.py rename to worlds/subnautica/exports.py index 7a69cffc8d..911c6a6f69 100644 --- a/worlds/subnautica/Exports.py +++ b/worlds/subnautica/exports.py @@ -12,8 +12,8 @@ if __name__ == "__main__": os.chdir(new_home) sys.path.append(new_home) - from worlds.subnautica.Locations import Vector, location_table - from worlds.subnautica.Items import item_table, group_items, items_by_type + from worlds.subnautica.locations import Vector, location_table + from worlds.subnautica.items import item_table, group_items, items_by_type from NetUtils import encode export_folder = os.path.join(new_home, "Subnautica Export") diff --git a/worlds/subnautica/Items.py b/worlds/subnautica/items.py similarity index 100% rename from worlds/subnautica/Items.py rename to worlds/subnautica/items.py diff --git a/worlds/subnautica/Locations.py b/worlds/subnautica/locations.py similarity index 100% rename from worlds/subnautica/Locations.py rename to worlds/subnautica/locations.py diff --git a/worlds/subnautica/Options.py b/worlds/subnautica/options.py similarity index 98% rename from worlds/subnautica/Options.py rename to worlds/subnautica/options.py index 582e93eb0e..d8d727a9e1 100644 --- a/worlds/subnautica/Options.py +++ b/worlds/subnautica/options.py @@ -1,7 +1,7 @@ import typing from Options import Choice, Range, DeathLink, Toggle, DefaultOnToggle, StartInventoryPool -from .Creatures import all_creatures, Definitions +from .creatures import all_creatures, Definitions class SwimRule(Choice): @@ -103,7 +103,7 @@ class SubnauticaDeathLink(DeathLink): Note: can be toggled via in-game console command "deathlink".""" -options = { +option_definitions = { "swim_rule": SwimRule, "early_seaglide": EarlySeaglide, "free_samples": FreeSamples, diff --git a/worlds/subnautica/Rules.py b/worlds/subnautica/rules.py similarity index 92% rename from worlds/subnautica/Rules.py rename to worlds/subnautica/rules.py index 793c85be41..3b6c5cd4dd 100644 --- a/worlds/subnautica/Rules.py +++ b/worlds/subnautica/rules.py @@ -1,9 +1,9 @@ from typing import TYPE_CHECKING, Dict, Callable, Optional from worlds.generic.Rules import set_rule, add_rule -from .Locations import location_table, LocationDict -from .Creatures import all_creatures, aggressive, suffix, hatchable, containment -from .Options import AggressiveScanLogic, SwimRule +from .locations import location_table, LocationDict +from .creatures import all_creatures, aggressive, suffix, hatchable, containment +from .options import AggressiveScanLogic, SwimRule import math if TYPE_CHECKING: @@ -290,16 +290,16 @@ aggression_rules: Dict[int, Callable[["CollectionState", int], bool]] = { def set_rules(subnautica_world: "SubnauticaWorld"): player = subnautica_world.player - world = subnautica_world.multiworld + multiworld = subnautica_world.multiworld for loc in location_table.values(): - set_location_rule(world, player, loc) + set_location_rule(multiworld, player, loc) if subnautica_world.creatures_to_scan: - option = world.creature_scan_logic[player] + option = multiworld.creature_scan_logic[player] for creature_name in subnautica_world.creatures_to_scan: - location = set_creature_rule(world, player, creature_name) + location = set_creature_rule(multiworld, player, creature_name) if creature_name in containment: # there is no other way, hard-required containment add_rule(location, lambda state: has_containment(state, player)) elif creature_name in aggressive: @@ -309,7 +309,7 @@ def set_rules(subnautica_world: "SubnauticaWorld"): lambda state, loc_rule=get_aggression_rule(option, creature_name): loc_rule(state, player)) # Victory locations - set_rule(world.get_location("Neptune Launch", player), + set_rule(multiworld.get_location("Neptune Launch", player), lambda state: get_max_depth(state, player) >= 1444 and has_mobile_vehicle_bay(state, player) and @@ -322,13 +322,14 @@ def set_rules(subnautica_world: "SubnauticaWorld"): state.has("Ion Battery", player) and has_cyclops_shield(state, player)) - set_rule(world.get_location("Disable Quarantine", player), lambda state: - get_max_depth(state, player) >= 1444) + set_rule(multiworld.get_location("Disable Quarantine", player), + lambda state: get_max_depth(state, player) >= 1444) - set_rule(world.get_location("Full Infection", player), lambda state: - get_max_depth(state, player) >= 900) + set_rule(multiworld.get_location("Full Infection", player), + lambda state: get_max_depth(state, player) >= 900) - room = world.get_location("Aurora Drive Room - Upgrade Console", player) - set_rule(world.get_location("Repair Aurora Drive", player), lambda state: room.can_reach(state)) + room = multiworld.get_location("Aurora Drive Room - Upgrade Console", player) + set_rule(multiworld.get_location("Repair Aurora Drive", player), + lambda state: room.can_reach(state)) - world.completion_condition[player] = lambda state: state.has("Victory", player) + multiworld.completion_condition[player] = lambda state: state.has("Victory", player) diff --git a/worlds/subnautica/test/__init__.py b/worlds/subnautica/test/__init__.py index d938830737..69f0b6c7cd 100644 --- a/worlds/subnautica/test/__init__.py +++ b/worlds/subnautica/test/__init__.py @@ -15,11 +15,11 @@ class SubnauticaTest(unittest.TestCase): self.assertGreater(self.scancutoff, id) def testGroupAssociation(self): - from worlds.subnautica import Items - for item_id, item_data in Items.item_table.items(): - if item_data.type == Items.ItemType.group: + from worlds.subnautica import items + for item_id, item_data in items.item_table.items(): + if item_data.type == items.ItemType.group: with self.subTest(item=item_data.name): - self.assertIn(item_id, Items.group_items) - for item_id in Items.group_items: + self.assertIn(item_id, items.group_items) + for item_id in items.group_items: with self.subTest(item_id=item_id): - self.assertEqual(Items.item_table[item_id].type, Items.ItemType.group) + self.assertEqual(items.item_table[item_id].type, items.ItemType.group) From 63c7f1deae36884e44ed586cc919d987448c084e Mon Sep 17 00:00:00 2001 From: el-u <109771707+el-u@users.noreply.github.com> Date: Sun, 15 Oct 2023 04:53:28 +0200 Subject: [PATCH 115/144] lufia2ac: switch to new options system (#2289) --- worlds/lufia2ac/Options.py | 5 +++-- worlds/lufia2ac/__init__.py | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/worlds/lufia2ac/Options.py b/worlds/lufia2ac/Options.py index c3bbadc9ba..3f1c58f9d0 100644 --- a/worlds/lufia2ac/Options.py +++ b/worlds/lufia2ac/Options.py @@ -5,7 +5,8 @@ from dataclasses import dataclass from itertools import accumulate, chain, combinations from typing import Any, cast, Dict, Iterator, List, Mapping, Optional, Set, Tuple, Type, TYPE_CHECKING, Union -from Options import AssembleOptions, Choice, DeathLink, ItemDict, Range, SpecialRange, TextChoice, Toggle +from Options import AssembleOptions, Choice, DeathLink, ItemDict, PerGameCommonOptions, Range, SpecialRange, \ + TextChoice, Toggle from .Enemies import enemy_name_to_sprite if TYPE_CHECKING: @@ -697,7 +698,7 @@ class ShufflePartyMembers(Toggle): @dataclass -class L2ACOptions: +class L2ACOptions(PerGameCommonOptions): blue_chest_chance: BlueChestChance blue_chest_count: BlueChestCount boss: Boss diff --git a/worlds/lufia2ac/__init__.py b/worlds/lufia2ac/__init__.py index f14048a2c4..fad7109a4a 100644 --- a/worlds/lufia2ac/__init__.py +++ b/worlds/lufia2ac/__init__.py @@ -2,11 +2,11 @@ import base64 import itertools import os from enum import IntFlag -from typing import Any, ClassVar, Dict, get_type_hints, Iterator, List, Set, Tuple +from typing import Any, ClassVar, Dict, Iterator, List, Set, Tuple, Type import settings from BaseClasses import Item, ItemClassification, Location, MultiWorld, Region, Tutorial -from Options import AssembleOptions +from Options import PerGameCommonOptions from Utils import __version__ from worlds.AutoWorld import WebWorld, World from worlds.generic.Rules import add_rule, set_rule @@ -54,7 +54,8 @@ class L2ACWorld(World): game: ClassVar[str] = "Lufia II Ancient Cave" web: ClassVar[WebWorld] = L2ACWeb() - option_definitions: ClassVar[Dict[str, AssembleOptions]] = get_type_hints(L2ACOptions) + options_dataclass: ClassVar[Type[PerGameCommonOptions]] = L2ACOptions + options: L2ACOptions settings: ClassVar[L2ACSettings] item_name_to_id: ClassVar[Dict[str, int]] = l2ac_item_name_to_id location_name_to_id: ClassVar[Dict[str, int]] = l2ac_location_name_to_id @@ -87,7 +88,7 @@ class L2ACWorld(World): bytearray(f"L2AC{__version__.replace('.', '')[:3]}_{self.player}_{self.multiworld.seed}", "utf8")[:21] self.rom_name.extend([0] * (21 - len(self.rom_name))) - self.o = L2ACOptions(**{opt: getattr(self.multiworld, opt)[self.player] for opt in self.option_definitions}) + self.o = self.options if self.o.blue_chest_count < self.o.custom_item_pool.count: raise ValueError(f"Number of items in custom_item_pool ({self.o.custom_item_pool.count}) is " From e27aeac2e5f9a1f70baeabe6ef0d0508db6d31a8 Mon Sep 17 00:00:00 2001 From: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> Date: Mon, 16 Oct 2023 20:59:07 -0400 Subject: [PATCH 116/144] HK: Update Setup Guide to use/mention Lumafly (#2308) --- worlds/hk/docs/setup_en.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/worlds/hk/docs/setup_en.md b/worlds/hk/docs/setup_en.md index adf975ff51..fef0f051fe 100644 --- a/worlds/hk/docs/setup_en.md +++ b/worlds/hk/docs/setup_en.md @@ -1,27 +1,27 @@ # Hollow Knight for Archipelago Setup Guide ## Required Software -* Download and unzip the Scarab+ Mod Manager from the [Scarab+ website](https://themulhima.github.io/Scarab/). +* Download and unzip the Lumafly Mod Manager from the [Lumafly website](https://themulhima.github.io/Lumafly/). * A legal copy of Hollow Knight. -## Installing the Archipelago Mod using Scarab+ -1. Launch Scarab+ and ensure it locates your Hollow Knight installation directory. +## Installing the Archipelago Mod using Lumafly +1. Launch Lumafly and ensure it locates your Hollow Knight installation directory. 2. Click the "Install" button near the "Archipelago" mod entry. * If desired, also install "Archipelago Map Mod" to use as an in-game tracker. 3. Launch the game, you're all set! -### What to do if Scarab+ fails to find your XBox Game Pass installation directory +### What to do if Lumafly fails to find your XBox Game Pass installation directory 1. Enter the XBox app and move your mouse over "Hollow Knight" on the left sidebar. 2. Click the three points then click "Manage". 3. Go to the "Files" tab and select "Browse...". 4. Click "Hollow Knight", then "Content", then click the path bar and copy it. -5. Run Scarab+ as an administrator and, when it asks you for the path, paste what you copied in step 4. +5. Run Lumafly as an administrator and, when it asks you for the path, paste what you copied in step 4. #### Alternative Method: 1. Click on your profile then "Settings". 2. Go to the "General" tab and select "CHANGE FOLDER". 3. Look for a folder where you want to install the game (preferably inside a folder on your desktop) and copy the path. -4. Run Scarab+ as an administrator and, when it asks you for the path, paste what you copied in step 3. +4. Run Lumafly as an administrator and, when it asks you for the path, paste what you copied in step 3. Note: The path folder needs to have the "Hollow Knight_Data" folder inside. From 13b68ecb154fb09e3cf41e3c3b81a3a1d1423d92 Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Tue, 17 Oct 2023 01:20:34 -0400 Subject: [PATCH 117/144] =?UTF-8?q?Pok=C3=A9mon=20R/B:=20Door=20Shuffle=20?= =?UTF-8?q?fixes=20(#2314)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Door shuffle fixes * Add Rt 23's Victory Road exit door to list of unreachable outdoor entrances --- worlds/pokemon_rb/locations.py | 2 +- worlds/pokemon_rb/regions.py | 27 ++++++++++++++++----------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/worlds/pokemon_rb/locations.py b/worlds/pokemon_rb/locations.py index ec6375859b..4f1b55a00d 100644 --- a/worlds/pokemon_rb/locations.py +++ b/worlds/pokemon_rb/locations.py @@ -795,7 +795,7 @@ location_data = [ LocationData("Pewter Gym", "Defeat Brock", "Defeat Brock", event=True), LocationData("Cerulean Gym", "Defeat Misty", "Defeat Misty", event=True), LocationData("Vermilion Gym", "Defeat Lt. Surge", "Defeat Lt. Surge", event=True), - LocationData("Celadon Gym", "Defeat Erika", "Defeat Erika", event=True), + LocationData("Celadon Gym-C", "Defeat Erika", "Defeat Erika", event=True), LocationData("Fuchsia Gym", "Defeat Koga", "Defeat Koga", event=True), LocationData("Cinnabar Gym", "Defeat Blaine", "Defeat Blaine", event=True), LocationData("Saffron Gym-C", "Defeat Sabrina", "Defeat Sabrina", event=True), diff --git a/worlds/pokemon_rb/regions.py b/worlds/pokemon_rb/regions.py index cc788dd2ba..431b23f49a 100644 --- a/worlds/pokemon_rb/regions.py +++ b/worlds/pokemon_rb/regions.py @@ -1456,7 +1456,9 @@ mansion_stair_destinations = [ unreachable_outdoor_entrances = [ "Route 4-C to Mt Moon B1F-NE", "Fuchsia City-Good Rod House Backyard to Fuchsia Good Rod House", - "Cerulean City-Badge House Backyard to Cerulean Badge House" + "Cerulean City-Badge House Backyard to Cerulean Badge House", + # TODO: This doesn't need to be forced if fly location is Pokemon League? + "Route 23-N to Victory Road 2F-E" ] @@ -2220,7 +2222,7 @@ def create_regions(self): "Cinnabar Gym - Blaine Prize", "Viridian Gym - Giovanni Prize"]: badge_locs.append(multiworld.get_location(loc, player)) multiworld.random.shuffle(badges) - while badges[3].name == "Cascade Badge" and multiworld.badges_needed_for_hm_moves[player] == "on": + while badges[3].name == "Cascade Badge" and multiworld.badges_needed_for_hm_moves[player]: multiworld.random.shuffle(badges) for badge, loc in zip(badges, badge_locs): loc.place_locked_item(badge) @@ -2266,10 +2268,10 @@ def create_regions(self): ] def adds_reachable_entrances(entrances_copy, item): - state.collect(item, False) + state_copy = state.copy() + state_copy.collect(item, False) ret = len([entrance for entrance in entrances_copy if entrance in reachable_entrances or - entrance.parent_region.can_reach(state)]) > len(reachable_entrances) - state.remove(item) + entrance.parent_region.can_reach(state_copy)]) > len(reachable_entrances) return ret def dead_end(entrances_copy, e): @@ -2304,9 +2306,16 @@ def create_regions(self): starting_entrances = len(entrances) dc_connected = [] event_locations = self.multiworld.get_filled_locations(player) + rock_tunnel_entrances = [entrance for entrance in entrances if "Rock Tunnel" in entrance.name] + entrances = [entrance for entrance in entrances if entrance not in rock_tunnel_entrances] while entrances: state.update_reachable_regions(player) state.sweep_for_events(locations=event_locations) + + if rock_tunnel_entrances and logic.rock_tunnel(state, player): + entrances += rock_tunnel_entrances + rock_tunnel_entrances = None + reachable_entrances = [entrance for entrance in entrances if entrance in reachable_entrances or entrance.parent_region.can_reach(state)] assert reachable_entrances, \ @@ -2328,12 +2337,8 @@ def create_regions(self): # entrances list is empty while it's being sorted, must pass a copy to iterate through entrances_copy = entrances.copy() if multiworld.door_shuffle[player] == "decoupled": - if len(reachable_entrances) <= 8 and not logic.rock_tunnel(state, player): - entrances.sort(key=lambda e: 1 if "Rock Tunnel" in e.name else 2 if e.connected_region is not - None else 3 if e not in reachable_entrances else 0) - else: - entrances.sort(key=lambda e: 1 if e.connected_region is not None else 2 if e not in - reachable_entrances else 0) + entrances.sort(key=lambda e: 1 if e.connected_region is not None else 2 if e not in + reachable_entrances else 0) assert entrances[0].connected_region is None,\ "Ran out of valid reachable entrances in Pokemon Red and Blue door shuffle" elif len(reachable_entrances) > (1 if multiworld.door_shuffle[player] == "insanity" else 8) and len( From 11ebc523a9e96838206bde95e2b15abb3192d4e2 Mon Sep 17 00:00:00 2001 From: Trevor L <80716066+TRPG0@users.noreply.github.com> Date: Wed, 18 Oct 2023 13:50:57 -0600 Subject: [PATCH 118/144] Hylics 2: Various fixes and APWorld support (#2324) - Fix generation failing with certain gesture shuffle options - Fixed passing ItemDict to multidata instead of item code - Don't allow CHARGE UP to be placed at Foglast: TV - APWorld support by removing LogicMixin from Rules.py --- setup.py | 1 - worlds/hylics2/Rules.py | 550 +++++++++++++++++++++++-------------- worlds/hylics2/__init__.py | 12 +- 3 files changed, 349 insertions(+), 214 deletions(-) diff --git a/setup.py b/setup.py index 6d4d947dbd..cea60dab83 100644 --- a/setup.py +++ b/setup.py @@ -71,7 +71,6 @@ non_apworlds: set = { "Clique", "DLCQuest", "Final Fantasy", - "Hylics 2", "Kingdom Hearts 2", "Lufia II Ancient Cave", "Meritous", diff --git a/worlds/hylics2/Rules.py b/worlds/hylics2/Rules.py index 12c22e01cd..6c55c8745b 100644 --- a/worlds/hylics2/Rules.py +++ b/worlds/hylics2/Rules.py @@ -1,91 +1,128 @@ from worlds.generic.Rules import add_rule -from ..AutoWorld import LogicMixin +from BaseClasses import CollectionState -class Hylics2Logic(LogicMixin): +def air_dash(state: CollectionState, player: int) -> bool: + return state.has("PNEUMATOPHORE", player) - def _hylics2_can_air_dash(self, player): - return self.has("PNEUMATOPHORE", player) - def _hylics2_has_airship(self, player): - return self.has("DOCK KEY", player) +def airship(state: CollectionState, player: int) -> bool: + return state.has("DOCK KEY", player) - def _hylics2_has_jail_key(self, player): - return self.has("JAIL KEY", player) - def _hylics2_has_paddle(self, player): - return self.has("PADDLE", player) +def jail_key(state: CollectionState, player: int) -> bool: + return state.has("JAIL KEY", player) - def _hylics2_has_worm_room_key(self, player): - return self.has("WORM ROOM KEY", player) - def _hylics2_has_bridge_key(self, player): - return self.has("BRIDGE KEY", player) +def paddle(state: CollectionState, player: int) -> bool: + return state.has("PADDLE", player) - def _hylics2_has_upper_chamber_key(self, player): - return self.has("UPPER CHAMBER KEY", player) - def _hylics2_has_vessel_room_key(self, player): - return self.has("VESSEL ROOM KEY", player) +def worm_room_key(state: CollectionState, player: int) -> bool: + return state.has("WORM ROOM KEY", player) - def _hylics2_has_house_key(self, player): - return self.has("HOUSE KEY", player) - def _hylics2_has_cave_key(self, player): - return self.has("CAVE KEY", player) +def bridge_key(state: CollectionState, player: int) -> bool: + return state.has("BRIDGE KEY", player) - def _hylics2_has_skull_bomb(self, player): - return self.has("SKULL BOMB", player) - def _hylics2_has_tower_key(self, player): - return self.has("TOWER KEY", player) +def upper_chamber_key(state: CollectionState, player: int) -> bool: + return state.has("UPPER CHAMBER KEY", player) - def _hylics2_has_deep_key(self, player): - return self.has("DEEP KEY", player) - def _hylics2_has_upper_house_key(self, player): - return self.has("UPPER HOUSE KEY", player) +def vessel_room_key(state: CollectionState, player: int) -> bool: + return state.has("VESSEL ROOM KEY", player) - def _hylics2_has_clicker(self, player): - return self.has("CLICKER", player) - def _hylics2_has_tokens(self, player): - return self.has("SAGE TOKEN", player, 3) +def house_key(state: CollectionState, player: int) -> bool: + return state.has("HOUSE KEY", player) - def _hylics2_has_charge_up(self, player): - return self.has("CHARGE UP", player) - def _hylics2_has_cup(self, player): - return self.has("PAPER CUP", player, 1) +def cave_key(state: CollectionState, player: int) -> bool: + return state.has("CAVE KEY", player) - def _hylics2_has_1_member(self, player): - return self.has("Pongorma", player) or self.has("Dedusmuln", player) or self.has("Somsnosa", player) - def _hylics2_has_2_members(self, player): - return (self.has("Pongorma", player) and self.has("Dedusmuln", player)) or\ - (self.has("Pongorma", player) and self.has("Somsnosa", player)) or\ - (self.has("Dedusmuln", player) and self.has("Somsnosa", player)) +def skull_bomb(state: CollectionState, player: int) -> bool: + return state.has("SKULL BOMB", player) - def _hylics2_has_3_members(self, player): - return self.has("Pongorma", player) and self.has("Dedusmuln", player) and self.has("Somsnosa", player) - def _hylics2_enter_arcade2(self, player): - return self._hylics2_can_air_dash(player) and self._hylics2_has_airship(player) +def tower_key(state: CollectionState, player: int) -> bool: + return state.has("TOWER KEY", player) - def _hylics2_enter_wormpod(self, player): - return self._hylics2_has_airship(player) and self._hylics2_has_worm_room_key(player) and\ - self._hylics2_has_paddle(player) - def _hylics2_enter_sageship(self, player): - return self._hylics2_has_skull_bomb(player) and self._hylics2_has_airship(player) and\ - self._hylics2_has_paddle(player) +def deep_key(state: CollectionState, player: int) -> bool: + return state.has("DEEP KEY", player) - def _hylics2_enter_foglast(self, player): - return self._hylics2_enter_wormpod(player) - def _hylics2_enter_hylemxylem(self, player): - return self._hylics2_can_air_dash(player) and self._hylics2_enter_foglast(player) and\ - self._hylics2_has_bridge_key(player) +def upper_house_key(state: CollectionState, player: int) -> bool: + return state.has("UPPER HOUSE KEY", player) + + +def clicker(state: CollectionState, player: int) -> bool: + return state.has("CLICKER", player) + + +def all_tokens(state: CollectionState, player: int) -> bool: + return state.has("SAGE TOKEN", player, 3) + + +def charge_up(state: CollectionState, player: int) -> bool: + return state.has("CHARGE UP", player) + + +def paper_cup(state: CollectionState, player: int) -> bool: + return state.has("PAPER CUP", player) + + +def party_1(state: CollectionState, player: int) -> bool: + return state.has_any({"Pongorma", "Dedusmuln", "Somsnosa"}, player) + + +def party_2(state: CollectionState, player: int) -> bool: + return ( + state.has_all({"Pongorma", "Dedusmuln"}, player) + or state.has_all({"Pongorma", "Somsnosa"}, player) + or state.has_all({"Dedusmuln", "Somsnosa"}, player) + ) + + +def party_3(state: CollectionState, player: int) -> bool: + return state.has_all({"Pongorma", "Dedusmuln", "Somsnosa"}, player) + + +def enter_arcade2(state: CollectionState, player: int) -> bool: + return ( + air_dash(state, player) + and airship(state, player) + ) + + +def enter_wormpod(state: CollectionState, player: int) -> bool: + return ( + airship(state, player) + and worm_room_key(state, player) + and paddle(state, player) + ) + + +def enter_sageship(state: CollectionState, player: int) -> bool: + return ( + skull_bomb(state, player) + and airship(state, player) + and paddle(state, player) + ) + + +def enter_foglast(state: CollectionState, player: int) -> bool: + return enter_wormpod(state, player) + + +def enter_hylemxylem(state: CollectionState, player: int) -> bool: + return ( + air_dash(state, player) + and enter_foglast(state, player) + and bridge_key(state, player) + ) def set_rules(hylics2world): @@ -94,342 +131,439 @@ def set_rules(hylics2world): # Afterlife add_rule(world.get_location("Afterlife: TV", player), - lambda state: state._hylics2_has_cave_key(player)) + lambda state: cave_key(state, player)) # New Muldul add_rule(world.get_location("New Muldul: Underground Chest", player), - lambda state: state._hylics2_can_air_dash(player)) + lambda state: air_dash(state, player)) add_rule(world.get_location("New Muldul: TV", player), - lambda state: state._hylics2_has_house_key(player)) + lambda state: house_key(state, player)) add_rule(world.get_location("New Muldul: Upper House Chest 1", player), - lambda state: state._hylics2_has_upper_house_key(player)) + lambda state: upper_house_key(state, player)) add_rule(world.get_location("New Muldul: Upper House Chest 2", player), - lambda state: state._hylics2_has_upper_house_key(player)) + lambda state: upper_house_key(state, player)) add_rule(world.get_location("New Muldul: Pot above Vault", player), - lambda state: state._hylics2_can_air_dash(player)) + lambda state: air_dash(state, player)) # New Muldul Vault add_rule(world.get_location("New Muldul: Rescued Blerol 1", player), - lambda state: ((state._hylics2_has_jail_key(player) and state._hylics2_has_paddle(player)) and\ - (state._hylics2_can_air_dash(player) or state._hylics2_has_airship(player))) or\ - state._hylics2_enter_hylemxylem(player)) + lambda state: ( + ( + ( + jail_key(state, player) + and paddle(state, player) + ) + and ( + air_dash(state, player) + or airship(state, player) + ) + ) + or enter_hylemxylem(state, player) + )) add_rule(world.get_location("New Muldul: Rescued Blerol 2", player), - lambda state: ((state._hylics2_has_jail_key(player) and state._hylics2_has_paddle(player)) and\ - (state._hylics2_can_air_dash(player) or state._hylics2_has_airship(player))) or\ - state._hylics2_enter_hylemxylem(player)) + lambda state: ( + ( + ( + jail_key(state, player) + and paddle(state, player) + ) + and ( + air_dash(state, player) + or airship(state, player) + ) + ) + or enter_hylemxylem(state, player) + )) add_rule(world.get_location("New Muldul: Vault Left Chest", player), - lambda state: state._hylics2_enter_hylemxylem(player)) + lambda state: enter_hylemxylem(state, player)) add_rule(world.get_location("New Muldul: Vault Right Chest", player), - lambda state: state._hylics2_enter_hylemxylem(player)) + lambda state: enter_hylemxylem(state, player)) add_rule(world.get_location("New Muldul: Vault Bomb", player), - lambda state: state._hylics2_enter_hylemxylem(player)) + lambda state: enter_hylemxylem(state, player)) # Viewax's Edifice add_rule(world.get_location("Viewax's Edifice: Canopic Jar", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Viewax's Edifice: Cave Sarcophagus", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Viewax's Edifice: Shielded Key", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Viewax's Edifice: Shielded Key", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Viewax's Edifice: Tower Pot", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Viewax's Edifice: Tower Jar", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Viewax's Edifice: Tower Chest", player), - lambda state: state._hylics2_has_paddle(player) and state._hylics2_has_tower_key(player)) + lambda state: ( + paddle(state, player) + and tower_key(state, player) + )) add_rule(world.get_location("Viewax's Edifice: Viewax Pot", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Viewax's Edifice: Defeat Viewax", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Viewax's Edifice: TV", player), - lambda state: state._hylics2_has_paddle(player) and state._hylics2_has_jail_key(player)) + lambda state: ( + paddle(state, player) + and jail_key(state, player) + )) add_rule(world.get_location("Viewax's Edifice: Sage Fridge", player), - lambda state: state._hylics2_can_air_dash(player)) + lambda state: air_dash(state, player)) add_rule(world.get_location("Viewax's Edifice: Sage Item 1", player), - lambda state: state._hylics2_can_air_dash(player)) + lambda state: air_dash(state, player)) add_rule(world.get_location("Viewax's Edifice: Sage Item 2", player), - lambda state: state._hylics2_can_air_dash(player)) + lambda state: air_dash(state, player)) # Arcade 1 add_rule(world.get_location("Arcade 1: Key", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Arcade 1: Coin Dash", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Arcade 1: Burrito Alcove 1", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Arcade 1: Burrito Alcove 2", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Arcade 1: Behind Spikes Banana", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Arcade 1: Pyramid Banana", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Arcade 1: Moving Platforms Muscle Applique", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Arcade 1: Bed Banana", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) # Airship add_rule(world.get_location("Airship: Talk to Somsnosa", player), - lambda state: state._hylics2_has_worm_room_key(player)) + lambda state: worm_room_key(state, player)) # Foglast add_rule(world.get_location("Foglast: Underground Sarcophagus", player), - lambda state: state._hylics2_can_air_dash(player)) + lambda state: air_dash(state, player)) add_rule(world.get_location("Foglast: Shielded Key", player), - lambda state: state._hylics2_can_air_dash(player)) + lambda state: air_dash(state, player)) add_rule(world.get_location("Foglast: TV", player), - lambda state: state._hylics2_can_air_dash(player) and state._hylics2_has_clicker(player)) + lambda state: ( + air_dash(state, player) + and clicker(state, player) + )) add_rule(world.get_location("Foglast: Buy Clicker", player), - lambda state: state._hylics2_can_air_dash(player)) + lambda state: air_dash(state, player)) add_rule(world.get_location("Foglast: Shielded Chest", player), - lambda state: state._hylics2_can_air_dash(player)) + lambda state: air_dash(state, player)) add_rule(world.get_location("Foglast: Cave Fridge", player), - lambda state: state._hylics2_can_air_dash(player)) + lambda state: air_dash(state, player)) add_rule(world.get_location("Foglast: Roof Sarcophagus", player), - lambda state: state._hylics2_can_air_dash(player) and state._hylics2_has_bridge_key(player)) + lambda state: ( + air_dash(state, player) + and bridge_key(state, player) + )) add_rule(world.get_location("Foglast: Under Lair Sarcophagus 1", player), - lambda state: state._hylics2_can_air_dash(player) and state._hylics2_has_bridge_key(player)) + lambda state: ( + air_dash(state, player) + and bridge_key(state, player) + )) add_rule(world.get_location("Foglast: Under Lair Sarcophagus 2", player), - lambda state: state._hylics2_can_air_dash(player) and state._hylics2_has_bridge_key(player)) + lambda state: ( + air_dash(state, player) + and bridge_key(state, player) + )) add_rule(world.get_location("Foglast: Under Lair Sarcophagus 3", player), - lambda state: state._hylics2_can_air_dash(player) and state._hylics2_has_bridge_key(player)) + lambda state: ( + air_dash(state, player) + and bridge_key(state, player) + )) add_rule(world.get_location("Foglast: Sage Sarcophagus", player), - lambda state: state._hylics2_can_air_dash(player) and state._hylics2_has_bridge_key(player)) + lambda state: ( + air_dash(state, player) + and bridge_key(state, player) + )) add_rule(world.get_location("Foglast: Sage Item 1", player), - lambda state: state._hylics2_can_air_dash(player) and state._hylics2_has_bridge_key(player)) + lambda state: ( + air_dash(state, player) + and bridge_key(state, player) + )) add_rule(world.get_location("Foglast: Sage Item 2", player), - lambda state: state._hylics2_can_air_dash(player) and state._hylics2_has_bridge_key(player)) + lambda state: ( + air_dash(state, player) + and bridge_key(state, player) + )) # Drill Castle add_rule(world.get_location("Drill Castle: Island Banana", player), - lambda state: state._hylics2_can_air_dash(player)) + lambda state: air_dash(state, player)) add_rule(world.get_location("Drill Castle: Island Pot", player), - lambda state: state._hylics2_can_air_dash(player)) + lambda state: air_dash(state, player)) add_rule(world.get_location("Drill Castle: Cave Sarcophagus", player), - lambda state: state._hylics2_can_air_dash(player)) + lambda state: air_dash(state, player)) add_rule(world.get_location("Drill Castle: TV", player), - lambda state: state._hylics2_can_air_dash(player)) + lambda state: air_dash(state, player)) # Sage Labyrinth add_rule(world.get_location("Sage Labyrinth: Sage Item 1", player), - lambda state: state._hylics2_has_deep_key(player)) + lambda state: deep_key(state, player)) add_rule(world.get_location("Sage Labyrinth: Sage Item 2", player), - lambda state: state._hylics2_has_deep_key(player)) + lambda state: deep_key(state, player)) add_rule(world.get_location("Sage Labyrinth: Sage Left Arm", player), - lambda state: state._hylics2_has_deep_key(player)) + lambda state: deep_key(state, player)) add_rule(world.get_location("Sage Labyrinth: Sage Right Arm", player), - lambda state: state._hylics2_has_deep_key(player)) + lambda state: deep_key(state, player)) add_rule(world.get_location("Sage Labyrinth: Sage Left Leg", player), - lambda state: state._hylics2_has_deep_key(player)) + lambda state: deep_key(state, player)) add_rule(world.get_location("Sage Labyrinth: Sage Right Leg", player), - lambda state: state._hylics2_has_deep_key(player)) + lambda state: deep_key(state, player)) # Sage Airship add_rule(world.get_location("Sage Airship: TV", player), - lambda state: state._hylics2_has_tokens(player)) + lambda state: all_tokens(state, player)) # Hylemxylem add_rule(world.get_location("Hylemxylem: Upper Chamber Banana", player), - lambda state: state._hylics2_has_upper_chamber_key(player)) + lambda state: upper_chamber_key(state, player)) add_rule(world.get_location("Hylemxylem: Across Upper Reservoir Chest", player), - lambda state: state._hylics2_has_upper_chamber_key(player)) + lambda state: upper_chamber_key(state, player)) add_rule(world.get_location("Hylemxylem: Drained Lower Reservoir Chest", player), - lambda state: state._hylics2_has_upper_chamber_key(player)) + lambda state: upper_chamber_key(state, player)) add_rule(world.get_location("Hylemxylem: Drained Lower Reservoir Burrito 1", player), - lambda state: state._hylics2_has_upper_chamber_key(player)) + lambda state: upper_chamber_key(state, player)) add_rule(world.get_location("Hylemxylem: Drained Lower Reservoir Burrito 2", player), - lambda state: state._hylics2_has_upper_chamber_key(player)) + lambda state: upper_chamber_key(state, player)) add_rule(world.get_location("Hylemxylem: Lower Reservoir Hole Pot 1", player), - lambda state: state._hylics2_has_upper_chamber_key(player)) + lambda state: upper_chamber_key(state, player)) add_rule(world.get_location("Hylemxylem: Lower Reservoir Hole Pot 2", player), - lambda state: state._hylics2_has_upper_chamber_key(player)) + lambda state: upper_chamber_key(state, player)) add_rule(world.get_location("Hylemxylem: Lower Reservoir Hole Pot 3", player), - lambda state: state._hylics2_has_upper_chamber_key(player)) + lambda state: upper_chamber_key(state, player)) add_rule(world.get_location("Hylemxylem: Lower Reservoir Hole Sarcophagus", player), - lambda state: state._hylics2_has_upper_chamber_key(player)) + lambda state: upper_chamber_key(state, player)) add_rule(world.get_location("Hylemxylem: Drained Upper Reservoir Burrito 1", player), - lambda state: state._hylics2_has_upper_chamber_key(player)) + lambda state: upper_chamber_key(state, player)) add_rule(world.get_location("Hylemxylem: Drained Upper Reservoir Burrito 2", player), - lambda state: state._hylics2_has_upper_chamber_key(player)) + lambda state: upper_chamber_key(state, player)) add_rule(world.get_location("Hylemxylem: Drained Upper Reservoir Burrito 3", player), - lambda state: state._hylics2_has_upper_chamber_key(player)) + lambda state: upper_chamber_key(state, player)) add_rule(world.get_location("Hylemxylem: Upper Reservoir Hole Key", player), - lambda state: state._hylics2_has_upper_chamber_key(player)) + lambda state: upper_chamber_key(state, player)) # extra rules if Extra Items in Logic is enabled if world.extra_items_in_logic[player]: for i in world.get_region("Foglast", player).entrances: - add_rule(i, lambda state: state._hylics2_has_charge_up(player)) + add_rule(i, lambda state: charge_up(state, player)) for i in world.get_region("Sage Airship", player).entrances: - add_rule(i, lambda state: state._hylics2_has_charge_up(player) and state._hylics2_has_cup(player) and\ - state._hylics2_has_worm_room_key(player)) + add_rule(i, lambda state: ( + charge_up(state, player) + and paper_cup(state, player) + and worm_room_key(state, player) + )) for i in world.get_region("Hylemxylem", player).entrances: - add_rule(i, lambda state: state._hylics2_has_charge_up(player) and state._hylics2_has_cup(player)) + add_rule(i, lambda state: ( + charge_up(state, player) + and paper_cup(state, player) + )) add_rule(world.get_location("Sage Labyrinth: Motor Hunter Sarcophagus", player), - lambda state: state._hylics2_has_charge_up(player) and state._hylics2_has_cup(player)) + lambda state: ( + charge_up(state, player) + and paper_cup(state, player) + )) # extra rules if Shuffle Party Members is enabled if world.party_shuffle[player]: for i in world.get_region("Arcade Island", player).entrances: - add_rule(i, lambda state: state._hylics2_has_3_members(player)) + add_rule(i, lambda state: party_3(state, player)) for i in world.get_region("Foglast", player).entrances: - add_rule(i, lambda state: state._hylics2_has_3_members(player) or\ - (state._hylics2_has_2_members(player) and state._hylics2_has_jail_key(player))) + add_rule(i, lambda state: ( + party_3(state, player) + or ( + party_2(state, player) + and jail_key(state, player) + ) + )) for i in world.get_region("Sage Airship", player).entrances: - add_rule(i, lambda state: state._hylics2_has_3_members(player)) + add_rule(i, lambda state: party_3(state, player)) for i in world.get_region("Hylemxylem", player).entrances: - add_rule(i, lambda state: state._hylics2_has_3_members(player)) + add_rule(i, lambda state: party_3(state, player)) add_rule(world.get_location("Viewax's Edifice: Defeat Viewax", player), - lambda state: state._hylics2_has_2_members(player)) + lambda state: party_2(state, player)) add_rule(world.get_location("New Muldul: Rescued Blerol 1", player), - lambda state: state._hylics2_has_2_members(player)) + lambda state: party_2(state, player)) add_rule(world.get_location("New Muldul: Rescued Blerol 2", player), - lambda state: state._hylics2_has_2_members(player)) + lambda state: party_2(state, player)) add_rule(world.get_location("New Muldul: Vault Left Chest", player), - lambda state: state._hylics2_has_3_members(player)) + lambda state: party_3(state, player)) add_rule(world.get_location("New Muldul: Vault Right Chest", player), - lambda state: state._hylics2_has_3_members(player)) + lambda state: party_3(state, player)) add_rule(world.get_location("New Muldul: Vault Bomb", player), - lambda state: state._hylics2_has_3_members(player)) + lambda state: party_3(state, player)) add_rule(world.get_location("Juice Ranch: Battle with Somsnosa", player), - lambda state: state._hylics2_has_2_members(player)) + lambda state: party_2(state, player)) add_rule(world.get_location("Juice Ranch: Somsnosa Joins", player), - lambda state: state._hylics2_has_2_members(player)) + lambda state: party_2(state, player)) add_rule(world.get_location("Airship: Talk to Somsnosa", player), - lambda state: state._hylics2_has_3_members(player)) + lambda state: party_3(state, player)) add_rule(world.get_location("Sage Labyrinth: Motor Hunter Sarcophagus", player), - lambda state: state._hylics2_has_3_members(player)) + lambda state: party_3(state, player)) # extra rules if Shuffle Red Medallions is enabled if world.medallion_shuffle[player]: add_rule(world.get_location("New Muldul: Upper House Medallion", player), - lambda state: state._hylics2_has_upper_house_key(player)) + lambda state: upper_house_key(state, player)) add_rule(world.get_location("New Muldul: Vault Rear Left Medallion", player), - lambda state: state._hylics2_enter_foglast(player) and state._hylics2_has_bridge_key(player)) + lambda state: ( + enter_foglast(state, player) + and bridge_key(state, player) + )) add_rule(world.get_location("New Muldul: Vault Rear Right Medallion", player), - lambda state: state._hylics2_enter_foglast(player) and state._hylics2_has_bridge_key(player)) + lambda state: ( + enter_foglast(state, player) + and bridge_key(state, player) + )) add_rule(world.get_location("New Muldul: Vault Center Medallion", player), - lambda state: state._hylics2_enter_foglast(player) and state._hylics2_has_bridge_key(player)) + lambda state: ( + enter_foglast(state, player) + and bridge_key(state, player) + )) add_rule(world.get_location("New Muldul: Vault Front Left Medallion", player), - lambda state: state._hylics2_enter_foglast(player) and state._hylics2_has_bridge_key(player)) + lambda state: ( + enter_foglast(state, player) + and bridge_key(state, player) + )) add_rule(world.get_location("New Muldul: Vault Front Right Medallion", player), - lambda state: state._hylics2_enter_foglast(player) and state._hylics2_has_bridge_key(player)) + lambda state: ( + enter_foglast(state, player) + and bridge_key(state, player) + )) add_rule(world.get_location("Viewax's Edifice: Fort Wall Medallion", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Viewax's Edifice: Jar Medallion", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Viewax's Edifice: Sage Chair Medallion", player), - lambda state: state._hylics2_can_air_dash(player)) + lambda state: air_dash(state, player)) add_rule(world.get_location("Arcade 1: Lonely Medallion", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Arcade 1: Alcove Medallion", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Foglast: Under Lair Medallion", player), - lambda state: state._hylics2_has_bridge_key(player)) + lambda state: bridge_key(state, player)) add_rule(world.get_location("Foglast: Mid-Air Medallion", player), - lambda state: state._hylics2_can_air_dash(player)) + lambda state: air_dash(state, player)) add_rule(world.get_location("Foglast: Top of Tower Medallion", player), - lambda state: state._hylics2_has_paddle(player)) + lambda state: paddle(state, player)) add_rule(world.get_location("Hylemxylem: Lower Reservoir Hole Medallion", player), - lambda state: state._hylics2_has_upper_chamber_key(player)) + lambda state: upper_chamber_key(state, player)) - # extra rules is Shuffle Red Medallions and Party Shuffle are enabled + # extra rules if Shuffle Red Medallions and Party Shuffle are enabled if world.party_shuffle[player] and world.medallion_shuffle[player]: add_rule(world.get_location("New Muldul: Vault Rear Left Medallion", player), - lambda state: state._hylics2_has_3_members(player)) + lambda state: party_3(state, player)) add_rule(world.get_location("New Muldul: Vault Rear Right Medallion", player), - lambda state: state._hylics2_has_3_members(player)) + lambda state: party_3(state, player)) add_rule(world.get_location("New Muldul: Vault Center Medallion", player), - lambda state: state._hylics2_has_3_members(player)) + lambda state: party_3(state, player)) add_rule(world.get_location("New Muldul: Vault Front Left Medallion", player), - lambda state: state._hylics2_has_3_members(player)) + lambda state: party_3(state, player)) add_rule(world.get_location("New Muldul: Vault Front Right Medallion", player), - lambda state: state._hylics2_has_3_members(player)) + lambda state: party_3(state, player)) # entrances for i in world.get_region("Airship", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) for i in world.get_region("Arcade Island", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player) and state._hylics2_can_air_dash(player)) + add_rule(i, lambda state: ( + airship(state, player) + and air_dash(state, player) + )) for i in world.get_region("Worm Pod", player).entrances: - add_rule(i, lambda state: state._hylics2_enter_wormpod(player)) + add_rule(i, lambda state: enter_wormpod(state, player)) for i in world.get_region("Foglast", player).entrances: - add_rule(i, lambda state: state._hylics2_enter_foglast(player)) + add_rule(i, lambda state: enter_foglast(state, player)) for i in world.get_region("Sage Labyrinth", player).entrances: - add_rule(i, lambda state: state._hylics2_has_skull_bomb(player)) + add_rule(i, lambda state: skull_bomb(state, player)) for i in world.get_region("Sage Airship", player).entrances: - add_rule(i, lambda state: state._hylics2_enter_sageship(player)) + add_rule(i, lambda state: enter_sageship(state, player)) for i in world.get_region("Hylemxylem", player).entrances: - add_rule(i, lambda state: state._hylics2_enter_hylemxylem(player)) + add_rule(i, lambda state: enter_hylemxylem(state, player)) # random start logic (default) if ((not world.random_start[player]) or \ (world.random_start[player] and hylics2world.start_location == "Waynehouse")): # entrances for i in world.get_region("Viewax", player).entrances: - add_rule(i, lambda state: state._hylics2_can_air_dash(player) or state._hylics2_has_airship(player)) + add_rule(i, lambda state: ( + air_dash(state, player) + and airship(state, player) + )) for i in world.get_region("TV Island", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) for i in world.get_region("Shield Facility", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) for i in world.get_region("Juice Ranch", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) # random start logic (Viewax's Edifice) elif (world.random_start[player] and hylics2world.start_location == "Viewax's Edifice"): for i in world.get_region("Waynehouse", player).entrances: - add_rule(i, lambda state: state._hylics2_can_air_dash(player) or state._hylics2_has_airship(player)) + add_rule(i, lambda state: ( + air_dash(state, player) + or airship(state, player) + )) for i in world.get_region("New Muldul", player).entrances: - add_rule(i, lambda state: state._hylics2_can_air_dash(player) or state._hylics2_has_airship(player)) + add_rule(i, lambda state: ( + air_dash(state, player) + or airship(state, player) + )) for i in world.get_region("New Muldul Vault", player).entrances: - add_rule(i, lambda state: state._hylics2_can_air_dash(player) or state._hylics2_has_airship(player)) + add_rule(i, lambda state: ( + air_dash(state, player) + or airship(state, player) + )) for i in world.get_region("Drill Castle", player).entrances: - add_rule(i, lambda state: state._hylics2_can_air_dash(player) or state._hylics2_has_airship(player)) + add_rule(i, lambda state: ( + air_dash(state, player) + or airship(state, player) + )) for i in world.get_region("TV Island", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) for i in world.get_region("Shield Facility", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) for i in world.get_region("Juice Ranch", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) for i in world.get_region("Sage Labyrinth", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) # random start logic (TV Island) elif (world.random_start[player] and hylics2world.start_location == "TV Island"): for i in world.get_region("Waynehouse", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) for i in world.get_region("New Muldul", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) for i in world.get_region("New Muldul Vault", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) for i in world.get_region("Drill Castle", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) for i in world.get_region("Viewax", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) for i in world.get_region("Shield Facility", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) for i in world.get_region("Juice Ranch", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) for i in world.get_region("Sage Labyrinth", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) # random start logic (Shield Facility) elif (world.random_start[player] and hylics2world.start_location == "Shield Facility"): for i in world.get_region("Waynehouse", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) for i in world.get_region("New Muldul", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) for i in world.get_region("New Muldul Vault", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) for i in world.get_region("Drill Castle", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) for i in world.get_region("Viewax", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) for i in world.get_region("TV Island", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) for i in world.get_region("Sage Labyrinth", player).entrances: - add_rule(i, lambda state: state._hylics2_has_airship(player)) + add_rule(i, lambda state: airship(state, player)) \ No newline at end of file diff --git a/worlds/hylics2/__init__.py b/worlds/hylics2/__init__.py index f721fb4749..19d901bf5a 100644 --- a/worlds/hylics2/__init__.py +++ b/worlds/hylics2/__init__.py @@ -130,11 +130,11 @@ class Hylics2World(World): tvs = list(Locations.tv_location_table.items()) # if Extra Items in Logic is enabled place CHARGE UP first and make sure it doesn't get - # placed at Sage Airship: TV + # placed at Sage Airship: TV or Foglast: TV if self.multiworld.extra_items_in_logic[self.player]: tv = self.multiworld.random.choice(tvs) gest = gestures.index((200681, Items.gesture_item_table[200681])) - while tv[1]["name"] == "Sage Airship: TV": + while tv[1]["name"] == "Sage Airship: TV" or tv[1]["name"] == "Foglast: TV": tv = self.multiworld.random.choice(tvs) self.multiworld.get_location(tv[1]["name"], self.player)\ .place_locked_item(self.add_item(gestures[gest][1]["name"], gestures[gest][1]["classification"], @@ -146,7 +146,7 @@ class Hylics2World(World): gest = self.multiworld.random.choice(gestures) tv = self.multiworld.random.choice(tvs) self.multiworld.get_location(tv[1]["name"], self.player)\ - .place_locked_item(self.add_item(gest[1]["name"], gest[1]["classification"], gest[1])) + .place_locked_item(self.add_item(gest[1]["name"], gest[1]["classification"], gest[0])) gestures.remove(gest) tvs.remove(tv) @@ -232,8 +232,10 @@ class Hylics2World(World): # create location for beating the game and place Victory event there loc = Location(self.player, "Defeat Gibby", None, self.multiworld.get_region("Hylemxylem", self.player)) loc.place_locked_item(self.create_event("Victory")) - set_rule(loc, lambda state: state._hylics2_has_upper_chamber_key(self.player) - and state._hylics2_has_vessel_room_key(self.player)) + set_rule(loc, lambda state: ( + state.has("UPPER CHAMBER KEY", self.player) + and state.has("VESSEL ROOM KEY", self.player) + )) self.multiworld.get_region("Hylemxylem", self.player).locations.append(loc) self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player) From 5ca1ababfdd94a6e309d97f658065aeb86b7a463 Mon Sep 17 00:00:00 2001 From: agilbert1412 Date: Wed, 18 Oct 2023 15:53:12 -0400 Subject: [PATCH 119/144] DLC Quest: Fix code structure, typos, poor code quality (#2066) "Added a bunch of tests to make sure I don't break anything during refactoring Huge cleanup in the Regions file, extract methods, remove code duplicate, fix typos, fix variable naming conventions, etc. Small cleanup in other places, minor stuff just what was needed for Regions" --- worlds/dlcquest/Regions.py | 415 +++++++------------- worlds/dlcquest/test/TestItemShuffle.py | 130 ++++++ worlds/dlcquest/test/TestOptionsLong.py | 87 ++++ worlds/dlcquest/test/__init__.py | 53 +++ worlds/dlcquest/test/checks/__init__.py | 0 worlds/dlcquest/test/checks/world_checks.py | 42 ++ worlds/dlcquest/test/option_names.py | 5 + 7 files changed, 455 insertions(+), 277 deletions(-) create mode 100644 worlds/dlcquest/test/TestItemShuffle.py create mode 100644 worlds/dlcquest/test/TestOptionsLong.py create mode 100644 worlds/dlcquest/test/__init__.py create mode 100644 worlds/dlcquest/test/checks/__init__.py create mode 100644 worlds/dlcquest/test/checks/world_checks.py create mode 100644 worlds/dlcquest/test/option_names.py diff --git a/worlds/dlcquest/Regions.py b/worlds/dlcquest/Regions.py index dfb5f6c021..402ac722a0 100644 --- a/worlds/dlcquest/Regions.py +++ b/worlds/dlcquest/Regions.py @@ -1,4 +1,5 @@ import math +from typing import List from BaseClasses import Entrance, MultiWorld, Region from . import Options @@ -9,318 +10,178 @@ DLCQuestRegion = ["Movement Pack", "Behind Tree", "Psychological Warfare", "Doub "Double Jump Behind the Tree", "The Forest", "Final Room"] -def add_coin_freemium(region: Region, Coin: int, player: int): - number_coin = f"{Coin} coins freemium" - location_coin = f"{region.name} coins freemium" +def add_coin_lfod(region: Region, coin: int, player: int): + add_coin(region, coin, player, " coins freemium") + + +def add_coin_dlcquest(region: Region, coin: int, player: int): + add_coin(region, coin, player, " coins") + + +def add_coin(region: Region, coin: int, player: int, suffix: str): + number_coin = f"{coin}{suffix}" + location_coin = f"{region.name}{suffix}" location = DLCQuestLocation(player, location_coin, None, region) region.locations.append(location) location.place_locked_item(create_event(player, number_coin)) -def add_coin_dlcquest(region: Region, Coin: int, player: int): - number_coin = f"{Coin} coins" - location_coin = f"{region.name} coins" - location = DLCQuestLocation(player, location_coin, None, region) - region.locations.append(location) - location.place_locked_item(create_event(player, number_coin)) +def create_regions(multiworld: MultiWorld, player: int, world_options: Options.DLCQuestOptions): + region_menu = Region("Menu", player, multiworld) + has_campaign_basic = world_options.campaign == Options.Campaign.option_basic or world_options.campaign == Options.Campaign.option_both + has_campaign_lfod = world_options.campaign == Options.Campaign.option_live_freemium_or_die or world_options.campaign == Options.Campaign.option_both + has_coinsanity = world_options.coinsanity == Options.CoinSanity.option_coin + coin_bundle_size = world_options.coinbundlequantity.value + has_item_shuffle = world_options.item_shuffle == Options.ItemShuffle.option_shuffled + + multiworld.regions.append(region_menu) + + create_regions_basic_campaign(has_campaign_basic, region_menu, has_item_shuffle, has_coinsanity, coin_bundle_size, player, multiworld) + + create_regions_lfod_campaign(coin_bundle_size, has_campaign_lfod, has_coinsanity, has_item_shuffle, multiworld, player, region_menu) -def create_regions(world: MultiWorld, player: int, World_Options: Options.DLCQuestOptions): - Regmenu = Region("Menu", player, world) - if (World_Options.campaign == Options.Campaign.option_basic or World_Options.campaign - == Options.Campaign.option_both): - Regmenu.exits += [Entrance(player, "DLC Quest Basic", Regmenu)] - if (World_Options.campaign == Options.Campaign.option_live_freemium_or_die or World_Options.campaign - == Options.Campaign.option_both): - Regmenu.exits += [Entrance(player, "Live Freemium or Die", Regmenu)] - world.regions.append(Regmenu) +def create_regions_basic_campaign(has_campaign_basic: bool, region_menu: Region, has_item_shuffle: bool, has_coinsanity: bool, + coin_bundle_size: int, player: int, world: MultiWorld): + if not has_campaign_basic: + return - if (World_Options.campaign == Options.Campaign.option_basic or World_Options.campaign - == Options.Campaign.option_both): + region_menu.exits += [Entrance(player, "DLC Quest Basic", region_menu)] + locations_move_right = ["Movement Pack", "Animation Pack", "Audio Pack", "Pause Menu Pack"] + region_move_right = create_region_and_locations_basic("Move Right", locations_move_right, ["Moving"], player, world, 4) + create_coinsanity_locations_dlc_quest(has_coinsanity, coin_bundle_size, player, region_move_right) + locations_movement_pack = ["Time is Money Pack", "Psychological Warfare Pack", "Armor for your Horse Pack", "Shepherd Sheep"] + locations_movement_pack += conditional_location(has_item_shuffle, "Sword") + create_region_and_locations_basic("Movement Pack", locations_movement_pack, ["Tree", "Cloud"], player, world, 46) + locations_behind_tree = ["Double Jump Pack", "Map Pack", "Between Trees Sheep", "Hole in the Wall Sheep"] + conditional_location(has_item_shuffle, "Gun") + create_region_and_locations_basic("Behind Tree", locations_behind_tree, ["Behind Tree Double Jump", "Forest Entrance"], player, world, 60) + create_region_and_locations_basic("Psychological Warfare", ["West Cave Sheep"], ["Cloud Double Jump"], player, world, 100) + locations_double_jump_left = ["Pet Pack", "Top Hat Pack", "North West Alcove Sheep"] + create_region_and_locations_basic("Double Jump Total Left", locations_double_jump_left, ["Cave Tree", "Cave Roof"], player, world, 50) + create_region_and_locations_basic("Double Jump Total Left Cave", ["Top Hat Sheep"], [], player, world, 9) + create_region_and_locations_basic("Double Jump Total Left Roof", ["North West Ceiling Sheep"], [], player, world, 10) + locations_double_jump_left_ceiling = ["Sexy Outfits Pack", "Double Jump Alcove Sheep", "Sexy Outfits Sheep"] + create_region_and_locations_basic("Double Jump Behind Tree", locations_double_jump_left_ceiling, ["True Double Jump"], player, world, 89) + create_region_and_locations_basic("True Double Jump Behind Tree", ["Double Jump Floating Sheep", "Cutscene Sheep"], [], player, world, 7) + create_region_and_locations_basic("The Forest", ["Gun Pack", "Night Map Pack"], ["Behind Ogre", "Forest Double Jump"], player, world, 171) + create_region_and_locations_basic("The Forest with double Jump", ["The Zombie Pack", "Forest Low Sheep"], ["Forest True Double Jump"], player, world, 76) + create_region_and_locations_basic("The Forest with double Jump Part 2", ["Forest High Sheep"], [], player, world, 203) + region_final_boss_room = create_region_and_locations_basic("The Final Boss Room", ["Finish the Fight Pack"], [], player, world) - Regmoveright = Region("Move Right", player, world, "Start of the basic game") - Locmoveright_name = ["Movement Pack", "Animation Pack", "Audio Pack", "Pause Menu Pack"] - Regmoveright.exits = [Entrance(player, "Moving", Regmoveright)] - Regmoveright.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regmoveright) for - loc_name in Locmoveright_name] - add_coin_dlcquest(Regmoveright, 4, player) - if World_Options.coinsanity == Options.CoinSanity.option_coin: - coin_bundle_needed = math.floor(825 / World_Options.coinbundlequantity) - for i in range(coin_bundle_needed): - item_coin = f"DLC Quest: {World_Options.coinbundlequantity * (i + 1)} Coin" - Regmoveright.locations += [ - DLCQuestLocation(player, item_coin, location_table[item_coin], Regmoveright)] - if 825 % World_Options.coinbundlequantity != 0: - Regmoveright.locations += [ - DLCQuestLocation(player, "DLC Quest: 825 Coin", location_table["DLC Quest: 825 Coin"], - Regmoveright)] - world.regions.append(Regmoveright) + create_victory_event(region_final_boss_room, "Winning Basic", "Victory Basic", player) - Regmovpack = Region("Movement Pack", player, world) - Locmovpack_name = ["Time is Money Pack", "Psychological Warfare Pack", "Armor for your Horse Pack", - "Shepherd Sheep"] - if World_Options.item_shuffle == Options.ItemShuffle.option_shuffled: - Locmovpack_name += ["Sword"] - Regmovpack.exits = [Entrance(player, "Tree", Regmovpack), Entrance(player, "Cloud", Regmovpack)] - Regmovpack.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regmovpack) for loc_name - in Locmovpack_name] - add_coin_dlcquest(Regmovpack, 46, player) - world.regions.append(Regmovpack) + connect_entrances_basic(player, world) - Regbtree = Region("Behind Tree", player, world) - Locbtree_name = ["Double Jump Pack", "Map Pack", "Between Trees Sheep", "Hole in the Wall Sheep"] - if World_Options.item_shuffle == Options.ItemShuffle.option_shuffled: - Locbtree_name += ["Gun"] - Regbtree.exits = [Entrance(player, "Behind Tree Double Jump", Regbtree), - Entrance(player, "Forest Entrance", Regbtree)] - Regbtree.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regbtree) for loc_name in - Locbtree_name] - add_coin_dlcquest(Regbtree, 60, player) - world.regions.append(Regbtree) - Regpsywarfare = Region("Psychological Warfare", player, world) - Locpsywarfare_name = ["West Cave Sheep"] - Regpsywarfare.exits = [Entrance(player, "Cloud Double Jump", Regpsywarfare)] - Regpsywarfare.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regpsywarfare) for - loc_name in Locpsywarfare_name] - add_coin_dlcquest(Regpsywarfare, 100, player) - world.regions.append(Regpsywarfare) +def create_regions_lfod_campaign(coin_bundle_size, has_campaign_lfod, has_coinsanity, has_item_shuffle, multiworld, player, region_menu): + if not has_campaign_lfod: + return - Regdoubleleft = Region("Double Jump Total Left", player, world) - Locdoubleleft_name = ["Pet Pack", "Top Hat Pack", "North West Alcove Sheep"] - Regdoubleleft.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regdoubleleft) for - loc_name in - Locdoubleleft_name] - Regdoubleleft.exits = [Entrance(player, "Cave Tree", Regdoubleleft), - Entrance(player, "Cave Roof", Regdoubleleft)] - add_coin_dlcquest(Regdoubleleft, 50, player) - world.regions.append(Regdoubleleft) + region_menu.exits += [Entrance(player, "Live Freemium or Die", region_menu)] + locations_lfod_start = ["Particles Pack", "Day One Patch Pack", "Checkpoint Pack", "Incredibly Important Pack", + "Nice Try", "Story is Important", "I Get That Reference!"] + conditional_location(has_item_shuffle, "Wooden Sword") + region_lfod_start = create_region_and_locations_lfod("Freemium Start", locations_lfod_start, ["Vines"], player, multiworld, 50) + create_coinsanity_locations_lfod(has_coinsanity, coin_bundle_size, player, region_lfod_start) + locations_behind_vines = ["Wall Jump Pack", "Health Bar Pack", "Parallax Pack"] + conditional_location(has_item_shuffle, "Pickaxe") + create_region_and_locations_lfod("Behind the Vines", locations_behind_vines, ["Wall Jump Entrance"], player, multiworld, 95) + locations_wall_jump = ["Harmless Plants Pack", "Death of Comedy Pack", "Canadian Dialog Pack", "DLC NPC Pack"] + create_region_and_locations_lfod("Wall Jump", locations_wall_jump, ["Harmless Plants", "Pickaxe Hard Cave"], player, multiworld, 150) + create_region_and_locations_lfod("Fake Ending", ["Cut Content Pack", "Name Change Pack"], ["Name Change Entrance", "Cut Content Entrance"], player, + multiworld) + create_region_and_locations_lfod("Hard Cave", [], ["Hard Cave Wall Jump"], player, multiworld, 20) + create_region_and_locations_lfod("Hard Cave Wall Jump", ["Increased HP Pack"], [], player, multiworld, 130) + create_region_and_locations_lfod("Cut Content", conditional_location(has_item_shuffle, "Humble Indie Bindle"), [], player, multiworld, 200) + create_region_and_locations_lfod("Name Change", conditional_location(has_item_shuffle, "Box of Various Supplies"), ["Behind Rocks"], player, multiworld) + create_region_and_locations_lfod("Top Right", ["Season Pass", "High Definition Next Gen Pack"], ["Blizzard"], player, multiworld, 90) + create_region_and_locations_lfod("Season", ["Remove Ads Pack", "Not Exactly Noble"], ["Boss Door"], player, multiworld, 154) + region_final_boss = create_region_and_locations_lfod("Final Boss", ["Big Sword Pack", "Really Big Sword Pack", "Unfathomable Sword Pack"], [], player, multiworld) - Regdoubleleftcave = Region("Double Jump Total Left Cave", player, world) - Locdoubleleftcave_name = ["Top Hat Sheep"] - Regdoubleleftcave.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regdoubleleftcave) - for loc_name in Locdoubleleftcave_name] - add_coin_dlcquest(Regdoubleleftcave, 9, player) - world.regions.append(Regdoubleleftcave) + create_victory_event(region_final_boss, "Winning Freemium", "Victory Freemium", player) - Regdoubleleftroof = Region("Double Jump Total Left Roof", player, world) - Locdoubleleftroof_name = ["North West Ceiling Sheep"] - Regdoubleleftroof.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regdoubleleftroof) - for loc_name in Locdoubleleftroof_name] - add_coin_dlcquest(Regdoubleleftroof, 10, player) - world.regions.append(Regdoubleleftroof) + connect_entrances_lfod(multiworld, player) - Regdoubletree = Region("Double Jump Behind Tree", player, world) - Locdoubletree_name = ["Sexy Outfits Pack", "Double Jump Alcove Sheep", "Sexy Outfits Sheep"] - Regdoubletree.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regdoubletree) for - loc_name in - Locdoubletree_name] - Regdoubletree.exits = [Entrance(player, "True Double Jump", Regdoubletree)] - add_coin_dlcquest(Regdoubletree, 89, player) - world.regions.append(Regdoubletree) - Regtruedoublejump = Region("True Double Jump Behind Tree", player, world) - Loctruedoublejump_name = ["Double Jump Floating Sheep", "Cutscene Sheep"] - Regtruedoublejump.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regtruedoublejump) - for loc_name in Loctruedoublejump_name] - add_coin_dlcquest(Regtruedoublejump, 7, player) - world.regions.append(Regtruedoublejump) +def conditional_location(condition: bool, location: str) -> List[str]: + return conditional_locations(condition, [location]) - Regforest = Region("The Forest", player, world) - Locforest_name = ["Gun Pack", "Night Map Pack"] - Regforest.exits = [Entrance(player, "Behind Ogre", Regforest), - Entrance(player, "Forest Double Jump", Regforest)] - Regforest.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regforest) for loc_name in - Locforest_name] - add_coin_dlcquest(Regforest, 171, player) - world.regions.append(Regforest) - Regforestdoublejump = Region("The Forest whit double Jump", player, world) - Locforestdoublejump_name = ["The Zombie Pack", "Forest Low Sheep"] - Regforestdoublejump.exits = [Entrance(player, "Forest True Double Jump", Regforestdoublejump)] - Regforestdoublejump.locations += [ - DLCQuestLocation(player, loc_name, location_table[loc_name], Regforestdoublejump) for loc_name in - Locforestdoublejump_name] - add_coin_dlcquest(Regforestdoublejump, 76, player) - world.regions.append(Regforestdoublejump) +def conditional_locations(condition: bool, locations: List[str]) -> List[str]: + return locations if condition else [] - Regforesttruedoublejump = Region("The Forest whit double Jump Part 2", player, world) - Locforesttruedoublejump_name = ["Forest High Sheep"] - Regforesttruedoublejump.locations += [ - DLCQuestLocation(player, loc_name, location_table[loc_name], Regforesttruedoublejump) - for loc_name in Locforesttruedoublejump_name] - add_coin_dlcquest(Regforesttruedoublejump, 203, player) - world.regions.append(Regforesttruedoublejump) - Regfinalroom = Region("The Final Boss Room", player, world) - Locfinalroom_name = ["Finish the Fight Pack"] - Regfinalroom.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regfinalroom) for - loc_name in - Locfinalroom_name] - world.regions.append(Regfinalroom) +def create_region_and_locations_basic(region_name: str, locations: List[str], exits: List[str], player: int, multiworld: MultiWorld, + number_coins: int = 0) -> Region: + return create_region_and_locations(region_name, locations, exits, player, multiworld, number_coins, 0) - loc_win = DLCQuestLocation(player, "Winning Basic", None, world.get_region("The Final Boss Room", player)) - world.get_region("The Final Boss Room", player).locations.append(loc_win) - loc_win.place_locked_item(create_event(player, "Victory Basic")) - world.get_entrance("DLC Quest Basic", player).connect(world.get_region("Move Right", player)) +def create_region_and_locations_lfod(region_name: str, locations: List[str], exits: List[str], player: int, multiworld: MultiWorld, + number_coins: int = 0) -> Region: + return create_region_and_locations(region_name, locations, exits, player, multiworld, 0, number_coins) - world.get_entrance("Moving", player).connect(world.get_region("Movement Pack", player)) - world.get_entrance("Tree", player).connect(world.get_region("Behind Tree", player)) +def create_region_and_locations(region_name: str, locations: List[str], exits: List[str], player: int, multiworld: MultiWorld, + number_coins_basic: int, number_coins_lfod: int) -> Region: + region = Region(region_name, player, multiworld) + region.exits = [Entrance(player, exit_name, region) for exit_name in exits] + region.locations += [DLCQuestLocation(player, name, location_table[name], region) for name in locations] + if number_coins_basic > 0: + add_coin_dlcquest(region, number_coins_basic, player) + if number_coins_lfod > 0: + add_coin_lfod(region, number_coins_lfod, player) + multiworld.regions.append(region) + return region - world.get_entrance("Cloud", player).connect(world.get_region("Psychological Warfare", player)) - world.get_entrance("Cloud Double Jump", player).connect(world.get_region("Double Jump Total Left", player)) +def create_victory_event(region_victory: Region, event_name: str, item_name: str, player: int): + location_victory = DLCQuestLocation(player, event_name, None, region_victory) + region_victory.locations.append(location_victory) + location_victory.place_locked_item(create_event(player, item_name)) - world.get_entrance("Cave Tree", player).connect(world.get_region("Double Jump Total Left Cave", player)) - world.get_entrance("Cave Roof", player).connect(world.get_region("Double Jump Total Left Roof", player)) +def connect_entrances_basic(player, world): + world.get_entrance("DLC Quest Basic", player).connect(world.get_region("Move Right", player)) + world.get_entrance("Moving", player).connect(world.get_region("Movement Pack", player)) + world.get_entrance("Tree", player).connect(world.get_region("Behind Tree", player)) + world.get_entrance("Cloud", player).connect(world.get_region("Psychological Warfare", player)) + world.get_entrance("Cloud Double Jump", player).connect(world.get_region("Double Jump Total Left", player)) + world.get_entrance("Cave Tree", player).connect(world.get_region("Double Jump Total Left Cave", player)) + world.get_entrance("Cave Roof", player).connect(world.get_region("Double Jump Total Left Roof", player)) + world.get_entrance("Forest Entrance", player).connect(world.get_region("The Forest", player)) + world.get_entrance("Behind Tree Double Jump", player).connect(world.get_region("Double Jump Behind Tree", player)) + world.get_entrance("Behind Ogre", player).connect(world.get_region("The Final Boss Room", player)) + world.get_entrance("Forest Double Jump", player).connect(world.get_region("The Forest with double Jump", player)) + world.get_entrance("Forest True Double Jump", player).connect(world.get_region("The Forest with double Jump Part 2", player)) + world.get_entrance("True Double Jump", player).connect(world.get_region("True Double Jump Behind Tree", player)) - world.get_entrance("Forest Entrance", player).connect(world.get_region("The Forest", player)) - world.get_entrance("Behind Tree Double Jump", player).connect( - world.get_region("Double Jump Behind Tree", player)) +def connect_entrances_lfod(multiworld, player): + multiworld.get_entrance("Live Freemium or Die", player).connect(multiworld.get_region("Freemium Start", player)) + multiworld.get_entrance("Vines", player).connect(multiworld.get_region("Behind the Vines", player)) + multiworld.get_entrance("Wall Jump Entrance", player).connect(multiworld.get_region("Wall Jump", player)) + multiworld.get_entrance("Harmless Plants", player).connect(multiworld.get_region("Fake Ending", player)) + multiworld.get_entrance("Pickaxe Hard Cave", player).connect(multiworld.get_region("Hard Cave", player)) + multiworld.get_entrance("Hard Cave Wall Jump", player).connect(multiworld.get_region("Hard Cave Wall Jump", player)) + multiworld.get_entrance("Name Change Entrance", player).connect(multiworld.get_region("Name Change", player)) + multiworld.get_entrance("Cut Content Entrance", player).connect(multiworld.get_region("Cut Content", player)) + multiworld.get_entrance("Behind Rocks", player).connect(multiworld.get_region("Top Right", player)) + multiworld.get_entrance("Blizzard", player).connect(multiworld.get_region("Season", player)) + multiworld.get_entrance("Boss Door", player).connect(multiworld.get_region("Final Boss", player)) - world.get_entrance("Behind Ogre", player).connect(world.get_region("The Final Boss Room", player)) - world.get_entrance("Forest Double Jump", player).connect( - world.get_region("The Forest whit double Jump", player)) +def create_coinsanity_locations_dlc_quest(has_coinsanity: bool, coin_bundle_size: int, player: int, region_move_right: Region): + create_coinsanity_locations(has_coinsanity, coin_bundle_size, player, region_move_right, 825, "DLC Quest") - world.get_entrance("Forest True Double Jump", player).connect( - world.get_region("The Forest whit double Jump Part 2", player)) - world.get_entrance("True Double Jump", player).connect(world.get_region("True Double Jump Behind Tree", player)) +def create_coinsanity_locations_lfod(has_coinsanity: bool, coin_bundle_size: int, player: int, region_lfod_start: Region): + create_coinsanity_locations(has_coinsanity, coin_bundle_size, player, region_lfod_start, 889, "Live Freemium or Die") - if (World_Options.campaign == Options.Campaign.option_live_freemium_or_die or World_Options.campaign - == Options.Campaign.option_both): - Regfreemiumstart = Region("Freemium Start", player, world) - Locfreemiumstart_name = ["Particles Pack", "Day One Patch Pack", "Checkpoint Pack", "Incredibly Important Pack", - "Nice Try", "Story is Important", "I Get That Reference!"] - if World_Options.item_shuffle == Options.ItemShuffle.option_shuffled: - Locfreemiumstart_name += ["Wooden Sword"] - Regfreemiumstart.exits = [Entrance(player, "Vines", Regfreemiumstart)] - Regfreemiumstart.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regfreemiumstart) - for loc_name in - Locfreemiumstart_name] - add_coin_freemium(Regfreemiumstart, 50, player) - if World_Options.coinsanity == Options.CoinSanity.option_coin: - coin_bundle_needed = math.floor(889 / World_Options.coinbundlequantity) - for i in range(coin_bundle_needed): - item_coin_freemium = f"Live Freemium or Die: {World_Options.coinbundlequantity * (i + 1)} Coin" - Regfreemiumstart.locations += [ - DLCQuestLocation(player, item_coin_freemium, location_table[item_coin_freemium], - Regfreemiumstart)] - if 889 % World_Options.coinbundlequantity != 0: - Regfreemiumstart.locations += [ - DLCQuestLocation(player, "Live Freemium or Die: 889 Coin", - location_table["Live Freemium or Die: 889 Coin"], - Regfreemiumstart)] - world.regions.append(Regfreemiumstart) +def create_coinsanity_locations(has_coinsanity: bool, coin_bundle_size: int, player: int, region: Region, last_coin_number: int, campaign_prefix: str): + if not has_coinsanity: + return - Regbehindvine = Region("Behind the Vines", player, world) - Locbehindvine_name = ["Wall Jump Pack", "Health Bar Pack", "Parallax Pack"] - if World_Options.item_shuffle == Options.ItemShuffle.option_shuffled: - Locbehindvine_name += ["Pickaxe"] - Regbehindvine.exits = [Entrance(player, "Wall Jump Entrance", Regbehindvine)] - Regbehindvine.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regbehindvine) for - loc_name in Locbehindvine_name] - add_coin_freemium(Regbehindvine, 95, player) - world.regions.append(Regbehindvine) - - Regwalljump = Region("Wall Jump", player, world) - Locwalljump_name = ["Harmless Plants Pack", "Death of Comedy Pack", "Canadian Dialog Pack", "DLC NPC Pack"] - Regwalljump.exits = [Entrance(player, "Harmless Plants", Regwalljump), - Entrance(player, "Pickaxe Hard Cave", Regwalljump)] - Regwalljump.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regwalljump) for - loc_name in Locwalljump_name] - add_coin_freemium(Regwalljump, 150, player) - world.regions.append(Regwalljump) - - Regfakeending = Region("Fake Ending", player, world) - Locfakeending_name = ["Cut Content Pack", "Name Change Pack"] - Regfakeending.exits = [Entrance(player, "Name Change Entrance", Regfakeending), - Entrance(player, "Cut Content Entrance", Regfakeending)] - Regfakeending.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regfakeending) for - loc_name in Locfakeending_name] - world.regions.append(Regfakeending) - - Reghardcave = Region("Hard Cave", player, world) - add_coin_freemium(Reghardcave, 20, player) - Reghardcave.exits = [Entrance(player, "Hard Cave Wall Jump", Reghardcave)] - world.regions.append(Reghardcave) - - Reghardcavewalljump = Region("Hard Cave Wall Jump", player, world) - Lochardcavewalljump_name = ["Increased HP Pack"] - Reghardcavewalljump.locations += [ - DLCQuestLocation(player, loc_name, location_table[loc_name], Reghardcavewalljump) for - loc_name in Lochardcavewalljump_name] - add_coin_freemium(Reghardcavewalljump, 130, player) - world.regions.append(Reghardcavewalljump) - - Regcutcontent = Region("Cut Content", player, world) - Loccutcontent_name = [] - if World_Options.item_shuffle == Options.ItemShuffle.option_shuffled: - Loccutcontent_name += ["Humble Indie Bindle"] - Regcutcontent.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regcutcontent) for - loc_name in Loccutcontent_name] - add_coin_freemium(Regcutcontent, 200, player) - world.regions.append(Regcutcontent) - - Regnamechange = Region("Name Change", player, world) - Locnamechange_name = [] - if World_Options.item_shuffle == Options.ItemShuffle.option_shuffled: - Locnamechange_name += ["Box of Various Supplies"] - Regnamechange.exits = [Entrance(player, "Behind Rocks", Regnamechange)] - Regnamechange.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regnamechange) for - loc_name in Locnamechange_name] - world.regions.append(Regnamechange) - - Regtopright = Region("Top Right", player, world) - Loctopright_name = ["Season Pass", "High Definition Next Gen Pack"] - Regtopright.exits = [Entrance(player, "Blizzard", Regtopright)] - Regtopright.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regtopright) for - loc_name in Loctopright_name] - add_coin_freemium(Regtopright, 90, player) - world.regions.append(Regtopright) - - Regseason = Region("Season", player, world) - Locseason_name = ["Remove Ads Pack", "Not Exactly Noble"] - Regseason.exits = [Entrance(player, "Boss Door", Regseason)] - Regseason.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regseason) for - loc_name in Locseason_name] - add_coin_freemium(Regseason, 154, player) - world.regions.append(Regseason) - - Regfinalboss = Region("Final Boss", player, world) - Locfinalboss_name = ["Big Sword Pack", "Really Big Sword Pack", "Unfathomable Sword Pack"] - Regfinalboss.locations += [DLCQuestLocation(player, loc_name, location_table[loc_name], Regfinalboss) for - loc_name in Locfinalboss_name] - world.regions.append(Regfinalboss) - - loc_wining = DLCQuestLocation(player, "Winning Freemium", None, world.get_region("Final Boss", player)) - world.get_region("Final Boss", player).locations.append(loc_wining) - loc_wining.place_locked_item(create_event(player, "Victory Freemium")) - - world.get_entrance("Live Freemium or Die", player).connect(world.get_region("Freemium Start", player)) - - world.get_entrance("Vines", player).connect(world.get_region("Behind the Vines", player)) - - world.get_entrance("Wall Jump Entrance", player).connect(world.get_region("Wall Jump", player)) - - world.get_entrance("Harmless Plants", player).connect(world.get_region("Fake Ending", player)) - - world.get_entrance("Pickaxe Hard Cave", player).connect(world.get_region("Hard Cave", player)) - - world.get_entrance("Hard Cave Wall Jump", player).connect(world.get_region("Hard Cave Wall Jump", player)) - - world.get_entrance("Name Change Entrance", player).connect(world.get_region("Name Change", player)) - - world.get_entrance("Cut Content Entrance", player).connect(world.get_region("Cut Content", player)) - - world.get_entrance("Behind Rocks", player).connect(world.get_region("Top Right", player)) - - world.get_entrance("Blizzard", player).connect(world.get_region("Season", player)) - - world.get_entrance("Boss Door", player).connect(world.get_region("Final Boss", player)) + coin_bundle_needed = math.ceil(last_coin_number / coin_bundle_size) + for i in range(1, coin_bundle_needed + 1): + number_coins = min(last_coin_number, coin_bundle_size * i) + item_coin = f"{campaign_prefix}: {number_coins} Coin" + region.locations += [DLCQuestLocation(player, item_coin, location_table[item_coin], region)] diff --git a/worlds/dlcquest/test/TestItemShuffle.py b/worlds/dlcquest/test/TestItemShuffle.py new file mode 100644 index 0000000000..bfe999246a --- /dev/null +++ b/worlds/dlcquest/test/TestItemShuffle.py @@ -0,0 +1,130 @@ +from . import DLCQuestTestBase +from .. import Options + +sword = "Sword" +gun = "Gun" +wooden_sword = "Wooden Sword" +pickaxe = "Pickaxe" +humble_bindle = "Humble Indie Bindle" +box_supplies = "Box of Various Supplies" +items = [sword, gun, wooden_sword, pickaxe, humble_bindle, box_supplies] + +important_pack = "Incredibly Important Pack" + + +class TestItemShuffle(DLCQuestTestBase): + options = {Options.ItemShuffle.internal_name: Options.ItemShuffle.option_shuffled, + Options.Campaign.internal_name: Options.Campaign.option_both} + + def test_items_in_pool(self): + item_names = {item.name for item in self.multiworld.get_items()} + for item in items: + with self.subTest(f"{item}"): + self.assertIn(item, item_names) + + def test_item_locations_in_pool(self): + location_names = {location.name for location in self.multiworld.get_locations()} + for item_location in items: + with self.subTest(f"{item_location}"): + self.assertIn(item_location, location_names) + + def test_sword_location_has_correct_rules(self): + self.assertFalse(self.can_reach_location(sword)) + movement_pack = self.multiworld.create_item("Movement Pack", self.player) + self.collect(movement_pack) + self.assertFalse(self.can_reach_location(sword)) + time_pack = self.multiworld.create_item("Time is Money Pack", self.player) + self.collect(time_pack) + self.assertTrue(self.can_reach_location(sword)) + + def test_gun_location_has_correct_rules(self): + self.assertFalse(self.can_reach_location(gun)) + movement_pack = self.multiworld.create_item("Movement Pack", self.player) + self.collect(movement_pack) + self.assertFalse(self.can_reach_location(gun)) + sword_item = self.multiworld.create_item(sword, self.player) + self.collect(sword_item) + self.assertFalse(self.can_reach_location(gun)) + gun_pack = self.multiworld.create_item("Gun Pack", self.player) + self.collect(gun_pack) + self.assertTrue(self.can_reach_location(gun)) + + def test_wooden_sword_location_has_correct_rules(self): + self.assertFalse(self.can_reach_location(wooden_sword)) + important_pack_item = self.multiworld.create_item(important_pack, self.player) + self.collect(important_pack_item) + self.assertTrue(self.can_reach_location(wooden_sword)) + + def test_bindle_location_has_correct_rules(self): + self.assertFalse(self.can_reach_location(humble_bindle)) + wooden_sword_item = self.multiworld.create_item(wooden_sword, self.player) + self.collect(wooden_sword_item) + self.assertFalse(self.can_reach_location(humble_bindle)) + plants_pack = self.multiworld.create_item("Harmless Plants Pack", self.player) + self.collect(plants_pack) + self.assertFalse(self.can_reach_location(humble_bindle)) + wall_jump_pack = self.multiworld.create_item("Wall Jump Pack", self.player) + self.collect(wall_jump_pack) + self.assertFalse(self.can_reach_location(humble_bindle)) + name_change_pack = self.multiworld.create_item("Name Change Pack", self.player) + self.collect(name_change_pack) + self.assertFalse(self.can_reach_location(humble_bindle)) + cut_content_pack = self.multiworld.create_item("Cut Content Pack", self.player) + self.collect(cut_content_pack) + self.assertFalse(self.can_reach_location(humble_bindle)) + box_supplies_item = self.multiworld.create_item(box_supplies, self.player) + self.collect(box_supplies_item) + self.assertTrue(self.can_reach_location(humble_bindle)) + + def test_box_supplies_location_has_correct_rules(self): + self.assertFalse(self.can_reach_location(box_supplies)) + wooden_sword_item = self.multiworld.create_item(wooden_sword, self.player) + self.collect(wooden_sword_item) + self.assertFalse(self.can_reach_location(box_supplies)) + plants_pack = self.multiworld.create_item("Harmless Plants Pack", self.player) + self.collect(plants_pack) + self.assertFalse(self.can_reach_location(box_supplies)) + wall_jump_pack = self.multiworld.create_item("Wall Jump Pack", self.player) + self.collect(wall_jump_pack) + self.assertFalse(self.can_reach_location(box_supplies)) + name_change_pack = self.multiworld.create_item("Name Change Pack", self.player) + self.collect(name_change_pack) + self.assertFalse(self.can_reach_location(box_supplies)) + cut_content_pack = self.multiworld.create_item("Cut Content Pack", self.player) + self.collect(cut_content_pack) + self.assertTrue(self.can_reach_location(box_supplies)) + + def test_pickaxe_location_has_correct_rules(self): + self.assertFalse(self.can_reach_location(pickaxe)) + wooden_sword_item = self.multiworld.create_item(wooden_sword, self.player) + self.collect(wooden_sword_item) + self.assertFalse(self.can_reach_location(pickaxe)) + plants_pack = self.multiworld.create_item("Harmless Plants Pack", self.player) + self.collect(plants_pack) + self.assertFalse(self.can_reach_location(pickaxe)) + wall_jump_pack = self.multiworld.create_item("Wall Jump Pack", self.player) + self.collect(wall_jump_pack) + self.assertFalse(self.can_reach_location(pickaxe)) + name_change_pack = self.multiworld.create_item("Name Change Pack", self.player) + self.collect(name_change_pack) + self.assertFalse(self.can_reach_location(pickaxe)) + bindle_item = self.multiworld.create_item("Humble Indie Bindle", self.player) + self.collect(bindle_item) + self.assertTrue(self.can_reach_location(pickaxe)) + + +class TestNoItemShuffle(DLCQuestTestBase): + options = {Options.ItemShuffle.internal_name: Options.ItemShuffle.option_disabled, + Options.Campaign.internal_name: Options.Campaign.option_both} + + def test_items_not_in_pool(self): + item_names = {item.name for item in self.multiworld.get_items()} + for item in items: + with self.subTest(f"{item}"): + self.assertNotIn(item, item_names) + + def test_item_locations_not_in_pool(self): + location_names = {location.name for location in self.multiworld.get_locations()} + for item_location in items: + with self.subTest(f"{item_location}"): + self.assertNotIn(item_location, location_names) \ No newline at end of file diff --git a/worlds/dlcquest/test/TestOptionsLong.py b/worlds/dlcquest/test/TestOptionsLong.py new file mode 100644 index 0000000000..d0a5c0ed7d --- /dev/null +++ b/worlds/dlcquest/test/TestOptionsLong.py @@ -0,0 +1,87 @@ +from typing import Dict + +from BaseClasses import MultiWorld +from Options import SpecialRange +from .option_names import options_to_include +from .checks.world_checks import assert_can_win, assert_same_number_items_locations +from . import DLCQuestTestBase, setup_dlc_quest_solo_multiworld +from ... import AutoWorldRegister + + +def basic_checks(tester: DLCQuestTestBase, multiworld: MultiWorld): + assert_can_win(tester, multiworld) + assert_same_number_items_locations(tester, multiworld) + + +def get_option_choices(option) -> Dict[str, int]: + if issubclass(option, SpecialRange): + return option.special_range_names + elif option.options: + return option.options + return {} + + +class TestGenerateDynamicOptions(DLCQuestTestBase): + def test_given_option_pair_when_generate_then_basic_checks(self): + num_options = len(options_to_include) + for option1_index in range(0, num_options): + for option2_index in range(option1_index + 1, num_options): + option1 = options_to_include[option1_index] + option2 = options_to_include[option2_index] + option1_choices = get_option_choices(option1) + option2_choices = get_option_choices(option2) + for key1 in option1_choices: + for key2 in option2_choices: + with self.subTest(f"{option1.internal_name}: {key1}, {option2.internal_name}: {key2}"): + choices = {option1.internal_name: option1_choices[key1], + option2.internal_name: option2_choices[key2]} + multiworld = setup_dlc_quest_solo_multiworld(choices) + basic_checks(self, multiworld) + + def test_given_option_truple_when_generate_then_basic_checks(self): + num_options = len(options_to_include) + for option1_index in range(0, num_options): + for option2_index in range(option1_index + 1, num_options): + for option3_index in range(option2_index + 1, num_options): + option1 = options_to_include[option1_index] + option2 = options_to_include[option2_index] + option3 = options_to_include[option3_index] + option1_choices = get_option_choices(option1) + option2_choices = get_option_choices(option2) + option3_choices = get_option_choices(option3) + for key1 in option1_choices: + for key2 in option2_choices: + for key3 in option3_choices: + with self.subTest(f"{option1.internal_name}: {key1}, {option2.internal_name}: {key2}, {option3.internal_name}: {key3}"): + choices = {option1.internal_name: option1_choices[key1], + option2.internal_name: option2_choices[key2], + option3.internal_name: option3_choices[key3]} + multiworld = setup_dlc_quest_solo_multiworld(choices) + basic_checks(self, multiworld) + + def test_given_option_quartet_when_generate_then_basic_checks(self): + num_options = len(options_to_include) + for option1_index in range(0, num_options): + for option2_index in range(option1_index + 1, num_options): + for option3_index in range(option2_index + 1, num_options): + for option4_index in range(option3_index + 1, num_options): + option1 = options_to_include[option1_index] + option2 = options_to_include[option2_index] + option3 = options_to_include[option3_index] + option4 = options_to_include[option4_index] + option1_choices = get_option_choices(option1) + option2_choices = get_option_choices(option2) + option3_choices = get_option_choices(option3) + option4_choices = get_option_choices(option4) + for key1 in option1_choices: + for key2 in option2_choices: + for key3 in option3_choices: + for key4 in option4_choices: + with self.subTest( + f"{option1.internal_name}: {key1}, {option2.internal_name}: {key2}, {option3.internal_name}: {key3}, {option4.internal_name}: {key4}"): + choices = {option1.internal_name: option1_choices[key1], + option2.internal_name: option2_choices[key2], + option3.internal_name: option3_choices[key3], + option4.internal_name: option4_choices[key4]} + multiworld = setup_dlc_quest_solo_multiworld(choices) + basic_checks(self, multiworld) diff --git a/worlds/dlcquest/test/__init__.py b/worlds/dlcquest/test/__init__.py new file mode 100644 index 0000000000..e998bd8a5e --- /dev/null +++ b/worlds/dlcquest/test/__init__.py @@ -0,0 +1,53 @@ +from typing import ClassVar + +from typing import Dict, FrozenSet, Tuple, Any +from argparse import Namespace + +from BaseClasses import MultiWorld +from test.TestBase import WorldTestBase +from .. import DLCqworld +from test.general import gen_steps, setup_solo_multiworld as setup_base_solo_multiworld +from worlds.AutoWorld import call_all + + +class DLCQuestTestBase(WorldTestBase): + game = "DLCQuest" + world: DLCqworld + player: ClassVar[int] = 1 + + def world_setup(self, *args, **kwargs): + super().world_setup(*args, **kwargs) + if self.constructed: + self.world = self.multiworld.worlds[self.player] # noqa + + @property + def run_default_tests(self) -> bool: + # world_setup is overridden, so it'd always run default tests when importing DLCQuestTestBase + is_not_dlc_test = type(self) is not DLCQuestTestBase + should_run_default_tests = is_not_dlc_test and super().run_default_tests + return should_run_default_tests + + +def setup_dlc_quest_solo_multiworld(test_options=None, seed=None, _cache: Dict[FrozenSet[Tuple[str, Any]], MultiWorld] = {}) -> MultiWorld: #noqa + if test_options is None: + test_options = {} + + # Yes I reuse the worlds generated between tests, its speeds the execution by a couple seconds + frozen_options = frozenset(test_options.items()).union({seed}) + if frozen_options in _cache: + return _cache[frozen_options] + + multiworld = setup_base_solo_multiworld(DLCqworld, ()) + multiworld.set_seed(seed) + # print(f"Seed: {multiworld.seed}") # Uncomment to print the seed for every test + args = Namespace() + for name, option in DLCqworld.options_dataclass.type_hints.items(): + value = option(test_options[name]) if name in test_options else option.from_any(option.default) + setattr(args, name, {1: value}) + multiworld.set_options(args) + for step in gen_steps: + call_all(multiworld, step) + + _cache[frozen_options] = multiworld + + return multiworld diff --git a/worlds/dlcquest/test/checks/__init__.py b/worlds/dlcquest/test/checks/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/worlds/dlcquest/test/checks/world_checks.py b/worlds/dlcquest/test/checks/world_checks.py new file mode 100644 index 0000000000..a97093d620 --- /dev/null +++ b/worlds/dlcquest/test/checks/world_checks.py @@ -0,0 +1,42 @@ +from typing import List + +from BaseClasses import MultiWorld, ItemClassification +from .. import DLCQuestTestBase +from ... import Options + + +def get_all_item_names(multiworld: MultiWorld) -> List[str]: + return [item.name for item in multiworld.itempool] + + +def get_all_location_names(multiworld: MultiWorld) -> List[str]: + return [location.name for location in multiworld.get_locations() if not location.event] + + +def assert_victory_exists(tester: DLCQuestTestBase, multiworld: MultiWorld): + campaign = multiworld.campaign[1] + all_items = [item.name for item in multiworld.get_items()] + if campaign == Options.Campaign.option_basic or campaign == Options.Campaign.option_both: + tester.assertIn("Victory Basic", all_items) + if campaign == Options.Campaign.option_live_freemium_or_die or campaign == Options.Campaign.option_both: + tester.assertIn("Victory Freemium", all_items) + + +def collect_all_then_assert_can_win(tester: DLCQuestTestBase, multiworld: MultiWorld): + for item in multiworld.get_items(): + multiworld.state.collect(item) + campaign = multiworld.campaign[1] + if campaign == Options.Campaign.option_basic or campaign == Options.Campaign.option_both: + tester.assertTrue(multiworld.find_item("Victory Basic", 1).can_reach(multiworld.state)) + if campaign == Options.Campaign.option_live_freemium_or_die or campaign == Options.Campaign.option_both: + tester.assertTrue(multiworld.find_item("Victory Freemium", 1).can_reach(multiworld.state)) + + +def assert_can_win(tester: DLCQuestTestBase, multiworld: MultiWorld): + assert_victory_exists(tester, multiworld) + collect_all_then_assert_can_win(tester, multiworld) + + +def assert_same_number_items_locations(tester: DLCQuestTestBase, multiworld: MultiWorld): + non_event_locations = [location for location in multiworld.get_locations() if not location.event] + tester.assertEqual(len(multiworld.itempool), len(non_event_locations)) \ No newline at end of file diff --git a/worlds/dlcquest/test/option_names.py b/worlds/dlcquest/test/option_names.py new file mode 100644 index 0000000000..4a4b46e906 --- /dev/null +++ b/worlds/dlcquest/test/option_names.py @@ -0,0 +1,5 @@ +from .. import DLCqworld + +options_to_exclude = ["progression_balancing", "accessibility", "start_inventory", "start_hints", "death_link"] +options_to_include = [option for option_name, option in DLCqworld.options_dataclass.type_hints.items() + if option_name not in options_to_exclude] From 7aab9d44394b005dc1c0ee6e83d25f4bc9326ecb Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Wed, 18 Oct 2023 15:55:03 -0400 Subject: [PATCH 120/144] =?UTF-8?q?Docs:=20Recommend=20Bizhawk=20Version?= =?UTF-8?q?=202.9.1=20for=20Pok=C3=A9mon=20R/B=20(#2320)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- worlds/pokemon_rb/docs/setup_en.md | 6 +++--- worlds/pokemon_rb/docs/setup_es.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/worlds/pokemon_rb/docs/setup_en.md b/worlds/pokemon_rb/docs/setup_en.md index 488f3fdc07..7ba9b3aa09 100644 --- a/worlds/pokemon_rb/docs/setup_en.md +++ b/worlds/pokemon_rb/docs/setup_en.md @@ -7,7 +7,7 @@ As we are using BizHawk, this guide is only applicable to Windows and Linux syst ## Required Software - BizHawk: [BizHawk Releases from TASVideos](https://tasvideos.org/BizHawk/ReleaseHistory) - - Version 2.3.1 and later are supported. Version 2.7 is recommended for stability. + - Version 2.3.1 and later are supported. Version 2.9.1 is recommended. - Detailed installation instructions for BizHawk can be found at the above link. - Windows users must run the prereq installer first, which can also be found at the above link. - The built-in Archipelago client, which can be installed [here](https://github.com/ArchipelagoMW/Archipelago/releases) @@ -23,7 +23,7 @@ As we are using BizHawk, this guide is only applicable to Windows and Linux syst Once BizHawk has been installed, open EmuHawk and change the following settings: -- (≤ 2.8) Go to Config > Customize. Switch to the Advanced tab, then switch the Lua Core from "NLua+KopiLua" to +- (If using 2.8 or earlier) Go to Config > Customize. Switch to the Advanced tab, then switch the Lua Core from "NLua+KopiLua" to "Lua+LuaInterface". Then restart EmuHawk. This is required for the Lua script to function correctly. **NOTE: Even if "Lua+LuaInterface" is already selected, toggle between the two options and reselect it. Fresh installs** **of newer versions of EmuHawk have a tendency to show "Lua+LuaInterface" as the default selected option but still load** @@ -57,7 +57,7 @@ For `trainer_name` and `rival_name` the following regular characters are allowed * `‘’“”·… ABCDEFGHIJKLMNOPQRSTUVWXYZ():;[]abcdefghijklmnopqrstuvwxyzé'-?!.♂$×/,♀0123456789` -And the following special characters (these each take up one character): +And the following special characters (these each count as one character): * `<'d>` * `<'l>` * `<'t>` diff --git a/worlds/pokemon_rb/docs/setup_es.md b/worlds/pokemon_rb/docs/setup_es.md index 2a943da72f..a6a6aa6ce7 100644 --- a/worlds/pokemon_rb/docs/setup_es.md +++ b/worlds/pokemon_rb/docs/setup_es.md @@ -7,7 +7,7 @@ Al usar BizHawk, esta guía solo es aplicable en los sistemas de Windows y Linux ## Software Requerido - BizHawk: [BizHawk Releases en TASVideos](https://tasvideos.org/BizHawk/ReleaseHistory) - - La versión 2.3.1 y posteriores son soportadas. Se recomienda la versión 2.7 para estabilidad. + - La versión 2.3.1 y posteriores son soportadas. Se recomienda la versión 2.9.1. - Instrucciones de instalación detalladas para BizHawk se pueden encontrar en el enlace de arriba. - Los usuarios de Windows deben ejecutar el instalador de prerrequisitos (prereq installer) primero, que también se encuentra en el enlace de arriba. From 45e69f3d268a56badb3868e06e0ad303ef801740 Mon Sep 17 00:00:00 2001 From: Zach Parks Date: Wed, 18 Oct 2023 15:11:25 -0500 Subject: [PATCH 121/144] Docs: Triage role expectations documentation. (#2325) Co-authored-by: Scipio Wright --- docs/triage role expectations.md | 100 +++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 docs/triage role expectations.md diff --git a/docs/triage role expectations.md b/docs/triage role expectations.md new file mode 100644 index 0000000000..5b4cab2275 --- /dev/null +++ b/docs/triage role expectations.md @@ -0,0 +1,100 @@ +# Triage Role Expectations + +Users with Triage-level access are selected contributors who can and wish to proactively label/triage issues and pull +requests without being granted write access to the Archipelago repository. + +Triage users are not necessarily official members of the Archipelago organization, for the list of core maintainers, +please reference [ArchipelagoMW Members](https://github.com/orgs/ArchipelagoMW/people) page. + +## Access Permissions + +Triage users have the following permissions: + +* Apply/dismiss labels on all issues and pull requests. +* Close, reopen, and assign all issues and pull requests. +* Mark issues and pull requests as duplicate. +* Request pull request reviews from repository members. +* Hide comments in issues or pull requests from public view. + * Hidden comments are not deleted and can be reversed by another triage user or repository member with write access. +* And all other standard permissions granted to regular GitHub users. + +For more details on permissions granted by the Triage role, see +[GitHub's Role Documentation](https://docs.github.com/en/organizations/managing-user-access-to-your-organizations-repositories/managing-repository-roles/repository-roles-for-an-organization). + +## Expectations + +Users with triage-level permissions have no expectation to review code, but, if desired, to review pull requests/issues +and apply the relevant labels and ping/request reviews from any relevant [code owners](./CODEOWNERS) for review. Triage +users are also expected not to close others' issues or pull requests without strong reason to do so (with exception of +`meta: invalid` or `meta: duplicate` scenarios, which are listed below). When in doubt, defer to a core maintainer. + +Triage users are not "moderators" for others' issues or pull requests. However, they may voice their opinions/feedback +on issues or pull requests, just the same as any other GitHub user contributing to Archipelago. + +## Labeling + +As of the time of writing this document, there are 15 distinct labels that can be applied to issues and pull requests. + +### Affects + +These labels notate if certain issues or pull requests affect critical aspects of Archipelago that may require specific +review. More than one of these labels can be used on a issue or pull request, if relevant. + +* `affects: core` is to be applied to issues/PRs that may affect core Archipelago functionality and should be reviewed +with additional scrutiny. + * Core is defined as any files not contained in the `WebHostLib` directory or individual world implementations + directories inside the `worlds` directory, not including `worlds/generic`. +* `affects: webhost` is to be applied to issues/PRs that may affect the core WebHost portion of Archipelago. In +general, this is anything being modified inside the `WebHostLib` directory or `WebHost.py` file. +* `affects: release/blocker` is to be applied for any issues/PRs that may either negatively impact (issues) or propose +to resolve critical issues (pull requests) that affect the current or next official release of Archipelago and should be +given top priority for review. + +### Is + +These labels notate what kinds of changes are being made or proposed in issues or pull requests. More than one of these +labels can be used on a issue or pull request, if relevant, but at least one of these labels should be applied to every +pull request and issue. + +* `is: bug/fix` is to be applied to issues/PRs that report or resolve an issue in core, web, or individual world +implementations. +* `is: documentation` is to be applied to issues/PRs that relate to adding, updating, or removing documentation in +core, web, or individual world implementations without modifying actual code. +* `is: enhancement` is to be applied to issues/PRs that relate to adding, modifying, or removing functionality in +core, web, or individual world implementations. +* `is: refactor/cleanup` is to be applied to issues/PRs that relate to reorganizing existing code to improve +readability or performance without adding, modifying, or removing functionality or fixing known regressions. +* `is: maintenance` is to be applied to issues/PRs that don't modify logic, refactor existing code, change features. +This is typically reserved for pull requests that need to update dependencies or increment version numbers without +resolving existing issues. +* `is: new game` is to be applied to any pull requests that introduce a new game for the first time to the `worlds` +directory. + * Issues should not be opened and classified with `is: new game`, and instead should be directed to the + #future-game-design channel in Archipelago for opening suggestions. If they are opened, they should be labeled + with `meta: invalid` and closed. + * Pull requests for new games should only have this label, as enhancement, documentation, bug/fix, refactor, and + possibly maintenance is implied. + +### Meta + +These labels allow additional quick meta information for contributors or reviewers for issues and pull requests. They +have specific situations where they should be applied. + +* `meta: duplicate` is to be applied to any issues/PRs that are duplicate of another issue/PR that was already opened. + * These should be immediately closed after leaving a comment, directing to the original issue or pull request. +* `meta: invalid` is to be applied to any issues/PRs that do not relate to Archipelago or are inappropriate for +discussion on GitHub. + * These should be immediately closed afterwards. +* `meta: help wanted` is to be applied to any issues/PRs that require additional attention for whatever reason. + * These should include a comment describing what kind of help is requested when the label is added. + * Some common reasons include, but are not limited to: Breaking API changes that require developer input/testing or + pull requests with large line changes that need additional reviewers to be reviewed effectively. + * This label may require some programming experience and familiarity with Archipelago source to determine if + requesting additional attention for help is warranted. +* `meta: good first issue` is to be applied to any issues that may be a good starting ground for new contributors to try +and tackle. + * This label may require some programming experience and familiarity with Archipelago source to determine if an + issue is a "good first issue". +* `meta: wontfix` is to be applied for any issues/PRs that are opened that will not be actioned because it's out of +scope or determined to not be an issue. + * This should be reserved for use by a world's code owner(s) on their relevant world or by core maintainers. From e8a48da315c3e9e5c769e23cdb92b18394bf388c Mon Sep 17 00:00:00 2001 From: Aaron Wagener Date: Wed, 18 Oct 2023 16:04:12 -0500 Subject: [PATCH 122/144] SM: fix missing option import (#2326) --- worlds/sm/__init__.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/worlds/sm/__init__.py b/worlds/sm/__init__.py index 9d6f28607e..f208e600b9 100644 --- a/worlds/sm/__init__.py +++ b/worlds/sm/__init__.py @@ -1,18 +1,17 @@ from __future__ import annotations -import logging -import copy -import os -import threading import base64 -import settings +import copy +import logging +import threading import typing from typing import Any, Dict, Iterable, List, Set, TextIO, TypedDict -from BaseClasses import Region, Entrance, Location, MultiWorld, Item, ItemClassification, CollectionState, Tutorial -from Fill import fill_restrictive -from worlds.AutoWorld import World, AutoLogicRegister, WebWorld -from worlds.generic.Rules import set_rule, add_rule, add_item_rule +import settings +from BaseClasses import CollectionState, Entrance, Item, ItemClassification, Location, MultiWorld, Region, Tutorial +from Options import Accessibility +from worlds.AutoWorld import AutoLogicRegister, WebWorld, World +from worlds.generic.Rules import add_rule, set_rule logger = logging.getLogger("Super Metroid") From 1c7c83c69e97a19064a3bf1978be882662e8aee1 Mon Sep 17 00:00:00 2001 From: PsyMarth Date: Wed, 18 Oct 2023 14:53:54 -0700 Subject: [PATCH 123/144] OoT: Update Utils.py (#2310) Removed optional maxsize parameter, setting it to the default of 128. --- worlds/oot/Utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/oot/Utils.py b/worlds/oot/Utils.py index c2444cd1fe..9faffbdedd 100644 --- a/worlds/oot/Utils.py +++ b/worlds/oot/Utils.py @@ -11,7 +11,7 @@ def data_path(*args): return os.path.join(os.path.dirname(__file__), 'data', *args) -@lru_cache(maxsize=13) # Cache Overworld.json and the 12 dungeons +@lru_cache def read_json(file_path): json_string = "" with io.open(file_path, 'r') as file: From 38c9ee146d32e5930f1ac8fa2817616f51f01128 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 18 Oct 2023 15:26:52 -0700 Subject: [PATCH 124/144] WebHost: Refactor weighted-settings.js (#2318) * Refactor weighted-settings.js This moves most of the infrastructure into two classes: * WeightedSettings covers the settings page as a whole. It tracks the user's current settings in local storage as well as the game data from the server so they don't need to be manually passed around from function to function. * GameSettings covers the settings for a single game, and provides a view of the current settings and the game data just for that game. * Fix item count updating --- WebHostLib/static/assets/weighted-settings.js | 2042 +++++++++-------- 1 file changed, 1037 insertions(+), 1005 deletions(-) diff --git a/WebHostLib/static/assets/weighted-settings.js b/WebHostLib/static/assets/weighted-settings.js index fb7d3a349b..2cd61d2e6e 100644 --- a/WebHostLib/static/assets/weighted-settings.js +++ b/WebHostLib/static/assets/weighted-settings.js @@ -1,14 +1,14 @@ window.addEventListener('load', () => { - fetchSettingData().then((results) => { + fetchSettingData().then((data) => { let settingHash = localStorage.getItem('weighted-settings-hash'); if (!settingHash) { // If no hash data has been set before, set it now - settingHash = md5(JSON.stringify(results)); + settingHash = md5(JSON.stringify(data)); localStorage.setItem('weighted-settings-hash', settingHash); localStorage.removeItem('weighted-settings'); } - if (settingHash !== md5(JSON.stringify(results))) { + if (settingHash !== md5(JSON.stringify(data))) { const userMessage = document.getElementById('user-message'); userMessage.innerText = "Your settings are out of date! Click here to update them! Be aware this will reset " + "them all to default."; @@ -17,23 +17,22 @@ window.addEventListener('load', () => { } // Page setup - createDefaultSettings(results); - buildUI(results); - updateVisibleGames(); + const settings = new WeightedSettings(data); + settings.buildUI(); + settings.updateVisibleGames(); adjustHeaderWidth(); // Event listeners - document.getElementById('export-settings').addEventListener('click', () => exportSettings()); - document.getElementById('generate-race').addEventListener('click', () => generateGame(true)); - document.getElementById('generate-game').addEventListener('click', () => generateGame()); + document.getElementById('export-settings').addEventListener('click', () => settings.export()); + document.getElementById('generate-race').addEventListener('click', () => settings.generateGame(true)); + document.getElementById('generate-game').addEventListener('click', () => settings.generateGame()); // Name input field - const weightedSettings = JSON.parse(localStorage.getItem('weighted-settings')); const nameInput = document.getElementById('player-name'); nameInput.setAttribute('data-type', 'data'); nameInput.setAttribute('data-setting', 'name'); - nameInput.addEventListener('keyup', updateBaseSetting); - nameInput.value = weightedSettings.name; + nameInput.addEventListener('keyup', (evt) => settings.updateBaseSetting(evt)); + nameInput.value = settings.current.name; }); }); @@ -50,48 +49,65 @@ const fetchSettingData = () => new Promise((resolve, reject) => { }); }); -const createDefaultSettings = (settingData) => { - if (!localStorage.getItem('weighted-settings')) { - const newSettings = {}; +/// The weighted settings across all games. +class WeightedSettings { + // The data from the server describing the types of settings available for + // each game, as a JSON-safe blob. + data; + + // The settings chosen by the user as they'd appear in the YAML file, stored + // to and retrieved from local storage. + current; + + // A record mapping game names to the associated GameSettings. + games; + + constructor(data) { + this.data = data; + this.current = JSON.parse(localStorage.getItem('weighted-settings')); + this.games = Object.keys(this.data.games).map((game) => new GameSettings(this, game)); + if (this.current) { return; } + + this.current = {}; // Transfer base options directly - for (let baseOption of Object.keys(settingData.baseOptions)){ - newSettings[baseOption] = settingData.baseOptions[baseOption]; + for (let baseOption of Object.keys(this.data.baseOptions)){ + this.current[baseOption] = this.data.baseOptions[baseOption]; } // Set options per game - for (let game of Object.keys(settingData.games)) { + for (let game of Object.keys(this.data.games)) { // Initialize game object - newSettings[game] = {}; + this.current[game] = {}; // Transfer game settings - for (let gameSetting of Object.keys(settingData.games[game].gameSettings)){ - newSettings[game][gameSetting] = {}; + for (let gameSetting of Object.keys(this.data.games[game].gameSettings)){ + this.current[game][gameSetting] = {}; - const setting = settingData.games[game].gameSettings[gameSetting]; + const setting = this.data.games[game].gameSettings[gameSetting]; switch(setting.type){ case 'select': setting.options.forEach((option) => { - newSettings[game][gameSetting][option.value] = + this.current[game][gameSetting][option.value] = (setting.hasOwnProperty('defaultValue') && setting.defaultValue === option.value) ? 25 : 0; }); break; case 'range': case 'special_range': - newSettings[game][gameSetting]['random'] = 0; - newSettings[game][gameSetting]['random-low'] = 0; - newSettings[game][gameSetting]['random-high'] = 0; + this.current[game][gameSetting]['random'] = 0; + this.current[game][gameSetting]['random-low'] = 0; + this.current[game][gameSetting]['random-high'] = 0; if (setting.hasOwnProperty('defaultValue')) { - newSettings[game][gameSetting][setting.defaultValue] = 25; + this.current[game][gameSetting][setting.defaultValue] = 25; } else { - newSettings[game][gameSetting][setting.min] = 25; + this.current[game][gameSetting][setting.min] = 25; } break; case 'items-list': case 'locations-list': case 'custom-list': - newSettings[game][gameSetting] = setting.defaultValue; + this.current[game][gameSetting] = setting.defaultValue; break; default: @@ -99,33 +115,301 @@ const createDefaultSettings = (settingData) => { } } - newSettings[game].start_inventory = {}; - newSettings[game].exclude_locations = []; - newSettings[game].priority_locations = []; - newSettings[game].local_items = []; - newSettings[game].non_local_items = []; - newSettings[game].start_hints = []; - newSettings[game].start_location_hints = []; + this.current[game].start_inventory = {}; + this.current[game].exclude_locations = []; + this.current[game].priority_locations = []; + this.current[game].local_items = []; + this.current[game].non_local_items = []; + this.current[game].start_hints = []; + this.current[game].start_location_hints = []; } - localStorage.setItem('weighted-settings', JSON.stringify(newSettings)); + this.save(); } -}; -const buildUI = (settingData) => { - // Build the game-choice div - buildGameChoice(settingData.games); + // Saves the current settings to local storage. + save() { + localStorage.setItem('weighted-settings', JSON.stringify(this.current)); + } - const gamesWrapper = document.getElementById('games-wrapper'); - Object.keys(settingData.games).forEach((game) => { + buildUI() { + // Build the game-choice div + this.#buildGameChoice(); + + const gamesWrapper = document.getElementById('games-wrapper'); + this.games.forEach((game) => { + gamesWrapper.appendChild(game.buildUI()); + }); + } + + #buildGameChoice() { + const gameChoiceDiv = document.getElementById('game-choice'); + const h2 = document.createElement('h2'); + h2.innerText = 'Game Select'; + gameChoiceDiv.appendChild(h2); + + const gameSelectDescription = document.createElement('p'); + gameSelectDescription.classList.add('setting-description'); + gameSelectDescription.innerText = 'Choose which games you might be required to play.'; + gameChoiceDiv.appendChild(gameSelectDescription); + + const hintText = document.createElement('p'); + hintText.classList.add('hint-text'); + hintText.innerText = 'If a game\'s value is greater than zero, you can click it\'s name to jump ' + + 'to that section.' + gameChoiceDiv.appendChild(hintText); + + // Build the game choice table + const table = document.createElement('table'); + const tbody = document.createElement('tbody'); + + Object.keys(this.data.games).forEach((game) => { + const tr = document.createElement('tr'); + const tdLeft = document.createElement('td'); + tdLeft.classList.add('td-left'); + const span = document.createElement('span'); + span.innerText = game; + span.setAttribute('id', `${game}-game-option`) + tdLeft.appendChild(span); + tr.appendChild(tdLeft); + + const tdMiddle = document.createElement('td'); + tdMiddle.classList.add('td-middle'); + const range = document.createElement('input'); + range.setAttribute('type', 'range'); + range.setAttribute('min', 0); + range.setAttribute('max', 50); + range.setAttribute('data-type', 'weight'); + range.setAttribute('data-setting', 'game'); + range.setAttribute('data-option', game); + range.value = this.current.game[game]; + range.addEventListener('change', (evt) => { + this.updateBaseSetting(evt); + this.updateVisibleGames(); // Show or hide games based on the new settings + }); + tdMiddle.appendChild(range); + tr.appendChild(tdMiddle); + + const tdRight = document.createElement('td'); + tdRight.setAttribute('id', `game-${game}`) + tdRight.classList.add('td-right'); + tdRight.innerText = range.value; + tr.appendChild(tdRight); + tbody.appendChild(tr); + }); + + table.appendChild(tbody); + gameChoiceDiv.appendChild(table); + } + + // Verifies that `this.settings` meets all the requirements for world + // generation, normalizes it for serialization, and returns the result. + #validateSettings() { + const settings = structuredClone(this.current); + const userMessage = document.getElementById('user-message'); + let errorMessage = null; + + // User must choose a name for their file + if (!settings.name || settings.name.trim().length === 0 || settings.name.toLowerCase().trim() === 'player') { + userMessage.innerText = 'You forgot to set your player name at the top of the page!'; + userMessage.classList.add('visible'); + userMessage.scrollIntoView({ + behavior: 'smooth', + block: 'start', + }); + return; + } + + // Clean up the settings output + Object.keys(settings.game).forEach((game) => { + // Remove any disabled games + if (settings.game[game] === 0) { + delete settings.game[game]; + delete settings[game]; + return; + } + + Object.keys(settings[game]).forEach((setting) => { + // Remove any disabled options + Object.keys(settings[game][setting]).forEach((option) => { + if (settings[game][setting][option] === 0) { + delete settings[game][setting][option]; + } + }); + + if ( + Object.keys(settings[game][setting]).length === 0 && + !Array.isArray(settings[game][setting]) && + setting !== 'start_inventory' + ) { + errorMessage = `${game} // ${setting} has no values above zero!`; + } + + // Remove weights from options with only one possibility + if ( + Object.keys(settings[game][setting]).length === 1 && + !Array.isArray(settings[game][setting]) && + setting !== 'start_inventory' + ) { + settings[game][setting] = Object.keys(settings[game][setting])[0]; + } + + // Remove empty arrays + else if ( + ['exclude_locations', 'priority_locations', 'local_items', + 'non_local_items', 'start_hints', 'start_location_hints'].includes(setting) && + settings[game][setting].length === 0 + ) { + delete settings[game][setting]; + } + + // Remove empty start inventory + else if ( + setting === 'start_inventory' && + Object.keys(settings[game]['start_inventory']).length === 0 + ) { + delete settings[game]['start_inventory']; + } + }); + }); + + if (Object.keys(settings.game).length === 0) { + errorMessage = 'You have not chosen a game to play!'; + } + + // Remove weights if there is only one game + else if (Object.keys(settings.game).length === 1) { + settings.game = Object.keys(settings.game)[0]; + } + + // If an error occurred, alert the user and do not export the file + if (errorMessage) { + userMessage.innerText = errorMessage; + userMessage.classList.add('visible'); + userMessage.scrollIntoView({ + behavior: 'smooth', + block: 'start', + }); + return; + } + + // If no error occurred, hide the user message if it is visible + userMessage.classList.remove('visible'); + return settings; + } + + updateVisibleGames() { + Object.entries(this.current.game).forEach(([game, weight]) => { + const gameDiv = document.getElementById(`${game}-div`); + const gameOption = document.getElementById(`${game}-game-option`); + if (parseInt(weight, 10) > 0) { + gameDiv.classList.remove('invisible'); + gameOption.classList.add('jump-link'); + gameOption.addEventListener('click', () => { + const gameDiv = document.getElementById(`${game}-div`); + if (gameDiv.classList.contains('invisible')) { return; } + gameDiv.scrollIntoView({ + behavior: 'smooth', + block: 'start', + }); + }); + } else { + gameDiv.classList.add('invisible'); + gameOption.classList.remove('jump-link'); + } + }); + } + + updateBaseSetting(event) { + const setting = event.target.getAttribute('data-setting'); + const option = event.target.getAttribute('data-option'); + const type = event.target.getAttribute('data-type'); + + switch(type){ + case 'weight': + this.current[setting][option] = isNaN(event.target.value) ? event.target.value : parseInt(event.target.value, 10); + document.getElementById(`${setting}-${option}`).innerText = event.target.value; + break; + case 'data': + this.current[setting] = isNaN(event.target.value) ? event.target.value : parseInt(event.target.value, 10); + break; + } + + this.save(); + } + + export() { + const settings = this.#validateSettings(); + if (!settings) { return; } + + const yamlText = jsyaml.safeDump(settings, { noCompatMode: true }).replaceAll(/'(\d+)':/g, (x, y) => `${y}:`); + download(`${document.getElementById('player-name').value}.yaml`, yamlText); + } + + generateGame(raceMode = false) { + const settings = this.#validateSettings(); + if (!settings) { return; } + + axios.post('/api/generate', { + weights: { player: JSON.stringify(settings) }, + presetData: { player: JSON.stringify(settings) }, + playerCount: 1, + spoiler: 3, + race: raceMode ? '1' : '0', + }).then((response) => { + window.location.href = response.data.url; + }).catch((error) => { + const userMessage = document.getElementById('user-message'); + userMessage.innerText = 'Something went wrong and your game could not be generated.'; + if (error.response.data.text) { + userMessage.innerText += ' ' + error.response.data.text; + } + userMessage.classList.add('visible'); + userMessage.scrollIntoView({ + behavior: 'smooth', + block: 'start', + }); + console.error(error); + }); + } +} + +// Settings for an individual game. +class GameSettings { + // The WeightedSettings that contains this game's settings. Used to save + // settings after editing. + #allSettings; + + // The name of this game. + name; + + // The data from the server describing the types of settings available for + // this game, as a JSON-safe blob. + get data() { + return this.#allSettings.data.games[this.name]; + } + + // The settings chosen by the user as they'd appear in the YAML file, stored + // to and retrieved from local storage. + get current() { + return this.#allSettings.current[this.name]; + } + + constructor(allSettings, name) { + this.#allSettings = allSettings; + this.name = name; + } + + // Builds and returns the settings UI for this game. + buildUI() { // Create game div, invisible by default const gameDiv = document.createElement('div'); - gameDiv.setAttribute('id', `${game}-div`); + gameDiv.setAttribute('id', `${this.name}-div`); gameDiv.classList.add('game-div'); gameDiv.classList.add('invisible'); const gameHeader = document.createElement('h2'); - gameHeader.innerText = game; + gameHeader.innerText = this.name; gameDiv.appendChild(gameHeader); const collapseButton = document.createElement('a'); @@ -137,24 +421,22 @@ const buildUI = (settingData) => { expandButton.classList.add('invisible'); gameDiv.appendChild(expandButton); - settingData.games[game].gameItems.sort((a, b) => (a > b ? 1 : (a < b ? -1 : 0))); - settingData.games[game].gameLocations.sort((a, b) => (a > b ? 1 : (a < b ? -1 : 0))); + // Sort items and locations alphabetically. + this.data.gameItems.sort(); + this.data.gameLocations.sort(); - const weightedSettingsDiv = buildWeightedSettingsDiv(game, settingData.games[game].gameSettings, - settingData.games[game].gameItems, settingData.games[game].gameLocations); + const weightedSettingsDiv = this.#buildWeightedSettingsDiv(); gameDiv.appendChild(weightedSettingsDiv); - const itemPoolDiv = buildItemsDiv(game, settingData.games[game].gameItems); + const itemPoolDiv = this.#buildItemsDiv(); gameDiv.appendChild(itemPoolDiv); - const hintsDiv = buildHintsDiv(game, settingData.games[game].gameItems, settingData.games[game].gameLocations); + const hintsDiv = this.#buildHintsDiv(); gameDiv.appendChild(hintsDiv); - const locationsDiv = buildLocationsDiv(game, settingData.games[game].gameLocations); + const locationsDiv = this.#buildLocationsDiv(); gameDiv.appendChild(locationsDiv); - gamesWrapper.appendChild(gameDiv); - collapseButton.addEventListener('click', () => { collapseButton.classList.add('invisible'); weightedSettingsDiv.classList.add('invisible'); @@ -172,257 +454,145 @@ const buildUI = (settingData) => { locationsDiv.classList.remove('invisible'); expandButton.classList.add('invisible'); }); - }); -}; -const buildGameChoice = (games) => { - const settings = JSON.parse(localStorage.getItem('weighted-settings')); - const gameChoiceDiv = document.getElementById('game-choice'); - const h2 = document.createElement('h2'); - h2.innerText = 'Game Select'; - gameChoiceDiv.appendChild(h2); + return gameDiv; + } - const gameSelectDescription = document.createElement('p'); - gameSelectDescription.classList.add('setting-description'); - gameSelectDescription.innerText = 'Choose which games you might be required to play.'; - gameChoiceDiv.appendChild(gameSelectDescription); + #buildWeightedSettingsDiv() { + const settingsWrapper = document.createElement('div'); + settingsWrapper.classList.add('settings-wrapper'); - const hintText = document.createElement('p'); - hintText.classList.add('hint-text'); - hintText.innerText = 'If a game\'s value is greater than zero, you can click it\'s name to jump ' + - 'to that section.' - gameChoiceDiv.appendChild(hintText); + Object.keys(this.data.gameSettings).forEach((settingName) => { + const setting = this.data.gameSettings[settingName]; + const settingWrapper = document.createElement('div'); + settingWrapper.classList.add('setting-wrapper'); - // Build the game choice table - const table = document.createElement('table'); - const tbody = document.createElement('tbody'); + const settingNameHeader = document.createElement('h4'); + settingNameHeader.innerText = setting.displayName; + settingWrapper.appendChild(settingNameHeader); - Object.keys(games).forEach((game) => { - const tr = document.createElement('tr'); - const tdLeft = document.createElement('td'); - tdLeft.classList.add('td-left'); - const span = document.createElement('span'); - span.innerText = game; - span.setAttribute('id', `${game}-game-option`) - tdLeft.appendChild(span); - tr.appendChild(tdLeft); + const settingDescription = document.createElement('p'); + settingDescription.classList.add('setting-description'); + settingDescription.innerText = setting.description.replace(/(\n)/g, ' '); + settingWrapper.appendChild(settingDescription); - const tdMiddle = document.createElement('td'); - tdMiddle.classList.add('td-middle'); - const range = document.createElement('input'); - range.setAttribute('type', 'range'); - range.setAttribute('min', 0); - range.setAttribute('max', 50); - range.setAttribute('data-type', 'weight'); - range.setAttribute('data-setting', 'game'); - range.setAttribute('data-option', game); - range.value = settings.game[game]; - range.addEventListener('change', (evt) => { - updateBaseSetting(evt); - updateVisibleGames(); // Show or hide games based on the new settings - }); - tdMiddle.appendChild(range); - tr.appendChild(tdMiddle); + switch(setting.type){ + case 'select': + const optionTable = document.createElement('table'); + const tbody = document.createElement('tbody'); - const tdRight = document.createElement('td'); - tdRight.setAttribute('id', `game-${game}`) - tdRight.classList.add('td-right'); - tdRight.innerText = range.value; - tr.appendChild(tdRight); - tbody.appendChild(tr); - }); - - table.appendChild(tbody); - gameChoiceDiv.appendChild(table); -}; - -const buildWeightedSettingsDiv = (game, settings, gameItems, gameLocations) => { - const currentSettings = JSON.parse(localStorage.getItem('weighted-settings')); - const settingsWrapper = document.createElement('div'); - settingsWrapper.classList.add('settings-wrapper'); - - Object.keys(settings).forEach((settingName) => { - const setting = settings[settingName]; - const settingWrapper = document.createElement('div'); - settingWrapper.classList.add('setting-wrapper'); - - const settingNameHeader = document.createElement('h4'); - settingNameHeader.innerText = setting.displayName; - settingWrapper.appendChild(settingNameHeader); - - const settingDescription = document.createElement('p'); - settingDescription.classList.add('setting-description'); - settingDescription.innerText = setting.description.replace(/(\n)/g, ' '); - settingWrapper.appendChild(settingDescription); - - switch(setting.type){ - case 'select': - const optionTable = document.createElement('table'); - const tbody = document.createElement('tbody'); - - // Add a weight range for each option - setting.options.forEach((option) => { - const tr = document.createElement('tr'); - const tdLeft = document.createElement('td'); - tdLeft.classList.add('td-left'); - tdLeft.innerText = option.name; - tr.appendChild(tdLeft); - - const tdMiddle = document.createElement('td'); - tdMiddle.classList.add('td-middle'); - const range = document.createElement('input'); - range.setAttribute('type', 'range'); - range.setAttribute('data-game', game); - range.setAttribute('data-setting', settingName); - range.setAttribute('data-option', option.value); - range.setAttribute('data-type', setting.type); - range.setAttribute('min', 0); - range.setAttribute('max', 50); - range.addEventListener('change', updateRangeSetting); - range.value = currentSettings[game][settingName][option.value]; - tdMiddle.appendChild(range); - tr.appendChild(tdMiddle); - - const tdRight = document.createElement('td'); - tdRight.setAttribute('id', `${game}-${settingName}-${option.value}`) - tdRight.classList.add('td-right'); - tdRight.innerText = range.value; - tr.appendChild(tdRight); - - tbody.appendChild(tr); - }); - - optionTable.appendChild(tbody); - settingWrapper.appendChild(optionTable); - break; - - case 'range': - case 'special_range': - const rangeTable = document.createElement('table'); - const rangeTbody = document.createElement('tbody'); - - if (((setting.max - setting.min) + 1) < 11) { - for (let i=setting.min; i <= setting.max; ++i) { + // Add a weight range for each option + setting.options.forEach((option) => { const tr = document.createElement('tr'); const tdLeft = document.createElement('td'); tdLeft.classList.add('td-left'); - tdLeft.innerText = i; + tdLeft.innerText = option.name; tr.appendChild(tdLeft); const tdMiddle = document.createElement('td'); tdMiddle.classList.add('td-middle'); const range = document.createElement('input'); range.setAttribute('type', 'range'); - range.setAttribute('id', `${game}-${settingName}-${i}-range`); - range.setAttribute('data-game', game); + range.setAttribute('data-game', this.name); range.setAttribute('data-setting', settingName); - range.setAttribute('data-option', i); + range.setAttribute('data-option', option.value); + range.setAttribute('data-type', setting.type); range.setAttribute('min', 0); range.setAttribute('max', 50); - range.addEventListener('change', updateRangeSetting); - range.value = currentSettings[game][settingName][i] || 0; + range.addEventListener('change', (evt) => this.#updateRangeSetting(evt)); + range.value = this.current[settingName][option.value]; tdMiddle.appendChild(range); tr.appendChild(tdMiddle); const tdRight = document.createElement('td'); - tdRight.setAttribute('id', `${game}-${settingName}-${i}`) + tdRight.setAttribute('id', `${this.name}-${settingName}-${option.value}`); tdRight.classList.add('td-right'); tdRight.innerText = range.value; tr.appendChild(tdRight); - rangeTbody.appendChild(tr); - } - } else { - const hintText = document.createElement('p'); - hintText.classList.add('hint-text'); - hintText.innerHTML = 'This is a range option. You may enter a valid numerical value in the text box ' + - `below, then press the "Add" button to add a weight for it.
    Minimum value: ${setting.min}
    ` + - `Maximum value: ${setting.max}`; - - if (setting.hasOwnProperty('value_names')) { - hintText.innerHTML += '

    Certain values have special meaning:'; - Object.keys(setting.value_names).forEach((specialName) => { - hintText.innerHTML += `
    ${specialName}: ${setting.value_names[specialName]}`; - }); - } - - settingWrapper.appendChild(hintText); - - const addOptionDiv = document.createElement('div'); - addOptionDiv.classList.add('add-option-div'); - const optionInput = document.createElement('input'); - optionInput.setAttribute('id', `${game}-${settingName}-option`); - optionInput.setAttribute('placeholder', `${setting.min} - ${setting.max}`); - addOptionDiv.appendChild(optionInput); - const addOptionButton = document.createElement('button'); - addOptionButton.innerText = 'Add'; - addOptionDiv.appendChild(addOptionButton); - settingWrapper.appendChild(addOptionDiv); - optionInput.addEventListener('keydown', (evt) => { - if (evt.key === 'Enter') { addOptionButton.dispatchEvent(new Event('click')); } + tbody.appendChild(tr); }); - addOptionButton.addEventListener('click', () => { - const optionInput = document.getElementById(`${game}-${settingName}-option`); - let option = optionInput.value; - if (!option || !option.trim()) { return; } - option = parseInt(option, 10); - if ((option < setting.min) || (option > setting.max)) { return; } - optionInput.value = ''; - if (document.getElementById(`${game}-${settingName}-${option}-range`)) { return; } + optionTable.appendChild(tbody); + settingWrapper.appendChild(optionTable); + break; - const tr = document.createElement('tr'); - const tdLeft = document.createElement('td'); - tdLeft.classList.add('td-left'); - tdLeft.innerText = option; - tr.appendChild(tdLeft); + case 'range': + case 'special_range': + const rangeTable = document.createElement('table'); + const rangeTbody = document.createElement('tbody'); - const tdMiddle = document.createElement('td'); - tdMiddle.classList.add('td-middle'); - const range = document.createElement('input'); - range.setAttribute('type', 'range'); - range.setAttribute('id', `${game}-${settingName}-${option}-range`); - range.setAttribute('data-game', game); - range.setAttribute('data-setting', settingName); - range.setAttribute('data-option', option); - range.setAttribute('min', 0); - range.setAttribute('max', 50); - range.addEventListener('change', updateRangeSetting); - range.value = currentSettings[game][settingName][parseInt(option, 10)]; - tdMiddle.appendChild(range); - tr.appendChild(tdMiddle); + if (((setting.max - setting.min) + 1) < 11) { + for (let i=setting.min; i <= setting.max; ++i) { + const tr = document.createElement('tr'); + const tdLeft = document.createElement('td'); + tdLeft.classList.add('td-left'); + tdLeft.innerText = i; + tr.appendChild(tdLeft); - const tdRight = document.createElement('td'); - tdRight.setAttribute('id', `${game}-${settingName}-${option}`) - tdRight.classList.add('td-right'); - tdRight.innerText = range.value; - tr.appendChild(tdRight); + const tdMiddle = document.createElement('td'); + tdMiddle.classList.add('td-middle'); + const range = document.createElement('input'); + range.setAttribute('type', 'range'); + range.setAttribute('id', `${this.name}-${settingName}-${i}-range`); + range.setAttribute('data-game', this.name); + range.setAttribute('data-setting', settingName); + range.setAttribute('data-option', i); + range.setAttribute('min', 0); + range.setAttribute('max', 50); + range.addEventListener('change', (evt) => this.#updateRangeSetting(evt)); + range.value = this.current[settingName][i] || 0; + tdMiddle.appendChild(range); + tr.appendChild(tdMiddle); - const tdDelete = document.createElement('td'); - tdDelete.classList.add('td-delete'); - const deleteButton = document.createElement('span'); - deleteButton.classList.add('range-option-delete'); - deleteButton.innerText = '❌'; - deleteButton.addEventListener('click', () => { - range.value = 0; - range.dispatchEvent(new Event('change')); - rangeTbody.removeChild(tr); + const tdRight = document.createElement('td'); + tdRight.setAttribute('id', `${this.name}-${settingName}-${i}`) + tdRight.classList.add('td-right'); + tdRight.innerText = range.value; + tr.appendChild(tdRight); + + rangeTbody.appendChild(tr); + } + } else { + const hintText = document.createElement('p'); + hintText.classList.add('hint-text'); + hintText.innerHTML = 'This is a range option. You may enter a valid numerical value in the text box ' + + `below, then press the "Add" button to add a weight for it.
    Minimum value: ${setting.min}
    ` + + `Maximum value: ${setting.max}`; + + if (setting.hasOwnProperty('value_names')) { + hintText.innerHTML += '

    Certain values have special meaning:'; + Object.keys(setting.value_names).forEach((specialName) => { + hintText.innerHTML += `
    ${specialName}: ${setting.value_names[specialName]}`; + }); + } + + settingWrapper.appendChild(hintText); + + const addOptionDiv = document.createElement('div'); + addOptionDiv.classList.add('add-option-div'); + const optionInput = document.createElement('input'); + optionInput.setAttribute('id', `${this.name}-${settingName}-option`); + optionInput.setAttribute('placeholder', `${setting.min} - ${setting.max}`); + addOptionDiv.appendChild(optionInput); + const addOptionButton = document.createElement('button'); + addOptionButton.innerText = 'Add'; + addOptionDiv.appendChild(addOptionButton); + settingWrapper.appendChild(addOptionDiv); + optionInput.addEventListener('keydown', (evt) => { + if (evt.key === 'Enter') { addOptionButton.dispatchEvent(new Event('click')); } }); - tdDelete.appendChild(deleteButton); - tr.appendChild(tdDelete); - rangeTbody.appendChild(tr); + addOptionButton.addEventListener('click', () => { + const optionInput = document.getElementById(`${this.name}-${settingName}-option`); + let option = optionInput.value; + if (!option || !option.trim()) { return; } + option = parseInt(option, 10); + if ((option < setting.min) || (option > setting.max)) { return; } + optionInput.value = ''; + if (document.getElementById(`${this.name}-${settingName}-${option}-range`)) { return; } - // Save new option to settings - range.dispatchEvent(new Event('change')); - }); - - Object.keys(currentSettings[game][settingName]).forEach((option) => { - // These options are statically generated below, and should always appear even if they are deleted - // from localStorage - if (['random-low', 'random', 'random-high'].includes(option)) { return; } - - const tr = document.createElement('tr'); + const tr = document.createElement('tr'); const tdLeft = document.createElement('td'); tdLeft.classList.add('td-left'); tdLeft.innerText = option; @@ -432,19 +602,19 @@ const buildWeightedSettingsDiv = (game, settings, gameItems, gameLocations) => { tdMiddle.classList.add('td-middle'); const range = document.createElement('input'); range.setAttribute('type', 'range'); - range.setAttribute('id', `${game}-${settingName}-${option}-range`); - range.setAttribute('data-game', game); + range.setAttribute('id', `${this.name}-${settingName}-${option}-range`); + range.setAttribute('data-game', this.name); range.setAttribute('data-setting', settingName); range.setAttribute('data-option', option); range.setAttribute('min', 0); range.setAttribute('max', 50); - range.addEventListener('change', updateRangeSetting); - range.value = currentSettings[game][settingName][parseInt(option, 10)]; + range.addEventListener('change', (evt) => this.#updateRangeSetting(evt)); + range.value = this.current[settingName][parseInt(option, 10)]; tdMiddle.appendChild(range); tr.appendChild(tdMiddle); const tdRight = document.createElement('td'); - tdRight.setAttribute('id', `${game}-${settingName}-${option}`) + tdRight.setAttribute('id', `${this.name}-${settingName}-${option}`) tdRight.classList.add('td-right'); tdRight.innerText = range.value; tr.appendChild(tdRight); @@ -456,762 +626,651 @@ const buildWeightedSettingsDiv = (game, settings, gameItems, gameLocations) => { deleteButton.innerText = '❌'; deleteButton.addEventListener('click', () => { range.value = 0; - const changeEvent = new Event('change'); - changeEvent.action = 'rangeDelete'; - range.dispatchEvent(changeEvent); + range.dispatchEvent(new Event('change')); rangeTbody.removeChild(tr); }); tdDelete.appendChild(deleteButton); tr.appendChild(tdDelete); rangeTbody.appendChild(tr); + + // Save new option to settings + range.dispatchEvent(new Event('change')); + }); + + Object.keys(this.current[settingName]).forEach((option) => { + // These options are statically generated below, and should always appear even if they are deleted + // from localStorage + if (['random-low', 'random', 'random-high'].includes(option)) { return; } + + const tr = document.createElement('tr'); + const tdLeft = document.createElement('td'); + tdLeft.classList.add('td-left'); + tdLeft.innerText = option; + tr.appendChild(tdLeft); + + const tdMiddle = document.createElement('td'); + tdMiddle.classList.add('td-middle'); + const range = document.createElement('input'); + range.setAttribute('type', 'range'); + range.setAttribute('id', `${this.name}-${settingName}-${option}-range`); + range.setAttribute('data-game', this.name); + range.setAttribute('data-setting', settingName); + range.setAttribute('data-option', option); + range.setAttribute('min', 0); + range.setAttribute('max', 50); + range.addEventListener('change', (evt) => this.#updateRangeSetting(evt)); + range.value = this.current[settingName][parseInt(option, 10)]; + tdMiddle.appendChild(range); + tr.appendChild(tdMiddle); + + const tdRight = document.createElement('td'); + tdRight.setAttribute('id', `${this.name}-${settingName}-${option}`) + tdRight.classList.add('td-right'); + tdRight.innerText = range.value; + tr.appendChild(tdRight); + + const tdDelete = document.createElement('td'); + tdDelete.classList.add('td-delete'); + const deleteButton = document.createElement('span'); + deleteButton.classList.add('range-option-delete'); + deleteButton.innerText = '❌'; + deleteButton.addEventListener('click', () => { + range.value = 0; + const changeEvent = new Event('change'); + changeEvent.action = 'rangeDelete'; + range.dispatchEvent(changeEvent); + rangeTbody.removeChild(tr); + }); + tdDelete.appendChild(deleteButton); + tr.appendChild(tdDelete); + + rangeTbody.appendChild(tr); + }); + } + + ['random', 'random-low', 'random-high'].forEach((option) => { + const tr = document.createElement('tr'); + const tdLeft = document.createElement('td'); + tdLeft.classList.add('td-left'); + switch(option){ + case 'random': + tdLeft.innerText = 'Random'; + break; + case 'random-low': + tdLeft.innerText = "Random (Low)"; + break; + case 'random-high': + tdLeft.innerText = "Random (High)"; + break; + } + tr.appendChild(tdLeft); + + const tdMiddle = document.createElement('td'); + tdMiddle.classList.add('td-middle'); + const range = document.createElement('input'); + range.setAttribute('type', 'range'); + range.setAttribute('id', `${this.name}-${settingName}-${option}-range`); + range.setAttribute('data-game', this.name); + range.setAttribute('data-setting', settingName); + range.setAttribute('data-option', option); + range.setAttribute('min', 0); + range.setAttribute('max', 50); + range.addEventListener('change', (evt) => this.#updateRangeSetting(evt)); + range.value = this.current[settingName][option]; + tdMiddle.appendChild(range); + tr.appendChild(tdMiddle); + + const tdRight = document.createElement('td'); + tdRight.setAttribute('id', `${this.name}-${settingName}-${option}`) + tdRight.classList.add('td-right'); + tdRight.innerText = range.value; + tr.appendChild(tdRight); + rangeTbody.appendChild(tr); }); - } - ['random', 'random-low', 'random-high'].forEach((option) => { - const tr = document.createElement('tr'); - const tdLeft = document.createElement('td'); - tdLeft.classList.add('td-left'); - switch(option){ - case 'random': - tdLeft.innerText = 'Random'; - break; - case 'random-low': - tdLeft.innerText = "Random (Low)"; - break; - case 'random-high': - tdLeft.innerText = "Random (High)"; - break; + rangeTable.appendChild(rangeTbody); + settingWrapper.appendChild(rangeTable); + break; + + case 'items-list': + const itemsList = document.createElement('div'); + itemsList.classList.add('simple-list'); + + Object.values(this.data.gameItems).forEach((item) => { + const itemRow = document.createElement('div'); + itemRow.classList.add('list-row'); + + const itemLabel = document.createElement('label'); + itemLabel.setAttribute('for', `${this.name}-${settingName}-${item}`) + + const itemCheckbox = document.createElement('input'); + itemCheckbox.setAttribute('id', `${this.name}-${settingName}-${item}`); + itemCheckbox.setAttribute('type', 'checkbox'); + itemCheckbox.setAttribute('data-game', this.name); + itemCheckbox.setAttribute('data-setting', settingName); + itemCheckbox.setAttribute('data-option', item.toString()); + itemCheckbox.addEventListener('change', (evt) => this.#updateListSetting(evt)); + if (this.current[settingName].includes(item)) { + itemCheckbox.setAttribute('checked', '1'); } - tr.appendChild(tdLeft); - const tdMiddle = document.createElement('td'); - tdMiddle.classList.add('td-middle'); - const range = document.createElement('input'); - range.setAttribute('type', 'range'); - range.setAttribute('id', `${game}-${settingName}-${option}-range`); - range.setAttribute('data-game', game); - range.setAttribute('data-setting', settingName); - range.setAttribute('data-option', option); - range.setAttribute('min', 0); - range.setAttribute('max', 50); - range.addEventListener('change', updateRangeSetting); - range.value = currentSettings[game][settingName][option]; - tdMiddle.appendChild(range); - tr.appendChild(tdMiddle); + const itemName = document.createElement('span'); + itemName.innerText = item.toString(); - const tdRight = document.createElement('td'); - tdRight.setAttribute('id', `${game}-${settingName}-${option}`) - tdRight.classList.add('td-right'); - tdRight.innerText = range.value; - tr.appendChild(tdRight); - rangeTbody.appendChild(tr); - }); + itemLabel.appendChild(itemCheckbox); + itemLabel.appendChild(itemName); - rangeTable.appendChild(rangeTbody); - settingWrapper.appendChild(rangeTable); - break; + itemRow.appendChild(itemLabel); + itemsList.appendChild((itemRow)); + }); - case 'items-list': - const itemsList = document.createElement('div'); - itemsList.classList.add('simple-list'); + settingWrapper.appendChild(itemsList); + break; - Object.values(gameItems).forEach((item) => { - const itemRow = document.createElement('div'); - itemRow.classList.add('list-row'); + case 'locations-list': + const locationsList = document.createElement('div'); + locationsList.classList.add('simple-list'); - const itemLabel = document.createElement('label'); - itemLabel.setAttribute('for', `${game}-${settingName}-${item}`) + Object.values(this.data.gameLocations).forEach((location) => { + const locationRow = document.createElement('div'); + locationRow.classList.add('list-row'); - const itemCheckbox = document.createElement('input'); - itemCheckbox.setAttribute('id', `${game}-${settingName}-${item}`); - itemCheckbox.setAttribute('type', 'checkbox'); - itemCheckbox.setAttribute('data-game', game); - itemCheckbox.setAttribute('data-setting', settingName); - itemCheckbox.setAttribute('data-option', item.toString()); - itemCheckbox.addEventListener('change', updateListSetting); - if (currentSettings[game][settingName].includes(item)) { - itemCheckbox.setAttribute('checked', '1'); - } + const locationLabel = document.createElement('label'); + locationLabel.setAttribute('for', `${this.name}-${settingName}-${location}`) - const itemName = document.createElement('span'); - itemName.innerText = item.toString(); + const locationCheckbox = document.createElement('input'); + locationCheckbox.setAttribute('id', `${this.name}-${settingName}-${location}`); + locationCheckbox.setAttribute('type', 'checkbox'); + locationCheckbox.setAttribute('data-game', this.name); + locationCheckbox.setAttribute('data-setting', settingName); + locationCheckbox.setAttribute('data-option', location.toString()); + locationCheckbox.addEventListener('change', (evt) => this.#updateListSetting(evt)); + if (this.current[settingName].includes(location)) { + locationCheckbox.setAttribute('checked', '1'); + } - itemLabel.appendChild(itemCheckbox); - itemLabel.appendChild(itemName); + const locationName = document.createElement('span'); + locationName.innerText = location.toString(); - itemRow.appendChild(itemLabel); - itemsList.appendChild((itemRow)); - }); + locationLabel.appendChild(locationCheckbox); + locationLabel.appendChild(locationName); - settingWrapper.appendChild(itemsList); - break; + locationRow.appendChild(locationLabel); + locationsList.appendChild((locationRow)); + }); - case 'locations-list': - const locationsList = document.createElement('div'); - locationsList.classList.add('simple-list'); + settingWrapper.appendChild(locationsList); + break; - Object.values(gameLocations).forEach((location) => { - const locationRow = document.createElement('div'); - locationRow.classList.add('list-row'); + case 'custom-list': + const customList = document.createElement('div'); + customList.classList.add('simple-list'); - const locationLabel = document.createElement('label'); - locationLabel.setAttribute('for', `${game}-${settingName}-${location}`) + Object.values(this.data.gameSettings[settingName].options).forEach((listItem) => { + const customListRow = document.createElement('div'); + customListRow.classList.add('list-row'); - const locationCheckbox = document.createElement('input'); - locationCheckbox.setAttribute('id', `${game}-${settingName}-${location}`); - locationCheckbox.setAttribute('type', 'checkbox'); - locationCheckbox.setAttribute('data-game', game); - locationCheckbox.setAttribute('data-setting', settingName); - locationCheckbox.setAttribute('data-option', location.toString()); - locationCheckbox.addEventListener('change', updateListSetting); - if (currentSettings[game][settingName].includes(location)) { - locationCheckbox.setAttribute('checked', '1'); - } + const customItemLabel = document.createElement('label'); + customItemLabel.setAttribute('for', `${this.name}-${settingName}-${listItem}`) - const locationName = document.createElement('span'); - locationName.innerText = location.toString(); + const customItemCheckbox = document.createElement('input'); + customItemCheckbox.setAttribute('id', `${this.name}-${settingName}-${listItem}`); + customItemCheckbox.setAttribute('type', 'checkbox'); + customItemCheckbox.setAttribute('data-game', this.name); + customItemCheckbox.setAttribute('data-setting', settingName); + customItemCheckbox.setAttribute('data-option', listItem.toString()); + customItemCheckbox.addEventListener('change', (evt) => this.#updateListSetting(evt)); + if (this.current[settingName].includes(listItem)) { + customItemCheckbox.setAttribute('checked', '1'); + } - locationLabel.appendChild(locationCheckbox); - locationLabel.appendChild(locationName); + const customItemName = document.createElement('span'); + customItemName.innerText = listItem.toString(); - locationRow.appendChild(locationLabel); - locationsList.appendChild((locationRow)); - }); + customItemLabel.appendChild(customItemCheckbox); + customItemLabel.appendChild(customItemName); - settingWrapper.appendChild(locationsList); - break; + customListRow.appendChild(customItemLabel); + customList.appendChild((customListRow)); + }); - case 'custom-list': - const customList = document.createElement('div'); - customList.classList.add('simple-list'); + settingWrapper.appendChild(customList); + break; - Object.values(settings[settingName].options).forEach((listItem) => { - const customListRow = document.createElement('div'); - customListRow.classList.add('list-row'); - - const customItemLabel = document.createElement('label'); - customItemLabel.setAttribute('for', `${game}-${settingName}-${listItem}`) - - const customItemCheckbox = document.createElement('input'); - customItemCheckbox.setAttribute('id', `${game}-${settingName}-${listItem}`); - customItemCheckbox.setAttribute('type', 'checkbox'); - customItemCheckbox.setAttribute('data-game', game); - customItemCheckbox.setAttribute('data-setting', settingName); - customItemCheckbox.setAttribute('data-option', listItem.toString()); - customItemCheckbox.addEventListener('change', updateListSetting); - if (currentSettings[game][settingName].includes(listItem)) { - customItemCheckbox.setAttribute('checked', '1'); - } - - const customItemName = document.createElement('span'); - customItemName.innerText = listItem.toString(); - - customItemLabel.appendChild(customItemCheckbox); - customItemLabel.appendChild(customItemName); - - customListRow.appendChild(customItemLabel); - customList.appendChild((customListRow)); - }); - - settingWrapper.appendChild(customList); - break; - - default: - console.error(`Unknown setting type for ${game} setting ${settingName}: ${setting.type}`); - return; - } - - settingsWrapper.appendChild(settingWrapper); - }); - - return settingsWrapper; -}; - -const buildItemsDiv = (game, items) => { - // Sort alphabetical, in pace - items.sort(); - - const currentSettings = JSON.parse(localStorage.getItem('weighted-settings')); - const itemsDiv = document.createElement('div'); - itemsDiv.classList.add('items-div'); - - const itemsDivHeader = document.createElement('h3'); - itemsDivHeader.innerText = 'Item Pool'; - itemsDiv.appendChild(itemsDivHeader); - - const itemsDescription = document.createElement('p'); - itemsDescription.classList.add('setting-description'); - itemsDescription.innerText = 'Choose if you would like to start with items, or control if they are placed in ' + - 'your seed or someone else\'s.'; - itemsDiv.appendChild(itemsDescription); - - const itemsHint = document.createElement('p'); - itemsHint.classList.add('hint-text'); - itemsHint.innerText = 'Drag and drop items from one box to another.'; - itemsDiv.appendChild(itemsHint); - - const itemsWrapper = document.createElement('div'); - itemsWrapper.classList.add('items-wrapper'); - - // Create container divs for each category - const availableItemsWrapper = document.createElement('div'); - availableItemsWrapper.classList.add('item-set-wrapper'); - availableItemsWrapper.innerText = 'Available Items'; - const availableItems = document.createElement('div'); - availableItems.classList.add('item-container'); - availableItems.setAttribute('id', `${game}-available_items`); - availableItems.addEventListener('dragover', itemDragoverHandler); - availableItems.addEventListener('drop', itemDropHandler); - - const startInventoryWrapper = document.createElement('div'); - startInventoryWrapper.classList.add('item-set-wrapper'); - startInventoryWrapper.innerText = 'Start Inventory'; - const startInventory = document.createElement('div'); - startInventory.classList.add('item-container'); - startInventory.setAttribute('id', `${game}-start_inventory`); - startInventory.setAttribute('data-setting', 'start_inventory'); - startInventory.addEventListener('dragover', itemDragoverHandler); - startInventory.addEventListener('drop', itemDropHandler); - - const localItemsWrapper = document.createElement('div'); - localItemsWrapper.classList.add('item-set-wrapper'); - localItemsWrapper.innerText = 'Local Items'; - const localItems = document.createElement('div'); - localItems.classList.add('item-container'); - localItems.setAttribute('id', `${game}-local_items`); - localItems.setAttribute('data-setting', 'local_items') - localItems.addEventListener('dragover', itemDragoverHandler); - localItems.addEventListener('drop', itemDropHandler); - - const nonLocalItemsWrapper = document.createElement('div'); - nonLocalItemsWrapper.classList.add('item-set-wrapper'); - nonLocalItemsWrapper.innerText = 'Non-Local Items'; - const nonLocalItems = document.createElement('div'); - nonLocalItems.classList.add('item-container'); - nonLocalItems.setAttribute('id', `${game}-non_local_items`); - nonLocalItems.setAttribute('data-setting', 'non_local_items'); - nonLocalItems.addEventListener('dragover', itemDragoverHandler); - nonLocalItems.addEventListener('drop', itemDropHandler); - - // Populate the divs - items.forEach((item) => { - if (Object.keys(currentSettings[game].start_inventory).includes(item)){ - const itemDiv = buildItemQtyDiv(game, item); - itemDiv.setAttribute('data-setting', 'start_inventory'); - startInventory.appendChild(itemDiv); - } else if (currentSettings[game].local_items.includes(item)) { - const itemDiv = buildItemDiv(game, item); - itemDiv.setAttribute('data-setting', 'local_items'); - localItems.appendChild(itemDiv); - } else if (currentSettings[game].non_local_items.includes(item)) { - const itemDiv = buildItemDiv(game, item); - itemDiv.setAttribute('data-setting', 'non_local_items'); - nonLocalItems.appendChild(itemDiv); - } else { - const itemDiv = buildItemDiv(game, item); - availableItems.appendChild(itemDiv); - } - }); - - availableItemsWrapper.appendChild(availableItems); - startInventoryWrapper.appendChild(startInventory); - localItemsWrapper.appendChild(localItems); - nonLocalItemsWrapper.appendChild(nonLocalItems); - itemsWrapper.appendChild(availableItemsWrapper); - itemsWrapper.appendChild(startInventoryWrapper); - itemsWrapper.appendChild(localItemsWrapper); - itemsWrapper.appendChild(nonLocalItemsWrapper); - itemsDiv.appendChild(itemsWrapper); - return itemsDiv; -}; - -const buildItemDiv = (game, item) => { - const itemDiv = document.createElement('div'); - itemDiv.classList.add('item-div'); - itemDiv.setAttribute('id', `${game}-${item}`); - itemDiv.setAttribute('data-game', game); - itemDiv.setAttribute('data-item', item); - itemDiv.setAttribute('draggable', 'true'); - itemDiv.innerText = item; - itemDiv.addEventListener('dragstart', (evt) => { - evt.dataTransfer.setData('text/plain', itemDiv.getAttribute('id')); - }); - return itemDiv; -}; - -const buildItemQtyDiv = (game, item) => { - const currentSettings = JSON.parse(localStorage.getItem('weighted-settings')); - const itemQtyDiv = document.createElement('div'); - itemQtyDiv.classList.add('item-qty-div'); - itemQtyDiv.setAttribute('id', `${game}-${item}`); - itemQtyDiv.setAttribute('data-game', game); - itemQtyDiv.setAttribute('data-item', item); - itemQtyDiv.setAttribute('draggable', 'true'); - itemQtyDiv.innerText = item; - - const inputWrapper = document.createElement('div'); - inputWrapper.classList.add('item-qty-input-wrapper') - - const itemQty = document.createElement('input'); - itemQty.setAttribute('value', currentSettings[game].start_inventory.hasOwnProperty(item) ? - currentSettings[game].start_inventory[item] : '1'); - itemQty.setAttribute('data-game', game); - itemQty.setAttribute('data-setting', 'start_inventory'); - itemQty.setAttribute('data-option', item); - itemQty.setAttribute('maxlength', '3'); - itemQty.addEventListener('keyup', (evt) => { - evt.target.value = isNaN(parseInt(evt.target.value)) ? 0 : parseInt(evt.target.value); - updateItemSetting(evt); - }); - inputWrapper.appendChild(itemQty); - itemQtyDiv.appendChild(inputWrapper); - - itemQtyDiv.addEventListener('dragstart', (evt) => { - evt.dataTransfer.setData('text/plain', itemQtyDiv.getAttribute('id')); - }); - return itemQtyDiv; -}; - -const itemDragoverHandler = (evt) => { - evt.preventDefault(); -}; - -const itemDropHandler = (evt) => { - evt.preventDefault(); - const sourceId = evt.dataTransfer.getData('text/plain'); - const sourceDiv = document.getElementById(sourceId); - - const currentSettings = JSON.parse(localStorage.getItem('weighted-settings')); - const game = sourceDiv.getAttribute('data-game'); - const item = sourceDiv.getAttribute('data-item'); - - const oldSetting = sourceDiv.hasAttribute('data-setting') ? sourceDiv.getAttribute('data-setting') : null; - const newSetting = evt.target.hasAttribute('data-setting') ? evt.target.getAttribute('data-setting') : null; - - const itemDiv = newSetting === 'start_inventory' ? buildItemQtyDiv(game, item) : buildItemDiv(game, item); - - if (oldSetting) { - if (oldSetting === 'start_inventory') { - if (currentSettings[game][oldSetting].hasOwnProperty(item)) { - delete currentSettings[game][oldSetting][item]; + default: + console.error(`Unknown setting type for ${this.name} setting ${settingName}: ${setting.type}`); + return; } - } else { - if (currentSettings[game][oldSetting].includes(item)) { - currentSettings[game][oldSetting].splice(currentSettings[game][oldSetting].indexOf(item), 1); - } - } - } - if (newSetting) { - itemDiv.setAttribute('data-setting', newSetting); - document.getElementById(`${game}-${newSetting}`).appendChild(itemDiv); - if (newSetting === 'start_inventory') { - currentSettings[game][newSetting][item] = 1; - } else { - if (!currentSettings[game][newSetting].includes(item)){ - currentSettings[game][newSetting].push(item); - } - } - } else { - // No setting was assigned, this item has been removed from the settings - document.getElementById(`${game}-available_items`).appendChild(itemDiv); - } - - // Remove the source drag object - sourceDiv.parentElement.removeChild(sourceDiv); - - // Save the updated settings - localStorage.setItem('weighted-settings', JSON.stringify(currentSettings)); -}; - -const buildHintsDiv = (game, items, locations) => { - const currentSettings = JSON.parse(localStorage.getItem('weighted-settings')); - - // Sort alphabetical, in place - items.sort(); - locations.sort(); - - const hintsDiv = document.createElement('div'); - hintsDiv.classList.add('hints-div'); - const hintsHeader = document.createElement('h3'); - hintsHeader.innerText = 'Item & Location Hints'; - hintsDiv.appendChild(hintsHeader); - const hintsDescription = document.createElement('p'); - hintsDescription.classList.add('setting-description'); - hintsDescription.innerText = 'Choose any items or locations to begin the game with the knowledge of where those ' + - ' items are, or what those locations contain.'; - hintsDiv.appendChild(hintsDescription); - - const itemHintsContainer = document.createElement('div'); - itemHintsContainer.classList.add('hints-container'); - - // Item Hints - const itemHintsWrapper = document.createElement('div'); - itemHintsWrapper.classList.add('hints-wrapper'); - itemHintsWrapper.innerText = 'Starting Item Hints'; - - const itemHintsDiv = document.createElement('div'); - itemHintsDiv.classList.add('simple-list'); - items.forEach((item) => { - const itemRow = document.createElement('div'); - itemRow.classList.add('list-row'); - - const itemLabel = document.createElement('label'); - itemLabel.setAttribute('for', `${game}-start_hints-${item}`); - - const itemCheckbox = document.createElement('input'); - itemCheckbox.setAttribute('type', 'checkbox'); - itemCheckbox.setAttribute('id', `${game}-start_hints-${item}`); - itemCheckbox.setAttribute('data-game', game); - itemCheckbox.setAttribute('data-setting', 'start_hints'); - itemCheckbox.setAttribute('data-option', item); - if (currentSettings[game].start_hints.includes(item)) { - itemCheckbox.setAttribute('checked', 'true'); - } - itemCheckbox.addEventListener('change', updateListSetting); - itemLabel.appendChild(itemCheckbox); - - const itemName = document.createElement('span'); - itemName.innerText = item; - itemLabel.appendChild(itemName); - - itemRow.appendChild(itemLabel); - itemHintsDiv.appendChild(itemRow); - }); - - itemHintsWrapper.appendChild(itemHintsDiv); - itemHintsContainer.appendChild(itemHintsWrapper); - - // Starting Location Hints - const locationHintsWrapper = document.createElement('div'); - locationHintsWrapper.classList.add('hints-wrapper'); - locationHintsWrapper.innerText = 'Starting Location Hints'; - - const locationHintsDiv = document.createElement('div'); - locationHintsDiv.classList.add('simple-list'); - locations.forEach((location) => { - const locationRow = document.createElement('div'); - locationRow.classList.add('list-row'); - - const locationLabel = document.createElement('label'); - locationLabel.setAttribute('for', `${game}-start_location_hints-${location}`); - - const locationCheckbox = document.createElement('input'); - locationCheckbox.setAttribute('type', 'checkbox'); - locationCheckbox.setAttribute('id', `${game}-start_location_hints-${location}`); - locationCheckbox.setAttribute('data-game', game); - locationCheckbox.setAttribute('data-setting', 'start_location_hints'); - locationCheckbox.setAttribute('data-option', location); - if (currentSettings[game].start_location_hints.includes(location)) { - locationCheckbox.setAttribute('checked', '1'); - } - locationCheckbox.addEventListener('change', updateListSetting); - locationLabel.appendChild(locationCheckbox); - - const locationName = document.createElement('span'); - locationName.innerText = location; - locationLabel.appendChild(locationName); - - locationRow.appendChild(locationLabel); - locationHintsDiv.appendChild(locationRow); - }); - - locationHintsWrapper.appendChild(locationHintsDiv); - itemHintsContainer.appendChild(locationHintsWrapper); - - hintsDiv.appendChild(itemHintsContainer); - return hintsDiv; -}; - -const buildLocationsDiv = (game, locations) => { - const currentSettings = JSON.parse(localStorage.getItem('weighted-settings')); - locations.sort(); // Sort alphabetical, in-place - - const locationsDiv = document.createElement('div'); - locationsDiv.classList.add('locations-div'); - const locationsHeader = document.createElement('h3'); - locationsHeader.innerText = 'Priority & Exclusion Locations'; - locationsDiv.appendChild(locationsHeader); - const locationsDescription = document.createElement('p'); - locationsDescription.classList.add('setting-description'); - locationsDescription.innerText = 'Priority locations guarantee a progression item will be placed there while ' + - 'excluded locations will not contain progression or useful items.'; - locationsDiv.appendChild(locationsDescription); - - const locationsContainer = document.createElement('div'); - locationsContainer.classList.add('locations-container'); - - // Priority Locations - const priorityLocationsWrapper = document.createElement('div'); - priorityLocationsWrapper.classList.add('locations-wrapper'); - priorityLocationsWrapper.innerText = 'Priority Locations'; - - const priorityLocationsDiv = document.createElement('div'); - priorityLocationsDiv.classList.add('simple-list'); - locations.forEach((location) => { - const locationRow = document.createElement('div'); - locationRow.classList.add('list-row'); - - const locationLabel = document.createElement('label'); - locationLabel.setAttribute('for', `${game}-priority_locations-${location}`); - - const locationCheckbox = document.createElement('input'); - locationCheckbox.setAttribute('type', 'checkbox'); - locationCheckbox.setAttribute('id', `${game}-priority_locations-${location}`); - locationCheckbox.setAttribute('data-game', game); - locationCheckbox.setAttribute('data-setting', 'priority_locations'); - locationCheckbox.setAttribute('data-option', location); - if (currentSettings[game].priority_locations.includes(location)) { - locationCheckbox.setAttribute('checked', '1'); - } - locationCheckbox.addEventListener('change', updateListSetting); - locationLabel.appendChild(locationCheckbox); - - const locationName = document.createElement('span'); - locationName.innerText = location; - locationLabel.appendChild(locationName); - - locationRow.appendChild(locationLabel); - priorityLocationsDiv.appendChild(locationRow); - }); - - priorityLocationsWrapper.appendChild(priorityLocationsDiv); - locationsContainer.appendChild(priorityLocationsWrapper); - - // Exclude Locations - const excludeLocationsWrapper = document.createElement('div'); - excludeLocationsWrapper.classList.add('locations-wrapper'); - excludeLocationsWrapper.innerText = 'Exclude Locations'; - - const excludeLocationsDiv = document.createElement('div'); - excludeLocationsDiv.classList.add('simple-list'); - locations.forEach((location) => { - const locationRow = document.createElement('div'); - locationRow.classList.add('list-row'); - - const locationLabel = document.createElement('label'); - locationLabel.setAttribute('for', `${game}-exclude_locations-${location}`); - - const locationCheckbox = document.createElement('input'); - locationCheckbox.setAttribute('type', 'checkbox'); - locationCheckbox.setAttribute('id', `${game}-exclude_locations-${location}`); - locationCheckbox.setAttribute('data-game', game); - locationCheckbox.setAttribute('data-setting', 'exclude_locations'); - locationCheckbox.setAttribute('data-option', location); - if (currentSettings[game].exclude_locations.includes(location)) { - locationCheckbox.setAttribute('checked', '1'); - } - locationCheckbox.addEventListener('change', updateListSetting); - locationLabel.appendChild(locationCheckbox); - - const locationName = document.createElement('span'); - locationName.innerText = location; - locationLabel.appendChild(locationName); - - locationRow.appendChild(locationLabel); - excludeLocationsDiv.appendChild(locationRow); - }); - - excludeLocationsWrapper.appendChild(excludeLocationsDiv); - locationsContainer.appendChild(excludeLocationsWrapper); - - locationsDiv.appendChild(locationsContainer); - return locationsDiv; -}; - -const updateVisibleGames = () => { - const settings = JSON.parse(localStorage.getItem('weighted-settings')); - Object.keys(settings.game).forEach((game) => { - const gameDiv = document.getElementById(`${game}-div`); - const gameOption = document.getElementById(`${game}-game-option`); - if (parseInt(settings.game[game], 10) > 0) { - gameDiv.classList.remove('invisible'); - gameOption.classList.add('jump-link'); - gameOption.addEventListener('click', () => { - const gameDiv = document.getElementById(`${game}-div`); - if (gameDiv.classList.contains('invisible')) { return; } - gameDiv.scrollIntoView({ - behavior: 'smooth', - block: 'start', - }); - }); - } else { - gameDiv.classList.add('invisible'); - gameOption.classList.remove('jump-link'); - - } - }); -}; - -const updateBaseSetting = (event) => { - const settings = JSON.parse(localStorage.getItem('weighted-settings')); - const setting = event.target.getAttribute('data-setting'); - const option = event.target.getAttribute('data-option'); - const type = event.target.getAttribute('data-type'); - - switch(type){ - case 'weight': - settings[setting][option] = isNaN(event.target.value) ? event.target.value : parseInt(event.target.value, 10); - document.getElementById(`${setting}-${option}`).innerText = event.target.value; - break; - case 'data': - settings[setting] = isNaN(event.target.value) ? event.target.value : parseInt(event.target.value, 10); - break; - } - - localStorage.setItem('weighted-settings', JSON.stringify(settings)); -}; - -const updateRangeSetting = (evt) => { - const options = JSON.parse(localStorage.getItem('weighted-settings')); - const game = evt.target.getAttribute('data-game'); - const setting = evt.target.getAttribute('data-setting'); - const option = evt.target.getAttribute('data-option'); - document.getElementById(`${game}-${setting}-${option}`).innerText = evt.target.value; - if (evt.action && evt.action === 'rangeDelete') { - delete options[game][setting][option]; - } else { - options[game][setting][option] = parseInt(evt.target.value, 10); - } - localStorage.setItem('weighted-settings', JSON.stringify(options)); -}; - -const updateListSetting = (evt) => { - const options = JSON.parse(localStorage.getItem('weighted-settings')); - const game = evt.target.getAttribute('data-game'); - const setting = evt.target.getAttribute('data-setting'); - const option = evt.target.getAttribute('data-option'); - - if (evt.target.checked) { - // If the option is to be enabled and it is already enabled, do nothing - if (options[game][setting].includes(option)) { return; } - - options[game][setting].push(option); - } else { - // If the option is to be disabled and it is already disabled, do nothing - if (!options[game][setting].includes(option)) { return; } - - options[game][setting].splice(options[game][setting].indexOf(option), 1); - } - localStorage.setItem('weighted-settings', JSON.stringify(options)); -}; - -const updateItemSetting = (evt) => { - const options = JSON.parse(localStorage.getItem('weighted-settings')); - const game = evt.target.getAttribute('data-game'); - const setting = evt.target.getAttribute('data-setting'); - const option = evt.target.getAttribute('data-option'); - if (setting === 'start_inventory') { - options[game][setting][option] = evt.target.value.trim() ? parseInt(evt.target.value) : 0; - } else { - options[game][setting][option] = isNaN(evt.target.value) ? - evt.target.value : parseInt(evt.target.value, 10); - } - localStorage.setItem('weighted-settings', JSON.stringify(options)); -}; - -const validateSettings = () => { - const settings = JSON.parse(localStorage.getItem('weighted-settings')); - const userMessage = document.getElementById('user-message'); - let errorMessage = null; - - // User must choose a name for their file - if (!settings.name || settings.name.trim().length === 0 || settings.name.toLowerCase().trim() === 'player') { - userMessage.innerText = 'You forgot to set your player name at the top of the page!'; - userMessage.classList.add('visible'); - userMessage.scrollIntoView({ - behavior: 'smooth', - block: 'start', + settingsWrapper.appendChild(settingWrapper); }); - return; + + return settingsWrapper; } - // Clean up the settings output - Object.keys(settings.game).forEach((game) => { - // Remove any disabled games - if (settings.game[game] === 0) { - delete settings.game[game]; - delete settings[game]; - return; - } + #buildItemsDiv() { + const itemsDiv = document.createElement('div'); + itemsDiv.classList.add('items-div'); - Object.keys(settings[game]).forEach((setting) => { - // Remove any disabled options - Object.keys(settings[game][setting]).forEach((option) => { - if (settings[game][setting][option] === 0) { - delete settings[game][setting][option]; + const itemsDivHeader = document.createElement('h3'); + itemsDivHeader.innerText = 'Item Pool'; + itemsDiv.appendChild(itemsDivHeader); + + const itemsDescription = document.createElement('p'); + itemsDescription.classList.add('setting-description'); + itemsDescription.innerText = 'Choose if you would like to start with items, or control if they are placed in ' + + 'your seed or someone else\'s.'; + itemsDiv.appendChild(itemsDescription); + + const itemsHint = document.createElement('p'); + itemsHint.classList.add('hint-text'); + itemsHint.innerText = 'Drag and drop items from one box to another.'; + itemsDiv.appendChild(itemsHint); + + const itemsWrapper = document.createElement('div'); + itemsWrapper.classList.add('items-wrapper'); + + const itemDragoverHandler = (evt) => evt.preventDefault(); + const itemDropHandler = (evt) => this.#itemDropHandler(evt); + + // Create container divs for each category + const availableItemsWrapper = document.createElement('div'); + availableItemsWrapper.classList.add('item-set-wrapper'); + availableItemsWrapper.innerText = 'Available Items'; + const availableItems = document.createElement('div'); + availableItems.classList.add('item-container'); + availableItems.setAttribute('id', `${this.name}-available_items`); + availableItems.addEventListener('dragover', itemDragoverHandler); + availableItems.addEventListener('drop', itemDropHandler); + + const startInventoryWrapper = document.createElement('div'); + startInventoryWrapper.classList.add('item-set-wrapper'); + startInventoryWrapper.innerText = 'Start Inventory'; + const startInventory = document.createElement('div'); + startInventory.classList.add('item-container'); + startInventory.setAttribute('id', `${this.name}-start_inventory`); + startInventory.setAttribute('data-setting', 'start_inventory'); + startInventory.addEventListener('dragover', itemDragoverHandler); + startInventory.addEventListener('drop', itemDropHandler); + + const localItemsWrapper = document.createElement('div'); + localItemsWrapper.classList.add('item-set-wrapper'); + localItemsWrapper.innerText = 'Local Items'; + const localItems = document.createElement('div'); + localItems.classList.add('item-container'); + localItems.setAttribute('id', `${this.name}-local_items`); + localItems.setAttribute('data-setting', 'local_items') + localItems.addEventListener('dragover', itemDragoverHandler); + localItems.addEventListener('drop', itemDropHandler); + + const nonLocalItemsWrapper = document.createElement('div'); + nonLocalItemsWrapper.classList.add('item-set-wrapper'); + nonLocalItemsWrapper.innerText = 'Non-Local Items'; + const nonLocalItems = document.createElement('div'); + nonLocalItems.classList.add('item-container'); + nonLocalItems.setAttribute('id', `${this.name}-non_local_items`); + nonLocalItems.setAttribute('data-setting', 'non_local_items'); + nonLocalItems.addEventListener('dragover', itemDragoverHandler); + nonLocalItems.addEventListener('drop', itemDropHandler); + + // Populate the divs + this.data.gameItems.forEach((item) => { + if (Object.keys(this.current.start_inventory).includes(item)){ + const itemDiv = this.#buildItemQtyDiv(item); + itemDiv.setAttribute('data-setting', 'start_inventory'); + startInventory.appendChild(itemDiv); + } else if (this.current.local_items.includes(item)) { + const itemDiv = this.#buildItemDiv(item); + itemDiv.setAttribute('data-setting', 'local_items'); + localItems.appendChild(itemDiv); + } else if (this.current.non_local_items.includes(item)) { + const itemDiv = this.#buildItemDiv(item); + itemDiv.setAttribute('data-setting', 'non_local_items'); + nonLocalItems.appendChild(itemDiv); + } else { + const itemDiv = this.#buildItemDiv(item); + availableItems.appendChild(itemDiv); + } + }); + + availableItemsWrapper.appendChild(availableItems); + startInventoryWrapper.appendChild(startInventory); + localItemsWrapper.appendChild(localItems); + nonLocalItemsWrapper.appendChild(nonLocalItems); + itemsWrapper.appendChild(availableItemsWrapper); + itemsWrapper.appendChild(startInventoryWrapper); + itemsWrapper.appendChild(localItemsWrapper); + itemsWrapper.appendChild(nonLocalItemsWrapper); + itemsDiv.appendChild(itemsWrapper); + return itemsDiv; + } + + #buildItemDiv(item) { + const itemDiv = document.createElement('div'); + itemDiv.classList.add('item-div'); + itemDiv.setAttribute('id', `${this.name}-${item}`); + itemDiv.setAttribute('data-game', this.name); + itemDiv.setAttribute('data-item', item); + itemDiv.setAttribute('draggable', 'true'); + itemDiv.innerText = item; + itemDiv.addEventListener('dragstart', (evt) => { + evt.dataTransfer.setData('text/plain', itemDiv.getAttribute('id')); + }); + return itemDiv; + } + + #buildItemQtyDiv(item) { + const itemQtyDiv = document.createElement('div'); + itemQtyDiv.classList.add('item-qty-div'); + itemQtyDiv.setAttribute('id', `${this.name}-${item}`); + itemQtyDiv.setAttribute('data-game', this.name); + itemQtyDiv.setAttribute('data-item', item); + itemQtyDiv.setAttribute('draggable', 'true'); + itemQtyDiv.innerText = item; + + const inputWrapper = document.createElement('div'); + inputWrapper.classList.add('item-qty-input-wrapper') + + const itemQty = document.createElement('input'); + itemQty.setAttribute('value', this.current.start_inventory.hasOwnProperty(item) ? + this.current.start_inventory[item] : '1'); + itemQty.setAttribute('data-game', this.name); + itemQty.setAttribute('data-setting', 'start_inventory'); + itemQty.setAttribute('data-option', item); + itemQty.setAttribute('maxlength', '3'); + itemQty.addEventListener('keyup', (evt) => { + evt.target.value = isNaN(parseInt(evt.target.value)) ? 0 : parseInt(evt.target.value); + this.#updateItemSetting(evt); + }); + inputWrapper.appendChild(itemQty); + itemQtyDiv.appendChild(inputWrapper); + + itemQtyDiv.addEventListener('dragstart', (evt) => { + evt.dataTransfer.setData('text/plain', itemQtyDiv.getAttribute('id')); + }); + return itemQtyDiv; + } + + #itemDropHandler(evt) { + evt.preventDefault(); + const sourceId = evt.dataTransfer.getData('text/plain'); + const sourceDiv = document.getElementById(sourceId); + + const item = sourceDiv.getAttribute('data-item'); + + const oldSetting = sourceDiv.hasAttribute('data-setting') ? sourceDiv.getAttribute('data-setting') : null; + const newSetting = evt.target.hasAttribute('data-setting') ? evt.target.getAttribute('data-setting') : null; + + const itemDiv = newSetting === 'start_inventory' ? this.#buildItemQtyDiv(item) : this.#buildItemDiv(item); + + if (oldSetting) { + if (oldSetting === 'start_inventory') { + if (this.current[oldSetting].hasOwnProperty(item)) { + delete this.current[oldSetting][item]; + } + } else { + if (this.current[oldSetting].includes(item)) { + this.current[oldSetting].splice(this.current[oldSetting].indexOf(item), 1); } - }); - - if ( - Object.keys(settings[game][setting]).length === 0 && - !Array.isArray(settings[game][setting]) && - setting !== 'start_inventory' - ) { - errorMessage = `${game} // ${setting} has no values above zero!`; } + } - // Remove weights from options with only one possibility - if ( - Object.keys(settings[game][setting]).length === 1 && - !Array.isArray(settings[game][setting]) && - setting !== 'start_inventory' - ) { - settings[game][setting] = Object.keys(settings[game][setting])[0]; + if (newSetting) { + itemDiv.setAttribute('data-setting', newSetting); + document.getElementById(`${this.name}-${newSetting}`).appendChild(itemDiv); + if (newSetting === 'start_inventory') { + this.current[newSetting][item] = 1; + } else { + if (!this.current[newSetting].includes(item)){ + this.current[newSetting].push(item); + } } + } else { + // No setting was assigned, this item has been removed from the settings + document.getElementById(`${this.name}-available_items`).appendChild(itemDiv); + } - // Remove empty arrays - else if ( - ['exclude_locations', 'priority_locations', 'local_items', - 'non_local_items', 'start_hints', 'start_location_hints'].includes(setting) && - settings[game][setting].length === 0 - ) { - delete settings[game][setting]; - } + // Remove the source drag object + sourceDiv.parentElement.removeChild(sourceDiv); - // Remove empty start inventory - else if ( - setting === 'start_inventory' && - Object.keys(settings[game]['start_inventory']).length === 0 - ) { - delete settings[game]['start_inventory']; + // Save the updated settings + this.save(); + } + + #buildHintsDiv() { + const hintsDiv = document.createElement('div'); + hintsDiv.classList.add('hints-div'); + const hintsHeader = document.createElement('h3'); + hintsHeader.innerText = 'Item & Location Hints'; + hintsDiv.appendChild(hintsHeader); + const hintsDescription = document.createElement('p'); + hintsDescription.classList.add('setting-description'); + hintsDescription.innerText = 'Choose any items or locations to begin the game with the knowledge of where those ' + + ' items are, or what those locations contain.'; + hintsDiv.appendChild(hintsDescription); + + const itemHintsContainer = document.createElement('div'); + itemHintsContainer.classList.add('hints-container'); + + // Item Hints + const itemHintsWrapper = document.createElement('div'); + itemHintsWrapper.classList.add('hints-wrapper'); + itemHintsWrapper.innerText = 'Starting Item Hints'; + + const itemHintsDiv = document.createElement('div'); + itemHintsDiv.classList.add('simple-list'); + this.data.gameItems.forEach((item) => { + const itemRow = document.createElement('div'); + itemRow.classList.add('list-row'); + + const itemLabel = document.createElement('label'); + itemLabel.setAttribute('for', `${this.name}-start_hints-${item}`); + + const itemCheckbox = document.createElement('input'); + itemCheckbox.setAttribute('type', 'checkbox'); + itemCheckbox.setAttribute('id', `${this.name}-start_hints-${item}`); + itemCheckbox.setAttribute('data-game', this.name); + itemCheckbox.setAttribute('data-setting', 'start_hints'); + itemCheckbox.setAttribute('data-option', item); + if (this.current.start_hints.includes(item)) { + itemCheckbox.setAttribute('checked', 'true'); } + itemCheckbox.addEventListener('change', (evt) => this.#updateListSetting(evt)); + itemLabel.appendChild(itemCheckbox); + + const itemName = document.createElement('span'); + itemName.innerText = item; + itemLabel.appendChild(itemName); + + itemRow.appendChild(itemLabel); + itemHintsDiv.appendChild(itemRow); }); - }); - if (Object.keys(settings.game).length === 0) { - errorMessage = 'You have not chosen a game to play!'; - } + itemHintsWrapper.appendChild(itemHintsDiv); + itemHintsContainer.appendChild(itemHintsWrapper); - // Remove weights if there is only one game - else if (Object.keys(settings.game).length === 1) { - settings.game = Object.keys(settings.game)[0]; - } + // Starting Location Hints + const locationHintsWrapper = document.createElement('div'); + locationHintsWrapper.classList.add('hints-wrapper'); + locationHintsWrapper.innerText = 'Starting Location Hints'; - // If an error occurred, alert the user and do not export the file - if (errorMessage) { - userMessage.innerText = errorMessage; - userMessage.classList.add('visible'); - userMessage.scrollIntoView({ - behavior: 'smooth', - block: 'start', + const locationHintsDiv = document.createElement('div'); + locationHintsDiv.classList.add('simple-list'); + this.data.gameLocations.forEach((location) => { + const locationRow = document.createElement('div'); + locationRow.classList.add('list-row'); + + const locationLabel = document.createElement('label'); + locationLabel.setAttribute('for', `${this.name}-start_location_hints-${location}`); + + const locationCheckbox = document.createElement('input'); + locationCheckbox.setAttribute('type', 'checkbox'); + locationCheckbox.setAttribute('id', `${this.name}-start_location_hints-${location}`); + locationCheckbox.setAttribute('data-game', this.name); + locationCheckbox.setAttribute('data-setting', 'start_location_hints'); + locationCheckbox.setAttribute('data-option', location); + if (this.current.start_location_hints.includes(location)) { + locationCheckbox.setAttribute('checked', '1'); + } + locationCheckbox.addEventListener('change', (evt) => this.#updateListSetting(evt)); + locationLabel.appendChild(locationCheckbox); + + const locationName = document.createElement('span'); + locationName.innerText = location; + locationLabel.appendChild(locationName); + + locationRow.appendChild(locationLabel); + locationHintsDiv.appendChild(locationRow); }); - return; + + locationHintsWrapper.appendChild(locationHintsDiv); + itemHintsContainer.appendChild(locationHintsWrapper); + + hintsDiv.appendChild(itemHintsContainer); + return hintsDiv; } - // If no error occurred, hide the user message if it is visible - userMessage.classList.remove('visible'); - return settings; -}; + #buildLocationsDiv() { + const locationsDiv = document.createElement('div'); + locationsDiv.classList.add('locations-div'); + const locationsHeader = document.createElement('h3'); + locationsHeader.innerText = 'Priority & Exclusion Locations'; + locationsDiv.appendChild(locationsHeader); + const locationsDescription = document.createElement('p'); + locationsDescription.classList.add('setting-description'); + locationsDescription.innerText = 'Priority locations guarantee a progression item will be placed there while ' + + 'excluded locations will not contain progression or useful items.'; + locationsDiv.appendChild(locationsDescription); -const exportSettings = () => { - const settings = validateSettings(); - if (!settings) { return; } + const locationsContainer = document.createElement('div'); + locationsContainer.classList.add('locations-container'); - const yamlText = jsyaml.safeDump(settings, { noCompatMode: true }).replaceAll(/'(\d+)':/g, (x, y) => `${y}:`); - download(`${document.getElementById('player-name').value}.yaml`, yamlText); -}; + // Priority Locations + const priorityLocationsWrapper = document.createElement('div'); + priorityLocationsWrapper.classList.add('locations-wrapper'); + priorityLocationsWrapper.innerText = 'Priority Locations'; + + const priorityLocationsDiv = document.createElement('div'); + priorityLocationsDiv.classList.add('simple-list'); + this.data.gameLocations.forEach((location) => { + const locationRow = document.createElement('div'); + locationRow.classList.add('list-row'); + + const locationLabel = document.createElement('label'); + locationLabel.setAttribute('for', `${this.name}-priority_locations-${location}`); + + const locationCheckbox = document.createElement('input'); + locationCheckbox.setAttribute('type', 'checkbox'); + locationCheckbox.setAttribute('id', `${this.name}-priority_locations-${location}`); + locationCheckbox.setAttribute('data-game', this.name); + locationCheckbox.setAttribute('data-setting', 'priority_locations'); + locationCheckbox.setAttribute('data-option', location); + if (this.current.priority_locations.includes(location)) { + locationCheckbox.setAttribute('checked', '1'); + } + locationCheckbox.addEventListener('change', (evt) => this.#updateListSetting(evt)); + locationLabel.appendChild(locationCheckbox); + + const locationName = document.createElement('span'); + locationName.innerText = location; + locationLabel.appendChild(locationName); + + locationRow.appendChild(locationLabel); + priorityLocationsDiv.appendChild(locationRow); + }); + + priorityLocationsWrapper.appendChild(priorityLocationsDiv); + locationsContainer.appendChild(priorityLocationsWrapper); + + // Exclude Locations + const excludeLocationsWrapper = document.createElement('div'); + excludeLocationsWrapper.classList.add('locations-wrapper'); + excludeLocationsWrapper.innerText = 'Exclude Locations'; + + const excludeLocationsDiv = document.createElement('div'); + excludeLocationsDiv.classList.add('simple-list'); + this.data.gameLocations.forEach((location) => { + const locationRow = document.createElement('div'); + locationRow.classList.add('list-row'); + + const locationLabel = document.createElement('label'); + locationLabel.setAttribute('for', `${this.name}-exclude_locations-${location}`); + + const locationCheckbox = document.createElement('input'); + locationCheckbox.setAttribute('type', 'checkbox'); + locationCheckbox.setAttribute('id', `${this.name}-exclude_locations-${location}`); + locationCheckbox.setAttribute('data-game', this.name); + locationCheckbox.setAttribute('data-setting', 'exclude_locations'); + locationCheckbox.setAttribute('data-option', location); + if (this.current.exclude_locations.includes(location)) { + locationCheckbox.setAttribute('checked', '1'); + } + locationCheckbox.addEventListener('change', (evt) => this.#updateListSetting(evt)); + locationLabel.appendChild(locationCheckbox); + + const locationName = document.createElement('span'); + locationName.innerText = location; + locationLabel.appendChild(locationName); + + locationRow.appendChild(locationLabel); + excludeLocationsDiv.appendChild(locationRow); + }); + + excludeLocationsWrapper.appendChild(excludeLocationsDiv); + locationsContainer.appendChild(excludeLocationsWrapper); + + locationsDiv.appendChild(locationsContainer); + return locationsDiv; + } + + #updateRangeSetting(evt) { + const setting = evt.target.getAttribute('data-setting'); + const option = evt.target.getAttribute('data-option'); + document.getElementById(`${this.name}-${setting}-${option}`).innerText = evt.target.value; + if (evt.action && evt.action === 'rangeDelete') { + delete this.current[setting][option]; + } else { + this.current[setting][option] = parseInt(evt.target.value, 10); + } + this.save(); + } + + #updateListSetting(evt) { + const setting = evt.target.getAttribute('data-setting'); + const option = evt.target.getAttribute('data-option'); + + if (evt.target.checked) { + // If the option is to be enabled and it is already enabled, do nothing + if (this.current[setting].includes(option)) { return; } + + this.current[setting].push(option); + } else { + // If the option is to be disabled and it is already disabled, do nothing + if (!this.current[setting].includes(option)) { return; } + + this.current[setting].splice(this.current[setting].indexOf(option), 1); + } + this.save(); + } + + #updateItemSetting(evt) { + const setting = evt.target.getAttribute('data-setting'); + const option = evt.target.getAttribute('data-option'); + if (setting === 'start_inventory') { + this.current[setting][option] = evt.target.value.trim() ? parseInt(evt.target.value) : 0; + } else { + this.current[setting][option] = isNaN(evt.target.value) ? + evt.target.value : parseInt(evt.target.value, 10); + } + this.save(); + } + + // Saves the current settings to local storage. + save() { + this.#allSettings.save(); + } +} /** Create an anchor and trigger a download of a text file. */ const download = (filename, text) => { @@ -1223,30 +1282,3 @@ const download = (filename, text) => { downloadLink.click(); document.body.removeChild(downloadLink); }; - -const generateGame = (raceMode = false) => { - const settings = validateSettings(); - if (!settings) { return; } - - axios.post('/api/generate', { - weights: { player: JSON.stringify(settings) }, - presetData: { player: JSON.stringify(settings) }, - playerCount: 1, - spoiler: 3, - race: raceMode ? '1' : '0', - }).then((response) => { - window.location.href = response.data.url; - }).catch((error) => { - const userMessage = document.getElementById('user-message'); - userMessage.innerText = 'Something went wrong and your game could not be generated.'; - if (error.response.data.text) { - userMessage.innerText += ' ' + error.response.data.text; - } - userMessage.classList.add('visible'); - userMessage.scrollIntoView({ - behavior: 'smooth', - block: 'start', - }); - console.error(error); - }); -}; From b707619aad6bf557559ad0a619cf716c51676747 Mon Sep 17 00:00:00 2001 From: Bryce Wilson Date: Wed, 18 Oct 2023 22:07:15 -0700 Subject: [PATCH 125/144] BizHawkClient: Add autostart setting (#2322) --- settings.py | 20 ++++++++++++++++++++ worlds/_bizhawk/context.py | 21 +++++++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/settings.py b/settings.py index a7dcbbf8dd..acae86095c 100644 --- a/settings.py +++ b/settings.py @@ -694,6 +694,25 @@ does nothing if not found snes_rom_start: Union[SnesRomStart, bool] = True +class BizHawkClientOptions(Group): + class EmuHawkPath(UserFilePath): + """ + The location of the EmuHawk you want to auto launch patched ROMs with + """ + is_exe = True + description = "EmuHawk Executable" + + class RomStart(str): + """ + Set this to true to autostart a patched ROM in BizHawk with the connector script, + to false to never open the patched rom automatically, + or to a path to an external program to open the ROM file with that instead. + """ + + emuhawk_path: EmuHawkPath = EmuHawkPath(None) + rom_start: Union[RomStart, bool] = True + + # Top-level group with lazy loading of worlds class Settings(Group): @@ -701,6 +720,7 @@ class Settings(Group): server_options: ServerOptions = ServerOptions() generator: GeneratorOptions = GeneratorOptions() sni_options: SNIOptions = SNIOptions() + bizhawkclient_options: BizHawkClientOptions = BizHawkClientOptions() _filename: Optional[str] = None diff --git a/worlds/_bizhawk/context.py b/worlds/_bizhawk/context.py index 6e53b370af..465334274e 100644 --- a/worlds/_bizhawk/context.py +++ b/worlds/_bizhawk/context.py @@ -5,6 +5,7 @@ checking or launching the client, otherwise it will probably cause circular impo import asyncio +import subprocess import traceback from typing import Any, Dict, Optional @@ -146,8 +147,24 @@ async def _game_watcher(ctx: BizHawkClientContext): async def _run_game(rom: str): - import webbrowser - webbrowser.open(rom) + import os + auto_start = Utils.get_settings().bizhawkclient_options.rom_start + + if auto_start is True: + emuhawk_path = Utils.get_settings().bizhawkclient_options.emuhawk_path + subprocess.Popen([emuhawk_path, "--lua=data/lua/connector_bizhawk_generic.lua", os.path.realpath(rom)], + cwd=Utils.local_path("."), + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + elif isinstance(auto_start, str): + import shlex + + subprocess.Popen([*shlex.split(auto_start), os.path.realpath(rom)], + cwd=Utils.local_path("."), + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) async def _patch_and_run_game(patch_file: str): From fb6b66463da5235603abe01aa3110098376ce17d Mon Sep 17 00:00:00 2001 From: Aaron Wagener Date: Thu, 19 Oct 2023 18:36:18 -0500 Subject: [PATCH 126/144] OC2: fix mistakes when moving to new options api (#2332) --- worlds/overcooked2/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/worlds/overcooked2/__init__.py b/worlds/overcooked2/__init__.py index 2bf523b347..0451f32bdd 100644 --- a/worlds/overcooked2/__init__.py +++ b/worlds/overcooked2/__init__.py @@ -172,7 +172,7 @@ class Overcooked2World(World): # random priority locations have no desirable effect on solo seeds return list() - balancing_mode = self.get_options()["LocationBalancing"] + balancing_mode = self.options.location_balancing if balancing_mode == LocationBalancingMode.disabled: # Location balancing is disabled, progression density is purely determined by filler @@ -528,7 +528,7 @@ class Overcooked2World(World): # Game Modifications "LevelPurchaseRequirements": level_purchase_requirements, "Custom66TimerScale": max(0.4, 0.25 + (1.0 - star_threshold_scale)*0.6), - "ShortHordeLevels": self.options.short_horde_levels, + "ShortHordeLevels": self.options.short_horde_levels.result, "CustomLevelOrder": custom_level_order, # Items (Starting Inventory) @@ -584,6 +584,7 @@ class Overcooked2World(World): "TwoStars": star_threshold_scale * 0.75, "OneStar": star_threshold_scale * 0.35, } + base_data["AlwaysServeOldestOrder"] = self.options.always_serve_oldest_order.result return base_data From 385803eb5ce3b9df4ca5d9307e0d2046be7b3854 Mon Sep 17 00:00:00 2001 From: Justus Lind Date: Fri, 20 Oct 2023 10:13:17 +1000 Subject: [PATCH 127/144] Muse Dash: Add support for specifying specific DLCs (#2329) --- worlds/musedash/Items.py | 2 +- worlds/musedash/MuseDashCollection.py | 38 ++++++-- worlds/musedash/MuseDashData.txt | 94 ++++++++++++-------- worlds/musedash/Options.py | 52 ++++++----- worlds/musedash/__init__.py | 59 ++++++------ worlds/musedash/test/TestCollection.py | 17 +++- worlds/musedash/test/TestDifficultyRanges.py | 3 +- 7 files changed, 166 insertions(+), 99 deletions(-) diff --git a/worlds/musedash/Items.py b/worlds/musedash/Items.py index be229228bd..63fd3aa51b 100644 --- a/worlds/musedash/Items.py +++ b/worlds/musedash/Items.py @@ -6,7 +6,7 @@ class SongData(NamedTuple): """Special data container to contain the metadata of each song to make filtering work.""" code: Optional[int] - song_is_free: bool + album: str streamer_mode: bool easy: Optional[int] hard: Optional[int] diff --git a/worlds/musedash/MuseDashCollection.py b/worlds/musedash/MuseDashCollection.py index 7812e28b7a..1807dce2f9 100644 --- a/worlds/musedash/MuseDashCollection.py +++ b/worlds/musedash/MuseDashCollection.py @@ -1,5 +1,5 @@ from .Items import SongData, AlbumData -from typing import Dict, List, Optional +from typing import Dict, List, Set, Optional from collections import ChainMap @@ -15,13 +15,21 @@ class MuseDashCollections: MUSIC_SHEET_NAME: str = "Music Sheet" MUSIC_SHEET_CODE: int = STARTING_CODE - FREE_ALBUMS = [ + FREE_ALBUMS: List[str] = [ "Default Music", "Budget Is Burning: Nano Core", "Budget Is Burning Vol.1", ] - DIFF_OVERRIDES = [ + MUSE_PLUS_DLC: str = "Muse Plus" + DLC: List[str] = [ + # MUSE_PLUS_DLC, # To be included when OptionSets are rendered as part of basic settings. + # "maimai DX Limited-time Suite", # Part of Muse Plus. Goes away 31st Jan 2026. + "Miku in Museland", # Paid DLC not included in Muse Plus + "MSR Anthology", # Part of Muse Plus. Goes away 20th Jan 2024. + ] + + DIFF_OVERRIDES: List[str] = [ "MuseDash ka nanika hi", "Rush-Hour", "Find this Month's Featured Playlist", @@ -48,8 +56,8 @@ class MuseDashCollections: "Error SFX Trap": STARTING_CODE + 9, } - item_names_to_id = ChainMap({}, sfx_trap_items, vfx_trap_items) - location_names_to_id = ChainMap(song_locations, album_locations) + item_names_to_id: ChainMap = ChainMap({}, sfx_trap_items, vfx_trap_items) + location_names_to_id: ChainMap = ChainMap(song_locations, album_locations) def __init__(self) -> None: self.item_names_to_id[self.MUSIC_SHEET_NAME] = self.MUSIC_SHEET_CODE @@ -70,7 +78,6 @@ class MuseDashCollections: # Data is in the format 'Song|UID|Album|StreamerMode|EasyDiff|HardDiff|MasterDiff|SecretDiff' song_name = sections[0] # [1] is used in the client copy to make sure item id's match. - song_is_free = album in self.FREE_ALBUMS steamer_mode = sections[3] == "True" if song_name in self.DIFF_OVERRIDES: @@ -84,7 +91,7 @@ class MuseDashCollections: diff_of_hard = self.parse_song_difficulty(sections[5]) diff_of_master = self.parse_song_difficulty(sections[6]) - self.song_items[song_name] = SongData(item_id_index, song_is_free, steamer_mode, + self.song_items[song_name] = SongData(item_id_index, album, steamer_mode, diff_of_easy, diff_of_hard, diff_of_master) item_id_index += 1 @@ -102,13 +109,13 @@ class MuseDashCollections: self.song_locations[f"{name}-1"] = location_id_index + 1 location_id_index += 2 - def get_songs_with_settings(self, dlc_songs: bool, streamer_mode_active: bool, + def get_songs_with_settings(self, dlc_songs: Set[str], streamer_mode_active: bool, diff_lower: int, diff_higher: int) -> List[str]: """Gets a list of all songs that match the filter settings. Difficulty thresholds are inclusive.""" filtered_list = [] for songKey, songData in self.song_items.items(): - if not dlc_songs and not songData.song_is_free: + if not self.song_matches_dlc_filter(songData, dlc_songs): continue if streamer_mode_active and not songData.streamer_mode: @@ -128,6 +135,19 @@ class MuseDashCollections: return filtered_list + def song_matches_dlc_filter(self, song: SongData, dlc_songs: Set[str]) -> bool: + if song.album in self.FREE_ALBUMS: + return True + + if song.album in dlc_songs: + return True + + # Muse Plus provides access to any DLC not included as a seperate pack + if song.album not in self.DLC and self.MUSE_PLUS_DLC in dlc_songs: + return True + + return False + def parse_song_difficulty(self, difficulty: str) -> Optional[int]: """Attempts to parse the song difficulty.""" if len(difficulty) <= 0 or difficulty == "?" or difficulty == "¿": diff --git a/worlds/musedash/MuseDashData.txt b/worlds/musedash/MuseDashData.txt index 8d6c3f3753..bd07fef7af 100644 --- a/worlds/musedash/MuseDashData.txt +++ b/worlds/musedash/MuseDashData.txt @@ -51,42 +51,42 @@ Mujinku-Vacuum|0-28|Default Music|False|5|7|11| MilK|0-36|Default Music|False|5|7|9| umpopoff|0-41|Default Music|False|0|?|0| Mopemope|0-45|Default Music|False|4|7|9|11 -The Happycore Idol|43-0|Just as Planned Plus|True|2|5|7| -Amatsumikaboshi|43-1|Just as Planned Plus|True|4|6|8|10 -ARIGA THESIS|43-2|Just as Planned Plus|True|3|6|10| -Night of Nights|43-3|Just as Planned Plus|False|4|7|10| -#Psychedelic_Meguro_River|43-4|Just as Planned Plus|False|3|6|8| -can you feel it|43-5|Just as Planned Plus|False|4|6|8|9 -Midnight O'clock|43-6|Just as Planned Plus|True|3|6|8| -Rin|43-7|Just as Planned Plus|True|5|7|10| -Smile-mileS|43-8|Just as Planned Plus|False|6|8|10| -Believing and Being|43-9|Just as Planned Plus|True|4|6|9| -Catalyst|43-10|Just as Planned Plus|False|5|7|9| -don't!stop!eroero!|43-11|Just as Planned Plus|True|5|7|9| -pa pi pu pi pu pi pa|43-12|Just as Planned Plus|False|6|8|10| -Sand Maze|43-13|Just as Planned Plus|True|6|8|10|11 -Diffraction|43-14|Just as Planned Plus|True|5|8|10| -AKUMU|43-15|Just as Planned Plus|False|4|6|8| -Queen Aluett|43-16|Just as Planned Plus|True|7|9|11| -DROPS|43-17|Just as Planned Plus|False|2|5|8| -Frightfully-insane Flan-chan's frightful song|43-18|Just as Planned Plus|False|5|7|10| -snooze|43-19|Just as Planned Plus|False|5|7|10| -Kuishinbo Hacker feat.Kuishinbo Akachan|43-20|Just as Planned Plus|True|5|7|9| -Inu no outa|43-21|Just as Planned Plus|True|3|5|7| -Prism Fountain|43-22|Just as Planned Plus|True|7|9|11| -Gospel|43-23|Just as Planned Plus|False|4|6|9| +The Happycore Idol|43-0|MD Plus Project|True|2|5|7| +Amatsumikaboshi|43-1|MD Plus Project|True|4|6|8|10 +ARIGA THESIS|43-2|MD Plus Project|True|3|6|10| +Night of Nights|43-3|MD Plus Project|False|4|7|10| +#Psychedelic_Meguro_River|43-4|MD Plus Project|False|3|6|8| +can you feel it|43-5|MD Plus Project|False|4|6|8|9 +Midnight O'clock|43-6|MD Plus Project|True|3|6|8| +Rin|43-7|MD Plus Project|True|5|7|10| +Smile-mileS|43-8|MD Plus Project|False|6|8|10| +Believing and Being|43-9|MD Plus Project|True|4|6|9| +Catalyst|43-10|MD Plus Project|False|5|7|9| +don't!stop!eroero!|43-11|MD Plus Project|True|5|7|9| +pa pi pu pi pu pi pa|43-12|MD Plus Project|False|6|8|10| +Sand Maze|43-13|MD Plus Project|True|6|8|10|11 +Diffraction|43-14|MD Plus Project|True|5|8|10| +AKUMU|43-15|MD Plus Project|False|4|6|8| +Queen Aluett|43-16|MD Plus Project|True|7|9|11| +DROPS|43-17|MD Plus Project|False|2|5|8| +Frightfully-insane Flan-chan's frightful song|43-18|MD Plus Project|False|5|7|10| +snooze|43-19|MD Plus Project|False|5|7|10| +Kuishinbo Hacker feat.Kuishinbo Akachan|43-20|MD Plus Project|True|5|7|9| +Inu no outa|43-21|MD Plus Project|True|3|5|7| +Prism Fountain|43-22|MD Plus Project|True|7|9|11| +Gospel|43-23|MD Plus Project|False|4|6|9| East Ai Li Lovely|62-0|Happy Otaku Pack Vol.17|False|2|4|7| Mori Umi no Fune|62-1|Happy Otaku Pack Vol.17|True|5|7|9| Ooi|62-2|Happy Otaku Pack Vol.17|True|5|7|10| Numatta!!|62-3|Happy Otaku Pack Vol.17|True|5|7|9| -SATELLITE|62-4|Happy Otaku Pack Vol.17|False|5|7|9| +SATELLITE|62-4|Happy Otaku Pack Vol.17|False|5|7|9|10 Fantasia Sonata Colorful feat. V!C|62-5|Happy Otaku Pack Vol.17|True|6|8|11| MuseDash ka nanika hi|61-0|Ola Dash|True|?|?|¿| Aleph-0|61-1|Ola Dash|True|7|9|11| Buttoba Supernova|61-2|Ola Dash|False|5|7|10|11 Rush-Hour|61-3|Ola Dash|False|IG|Jh|a2|Eh 3rd Avenue|61-4|Ola Dash|False|3|5|〇| -WORLDINVADER|61-5|Ola Dash|True|5|8|10| +WORLDINVADER|61-5|Ola Dash|True|5|8|10|11 N3V3R G3T OV3R|60-0|maimai DX Limited-time Suite|True|4|7|10| Oshama Scramble!|60-1|maimai DX Limited-time Suite|True|5|7|10| Valsqotch|60-2|maimai DX Limited-time Suite|True|5|9|11| @@ -450,13 +450,13 @@ Love Patrol|63-2|MUSE RADIO FM104|True|3|5|7| Mahorova|63-3|MUSE RADIO FM104|True|3|5|8| Yoru no machi|63-4|MUSE RADIO FM104|True|1|4|7| INTERNET YAMERO|63-5|MUSE RADIO FM104|True|6|8|10| -Abracadabra|43-24|Just as Planned Plus|False|6|8|10| -Squalldecimator feat. EZ-Ven|43-25|Just as Planned Plus|True|5|7|9| -Amateras Rhythm|43-26|Just as Planned Plus|True|6|8|11| -Record one's Dream|43-27|Just as Planned Plus|False|4|7|10| -Lunatic|43-28|Just as Planned Plus|True|5|8|10| -Jiumeng|43-29|Just as Planned Plus|True|3|6|8| -The Day We Become Family|43-30|Just as Planned Plus|True|3|5|8| +Abracadabra|43-24|MD Plus Project|False|6|8|10| +Squalldecimator feat. EZ-Ven|43-25|MD Plus Project|True|5|7|9| +Amateras Rhythm|43-26|MD Plus Project|True|6|8|11| +Record one's Dream|43-27|MD Plus Project|False|4|7|10| +Lunatic|43-28|MD Plus Project|True|5|8|10| +Jiumeng|43-29|MD Plus Project|True|3|6|8| +The Day We Become Family|43-30|MD Plus Project|True|3|5|8| Sutori ma FIRE!?!?|64-0|COSMIC RADIO PEROLIST|True|3|5|8| Tanuki Step|64-1|COSMIC RADIO PEROLIST|True|5|7|10|11 Space Stationery|64-2|COSMIC RADIO PEROLIST|True|5|7|10| @@ -465,7 +465,27 @@ Kawai Splendid Space Thief|64-4|COSMIC RADIO PEROLIST|False|6|8|10|11 Night City Runway|64-5|COSMIC RADIO PEROLIST|True|4|6|8| Chaos Shotgun feat. ChumuNote|64-6|COSMIC RADIO PEROLIST|True|6|8|10| mew mew magical summer|64-7|COSMIC RADIO PEROLIST|False|5|8|10|11 -BrainDance|65-0|Neon Abyss|True|3|6|9| -My Focus!|65-1|Neon Abyss|True|5|7|10| -ABABABA BURST|65-2|Neon Abyss|True|5|7|9| -ULTRA HIGHER|65-3|Neon Abyss|True|4|7|10| \ No newline at end of file +BrainDance|65-0|NeonAbyss|True|3|6|9| +My Focus!|65-1|NeonAbyss|True|5|7|10| +ABABABA BURST|65-2|NeonAbyss|True|5|7|9| +ULTRA HIGHER|65-3|NeonAbyss|True|4|7|10| +Silver Bullet|43-31|MD Plus Project|True|5|7|10| +Random|43-32|MD Plus Project|True|4|7|9| +OTOGE-BOSS-KYOKU-CHAN|43-33|MD Plus Project|False|6|8|10|11 +Crow Rabbit|43-34|MD Plus Project|True|7|9|11| +SyZyGy|43-35|MD Plus Project|True|6|8|10|11 +Mermaid Radio|43-36|MD Plus Project|True|3|5|7| +Helixir|43-37|MD Plus Project|False|6|8|10| +Highway Cruisin'|43-38|MD Plus Project|False|3|5|8| +JACK PT BOSS|43-39|MD Plus Project|False|6|8|10| +Time Capsule|43-40|MD Plus Project|False|7|9|11| +39 Music!|66-0|Miku in Museland|False|3|5|8| +Hand in Hand|66-1|Miku in Museland|False|1|3|6| +Cynical Night Plan|66-2|Miku in Museland|False|4|6|8| +God-ish|66-3|Miku in Museland|False|4|7|10| +Darling Dance|66-4|Miku in Museland|False|4|7|9| +Hatsune Creation Myth|66-5|Miku in Museland|False|6|8|10| +The Vampire|66-6|Miku in Museland|False|4|6|9| +Future Eve|66-7|Miku in Museland|False|4|8|11| +Unknown Mother Goose|66-8|Miku in Museland|False|4|8|10| +Shun-ran|66-9|Miku in Museland|False|4|7|9| \ No newline at end of file diff --git a/worlds/musedash/Options.py b/worlds/musedash/Options.py index b2f15ecc8e..3fe28187fa 100644 --- a/worlds/musedash/Options.py +++ b/worlds/musedash/Options.py @@ -1,10 +1,19 @@ from typing import Dict -from Options import Toggle, Option, Range, Choice, DeathLink, ItemSet +from Options import Toggle, Option, Range, Choice, DeathLink, ItemSet, OptionSet, PerGameCommonOptions +from dataclasses import dataclass +from .MuseDashCollection import MuseDashCollections class AllowJustAsPlannedDLCSongs(Toggle): - """Whether [Just as Planned]/[Muse Plus] DLC Songs, and all the DLCs along with it, will be included in the randomizer.""" - display_name = "Allow [Just as Planned]/[Muse Plus] DLC Songs" + """Whether [Muse Plus] DLC Songs, and all the albums included in it, can be chosen as randomised songs. + Note: The [Just As Planned] DLC contains all [Muse Plus] songs.""" + display_name = "Allow [Muse Plus] DLC Songs" + +class DLCMusicPacks(OptionSet): + """Which non-[Muse Plus] DLC packs can be chosen as randomised songs.""" + display_name = "DLC Packs" + default = {} + valid_keys = [dlc for dlc in MuseDashCollections.DLC] class StreamerModeEnabled(Toggle): @@ -159,21 +168,22 @@ class ExcludeSongs(ItemSet): display_name = "Exclude Songs" -musedash_options: Dict[str, type(Option)] = { - "allow_just_as_planned_dlc_songs": AllowJustAsPlannedDLCSongs, - "streamer_mode_enabled": StreamerModeEnabled, - "starting_song_count": StartingSongs, - "additional_song_count": AdditionalSongs, - "additional_item_percentage": AdditionalItemPercentage, - "song_difficulty_mode": DifficultyMode, - "song_difficulty_min": DifficultyModeOverrideMin, - "song_difficulty_max": DifficultyModeOverrideMax, - "grade_needed": GradeNeeded, - "music_sheet_count_percentage": MusicSheetCountPercentage, - "music_sheet_win_count_percentage": MusicSheetWinCountPercentage, - "available_trap_types": TrapTypes, - "trap_count_percentage": TrapCountPercentage, - "death_link": DeathLink, - "include_songs": IncludeSongs, - "exclude_songs": ExcludeSongs -} +@dataclass +class MuseDashOptions(PerGameCommonOptions): + allow_just_as_planned_dlc_songs: AllowJustAsPlannedDLCSongs + dlc_packs: DLCMusicPacks + streamer_mode_enabled: StreamerModeEnabled + starting_song_count: StartingSongs + additional_song_count: AdditionalSongs + additional_item_percentage: AdditionalItemPercentage + song_difficulty_mode: DifficultyMode + song_difficulty_min: DifficultyModeOverrideMin + song_difficulty_max: DifficultyModeOverrideMax + grade_needed: GradeNeeded + music_sheet_count_percentage: MusicSheetCountPercentage + music_sheet_win_count_percentage: MusicSheetWinCountPercentage + available_trap_types: TrapTypes + trap_count_percentage: TrapCountPercentage + death_link: DeathLink + include_songs: IncludeSongs + exclude_songs: ExcludeSongs diff --git a/worlds/musedash/__init__.py b/worlds/musedash/__init__.py index 754d2352e0..63ce123c93 100644 --- a/worlds/musedash/__init__.py +++ b/worlds/musedash/__init__.py @@ -1,10 +1,10 @@ from worlds.AutoWorld import World, WebWorld -from worlds.generic.Rules import set_rule from BaseClasses import Region, Item, ItemClassification, Entrance, Tutorial -from typing import List +from typing import List, ClassVar, Type from math import floor +from Options import PerGameCommonOptions -from .Options import musedash_options +from .Options import MuseDashOptions from .Items import MuseDashSongItem, MuseDashFixedItem from .Locations import MuseDashLocation from .MuseDashCollection import MuseDashCollections @@ -47,9 +47,9 @@ class MuseDashWorld(World): # World Options game = "Muse Dash" - option_definitions = musedash_options + options_dataclass: ClassVar[Type[PerGameCommonOptions]] = MuseDashOptions topology_present = False - data_version = 9 + data_version = 10 web = MuseDashWebWorld() # Necessary Data @@ -66,14 +66,17 @@ class MuseDashWorld(World): location_count: int def generate_early(self): - dlc_songs = self.multiworld.allow_just_as_planned_dlc_songs[self.player] - streamer_mode = self.multiworld.streamer_mode_enabled[self.player] + dlc_songs = {key for key in self.options.dlc_packs.value} + if (self.options.allow_just_as_planned_dlc_songs.value): + dlc_songs.add(self.md_collection.MUSE_PLUS_DLC) + + streamer_mode = self.options.streamer_mode_enabled (lower_diff_threshold, higher_diff_threshold) = self.get_difficulty_range() # The minimum amount of songs to make an ok rando would be Starting Songs + 10 interim songs + Goal song. # - Interim songs being equal to max starting song count. # Note: The worst settings still allow 25 songs (Streamer Mode + No DLC). - starter_song_count = self.multiworld.starting_song_count[self.player].value + starter_song_count = self.options.starting_song_count.value while True: # In most cases this should only need to run once @@ -104,9 +107,9 @@ class MuseDashWorld(World): def handle_plando(self, available_song_keys: List[str]) -> List[str]: song_items = self.md_collection.song_items - start_items = self.multiworld.start_inventory[self.player].value.keys() - include_songs = self.multiworld.include_songs[self.player].value - exclude_songs = self.multiworld.exclude_songs[self.player].value + start_items = self.options.start_inventory.value.keys() + include_songs = self.options.include_songs.value + exclude_songs = self.options.exclude_songs.value self.starting_songs = [s for s in start_items if s in song_items] self.included_songs = [s for s in include_songs if s in song_items and s not in self.starting_songs] @@ -115,8 +118,8 @@ class MuseDashWorld(World): and s not in include_songs and s not in exclude_songs] def create_song_pool(self, available_song_keys: List[str]): - starting_song_count = self.multiworld.starting_song_count[self.player].value - additional_song_count = self.multiworld.additional_song_count[self.player].value + starting_song_count = self.options.starting_song_count.value + additional_song_count = self.options.additional_song_count.value self.random.shuffle(available_song_keys) @@ -150,7 +153,7 @@ class MuseDashWorld(World): # Then attempt to fufill any remaining songs for interim songs if len(self.included_songs) < additional_song_count: - for _ in range(len(self.included_songs), self.multiworld.additional_song_count[self.player]): + for _ in range(len(self.included_songs), self.options.additional_song_count): if len(available_song_keys) <= 0: break self.included_songs.append(available_song_keys.pop()) @@ -258,40 +261,40 @@ class MuseDashWorld(World): state.has(self.md_collection.MUSIC_SHEET_NAME, self.player, self.get_music_sheet_win_count()) def get_available_traps(self) -> List[str]: - dlc_songs = self.multiworld.allow_just_as_planned_dlc_songs[self.player] + sfx_traps_available = self.options.allow_just_as_planned_dlc_songs.value trap_list = [] - if self.multiworld.available_trap_types[self.player].value & 1 != 0: + if self.options.available_trap_types.value & 1 != 0: trap_list += self.md_collection.vfx_trap_items.keys() # SFX options are only available under Just as Planned DLC. - if dlc_songs and self.multiworld.available_trap_types[self.player].value & 2 != 0: + if sfx_traps_available and self.options.available_trap_types.value & 2 != 0: trap_list += self.md_collection.sfx_trap_items.keys() return trap_list def get_additional_item_percentage(self) -> int: - trap_count = self.multiworld.trap_count_percentage[self.player].value - song_count = self.multiworld.music_sheet_count_percentage[self.player].value - return max(trap_count + song_count, self.multiworld.additional_item_percentage[self.player].value) + trap_count = self.options.trap_count_percentage.value + song_count = self.options.music_sheet_count_percentage.value + return max(trap_count + song_count, self.options.additional_item_percentage.value) def get_trap_count(self) -> int: - multiplier = self.multiworld.trap_count_percentage[self.player].value / 100.0 + multiplier = self.options.trap_count_percentage.value / 100.0 trap_count = (len(self.starting_songs) * 2) + len(self.included_songs) return max(0, floor(trap_count * multiplier)) def get_music_sheet_count(self) -> int: - multiplier = self.multiworld.music_sheet_count_percentage[self.player].value / 100.0 + multiplier = self.options.music_sheet_count_percentage.value / 100.0 song_count = (len(self.starting_songs) * 2) + len(self.included_songs) return max(1, floor(song_count * multiplier)) def get_music_sheet_win_count(self) -> int: - multiplier = self.multiworld.music_sheet_win_count_percentage[self.player].value / 100.0 + multiplier = self.options.music_sheet_win_count_percentage.value / 100.0 sheet_count = self.get_music_sheet_count() return max(1, floor(sheet_count * multiplier)) def get_difficulty_range(self) -> List[int]: - difficulty_mode = self.multiworld.song_difficulty_mode[self.player] + difficulty_mode = self.options.song_difficulty_mode # Valid difficulties are between 1 and 11. But make it 0 to 12 for safety difficulty_bounds = [0, 12] @@ -309,8 +312,8 @@ class MuseDashWorld(World): elif difficulty_mode == 5: difficulty_bounds[0] = 10 elif difficulty_mode == 6: - minimum_difficulty = self.multiworld.song_difficulty_min[self.player].value - maximum_difficulty = self.multiworld.song_difficulty_max[self.player].value + minimum_difficulty = self.options.song_difficulty_min.value + maximum_difficulty = self.options.song_difficulty_max.value difficulty_bounds[0] = min(minimum_difficulty, maximum_difficulty) difficulty_bounds[1] = max(minimum_difficulty, maximum_difficulty) @@ -320,7 +323,7 @@ class MuseDashWorld(World): def fill_slot_data(self): return { "victoryLocation": self.victory_song_name, - "deathLink": self.multiworld.death_link[self.player].value, + "deathLink": self.options.death_link.value, "musicSheetWinCount": self.get_music_sheet_win_count(), - "gradeNeeded": self.multiworld.grade_needed[self.player].value + "gradeNeeded": self.options.grade_needed.value } diff --git a/worlds/musedash/test/TestCollection.py b/worlds/musedash/test/TestCollection.py index 23348af104..f9422388ae 100644 --- a/worlds/musedash/test/TestCollection.py +++ b/worlds/musedash/test/TestCollection.py @@ -36,14 +36,27 @@ class CollectionsTest(unittest.TestCase): def test_free_dlc_included_in_base_songs(self) -> None: collection = MuseDashCollections() - songs = collection.get_songs_with_settings(False, False, 0, 11) + songs = collection.get_songs_with_settings(set(), False, 0, 12) self.assertIn("Glimmer", songs, "Budget Is Burning Vol.1 is not being included in base songs") self.assertIn("Out of Sense", songs, "Budget Is Burning: Nano Core is not being included in base songs") + def test_dlcs(self) -> None: + collection = MuseDashCollections() + free_song_count = len(collection.get_songs_with_settings(set(), False, 0, 12)) + known_mp_song = "The Happycore Idol" + + for dlc in collection.DLC: + songs_with_dlc = collection.get_songs_with_settings({dlc}, False, 0, 12) + self.assertGreater(len(songs_with_dlc), free_song_count, f"DLC {dlc} did not include extra songs.") + if dlc == collection.MUSE_PLUS_DLC: + self.assertIn(known_mp_song, songs_with_dlc, f"Muse Plus missing muse plus song.") + else: + self.assertNotIn(known_mp_song, songs_with_dlc, f"DLC {dlc} includes Muse Plus songs.") + def test_remove_songs_are_not_generated(self) -> None: collection = MuseDashCollections() - songs = collection.get_songs_with_settings(True, False, 0, 11) + songs = collection.get_songs_with_settings({x for x in collection.DLC}, False, 0, 12) for song_name in self.REMOVED_SONGS: self.assertNotIn(song_name, songs, f"Song '{song_name}' wasn't removed correctly.") diff --git a/worlds/musedash/test/TestDifficultyRanges.py b/worlds/musedash/test/TestDifficultyRanges.py index 58817d0fc3..01420347af 100644 --- a/worlds/musedash/test/TestDifficultyRanges.py +++ b/worlds/musedash/test/TestDifficultyRanges.py @@ -4,6 +4,7 @@ from . import MuseDashTestBase class DifficultyRanges(MuseDashTestBase): def test_all_difficulty_ranges(self) -> None: muse_dash_world = self.multiworld.worlds[1] + dlc_set = {x for x in muse_dash_world.md_collection.DLC} difficulty_choice = self.multiworld.song_difficulty_mode[1] difficulty_min = self.multiworld.song_difficulty_min[1] difficulty_max = self.multiworld.song_difficulty_max[1] @@ -12,7 +13,7 @@ class DifficultyRanges(MuseDashTestBase): self.assertEqual(inputRange[0], lower) self.assertEqual(inputRange[1], upper) - songs = muse_dash_world.md_collection.get_songs_with_settings(True, False, inputRange[0], inputRange[1]) + songs = muse_dash_world.md_collection.get_songs_with_settings(dlc_set, False, inputRange[0], inputRange[1]) for songKey in songs: song = muse_dash_world.md_collection.song_items[songKey] if (song.easy is not None and inputRange[0] <= song.easy <= inputRange[1]): From b82f48fe4b408ccfd66c9eb3f1eaefda19fb665b Mon Sep 17 00:00:00 2001 From: espeon65536 <81029175+espeon65536@users.noreply.github.com> Date: Thu, 19 Oct 2023 18:23:32 -0600 Subject: [PATCH 128/144] Core: guard against plandoing items onto event locations (#2284) --- Fill.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/Fill.py b/Fill.py index 600d18ef2a..94528a307f 100644 --- a/Fill.py +++ b/Fill.py @@ -897,19 +897,22 @@ def distribute_planned(world: MultiWorld) -> None: for item_name in items: item = world.worlds[player].create_item(item_name) for location in reversed(candidates): - 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 + if (location.address is None) == (item.code is None): # either both None or both not None + 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.append(f"Can't place item at {location} due to fill condition not met.") else: - err.append(f"Can't place item at {location} due to fill condition not met.") + err.append(f"{item_name} not allowed at {location}.") else: - err.append(f"{item_name} not allowed at {location}.") + err.append(f"Cannot place {item_name} into already filled location {location}.") else: - err.append(f"Cannot place {item_name} into already filled location {location}.") + err.append(f"Mismatch between {item_name} and {location}, only one is an event.") if count == maxcount: break if count < placement['count']['min']: From 56796b7ee8b0177c1387657152865c7909037886 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Fri, 20 Oct 2023 02:58:41 +0200 Subject: [PATCH 129/144] WebHost: minor css changes to make Supported Games page usable without js (#2266) * WebHost: minor css changes to make Supported Games page usable without js * Update JS to use querySelectorAll, remove most id attributes from elements, use relative element selectors * Hide content when clearing search bar * Remove `console.log`, remove TODO --------- Co-authored-by: Chris Wilson --- WebHostLib/static/assets/supportedGames.js | 87 ++++++++------------- WebHostLib/static/styles/supportedGames.css | 12 ++- WebHostLib/templates/supportedGames.html | 28 ++++++- 3 files changed, 67 insertions(+), 60 deletions(-) diff --git a/WebHostLib/static/assets/supportedGames.js b/WebHostLib/static/assets/supportedGames.js index 1acf0e0cc5..56eb15b5e5 100644 --- a/WebHostLib/static/assets/supportedGames.js +++ b/WebHostLib/static/assets/supportedGames.js @@ -1,51 +1,32 @@ window.addEventListener('load', () => { - const gameHeaders = document.getElementsByClassName('collapse-toggle'); - Array.from(gameHeaders).forEach((header) => { - const gameName = header.getAttribute('data-game'); - header.addEventListener('click', () => { - const gameArrow = document.getElementById(`${gameName}-arrow`); - const gameInfo = document.getElementById(gameName); - if (gameInfo.classList.contains('collapsed')) { - gameArrow.innerText = '▼'; - gameInfo.classList.remove('collapsed'); - } else { - gameArrow.innerText = '▶'; - gameInfo.classList.add('collapsed'); - } - }); - }); + // Add toggle listener to all elements with .collapse-toggle + const toggleButtons = document.querySelectorAll('.collapse-toggle'); + toggleButtons.forEach((e) => e.addEventListener('click', toggleCollapse)); // Handle game filter input const gameSearch = document.getElementById('game-search'); gameSearch.value = ''; - gameSearch.addEventListener('input', (evt) => { if (!evt.target.value.trim()) { // If input is empty, display all collapsed games - return Array.from(gameHeaders).forEach((header) => { + return toggleButtons.forEach((header) => { header.style.display = null; - const gameName = header.getAttribute('data-game'); - document.getElementById(`${gameName}-arrow`).innerText = '▶'; - document.getElementById(gameName).classList.add('collapsed'); + header.firstElementChild.innerText = '▶'; + header.nextElementSibling.classList.add('collapsed'); }); } // Loop over all the games - Array.from(gameHeaders).forEach((header) => { - const gameName = header.getAttribute('data-game'); - const gameArrow = document.getElementById(`${gameName}-arrow`); - const gameInfo = document.getElementById(gameName); - + toggleButtons.forEach((header) => { // If the game name includes the search string, display the game. If not, hide it - if (gameName.toLowerCase().includes(evt.target.value.toLowerCase())) { + if (header.getAttribute('data-game').toLowerCase().includes(evt.target.value.toLowerCase())) { header.style.display = null; - gameArrow.innerText = '▼'; - gameInfo.classList.remove('collapsed'); + header.firstElementChild.innerText = '▼'; + header.nextElementSibling.classList.remove('collapsed'); } else { - console.log(header); header.style.display = 'none'; - gameArrow.innerText = '▶'; - gameInfo.classList.add('collapsed'); + header.firstElementChild.innerText = '▶'; + header.nextElementSibling.classList.add('collapsed'); } }); }); @@ -54,30 +35,30 @@ window.addEventListener('load', () => { document.getElementById('collapse-all').addEventListener('click', collapseAll); }); -const expandAll = () => { - const gameHeaders = document.getElementsByClassName('collapse-toggle'); - // Loop over all the games - Array.from(gameHeaders).forEach((header) => { - const gameName = header.getAttribute('data-game'); - const gameArrow = document.getElementById(`${gameName}-arrow`); - const gameInfo = document.getElementById(gameName); +const toggleCollapse = (evt) => { + const gameArrow = evt.target.firstElementChild; + const gameInfo = evt.target.nextElementSibling; + if (gameInfo.classList.contains('collapsed')) { + gameArrow.innerText = '▼'; + gameInfo.classList.remove('collapsed'); + } else { + gameArrow.innerText = '▶'; + gameInfo.classList.add('collapsed'); + } +}; - if (header.style.display === 'none') { return; } - gameArrow.innerText = '▼'; - gameInfo.classList.remove('collapsed'); - }); +const expandAll = () => { + document.querySelectorAll('.collapse-toggle').forEach((header) => { + if (header.style.display === 'none') { return; } + header.firstElementChild.innerText = '▼'; + header.nextElementSibling.classList.remove('collapsed'); + }); }; const collapseAll = () => { - const gameHeaders = document.getElementsByClassName('collapse-toggle'); - // Loop over all the games - Array.from(gameHeaders).forEach((header) => { - const gameName = header.getAttribute('data-game'); - const gameArrow = document.getElementById(`${gameName}-arrow`); - const gameInfo = document.getElementById(gameName); - - if (header.style.display === 'none') { return; } - gameArrow.innerText = '▶'; - gameInfo.classList.add('collapsed'); - }); + document.querySelectorAll('.collapse-toggle').forEach((header) => { + if (header.style.display === 'none') { return; } + header.firstElementChild.innerText = '▶'; + header.nextElementSibling.classList.add('collapsed'); + }); }; diff --git a/WebHostLib/static/styles/supportedGames.css b/WebHostLib/static/styles/supportedGames.css index 1e9a98c17a..7396daa954 100644 --- a/WebHostLib/static/styles/supportedGames.css +++ b/WebHostLib/static/styles/supportedGames.css @@ -18,10 +18,16 @@ margin-bottom: 2px; } +#games .collapse-toggle{ + cursor: pointer; +} + #games h2 .collapse-arrow{ font-size: 20px; + display: inline-block; /* make vertical-align work */ + padding-bottom: 9px; vertical-align: middle; - cursor: pointer; + padding-right: 8px; } #games p.collapsed{ @@ -42,12 +48,12 @@ margin-bottom: 7px; } -#games #page-controls{ +#games .page-controls{ display: flex; flex-direction: row; margin-top: 0.25rem; } -#games #page-controls button{ +#games .page-controls button{ margin-left: 0.5rem; } diff --git a/WebHostLib/templates/supportedGames.html b/WebHostLib/templates/supportedGames.html index 63b70216d7..f1514d8353 100644 --- a/WebHostLib/templates/supportedGames.html +++ b/WebHostLib/templates/supportedGames.html @@ -5,15 +5,35 @@ + {% endblock %} {% block body %} {% include 'header/oceanHeader.html' %}

    Currently Supported Games

    -
    +

    -
    +
    @@ -22,9 +42,9 @@ {% for game_name in worlds | title_sorted %} {% set world = worlds[game_name] %}

    -  {{ game_name }} + {{ game_name }}

    -
    + Starting Resources
    + Weapon & Armor Upgrades
    + Base
     
    +
    Infantry
    + Vehicles
    +
    Starships
    - Dominion -
    + Mercenaries
    - Lab Upgrades + + General Upgrades
    + Protoss Units
    Rooms:  + {% call macros.list_rooms(seed.rooms | selectattr("owner", "eq", session["_id"])) %}
  • Create New Room From e377068d1fcc1f7d09035ad5a65e66938de75273 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Mon, 2 Oct 2023 20:17:34 +0200 Subject: [PATCH 085/144] Core: more gitignore (#2249) gitignore versioned venvs, prof output, appimagetool and sni downloads --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index e374a12954..f4bcd35c32 100644 --- a/.gitignore +++ b/.gitignore @@ -33,11 +33,14 @@ setups build bundle/components.wxs dist +/prof/ README.html .vs/ EnemizerCLI/ /Players/ /SNI/ +/sni-*/ +/appimagetool* /host.yaml /options.yaml /config.yaml @@ -140,6 +143,7 @@ ipython_config.py .venv* env/ venv/ +/venv*/ ENV/ env.bak/ venv.bak/ From 24403eba1b167c1e3f45f57531a42867c301a76e Mon Sep 17 00:00:00 2001 From: Bryce Wilson Date: Mon, 2 Oct 2023 11:52:00 -0700 Subject: [PATCH 086/144] Launcher: Allow opening patches for clients without an exe (#2176) * Launcher: Allow opening patches for clients without an exe * Launcher: Restore behavior for not showing patch suffixes for clients that aren't installed --- Launcher.py | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/Launcher.py b/Launcher.py index a1548d594c..9e184bf108 100644 --- a/Launcher.py +++ b/Launcher.py @@ -50,17 +50,22 @@ def open_host_yaml(): def open_patch(): suffixes = [] for c in components: - if isfile(get_exe(c)[-1]): - suffixes += c.file_identifier.suffixes if c.type == Type.CLIENT and \ - isinstance(c.file_identifier, SuffixIdentifier) else [] + if c.type == Type.CLIENT and \ + isinstance(c.file_identifier, SuffixIdentifier) and \ + (c.script_name is None or isfile(get_exe(c)[-1])): + suffixes += c.file_identifier.suffixes try: - filename = open_filename('Select patch', (('Patches', suffixes),)) + filename = open_filename("Select patch", (("Patches", suffixes),)) except Exception as e: - messagebox('Error', str(e), error=True) + messagebox("Error", str(e), error=True) else: file, component = identify(filename) if file and component: - launch([*get_exe(component), file], component.cli) + exe = get_exe(component) + if exe is None or not isfile(exe[-1]): + exe = get_exe("Launcher") + + launch([*exe, file], component.cli) def generate_yamls(): @@ -107,7 +112,7 @@ def identify(path: Union[None, str]): return None, None for component in components: if component.handles_file(path): - return path, component + return path, component elif path == component.display_name or path == component.script_name: return None, component return None, None @@ -117,25 +122,25 @@ def get_exe(component: Union[str, Component]) -> Optional[Sequence[str]]: if isinstance(component, str): name = component component = None - if name.startswith('Archipelago'): + if name.startswith("Archipelago"): name = name[11:] - if name.endswith('.exe'): + if name.endswith(".exe"): name = name[:-4] - if name.endswith('.py'): + if name.endswith(".py"): name = name[:-3] if not name: return None for c in components: - if c.script_name == name or c.frozen_name == f'Archipelago{name}': + if c.script_name == name or c.frozen_name == f"Archipelago{name}": component = c break if not component: return None if is_frozen(): - suffix = '.exe' if is_windows else '' - return [local_path(f'{component.frozen_name}{suffix}')] + suffix = ".exe" if is_windows else "" + return [local_path(f"{component.frozen_name}{suffix}")] if component.frozen_name else None else: - return [sys.executable, local_path(f'{component.script_name}.py')] + return [sys.executable, local_path(f"{component.script_name}.py")] if component.script_name else None def launch(exe, in_terminal=False): From bc11c9dfd444b24e988d46326752d0d55189cba5 Mon Sep 17 00:00:00 2001 From: Bryce Wilson Date: Mon, 2 Oct 2023 17:44:19 -0700 Subject: [PATCH 087/144] BizHawkClient: Add BizHawkClient (#1978) Adds a generic client that can communicate with BizHawk. Similar to SNIClient, but for arbitrary systems and doesn't have an intermediary application like SNI. --- BizHawkClient.py | 9 + data/lua/base64.lua | 119 ++++++ data/lua/connector_bizhawk_generic.lua | 564 +++++++++++++++++++++++++ inno_setup.iss | 4 + worlds/LauncherComponents.py | 3 + worlds/_bizhawk/__init__.py | 326 ++++++++++++++ worlds/_bizhawk/client.py | 87 ++++ worlds/_bizhawk/context.py | 188 +++++++++ 8 files changed, 1300 insertions(+) create mode 100644 BizHawkClient.py create mode 100644 data/lua/base64.lua create mode 100644 data/lua/connector_bizhawk_generic.lua create mode 100644 worlds/_bizhawk/__init__.py create mode 100644 worlds/_bizhawk/client.py create mode 100644 worlds/_bizhawk/context.py diff --git a/BizHawkClient.py b/BizHawkClient.py new file mode 100644 index 0000000000..86c8e5197e --- /dev/null +++ b/BizHawkClient.py @@ -0,0 +1,9 @@ +from __future__ import annotations + +import ModuleUpdate +ModuleUpdate.update() + +from worlds._bizhawk.context import launch + +if __name__ == "__main__": + launch() diff --git a/data/lua/base64.lua b/data/lua/base64.lua new file mode 100644 index 0000000000..ebe8064353 --- /dev/null +++ b/data/lua/base64.lua @@ -0,0 +1,119 @@ +-- This file originates from this repository: https://github.com/iskolbin/lbase64 +-- It was modified to translate between base64 strings and lists of bytes instead of base64 strings and strings. + +local base64 = {} + +local extract = _G.bit32 and _G.bit32.extract -- Lua 5.2/Lua 5.3 in compatibility mode +if not extract then + if _G._VERSION == "Lua 5.4" then + extract = load[[return function( v, from, width ) + return ( v >> from ) & ((1 << width) - 1) + end]]() + elseif _G.bit then -- LuaJIT + local shl, shr, band = _G.bit.lshift, _G.bit.rshift, _G.bit.band + extract = function( v, from, width ) + return band( shr( v, from ), shl( 1, width ) - 1 ) + end + elseif _G._VERSION == "Lua 5.1" then + extract = function( v, from, width ) + local w = 0 + local flag = 2^from + for i = 0, width-1 do + local flag2 = flag + flag + if v % flag2 >= flag then + w = w + 2^i + end + flag = flag2 + end + return w + end + end +end + + +function base64.makeencoder( s62, s63, spad ) + local encoder = {} + for b64code, char in pairs{[0]='A','B','C','D','E','F','G','H','I','J', + 'K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y', + 'Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n', + 'o','p','q','r','s','t','u','v','w','x','y','z','0','1','2', + '3','4','5','6','7','8','9',s62 or '+',s63 or'/',spad or'='} do + encoder[b64code] = char:byte() + end + return encoder +end + +function base64.makedecoder( s62, s63, spad ) + local decoder = {} + for b64code, charcode in pairs( base64.makeencoder( s62, s63, spad )) do + decoder[charcode] = b64code + end + return decoder +end + +local DEFAULT_ENCODER = base64.makeencoder() +local DEFAULT_DECODER = base64.makedecoder() + +local char, concat = string.char, table.concat + +function base64.encode( arr, encoder ) + encoder = encoder or DEFAULT_ENCODER + local t, k, n = {}, 1, #arr + local lastn = n % 3 + for i = 1, n-lastn, 3 do + local a, b, c = arr[i], arr[i + 1], arr[i + 2] + local v = a*0x10000 + b*0x100 + c + local s + s = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[extract(v,6,6)], encoder[extract(v,0,6)]) + t[k] = s + k = k + 1 + end + if lastn == 2 then + local a, b = arr[n-1], arr[n] + local v = a*0x10000 + b*0x100 + t[k] = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[extract(v,6,6)], encoder[64]) + elseif lastn == 1 then + local v = arr[n]*0x10000 + t[k] = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[64], encoder[64]) + end + return concat( t ) +end + +function base64.decode( b64, decoder ) + decoder = decoder or DEFAULT_DECODER + local pattern = '[^%w%+%/%=]' + if decoder then + local s62, s63 + for charcode, b64code in pairs( decoder ) do + if b64code == 62 then s62 = charcode + elseif b64code == 63 then s63 = charcode + end + end + pattern = ('[^%%w%%%s%%%s%%=]'):format( char(s62), char(s63) ) + end + b64 = b64:gsub( pattern, '' ) + local t, k = {}, 1 + local n = #b64 + local padding = b64:sub(-2) == '==' and 2 or b64:sub(-1) == '=' and 1 or 0 + for i = 1, padding > 0 and n-4 or n, 4 do + local a, b, c, d = b64:byte( i, i+3 ) + local s + local v = decoder[a]*0x40000 + decoder[b]*0x1000 + decoder[c]*0x40 + decoder[d] + table.insert(t,extract(v,16,8)) + table.insert(t,extract(v,8,8)) + table.insert(t,extract(v,0,8)) + end + if padding == 1 then + local a, b, c = b64:byte( n-3, n-1 ) + local v = decoder[a]*0x40000 + decoder[b]*0x1000 + decoder[c]*0x40 + table.insert(t,extract(v,16,8)) + table.insert(t,extract(v,8,8)) + elseif padding == 2 then + local a, b = b64:byte( n-3, n-2 ) + local v = decoder[a]*0x40000 + decoder[b]*0x1000 + table.insert(t,extract(v,16,8)) + end + return t +end + +return base64 diff --git a/data/lua/connector_bizhawk_generic.lua b/data/lua/connector_bizhawk_generic.lua new file mode 100644 index 0000000000..b0b06de447 --- /dev/null +++ b/data/lua/connector_bizhawk_generic.lua @@ -0,0 +1,564 @@ +--[[ +Copyright (c) 2023 Zunawe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +]] + +local SCRIPT_VERSION = 1 + +--[[ +This script expects to receive JSON and will send JSON back. A message should +be a list of 1 or more requests which will be executed in order. Each request +will have a corresponding response in the same order. + +Every individual request and response is a JSON object with at minimum one +field `type`. The value of `type` determines what other fields may exist. + +To get the script version, instead of JSON, send "VERSION" to get the script +version directly (e.g. "2"). + +#### Ex. 1 + +Request: `[{"type": "PING"}]` + +Response: `[{"type": "PONG"}]` + +--- + +#### Ex. 2 + +Request: `[{"type": "LOCK"}, {"type": "HASH"}]` + +Response: `[{"type": "LOCKED"}, {"type": "HASH_RESPONSE", "value": "F7D18982"}]` + +--- + +#### Ex. 3 + +Request: + +```json +[ + {"type": "GUARD", "address": 100, "expected_data": "aGVsbG8=", "domain": "System Bus"}, + {"type": "READ", "address": 500, "size": 4, "domain": "ROM"} +] +``` + +Response: + +```json +[ + {"type": "GUARD_RESPONSE", "address": 100, "value": true}, + {"type": "READ_RESPONSE", "value": "dGVzdA=="} +] +``` + +--- + +#### Ex. 4 + +Request: + +```json +[ + {"type": "GUARD", "address": 100, "expected_data": "aGVsbG8=", "domain": "System Bus"}, + {"type": "READ", "address": 500, "size": 4, "domain": "ROM"} +] +``` + +Response: + +```json +[ + {"type": "GUARD_RESPONSE", "address": 100, "value": false}, + {"type": "GUARD_RESPONSE", "address": 100, "value": false} +] +``` + +--- + +### Supported Request Types + +- `PING` + Does nothing; resets timeout. + + Expected Response Type: `PONG` + +- `SYSTEM` + Returns the system of the currently loaded ROM (N64, GBA, etc...). + + Expected Response Type: `SYSTEM_RESPONSE` + +- `PREFERRED_CORES` + Returns the user's default cores for systems with multiple cores. If the + current ROM's system has multiple cores, the one that is currently + running is very probably the preferred core. + + Expected Response Type: `PREFERRED_CORES_RESPONSE` + +- `HASH` + Returns the hash of the currently loaded ROM calculated by BizHawk. + + Expected Response Type: `HASH_RESPONSE` + +- `GUARD` + Checks a section of memory against `expected_data`. If the bytes starting + at `address` do not match `expected_data`, the response will have `value` + set to `false`, and all subsequent requests will not be executed and + receive the same `GUARD_RESPONSE`. + + Expected Response Type: `GUARD_RESPONSE` + + Additional Fields: + - `address` (`int`): The address of the memory to check + - `expected_data` (string): A base64 string of contiguous data + - `domain` (`string`): The name of the memory domain the address + corresponds to + +- `LOCK` + Halts emulation and blocks on incoming requests until an `UNLOCK` request + is received or the client times out. All requests processed while locked + will happen on the same frame. + + Expected Response Type: `LOCKED` + +- `UNLOCK` + Resumes emulation after the current list of requests is done being + executed. + + Expected Response Type: `UNLOCKED` + +- `READ` + Reads an array of bytes at the provided address. + + Expected Response Type: `READ_RESPONSE` + + Additional Fields: + - `address` (`int`): The address of the memory to read + - `size` (`int`): The number of bytes to read + - `domain` (`string`): The name of the memory domain the address + corresponds to + +- `WRITE` + Writes an array of bytes to the provided address. + + Expected Response Type: `WRITE_RESPONSE` + + Additional Fields: + - `address` (`int`): The address of the memory to write to + - `value` (`string`): A base64 string representing the data to write + - `domain` (`string`): The name of the memory domain the address + corresponds to + +- `DISPLAY_MESSAGE` + Adds a message to the message queue which will be displayed using + `gui.addmessage` according to the message interval. + + Expected Response Type: `DISPLAY_MESSAGE_RESPONSE` + + Additional Fields: + - `message` (`string`): The string to display + +- `SET_MESSAGE_INTERVAL` + Sets the minimum amount of time to wait between displaying messages. + Potentially useful if you add many messages quickly but want players + to be able to read each of them. + + Expected Response Type: `SET_MESSAGE_INTERVAL_RESPONSE` + + Additional Fields: + - `value` (`number`): The number of seconds to set the interval to + + +### Response Types + +- `PONG` + Acknowledges `PING`. + +- `SYSTEM_RESPONSE` + Contains the name of the system for currently running ROM. + + Additional Fields: + - `value` (`string`): The returned system name + +- `PREFERRED_CORES_RESPONSE` + Contains the user's preferred cores for systems with multiple supported + cores. Currently includes NES, SNES, GB, GBC, DGB, SGB, PCE, PCECD, and + SGX. + + Additional Fields: + - `value` (`{[string]: [string]}`): A dictionary map from system name to + core name + +- `HASH_RESPONSE` + Contains the hash of the currently loaded ROM calculated by BizHawk. + + Additional Fields: + - `value` (`string`): The returned hash + +- `GUARD_RESPONSE` + The result of an attempted `GUARD` request. + + Additional Fields: + - `value` (`boolean`): true if the memory was validated, false if not + - `address` (`int`): The address of the memory that was invalid (the same + address provided by the `GUARD`, not the address of the individual invalid + byte) + +- `LOCKED` + Acknowledges `LOCK`. + +- `UNLOCKED` + Acknowledges `UNLOCK`. + +- `READ_RESPONSE` + Contains the result of a `READ` request. + + Additional Fields: + - `value` (`string`): A base64 string representing the read data + +- `WRITE_RESPONSE` + Acknowledges `WRITE`. + +- `DISPLAY_MESSAGE_RESPONSE` + Acknowledges `DISPLAY_MESSAGE`. + +- `SET_MESSAGE_INTERVAL_RESPONSE` + Acknowledges `SET_MESSAGE_INTERVAL`. + +- `ERROR` + Signifies that something has gone wrong while processing a request. + + Additional Fields: + - `err` (`string`): A description of the problem +]] + +local base64 = require("base64") +local socket = require("socket") +local json = require("json") + +-- Set to log incoming requests +-- Will cause lag due to large console output +local DEBUG = false + +local SOCKET_PORT = 43055 + +local STATE_NOT_CONNECTED = 0 +local STATE_CONNECTED = 1 + +local server = nil +local client_socket = nil + +local current_state = STATE_NOT_CONNECTED + +local timeout_timer = 0 +local message_timer = 0 +local message_interval = 0 +local prev_time = 0 +local current_time = 0 + +local locked = false + +local rom_hash = nil + +local lua_major, lua_minor = _VERSION:match("Lua (%d+)%.(%d+)") +lua_major = tonumber(lua_major) +lua_minor = tonumber(lua_minor) + +if lua_major > 5 or (lua_major == 5 and lua_minor >= 3) then + require("lua_5_3_compat") +end + +local bizhawk_version = client.getversion() +local bizhawk_major, bizhawk_minor, bizhawk_patch = bizhawk_version:match("(%d+)%.(%d+)%.?(%d*)") +bizhawk_major = tonumber(bizhawk_major) +bizhawk_minor = tonumber(bizhawk_minor) +if bizhawk_patch == "" then + bizhawk_patch = 0 +else + bizhawk_patch = tonumber(bizhawk_patch) +end + +function queue_push (self, value) + self[self.right] = value + self.right = self.right + 1 +end + +function queue_is_empty (self) + return self.right == self.left +end + +function queue_shift (self) + value = self[self.left] + self[self.left] = nil + self.left = self.left + 1 + return value +end + +function new_queue () + local queue = {left = 1, right = 1} + return setmetatable(queue, {__index = {is_empty = queue_is_empty, push = queue_push, shift = queue_shift}}) +end + +local message_queue = new_queue() + +function lock () + locked = true + client_socket:settimeout(2) +end + +function unlock () + locked = false + client_socket:settimeout(0) +end + +function process_request (req) + local res = {} + + if req["type"] == "PING" then + res["type"] = "PONG" + + elseif req["type"] == "SYSTEM" then + res["type"] = "SYSTEM_RESPONSE" + res["value"] = emu.getsystemid() + + elseif req["type"] == "PREFERRED_CORES" then + local preferred_cores = client.getconfig().PreferredCores + res["type"] = "PREFERRED_CORES_RESPONSE" + res["value"] = {} + res["value"]["NES"] = preferred_cores.NES + res["value"]["SNES"] = preferred_cores.SNES + res["value"]["GB"] = preferred_cores.GB + res["value"]["GBC"] = preferred_cores.GBC + res["value"]["DGB"] = preferred_cores.DGB + res["value"]["SGB"] = preferred_cores.SGB + res["value"]["PCE"] = preferred_cores.PCE + res["value"]["PCECD"] = preferred_cores.PCECD + res["value"]["SGX"] = preferred_cores.SGX + + elseif req["type"] == "HASH" then + res["type"] = "HASH_RESPONSE" + res["value"] = rom_hash + + elseif req["type"] == "GUARD" then + res["type"] = "GUARD_RESPONSE" + local expected_data = base64.decode(req["expected_data"]) + + local actual_data = memory.read_bytes_as_array(req["address"], #expected_data, req["domain"]) + + local data_is_validated = true + for i, byte in ipairs(actual_data) do + if byte ~= expected_data[i] then + data_is_validated = false + break + end + end + + res["value"] = data_is_validated + res["address"] = req["address"] + + elseif req["type"] == "LOCK" then + res["type"] = "LOCKED" + lock() + + elseif req["type"] == "UNLOCK" then + res["type"] = "UNLOCKED" + unlock() + + elseif req["type"] == "READ" then + res["type"] = "READ_RESPONSE" + res["value"] = base64.encode(memory.read_bytes_as_array(req["address"], req["size"], req["domain"])) + + elseif req["type"] == "WRITE" then + res["type"] = "WRITE_RESPONSE" + memory.write_bytes_as_array(req["address"], base64.decode(req["value"]), req["domain"]) + + elseif req["type"] == "DISPLAY_MESSAGE" then + res["type"] = "DISPLAY_MESSAGE_RESPONSE" + message_queue:push(req["message"]) + + elseif req["type"] == "SET_MESSAGE_INTERVAL" then + res["type"] = "SET_MESSAGE_INTERVAL_RESPONSE" + message_interval = req["value"] + + else + res["type"] = "ERROR" + res["err"] = "Unknown command: "..req["type"] + end + + return res +end + +-- Receive data from AP client and send message back +function send_receive () + local message, err = client_socket:receive() + + -- Handle errors + if err == "closed" then + if current_state == STATE_CONNECTED then + print("Connection to client closed") + end + current_state = STATE_NOT_CONNECTED + return + elseif err == "timeout" then + unlock() + return + elseif err ~= nil then + print(err) + current_state = STATE_NOT_CONNECTED + unlock() + return + end + + -- Reset timeout timer + timeout_timer = 5 + + -- Process received data + if DEBUG then + print("Received Message ["..emu.framecount().."]: "..'"'..message..'"') + end + + if message == "VERSION" then + local result, err client_socket:send(tostring(SCRIPT_VERSION).."\n") + else + local res = {} + local data = json.decode(message) + local failed_guard_response = nil + for i, req in ipairs(data) do + if failed_guard_response ~= nil then + res[i] = failed_guard_response + else + -- An error is more likely to cause an NLua exception than to return an error here + local status, response = pcall(process_request, req) + if status then + res[i] = response + + -- If the GUARD validation failed, skip the remaining commands + if response["type"] == "GUARD_RESPONSE" and not response["value"] then + failed_guard_response = response + end + else + res[i] = {type = "ERROR", err = response} + end + end + end + + client_socket:send(json.encode(res).."\n") + end +end + +function main () + server, err = socket.bind("localhost", SOCKET_PORT) + if err ~= nil then + print(err) + return + end + + while true do + current_time = socket.socket.gettime() + timeout_timer = timeout_timer - (current_time - prev_time) + message_timer = message_timer - (current_time - prev_time) + prev_time = current_time + + if message_timer <= 0 and not message_queue:is_empty() then + gui.addmessage(message_queue:shift()) + message_timer = message_interval + end + + if current_state == STATE_NOT_CONNECTED then + if emu.framecount() % 60 == 0 then + server:settimeout(2) + local client, timeout = server:accept() + if timeout == nil then + print("Client connected") + current_state = STATE_CONNECTED + client_socket = client + client_socket:settimeout(0) + else + print("No client found. Trying again...") + end + end + else + repeat + send_receive() + until not locked + + if timeout_timer <= 0 then + print("Client timed out") + current_state = STATE_NOT_CONNECTED + end + end + + coroutine.yield() + end +end + +event.onexit(function () + print("\n-- Restarting Script --\n") + if server ~= nil then + server:close() + end +end) + +if bizhawk_major < 2 or (bizhawk_major == 2 and bizhawk_minor < 7) then + print("Must use BizHawk 2.7.0 or newer") +elseif bizhawk_major > 2 or (bizhawk_major == 2 and bizhawk_minor > 9) then + print("Warning: This version of BizHawk is newer than this script. If it doesn't work, consider downgrading to 2.9.") +else + if emu.getsystemid() == "NULL" then + print("No ROM is loaded. Please load a ROM.") + while emu.getsystemid() == "NULL" do + emu.frameadvance() + end + end + + rom_hash = gameinfo.getromhash() + + print("Waiting for client to connect. Emulation will freeze intermittently until a client is found.\n") + + local co = coroutine.create(main) + function tick () + local status, err = coroutine.resume(co) + + if not status then + print("\nERROR: "..err) + print("Consider reporting this crash.\n") + + if server ~= nil then + server:close() + end + + co = coroutine.create(main) + end + end + + -- Gambatte has a setting which can cause script execution to become + -- misaligned, so for GB and GBC we explicitly set the callback on + -- vblank instead. + -- https://github.com/TASEmulators/BizHawk/issues/3711 + if emu.getsystemid() == "GB" or emu.getsystemid() == "GBC" then + event.onmemoryexecute(tick, 0x40, "tick", "System Bus") + else + event.onframeend(tick) + end + + while true do + emu.frameadvance() + end +end diff --git a/inno_setup.iss b/inno_setup.iss index 147cd74dca..3c1bdc4571 100644 --- a/inno_setup.iss +++ b/inno_setup.iss @@ -74,6 +74,7 @@ Name: "client/sni/sm"; Description: "SNI Client - Super Metroid Patch Setup"; Name: "client/sni/dkc3"; Description: "SNI Client - Donkey Kong Country 3 Patch Setup"; Types: full playing; Flags: disablenouninstallwarning Name: "client/sni/smw"; Description: "SNI Client - Super Mario World Patch Setup"; Types: full playing; Flags: disablenouninstallwarning Name: "client/sni/l2ac"; Description: "SNI Client - Lufia II Ancient Cave Patch Setup"; Types: full playing; Flags: disablenouninstallwarning +Name: "client/bizhawk"; Description: "BizHawk Client"; Types: full playing Name: "client/factorio"; Description: "Factorio"; Types: full playing Name: "client/kh2"; Description: "Kingdom Hearts 2"; Types: full playing Name: "client/minecraft"; Description: "Minecraft"; Types: full playing; ExtraDiskSpaceRequired: 226894278 @@ -122,6 +123,7 @@ Source: "{#source_path}\ArchipelagoServer.exe"; DestDir: "{app}"; Flags: ignorev Source: "{#source_path}\ArchipelagoFactorioClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/factorio Source: "{#source_path}\ArchipelagoTextClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/text Source: "{#source_path}\ArchipelagoSNIClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/sni +Source: "{#source_path}\ArchipelagoBizHawkClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/bizhawk Source: "{#source_path}\ArchipelagoLinksAwakeningClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/ladx Source: "{#source_path}\ArchipelagoLttPAdjuster.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/sni/lttp or generator/lttp Source: "{#source_path}\ArchipelagoMinecraftClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/minecraft @@ -146,6 +148,7 @@ Name: "{group}\{#MyAppName} Launcher"; Filename: "{app}\ArchipelagoLauncher.exe" Name: "{group}\{#MyAppName} Server"; Filename: "{app}\ArchipelagoServer"; Components: server Name: "{group}\{#MyAppName} Text Client"; Filename: "{app}\ArchipelagoTextClient.exe"; Components: client/text Name: "{group}\{#MyAppName} SNI Client"; Filename: "{app}\ArchipelagoSNIClient.exe"; Components: client/sni +Name: "{group}\{#MyAppName} BizHawk Client"; Filename: "{app}\ArchipelagoBizHawkClient.exe"; Components: client/bizhawk Name: "{group}\{#MyAppName} Factorio Client"; Filename: "{app}\ArchipelagoFactorioClient.exe"; Components: client/factorio Name: "{group}\{#MyAppName} Minecraft Client"; Filename: "{app}\ArchipelagoMinecraftClient.exe"; Components: client/minecraft Name: "{group}\{#MyAppName} Ocarina of Time Client"; Filename: "{app}\ArchipelagoOoTClient.exe"; Components: client/oot @@ -166,6 +169,7 @@ Name: "{commondesktop}\{#MyAppName} Folder"; Filename: "{app}"; Tasks: desktopic Name: "{commondesktop}\{#MyAppName} Launcher"; Filename: "{app}\ArchipelagoLauncher.exe"; Tasks: desktopicon Name: "{commondesktop}\{#MyAppName} Server"; Filename: "{app}\ArchipelagoServer"; Tasks: desktopicon; Components: server Name: "{commondesktop}\{#MyAppName} SNI Client"; Filename: "{app}\ArchipelagoSNIClient.exe"; Tasks: desktopicon; Components: client/sni +Name: "{commondesktop}\{#MyAppName} BizHawk Client"; Filename: "{app}\ArchipelagoBizHawkClient.exe"; Tasks: desktopicon; Components: client/bizhawk Name: "{commondesktop}\{#MyAppName} Factorio Client"; Filename: "{app}\ArchipelagoFactorioClient.exe"; Tasks: desktopicon; Components: client/factorio Name: "{commondesktop}\{#MyAppName} Minecraft Client"; Filename: "{app}\ArchipelagoMinecraftClient.exe"; Tasks: desktopicon; Components: client/minecraft Name: "{commondesktop}\{#MyAppName} Ocarina of Time Client"; Filename: "{app}\ArchipelagoOoTClient.exe"; Tasks: desktopicon; Components: client/oot diff --git a/worlds/LauncherComponents.py b/worlds/LauncherComponents.py index c3ae2b0495..2d445a77b8 100644 --- a/worlds/LauncherComponents.py +++ b/worlds/LauncherComponents.py @@ -89,6 +89,9 @@ components: List[Component] = [ Component('SNI Client', 'SNIClient', file_identifier=SuffixIdentifier('.apz3', '.apm3', '.apsoe', '.aplttp', '.apsm', '.apsmz3', '.apdkc3', '.apsmw', '.apl2ac')), + # BizHawk + Component("BizHawk Client", "BizHawkClient", component_type=Type.CLIENT, + file_identifier=SuffixIdentifier()), Component('Links Awakening DX Client', 'LinksAwakeningClient', file_identifier=SuffixIdentifier('.apladx')), Component('LttP Adjuster', 'LttPAdjuster'), diff --git a/worlds/_bizhawk/__init__.py b/worlds/_bizhawk/__init__.py new file mode 100644 index 0000000000..cdf227ec7b --- /dev/null +++ b/worlds/_bizhawk/__init__.py @@ -0,0 +1,326 @@ +""" +A module for interacting with BizHawk through `connector_bizhawk_generic.lua`. + +Any mention of `domain` in this module refers to the names BizHawk gives to memory domains in its own lua api. They are +naively passed to BizHawk without validation or modification. +""" + +import asyncio +import base64 +import enum +import json +import typing + + +BIZHAWK_SOCKET_PORT = 43055 +EXPECTED_SCRIPT_VERSION = 1 + + +class ConnectionStatus(enum.IntEnum): + NOT_CONNECTED = 1 + TENTATIVE = 2 + CONNECTED = 3 + + +class BizHawkContext: + streams: typing.Optional[typing.Tuple[asyncio.StreamReader, asyncio.StreamWriter]] + connection_status: ConnectionStatus + + def __init__(self) -> None: + self.streams = None + self.connection_status = ConnectionStatus.NOT_CONNECTED + + +class NotConnectedError(Exception): + """Raised when something tries to make a request to the connector script before a connection has been established""" + pass + + +class RequestFailedError(Exception): + """Raised when the connector script did not respond to a request""" + pass + + +class ConnectorError(Exception): + """Raised when the connector script encounters an error while processing a request""" + pass + + +class SyncError(Exception): + """Raised when the connector script responded with a mismatched response type""" + pass + + +async def connect(ctx: BizHawkContext) -> bool: + """Attempts to establish a connection with the connector script. Returns True if successful.""" + try: + ctx.streams = await asyncio.open_connection("localhost", BIZHAWK_SOCKET_PORT) + ctx.connection_status = ConnectionStatus.TENTATIVE + return True + except (TimeoutError, ConnectionRefusedError): + ctx.streams = None + ctx.connection_status = ConnectionStatus.NOT_CONNECTED + return False + + +def disconnect(ctx: BizHawkContext) -> None: + """Closes the connection to the connector script.""" + if ctx.streams is not None: + ctx.streams[1].close() + ctx.streams = None + ctx.connection_status = ConnectionStatus.NOT_CONNECTED + + +async def get_script_version(ctx: BizHawkContext) -> int: + if ctx.streams is None: + raise NotConnectedError("You tried to send a request before a connection to BizHawk was made") + + try: + reader, writer = ctx.streams + writer.write("VERSION".encode("ascii") + b"\n") + await asyncio.wait_for(writer.drain(), timeout=5) + + version = await asyncio.wait_for(reader.readline(), timeout=5) + + if version == b"": + writer.close() + ctx.streams = None + ctx.connection_status = ConnectionStatus.NOT_CONNECTED + raise RequestFailedError("Connection closed") + + return int(version.decode("ascii")) + except asyncio.TimeoutError as exc: + writer.close() + ctx.streams = None + ctx.connection_status = ConnectionStatus.NOT_CONNECTED + raise RequestFailedError("Connection timed out") from exc + except ConnectionResetError as exc: + writer.close() + ctx.streams = None + ctx.connection_status = ConnectionStatus.NOT_CONNECTED + raise RequestFailedError("Connection reset") from exc + + +async def send_requests(ctx: BizHawkContext, req_list: typing.List[typing.Dict[str, typing.Any]]) -> typing.List[typing.Dict[str, typing.Any]]: + """Sends a list of requests to the BizHawk connector and returns their responses. + + It's likely you want to use the wrapper functions instead of this.""" + if ctx.streams is None: + raise NotConnectedError("You tried to send a request before a connection to BizHawk was made") + + try: + reader, writer = ctx.streams + writer.write(json.dumps(req_list).encode("utf-8") + b"\n") + await asyncio.wait_for(writer.drain(), timeout=5) + + res = await asyncio.wait_for(reader.readline(), timeout=5) + + if res == b"": + writer.close() + ctx.streams = None + ctx.connection_status = ConnectionStatus.NOT_CONNECTED + raise RequestFailedError("Connection closed") + + if ctx.connection_status == ConnectionStatus.TENTATIVE: + ctx.connection_status = ConnectionStatus.CONNECTED + + ret = json.loads(res.decode("utf-8")) + for response in ret: + if response["type"] == "ERROR": + raise ConnectorError(response["err"]) + + return ret + except asyncio.TimeoutError as exc: + writer.close() + ctx.streams = None + ctx.connection_status = ConnectionStatus.NOT_CONNECTED + raise RequestFailedError("Connection timed out") from exc + except ConnectionResetError as exc: + writer.close() + ctx.streams = None + ctx.connection_status = ConnectionStatus.NOT_CONNECTED + raise RequestFailedError("Connection reset") from exc + + +async def ping(ctx: BizHawkContext) -> None: + """Sends a PING request and receives a PONG response.""" + res = (await send_requests(ctx, [{"type": "PING"}]))[0] + + if res["type"] != "PONG": + raise SyncError(f"Expected response of type PONG but got {res['type']}") + + +async def get_hash(ctx: BizHawkContext) -> str: + """Gets the system name for the currently loaded ROM""" + res = (await send_requests(ctx, [{"type": "HASH"}]))[0] + + if res["type"] != "HASH_RESPONSE": + raise SyncError(f"Expected response of type HASH_RESPONSE but got {res['type']}") + + return res["value"] + + +async def get_system(ctx: BizHawkContext) -> str: + """Gets the system name for the currently loaded ROM""" + res = (await send_requests(ctx, [{"type": "SYSTEM"}]))[0] + + if res["type"] != "SYSTEM_RESPONSE": + raise SyncError(f"Expected response of type SYSTEM_RESPONSE but got {res['type']}") + + return res["value"] + + +async def get_cores(ctx: BizHawkContext) -> typing.Dict[str, str]: + """Gets the preferred cores for systems with multiple cores. Only systems with multiple available cores have + entries.""" + res = (await send_requests(ctx, [{"type": "PREFERRED_CORES"}]))[0] + + if res["type"] != "PREFERRED_CORES_RESPONSE": + raise SyncError(f"Expected response of type PREFERRED_CORES_RESPONSE but got {res['type']}") + + return res["value"] + + +async def lock(ctx: BizHawkContext) -> None: + """Locks BizHawk in anticipation of receiving more requests this frame. + + Consider using guarded reads and writes instead of locks if possible. + + While locked, emulation will halt and the connector will block on incoming requests until an `UNLOCK` request is + sent. Remember to unlock when you're done, or the emulator will appear to freeze. + + Sending multiple lock commands is the same as sending one.""" + res = (await send_requests(ctx, [{"type": "LOCK"}]))[0] + + if res["type"] != "LOCKED": + raise SyncError(f"Expected response of type LOCKED but got {res['type']}") + + +async def unlock(ctx: BizHawkContext) -> None: + """Unlocks BizHawk to allow it to resume emulation. See `lock` for more info. + + Sending multiple unlock commands is the same as sending one.""" + res = (await send_requests(ctx, [{"type": "UNLOCK"}]))[0] + + if res["type"] != "UNLOCKED": + raise SyncError(f"Expected response of type UNLOCKED but got {res['type']}") + + +async def display_message(ctx: BizHawkContext, message: str) -> None: + """Displays the provided message in BizHawk's message queue.""" + res = (await send_requests(ctx, [{"type": "DISPLAY_MESSAGE", "message": message}]))[0] + + if res["type"] != "DISPLAY_MESSAGE_RESPONSE": + raise SyncError(f"Expected response of type DISPLAY_MESSAGE_RESPONSE but got {res['type']}") + + +async def set_message_interval(ctx: BizHawkContext, value: float) -> None: + """Sets the minimum amount of time in seconds to wait between queued messages. The default value of 0 will allow one + new message to display per frame.""" + res = (await send_requests(ctx, [{"type": "SET_MESSAGE_INTERVAL", "value": value}]))[0] + + if res["type"] != "SET_MESSAGE_INTERVAL_RESPONSE": + raise SyncError(f"Expected response of type SET_MESSAGE_INTERVAL_RESPONSE but got {res['type']}") + + +async def guarded_read(ctx: BizHawkContext, read_list: typing.List[typing.Tuple[int, int, str]], + guard_list: typing.List[typing.Tuple[int, typing.Iterable[int], str]]) -> typing.Optional[typing.List[bytes]]: + """Reads an array of bytes at 1 or more addresses if and only if every byte in guard_list matches its expected + value. + + Items in read_list should be organized (address, size, domain) where + - `address` is the address of the first byte of data + - `size` is the number of bytes to read + - `domain` is the name of the region of memory the address corresponds to + + Items in `guard_list` should be organized `(address, expected_data, domain)` where + - `address` is the address of the first byte of data + - `expected_data` is the bytes that the data starting at this address is expected to match + - `domain` is the name of the region of memory the address corresponds to + + Returns None if any item in guard_list failed to validate. Otherwise returns a list of bytes in the order they + were requested.""" + res = await send_requests(ctx, [{ + "type": "GUARD", + "address": address, + "expected_data": base64.b64encode(bytes(expected_data)).decode("ascii"), + "domain": domain + } for address, expected_data, domain in guard_list] + [{ + "type": "READ", + "address": address, + "size": size, + "domain": domain + } for address, size, domain in read_list]) + + ret: typing.List[bytes] = [] + for item in res: + if item["type"] == "GUARD_RESPONSE": + if not item["value"]: + return None + else: + if item["type"] != "READ_RESPONSE": + raise SyncError(f"Expected response of type READ_RESPONSE or GUARD_RESPONSE but got {res['type']}") + + ret.append(base64.b64decode(item["value"])) + + return ret + + +async def read(ctx: BizHawkContext, read_list: typing.List[typing.Tuple[int, int, str]]) -> typing.List[bytes]: + """Reads data at 1 or more addresses. + + Items in `read_list` should be organized `(address, size, domain)` where + - `address` is the address of the first byte of data + - `size` is the number of bytes to read + - `domain` is the name of the region of memory the address corresponds to + + Returns a list of bytes in the order they were requested.""" + return await guarded_read(ctx, read_list, []) + + +async def guarded_write(ctx: BizHawkContext, write_list: typing.List[typing.Tuple[int, typing.Iterable[int], str]], + guard_list: typing.List[typing.Tuple[int, typing.Iterable[int], str]]) -> bool: + """Writes data to 1 or more addresses if and only if every byte in guard_list matches its expected value. + + Items in `write_list` should be organized `(address, value, domain)` where + - `address` is the address of the first byte of data + - `value` is a list of bytes to write, in order, starting at `address` + - `domain` is the name of the region of memory the address corresponds to + + Items in `guard_list` should be organized `(address, expected_data, domain)` where + - `address` is the address of the first byte of data + - `expected_data` is the bytes that the data starting at this address is expected to match + - `domain` is the name of the region of memory the address corresponds to + + Returns False if any item in guard_list failed to validate. Otherwise returns True.""" + res = await send_requests(ctx, [{ + "type": "GUARD", + "address": address, + "expected_data": base64.b64encode(bytes(expected_data)).decode("ascii"), + "domain": domain + } for address, expected_data, domain in guard_list] + [{ + "type": "WRITE", + "address": address, + "value": base64.b64encode(bytes(value)).decode("ascii"), + "domain": domain + } for address, value, domain in write_list]) + + for item in res: + if item["type"] == "GUARD_RESPONSE": + if not item["value"]: + return False + else: + if item["type"] != "WRITE_RESPONSE": + raise SyncError(f"Expected response of type WRITE_RESPONSE or GUARD_RESPONSE but got {res['type']}") + + return True + + +async def write(ctx: BizHawkContext, write_list: typing.List[typing.Tuple[int, typing.Iterable[int], str]]) -> None: + """Writes data to 1 or more addresses. + + Items in write_list should be organized `(address, value, domain)` where + - `address` is the address of the first byte of data + - `value` is a list of bytes to write, in order, starting at `address` + - `domain` is the name of the region of memory the address corresponds to""" + await guarded_write(ctx, write_list, []) diff --git a/worlds/_bizhawk/client.py b/worlds/_bizhawk/client.py new file mode 100644 index 0000000000..b614c083ba --- /dev/null +++ b/worlds/_bizhawk/client.py @@ -0,0 +1,87 @@ +""" +A module containing the BizHawkClient base class and metaclass +""" + + +from __future__ import annotations + +import abc +from typing import TYPE_CHECKING, Any, ClassVar, Dict, Optional, Tuple, Union + +from worlds.LauncherComponents import Component, SuffixIdentifier, Type, components, launch_subprocess + +if TYPE_CHECKING: + from .context import BizHawkClientContext +else: + BizHawkClientContext = object + + +class AutoBizHawkClientRegister(abc.ABCMeta): + game_handlers: ClassVar[Dict[Tuple[str, ...], Dict[str, BizHawkClient]]] = {} + + def __new__(cls, name: str, bases: Tuple[type, ...], namespace: Dict[str, Any]) -> AutoBizHawkClientRegister: + new_class = super().__new__(cls, name, bases, namespace) + + if "system" in namespace: + systems = (namespace["system"],) if type(namespace["system"]) is str else tuple(sorted(namespace["system"])) + if systems not in AutoBizHawkClientRegister.game_handlers: + AutoBizHawkClientRegister.game_handlers[systems] = {} + + if "game" in namespace: + AutoBizHawkClientRegister.game_handlers[systems][namespace["game"]] = new_class() + + return new_class + + @staticmethod + async def get_handler(ctx: BizHawkClientContext, system: str) -> Optional[BizHawkClient]: + for systems, handlers in AutoBizHawkClientRegister.game_handlers.items(): + if system in systems: + for handler in handlers.values(): + if await handler.validate_rom(ctx): + return handler + + return None + + +class BizHawkClient(abc.ABC, metaclass=AutoBizHawkClientRegister): + system: ClassVar[Union[str, Tuple[str, ...]]] + """The system that the game this client is for runs on""" + + game: ClassVar[str] + """The game this client is for""" + + @abc.abstractmethod + async def validate_rom(self, ctx: BizHawkClientContext) -> bool: + """Should return whether the currently loaded ROM should be handled by this client. You might read the game name + from the ROM header, for example. This function will only be asked to validate ROMs from the system set by the + client class, so you do not need to check the system yourself. + + Once this function has determined that the ROM should be handled by this client, it should also modify `ctx` + as necessary (such as setting `ctx.game = self.game`, modifying `ctx.items_handling`, etc...).""" + ... + + async def set_auth(self, ctx: BizHawkClientContext) -> None: + """Should set ctx.auth in anticipation of sending a `Connected` packet. You may override this if you store slot + name in your patched ROM. If ctx.auth is not set after calling, the player will be prompted to enter their + username.""" + pass + + @abc.abstractmethod + async def game_watcher(self, ctx: BizHawkClientContext) -> None: + """Runs on a loop with the approximate interval `ctx.watcher_timeout`. The currently loaded ROM is guaranteed + to have passed your validator when this function is called, and the emulator is very likely to be connected.""" + ... + + def on_package(self, ctx: BizHawkClientContext, cmd: str, args: dict) -> None: + """For handling packages from the server. Called from `BizHawkClientContext.on_package`.""" + pass + + +def launch_client(*args) -> None: + from .context import launch + launch_subprocess(launch, name="BizHawkClient") + + +if not any(component.script_name == "BizHawkClient" for component in components): + components.append(Component("BizHawk Client", "BizHawkClient", component_type=Type.CLIENT, func=launch_client, + file_identifier=SuffixIdentifier())) diff --git a/worlds/_bizhawk/context.py b/worlds/_bizhawk/context.py new file mode 100644 index 0000000000..6e53b370af --- /dev/null +++ b/worlds/_bizhawk/context.py @@ -0,0 +1,188 @@ +""" +A module containing context and functions relevant to running the client. This module should only be imported for type +checking or launching the client, otherwise it will probably cause circular import issues. +""" + + +import asyncio +import traceback +from typing import Any, Dict, Optional + +from CommonClient import CommonContext, ClientCommandProcessor, get_base_parser, server_loop, logger, gui_enabled +import Patch +import Utils + +from . import BizHawkContext, ConnectionStatus, RequestFailedError, connect, disconnect, get_hash, get_script_version, \ + get_system, ping +from .client import BizHawkClient, AutoBizHawkClientRegister + + +EXPECTED_SCRIPT_VERSION = 1 + + +class BizHawkClientCommandProcessor(ClientCommandProcessor): + def _cmd_bh(self): + """Shows the current status of the client's connection to BizHawk""" + if isinstance(self.ctx, BizHawkClientContext): + if self.ctx.bizhawk_ctx.connection_status == ConnectionStatus.NOT_CONNECTED: + logger.info("BizHawk Connection Status: Not Connected") + elif self.ctx.bizhawk_ctx.connection_status == ConnectionStatus.TENTATIVE: + logger.info("BizHawk Connection Status: Tentatively Connected") + elif self.ctx.bizhawk_ctx.connection_status == ConnectionStatus.CONNECTED: + logger.info("BizHawk Connection Status: Connected") + + +class BizHawkClientContext(CommonContext): + command_processor = BizHawkClientCommandProcessor + client_handler: Optional[BizHawkClient] + slot_data: Optional[Dict[str, Any]] = None + rom_hash: Optional[str] = None + bizhawk_ctx: BizHawkContext + + watcher_timeout: float + """The maximum amount of time the game watcher loop will wait for an update from the server before executing""" + + def __init__(self, server_address: Optional[str], password: Optional[str]): + super().__init__(server_address, password) + self.client_handler = None + self.bizhawk_ctx = BizHawkContext() + self.watcher_timeout = 0.5 + + def run_gui(self): + from kvui import GameManager + + class BizHawkManager(GameManager): + base_title = "Archipelago BizHawk Client" + + self.ui = BizHawkManager(self) + self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI") + + def on_package(self, cmd, args): + if cmd == "Connected": + self.slot_data = args.get("slot_data", None) + + if self.client_handler is not None: + self.client_handler.on_package(self, cmd, args) + + +async def _game_watcher(ctx: BizHawkClientContext): + showed_connecting_message = False + showed_connected_message = False + showed_no_handler_message = False + + while not ctx.exit_event.is_set(): + try: + await asyncio.wait_for(ctx.watcher_event.wait(), ctx.watcher_timeout) + except asyncio.TimeoutError: + pass + + ctx.watcher_event.clear() + + try: + if ctx.bizhawk_ctx.connection_status == ConnectionStatus.NOT_CONNECTED: + showed_connected_message = False + + if not showed_connecting_message: + logger.info("Waiting to connect to BizHawk...") + showed_connecting_message = True + + if not await connect(ctx.bizhawk_ctx): + continue + + showed_no_handler_message = False + + script_version = await get_script_version(ctx.bizhawk_ctx) + + if script_version != EXPECTED_SCRIPT_VERSION: + logger.info(f"Connector script is incompatible. Expected version {EXPECTED_SCRIPT_VERSION} but got {script_version}. Disconnecting.") + disconnect(ctx.bizhawk_ctx) + continue + + showed_connecting_message = False + + await ping(ctx.bizhawk_ctx) + + if not showed_connected_message: + showed_connected_message = True + logger.info("Connected to BizHawk") + + rom_hash = await get_hash(ctx.bizhawk_ctx) + if ctx.rom_hash is not None and ctx.rom_hash != rom_hash: + if ctx.server is not None: + logger.info(f"ROM changed. Disconnecting from server.") + await ctx.disconnect(True) + + ctx.auth = None + ctx.username = None + ctx.rom_hash = rom_hash + + if ctx.client_handler is None: + system = await get_system(ctx.bizhawk_ctx) + ctx.client_handler = await AutoBizHawkClientRegister.get_handler(ctx, system) + + if ctx.client_handler is None: + if not showed_no_handler_message: + logger.info("No handler was found for this game") + showed_no_handler_message = True + continue + else: + showed_no_handler_message = False + logger.info(f"Running handler for {ctx.client_handler.game}") + + except RequestFailedError as exc: + logger.info(f"Lost connection to BizHawk: {exc.args[0]}") + continue + + # Get slot name and send `Connect` + if ctx.server is not None and ctx.username is None: + await ctx.client_handler.set_auth(ctx) + + if ctx.auth is None: + await ctx.get_username() + + await ctx.send_connect() + + await ctx.client_handler.game_watcher(ctx) + + +async def _run_game(rom: str): + import webbrowser + webbrowser.open(rom) + + +async def _patch_and_run_game(patch_file: str): + metadata, output_file = Patch.create_rom_file(patch_file) + Utils.async_start(_run_game(output_file)) + + +def launch() -> None: + async def main(): + parser = get_base_parser() + parser.add_argument("patch_file", default="", type=str, nargs="?", help="Path to an Archipelago patch file") + args = parser.parse_args() + + ctx = BizHawkClientContext(args.connect, args.password) + ctx.server_task = asyncio.create_task(server_loop(ctx), name="ServerLoop") + + if gui_enabled: + ctx.run_gui() + ctx.run_cli() + + if args.patch_file != "": + Utils.async_start(_patch_and_run_game(args.patch_file)) + + watcher_task = asyncio.create_task(_game_watcher(ctx), name="GameWatcher") + + try: + await watcher_task + except Exception as e: + logger.error("".join(traceback.format_exception(e))) + + await ctx.exit_event.wait() + await ctx.shutdown() + + Utils.init_logging("BizHawkClient", exception_logger="Client") + import colorama + colorama.init() + asyncio.run(main()) + colorama.deinit() From 6b48f9aac55b672fa17c91abacc58f3c72070a24 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Tue, 3 Oct 2023 02:52:58 +0200 Subject: [PATCH 088/144] WebHost: link to stats from the use statistics directly on landing (#2242) --- WebHostLib/static/styles/landing.css | 3 --- WebHostLib/templates/landing.html | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/WebHostLib/static/styles/landing.css b/WebHostLib/static/styles/landing.css index 202c43badd..96975553c1 100644 --- a/WebHostLib/static/styles/landing.css +++ b/WebHostLib/static/styles/landing.css @@ -235,9 +235,6 @@ html{ line-height: 30px; } -#landing .variable{ - color: #ffff00; -} .landing-deco{ position: absolute; diff --git a/WebHostLib/templates/landing.html b/WebHostLib/templates/landing.html index fd45b78cfb..b489ef18ac 100644 --- a/WebHostLib/templates/landing.html +++ b/WebHostLib/templates/landing.html @@ -49,9 +49,9 @@ our crazy idea into a reality.

    - {{ seeds }} + {{ seeds }} games were generated and - {{ rooms }} + {{ rooms }} were hosted in the last 7 days.

    From cdbb2cf7b769eb6614d63bc2233e1ba01635f5dc Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Tue, 3 Oct 2023 09:47:22 +0200 Subject: [PATCH 089/144] Core: fix unittest world discovery (#2262) --- test/__init__.py | 3 ++- test/worlds/__init__.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/__init__.py b/test/__init__.py index 03716a10d7..37ebe3f627 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -13,5 +13,6 @@ ModuleUpdate.update_ran = True # don't upgrade import Utils -Utils.local_path.cached_path = pathlib.Path(__file__).parent.parent +file_path = pathlib.Path(__file__).parent.parent +Utils.local_path.cached_path = file_path Utils.user_path() # initialize cached_path diff --git a/test/worlds/__init__.py b/test/worlds/__init__.py index d1817cc674..cf396111bf 100644 --- a/test/worlds/__init__.py +++ b/test/worlds/__init__.py @@ -1,7 +1,7 @@ def load_tests(loader, standard_tests, pattern): import os import unittest - from ..TestBase import file_path + from .. import file_path from worlds.AutoWorld import AutoWorldRegister suite = unittest.TestSuite() From 78057476f309c3c012b5534b83a2d25c6b3b2a42 Mon Sep 17 00:00:00 2001 From: Aaron Wagener Date: Tue, 3 Oct 2023 05:19:09 -0500 Subject: [PATCH 090/144] Docs: python 3.11 works now (#2258) * Docs: python 3.11 works now * change to py 3.12 unsupported --- docs/running from source.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/running from source.md b/docs/running from source.md index c0f4bf5802..b7367308d8 100644 --- a/docs/running from source.md +++ b/docs/running from source.md @@ -8,7 +8,7 @@ use that version. These steps are for developers or platforms without compiled r What you'll need: * [Python 3.8.7 or newer](https://www.python.org/downloads/), not the Windows Store version - * **Python 3.11 does not work currently** + * **Python 3.12 is currently unsupported** * pip: included in downloads from python.org, separate in many Linux distributions * Matching C compiler * possibly optional, read operating system specific sections @@ -30,7 +30,7 @@ After this, you should be able to run the programs. Recommended steps * Download and install a "Windows installer (64-bit)" from the [Python download page](https://www.python.org/downloads) - * **Python 3.11 does not work currently** + * **Python 3.12 is currently unsupported** * **Optional**: Download and install Visual Studio Build Tools from [Visual Studio Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/). From f6e92a18de0b5cccd8a5b439f951c24ebfff6489 Mon Sep 17 00:00:00 2001 From: Aaron Wagener Date: Wed, 4 Oct 2023 11:23:29 -0500 Subject: [PATCH 091/144] The Messenger: Fix items accessibility region rule (#2263) --- worlds/messenger/Rules.py | 3 ++- worlds/messenger/test/TestAccess.py | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/worlds/messenger/Rules.py b/worlds/messenger/Rules.py index c24f60fbaa..664bf5f6d7 100644 --- a/worlds/messenger/Rules.py +++ b/worlds/messenger/Rules.py @@ -263,5 +263,6 @@ def set_self_locking_items(multiworld: MultiWorld, player: int) -> None: allow_self_locking_items(multiworld.get_location("Elemental Skylands Seal - Water", player), "Currents Master") # add these locations when seals and shards aren't shuffled elif not multiworld.shuffle_shards[player]: - allow_self_locking_items(multiworld.get_region("Cloud Ruins Right", player), "Ruxxtin's Amulet") + for entrance in multiworld.get_region("Cloud Ruins", player).entrances: + entrance.access_rule = lambda state: state.has("Wingsuit", player) or state.has("Rope Dart", player) allow_self_locking_items(multiworld.get_region("Forlorn Temple", player), *PHOBEKINS) diff --git a/worlds/messenger/test/TestAccess.py b/worlds/messenger/test/TestAccess.py index 452ed1189f..e95a81c5c1 100644 --- a/worlds/messenger/test/TestAccess.py +++ b/worlds/messenger/test/TestAccess.py @@ -163,6 +163,8 @@ class ItemsAccessTest(MessengerTestBase): "Forlorn Temple - Demon King": PHOBEKINS } + self.multiworld.state = self.multiworld.get_all_state(True) + self.remove_by_name(location_lock_pairs.values()) for loc in location_lock_pairs: for item_name in location_lock_pairs[loc]: item = self.get_item_by_name(item_name) From 6c4a3959c36a47863ec2a7ef1d9fe51d51dc9733 Mon Sep 17 00:00:00 2001 From: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com> Date: Wed, 4 Oct 2023 16:52:34 -0400 Subject: [PATCH 092/144] Docs: Categorize Commands in Guide (#2213) * Update commands_en.md Commands re-ordered and put into categories Some commands were better documented / explained more clearly Other formatting changes * Status command moved to General category and elaboration on getitem command * "Multi-world" -> "Multiworld" * Moved game-specific local commands to game pages --- worlds/factorio/docs/en_Factorio.md | 6 ++ worlds/ff1/docs/en_Final Fantasy.md | 5 + worlds/generic/docs/commands_en.md | 160 +++++++++++++++------------- 3 files changed, 97 insertions(+), 74 deletions(-) diff --git a/worlds/factorio/docs/en_Factorio.md b/worlds/factorio/docs/en_Factorio.md index 61bceb3820..dbc33d05df 100644 --- a/worlds/factorio/docs/en_Factorio.md +++ b/worlds/factorio/docs/en_Factorio.md @@ -42,3 +42,9 @@ depositing excess energy and supplementing energy deficits, much like Accumulato Each placed EnergyLink Bridge provides 10 MW of throughput. The shared storage has unlimited capacity, but 25% of energy is lost during depositing. The amount of energy currently in the shared storage is displayed in the Archipelago client. It can also be queried by typing `/energy-link` in-game. + +## Unique Local Commands +The following commands are only available when using the FactorioClient to play Factorio with Archipelago. + +- `/factorio ` Sends the command argument to the Factorio server as a command. +- `/energy-link` Displays the amount of energy currently in shared storage for EnergyLink diff --git a/worlds/ff1/docs/en_Final Fantasy.md b/worlds/ff1/docs/en_Final Fantasy.md index 29d4d29f80..8962919743 100644 --- a/worlds/ff1/docs/en_Final Fantasy.md +++ b/worlds/ff1/docs/en_Final Fantasy.md @@ -24,3 +24,8 @@ All items can appear in other players worlds, including consumables, shards, wea All local and remote items appear the same. Final Fantasy will say that you received an item, then BOTH the client log and the emulator will display what was found external to the in-game text box. + +## Unique Local Commands +The following command is only available when using the FF1Client for the Final Fantasy Randomizer. + +- `/nes` Shows the current status of the NES connection. diff --git a/worlds/generic/docs/commands_en.md b/worlds/generic/docs/commands_en.md index e52ea20fd2..3e7c0bd4bd 100644 --- a/worlds/generic/docs/commands_en.md +++ b/worlds/generic/docs/commands_en.md @@ -1,96 +1,108 @@ -### Helpful Commands +# Helpful Commands Commands are split into two types: client commands and server commands. Client commands are commands which are executed by the client and do not affect the Archipelago remote session. Server commands are commands which are executed by the Archipelago server and affect the Archipelago session or otherwise provide feedback from the server. -In clients which have their own commands the commands are typically prepended by a forward slash:`/`. Remote commands -are always submitted to the server prepended with an exclamation point: `!`. +In clients which have their own commands the commands are typically prepended by a forward slash: `/`. -#### Local Commands +Server commands are always submitted to the server prepended with an exclamation point: `!`.
    -The following list is a list of client commands which may be available to you through your Archipelago client. You +# Server Commands + +Server commands may be executed by any client which allows for sending text chat to the Archipelago server. If your +client does not allow for sending chat then you may connect to your game slot with the TextClient which comes with the +Archipelago installation. In order to execute the command you need to merely send a text message with the command, +including the exclamation point. + +### General +- `!help` Returns a listing of available commands. +- `!license` Returns the software licensing information. +- `!options` Returns the current server options, including password in plaintext. +- `!players` Returns info about the currently connected and non-connected players. +- `!status` Returns information about the connection status and check completion numbers for all players in the current room.
    (Optionally mention a Tag name and get information on who has that Tag. For example: !status DeathLink) + + +### Utilities +- `!countdown ` Starts a countdown using the given seconds value. Useful for synchronizing starts. + Defaults to 10 seconds if no argument is provided. +- `!alias ` Sets your alias, which allows you to use commands with the alias rather than your provided name. +- `!admin ` Executes a command as if you typed it into the server console. Remote administration must be + enabled. + +### Information +- `!remaining` Lists the items remaining in your game, but not where they are or who they go to. +- `!missing` Lists the location checks you are missing from the server's perspective. +- `!checked` Lists all the location checks you've done from the server's perspective. + +### Hints +- `!hint` Lists all hints relevant to your world, the number of points you have for hints, and how much a hint costs. +- `!hint ` Tells you the game world and location your item is in, uses points earned from completing locations. +- `!hint_location ` Tells you what item is in a specific location, uses points earned from completing locations. + +### Collect/Release +- `!collect` Grants you all the remaining items for your world by collecting them from all games. Typically used after +goal completion. +- `!release` Releases all items contained in your world to other worlds. Typically, done automatically by the sever, but +can be configured to allow/require manual usage of this command. + +### Cheats +- `!getitem ` Cheats an item to the currently connected slot, if it is enabled in the server. + + +## Host only (on Archipelago.gg or in your server console) + +### General +- `/help` Returns a list of commands available in the console. +- `/license` Returns the software licensing information. +- `/options` Lists the server's current options, including password in plaintext. +- `/players` List currently connected players. +- `/save` Saves the state of the current multiworld. Note that the server auto-saves on a minute basis. +- `/exit` Shutdown the server + +### Utilities +- `/countdown ` Starts a countdown sent to all players via text chat. Defaults to 10 seconds if no + argument is provided. +- `/option
  • - Logistic Science Pack - - Military Science Pack - - Chemical Science Pack - - Production Science Pack - - Utility Science Pack - - Space Science Pack + {{ name }} {% if player_inventory["logistic-science-pack"] or prog_science %}✔{% endif %}{% if player_inventory["military-science-pack"] or prog_science > 1%}✔{% endif %}{% if player_inventory["chemical-science-pack"] or prog_science > 2%}✔{% endif %}{% if player_inventory["production-science-pack"] or prog_science > 3%}✔{% endif %}{% if player_inventory["utility-science-pack"] or prog_science > 4%}✔{% endif %}{% if player_inventory["space-science-pack"] or prog_science > 5%}✔{% endif %}{% if player_inventory[internal_name] or prog_science > loop.index0 %}✔{% endif %}
    + {% if combined_yaml %} +

    Combined File Download

    +

    Download

    + {% endif %}
    {% endblock %} diff --git a/worlds/alttp/docs/multiworld_de.md b/worlds/alttp/docs/multiworld_de.md index 38009fb58e..8ccd1a87a6 100644 --- a/worlds/alttp/docs/multiworld_de.md +++ b/worlds/alttp/docs/multiworld_de.md @@ -67,7 +67,7 @@ Wenn du eine Option nicht gewählt haben möchtest, setze ihren Wert einfach auf ### Überprüfung deiner YAML-Datei -Wenn man sichergehen will, ob die YAML-Datei funktioniert, kann man dies bei der [YAML Validator](/mysterycheck) Seite +Wenn man sichergehen will, ob die YAML-Datei funktioniert, kann man dies bei der [YAML Validator](/check) Seite tun. ## ein Einzelspielerspiel erstellen diff --git a/worlds/alttp/docs/multiworld_es.md b/worlds/alttp/docs/multiworld_es.md index 8576318bb9..37aeda2a63 100644 --- a/worlds/alttp/docs/multiworld_es.md +++ b/worlds/alttp/docs/multiworld_es.md @@ -82,7 +82,7 @@ debe tener al menos un valor mayor que cero, si no la generación fallará. ### Verificando tu archivo YAML Si quieres validar que tu fichero YAML para asegurarte que funciona correctamente, puedes hacerlo en la pagina -[YAML Validator](/mysterycheck). +[YAML Validator](/check). ## Generar una partida para un jugador diff --git a/worlds/alttp/docs/multiworld_fr.md b/worlds/alttp/docs/multiworld_fr.md index 329ca65375..078a270f08 100644 --- a/worlds/alttp/docs/multiworld_fr.md +++ b/worlds/alttp/docs/multiworld_fr.md @@ -83,7 +83,7 @@ chaque paramètre il faut au moins une option qui soit paramétrée sur un nombr ### Vérifier son fichier YAML Si vous voulez valider votre fichier YAML pour être sûr qu'il fonctionne, vous pouvez le vérifier sur la page du -[Validateur de YAML](/mysterycheck). +[Validateur de YAML](/check). ## Générer une partie pour un joueur diff --git a/worlds/dkc3/docs/setup_en.md b/worlds/dkc3/docs/setup_en.md index bb10756300..9c4197286e 100644 --- a/worlds/dkc3/docs/setup_en.md +++ b/worlds/dkc3/docs/setup_en.md @@ -50,7 +50,7 @@ them. Player settings page: [Donkey Kong Country 3 Player Settings Page](/games/ ### Verifying your config file If you would like to validate your config file to make sure it works, you may do so on the YAML Validator page. YAML -validator page: [YAML Validation page](/mysterycheck) +validator page: [YAML Validation page](/check) ## Generating a Single-Player Game diff --git a/worlds/factorio/docs/setup_en.md b/worlds/factorio/docs/setup_en.md index 09ad431a21..b6d4545925 100644 --- a/worlds/factorio/docs/setup_en.md +++ b/worlds/factorio/docs/setup_en.md @@ -31,7 +31,7 @@ them. Factorio player settings page: [Factorio Settings Page](/games/Factorio/pl ### Verifying your config file If you would like to validate your config file to make sure it works, you may do so on the YAML Validator page. YAML -Validator page: [Yaml Validation Page](/mysterycheck) +Validator page: [Yaml Validation Page](/check) ## Connecting to Someone Else's Factorio Game diff --git a/worlds/generic/docs/setup_en.md b/worlds/generic/docs/setup_en.md index 132b88e285..93ae217e0d 100644 --- a/worlds/generic/docs/setup_en.md +++ b/worlds/generic/docs/setup_en.md @@ -40,7 +40,7 @@ game you will be playing as well as the settings you would like for that game. YAML is a format very similar to JSON however it is made to be more human-readable. If you are ever unsure of the validity of your YAML file you may check the file by uploading it to the check page on the Archipelago website: -[YAML Validation Page](/mysterycheck) +[YAML Validation Page](/check) ### Creating a YAML diff --git a/worlds/ladx/docs/setup_en.md b/worlds/ladx/docs/setup_en.md index 538d70d45e..e21c5bddc4 100644 --- a/worlds/ladx/docs/setup_en.md +++ b/worlds/ladx/docs/setup_en.md @@ -40,7 +40,7 @@ your personal settings and export a config file from them. ### Verifying your config file If you would like to validate your config file to make sure it works, you may do so on the -[YAML Validator](/mysterycheck) page. +[YAML Validator](/check) page. ## Generating a Single-Player Game diff --git a/worlds/lufia2ac/docs/setup_en.md b/worlds/lufia2ac/docs/setup_en.md index 4236c26e8a..3762f32fb4 100644 --- a/worlds/lufia2ac/docs/setup_en.md +++ b/worlds/lufia2ac/docs/setup_en.md @@ -44,7 +44,7 @@ your personal settings and export a config file from them. ### Verifying your config file If you would like to validate your config file to make sure it works, you may do so on the -[YAML Validator](/mysterycheck) page. +[YAML Validator](/check) page. ## Generating a Single-Player Game diff --git a/worlds/sm/docs/multiworld_en.md b/worlds/sm/docs/multiworld_en.md index ce91e7a7e4..1291507743 100644 --- a/worlds/sm/docs/multiworld_en.md +++ b/worlds/sm/docs/multiworld_en.md @@ -49,7 +49,7 @@ them. Player settings page: [Super Metroid Player Settings Page](/games/Super%20 ### Verifying your config file If you would like to validate your config file to make sure it works, you may do so on the YAML Validator page. YAML -validator page: [YAML Validation page](/mysterycheck) +validator page: [YAML Validation page](/check) ## Generating a Single-Player Game diff --git a/worlds/smw/docs/setup_en.md b/worlds/smw/docs/setup_en.md index 9ca8bdf58a..3967f544a0 100644 --- a/worlds/smw/docs/setup_en.md +++ b/worlds/smw/docs/setup_en.md @@ -50,7 +50,7 @@ them. Player settings page: [Super Mario World Player Settings Page](/games/Supe ### Verifying your config file If you would like to validate your config file to make sure it works, you may do so on the YAML Validator page. YAML -validator page: [YAML Validation page](/mysterycheck) +validator page: [YAML Validation page](/check) ## Joining a MultiWorld Game diff --git a/worlds/smz3/docs/multiworld_en.md b/worlds/smz3/docs/multiworld_en.md index da6e29ab69..53842a3c6f 100644 --- a/worlds/smz3/docs/multiworld_en.md +++ b/worlds/smz3/docs/multiworld_en.md @@ -47,7 +47,7 @@ them. Player settings page: [SMZ3 Player Settings Page](/games/SMZ3/player-setti ### Verifying your config file If you would like to validate your config file to make sure it works, you may do so on the YAML Validator page. YAML -validator page: [YAML Validation page](/mysterycheck) +validator page: [YAML Validation page](/check) ## Generating a Single-Player Game diff --git a/worlds/soe/docs/multiworld_en.md b/worlds/soe/docs/multiworld_en.md index d995cea56a..58b9aabf6a 100644 --- a/worlds/soe/docs/multiworld_en.md +++ b/worlds/soe/docs/multiworld_en.md @@ -29,7 +29,7 @@ them. Player settings page: [Secret of Evermore Player Settings PAge](/games/Sec ### Verifying your config file If you would like to validate your config file to make sure it works, you may do so on the YAML Validator -page: [YAML Validation page](/mysterycheck) +page: [YAML Validation page](/check) ## Generating a Single-Player Game diff --git a/worlds/tloz/docs/multiworld_en.md b/worlds/tloz/docs/multiworld_en.md index 581e8cf7b2..ae53d953b1 100644 --- a/worlds/tloz/docs/multiworld_en.md +++ b/worlds/tloz/docs/multiworld_en.md @@ -44,7 +44,7 @@ them. Player settings page: [The Legend of Zelda Player Settings Page](/games/Th ### Verifying your config file If you would like to validate your config file to make sure it works, you may do so on the YAML Validator page. YAML -validator page: [YAML Validation page](/mysterycheck) +validator page: [YAML Validation page](/check) ## Generating a Single-Player Game diff --git a/worlds/zillion/docs/setup_en.md b/worlds/zillion/docs/setup_en.md index 16000dbe3b..22dee5ee55 100644 --- a/worlds/zillion/docs/setup_en.md +++ b/worlds/zillion/docs/setup_en.md @@ -51,7 +51,7 @@ them. ### Verifying your config file -If you would like to validate your config file to make sure it works, you may do so on the [YAML Validator page](/mysterycheck). +If you would like to validate your config file to make sure it works, you may do so on the [YAML Validator page](/check). ## Generating a Single-Player Game From 9f126ad0d070c0eb42bdf416a11775aa7d01307f Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Sun, 22 Oct 2023 06:48:06 +0200 Subject: [PATCH 135/144] The Witness: Fix random events not having the correct probabilities (#2340) --- worlds/witness/__init__.py | 2 +- worlds/witness/hints.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/witness/__init__.py b/worlds/witness/__init__.py index faaafd598b..28eaba6404 100644 --- a/worlds/witness/__init__.py +++ b/worlds/witness/__init__.py @@ -66,7 +66,7 @@ class WitnessWorld(World): def _get_slot_data(self): return { - 'seed': self.multiworld.per_slot_randoms[self.player].randint(0, 1000000), + 'seed': self.random.randrange(0, 1000000), 'victory_location': int(self.player_logic.VICTORY_LOCATION, 16), 'panelhex_to_id': self.locat.CHECK_PANELHEX_TO_ID, 'item_id_to_door_hexes': StaticWitnessItems.get_item_to_door_mappings(), diff --git a/worlds/witness/hints.py b/worlds/witness/hints.py index 5d8bd5d370..4fd0edc429 100644 --- a/worlds/witness/hints.py +++ b/worlds/witness/hints.py @@ -306,7 +306,7 @@ def make_hints(multiworld: MultiWorld, player: int, hint_amount: int): else: hints.append((f"{loc} contains {item[0]}.", item[2])) - next_random_hint_is_item = multiworld.per_slot_randoms[player].randint(0, 2) + next_random_hint_is_item = multiworld.per_slot_randoms[player].randrange(0, 2) # Moving this to the new system is in the bigger refactoring PR while len(hints) < hint_amount: if next_random_hint_is_item: From 6e6fa13e441ca9a915cfd17f50ddf6b140ab3c7d Mon Sep 17 00:00:00 2001 From: Aaron Wagener Date: Sun, 22 Oct 2023 05:12:26 -0500 Subject: [PATCH 136/144] Tests: add multiworld seed to fill subtest (#2346) --- test/TestBase.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/TestBase.py b/test/TestBase.py index e6fbafd95a..ca7a19815c 100644 --- a/test/TestBase.py +++ b/test/TestBase.py @@ -172,7 +172,7 @@ class WorldTestBase(unittest.TestCase): items = (items,) for item in items: self.multiworld.state.collect(item) - + def remove_by_name(self, item_names: typing.Union[str, typing.Iterable[str]]) -> typing.List[Item]: """Remove all of the items in the item pool with the given names from state""" items = self.get_items_by_name(item_names) @@ -278,7 +278,6 @@ class WorldTestBase(unittest.TestCase): def testFill(self): """Generates a multiworld and validates placements with the defined options""" - # don't run this test if accessibility is set manually if not (self.run_default_tests and self.constructed): return from Fill import distribute_items_restrictive @@ -301,8 +300,8 @@ class WorldTestBase(unittest.TestCase): state.collect(location.item, True, location) return self.multiworld.has_beaten_game(state, 1) - - with self.subTest("Game", game=self.game): + + with self.subTest("Game", game=self.game, seed=self.multiworld.seed): distribute_items_restrictive(self.multiworld) call_all(self.multiworld, "post_fill") self.assertTrue(fulfills_accessibility(), "Collected all locations, but can't beat the game.") From 30da81c39043befff8c829fe816e10030ecf3bf0 Mon Sep 17 00:00:00 2001 From: Aaron Wagener Date: Sun, 22 Oct 2023 06:00:27 -0500 Subject: [PATCH 137/144] Tests: modern PEP8-ify core test modules and methods (#2298) * rename modules * rename methods * add docstrings to the general tests * add base import stub * test_base -> bases * print deprecation warning * redo 2346 --- docs/world api.md | 3 +- test/TestBase.py | 313 +----------------- test/bases.py | 309 +++++++++++++++++ test/general/__init__.py | 7 + test/general/{TestFill.py => test_fill.py} | 48 ++- .../{TestHelpers.py => test_helpers.py} | 13 +- .../{TestHostYAML.py => test_host_yaml.py} | 2 + test/general/{TestIDs.py => test_ids.py} | 20 +- ...TestImplemented.py => test_implemented.py} | 6 +- test/general/{TestItems.py => test_items.py} | 10 +- .../{TestLocations.py => test_locations.py} | 8 +- test/general/{TestNames.py => test_names.py} | 4 +- .../{TestOptions.py => test_options.py} | 3 +- ...stReachability.py => test_reachability.py} | 6 +- ...ocationStore.py => test_location_store.py} | 0 .../data/{OnePlayer => one_player}/test.yaml | 0 .../{TestGenerate.py => test_generate.py} | 2 +- ...estMultiServer.py => test_multi_server.py} | 0 .../{TestSIPrefix.py => test_si_prefix.py} | 0 ...estAPIGenerate.py => test_api_generate.py} | 4 +- test/webhost/{TestDocs.py => test_docs.py} | 4 +- ...eGeneration.py => test_file_generation.py} | 4 +- 22 files changed, 410 insertions(+), 356 deletions(-) create mode 100644 test/bases.py rename test/general/{TestFill.py => test_fill.py} (92%) rename test/general/{TestHelpers.py => test_helpers.py} (90%) rename test/general/{TestHostYAML.py => test_host_yaml.py} (87%) rename test/general/{TestIDs.py => test_ids.py} (82%) rename test/general/{TestImplemented.py => test_implemented.py} (93%) rename test/general/{TestItems.py => test_items.py} (88%) rename test/general/{TestLocations.py => test_locations.py} (96%) rename test/general/{TestNames.py => test_names.py} (92%) rename test/general/{TestOptions.py => test_options.py} (78%) rename test/general/{TestReachability.py => test_reachability.py} (91%) rename test/netutils/{TestLocationStore.py => test_location_store.py} (100%) rename test/programs/data/{OnePlayer => one_player}/test.yaml (100%) rename test/programs/{TestGenerate.py => test_generate.py} (98%) rename test/programs/{TestMultiServer.py => test_multi_server.py} (100%) rename test/utils/{TestSIPrefix.py => test_si_prefix.py} (100%) rename test/webhost/{TestAPIGenerate.py => test_api_generate.py} (93%) rename test/webhost/{TestDocs.py => test_docs.py} (96%) rename test/webhost/{TestFileGeneration.py => test_file_generation.py} (96%) diff --git a/docs/world api.md b/docs/world api.md index 6fb5b3ac9c..b128e2b146 100644 --- a/docs/world api.md +++ b/docs/world api.md @@ -759,8 +759,9 @@ multiworld for each test written using it. Within subsequent modules, classes sh TestBase, and can then define options to test in the class body, and run tests in each test method. Example `__init__.py` + ```python -from test.TestBase import WorldTestBase +from test.test_base import WorldTestBase class MyGameTestBase(WorldTestBase): diff --git a/test/TestBase.py b/test/TestBase.py index ca7a19815c..bfd92346d3 100644 --- a/test/TestBase.py +++ b/test/TestBase.py @@ -1,310 +1,3 @@ -import typing -import unittest -from argparse import Namespace - -from test.general import gen_steps -from worlds import AutoWorld -from worlds.AutoWorld import call_all - -from BaseClasses import Location, MultiWorld, CollectionState, ItemClassification, Item -from worlds.alttp.Items import ItemFactory - - -class TestBase(unittest.TestCase): - multiworld: MultiWorld - _state_cache = {} - - def get_state(self, items): - if (self.multiworld, tuple(items)) in self._state_cache: - return self._state_cache[self.multiworld, tuple(items)] - state = CollectionState(self.multiworld) - for item in items: - item.classification = ItemClassification.progression - state.collect(item, event=True) - state.sweep_for_events() - state.update_reachable_regions(1) - self._state_cache[self.multiworld, tuple(items)] = state - return state - - def get_path(self, state, region): - def flist_to_iter(node): - while node: - value, node = node - yield value - - from itertools import zip_longest - reversed_path_as_flist = state.path.get(region, (region, None)) - string_path_flat = reversed(list(map(str, flist_to_iter(reversed_path_as_flist)))) - # Now we combine the flat string list into (region, exit) pairs - pathsiter = iter(string_path_flat) - pathpairs = zip_longest(pathsiter, pathsiter) - return list(pathpairs) - - def run_location_tests(self, access_pool): - for i, (location, access, *item_pool) in enumerate(access_pool): - items = item_pool[0] - all_except = item_pool[1] if len(item_pool) > 1 else None - state = self._get_items(item_pool, all_except) - path = self.get_path(state, self.multiworld.get_location(location, 1).parent_region) - with self.subTest(msg="Reach Location", location=location, access=access, items=items, - all_except=all_except, path=path, entry=i): - - self.assertEqual(self.multiworld.get_location(location, 1).can_reach(state), access, - f"failed {self.multiworld.get_location(location, 1)} with: {item_pool}") - - # check for partial solution - if not all_except and access: # we are not supposed to be able to reach location with partial inventory - for missing_item in item_pool[0]: - with self.subTest(msg="Location reachable without required item", location=location, - items=item_pool[0], missing_item=missing_item, entry=i): - state = self._get_items_partial(item_pool, missing_item) - - self.assertEqual(self.multiworld.get_location(location, 1).can_reach(state), False, - f"failed {self.multiworld.get_location(location, 1)}: succeeded with " - f"{missing_item} removed from: {item_pool}") - - def run_entrance_tests(self, access_pool): - for i, (entrance, access, *item_pool) in enumerate(access_pool): - items = item_pool[0] - all_except = item_pool[1] if len(item_pool) > 1 else None - state = self._get_items(item_pool, all_except) - path = self.get_path(state, self.multiworld.get_entrance(entrance, 1).parent_region) - with self.subTest(msg="Reach Entrance", entrance=entrance, access=access, items=items, - all_except=all_except, path=path, entry=i): - - self.assertEqual(self.multiworld.get_entrance(entrance, 1).can_reach(state), access) - - # check for partial solution - if not all_except and access: # we are not supposed to be able to reach location with partial inventory - for missing_item in item_pool[0]: - with self.subTest(msg="Entrance reachable without required item", entrance=entrance, - items=item_pool[0], missing_item=missing_item, entry=i): - state = self._get_items_partial(item_pool, missing_item) - self.assertEqual(self.multiworld.get_entrance(entrance, 1).can_reach(state), False, - f"failed {self.multiworld.get_entrance(entrance, 1)} with: {item_pool}") - - def _get_items(self, item_pool, all_except): - if all_except and len(all_except) > 0: - items = self.multiworld.itempool[:] - items = [item for item in items if - item.name not in all_except and not ("Bottle" in item.name and "AnyBottle" in all_except)] - items.extend(ItemFactory(item_pool[0], 1)) - else: - items = ItemFactory(item_pool[0], 1) - return self.get_state(items) - - def _get_items_partial(self, item_pool, missing_item): - new_items = item_pool[0].copy() - new_items.remove(missing_item) - items = ItemFactory(new_items, 1) - return self.get_state(items) - - -class WorldTestBase(unittest.TestCase): - options: typing.Dict[str, typing.Any] = {} - multiworld: MultiWorld - - game: typing.ClassVar[str] # define game name in subclass, example "Secret of Evermore" - auto_construct: typing.ClassVar[bool] = True - """ automatically set up a world for each test in this class """ - - def setUp(self) -> None: - if self.auto_construct: - self.world_setup() - - def world_setup(self, seed: typing.Optional[int] = None) -> None: - if type(self) is WorldTestBase or \ - (hasattr(WorldTestBase, self._testMethodName) - and not self.run_default_tests and - getattr(self, self._testMethodName).__code__ is - getattr(WorldTestBase, self._testMethodName, None).__code__): - return # setUp gets called for tests defined in the base class. We skip world_setup here. - if not hasattr(self, "game"): - raise NotImplementedError("didn't define game name") - self.multiworld = MultiWorld(1) - self.multiworld.game[1] = self.game - self.multiworld.player_name = {1: "Tester"} - self.multiworld.set_seed(seed) - self.multiworld.state = CollectionState(self.multiworld) - args = Namespace() - for name, option in AutoWorld.AutoWorldRegister.world_types[self.game].options_dataclass.type_hints.items(): - setattr(args, name, { - 1: option.from_any(self.options.get(name, getattr(option, "default"))) - }) - self.multiworld.set_options(args) - for step in gen_steps: - call_all(self.multiworld, step) - - # methods that can be called within tests - def collect_all_but(self, item_names: typing.Union[str, typing.Iterable[str]], - state: typing.Optional[CollectionState] = None) -> None: - """Collects all pre-placed items and items in the multiworld itempool except those provided""" - if isinstance(item_names, str): - item_names = (item_names,) - if not state: - state = self.multiworld.state - for item in self.multiworld.get_items(): - if item.name not in item_names: - state.collect(item) - - def get_item_by_name(self, item_name: str) -> Item: - """Returns the first item found in placed items, or in the itempool with the matching name""" - for item in self.multiworld.get_items(): - if item.name == item_name: - return item - raise ValueError("No such item") - - def get_items_by_name(self, item_names: typing.Union[str, typing.Iterable[str]]) -> typing.List[Item]: - """Returns actual items from the itempool that match the provided name(s)""" - if isinstance(item_names, str): - item_names = (item_names,) - return [item for item in self.multiworld.itempool if item.name in item_names] - - def collect_by_name(self, item_names: typing.Union[str, typing.Iterable[str]]) -> typing.List[Item]: - """ collect all of the items in the item pool that have the given names """ - items = self.get_items_by_name(item_names) - self.collect(items) - return items - - def collect(self, items: typing.Union[Item, typing.Iterable[Item]]) -> None: - """Collects the provided item(s) into state""" - if isinstance(items, Item): - items = (items,) - for item in items: - self.multiworld.state.collect(item) - - def remove_by_name(self, item_names: typing.Union[str, typing.Iterable[str]]) -> typing.List[Item]: - """Remove all of the items in the item pool with the given names from state""" - items = self.get_items_by_name(item_names) - self.remove(items) - return items - - def remove(self, items: typing.Union[Item, typing.Iterable[Item]]) -> None: - """Removes the provided item(s) from state""" - if isinstance(items, Item): - items = (items,) - for item in items: - if item.location and item.location.event and item.location in self.multiworld.state.events: - self.multiworld.state.events.remove(item.location) - self.multiworld.state.remove(item) - - def can_reach_location(self, location: str) -> bool: - """Determines if the current state can reach the provided location name""" - return self.multiworld.state.can_reach(location, "Location", 1) - - def can_reach_entrance(self, entrance: str) -> bool: - """Determines if the current state can reach the provided entrance name""" - return self.multiworld.state.can_reach(entrance, "Entrance", 1) - - def can_reach_region(self, region: str) -> bool: - """Determines if the current state can reach the provided region name""" - return self.multiworld.state.can_reach(region, "Region", 1) - - def count(self, item_name: str) -> int: - """Returns the amount of an item currently in state""" - return self.multiworld.state.count(item_name, 1) - - def assertAccessDependency(self, - locations: typing.List[str], - possible_items: typing.Iterable[typing.Iterable[str]], - only_check_listed: bool = False) -> None: - """Asserts that the provided locations can't be reached without the listed items but can be reached with any - one of the provided combinations""" - all_items = [item_name for item_names in possible_items for item_name in item_names] - - state = CollectionState(self.multiworld) - self.collect_all_but(all_items, state) - if only_check_listed: - for location in locations: - self.assertFalse(state.can_reach(location, "Location", 1), f"{location} is reachable without {all_items}") - else: - for location in self.multiworld.get_locations(): - loc_reachable = state.can_reach(location, "Location", 1) - self.assertEqual(loc_reachable, location.name not in locations, - f"{location.name} is reachable without {all_items}" if loc_reachable - else f"{location.name} is not reachable without {all_items}") - for item_names in possible_items: - items = self.get_items_by_name(item_names) - for item in items: - state.collect(item) - for location in locations: - self.assertTrue(state.can_reach(location, "Location", 1), - f"{location} not reachable with {item_names}") - for item in items: - state.remove(item) - - def assertBeatable(self, beatable: bool): - """Asserts that the game can be beaten with the current state""" - self.assertEqual(self.multiworld.can_beat_game(self.multiworld.state), beatable) - - # following tests are automatically run - @property - def run_default_tests(self) -> bool: - """Not possible or identical to the base test that's always being run already""" - return (self.options - or self.setUp.__code__ is not WorldTestBase.setUp.__code__ - or self.world_setup.__code__ is not WorldTestBase.world_setup.__code__) - - @property - def constructed(self) -> bool: - """A multiworld has been constructed by this point""" - return hasattr(self, "game") and hasattr(self, "multiworld") - - def testAllStateCanReachEverything(self): - """Ensure all state can reach everything and complete the game with the defined options""" - if not (self.run_default_tests and self.constructed): - return - with self.subTest("Game", game=self.game): - excluded = self.multiworld.exclude_locations[1].value - state = self.multiworld.get_all_state(False) - for location in self.multiworld.get_locations(): - if location.name not in excluded: - with self.subTest("Location should be reached", location=location): - reachable = location.can_reach(state) - self.assertTrue(reachable, f"{location.name} unreachable") - with self.subTest("Beatable"): - self.multiworld.state = state - self.assertBeatable(True) - - def testEmptyStateCanReachSomething(self): - """Ensure empty state can reach at least one location with the defined options""" - if not (self.run_default_tests and self.constructed): - return - with self.subTest("Game", game=self.game): - state = CollectionState(self.multiworld) - locations = self.multiworld.get_reachable_locations(state, 1) - self.assertGreater(len(locations), 0, - "Need to be able to reach at least one location to get started.") - - def testFill(self): - """Generates a multiworld and validates placements with the defined options""" - if not (self.run_default_tests and self.constructed): - return - from Fill import distribute_items_restrictive - - # basically a shortened reimplementation of this method from core, in order to force the check is done - def fulfills_accessibility(): - locations = self.multiworld.get_locations(1).copy() - state = CollectionState(self.multiworld) - while locations: - sphere: typing.List[Location] = [] - for n in range(len(locations) - 1, -1, -1): - if locations[n].can_reach(state): - sphere.append(locations.pop(n)) - self.assertTrue(sphere or self.multiworld.accessibility[1] == "minimal", - f"Unreachable locations: {locations}") - if not sphere: - break - for location in sphere: - if location.item: - state.collect(location.item, True, location) - - return self.multiworld.has_beaten_game(state, 1) - - with self.subTest("Game", game=self.game, seed=self.multiworld.seed): - distribute_items_restrictive(self.multiworld) - call_all(self.multiworld, "post_fill") - self.assertTrue(fulfills_accessibility(), "Collected all locations, but can't beat the game.") - placed_items = [loc.item for loc in self.multiworld.get_locations() if loc.item and loc.item.code] - self.assertLessEqual(len(self.multiworld.itempool), len(placed_items), - "Unplaced Items remaining in itempool") +from .bases import TestBase, WorldTestBase +from warnings import warn +warn("TestBase was renamed to bases", DeprecationWarning) diff --git a/test/bases.py b/test/bases.py new file mode 100644 index 0000000000..5fe4df2014 --- /dev/null +++ b/test/bases.py @@ -0,0 +1,309 @@ +import typing +import unittest +from argparse import Namespace + +from test.general import gen_steps +from worlds import AutoWorld +from worlds.AutoWorld import call_all + +from BaseClasses import Location, MultiWorld, CollectionState, ItemClassification, Item +from worlds.alttp.Items import ItemFactory + + +class TestBase(unittest.TestCase): + multiworld: MultiWorld + _state_cache = {} + + def get_state(self, items): + if (self.multiworld, tuple(items)) in self._state_cache: + return self._state_cache[self.multiworld, tuple(items)] + state = CollectionState(self.multiworld) + for item in items: + item.classification = ItemClassification.progression + state.collect(item, event=True) + state.sweep_for_events() + state.update_reachable_regions(1) + self._state_cache[self.multiworld, tuple(items)] = state + return state + + def get_path(self, state, region): + def flist_to_iter(node): + while node: + value, node = node + yield value + + from itertools import zip_longest + reversed_path_as_flist = state.path.get(region, (region, None)) + string_path_flat = reversed(list(map(str, flist_to_iter(reversed_path_as_flist)))) + # Now we combine the flat string list into (region, exit) pairs + pathsiter = iter(string_path_flat) + pathpairs = zip_longest(pathsiter, pathsiter) + return list(pathpairs) + + def run_location_tests(self, access_pool): + for i, (location, access, *item_pool) in enumerate(access_pool): + items = item_pool[0] + all_except = item_pool[1] if len(item_pool) > 1 else None + state = self._get_items(item_pool, all_except) + path = self.get_path(state, self.multiworld.get_location(location, 1).parent_region) + with self.subTest(msg="Reach Location", location=location, access=access, items=items, + all_except=all_except, path=path, entry=i): + + self.assertEqual(self.multiworld.get_location(location, 1).can_reach(state), access, + f"failed {self.multiworld.get_location(location, 1)} with: {item_pool}") + + # check for partial solution + if not all_except and access: # we are not supposed to be able to reach location with partial inventory + for missing_item in item_pool[0]: + with self.subTest(msg="Location reachable without required item", location=location, + items=item_pool[0], missing_item=missing_item, entry=i): + state = self._get_items_partial(item_pool, missing_item) + + self.assertEqual(self.multiworld.get_location(location, 1).can_reach(state), False, + f"failed {self.multiworld.get_location(location, 1)}: succeeded with " + f"{missing_item} removed from: {item_pool}") + + def run_entrance_tests(self, access_pool): + for i, (entrance, access, *item_pool) in enumerate(access_pool): + items = item_pool[0] + all_except = item_pool[1] if len(item_pool) > 1 else None + state = self._get_items(item_pool, all_except) + path = self.get_path(state, self.multiworld.get_entrance(entrance, 1).parent_region) + with self.subTest(msg="Reach Entrance", entrance=entrance, access=access, items=items, + all_except=all_except, path=path, entry=i): + + self.assertEqual(self.multiworld.get_entrance(entrance, 1).can_reach(state), access) + + # check for partial solution + if not all_except and access: # we are not supposed to be able to reach location with partial inventory + for missing_item in item_pool[0]: + with self.subTest(msg="Entrance reachable without required item", entrance=entrance, + items=item_pool[0], missing_item=missing_item, entry=i): + state = self._get_items_partial(item_pool, missing_item) + self.assertEqual(self.multiworld.get_entrance(entrance, 1).can_reach(state), False, + f"failed {self.multiworld.get_entrance(entrance, 1)} with: {item_pool}") + + def _get_items(self, item_pool, all_except): + if all_except and len(all_except) > 0: + items = self.multiworld.itempool[:] + items = [item for item in items if + item.name not in all_except and not ("Bottle" in item.name and "AnyBottle" in all_except)] + items.extend(ItemFactory(item_pool[0], 1)) + else: + items = ItemFactory(item_pool[0], 1) + return self.get_state(items) + + def _get_items_partial(self, item_pool, missing_item): + new_items = item_pool[0].copy() + new_items.remove(missing_item) + items = ItemFactory(new_items, 1) + return self.get_state(items) + + +class WorldTestBase(unittest.TestCase): + options: typing.Dict[str, typing.Any] = {} + multiworld: MultiWorld + + game: typing.ClassVar[str] # define game name in subclass, example "Secret of Evermore" + auto_construct: typing.ClassVar[bool] = True + """ automatically set up a world for each test in this class """ + + def setUp(self) -> None: + if self.auto_construct: + self.world_setup() + + def world_setup(self, seed: typing.Optional[int] = None) -> None: + if type(self) is WorldTestBase or \ + (hasattr(WorldTestBase, self._testMethodName) + and not self.run_default_tests and + getattr(self, self._testMethodName).__code__ is + getattr(WorldTestBase, self._testMethodName, None).__code__): + return # setUp gets called for tests defined in the base class. We skip world_setup here. + if not hasattr(self, "game"): + raise NotImplementedError("didn't define game name") + self.multiworld = MultiWorld(1) + self.multiworld.game[1] = self.game + self.multiworld.player_name = {1: "Tester"} + self.multiworld.set_seed(seed) + self.multiworld.state = CollectionState(self.multiworld) + args = Namespace() + for name, option in AutoWorld.AutoWorldRegister.world_types[self.game].options_dataclass.type_hints.items(): + setattr(args, name, { + 1: option.from_any(self.options.get(name, getattr(option, "default"))) + }) + self.multiworld.set_options(args) + for step in gen_steps: + call_all(self.multiworld, step) + + # methods that can be called within tests + def collect_all_but(self, item_names: typing.Union[str, typing.Iterable[str]], + state: typing.Optional[CollectionState] = None) -> None: + """Collects all pre-placed items and items in the multiworld itempool except those provided""" + if isinstance(item_names, str): + item_names = (item_names,) + if not state: + state = self.multiworld.state + for item in self.multiworld.get_items(): + if item.name not in item_names: + state.collect(item) + + def get_item_by_name(self, item_name: str) -> Item: + """Returns the first item found in placed items, or in the itempool with the matching name""" + for item in self.multiworld.get_items(): + if item.name == item_name: + return item + raise ValueError("No such item") + + def get_items_by_name(self, item_names: typing.Union[str, typing.Iterable[str]]) -> typing.List[Item]: + """Returns actual items from the itempool that match the provided name(s)""" + if isinstance(item_names, str): + item_names = (item_names,) + return [item for item in self.multiworld.itempool if item.name in item_names] + + def collect_by_name(self, item_names: typing.Union[str, typing.Iterable[str]]) -> typing.List[Item]: + """ collect all of the items in the item pool that have the given names """ + items = self.get_items_by_name(item_names) + self.collect(items) + return items + + def collect(self, items: typing.Union[Item, typing.Iterable[Item]]) -> None: + """Collects the provided item(s) into state""" + if isinstance(items, Item): + items = (items,) + for item in items: + self.multiworld.state.collect(item) + + def remove_by_name(self, item_names: typing.Union[str, typing.Iterable[str]]) -> typing.List[Item]: + """Remove all of the items in the item pool with the given names from state""" + items = self.get_items_by_name(item_names) + self.remove(items) + return items + + def remove(self, items: typing.Union[Item, typing.Iterable[Item]]) -> None: + """Removes the provided item(s) from state""" + if isinstance(items, Item): + items = (items,) + for item in items: + if item.location and item.location.event and item.location in self.multiworld.state.events: + self.multiworld.state.events.remove(item.location) + self.multiworld.state.remove(item) + + def can_reach_location(self, location: str) -> bool: + """Determines if the current state can reach the provided location name""" + return self.multiworld.state.can_reach(location, "Location", 1) + + def can_reach_entrance(self, entrance: str) -> bool: + """Determines if the current state can reach the provided entrance name""" + return self.multiworld.state.can_reach(entrance, "Entrance", 1) + + def can_reach_region(self, region: str) -> bool: + """Determines if the current state can reach the provided region name""" + return self.multiworld.state.can_reach(region, "Region", 1) + + def count(self, item_name: str) -> int: + """Returns the amount of an item currently in state""" + return self.multiworld.state.count(item_name, 1) + + def assertAccessDependency(self, + locations: typing.List[str], + possible_items: typing.Iterable[typing.Iterable[str]], + only_check_listed: bool = False) -> None: + """Asserts that the provided locations can't be reached without the listed items but can be reached with any + one of the provided combinations""" + all_items = [item_name for item_names in possible_items for item_name in item_names] + + state = CollectionState(self.multiworld) + self.collect_all_but(all_items, state) + if only_check_listed: + for location in locations: + self.assertFalse(state.can_reach(location, "Location", 1), f"{location} is reachable without {all_items}") + else: + for location in self.multiworld.get_locations(): + loc_reachable = state.can_reach(location, "Location", 1) + self.assertEqual(loc_reachable, location.name not in locations, + f"{location.name} is reachable without {all_items}" if loc_reachable + else f"{location.name} is not reachable without {all_items}") + for item_names in possible_items: + items = self.get_items_by_name(item_names) + for item in items: + state.collect(item) + for location in locations: + self.assertTrue(state.can_reach(location, "Location", 1), + f"{location} not reachable with {item_names}") + for item in items: + state.remove(item) + + def assertBeatable(self, beatable: bool): + """Asserts that the game can be beaten with the current state""" + self.assertEqual(self.multiworld.can_beat_game(self.multiworld.state), beatable) + + # following tests are automatically run + @property + def run_default_tests(self) -> bool: + """Not possible or identical to the base test that's always being run already""" + return (self.options + or self.setUp.__code__ is not WorldTestBase.setUp.__code__ + or self.world_setup.__code__ is not WorldTestBase.world_setup.__code__) + + @property + def constructed(self) -> bool: + """A multiworld has been constructed by this point""" + return hasattr(self, "game") and hasattr(self, "multiworld") + + def test_all_state_can_reach_everything(self): + """Ensure all state can reach everything and complete the game with the defined options""" + if not (self.run_default_tests and self.constructed): + return + with self.subTest("Game", game=self.game): + excluded = self.multiworld.exclude_locations[1].value + state = self.multiworld.get_all_state(False) + for location in self.multiworld.get_locations(): + if location.name not in excluded: + with self.subTest("Location should be reached", location=location): + reachable = location.can_reach(state) + self.assertTrue(reachable, f"{location.name} unreachable") + with self.subTest("Beatable"): + self.multiworld.state = state + self.assertBeatable(True) + + def test_empty_state_can_reach_something(self): + """Ensure empty state can reach at least one location with the defined options""" + if not (self.run_default_tests and self.constructed): + return + with self.subTest("Game", game=self.game): + state = CollectionState(self.multiworld) + locations = self.multiworld.get_reachable_locations(state, 1) + self.assertGreater(len(locations), 0, + "Need to be able to reach at least one location to get started.") + + def test_fill(self): + """Generates a multiworld and validates placements with the defined options""" + if not (self.run_default_tests and self.constructed): + return + from Fill import distribute_items_restrictive + + # basically a shortened reimplementation of this method from core, in order to force the check is done + def fulfills_accessibility() -> bool: + locations = self.multiworld.get_locations(1).copy() + state = CollectionState(self.multiworld) + while locations: + sphere: typing.List[Location] = [] + for n in range(len(locations) - 1, -1, -1): + if locations[n].can_reach(state): + sphere.append(locations.pop(n)) + self.assertTrue(sphere or self.multiworld.accessibility[1] == "minimal", + f"Unreachable locations: {locations}") + if not sphere: + break + for location in sphere: + if location.item: + state.collect(location.item, True, location) + return self.multiworld.has_beaten_game(state, 1) + + with self.subTest("Game", game=self.game, seed=self.multiworld.seed): + distribute_items_restrictive(self.multiworld) + call_all(self.multiworld, "post_fill") + self.assertTrue(fulfills_accessibility(), "Collected all locations, but can't beat the game.") + placed_items = [loc.item for loc in self.multiworld.get_locations() if loc.item and loc.item.code] + self.assertLessEqual(len(self.multiworld.itempool), len(placed_items), + "Unplaced Items remaining in itempool") diff --git a/test/general/__init__.py b/test/general/__init__.py index d7ecc95749..5e0f22f4ec 100644 --- a/test/general/__init__.py +++ b/test/general/__init__.py @@ -8,6 +8,13 @@ gen_steps = ("generate_early", "create_regions", "create_items", "set_rules", "g def setup_solo_multiworld(world_type: Type[World], steps: Tuple[str, ...] = gen_steps) -> MultiWorld: + """ + Creates a multiworld with a single player of `world_type`, sets default options, and calls provided gen steps. + + :param world_type: Type of the world to generate a multiworld for + :param steps: The gen steps that should be called on the generated multiworld before returning. Default calls + steps through pre_fill + """ multiworld = MultiWorld(1) multiworld.game[1] = world_type.game multiworld.player_name = {1: "Tester"} diff --git a/test/general/TestFill.py b/test/general/test_fill.py similarity index 92% rename from test/general/TestFill.py rename to test/general/test_fill.py index 0933603dfd..4e8cc2edb7 100644 --- a/test/general/TestFill.py +++ b/test/general/test_fill.py @@ -72,7 +72,7 @@ class PlayerDefinition(object): return region -def fillRegion(world: MultiWorld, region: Region, items: List[Item]) -> List[Item]: +def fill_region(world: MultiWorld, region: Region, items: List[Item]) -> List[Item]: items = items.copy() while len(items) > 0: location = region.locations.pop(0) @@ -86,7 +86,7 @@ def fillRegion(world: MultiWorld, region: Region, items: List[Item]) -> List[Ite return items -def regionContains(region: Region, item: Item) -> bool: +def region_contains(region: Region, item: Item) -> bool: for location in region.locations: if location.item == item: return True @@ -133,6 +133,7 @@ def names(objs: list) -> Iterable[str]: class TestFillRestrictive(unittest.TestCase): def test_basic_fill(self): + """Tests `fill_restrictive` fills and removes the locations and items from their respective lists""" multi_world = generate_multi_world() player1 = generate_player_data(multi_world, 1, 2, 2) @@ -150,6 +151,7 @@ class TestFillRestrictive(unittest.TestCase): self.assertEqual([], player1.prog_items) def test_ordered_fill(self): + """Tests `fill_restrictive` fulfills set rules""" multi_world = generate_multi_world() player1 = generate_player_data(multi_world, 1, 2, 2) items = player1.prog_items @@ -166,6 +168,7 @@ class TestFillRestrictive(unittest.TestCase): self.assertEqual(locations[1].item, items[1]) def test_partial_fill(self): + """Tests that `fill_restrictive` returns unfilled locations""" multi_world = generate_multi_world() player1 = generate_player_data(multi_world, 1, 3, 2) @@ -191,6 +194,7 @@ class TestFillRestrictive(unittest.TestCase): self.assertEqual(player1.locations[0], loc2) def test_minimal_fill(self): + """Test that fill for minimal player can have unreachable items""" multi_world = generate_multi_world() player1 = generate_player_data(multi_world, 1, 2, 2) @@ -246,6 +250,7 @@ class TestFillRestrictive(unittest.TestCase): f'{item} is unreachable in {item.location}') def test_reversed_fill(self): + """Test a different set of rules can be satisfied""" multi_world = generate_multi_world() player1 = generate_player_data(multi_world, 1, 2, 2) @@ -264,6 +269,7 @@ class TestFillRestrictive(unittest.TestCase): self.assertEqual(loc1.item, item0) def test_multi_step_fill(self): + """Test that fill is able to satisfy multiple spheres""" multi_world = generate_multi_world() player1 = generate_player_data(multi_world, 1, 4, 4) @@ -288,6 +294,7 @@ class TestFillRestrictive(unittest.TestCase): self.assertEqual(locations[3].item, items[3]) def test_impossible_fill(self): + """Test that fill raises an error when it can't place any items""" multi_world = generate_multi_world() player1 = generate_player_data(multi_world, 1, 2, 2) items = player1.prog_items @@ -304,6 +311,7 @@ class TestFillRestrictive(unittest.TestCase): player1.locations.copy(), player1.prog_items.copy()) def test_circular_fill(self): + """Test that fill raises an error when it can't place all items""" multi_world = generate_multi_world() player1 = generate_player_data(multi_world, 1, 3, 3) @@ -324,6 +332,7 @@ class TestFillRestrictive(unittest.TestCase): player1.locations.copy(), player1.prog_items.copy()) def test_competing_fill(self): + """Test that fill raises an error when it can't place items in a way to satisfy the conditions""" multi_world = generate_multi_world() player1 = generate_player_data(multi_world, 1, 2, 2) @@ -340,6 +349,7 @@ class TestFillRestrictive(unittest.TestCase): player1.locations.copy(), player1.prog_items.copy()) def test_multiplayer_fill(self): + """Test that items can be placed across worlds""" multi_world = generate_multi_world(2) player1 = generate_player_data(multi_world, 1, 2, 2) player2 = generate_player_data(multi_world, 2, 2, 2) @@ -360,6 +370,7 @@ class TestFillRestrictive(unittest.TestCase): self.assertEqual(player2.locations[1].item, player2.prog_items[0]) def test_multiplayer_rules_fill(self): + """Test that fill across worlds satisfies the rules""" multi_world = generate_multi_world(2) player1 = generate_player_data(multi_world, 1, 2, 2) player2 = generate_player_data(multi_world, 2, 2, 2) @@ -383,6 +394,7 @@ class TestFillRestrictive(unittest.TestCase): self.assertEqual(player2.locations[1].item, player1.prog_items[1]) def test_restrictive_progress(self): + """Test that various spheres with different requirements can be filled""" multi_world = generate_multi_world() player1 = generate_player_data(multi_world, 1, prog_item_count=25) items = player1.prog_items.copy() @@ -405,6 +417,7 @@ class TestFillRestrictive(unittest.TestCase): locations, player1.prog_items) def test_swap_to_earlier_location_with_item_rule(self): + """Test that item swap happens and works as intended""" # test for PR#1109 multi_world = generate_multi_world(1) player1 = generate_player_data(multi_world, 1, 4, 4) @@ -430,6 +443,7 @@ class TestFillRestrictive(unittest.TestCase): self.assertEqual(sphere1_loc.item, allowed_item, "Wrong item in Sphere 1") def test_double_sweep(self): + """Test that sweep doesn't duplicate Event items when sweeping""" # test for PR1114 multi_world = generate_multi_world(1) player1 = generate_player_data(multi_world, 1, 1, 1) @@ -445,6 +459,7 @@ class TestFillRestrictive(unittest.TestCase): self.assertEqual(multi_world.state.prog_items[item.name, item.player], 1, "Sweep collected multiple times") def test_correct_item_instance_removed_from_pool(self): + """Test that a placed item gets removed from the submitted pool""" multi_world = generate_multi_world() player1 = generate_player_data(multi_world, 1, 2, 2) @@ -461,6 +476,7 @@ class TestFillRestrictive(unittest.TestCase): class TestDistributeItemsRestrictive(unittest.TestCase): def test_basic_distribute(self): + """Test that distribute_items_restrictive is deterministic""" multi_world = generate_multi_world() player1 = generate_player_data( multi_world, 1, 4, prog_item_count=2, basic_item_count=2) @@ -480,6 +496,7 @@ class TestDistributeItemsRestrictive(unittest.TestCase): self.assertFalse(locations[3].event) def test_excluded_distribute(self): + """Test that distribute_items_restrictive doesn't put advancement items on excluded locations""" multi_world = generate_multi_world() player1 = generate_player_data( multi_world, 1, 4, prog_item_count=2, basic_item_count=2) @@ -494,6 +511,7 @@ class TestDistributeItemsRestrictive(unittest.TestCase): self.assertFalse(locations[2].item.advancement) def test_non_excluded_item_distribute(self): + """Test that useful items aren't placed on excluded locations""" multi_world = generate_multi_world() player1 = generate_player_data( multi_world, 1, 4, prog_item_count=2, basic_item_count=2) @@ -508,6 +526,7 @@ class TestDistributeItemsRestrictive(unittest.TestCase): self.assertEqual(locations[1].item, basic_items[0]) def test_too_many_excluded_distribute(self): + """Test that fill fails if it can't place all progression items due to too many excluded locations""" multi_world = generate_multi_world() player1 = generate_player_data( multi_world, 1, 4, prog_item_count=2, basic_item_count=2) @@ -520,6 +539,7 @@ class TestDistributeItemsRestrictive(unittest.TestCase): self.assertRaises(FillError, distribute_items_restrictive, multi_world) def test_non_excluded_item_must_distribute(self): + """Test that fill fails if it can't place useful items due to too many excluded locations""" multi_world = generate_multi_world() player1 = generate_player_data( multi_world, 1, 4, prog_item_count=2, basic_item_count=2) @@ -534,6 +554,7 @@ class TestDistributeItemsRestrictive(unittest.TestCase): self.assertRaises(FillError, distribute_items_restrictive, multi_world) def test_priority_distribute(self): + """Test that priority locations receive advancement items""" multi_world = generate_multi_world() player1 = generate_player_data( multi_world, 1, 4, prog_item_count=2, basic_item_count=2) @@ -548,6 +569,7 @@ class TestDistributeItemsRestrictive(unittest.TestCase): self.assertTrue(locations[3].item.advancement) def test_excess_priority_distribute(self): + """Test that if there's more priority locations than advancement items, they can still fill""" multi_world = generate_multi_world() player1 = generate_player_data( multi_world, 1, 4, prog_item_count=2, basic_item_count=2) @@ -562,6 +584,7 @@ class TestDistributeItemsRestrictive(unittest.TestCase): self.assertFalse(locations[3].item.advancement) def test_multiple_world_priority_distribute(self): + """Test that priority fill can be satisfied for multiple worlds""" multi_world = generate_multi_world(3) player1 = generate_player_data( multi_world, 1, 4, prog_item_count=2, basic_item_count=2) @@ -591,7 +614,7 @@ class TestDistributeItemsRestrictive(unittest.TestCase): self.assertTrue(player3.locations[3].item.advancement) def test_can_remove_locations_in_fill_hook(self): - + """Test that distribute_items_restrictive calls the fill hook and allows for item and location removal""" multi_world = generate_multi_world() player1 = generate_player_data( multi_world, 1, 4, prog_item_count=2, basic_item_count=2) @@ -611,6 +634,7 @@ class TestDistributeItemsRestrictive(unittest.TestCase): self.assertIsNone(removed_location[0].item) def test_seed_robust_to_item_order(self): + """Test deterministic fill""" mw1 = generate_multi_world() gen1 = generate_player_data( mw1, 1, 4, prog_item_count=2, basic_item_count=2) @@ -628,6 +652,7 @@ class TestDistributeItemsRestrictive(unittest.TestCase): self.assertEqual(gen1.locations[3].item, gen2.locations[3].item) def test_seed_robust_to_location_order(self): + """Test deterministic fill even if locations in a region are reordered""" mw1 = generate_multi_world() gen1 = generate_player_data( mw1, 1, 4, prog_item_count=2, basic_item_count=2) @@ -646,6 +671,7 @@ class TestDistributeItemsRestrictive(unittest.TestCase): self.assertEqual(gen1.locations[3].item, gen2.locations[3].item) def test_can_reserve_advancement_items_for_general_fill(self): + """Test that priority locations fill still satisfies item rules""" multi_world = generate_multi_world() player1 = generate_player_data( multi_world, 1, location_count=5, prog_item_count=5) @@ -655,14 +681,14 @@ class TestDistributeItemsRestrictive(unittest.TestCase): location = player1.locations[0] location.progress_type = LocationProgressType.PRIORITY - location.item_rule = lambda item: item != items[ - 0] and item != items[1] and item != items[2] and item != items[3] + location.item_rule = lambda item: item not in items[:4] distribute_items_restrictive(multi_world) self.assertEqual(location.item, items[4]) def test_non_excluded_local_items(self): + """Test that local items get placed locally in a multiworld""" multi_world = generate_multi_world(2) player1 = generate_player_data( multi_world, 1, location_count=5, basic_item_count=5) @@ -683,6 +709,7 @@ class TestDistributeItemsRestrictive(unittest.TestCase): self.assertFalse(item.location.event, False) def test_early_items(self) -> None: + """Test that the early items API successfully places items early""" mw = generate_multi_world(2) player1 = generate_player_data(mw, 1, location_count=5, basic_item_count=5) player2 = generate_player_data(mw, 2, location_count=5, basic_item_count=5) @@ -762,21 +789,22 @@ class TestBalanceMultiworldProgression(unittest.TestCase): # Sphere 1 region = player1.generate_region(player1.menu, 20) - items = fillRegion(multi_world, region, [ + items = fill_region(multi_world, region, [ player1.prog_items[0]] + items) # Sphere 2 region = player1.generate_region( player1.regions[1], 20, lambda state: state.has(player1.prog_items[0].name, player1.id)) - items = fillRegion( + items = fill_region( multi_world, region, [player1.prog_items[1], player2.prog_items[0]] + items) # Sphere 3 region = player2.generate_region( player2.menu, 20, lambda state: state.has(player2.prog_items[0].name, player2.id)) - fillRegion(multi_world, region, [player2.prog_items[1]] + items) + fill_region(multi_world, region, [player2.prog_items[1]] + items) def test_balances_progression(self) -> None: + """Tests that progression balancing moves progression items earlier""" self.multi_world.progression_balancing[self.player1.id].value = 50 self.multi_world.progression_balancing[self.player2.id].value = 50 @@ -789,6 +817,7 @@ class TestBalanceMultiworldProgression(unittest.TestCase): self.player1.regions[1], self.player2.prog_items[0]) def test_balances_progression_light(self) -> None: + """Test that progression balancing still moves items earlier on minimum value""" self.multi_world.progression_balancing[self.player1.id].value = 1 self.multi_world.progression_balancing[self.player2.id].value = 1 @@ -802,6 +831,7 @@ class TestBalanceMultiworldProgression(unittest.TestCase): self.player1.regions[1], self.player2.prog_items[0]) def test_balances_progression_heavy(self) -> None: + """Test that progression balancing moves items earlier on maximum value""" self.multi_world.progression_balancing[self.player1.id].value = 99 self.multi_world.progression_balancing[self.player2.id].value = 99 @@ -815,6 +845,7 @@ class TestBalanceMultiworldProgression(unittest.TestCase): self.player1.regions[1], self.player2.prog_items[0]) def test_skips_balancing_progression(self) -> None: + """Test that progression balancing is skipped when players have it disabled""" self.multi_world.progression_balancing[self.player1.id].value = 0 self.multi_world.progression_balancing[self.player2.id].value = 0 @@ -827,6 +858,7 @@ class TestBalanceMultiworldProgression(unittest.TestCase): self.player1.regions[2], self.player2.prog_items[0]) def test_ignores_priority_locations(self) -> None: + """Test that progression items on priority locations don't get moved by balancing""" self.multi_world.progression_balancing[self.player1.id].value = 50 self.multi_world.progression_balancing[self.player2.id].value = 50 diff --git a/test/general/TestHelpers.py b/test/general/test_helpers.py similarity index 90% rename from test/general/TestHelpers.py rename to test/general/test_helpers.py index 17fdce653c..83b56b3438 100644 --- a/test/general/TestHelpers.py +++ b/test/general/test_helpers.py @@ -1,8 +1,7 @@ -from argparse import Namespace -from typing import Dict, Optional, Callable - -from BaseClasses import MultiWorld, CollectionState, Region import unittest +from typing import Callable, Dict, Optional + +from BaseClasses import CollectionState, MultiWorld, Region class TestHelpers(unittest.TestCase): @@ -15,7 +14,8 @@ class TestHelpers(unittest.TestCase): self.multiworld.player_name = {1: "Tester"} self.multiworld.set_seed() - def testRegionHelpers(self) -> None: + def test_region_helpers(self) -> None: + """Tests `Region.add_locations()` and `Region.add_exits()` have correct behavior""" regions: Dict[str, str] = { "TestRegion1": "I'm an apple", "TestRegion2": "I'm a banana", @@ -79,4 +79,5 @@ class TestHelpers(unittest.TestCase): current_region.add_exits(reg_exit_set[region]) exit_names = {_exit.name for _exit in current_region.exits} for reg_exit in reg_exit_set[region]: - self.assertTrue(f"{region} -> {reg_exit}" in exit_names, f"{region} -> {reg_exit} not in {exit_names}") + self.assertTrue(f"{region} -> {reg_exit}" in exit_names, + f"{region} -> {reg_exit} not in {exit_names}") diff --git a/test/general/TestHostYAML.py b/test/general/test_host_yaml.py similarity index 87% rename from test/general/TestHostYAML.py rename to test/general/test_host_yaml.py index f5fd406cac..9408f95b16 100644 --- a/test/general/TestHostYAML.py +++ b/test/general/test_host_yaml.py @@ -15,6 +15,7 @@ class TestIDs(unittest.TestCase): cls.yaml_options = Utils.parse_yaml(f.read()) def test_utils_in_yaml(self) -> None: + """Tests that the auto generated host.yaml has default settings in it""" for option_key, option_set in Utils.get_default_options().items(): with self.subTest(option_key): self.assertIn(option_key, self.yaml_options) @@ -22,6 +23,7 @@ class TestIDs(unittest.TestCase): self.assertIn(sub_option_key, self.yaml_options[option_key]) def test_yaml_in_utils(self) -> None: + """Tests that the auto generated host.yaml shows up in reference calls""" utils_options = Utils.get_default_options() for option_key, option_set in self.yaml_options.items(): with self.subTest(option_key): diff --git a/test/general/TestIDs.py b/test/general/test_ids.py similarity index 82% rename from test/general/TestIDs.py rename to test/general/test_ids.py index db1c9461b9..4edfb8d994 100644 --- a/test/general/TestIDs.py +++ b/test/general/test_ids.py @@ -3,35 +3,37 @@ from worlds.AutoWorld import AutoWorldRegister class TestIDs(unittest.TestCase): - def testUniqueItems(self): + def test_unique_items(self): + """Tests that every game has a unique ID per item in the datapackage""" known_item_ids = set() for gamename, world_type in AutoWorldRegister.world_types.items(): current = len(known_item_ids) known_item_ids |= set(world_type.item_id_to_name) self.assertEqual(len(known_item_ids) - len(world_type.item_id_to_name), current) - def testUniqueLocations(self): + def test_unique_locations(self): + """Tests that every game has a unique ID per location in the datapackage""" known_location_ids = set() for gamename, world_type in AutoWorldRegister.world_types.items(): current = len(known_location_ids) known_location_ids |= set(world_type.location_id_to_name) self.assertEqual(len(known_location_ids) - len(world_type.location_id_to_name), current) - def testRangeItems(self): + def test_range_items(self): """There are Javascript clients, which are limited to Number.MAX_SAFE_INTEGER due to 64bit float precision.""" for gamename, world_type in AutoWorldRegister.world_types.items(): with self.subTest(game=gamename): for item_id in world_type.item_id_to_name: self.assertLess(item_id, 2**53) - def testRangeLocations(self): + def test_range_locations(self): """There are Javascript clients, which are limited to Number.MAX_SAFE_INTEGER due to 64bit float precision.""" for gamename, world_type in AutoWorldRegister.world_types.items(): with self.subTest(game=gamename): for location_id in world_type.location_id_to_name: self.assertLess(location_id, 2**53) - def testReservedItems(self): + def test_reserved_items(self): """negative item IDs are reserved to the special "Archipelago" world.""" for gamename, world_type in AutoWorldRegister.world_types.items(): with self.subTest(game=gamename): @@ -42,7 +44,7 @@ class TestIDs(unittest.TestCase): for item_id in world_type.item_id_to_name: self.assertGreater(item_id, 0) - def testReservedLocations(self): + def test_reserved_locations(self): """negative location IDs are reserved to the special "Archipelago" world.""" for gamename, world_type in AutoWorldRegister.world_types.items(): with self.subTest(game=gamename): @@ -53,12 +55,14 @@ class TestIDs(unittest.TestCase): for location_id in world_type.location_id_to_name: self.assertGreater(location_id, 0) - def testDuplicateItemIDs(self): + def test_duplicate_item_ids(self): + """Test that a game doesn't have item id overlap within its own datapackage""" for gamename, world_type in AutoWorldRegister.world_types.items(): with self.subTest(game=gamename): self.assertEqual(len(world_type.item_id_to_name), len(world_type.item_name_to_id)) - def testDuplicateLocationIDs(self): + def test_duplicate_location_ids(self): + """Test that a game doesn't have location id overlap within its own datapackage""" for gamename, world_type in AutoWorldRegister.world_types.items(): with self.subTest(game=gamename): self.assertEqual(len(world_type.location_id_to_name), len(world_type.location_name_to_id)) diff --git a/test/general/TestImplemented.py b/test/general/test_implemented.py similarity index 93% rename from test/general/TestImplemented.py rename to test/general/test_implemented.py index 22c546eff1..67d0e5ff72 100644 --- a/test/general/TestImplemented.py +++ b/test/general/test_implemented.py @@ -5,7 +5,7 @@ from . import setup_solo_multiworld class TestImplemented(unittest.TestCase): - def testCompletionCondition(self): + def test_completion_condition(self): """Ensure a completion condition is set that has requirements.""" for game_name, world_type in AutoWorldRegister.world_types.items(): if not world_type.hidden and game_name not in {"Sudoku"}: @@ -13,7 +13,7 @@ class TestImplemented(unittest.TestCase): multiworld = setup_solo_multiworld(world_type) self.assertFalse(multiworld.completion_condition[1](multiworld.state)) - def testEntranceParents(self): + def test_entrance_parents(self): """Tests that the parents of created Entrances match the exiting Region.""" for game_name, world_type in AutoWorldRegister.world_types.items(): if not world_type.hidden: @@ -23,7 +23,7 @@ class TestImplemented(unittest.TestCase): for exit in region.exits: self.assertEqual(exit.parent_region, region) - def testStageMethods(self): + def test_stage_methods(self): """Tests that worlds don't try to implement certain steps that are only ever called as stage.""" for game_name, world_type in AutoWorldRegister.world_types.items(): if not world_type.hidden: diff --git a/test/general/TestItems.py b/test/general/test_items.py similarity index 88% rename from test/general/TestItems.py rename to test/general/test_items.py index 95eb8d28d9..464d246e1f 100644 --- a/test/general/TestItems.py +++ b/test/general/test_items.py @@ -4,7 +4,8 @@ from . import setup_solo_multiworld class TestBase(unittest.TestCase): - def testCreateItem(self): + def test_create_item(self): + """Test that a world can successfully create all items in its datapackage""" for game_name, world_type in AutoWorldRegister.world_types.items(): proxy_world = world_type(None, 0) # this is identical to MultiServer.py creating worlds for item_name in world_type.item_name_to_id: @@ -12,7 +13,7 @@ class TestBase(unittest.TestCase): item = proxy_world.create_item(item_name) self.assertEqual(item.name, item_name) - def testItemNameGroupHasValidItem(self): + def test_item_name_group_has_valid_item(self): """Test that all item name groups contain valid items. """ # This cannot test for Event names that you may have declared for logic, only sendable Items. # In such a case, you can add your entries to this Exclusion dict. Game Name -> Group Names @@ -33,7 +34,7 @@ class TestBase(unittest.TestCase): for item in items: self.assertIn(item, world_type.item_name_to_id) - def testItemNameGroupConflict(self): + def test_item_name_group_conflict(self): """Test that all item name groups aren't also item names.""" for game_name, world_type in AutoWorldRegister.world_types.items(): with self.subTest(game_name, game_name=game_name): @@ -41,7 +42,8 @@ class TestBase(unittest.TestCase): with self.subTest(group_name, group_name=group_name): self.assertNotIn(group_name, world_type.item_name_to_id) - def testItemCountGreaterEqualLocations(self): + def test_item_count_greater_equal_locations(self): + """Test that by the pre_fill step under default settings, each game submits items >= locations""" for game_name, world_type in AutoWorldRegister.world_types.items(): with self.subTest("Game", game=game_name): multiworld = setup_solo_multiworld(world_type) diff --git a/test/general/TestLocations.py b/test/general/test_locations.py similarity index 96% rename from test/general/TestLocations.py rename to test/general/test_locations.py index e77e7a6332..2e609a756f 100644 --- a/test/general/TestLocations.py +++ b/test/general/test_locations.py @@ -5,7 +5,7 @@ from . import setup_solo_multiworld class TestBase(unittest.TestCase): - def testCreateDuplicateLocations(self): + def test_create_duplicate_locations(self): """Tests that no two Locations share a name or ID.""" for game_name, world_type in AutoWorldRegister.world_types.items(): multiworld = setup_solo_multiworld(world_type) @@ -20,7 +20,7 @@ class TestBase(unittest.TestCase): self.assertLessEqual(locations.most_common(1)[0][1], 1, f"{world_type.game} has duplicate of location ID {locations.most_common(1)}") - def testLocationsInDatapackage(self): + def test_locations_in_datapackage(self): """Tests that created locations not filled before fill starts exist in the datapackage.""" for game_name, world_type in AutoWorldRegister.world_types.items(): with self.subTest("Game", game_name=game_name): @@ -30,7 +30,7 @@ class TestBase(unittest.TestCase): self.assertIn(location.name, world_type.location_name_to_id) self.assertEqual(location.address, world_type.location_name_to_id[location.name]) - def testLocationCreationSteps(self): + def test_location_creation_steps(self): """Tests that Regions and Locations aren't created after `create_items`.""" gen_steps = ("generate_early", "create_regions", "create_items") for game_name, world_type in AutoWorldRegister.world_types.items(): @@ -60,7 +60,7 @@ class TestBase(unittest.TestCase): self.assertGreaterEqual(location_count, len(multiworld.get_locations()), f"{game_name} modified locations count during pre_fill") - def testLocationGroup(self): + def test_location_group(self): """Test that all location name groups contain valid locations and don't share names.""" for game_name, world_type in AutoWorldRegister.world_types.items(): with self.subTest(game_name, game_name=game_name): diff --git a/test/general/TestNames.py b/test/general/test_names.py similarity index 92% rename from test/general/TestNames.py rename to test/general/test_names.py index 6dae53240d..7be76eed4b 100644 --- a/test/general/TestNames.py +++ b/test/general/test_names.py @@ -3,7 +3,7 @@ from worlds.AutoWorld import AutoWorldRegister class TestNames(unittest.TestCase): - def testItemNamesFormat(self): + def test_item_names_format(self): """Item names must not be all numeric in order to differentiate between ID and name in !hint""" for gamename, world_type in AutoWorldRegister.world_types.items(): with self.subTest(game=gamename): @@ -11,7 +11,7 @@ class TestNames(unittest.TestCase): self.assertFalse(item_name.isnumeric(), f"Item name \"{item_name}\" is invalid. It must not be numeric.") - def testLocationNameFormat(self): + def test_location_name_format(self): """Location names must not be all numeric in order to differentiate between ID and name in !hint_location""" for gamename, world_type in AutoWorldRegister.world_types.items(): with self.subTest(game=gamename): diff --git a/test/general/TestOptions.py b/test/general/test_options.py similarity index 78% rename from test/general/TestOptions.py rename to test/general/test_options.py index 4a3bd0b02a..e1136f93c9 100644 --- a/test/general/TestOptions.py +++ b/test/general/test_options.py @@ -3,7 +3,8 @@ from worlds.AutoWorld import AutoWorldRegister class TestOptions(unittest.TestCase): - def testOptionsHaveDocString(self): + def test_options_have_doc_string(self): + """Test that submitted options have their own specified docstring""" for gamename, world_type in AutoWorldRegister.world_types.items(): if not world_type.hidden: for option_key, option in world_type.options_dataclass.type_hints.items(): diff --git a/test/general/TestReachability.py b/test/general/test_reachability.py similarity index 91% rename from test/general/TestReachability.py rename to test/general/test_reachability.py index dd786b8352..828912ee35 100644 --- a/test/general/TestReachability.py +++ b/test/general/test_reachability.py @@ -31,7 +31,8 @@ class TestBase(unittest.TestCase): } } - def testDefaultAllStateCanReachEverything(self): + def test_default_all_state_can_reach_everything(self): + """Ensure all state can reach everything and complete the game with the defined options""" for game_name, world_type in AutoWorldRegister.world_types.items(): unreachable_regions = self.default_settings_unreachable_regions.get(game_name, set()) with self.subTest("Game", game=game_name): @@ -54,7 +55,8 @@ class TestBase(unittest.TestCase): with self.subTest("Completion Condition"): self.assertTrue(world.can_beat_game(state)) - def testDefaultEmptyStateCanReachSomething(self): + def test_default_empty_state_can_reach_something(self): + """Ensure empty state can reach at least one location with the defined options""" for game_name, world_type in AutoWorldRegister.world_types.items(): with self.subTest("Game", game=game_name): world = setup_solo_multiworld(world_type) diff --git a/test/netutils/TestLocationStore.py b/test/netutils/test_location_store.py similarity index 100% rename from test/netutils/TestLocationStore.py rename to test/netutils/test_location_store.py diff --git a/test/programs/data/OnePlayer/test.yaml b/test/programs/data/one_player/test.yaml similarity index 100% rename from test/programs/data/OnePlayer/test.yaml rename to test/programs/data/one_player/test.yaml diff --git a/test/programs/TestGenerate.py b/test/programs/test_generate.py similarity index 98% rename from test/programs/TestGenerate.py rename to test/programs/test_generate.py index 73e1d3b834..887a417ec9 100644 --- a/test/programs/TestGenerate.py +++ b/test/programs/test_generate.py @@ -16,7 +16,7 @@ class TestGenerateMain(unittest.TestCase): generate_dir = Path(Generate.__file__).parent run_dir = generate_dir / "test" # reproducible cwd that's neither __file__ nor Generate.__file__ - abs_input_dir = Path(__file__).parent / 'data' / 'OnePlayer' + abs_input_dir = Path(__file__).parent / 'data' / 'one_player' rel_input_dir = abs_input_dir.relative_to(run_dir) # directly supplied relative paths are relative to cwd yaml_input_dir = abs_input_dir.relative_to(generate_dir) # yaml paths are relative to user_path diff --git a/test/programs/TestMultiServer.py b/test/programs/test_multi_server.py similarity index 100% rename from test/programs/TestMultiServer.py rename to test/programs/test_multi_server.py diff --git a/test/utils/TestSIPrefix.py b/test/utils/test_si_prefix.py similarity index 100% rename from test/utils/TestSIPrefix.py rename to test/utils/test_si_prefix.py diff --git a/test/webhost/TestAPIGenerate.py b/test/webhost/test_api_generate.py similarity index 93% rename from test/webhost/TestAPIGenerate.py rename to test/webhost/test_api_generate.py index 8ea78f27f9..b8bdcb38c7 100644 --- a/test/webhost/TestAPIGenerate.py +++ b/test/webhost/test_api_generate.py @@ -19,11 +19,11 @@ class TestDocs(unittest.TestCase): cls.client = app.test_client() - def testCorrectErrorEmptyRequest(self): + def test_correct_error_empty_request(self): response = self.client.post("/api/generate") self.assertIn("No options found. Expected file attachment or json weights.", response.text) - def testGenerationQueued(self): + def test_generation_queued(self): options = { "Tester1": { diff --git a/test/webhost/TestDocs.py b/test/webhost/test_docs.py similarity index 96% rename from test/webhost/TestDocs.py rename to test/webhost/test_docs.py index f6ede1543e..68aba05f9d 100644 --- a/test/webhost/TestDocs.py +++ b/test/webhost/test_docs.py @@ -11,7 +11,7 @@ class TestDocs(unittest.TestCase): def setUpClass(cls) -> None: cls.tutorials_data = WebHost.create_ordered_tutorials_file() - def testHasTutorial(self): + def test_has_tutorial(self): games_with_tutorial = set(entry["gameTitle"] for entry in self.tutorials_data) for game_name, world_type in AutoWorldRegister.world_types.items(): if not world_type.hidden: @@ -27,7 +27,7 @@ class TestDocs(unittest.TestCase): self.fail(f"{game_name} has no setup tutorial. " f"Games with Tutorial: {games_with_tutorial}") - def testHasGameInfo(self): + def test_has_game_info(self): for game_name, world_type in AutoWorldRegister.world_types.items(): if not world_type.hidden: target_path = Utils.local_path("WebHostLib", "static", "generated", "docs", game_name) diff --git a/test/webhost/TestFileGeneration.py b/test/webhost/test_file_generation.py similarity index 96% rename from test/webhost/TestFileGeneration.py rename to test/webhost/test_file_generation.py index f01b70e14f..059f6b49a1 100644 --- a/test/webhost/TestFileGeneration.py +++ b/test/webhost/test_file_generation.py @@ -13,7 +13,7 @@ class TestFileGeneration(unittest.TestCase): # should not create the folder *here* cls.incorrect_path = os.path.join(os.path.split(os.path.dirname(__file__))[0], "WebHostLib") - def testOptions(self): + def test_options(self): from WebHostLib.options import create as create_options_files create_options_files() target = os.path.join(self.correct_path, "static", "generated", "configs") @@ -30,7 +30,7 @@ class TestFileGeneration(unittest.TestCase): for value in roll_options({file.name: f.read()})[0].values(): self.assertTrue(value is True, f"Default Options for template {file.name} cannot be run.") - def testTutorial(self): + def test_tutorial(self): WebHost.create_ordered_tutorials_file() self.assertTrue(os.path.exists(os.path.join(self.correct_path, "static", "generated", "tutorials.json"))) self.assertFalse(os.path.exists(os.path.join(self.incorrect_path, "static", "generated", "tutorials.json"))) From 50244342d9b64bfd0c8534da181c0aa5f292aaed Mon Sep 17 00:00:00 2001 From: BootsinSoots <102177943+BootsinSoots@users.noreply.github.com> Date: Sun, 22 Oct 2023 07:11:19 -0400 Subject: [PATCH 138/144] Docs: Added Note Explaining BK and fix typo in advanced settings (#2316) * Added Note Explaining BK Added suggested change regarding BK mode from Issue #2295 * Changed to Glossary hyperlink * Fix minor typo in exclude_locations * Update worlds/generic/docs/advanced_settings_en.md Co-authored-by: kindasneaki * Docs: Reformat advanced_settings_en/progression_balancing --------- Co-authored-by: kindasneaki Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> --- worlds/generic/docs/advanced_settings_en.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/worlds/generic/docs/advanced_settings_en.md b/worlds/generic/docs/advanced_settings_en.md index 456795dac4..6d5e20462f 100644 --- a/worlds/generic/docs/advanced_settings_en.md +++ b/worlds/generic/docs/advanced_settings_en.md @@ -108,7 +108,9 @@ guide: [Archipelago Plando Guide](/tutorial/Archipelago/plando/en) * `minimal` will only guarantee that the seed is beatable. You will be guaranteed able to finish the seed logically but may not be able to access all locations or acquire all items. A good example of this is having a big key in the big chest in a dungeon in ALTTP making it impossible to get and finish the dungeon. -* `progression_balancing` is a system the Archipelago generator uses to try and reduce "BK mode" as much as possible. +* `progression_balancing` is a system the Archipelago generator uses to try and reduce + ["BK mode"](/glossary/en/#burger-king-/-bk-mode) + as much as possible. This primarily involves moving necessary progression items into earlier logic spheres to make the games more accessible so that players almost always have something to do. This can be in a range from 0 to 99, and is 50 by default. This number represents a percentage of the furthest progressible player. @@ -130,7 +132,7 @@ guide: [Archipelago Plando Guide](/tutorial/Archipelago/plando/en) there without using any hint points. * `exclude_locations` lets you define any locations that you don't want to do and during generation will force a "junk" item which isn't necessary for progression to go in these locations. -* `priority_locations` is the inverse of `exlcude_locations`, forcing a progression item in the defined locations. +* `priority_locations` is the inverse of `exclude_locations`, forcing a progression item in the defined locations. * `item_links` allows players to link their items into a group with the same item link name and game. The items declared in `item_pool` get combined and when an item is found for the group, all players in the group receive it. Item links can also have local and non local items, forcing the items to either be placed within the worlds of the group or in From 724999fc43c377161c96dee553f362a551b58c22 Mon Sep 17 00:00:00 2001 From: espeon65536 <81029175+espeon65536@users.noreply.github.com> Date: Sun, 22 Oct 2023 10:38:47 -0600 Subject: [PATCH 139/144] Ocarina of Time: long-awaited bugfixes (#2344) - Added location name groups, so you can make your entire Water Temple priority to annoy everyone else - Significant improvement to ER generation success rate (~80% to >99%) - Changed `adult_trade_start` option to a choice option instead of a list (this shouldn't actually break any YAMLs though, due to the lesser-known property of lists parsing as a uniformly-weighted choice) - Major improvements to the option tooltips where needed. (Possibly too much text now) - Changed default hint distribution to `async` to help people's generation times. The tooltip explains that it removes WOTH hints so people hopefully don't get tripped up. - Makes stick and nut capacity upgrades useful items - Added shop prices and required trials to spoiler log - Added Cojiro to adult trade item group, because it had been forgotten previously - Fixed size-modified chests not being moved properly due to trap appearance changing the size - Fixed Thieves Hideout keyring not being allowed in start inventory - Fixed hint generation not accurately flagging barren locations on certain dungeon item shuffle settings - Fixed bug where you could plando arbitrarily-named items into the world, breaking everything --- worlds/oot/ItemPool.py | 4 +- worlds/oot/Location.py | 58 ++++- worlds/oot/LocationList.py | 2 +- worlds/oot/Options.py | 489 ++++++++++++++++++++++--------------- worlds/oot/Patches.py | 30 ++- worlds/oot/Rules.py | 3 - worlds/oot/__init__.py | 90 ++++--- 7 files changed, 424 insertions(+), 252 deletions(-) diff --git a/worlds/oot/ItemPool.py b/worlds/oot/ItemPool.py index 94e1011ddc..6ca6bc9268 100644 --- a/worlds/oot/ItemPool.py +++ b/worlds/oot/ItemPool.py @@ -350,7 +350,7 @@ def generate_itempool(ootworld): ootworld.itempool = [ootworld.create_item(item) for item in pool] for (location_name, item) in placed_items.items(): location = world.get_location(location_name, player) - location.place_locked_item(ootworld.create_item(item)) + location.place_locked_item(ootworld.create_item(item, allow_arbitrary_name=True)) def get_pool_core(world): @@ -675,7 +675,7 @@ def get_pool_core(world): world.remove_from_start_inventory.append('Scarecrow Song') if world.no_epona_race: - world.multiworld.push_precollected(world.create_item('Epona')) + world.multiworld.push_precollected(world.create_item('Epona', allow_arbitrary_name=True)) world.remove_from_start_inventory.append('Epona') if world.shuffle_smallkeys == 'vanilla': diff --git a/worlds/oot/Location.py b/worlds/oot/Location.py index e2b0e52e4d..3f7d75517e 100644 --- a/worlds/oot/Location.py +++ b/worlds/oot/Location.py @@ -2,6 +2,8 @@ from enum import Enum from .LocationList import location_table from BaseClasses import Location +non_indexed_location_types = {'Boss', 'Event', 'Drop', 'HintStone', 'Hint'} + location_id_offset = 67000 locnames_pre_70 = { "Gift from Sages", @@ -18,7 +20,7 @@ new_name_order = sorted(location_table.keys(), else 0) location_name_to_id = {name: (location_id_offset + index) for (index, name) in enumerate(new_name_order) - if location_table[name][0] not in {'Boss', 'Event', 'Drop', 'HintStone', 'Hint'}} + if location_table[name][0] not in non_indexed_location_types} class DisableType(Enum): ENABLED = 0 @@ -83,3 +85,57 @@ def LocationFactory(locations, player: int): return ret +def build_location_name_groups() -> dict: + + def fix_sing(t) -> tuple: + if isinstance(t, str): + return (t,) + return t + + def rename(d, k1, k2) -> None: + d[k2] = d[k1] + del d[k1] + + # whoever wrote the location table didn't realize they need to add a comma to mark a singleton as a tuple + # so we have to check types unfortunately + tags = set() + for v in location_table.values(): + if v[5] is not None: + tags.update(fix_sing(v[5])) + + sorted_tags = sorted(list(tags)) + + ret = { + tag: {k for k, v in location_table.items() + if v[5] is not None + and tag in fix_sing(v[5]) + and v[0] not in non_indexed_location_types} + for tag in sorted_tags + } + + # Delete tags which are a combination of other tags + del ret['Death Mountain'] + del ret['Forest'] + del ret['Gerudo'] + del ret['Kakariko'] + del ret['Market'] + + # Delete Vanilla and MQ tags because they are just way too broad + del ret['Vanilla'] + del ret['Master Quest'] + + rename(ret, 'Beehive', 'Beehives') + rename(ret, 'Cow', 'Cows') + rename(ret, 'Crate', 'Crates') + rename(ret, 'Deku Scrub', 'Deku Scrubs') + rename(ret, 'FlyingPot', 'Flying Pots') + rename(ret, 'Freestanding', 'Freestanding Items') + rename(ret, 'Pot', 'Pots') + rename(ret, 'RupeeTower', 'Rupee Groups') + rename(ret, 'SmallCrate', 'Small Crates') + rename(ret, 'the Market', 'Market') + rename(ret, 'the Graveyard', 'Graveyard') + rename(ret, 'the Lost Woods', 'Lost Woods') + + return ret + diff --git a/worlds/oot/LocationList.py b/worlds/oot/LocationList.py index 3f4602c428..27ad575699 100644 --- a/worlds/oot/LocationList.py +++ b/worlds/oot/LocationList.py @@ -238,7 +238,7 @@ location_table = OrderedDict([ ("Market Night Green Rupee Crate 1", ("Crate", 0x21, (0,0,24), None, 'Rupee (1)', ("the Market", "Market", "Crate"))), ("Market Night Green Rupee Crate 2", ("Crate", 0x21, (0,0,25), None, 'Rupee (1)', ("the Market", "Market", "Crate"))), ("Market Night Green Rupee Crate 3", ("Crate", 0x21, (0,0,26), None, 'Rupee (1)', ("the Market", "Market", "Crate"))), - ("Market Dog Lady House Crate", ("Crate", 0x35, (0,0,3), None, 'Rupees (5)', ("Market", "Market", "Crate"))), + ("Market Dog Lady House Crate", ("Crate", 0x35, (0,0,3), None, 'Rupees (5)', ("the Market", "Market", "Crate"))), ("Market Guard House Child Crate", ("Crate", 0x4D, (0,0,6), None, 'Rupee (1)', ("the Market", "Market", "Crate"))), ("Market Guard House Child Pot 1", ("Pot", 0x4D, (0,0,9), None, 'Rupee (1)', ("the Market", "Market", "Pot"))), ("Market Guard House Child Pot 2", ("Pot", 0x4D, (0,0,10), None, 'Rupee (1)', ("the Market", "Market", "Pot"))), diff --git a/worlds/oot/Options.py b/worlds/oot/Options.py index 03f5346cee..120027e29d 100644 --- a/worlds/oot/Options.py +++ b/worlds/oot/Options.py @@ -30,7 +30,17 @@ class TrackRandomRange(Range): class Logic(Choice): - """Set the logic used for the generator.""" + """Set the logic used for the generator. + Glitchless: Normal gameplay. Can enable more difficult logical paths using the Logic Tricks option. + Glitched: Many powerful glitches expected, such as bomb hovering and clipping. + Glitched is incompatible with the following settings: + - All forms of entrance randomizer + - MQ dungeons + - Pot shuffle + - Freestanding item shuffle + - Crate shuffle + - Beehive shuffle + No Logic: No logic is used when placing items. Not recommended for most players.""" display_name = "Logic Rules" option_glitchless = 0 option_glitched = 1 @@ -38,12 +48,16 @@ class Logic(Choice): class NightTokens(Toggle): - """Nighttime skulltulas will logically require Sun's Song.""" + """When enabled, nighttime skulltulas logically require Sun's Song.""" display_name = "Nighttime Skulltulas Expect Sun's Song" class Forest(Choice): - """Set the state of Kokiri Forest and the path to Deku Tree.""" + """Set the state of Kokiri Forest and the path to Deku Tree. + Open: Neither the forest exit nor the path to Deku Tree is blocked. + Closed Deku: The forest exit is not blocked; the path to Deku Tree requires Kokiri Sword and Deku Shield. + Closed: Path to Deku Tree requires sword and shield. The forest exit is blocked until Deku Tree is beaten. + Closed forest will force child start, and becomes Closed Deku if interior entrances, overworld entrances, warp songs, or random spawn positions are enabled.""" display_name = "Forest" option_open = 0 option_closed_deku = 1 @@ -53,7 +67,10 @@ class Forest(Choice): class Gate(Choice): - """Set the state of the Kakariko Village gate.""" + """Set the state of the Kakariko Village gate for child. The gate is always open as adult. + Open: The gate starts open. Happy Mask Shop opens upon receiving Zelda's Letter. + Zelda: The gate and Mask Shop open upon receiving Zelda's Letter, without needing to show it to the guard. + Closed: Vanilla behavior; the gate and Mask Shop open upon showing Zelda's Letter to the gate guard.""" display_name = "Kakariko Gate" option_open = 0 option_zelda = 1 @@ -61,12 +78,15 @@ class Gate(Choice): class DoorOfTime(DefaultOnToggle): - """Open the Door of Time by default, without the Song of Time.""" + """When enabled, the Door of Time starts opened, without needing Song of Time.""" display_name = "Open Door of Time" class Fountain(Choice): - """Set the state of King Zora, blocking the way to Zora's Fountain.""" + """Set the state of King Zora, blocking the way to Zora's Fountain. + Open: King Zora starts moved as both ages. Ruto's Letter is removed. + Adult: King Zora must be moved as child, but is always moved for adult. + Closed: Vanilla behavior; King Zora must be shown Ruto's Letter as child to move him as both ages.""" display_name = "Zora's Fountain" option_open = 0 option_adult = 1 @@ -75,7 +95,10 @@ class Fountain(Choice): class Fortress(Choice): - """Set the requirements for access to Gerudo Fortress.""" + """Set the requirements for access to Gerudo Fortress. + Normal: Vanilla behavior; all four carpenters must be rescued. + Fast: Only one carpenter must be rescued, which is the one in the bottom-left of the fortress. + Open: The Gerudo Valley bridge starts repaired. Gerudo Membership Card is given to start if not shuffled.""" display_name = "Gerudo Fortress" option_normal = 0 option_fast = 1 @@ -84,7 +107,14 @@ class Fortress(Choice): class Bridge(Choice): - """Set the requirements for the Rainbow Bridge.""" + """Set the requirements for the Rainbow Bridge. + Open: The bridge is always present. + Vanilla: Bridge requires Shadow Medallion, Spirit Medallion, and Light Arrows. + Stones: Bridge requires a configurable amount of Spiritual Stones. + Medallions: Bridge requires a configurable amount of medallions. + Dungeons: Bridge requires a configurable amount of rewards (stones + medallions). + Tokens: Bridge requires a configurable amount of gold skulltula tokens. + Hearts: Bridge requires a configurable amount of hearts.""" display_name = "Rainbow Bridge Requirement" option_open = 0 option_vanilla = 1 @@ -122,8 +152,9 @@ class StartingAge(Choice): class InteriorEntrances(Choice): - """Shuffles interior entrances. "Simple" shuffles houses and Great Fairies; "All" includes Windmill, Link's House, - Temple of Time, and Kak potion shop.""" + """Shuffles interior entrances. + Simple: Houses and Great Fairies are shuffled. + All: In addition to Simple, includes Windmill, Link's House, Temple of Time, and the Kakariko potion shop.""" display_name = "Shuffle Interior Entrances" option_off = 0 option_simple = 1 @@ -137,7 +168,9 @@ class GrottoEntrances(Toggle): class DungeonEntrances(Choice): - """Shuffles dungeon entrances. Opens Deku, Fire and BotW to both ages. "All" includes Ganon's Castle.""" + """Shuffles dungeon entrances. When enabled, both ages will have access to Fire Temple, Bottom of the Well, and Deku Tree. + Simple: Shuffle dungeon entrances except for Ganon's Castle. + All: Include Ganon's Castle as well.""" display_name = "Shuffle Dungeon Entrances" option_off = 0 option_simple = 1 @@ -146,7 +179,9 @@ class DungeonEntrances(Choice): class BossEntrances(Choice): - """Shuffles boss entrances. "Limited" prevents age-mixing of bosses.""" + """Shuffles boss entrances. + Limited: Bosses will be limited to the ages that typically fight them. + Full: Bosses may be fought as different ages than usual. Child can defeat Phantom Ganon and Bongo Bongo.""" display_name = "Shuffle Boss Entrances" option_off = 0 option_limited = 1 @@ -178,19 +213,19 @@ class SpawnPositions(Choice): alias_true = 3 -class MixEntrancePools(Choice): - """Shuffles entrances into a mixed pool instead of separate ones. "indoor" keeps overworld entrances separate; "all" - mixes them in.""" - display_name = "Mix Entrance Pools" - option_off = 0 - option_indoor = 1 - option_all = 2 +# class MixEntrancePools(Choice): +# """Shuffles entrances into a mixed pool instead of separate ones. "indoor" keeps overworld entrances separate; "all" +# mixes them in.""" +# display_name = "Mix Entrance Pools" +# option_off = 0 +# option_indoor = 1 +# option_all = 2 -class DecoupleEntrances(Toggle): - """Decouple entrances when shuffling them. Also adds the one-way entrance from Gerudo Valley to Lake Hylia if - overworld is shuffled.""" - display_name = "Decouple Entrances" +# class DecoupleEntrances(Toggle): +# """Decouple entrances when shuffling them. Also adds the one-way entrance from Gerudo Valley to Lake Hylia if +# overworld is shuffled.""" +# display_name = "Decouple Entrances" class TriforceHunt(Toggle): @@ -216,13 +251,17 @@ class ExtraTriforces(Range): class LogicalChus(Toggle): - """Bombchus are properly considered in logic. The first found pack will have 20 chus; Kokiri Shop and Bazaar sell - refills; bombchus open Bombchu Bowling.""" + """Bombchus are properly considered in logic. + The first found pack will always have 20 chus. + Kokiri Shop and Bazaar will sell refills at reduced cost. + Bombchus open Bombchu Bowling.""" display_name = "Bombchus Considered in Logic" class DungeonShortcuts(Choice): - """Shortcuts to dungeon bosses are available without any requirements.""" + """Shortcuts to dungeon bosses are available without any requirements. + If enabled, this will impact the logic of dungeons where shortcuts are available. + Choice: Use the option "dungeon_shortcuts_list" to choose shortcuts.""" display_name = "Dungeon Boss Shortcuts Mode" option_off = 0 option_choice = 1 @@ -246,7 +285,11 @@ class DungeonShortcutsList(OptionSet): class MQDungeons(Choice): - """Choose between vanilla and Master Quest dungeon layouts.""" + """Choose between vanilla and Master Quest dungeon layouts. + Vanilla: All layouts are vanilla. + MQ: All layouts are Master Quest. + Specific: Use the option "mq_dungeons_list" to choose which dungeons are MQ. + Count: Use the option "mq_dungeons_count" to choose a number of random dungeons as MQ.""" display_name = "MQ Dungeon Mode" option_vanilla = 0 option_mq = 1 @@ -255,7 +298,7 @@ class MQDungeons(Choice): class MQDungeonList(OptionSet): - """Chosen dungeons to be MQ layout.""" + """With MQ dungeons as Specific: chosen dungeons to be MQ layout.""" display_name = "MQ Dungeon List" valid_keys = { "Deku Tree", @@ -274,41 +317,41 @@ class MQDungeonList(OptionSet): class MQDungeonCount(TrackRandomRange): - """Number of MQ dungeons, chosen randomly.""" + """With MQ dungeons as Count: number of randomly-selected dungeons to be MQ layout.""" display_name = "MQ Dungeon Count" range_start = 0 range_end = 12 default = 0 -class EmptyDungeons(Choice): - """Pre-completed dungeons are barren and rewards are given for free.""" - display_name = "Pre-completed Dungeons Mode" - option_none = 0 - option_specific = 1 - option_count = 2 +# class EmptyDungeons(Choice): +# """Pre-completed dungeons are barren and rewards are given for free.""" +# display_name = "Pre-completed Dungeons Mode" +# option_none = 0 +# option_specific = 1 +# option_count = 2 -class EmptyDungeonList(OptionSet): - """Chosen dungeons to be pre-completed.""" - display_name = "Pre-completed Dungeon List" - valid_keys = { - "Deku Tree", - "Dodongo's Cavern", - "Jabu Jabu's Belly", - "Forest Temple", - "Fire Temple", - "Water Temple", - "Shadow Temple", - "Spirit Temple", - } +# class EmptyDungeonList(OptionSet): +# """Chosen dungeons to be pre-completed.""" +# display_name = "Pre-completed Dungeon List" +# valid_keys = { +# "Deku Tree", +# "Dodongo's Cavern", +# "Jabu Jabu's Belly", +# "Forest Temple", +# "Fire Temple", +# "Water Temple", +# "Shadow Temple", +# "Spirit Temple", +# } -class EmptyDungeonCount(Range): - display_name = "Pre-completed Dungeon Count" - range_start = 1 - range_end = 8 - default = 2 +# class EmptyDungeonCount(Range): +# display_name = "Pre-completed Dungeon Count" +# range_start = 1 +# range_end = 8 +# default = 2 world_options: typing.Dict[str, type(Option)] = { @@ -341,59 +384,8 @@ world_options: typing.Dict[str, type(Option)] = { } -# class LacsCondition(Choice): -# """Set the requirements for the Light Arrow Cutscene in the Temple of Time.""" -# display_name = "Light Arrow Cutscene Requirement" -# option_vanilla = 0 -# option_stones = 1 -# option_medallions = 2 -# option_dungeons = 3 -# option_tokens = 4 - - -# class LacsStones(Range): -# """Set the number of Spiritual Stones required for LACS.""" -# display_name = "Spiritual Stones Required for LACS" -# range_start = 0 -# range_end = 3 -# default = 3 - - -# class LacsMedallions(Range): -# """Set the number of medallions required for LACS.""" -# display_name = "Medallions Required for LACS" -# range_start = 0 -# range_end = 6 -# default = 6 - - -# class LacsRewards(Range): -# """Set the number of dungeon rewards required for LACS.""" -# display_name = "Dungeon Rewards Required for LACS" -# range_start = 0 -# range_end = 9 -# default = 9 - - -# class LacsTokens(Range): -# """Set the number of Gold Skulltula Tokens required for LACS.""" -# display_name = "Tokens Required for LACS" -# range_start = 0 -# range_end = 100 -# default = 40 - - -# lacs_options: typing.Dict[str, type(Option)] = { -# "lacs_condition": LacsCondition, -# "lacs_stones": LacsStones, -# "lacs_medallions": LacsMedallions, -# "lacs_rewards": LacsRewards, -# "lacs_tokens": LacsTokens, -# } - - class BridgeStones(Range): - """Set the number of Spiritual Stones required for the rainbow bridge.""" + """With Stones bridge: set the number of Spiritual Stones required.""" display_name = "Spiritual Stones Required for Bridge" range_start = 0 range_end = 3 @@ -401,7 +393,7 @@ class BridgeStones(Range): class BridgeMedallions(Range): - """Set the number of medallions required for the rainbow bridge.""" + """With Medallions bridge: set the number of medallions required.""" display_name = "Medallions Required for Bridge" range_start = 0 range_end = 6 @@ -409,7 +401,7 @@ class BridgeMedallions(Range): class BridgeRewards(Range): - """Set the number of dungeon rewards required for the rainbow bridge.""" + """With Dungeons bridge: set the number of dungeon rewards required.""" display_name = "Dungeon Rewards Required for Bridge" range_start = 0 range_end = 9 @@ -417,7 +409,7 @@ class BridgeRewards(Range): class BridgeTokens(Range): - """Set the number of Gold Skulltula Tokens required for the rainbow bridge.""" + """With Tokens bridge: set the number of Gold Skulltula Tokens required.""" display_name = "Tokens Required for Bridge" range_start = 0 range_end = 100 @@ -425,7 +417,7 @@ class BridgeTokens(Range): class BridgeHearts(Range): - """Set the number of hearts required for the rainbow bridge.""" + """With Hearts bridge: set the number of hearts required.""" display_name = "Hearts Required for Bridge" range_start = 4 range_end = 20 @@ -442,7 +434,15 @@ bridge_options: typing.Dict[str, type(Option)] = { class SongShuffle(Choice): - """Set where songs can appear.""" + """Set where songs can appear. + Song: Songs are shuffled into other song locations. + Dungeon: Songs are placed into end-of-dungeon locations: + - The 8 boss heart containers + - Sheik in Ice Cavern + - Lens of Truth chest in Bottom of the Well + - Ice Arrows chest in Gerudo Training Ground + - Impa at Hyrule Castle + Any: Songs can appear anywhere in the multiworld.""" display_name = "Shuffle Songs" option_song = 0 option_dungeon = 1 @@ -450,8 +450,10 @@ class SongShuffle(Choice): class ShopShuffle(Choice): - """Randomizes shop contents. "fixed_number" randomizes a specific number of items per shop; - "random_number" randomizes the value for each shop. """ + """Randomizes shop contents. + Off: Shops are not randomized at all. + Fixed Number: Shop contents are shuffled, and a specific number of multiworld locations exist in each shop, controlled by the "shop_slots" option. + Random Number: Same as Fixed Number, but the number of locations per shop is random and may differ between shops.""" display_name = "Shopsanity" option_off = 0 option_fixed_number = 1 @@ -459,15 +461,20 @@ class ShopShuffle(Choice): class ShopSlots(Range): - """Number of items per shop to be randomized into the main itempool. - Only active if Shopsanity is set to "fixed_number." """ + """With Shopsanity fixed number: quantity of multiworld locations per shop to be randomized.""" display_name = "Shuffled Shop Slots" range_start = 0 range_end = 4 class ShopPrices(Choice): - """Controls prices of shop items. "Normal" is a distribution from 0 to 300. "X Wallet" requires that wallet at max. "Affordable" is always 10 rupees.""" + """Controls prices of shop locations. + Normal: Balanced distribution from 0 to 300. + Affordable: Every shop location costs 10 rupees. + Starting Wallet: Prices capped at 99 rupees. + Adult's Wallet: Prices capped at 200 rupees. + Giant's Wallet: Prices capped at 500 rupees. + Tycoon's Wallet: Prices capped at 999 rupees.""" display_name = "Shopsanity Prices" option_normal = 0 option_affordable = 1 @@ -478,7 +485,10 @@ class ShopPrices(Choice): class TokenShuffle(Choice): - """Token rewards from Gold Skulltulas are shuffled into the pool.""" + """Token rewards from Gold Skulltulas can be shuffled into the pool. + Dungeons: Only skulltulas in dungeons are shuffled. + Overworld: Only skulltulas on the overworld (all skulltulas not in dungeons) are shuffled. + All: Every skulltula is shuffled.""" display_name = "Tokensanity" option_off = 0 option_dungeons = 1 @@ -487,7 +497,11 @@ class TokenShuffle(Choice): class ScrubShuffle(Choice): - """Shuffle the items sold by Business Scrubs, and set the prices.""" + """Shuffle the items sold by Business Scrubs, and set the prices. + Off: Only the three business scrubs that sell one-time upgrades in vanilla will have items at their vanilla prices. + Low/"Affordable": All scrub prices are 10 rupees. + Regular/"Expensive": All scrub prices are vanilla. + Random Prices: All scrub prices are randomized between 0 and 99 rupees.""" display_name = "Scrub Shuffle" option_off = 0 option_low = 1 @@ -513,7 +527,11 @@ class ShuffleOcarinas(Toggle): class ShuffleChildTrade(Choice): - """Controls the behavior of the start of the child trade quest.""" + """Controls the behavior of the start of the child trade quest. + Vanilla: Malon will give you the Weird Egg at Hyrule Castle. + Shuffle: Malon will give you a random item, and the Weird Egg is shuffled. + Skip Child Zelda: The game starts with Zelda already met, Zelda's Letter obtained, and the item from Impa obtained. + """ display_name = "Shuffle Child Trade Item" option_vanilla = 0 option_shuffle = 1 @@ -538,30 +556,39 @@ class ShuffleMedigoronCarpet(Toggle): class ShuffleFreestanding(Choice): - """Shuffles freestanding rupees, recovery hearts, Shadow Temple Spinning Pots, and Goron Pot.""" + """Shuffles freestanding rupees, recovery hearts, Shadow Temple Spinning Pots, and Goron Pot drops. + Dungeons: Only freestanding items in dungeons are shuffled. + Overworld: Only freestanding items in the overworld are shuffled. + All: All freestanding items are shuffled.""" display_name = "Shuffle Rupees & Hearts" option_off = 0 - option_all = 1 + option_dungeons = 1 option_overworld = 2 - option_dungeons = 3 + option_all = 3 class ShufflePots(Choice): - """Shuffles pots and flying pots which normally contain an item.""" + """Shuffles pots and flying pots which normally contain an item. + Dungeons: Only pots in dungeons are shuffled. + Overworld: Only pots in the overworld are shuffled. + All: All pots are shuffled.""" display_name = "Shuffle Pots" option_off = 0 - option_all = 1 + option_dungeons = 1 option_overworld = 2 - option_dungeons = 3 + option_all = 3 class ShuffleCrates(Choice): - """Shuffles large and small crates containing an item.""" + """Shuffles large and small crates containing an item. + Dungeons: Only crates in dungeons are shuffled. + Overworld: Only crates in the overworld are shuffled. + All: All crates are shuffled.""" display_name = "Shuffle Crates" option_off = 0 - option_all = 1 + option_dungeons = 1 option_overworld = 2 - option_dungeons = 3 + option_all = 3 class ShuffleBeehives(Toggle): @@ -597,72 +624,113 @@ shuffle_options: typing.Dict[str, type(Option)] = { class ShuffleMapCompass(Choice): - """Control where to shuffle dungeon maps and compasses.""" + """Control where to shuffle dungeon maps and compasses. + Remove: There will be no maps or compasses in the itempool. + Startwith: You start with all maps and compasses. + Vanilla: Maps and compasses remain vanilla. + Dungeon: Maps and compasses are shuffled within their original dungeon. + Regional: Maps and compasses are shuffled only in regions near the original dungeon. + Overworld: Maps and compasses are shuffled locally outside of dungeons. + Any Dungeon: Maps and compasses are shuffled locally in any dungeon. + Keysanity: Maps and compasses can be anywhere in the multiworld.""" display_name = "Maps & Compasses" option_remove = 0 option_startwith = 1 option_vanilla = 2 option_dungeon = 3 - option_overworld = 4 - option_any_dungeon = 5 - option_keysanity = 6 - option_regional = 7 + option_regional = 4 + option_overworld = 5 + option_any_dungeon = 6 + option_keysanity = 7 default = 1 - alias_anywhere = 6 + alias_anywhere = 7 class ShuffleKeys(Choice): - """Control where to shuffle dungeon small keys.""" + """Control where to shuffle dungeon small keys. + Remove/"Keysy": There will be no small keys in the itempool. All small key doors are automatically unlocked. + Vanilla: Small keys remain vanilla. You may start with extra small keys in some dungeons to prevent softlocks. + Dungeon: Small keys are shuffled within their original dungeon. + Regional: Small keys are shuffled only in regions near the original dungeon. + Overworld: Small keys are shuffled locally outside of dungeons. + Any Dungeon: Small keys are shuffled locally in any dungeon. + Keysanity: Small keys can be anywhere in the multiworld.""" display_name = "Small Keys" option_remove = 0 option_vanilla = 2 option_dungeon = 3 - option_overworld = 4 - option_any_dungeon = 5 - option_keysanity = 6 - option_regional = 7 + option_regional = 4 + option_overworld = 5 + option_any_dungeon = 6 + option_keysanity = 7 default = 3 alias_keysy = 0 - alias_anywhere = 6 + alias_anywhere = 7 class ShuffleGerudoKeys(Choice): - """Control where to shuffle the Thieves' Hideout small keys.""" + """Control where to shuffle the Thieves' Hideout small keys. + Vanilla: Hideout keys remain vanilla. + Regional: Hideout keys are shuffled only in the Gerudo Valley/Desert Colossus area. + Overworld: Hideout keys are shuffled locally outside of dungeons. + Any Dungeon: Hideout keys are shuffled locally in any dungeon. + Keysanity: Hideout keys can be anywhere in the multiworld.""" display_name = "Thieves' Hideout Keys" option_vanilla = 0 - option_overworld = 1 - option_any_dungeon = 2 - option_keysanity = 3 - option_regional = 4 - alias_anywhere = 3 + option_regional = 1 + option_overworld = 2 + option_any_dungeon = 3 + option_keysanity = 4 + alias_anywhere = 4 class ShuffleBossKeys(Choice): - """Control where to shuffle boss keys, except the Ganon's Castle Boss Key.""" + """Control where to shuffle boss keys, except the Ganon's Castle Boss Key. + Remove/"Keysy": There will be no boss keys in the itempool. All boss key doors are automatically unlocked. + Vanilla: Boss keys remain vanilla. You may start with extra small keys in some dungeons to prevent softlocks. + Dungeon: Boss keys are shuffled within their original dungeon. + Regional: Boss keys are shuffled only in regions near the original dungeon. + Overworld: Boss keys are shuffled locally outside of dungeons. + Any Dungeon: Boss keys are shuffled locally in any dungeon. + Keysanity: Boss keys can be anywhere in the multiworld.""" display_name = "Boss Keys" option_remove = 0 option_vanilla = 2 option_dungeon = 3 - option_overworld = 4 - option_any_dungeon = 5 - option_keysanity = 6 - option_regional = 7 + option_regional = 4 + option_overworld = 5 + option_any_dungeon = 6 + option_keysanity = 7 default = 3 alias_keysy = 0 - alias_anywhere = 6 + alias_anywhere = 7 class ShuffleGanonBK(Choice): - """Control how to shuffle the Ganon's Castle Boss Key.""" + """Control how to shuffle the Ganon's Castle Boss Key (GCBK). + Remove: GCBK is removed, and the boss key door is automatically unlocked. + Vanilla: GCBK remains vanilla. + Dungeon: GCBK is shuffled within its original dungeon. + Regional: GCBK is shuffled only in Hyrule Field, Market, and Hyrule Castle areas. + Overworld: GCBK is shuffled locally outside of dungeons. + Any Dungeon: GCBK is shuffled locally in any dungeon. + Keysanity: GCBK can be anywhere in the multiworld. + On LACS: GCBK is on the Light Arrow Cutscene, which requires Shadow and Spirit Medallions. + Stones: GCBK will be awarded when reaching the target number of Spiritual Stones. + Medallions: GCBK will be awarded when reaching the target number of medallions. + Dungeons: GCBK will be awarded when reaching the target number of dungeon rewards. + Tokens: GCBK will be awarded when reaching the target number of Gold Skulltula Tokens. + Hearts: GCBK will be awarded when reaching the target number of hearts. + """ display_name = "Ganon's Boss Key" option_remove = 0 option_vanilla = 2 option_dungeon = 3 - option_overworld = 4 - option_any_dungeon = 5 - option_keysanity = 6 - option_on_lacs = 7 - option_regional = 8 + option_regional = 4 + option_overworld = 5 + option_any_dungeon = 6 + option_keysanity = 7 + option_on_lacs = 8 option_stones = 9 option_medallions = 10 option_dungeons = 11 @@ -670,7 +738,7 @@ class ShuffleGanonBK(Choice): option_hearts = 13 default = 0 alias_keysy = 0 - alias_anywhere = 6 + alias_anywhere = 7 class EnhanceMC(Toggle): @@ -679,7 +747,7 @@ class EnhanceMC(Toggle): class GanonBKMedallions(Range): - """Set how many medallions are required to receive Ganon BK.""" + """With medallions GCBK: set how many medallions are required to receive GCBK.""" display_name = "Medallions Required for Ganon's BK" range_start = 1 range_end = 6 @@ -687,7 +755,7 @@ class GanonBKMedallions(Range): class GanonBKStones(Range): - """Set how many Spiritual Stones are required to receive Ganon BK.""" + """With stones GCBK: set how many Spiritual Stones are required to receive GCBK.""" display_name = "Spiritual Stones Required for Ganon's BK" range_start = 1 range_end = 3 @@ -695,7 +763,7 @@ class GanonBKStones(Range): class GanonBKRewards(Range): - """Set how many dungeon rewards are required to receive Ganon BK.""" + """With dungeons GCBK: set how many dungeon rewards are required to receive GCBK.""" display_name = "Dungeon Rewards Required for Ganon's BK" range_start = 1 range_end = 9 @@ -703,7 +771,7 @@ class GanonBKRewards(Range): class GanonBKTokens(Range): - """Set how many Gold Skulltula Tokens are required to receive Ganon BK.""" + """With tokens GCBK: set how many Gold Skulltula Tokens are required to receive GCBK.""" display_name = "Tokens Required for Ganon's BK" range_start = 1 range_end = 100 @@ -711,7 +779,7 @@ class GanonBKTokens(Range): class GanonBKHearts(Range): - """Set how many hearts are required to receive Ganon BK.""" + """With hearts GCBK: set how many hearts are required to receive GCBK.""" display_name = "Hearts Required for Ganon's BK" range_start = 4 range_end = 20 @@ -719,7 +787,9 @@ class GanonBKHearts(Range): class KeyRings(Choice): - """Dungeons have all small keys found at once, rather than individually.""" + """A key ring grants all dungeon small keys at once, rather than individually. + Choose: Use the option "key_rings_list" to choose which dungeons have key rings. + All: All dungeons have key rings instead of small keys.""" display_name = "Key Rings Mode" option_off = 0 option_choose = 1 @@ -728,7 +798,7 @@ class KeyRings(Choice): class KeyRingList(OptionSet): - """Select areas with keyrings rather than individual small keys.""" + """With key rings as Choose: select areas with key rings rather than individual small keys.""" display_name = "Key Ring Areas" valid_keys = { "Thieves' Hideout", @@ -828,7 +898,8 @@ class BigPoeCount(Range): class FAETorchCount(Range): - """Number of lit torches required to open Shadow Temple.""" + """Number of lit torches required to open Shadow Temple. + Does not affect logic; use the trick Shadow Temple Entry with Fire Arrows if desired.""" display_name = "Fire Arrow Entry Torch Count" range_start = 1 range_end = 24 @@ -853,7 +924,11 @@ timesavers_options: typing.Dict[str, type(Option)] = { class CorrectChestAppearance(Choice): - """Changes chest textures and/or sizes to match their contents. "Classic" is the old behavior of CSMC.""" + """Changes chest textures and/or sizes to match their contents. + Off: All chests have their vanilla size/appearance. + Textures: Chest textures reflect their contents. + Both: Like Textures, but progression items and boss keys get big chests, and other items get small chests. + Classic: Old behavior of CSMC; textures distinguish keys from non-keys, and size distinguishes importance.""" display_name = "Chest Appearance Matches Contents" option_off = 0 option_textures = 1 @@ -872,15 +947,24 @@ class InvisibleChests(Toggle): class CorrectPotCrateAppearance(Choice): - """Unchecked pots and crates have a different texture; unchecked beehives will wiggle. With textures_content, pots and crates have an appearance based on their contents; with textures_unchecked, all unchecked pots/crates have the same appearance.""" + """Changes the appearance of pots, crates, and beehives that contain items. + Off: Vanilla appearance for all containers. + Textures (Content): Unchecked pots and crates have a texture reflecting their contents. Unchecked beehives with progression items will wiggle. + Textures (Unchecked): Unchecked pots and crates are golden. Unchecked beehives will wiggle. + """ display_name = "Pot, Crate, and Beehive Appearance" option_off = 0 option_textures_content = 1 option_textures_unchecked = 2 + default = 2 class Hints(Choice): - """Gossip Stones can give hints about item locations.""" + """Gossip Stones can give hints about item locations. + None: Gossip Stones do not give hints. + Mask: Gossip Stones give hints with Mask of Truth. + Agony: Gossip Stones give hints wtih Stone of Agony. + Always: Gossip Stones always give hints.""" display_name = "Gossip Stones" option_none = 0 option_mask = 1 @@ -895,7 +979,9 @@ class MiscHints(DefaultOnToggle): class HintDistribution(Choice): - """Choose the hint distribution to use. Affects the frequency of strong hints, which items are always hinted, etc.""" + """Choose the hint distribution to use. Affects the frequency of strong hints, which items are always hinted, etc. + Detailed documentation on hint distributions can be found on the Archipelago GitHub or OoTRandomizer.com. + The Async hint distribution is intended for async multiworlds. It removes Way of the Hero hints to improve generation times, since they are not very useful in asyncs.""" display_name = "Hint Distribution" option_balanced = 0 option_ddr = 1 @@ -907,10 +993,13 @@ class HintDistribution(Choice): option_useless = 7 option_very_strong = 8 option_async = 9 + default = 9 class TextShuffle(Choice): - """Randomizes text in the game for comedic effect.""" + """Randomizes text in the game for comedic effect. + Except Hints: does not randomize important text such as hints, small/boss key information, and item prices. + Complete: randomizes every textbox, including the useful ones.""" display_name = "Text Shuffle" option_none = 0 option_except_hints = 1 @@ -946,7 +1035,8 @@ class HeroMode(Toggle): class StartingToD(Choice): - """Change the starting time of day.""" + """Change the starting time of day. + Daytime starts at Sunrise and ends at Sunset. Default is between Morning and Noon.""" display_name = "Starting Time of Day" option_default = 0 option_sunrise = 1 @@ -999,7 +1089,11 @@ misc_options: typing.Dict[str, type(Option)] = { } class ItemPoolValue(Choice): - """Changes the number of items available in the game.""" + """Changes the number of items available in the game. + Plentiful: One extra copy of every major item. + Balanced: Original item pool. + Scarce: Extra copies of major items are removed. Heart containers are removed. + Minimal: All major item upgrades not used for locations are removed. All health is removed.""" display_name = "Item Pool" option_plentiful = 0 option_balanced = 1 @@ -1009,7 +1103,12 @@ class ItemPoolValue(Choice): class IceTraps(Choice): - """Adds ice traps to the item pool.""" + """Adds ice traps to the item pool. + Off: All ice traps are removed. + Normal: The vanilla quantity of ice traps are placed. + On/"Extra": There is a chance for some extra ice traps to be placed. + Mayhem: All added junk items are ice traps. + Onslaught: All junk items are replaced by ice traps, even those in the base pool.""" display_name = "Ice Traps" option_off = 0 option_normal = 1 @@ -1021,34 +1120,27 @@ class IceTraps(Choice): class IceTrapVisual(Choice): - """Changes the appearance of ice traps as freestanding items.""" - display_name = "Ice Trap Appearance" + """Changes the appearance of traps, including other games' traps, as freestanding items.""" + display_name = "Trap Appearance" option_major_only = 0 option_junk_only = 1 option_anything = 2 -class AdultTradeStart(OptionSet): - """Choose the items that can appear to start the adult trade sequence. By default it is Claim Check only.""" - display_name = "Adult Trade Sequence Items" - default = {"Claim Check"} - valid_keys = { - "Pocket Egg", - "Pocket Cucco", - "Cojiro", - "Odd Mushroom", - "Poachers Saw", - "Broken Sword", - "Prescription", - "Eyeball Frog", - "Eyedrops", - "Claim Check", - } - - def __init__(self, value: typing.Iterable[str]): - if not value: - value = self.default - super().__init__(value) +class AdultTradeStart(Choice): + """Choose the item that starts the adult trade sequence.""" + display_name = "Adult Trade Sequence Start" + option_pocket_egg = 0 + option_pocket_cucco = 1 + option_cojiro = 2 + option_odd_mushroom = 3 + option_poachers_saw = 4 + option_broken_sword = 5 + option_prescription = 6 + option_eyeball_frog = 7 + option_eyedrops = 8 + option_claim_check = 9 + default = 9 itempool_options: typing.Dict[str, type(Option)] = { @@ -1068,7 +1160,7 @@ class Targeting(Choice): class DisplayDpad(DefaultOnToggle): - """Show dpad icon on HUD for quick actions (ocarina, hover boots, iron boots).""" + """Show dpad icon on HUD for quick actions (ocarina, hover boots, iron boots, mask).""" display_name = "Display D-Pad HUD" @@ -1191,7 +1283,6 @@ oot_options: typing.Dict[str, type(Option)] = { **world_options, **bridge_options, **dungeon_items_options, - # **lacs_options, **shuffle_options, **timesavers_options, **misc_options, diff --git a/worlds/oot/Patches.py b/worlds/oot/Patches.py index ab1e75d1b9..f83b34183c 100644 --- a/worlds/oot/Patches.py +++ b/worlds/oot/Patches.py @@ -2094,10 +2094,14 @@ def patch_rom(world, rom): if not world.dungeon_mq['Ganons Castle']: chest_name = 'Ganons Castle Light Trial Lullaby Chest' location = world.get_location(chest_name) - if location.item.game == 'Ocarina of Time': - item = read_rom_item(rom, location.item.index) + if not location.item.trap: + if location.item.game == 'Ocarina of Time': + item = read_rom_item(rom, location.item.index) + else: + item = read_rom_item(rom, AP_PROGRESSION if location.item.advancement else AP_JUNK) else: - item = read_rom_item(rom, AP_PROGRESSION if location.item.advancement else AP_JUNK) + looks_like_index = get_override_entry(world, location)[5] + item = read_rom_item(rom, looks_like_index) if item['chest_type'] in (GOLD_CHEST, GILDED_CHEST, SKULL_CHEST_BIG): rom.write_int16(0x321B176, 0xFC40) # original 0xFC48 @@ -2106,10 +2110,14 @@ def patch_rom(world, rom): chest_name = 'Spirit Temple Compass Chest' chest_address = 0x2B6B07C location = world.get_location(chest_name) - if location.item.game == 'Ocarina of Time': - item = read_rom_item(rom, location.item.index) + if not location.item.trap: + if location.item.game == 'Ocarina of Time': + item = read_rom_item(rom, location.item.index) + else: + item = read_rom_item(rom, AP_PROGRESSION if location.item.advancement else AP_JUNK) else: - item = read_rom_item(rom, AP_PROGRESSION if location.item.advancement else AP_JUNK) + looks_like_index = get_override_entry(world, location)[5] + item = read_rom_item(rom, looks_like_index) if item['chest_type'] in (BROWN_CHEST, SILVER_CHEST, SKULL_CHEST_SMALL): rom.write_int16(chest_address + 2, 0x0190) # X pos rom.write_int16(chest_address + 6, 0xFABC) # Z pos @@ -2120,10 +2128,14 @@ def patch_rom(world, rom): chest_address_0 = 0x21A02D0 # Address in setup 0 chest_address_2 = 0x21A06E4 # Address in setup 2 location = world.get_location(chest_name) - if location.item.game == 'Ocarina of Time': - item = read_rom_item(rom, location.item.index) + if not location.item.trap: + if location.item.game == 'Ocarina of Time': + item = read_rom_item(rom, location.item.index) + else: + item = read_rom_item(rom, AP_PROGRESSION if location.item.advancement else AP_JUNK) else: - item = read_rom_item(rom, AP_PROGRESSION if location.item.advancement else AP_JUNK) + looks_like_index = get_override_entry(world, location)[5] + item = read_rom_item(rom, looks_like_index) if item['chest_type'] in (BROWN_CHEST, SILVER_CHEST, SKULL_CHEST_SMALL): rom.write_int16(chest_address_0 + 6, 0x0172) # Z pos rom.write_int16(chest_address_2 + 6, 0x0172) # Z pos diff --git a/worlds/oot/Rules.py b/worlds/oot/Rules.py index 1f44cebdcf..fa198e0ce1 100644 --- a/worlds/oot/Rules.py +++ b/worlds/oot/Rules.py @@ -223,9 +223,6 @@ def set_shop_rules(ootworld): # The goal is to automatically set item rules based on age requirements in case entrances were shuffled def set_entrances_based_rules(ootworld): - if ootworld.multiworld.accessibility == 'beatable': - return - all_state = ootworld.multiworld.get_all_state(False) for location in filter(lambda location: location.type == 'Shop', ootworld.get_locations()): diff --git a/worlds/oot/__init__.py b/worlds/oot/__init__.py index 539abd9674..6af19683f4 100644 --- a/worlds/oot/__init__.py +++ b/worlds/oot/__init__.py @@ -10,7 +10,7 @@ from string import printable logger = logging.getLogger("Ocarina of Time") -from .Location import OOTLocation, LocationFactory, location_name_to_id +from .Location import OOTLocation, LocationFactory, location_name_to_id, build_location_name_groups from .Entrance import OOTEntrance from .EntranceShuffle import shuffle_random_entrances, entrance_shuffle_table, EntranceShuffleError from .HintList import getRequiredHints @@ -163,11 +163,13 @@ class OOTWorld(World): "Bottle with Big Poe", "Bottle with Red Potion", "Bottle with Green Potion", "Bottle with Blue Potion", "Bottle with Fairy", "Bottle with Fish", "Bottle with Blue Fire", "Bottle with Bugs", "Bottle with Poe"}, - "Adult Trade Item": {"Pocket Egg", "Pocket Cucco", "Odd Mushroom", + "Adult Trade Item": {"Pocket Egg", "Pocket Cucco", "Cojiro", "Odd Mushroom", "Odd Potion", "Poachers Saw", "Broken Sword", "Prescription", - "Eyeball Frog", "Eyedrops", "Claim Check"} + "Eyeball Frog", "Eyedrops", "Claim Check"}, } + location_name_groups = build_location_name_groups() + def __init__(self, world, player): self.hint_data_available = threading.Event() self.collectible_flags_available = threading.Event() @@ -384,6 +386,7 @@ class OOTWorld(World): self.mq_dungeons_mode = 'count' self.mq_dungeons_count = 0 self.dungeon_mq = {item['name']: (item['name'] in mq_dungeons) for item in dungeon_table} + self.dungeon_mq['Thieves Hideout'] = False # fix for bug in SaveContext:287 # Empty dungeon placeholder for the moment self.empty_dungeons = {name: False for name in self.dungeon_mq} @@ -409,6 +412,9 @@ class OOTWorld(World): self.starting_tod = self.starting_tod.replace('_', '-') self.shuffle_scrubs = self.shuffle_scrubs.replace('_prices', '') + # Convert adult trade option to expected Set + self.adult_trade_start = {self.adult_trade_start.title().replace('_', ' ')} + # Get hint distribution self.hint_dist_user = read_json(data_path('Hints', f'{self.hint_dist}.json')) @@ -446,7 +452,7 @@ class OOTWorld(World): self.always_hints = [hint.name for hint in getRequiredHints(self)] # Determine items which are not considered advancement based on settings. They will never be excluded. - self.nonadvancement_items = {'Double Defense'} + self.nonadvancement_items = {'Double Defense', 'Deku Stick Capacity', 'Deku Nut Capacity'} if (self.damage_multiplier != 'ohko' and self.damage_multiplier != 'quadruple' and self.shuffle_scrubs == 'off' and not self.shuffle_grotto_entrances): # nayru's love may be required to prevent forced damage @@ -633,16 +639,18 @@ class OOTWorld(World): self.multiworld.itempool.remove(item) self.hinted_dungeon_reward_locations[item.name] = loc - def create_item(self, name: str): + def create_item(self, name: str, allow_arbitrary_name: bool = False): if name in item_table: return OOTItem(name, self.player, item_table[name], False, (name in self.nonadvancement_items if getattr(self, 'nonadvancement_items', None) else False)) - return OOTItem(name, self.player, ('Event', True, None, None), True, False) + if allow_arbitrary_name: + return OOTItem(name, self.player, ('Event', True, None, None), True, False) + raise Exception(f"Invalid item name: {name}") def make_event_item(self, name, location, item=None): if item is None: - item = self.create_item(name) + item = self.create_item(name, allow_arbitrary_name=True) self.multiworld.push_item(location, item, collect=False) location.locked = True location.event = True @@ -800,23 +808,25 @@ class OOTWorld(World): self.multiworld.itempool.remove(item) self.multiworld.random.shuffle(locations) fill_restrictive(self.multiworld, self.multiworld.get_all_state(False), locations, stage_items, - single_player_placement=True, lock=True) + single_player_placement=True, lock=True, allow_excluded=True) else: for dungeon_info in dungeon_table: dungeon_name = dungeon_info['name'] locations = gather_locations(self.multiworld, fill_stage, self.player, dungeon=dungeon_name) if isinstance(locations, list): dungeon_items = list(filter(lambda item: dungeon_name in item.name, stage_items)) + if not dungeon_items: + continue for item in dungeon_items: self.multiworld.itempool.remove(item) self.multiworld.random.shuffle(locations) fill_restrictive(self.multiworld, self.multiworld.get_all_state(False), locations, dungeon_items, - single_player_placement=True, lock=True) + single_player_placement=True, lock=True, allow_excluded=True) # Place songs # 5 built-in retries because this section can fail sometimes if self.shuffle_song_items != 'any': - tries = 5 + tries = 10 if self.shuffle_song_items == 'song': song_locations = list(filter(lambda location: location.type == 'Song', self.multiworld.get_unfilled_locations(player=self.player))) @@ -852,7 +862,7 @@ class OOTWorld(World): try: self.multiworld.random.shuffle(song_locations) fill_restrictive(self.multiworld, self.multiworld.get_all_state(False), song_locations[:], songs[:], - True, True) + single_player_placement=True, lock=True, allow_excluded=True) logger.debug(f"Successfully placed songs for player {self.player} after {6 - tries} attempt(s)") except FillError as e: tries -= 1 @@ -888,7 +898,8 @@ class OOTWorld(World): self.multiworld.random.shuffle(shop_locations) for item in shop_prog + shop_junk: self.multiworld.itempool.remove(item) - fill_restrictive(self.multiworld, self.multiworld.get_all_state(False), shop_locations, shop_prog, True, True) + fill_restrictive(self.multiworld, self.multiworld.get_all_state(False), shop_locations, shop_prog, + single_player_placement=True, lock=True, allow_excluded=True) fast_fill(self.multiworld, shop_junk, shop_locations) for loc in shop_locations: loc.locked = True @@ -963,7 +974,7 @@ class OOTWorld(World): multiworld.itempool.remove(item) multiworld.random.shuffle(locations) fill_restrictive(multiworld, multiworld.get_all_state(False), locations, group_stage_items, - single_player_placement=False, lock=True) + single_player_placement=False, lock=True, allow_excluded=True) if fill_stage == 'Song': # We don't want song locations to contain progression unless it's a song # or it was marked as priority. @@ -984,7 +995,7 @@ class OOTWorld(World): multiworld.itempool.remove(item) multiworld.random.shuffle(locations) fill_restrictive(multiworld, multiworld.get_all_state(False), locations, group_dungeon_items, - single_player_placement=False, lock=True) + single_player_placement=False, lock=True, allow_excluded=True) def generate_output(self, output_directory: str): if self.hints != 'none': @@ -1051,7 +1062,10 @@ class OOTWorld(World): def stage_generate_output(cls, multiworld: MultiWorld, output_directory: str): def hint_type_players(hint_type: str) -> set: return {autoworld.player for autoworld in multiworld.get_game_worlds("Ocarina of Time") - if autoworld.hints != 'none' and autoworld.hint_dist_user['distribution'][hint_type]['copies'] > 0} + if autoworld.hints != 'none' + and autoworld.hint_dist_user['distribution'][hint_type]['copies'] > 0 + and (autoworld.hint_dist_user['distribution'][hint_type]['fixed'] > 0 + or autoworld.hint_dist_user['distribution'][hint_type]['weight'] > 0)} try: item_hint_players = hint_type_players('item') @@ -1078,10 +1092,10 @@ class OOTWorld(World): if loc.game == "Ocarina of Time" and loc.item.code and (not loc.locked or (oot_is_item_of_type(loc.item, 'Song') or - (oot_is_item_of_type(loc.item, 'SmallKey') and multiworld.worlds[loc.player].shuffle_smallkeys == 'any_dungeon') or - (oot_is_item_of_type(loc.item, 'HideoutSmallKey') and multiworld.worlds[loc.player].shuffle_hideoutkeys == 'any_dungeon') or - (oot_is_item_of_type(loc.item, 'BossKey') and multiworld.worlds[loc.player].shuffle_bosskeys == 'any_dungeon') or - (oot_is_item_of_type(loc.item, 'GanonBossKey') and multiworld.worlds[loc.player].shuffle_ganon_bosskey == 'any_dungeon'))): + (oot_is_item_of_type(loc.item, 'SmallKey') and multiworld.worlds[loc.player].shuffle_smallkeys in ('overworld', 'any_dungeon', 'regional')) or + (oot_is_item_of_type(loc.item, 'HideoutSmallKey') and multiworld.worlds[loc.player].shuffle_hideoutkeys in ('overworld', 'any_dungeon', 'regional')) or + (oot_is_item_of_type(loc.item, 'BossKey') and multiworld.worlds[loc.player].shuffle_bosskeys in ('overworld', 'any_dungeon', 'regional')) or + (oot_is_item_of_type(loc.item, 'GanonBossKey') and multiworld.worlds[loc.player].shuffle_ganon_bosskey in ('overworld', 'any_dungeon', 'regional')))): if loc.player in barren_hint_players: hint_area = get_hint_area(loc) items_by_region[loc.player][hint_area]['weight'] += 1 @@ -1096,7 +1110,12 @@ class OOTWorld(World): elif barren_hint_players or woth_hint_players: # Check only relevant oot locations for barren/woth for player in (barren_hint_players | woth_hint_players): for loc in multiworld.worlds[player].get_locations(): - if loc.item.code and (not loc.locked or oot_is_item_of_type(loc.item, 'Song')): + if loc.item.code and (not loc.locked or + (oot_is_item_of_type(loc.item, 'Song') or + (oot_is_item_of_type(loc.item, 'SmallKey') and multiworld.worlds[loc.player].shuffle_smallkeys in ('overworld', 'any_dungeon', 'regional')) or + (oot_is_item_of_type(loc.item, 'HideoutSmallKey') and multiworld.worlds[loc.player].shuffle_hideoutkeys in ('overworld', 'any_dungeon', 'regional')) or + (oot_is_item_of_type(loc.item, 'BossKey') and multiworld.worlds[loc.player].shuffle_bosskeys in ('overworld', 'any_dungeon', 'regional')) or + (oot_is_item_of_type(loc.item, 'GanonBossKey') and multiworld.worlds[loc.player].shuffle_ganon_bosskey in ('overworld', 'any_dungeon', 'regional')))): if player in barren_hint_players: hint_area = get_hint_area(loc) items_by_region[player][hint_area]['weight'] += 1 @@ -1183,6 +1202,15 @@ class OOTWorld(World): er_hint_data[self.player][location.address] = main_entrance.name logger.debug(f"Set {location.name} hint data to {main_entrance.name}") + def write_spoiler(self, spoiler_handle: typing.TextIO) -> None: + required_trials_str = ", ".join(t for t in self.skipped_trials if not self.skipped_trials[t]) + spoiler_handle.write(f"\n\nTrials ({self.multiworld.get_player_name(self.player)}): {required_trials_str}\n") + + if self.shopsanity != 'off': + spoiler_handle.write(f"\nShop Prices ({self.multiworld.get_player_name(self.player)}):\n") + for k, v in self.shop_prices.items(): + spoiler_handle.write(f"{k}: {v} Rupees\n") + # Key ring handling: # Key rings are multiple items glued together into one, so we need to give # the appropriate number of keys in the collection state when they are @@ -1265,25 +1293,13 @@ class OOTWorld(World): # Specifically ensures that only real items are gotten, not any events. # In particular, ensures that Time Travel needs to be found. def get_state_with_complete_itempool(self): - all_state = self.multiworld.get_all_state(use_cache=False) - # Remove event progression items - for item, player in all_state.prog_items: - if player == self.player and (item not in item_table or item_table[item][2] is None): - all_state.prog_items[(item, player)] = 0 - # Remove all events and checked locations - all_state.locations_checked = {loc for loc in all_state.locations_checked if loc.player != self.player} - all_state.events = {loc for loc in all_state.events if loc.player != self.player} + all_state = CollectionState(self.multiworld) + for item in self.multiworld.itempool: + if item.player == self.player: + self.multiworld.worlds[item.player].collect(all_state, item) # If free_scarecrow give Scarecrow Song if self.free_scarecrow: all_state.collect(self.create_item("Scarecrow Song"), event=True) - - # Invalidate caches - all_state.child_reachable_regions[self.player] = set() - all_state.adult_reachable_regions[self.player] = set() - all_state.child_blocked_connections[self.player] = set() - all_state.adult_blocked_connections[self.player] = set() - all_state.day_reachable_regions[self.player] = set() - all_state.dampe_reachable_regions[self.player] = set() all_state.stale[self.player] = True return all_state @@ -1349,7 +1365,7 @@ def gather_locations(multiworld: MultiWorld, condition = lambda location: location.name in dungeon_song_locations locations += filter(condition, multiworld.get_unfilled_locations(player=player)) else: - if any(map(lambda v: v in {'keysanity'}, fill_opts.values())): + if any(map(lambda v: v == 'keysanity', fill_opts.values())): return None for player, option in fill_opts.items(): condition = functools.partial(valid_dungeon_item_location, From 195cf60e8ab2bc3674850044a85def0a416a111e Mon Sep 17 00:00:00 2001 From: Alchav <59858495+Alchav@users.noreply.github.com> Date: Mon, 23 Oct 2023 13:28:16 -0400 Subject: [PATCH 140/144] =?UTF-8?q?Pok=C3=A9mon=20R/B:=20Door=20Shuffle=20?= =?UTF-8?q?efficiency=20improvement=20and=20crash=20fix=20(#2347)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sweep only current player's locations so that more players does not slow it down. Fix a slight possibility of Full door shuffle crash by only sorting for outdoor dead ends only when connecting from a non-dead end. --- worlds/pokemon_rb/regions.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/worlds/pokemon_rb/regions.py b/worlds/pokemon_rb/regions.py index 431b23f49a..1816d010c0 100644 --- a/worlds/pokemon_rb/regions.py +++ b/worlds/pokemon_rb/regions.py @@ -2267,9 +2267,12 @@ def create_regions(self): "Defeat Viridian Gym Giovanni", ] + event_locations = self.multiworld.get_filled_locations(player) + def adds_reachable_entrances(entrances_copy, item): state_copy = state.copy() - state_copy.collect(item, False) + state_copy.collect(item, True) + state.sweep_for_events(locations=event_locations) ret = len([entrance for entrance in entrances_copy if entrance in reachable_entrances or entrance.parent_region.can_reach(state_copy)]) > len(reachable_entrances) return ret @@ -2305,7 +2308,6 @@ def create_regions(self): starting_entrances = len(entrances) dc_connected = [] - event_locations = self.multiworld.get_filled_locations(player) rock_tunnel_entrances = [entrance for entrance in entrances if "Rock Tunnel" in entrance.name] entrances = [entrance for entrance in entrances if entrance not in rock_tunnel_entrances] while entrances: @@ -2330,10 +2332,6 @@ def create_regions(self): if multiworld.door_shuffle[player] == "full" or len(entrances) != len(reachable_entrances): entrances.sort(key=lambda e: e.name not in entrance_only) - if len(entrances) < 48 and multiworld.door_shuffle[player] == "full": - # Prevent a situation where the only remaining outdoor entrances are ones that cannot be reached - # except by connecting directly to it. - entrances.sort(key=lambda e: e.name in unreachable_outdoor_entrances) # entrances list is empty while it's being sorted, must pass a copy to iterate through entrances_copy = entrances.copy() if multiworld.door_shuffle[player] == "decoupled": @@ -2350,6 +2348,11 @@ def create_regions(self): dead_end(entrances_copy, e) else 2) if multiworld.door_shuffle[player] == "full": outdoor = outdoor_map(entrances[0].parent_region.name) + if len(entrances) < 48 and not outdoor: + # Prevent a situation where the only remaining outdoor entrances are ones that cannot be reached + # except by connecting directly to it. + entrances.sort(key=lambda e: e.name in unreachable_outdoor_entrances) + entrances.sort(key=lambda e: outdoor_map(e.parent_region.name) != outdoor) assert entrances[0] in reachable_entrances, \ "Ran out of valid reachable entrances in Pokemon Red and Blue door shuffle" From e394c316f5760e8e70bc642b6dbbd693fdf9e5e2 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Mon, 23 Oct 2023 22:07:24 +0200 Subject: [PATCH 141/144] Setup: new setup experience (read: torch almost all of it) (#2268) --- inno_setup.iss | 871 +++++-------------------------------------------- 1 file changed, 79 insertions(+), 792 deletions(-) diff --git a/inno_setup.iss b/inno_setup.iss index 3c1bdc4571..b6f40f7701 100644 --- a/inno_setup.iss +++ b/inno_setup.iss @@ -46,151 +46,33 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{ [Types] Name: "full"; Description: "Full installation" -Name: "hosting"; Description: "Installation for hosting purposes" -Name: "playing"; Description: "Installation for playing purposes" +Name: "minimal"; Description: "Minimal installation" Name: "custom"; Description: "Custom installation"; Flags: iscustom [Components] -Name: "core"; Description: "Core Files"; Types: full hosting playing custom; Flags: fixed -Name: "generator"; Description: "Generator"; Types: full hosting -Name: "generator/sm"; Description: "Super Metroid ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 3145728; Flags: disablenouninstallwarning -Name: "generator/dkc3"; Description: "Donkey Kong Country 3 ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 3145728; Flags: disablenouninstallwarning -Name: "generator/smw"; Description: "Super Mario World ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 3145728; Flags: disablenouninstallwarning -Name: "generator/soe"; Description: "Secret of Evermore ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 3145728; Flags: disablenouninstallwarning -Name: "generator/l2ac"; Description: "Lufia II Ancient Cave ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 2621440; Flags: disablenouninstallwarning -Name: "generator/lttp"; Description: "A Link to the Past ROM Setup and Enemizer"; Types: full hosting; ExtraDiskSpaceRequired: 5191680 -Name: "generator/oot"; Description: "Ocarina of Time ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 100663296; Flags: disablenouninstallwarning -Name: "generator/zl"; Description: "Zillion ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 150000; Flags: disablenouninstallwarning -Name: "generator/pkmn_r"; Description: "Pokemon Red ROM Setup"; Types: full hosting -Name: "generator/pkmn_b"; Description: "Pokemon Blue ROM Setup"; Types: full hosting -Name: "generator/mmbn3"; Description: "MegaMan Battle Network 3"; Types: full hosting; ExtraDiskSpaceRequired: 8388608; Flags: disablenouninstallwarning -Name: "generator/ladx"; Description: "Link's Awakening DX ROM Setup"; Types: full hosting -Name: "generator/tloz"; Description: "The Legend of Zelda ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 135168; Flags: disablenouninstallwarning -Name: "server"; Description: "Server"; Types: full hosting -Name: "client"; Description: "Clients"; Types: full playing -Name: "client/sni"; Description: "SNI Client"; Types: full playing -Name: "client/sni/lttp"; Description: "SNI Client - A Link to the Past Patch Setup"; Types: full playing; Flags: disablenouninstallwarning -Name: "client/sni/sm"; Description: "SNI Client - Super Metroid Patch Setup"; Types: full playing; Flags: disablenouninstallwarning -Name: "client/sni/dkc3"; Description: "SNI Client - Donkey Kong Country 3 Patch Setup"; Types: full playing; Flags: disablenouninstallwarning -Name: "client/sni/smw"; Description: "SNI Client - Super Mario World Patch Setup"; Types: full playing; Flags: disablenouninstallwarning -Name: "client/sni/l2ac"; Description: "SNI Client - Lufia II Ancient Cave Patch Setup"; Types: full playing; Flags: disablenouninstallwarning -Name: "client/bizhawk"; Description: "BizHawk Client"; Types: full playing -Name: "client/factorio"; Description: "Factorio"; Types: full playing -Name: "client/kh2"; Description: "Kingdom Hearts 2"; Types: full playing -Name: "client/minecraft"; Description: "Minecraft"; Types: full playing; ExtraDiskSpaceRequired: 226894278 -Name: "client/oot"; Description: "Ocarina of Time"; Types: full playing -Name: "client/ff1"; Description: "Final Fantasy 1"; Types: full playing -Name: "client/pkmn"; Description: "Pokemon Client" -Name: "client/pkmn/red"; Description: "Pokemon Client - Pokemon Red Setup"; Types: full playing; ExtraDiskSpaceRequired: 1048576 -Name: "client/pkmn/blue"; Description: "Pokemon Client - Pokemon Blue Setup"; Types: full playing; ExtraDiskSpaceRequired: 1048576 -Name: "client/mmbn3"; Description: "MegaMan Battle Network 3 Client"; Types: full playing; -Name: "client/ladx"; Description: "Link's Awakening Client"; Types: full playing; ExtraDiskSpaceRequired: 1048576 -Name: "client/cf"; Description: "ChecksFinder"; Types: full playing -Name: "client/sc2"; Description: "Starcraft 2"; Types: full playing -Name: "client/wargroove"; Description: "Wargroove"; Types: full playing -Name: "client/zl"; Description: "Zillion"; Types: full playing -Name: "client/tloz"; Description: "The Legend of Zelda"; Types: full playing -Name: "client/advn"; Description: "Adventure"; Types: full playing -Name: "client/ut"; Description: "Undertale"; Types: full playing -Name: "client/text"; Description: "Text, to !command and chat"; Types: full playing +Name: "core"; Description: "Archipelago"; Types: full minimal custom; Flags: fixed +Name: "lttp_sprites"; Description: "Download ""A Link to the Past"" player sprites"; Types: full; [Dirs] NAME: "{app}"; Flags: setntfscompression; Permissions: everyone-modify users-modify authusers-modify; [Files] -Source: "{code:GetROMPath}"; DestDir: "{app}"; DestName: "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"; Flags: external; Components: client/sni/lttp or generator/lttp -Source: "{code:GetSMROMPath}"; DestDir: "{app}"; DestName: "Super Metroid (JU).sfc"; Flags: external; Components: client/sni/sm or generator/sm -Source: "{code:GetDKC3ROMPath}"; DestDir: "{app}"; DestName: "Donkey Kong Country 3 - Dixie Kong's Double Trouble! (USA) (En,Fr).sfc"; Flags: external; Components: client/sni/dkc3 or generator/dkc3 -Source: "{code:GetSMWROMPath}"; DestDir: "{app}"; DestName: "Super Mario World (USA).sfc"; Flags: external; Components: client/sni/smw or generator/smw -Source: "{code:GetSoEROMPath}"; DestDir: "{app}"; DestName: "Secret of Evermore (USA).sfc"; Flags: external; Components: generator/soe -Source: "{code:GetL2ACROMPath}"; DestDir: "{app}"; DestName: "Lufia II - Rise of the Sinistrals (USA).sfc"; Flags: external; Components: generator/l2ac -Source: "{code:GetOoTROMPath}"; DestDir: "{app}"; DestName: "The Legend of Zelda - Ocarina of Time.z64"; Flags: external; Components: client/oot or generator/oot -Source: "{code:GetZlROMPath}"; DestDir: "{app}"; DestName: "Zillion (UE) [!].sms"; Flags: external; Components: client/zl or generator/zl -Source: "{code:GetRedROMPath}"; DestDir: "{app}"; DestName: "Pokemon Red (UE) [S][!].gb"; Flags: external; Components: client/pkmn/red or generator/pkmn_r -Source: "{code:GetBlueROMPath}"; DestDir: "{app}"; DestName: "Pokemon Blue (UE) [S][!].gb"; Flags: external; Components: client/pkmn/blue or generator/pkmn_b -Source: "{code:GetBN3ROMPath}"; DestDir: "{app}"; DestName: "Mega Man Battle Network 3 - Blue Version (USA).gba"; Flags: external; Components: client/mmbn3 -Source: "{code:GetLADXROMPath}"; DestDir: "{app}"; DestName: "Legend of Zelda, The - Link's Awakening DX (USA, Europe) (SGB Enhanced).gbc"; Flags: external; Components: client/ladx or generator/ladx -Source: "{code:GetTLoZROMPath}"; DestDir: "{app}"; DestName: "Legend of Zelda, The (U) (PRG0) [!].nes"; Flags: external; Components: client/tloz or generator/tloz -Source: "{code:GetAdvnROMPath}"; DestDir: "{app}"; DestName: "ADVNTURE.BIN"; Flags: external; Components: client/advn -Source: "{#source_path}\*"; Excludes: "*.sfc, *.log, data\sprites\alttpr, SNI, EnemizerCLI, Archipelago*.exe"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs -Source: "{#source_path}\SNI\*"; Excludes: "*.sfc, *.log"; DestDir: "{app}\SNI"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: client/sni -Source: "{#source_path}\EnemizerCLI\*"; Excludes: "*.sfc, *.log"; DestDir: "{app}\EnemizerCLI"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: generator/lttp - -Source: "{#source_path}\ArchipelagoLauncher.exe"; DestDir: "{app}"; Flags: ignoreversion; -Source: "{#source_path}\ArchipelagoLauncher(DEBUG).exe"; DestDir: "{app}"; Flags: ignoreversion; -Source: "{#source_path}\ArchipelagoGenerate.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: generator -Source: "{#source_path}\ArchipelagoServer.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: server -Source: "{#source_path}\ArchipelagoFactorioClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/factorio -Source: "{#source_path}\ArchipelagoTextClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/text -Source: "{#source_path}\ArchipelagoSNIClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/sni -Source: "{#source_path}\ArchipelagoBizHawkClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/bizhawk -Source: "{#source_path}\ArchipelagoLinksAwakeningClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/ladx -Source: "{#source_path}\ArchipelagoLttPAdjuster.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/sni/lttp or generator/lttp -Source: "{#source_path}\ArchipelagoMinecraftClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/minecraft -Source: "{#source_path}\ArchipelagoOoTClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/oot -Source: "{#source_path}\ArchipelagoOoTAdjuster.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/oot -Source: "{#source_path}\ArchipelagoZillionClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/zl -Source: "{#source_path}\ArchipelagoFF1Client.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/ff1 -Source: "{#source_path}\ArchipelagoPokemonClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/pkmn -Source: "{#source_path}\ArchipelagoChecksFinderClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/cf -Source: "{#source_path}\ArchipelagoStarcraft2Client.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/sc2 -Source: "{#source_path}\ArchipelagoMMBN3Client.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/mmbn3 -Source: "{#source_path}\ArchipelagoZelda1Client.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/tloz -Source: "{#source_path}\ArchipelagoWargrooveClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/wargroove -Source: "{#source_path}\ArchipelagoKH2Client.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/kh2 -Source: "{#source_path}\ArchipelagoAdventureClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/advn -Source: "{#source_path}\ArchipelagoUndertaleClient.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: client/ut +Source: "{#source_path}\*"; Excludes: "*.sfc, *.log, data\sprites\alttpr, SNI, EnemizerCLI"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs +Source: "{#source_path}\SNI\*"; Excludes: "*.sfc, *.log"; DestDir: "{app}\SNI"; Flags: ignoreversion recursesubdirs createallsubdirs; +Source: "{#source_path}\EnemizerCLI\*"; Excludes: "*.sfc, *.log"; DestDir: "{app}\EnemizerCLI"; Flags: ignoreversion recursesubdirs createallsubdirs; Source: "vc_redist.x64.exe"; DestDir: {tmp}; Flags: deleteafterinstall [Icons] Name: "{group}\{#MyAppName} Folder"; Filename: "{app}"; Name: "{group}\{#MyAppName} Launcher"; Filename: "{app}\ArchipelagoLauncher.exe" -Name: "{group}\{#MyAppName} Server"; Filename: "{app}\ArchipelagoServer"; Components: server -Name: "{group}\{#MyAppName} Text Client"; Filename: "{app}\ArchipelagoTextClient.exe"; Components: client/text -Name: "{group}\{#MyAppName} SNI Client"; Filename: "{app}\ArchipelagoSNIClient.exe"; Components: client/sni -Name: "{group}\{#MyAppName} BizHawk Client"; Filename: "{app}\ArchipelagoBizHawkClient.exe"; Components: client/bizhawk -Name: "{group}\{#MyAppName} Factorio Client"; Filename: "{app}\ArchipelagoFactorioClient.exe"; Components: client/factorio -Name: "{group}\{#MyAppName} Minecraft Client"; Filename: "{app}\ArchipelagoMinecraftClient.exe"; Components: client/minecraft -Name: "{group}\{#MyAppName} Ocarina of Time Client"; Filename: "{app}\ArchipelagoOoTClient.exe"; Components: client/oot -Name: "{group}\{#MyAppName} Zillion Client"; Filename: "{app}\ArchipelagoZillionClient.exe"; Components: client/zl -Name: "{group}\{#MyAppName} Final Fantasy 1 Client"; Filename: "{app}\ArchipelagoFF1Client.exe"; Components: client/ff1 -Name: "{group}\{#MyAppName} Pokemon Client"; Filename: "{app}\ArchipelagoPokemonClient.exe"; Components: client/pkmn -Name: "{group}\{#MyAppName} ChecksFinder Client"; Filename: "{app}\ArchipelagoChecksFinderClient.exe"; Components: client/cf -Name: "{group}\{#MyAppName} Starcraft 2 Client"; Filename: "{app}\ArchipelagoStarcraft2Client.exe"; Components: client/sc2 -Name: "{group}\{#MyAppName} MegaMan Battle Network 3 Client"; Filename: "{app}\ArchipelagoMMBN3Client.exe"; Components: client/mmbn3 -Name: "{group}\{#MyAppName} The Legend of Zelda Client"; Filename: "{app}\ArchipelagoZelda1Client.exe"; Components: client/tloz -Name: "{group}\{#MyAppName} Kingdom Hearts 2 Client"; Filename: "{app}\ArchipelagoKH2Client.exe"; Components: client/kh2 -Name: "{group}\{#MyAppName} Link's Awakening Client"; Filename: "{app}\ArchipelagoLinksAwakeningClient.exe"; Components: client/ladx -Name: "{group}\{#MyAppName} Adventure Client"; Filename: "{app}\ArchipelagoAdventureClient.exe"; Components: client/advn -Name: "{group}\{#MyAppName} Wargroove Client"; Filename: "{app}\ArchipelagoWargrooveClient.exe"; Components: client/wargroove -Name: "{group}\{#MyAppName} Undertale Client"; Filename: "{app}\ArchipelagoUndertaleClient.exe"; Components: client/ut Name: "{commondesktop}\{#MyAppName} Folder"; Filename: "{app}"; Tasks: desktopicon Name: "{commondesktop}\{#MyAppName} Launcher"; Filename: "{app}\ArchipelagoLauncher.exe"; Tasks: desktopicon -Name: "{commondesktop}\{#MyAppName} Server"; Filename: "{app}\ArchipelagoServer"; Tasks: desktopicon; Components: server -Name: "{commondesktop}\{#MyAppName} SNI Client"; Filename: "{app}\ArchipelagoSNIClient.exe"; Tasks: desktopicon; Components: client/sni -Name: "{commondesktop}\{#MyAppName} BizHawk Client"; Filename: "{app}\ArchipelagoBizHawkClient.exe"; Tasks: desktopicon; Components: client/bizhawk -Name: "{commondesktop}\{#MyAppName} Factorio Client"; Filename: "{app}\ArchipelagoFactorioClient.exe"; Tasks: desktopicon; Components: client/factorio -Name: "{commondesktop}\{#MyAppName} Minecraft Client"; Filename: "{app}\ArchipelagoMinecraftClient.exe"; Tasks: desktopicon; Components: client/minecraft -Name: "{commondesktop}\{#MyAppName} Ocarina of Time Client"; Filename: "{app}\ArchipelagoOoTClient.exe"; Tasks: desktopicon; Components: client/oot -Name: "{commondesktop}\{#MyAppName} Zillion Client"; Filename: "{app}\ArchipelagoZillionClient.exe"; Tasks: desktopicon; Components: client/zl -Name: "{commondesktop}\{#MyAppName} Final Fantasy 1 Client"; Filename: "{app}\ArchipelagoFF1Client.exe"; Tasks: desktopicon; Components: client/ff1 -Name: "{commondesktop}\{#MyAppName} Pokemon Client"; Filename: "{app}\ArchipelagoPokemonClient.exe"; Tasks: desktopicon; Components: client/pkmn -Name: "{commondesktop}\{#MyAppName} ChecksFinder Client"; Filename: "{app}\ArchipelagoChecksFinderClient.exe"; Tasks: desktopicon; Components: client/cf -Name: "{commondesktop}\{#MyAppName} Starcraft 2 Client"; Filename: "{app}\ArchipelagoStarcraft2Client.exe"; Tasks: desktopicon; Components: client/sc2 -Name: "{commondesktop}\{#MyAppName} MegaMan Battle Network 3 Client"; Filename: "{app}\ArchipelagoMMBN3Client.exe"; Tasks: desktopicon; Components: client/mmbn3 -Name: "{commondesktop}\{#MyAppName} The Legend of Zelda Client"; Filename: "{app}\ArchipelagoZelda1Client.exe"; Tasks: desktopicon; Components: client/tloz -Name: "{commondesktop}\{#MyAppName} Wargroove Client"; Filename: "{app}\ArchipelagoWargrooveClient.exe"; Tasks: desktopicon; Components: client/wargroove -Name: "{commondesktop}\{#MyAppName} Kingdom Hearts 2 Client"; Filename: "{app}\ArchipelagoKH2Client.exe"; Tasks: desktopicon; Components: client/kh2 -Name: "{commondesktop}\{#MyAppName} Link's Awakening Client"; Filename: "{app}\ArchipelagoLinksAwakeningClient.exe"; Tasks: desktopicon; Components: client/ladx -Name: "{commondesktop}\{#MyAppName} Adventure Client"; Filename: "{app}\ArchipelagoAdventureClient.exe"; Tasks: desktopicon; Components: client/advn -Name: "{commondesktop}\{#MyAppName} Undertale Client"; Filename: "{app}\ArchipelagoUndertaleClient.exe"; Tasks: desktopicon; Components: client/ut [Run] Filename: "{tmp}\vc_redist.x64.exe"; Parameters: "/passive /norestart"; Check: IsVCRedist64BitNeeded; StatusMsg: "Installing VC++ redistributable..." -Filename: "{app}\ArchipelagoLttPAdjuster"; Parameters: "--update_sprites"; StatusMsg: "Updating Sprite Library..."; Components: client/sni/lttp or generator/lttp -Filename: "{app}\ArchipelagoMinecraftClient.exe"; Parameters: "--install"; StatusMsg: "Installing Forge Server..."; Components: client/minecraft +Filename: "{app}\ArchipelagoLttPAdjuster"; Parameters: "--update_sprites"; StatusMsg: "Updating Sprite Library..."; Flags: nowait; Components: lttp_sprites Filename: "{app}\ArchipelagoLauncher"; Parameters: "--update_settings"; StatusMsg: "Updating host.yaml..."; Flags: runasoriginaluser runhidden Filename: "{app}\ArchipelagoLauncher"; Description: "{cm:LaunchProgram,{#StringChange('Launcher', '&', '&&')}}"; Flags: nowait postinstall skipifsilent @@ -206,101 +88,97 @@ Type: filesandordirs; Name: "{app}\EnemizerCLI*" [Registry] -Root: HKCR; Subkey: ".aplttp"; ValueData: "{#MyAppName}patch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}patch"; ValueData: "Archipelago Binary Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}patch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}patch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/sni +Root: HKCR; Subkey: ".aplttp"; ValueData: "{#MyAppName}patch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}patch"; ValueData: "Archipelago Binary Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}patch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}patch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; -Root: HKCR; Subkey: ".apsm"; ValueData: "{#MyAppName}smpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}smpatch"; ValueData: "Archipelago Super Metroid Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}smpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}smpatch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/sni +Root: HKCR; Subkey: ".apsm"; ValueData: "{#MyAppName}smpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}smpatch"; ValueData: "Archipelago Super Metroid Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}smpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}smpatch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; -Root: HKCR; Subkey: ".apdkc3"; ValueData: "{#MyAppName}dkc3patch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}dkc3patch"; ValueData: "Archipelago Donkey Kong Country 3 Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}dkc3patch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}dkc3patch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/sni +Root: HKCR; Subkey: ".apdkc3"; ValueData: "{#MyAppName}dkc3patch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}dkc3patch"; ValueData: "Archipelago Donkey Kong Country 3 Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}dkc3patch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}dkc3patch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; -Root: HKCR; Subkey: ".apsmw"; ValueData: "{#MyAppName}smwpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}smwpatch"; ValueData: "Archipelago Super Mario World Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}smwpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}smwpatch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/sni +Root: HKCR; Subkey: ".apsmw"; ValueData: "{#MyAppName}smwpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}smwpatch"; ValueData: "Archipelago Super Mario World Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}smwpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}smwpatch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; -Root: HKCR; Subkey: ".apzl"; ValueData: "{#MyAppName}zlpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/zl -Root: HKCR; Subkey: "{#MyAppName}zlpatch"; ValueData: "Archipelago Zillion Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/zl -Root: HKCR; Subkey: "{#MyAppName}zlpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoZillionClient.exe,0"; ValueType: string; ValueName: ""; Components: client/zl -Root: HKCR; Subkey: "{#MyAppName}zlpatch\shell\open\command"; ValueData: """{app}\ArchipelagoZillionClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/zl +Root: HKCR; Subkey: ".apzl"; ValueData: "{#MyAppName}zlpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}zlpatch"; ValueData: "Archipelago Zillion Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}zlpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoZillionClient.exe,0"; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}zlpatch\shell\open\command"; ValueData: """{app}\ArchipelagoZillionClient.exe"" ""%1"""; ValueType: string; ValueName: ""; -Root: HKCR; Subkey: ".apsmz3"; ValueData: "{#MyAppName}smz3patch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}smz3patch"; ValueData: "Archipelago SMZ3 Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}smz3patch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}smz3patch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/sni +Root: HKCR; Subkey: ".apsmz3"; ValueData: "{#MyAppName}smz3patch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}smz3patch"; ValueData: "Archipelago SMZ3 Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}smz3patch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}smz3patch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; -Root: HKCR; Subkey: ".apsoe"; ValueData: "{#MyAppName}soepatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}soepatch"; ValueData: "Archipelago Secret of Evermore Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}soepatch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}soepatch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/sni +Root: HKCR; Subkey: ".apsoe"; ValueData: "{#MyAppName}soepatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}soepatch"; ValueData: "Archipelago Secret of Evermore Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}soepatch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}soepatch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; -Root: HKCR; Subkey: ".apl2ac"; ValueData: "{#MyAppName}l2acpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}l2acpatch"; ValueData: "Archipelago Lufia II Ancient Cave Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}l2acpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; Components: client/sni -Root: HKCR; Subkey: "{#MyAppName}l2acpatch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/sni +Root: HKCR; Subkey: ".apl2ac"; ValueData: "{#MyAppName}l2acpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}l2acpatch"; ValueData: "Archipelago Lufia II Ancient Cave Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}l2acpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}l2acpatch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; -Root: HKCR; Subkey: ".apmc"; ValueData: "{#MyAppName}mcdata"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/minecraft -Root: HKCR; Subkey: "{#MyAppName}mcdata"; ValueData: "Archipelago Minecraft Data"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/minecraft -Root: HKCR; Subkey: "{#MyAppName}mcdata\DefaultIcon"; ValueData: "{app}\ArchipelagoMinecraftClient.exe,0"; ValueType: string; ValueName: ""; Components: client/minecraft -Root: HKCR; Subkey: "{#MyAppName}mcdata\shell\open\command"; ValueData: """{app}\ArchipelagoMinecraftClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/minecraft +Root: HKCR; Subkey: ".apmc"; ValueData: "{#MyAppName}mcdata"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}mcdata"; ValueData: "Archipelago Minecraft Data"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}mcdata\DefaultIcon"; ValueData: "{app}\ArchipelagoMinecraftClient.exe,0"; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}mcdata\shell\open\command"; ValueData: """{app}\ArchipelagoMinecraftClient.exe"" ""%1"""; ValueType: string; ValueName: ""; -Root: HKCR; Subkey: ".apz5"; ValueData: "{#MyAppName}n64zpf"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/oot -Root: HKCR; Subkey: "{#MyAppName}n64zpf"; ValueData: "Archipelago Ocarina of Time Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/oot -Root: HKCR; Subkey: "{#MyAppName}n64zpf\DefaultIcon"; ValueData: "{app}\ArchipelagoOoTClient.exe,0"; ValueType: string; ValueName: ""; Components: client/oot -Root: HKCR; Subkey: "{#MyAppName}n64zpf\shell\open\command"; ValueData: """{app}\ArchipelagoOoTClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/oot +Root: HKCR; Subkey: ".apz5"; ValueData: "{#MyAppName}n64zpf"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}n64zpf"; ValueData: "Archipelago Ocarina of Time Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}n64zpf\DefaultIcon"; ValueData: "{app}\ArchipelagoOoTClient.exe,0"; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}n64zpf\shell\open\command"; ValueData: """{app}\ArchipelagoOoTClient.exe"" ""%1"""; ValueType: string; ValueName: ""; -Root: HKCR; Subkey: ".apred"; ValueData: "{#MyAppName}pkmnrpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/pkmn -Root: HKCR; Subkey: "{#MyAppName}pkmnrpatch"; ValueData: "Archipelago Pokemon Red Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/pkmn -Root: HKCR; Subkey: "{#MyAppName}pkmnrpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoPokemonClient.exe,0"; ValueType: string; ValueName: ""; Components: client/pkmn -Root: HKCR; Subkey: "{#MyAppName}pkmnrpatch\shell\open\command"; ValueData: """{app}\ArchipelagoPokemonClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/pkmn +Root: HKCR; Subkey: ".apred"; ValueData: "{#MyAppName}pkmnrpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}pkmnrpatch"; ValueData: "Archipelago Pokemon Red Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}pkmnrpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoPokemonClient.exe,0"; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}pkmnrpatch\shell\open\command"; ValueData: """{app}\ArchipelagoPokemonClient.exe"" ""%1"""; ValueType: string; ValueName: ""; -Root: HKCR; Subkey: ".apblue"; ValueData: "{#MyAppName}pkmnbpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/pkmn -Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch"; ValueData: "Archipelago Pokemon Blue Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/pkmn -Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoPokemonClient.exe,0"; ValueType: string; ValueName: ""; Components: client/pkmn -Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch\shell\open\command"; ValueData: """{app}\ArchipelagoPokemonClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/pkmn +Root: HKCR; Subkey: ".apblue"; ValueData: "{#MyAppName}pkmnbpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch"; ValueData: "Archipelago Pokemon Blue Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoPokemonClient.exe,0"; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}pkmnbpatch\shell\open\command"; ValueData: """{app}\ArchipelagoPokemonClient.exe"" ""%1"""; ValueType: string; ValueName: ""; -Root: HKCR; Subkey: ".apbn3"; ValueData: "{#MyAppName}bn3bpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/mmbn3 -Root: HKCR; Subkey: "{#MyAppName}bn3bpatch"; ValueData: "Archipelago MegaMan Battle Network 3 Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/mmbn3 -Root: HKCR; Subkey: "{#MyAppName}bn3bpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoMMBN3Client.exe,0"; ValueType: string; ValueName: ""; Components: client/mmbn3 -Root: HKCR; Subkey: "{#MyAppName}bn3bpatch\shell\open\command"; ValueData: """{app}\ArchipelagoMMBN3Client.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/mmbn3 +Root: HKCR; Subkey: ".apbn3"; ValueData: "{#MyAppName}bn3bpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}bn3bpatch"; ValueData: "Archipelago MegaMan Battle Network 3 Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}bn3bpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoMMBN3Client.exe,0"; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}bn3bpatch\shell\open\command"; ValueData: """{app}\ArchipelagoMMBN3Client.exe"" ""%1"""; ValueType: string; ValueName: ""; -Root: HKCR; Subkey: ".apladx"; ValueData: "{#MyAppName}ladxpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/ladx -Root: HKCR; Subkey: "{#MyAppName}ladxpatch"; ValueData: "Archipelago Links Awakening DX Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/ladx -Root: HKCR; Subkey: "{#MyAppName}ladxpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoLinksAwakeningClient.exe,0"; ValueType: string; ValueName: ""; Components: client/ladx -Root: HKCR; Subkey: "{#MyAppName}ladxpatch\shell\open\command"; ValueData: """{app}\ArchipelagoLinksAwakeningClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/ladx +Root: HKCR; Subkey: ".apladx"; ValueData: "{#MyAppName}ladxpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}ladxpatch"; ValueData: "Archipelago Links Awakening DX Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}ladxpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoLinksAwakeningClient.exe,0"; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}ladxpatch\shell\open\command"; ValueData: """{app}\ArchipelagoLinksAwakeningClient.exe"" ""%1"""; ValueType: string; ValueName: ""; -Root: HKCR; Subkey: ".aptloz"; ValueData: "{#MyAppName}tlozpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/tloz -Root: HKCR; Subkey: "{#MyAppName}tlozpatch"; ValueData: "Archipelago The Legend of Zelda Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/tloz -Root: HKCR; Subkey: "{#MyAppName}tlozpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoZelda1Client.exe,0"; ValueType: string; ValueName: ""; Components: client/tloz -Root: HKCR; Subkey: "{#MyAppName}tlozpatch\shell\open\command"; ValueData: """{app}\ArchipelagoZelda1Client.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/tloz +Root: HKCR; Subkey: ".aptloz"; ValueData: "{#MyAppName}tlozpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}tlozpatch"; ValueData: "Archipelago The Legend of Zelda Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}tlozpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoZelda1Client.exe,0"; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}tlozpatch\shell\open\command"; ValueData: """{app}\ArchipelagoZelda1Client.exe"" ""%1"""; ValueType: string; ValueName: ""; -Root: HKCR; Subkey: ".apadvn"; ValueData: "{#MyAppName}advnpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/advn -Root: HKCR; Subkey: "{#MyAppName}advnpatch"; ValueData: "Archipelago Adventure Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/advn -Root: HKCR; Subkey: "{#MyAppName}advnpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoAdventureClient.exe,0"; ValueType: string; ValueName: ""; Components: client/advn -Root: HKCR; Subkey: "{#MyAppName}advnpatch\shell\open\command"; ValueData: """{app}\ArchipelagoAdventureClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/advn +Root: HKCR; Subkey: ".apadvn"; ValueData: "{#MyAppName}advnpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}advnpatch"; ValueData: "Archipelago Adventure Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}advnpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoAdventureClient.exe,0"; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}advnpatch\shell\open\command"; ValueData: """{app}\ArchipelagoAdventureClient.exe"" ""%1"""; ValueType: string; ValueName: ""; -Root: HKCR; Subkey: ".archipelago"; ValueData: "{#MyAppName}multidata"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: server -Root: HKCR; Subkey: "{#MyAppName}multidata"; ValueData: "Archipelago Server Data"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: server -Root: HKCR; Subkey: "{#MyAppName}multidata\DefaultIcon"; ValueData: "{app}\ArchipelagoServer.exe,0"; ValueType: string; ValueName: ""; Components: server -Root: HKCR; Subkey: "{#MyAppName}multidata\shell\open\command"; ValueData: """{app}\ArchipelagoServer.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: server +Root: HKCR; Subkey: ".archipelago"; ValueData: "{#MyAppName}multidata"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}multidata"; ValueData: "Archipelago Server Data"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}multidata\DefaultIcon"; ValueData: "{app}\ArchipelagoServer.exe,0"; ValueType: string; ValueName: ""; +Root: HKCR; Subkey: "{#MyAppName}multidata\shell\open\command"; ValueData: """{app}\ArchipelagoServer.exe"" ""%1"""; ValueType: string; ValueName: ""; -Root: HKCR; Subkey: "archipelago"; ValueType: "string"; ValueData: "Archipegalo Protocol"; Flags: uninsdeletekey; Components: client/text -Root: HKCR; Subkey: "archipelago"; ValueType: "string"; ValueName: "URL Protocol"; ValueData: ""; Components: client/text -Root: HKCR; Subkey: "archipelago\DefaultIcon"; ValueType: "string"; ValueData: "{app}\ArchipelagoTextClient.exe,0"; Components: client/text -Root: HKCR; Subkey: "archipelago\shell\open\command"; ValueType: "string"; ValueData: """{app}\ArchipelagoTextClient.exe"" ""%1"""; Components: client/text +Root: HKCR; Subkey: "archipelago"; ValueType: "string"; ValueData: "Archipegalo Protocol"; Flags: uninsdeletekey; +Root: HKCR; Subkey: "archipelago"; ValueType: "string"; ValueName: "URL Protocol"; ValueData: ""; +Root: HKCR; Subkey: "archipelago\DefaultIcon"; ValueType: "string"; ValueData: "{app}\ArchipelagoTextClient.exe,0"; +Root: HKCR; Subkey: "archipelago\shell\open\command"; ValueType: "string"; ValueData: """{app}\ArchipelagoTextClient.exe"" ""%1"""; [Code] -const - SHCONTCH_NOPROGRESSBOX = 4; - SHCONTCH_RESPONDYESTOALL = 16; - // See: https://stackoverflow.com/a/51614652/2287576 function IsVCRedist64BitNeeded(): boolean; var @@ -320,594 +198,3 @@ begin Result := True; end; end; - -var R : longint; - -var lttprom: string; -var LttPROMFilePage: TInputFileWizardPage; - -var smrom: string; -var SMRomFilePage: TInputFileWizardPage; - -var dkc3rom: string; -var DKC3RomFilePage: TInputFileWizardPage; - -var smwrom: string; -var SMWRomFilePage: TInputFileWizardPage; - -var soerom: string; -var SoERomFilePage: TInputFileWizardPage; - -var l2acrom: string; -var L2ACROMFilePage: TInputFileWizardPage; - -var ootrom: string; -var OoTROMFilePage: TInputFileWizardPage; - -var zlrom: string; -var ZlROMFilePage: TInputFileWizardPage; - -var redrom: string; -var RedROMFilePage: TInputFileWizardPage; - -var bluerom: string; -var BlueROMFilePage: TInputFileWizardPage; - -var bn3rom: string; -var BN3ROMFilePage: TInputFileWizardPage; - -var ladxrom: string; -var LADXROMFilePage: TInputFileWizardPage; - -var tlozrom: string; -var TLoZROMFilePage: TInputFileWizardPage; - -var advnrom: string; -var AdvnROMFilePage: TInputFileWizardPage; - -function GetSNESMD5OfFile(const rom: string): string; -var data: AnsiString; -begin - if LoadStringFromFile(rom, data) then - begin - if Length(data) mod 1024 = 512 then - begin - data := copy(data, 513, Length(data)-512); - end; - Result := GetMD5OfString(data); - end; -end; - -function GetSMSMD5OfFile(const rom: string): string; -var data: AnsiString; -begin - if LoadStringFromFile(rom, data) then - begin - Result := GetMD5OfString(data); - end; -end; - -function CheckRom(name: string; hash: string): string; -var rom: string; -begin - log('Handling ' + name) - rom := FileSearch(name, WizardDirValue()); - if Length(rom) > 0 then - begin - log('existing ROM found'); - log(IntToStr(CompareStr(GetSNESMD5OfFile(rom), hash))); - if CompareStr(GetSNESMD5OfFile(rom), hash) = 0 then - begin - log('existing ROM verified'); - Result := rom; - exit; - end; - log('existing ROM failed verification'); - end; -end; - -function CheckSMSRom(name: string; hash: string): string; -var rom: string; -begin - log('Handling ' + name) - rom := FileSearch(name, WizardDirValue()); - if Length(rom) > 0 then - begin - log('existing ROM found'); - log(IntToStr(CompareStr(GetSMSMD5OfFile(rom), hash))); - if CompareStr(GetSMSMD5OfFile(rom), hash) = 0 then - begin - log('existing ROM verified'); - Result := rom; - exit; - end; - log('existing ROM failed verification'); - end; -end; - -function CheckNESRom(name: string; hash: string): string; -var rom: string; -begin - log('Handling ' + name) - rom := FileSearch(name, WizardDirValue()); - if Length(rom) > 0 then - begin - log('existing ROM found'); - log(IntToStr(CompareStr(GetSMSMD5OfFile(rom), hash))); - if CompareStr(GetSMSMD5OfFile(rom), hash) = 0 then - begin - log('existing ROM verified'); - Result := rom; - exit; - end; - log('existing ROM failed verification'); - end; -end; - -function AddRomPage(name: string): TInputFileWizardPage; -begin - Result := - CreateInputFilePage( - wpSelectComponents, - 'Select ROM File', - 'Where is your ' + name + ' located?', - 'Select the file, then click Next.'); - - Result.Add( - 'Location of ROM file:', - 'SNES ROM files|*.sfc;*.smc|All files|*.*', - '.sfc'); -end; - - -function AddGBRomPage(name: string): TInputFileWizardPage; -begin - Result := - CreateInputFilePage( - wpSelectComponents, - 'Select ROM File', - 'Where is your ' + name + ' located?', - 'Select the file, then click Next.'); - - Result.Add( - 'Location of ROM file:', - 'GB ROM files|*.gb;*.gbc|All files|*.*', - '.gb'); -end; - -function AddGBARomPage(name: string): TInputFileWizardPage; -begin - Result := - CreateInputFilePage( - wpSelectComponents, - 'Select ROM File', - 'Where is your ' + name + ' located?', - 'Select the file, then click Next.'); - Result.Add( - 'Location of ROM file:', - 'GBA ROM files|*.gba|All files|*.*', - '.gba'); -end; - -function AddSMSRomPage(name: string): TInputFileWizardPage; -begin - Result := - CreateInputFilePage( - wpSelectComponents, - 'Select ROM File', - 'Where is your ' + name + ' located?', - 'Select the file, then click Next.'); - Result.Add( - 'Location of ROM file:', - 'SMS ROM files|*.sms|All files|*.*', - '.sms'); -end; - -function AddNESRomPage(name: string): TInputFileWizardPage; -begin - Result := - CreateInputFilePage( - wpSelectComponents, - 'Select ROM File', - 'Where is your ' + name + ' located?', - 'Select the file, then click Next.'); - - Result.Add( - 'Location of ROM file:', - 'NES ROM files|*.nes|All files|*.*', - '.nes'); -end; - -procedure AddOoTRomPage(); -begin - ootrom := FileSearch('The Legend of Zelda - Ocarina of Time.z64', WizardDirValue()); - if Length(ootrom) > 0 then - begin - log('existing ROM found'); - log(IntToStr(CompareStr(GetMD5OfFile(ootrom), '5bd1fe107bf8106b2ab6650abecd54d6'))); // normal - log(IntToStr(CompareStr(GetMD5OfFile(ootrom), '6697768a7a7df2dd27a692a2638ea90b'))); // byteswapped - log(IntToStr(CompareStr(GetMD5OfFile(ootrom), '05f0f3ebacbc8df9243b6148ffe4792f'))); // decompressed - if (CompareStr(GetMD5OfFile(ootrom), '5bd1fe107bf8106b2ab6650abecd54d6') = 0) or (CompareStr(GetMD5OfFile(ootrom), '6697768a7a7df2dd27a692a2638ea90b') = 0) or (CompareStr(GetMD5OfFile(ootrom), '05f0f3ebacbc8df9243b6148ffe4792f') = 0) then - begin - log('existing ROM verified'); - exit; - end; - log('existing ROM failed verification'); - end; - ootrom := '' - OoTROMFilePage := - CreateInputFilePage( - wpSelectComponents, - 'Select ROM File', - 'Where is your OoT 1.0 ROM located?', - 'Select the file, then click Next.'); - - OoTROMFilePage.Add( - 'Location of ROM file:', - 'N64 ROM files (*.z64, *.n64)|*.z64;*.n64|All files|*.*', - '.z64'); -end; - -function AddA26Page(name: string): TInputFileWizardPage; -begin - Result := - CreateInputFilePage( - wpSelectComponents, - 'Select ROM File', - 'Where is your ' + name + ' located?', - 'Select the file, then click Next.'); - - Result.Add( - 'Location of ROM file:', - 'A2600 ROM files|*.BIN;*.a26|All files|*.*', - '.BIN'); -end; - -function NextButtonClick(CurPageID: Integer): Boolean; -begin - if (assigned(LttPROMFilePage)) and (CurPageID = LttPROMFilePage.ID) then - Result := not (LttPROMFilePage.Values[0] = '') - else if (assigned(SMROMFilePage)) and (CurPageID = SMROMFilePage.ID) then - Result := not (SMROMFilePage.Values[0] = '') - else if (assigned(DKC3ROMFilePage)) and (CurPageID = DKC3ROMFilePage.ID) then - Result := not (DKC3ROMFilePage.Values[0] = '') - else if (assigned(SMWROMFilePage)) and (CurPageID = SMWROMFilePage.ID) then - Result := not (SMWROMFilePage.Values[0] = '') - else if (assigned(SoEROMFilePage)) and (CurPageID = SoEROMFilePage.ID) then - Result := not (SoEROMFilePage.Values[0] = '') - else if (assigned(L2ACROMFilePage)) and (CurPageID = L2ACROMFilePage.ID) then - Result := not (L2ACROMFilePage.Values[0] = '') - else if (assigned(OoTROMFilePage)) and (CurPageID = OoTROMFilePage.ID) then - Result := not (OoTROMFilePage.Values[0] = '') - else if (assigned(BN3ROMFilePage)) and (CurPageID = BN3ROMFilePage.ID) then - Result := not (BN3ROMFilePage.Values[0] = '') - else if (assigned(ZlROMFilePage)) and (CurPageID = ZlROMFilePage.ID) then - Result := not (ZlROMFilePage.Values[0] = '') - else if (assigned(RedROMFilePage)) and (CurPageID = RedROMFilePage.ID) then - Result := not (RedROMFilePage.Values[0] = '') - else if (assigned(BlueROMFilePage)) and (CurPageID = BlueROMFilePage.ID) then - Result := not (BlueROMFilePage.Values[0] = '') - else if (assigned(LADXROMFilePage)) and (CurPageID = LADXROMFilePage.ID) then - Result := not (LADXROMFilePage.Values[0] = '') - else if (assigned(TLoZROMFilePage)) and (CurPageID = TLoZROMFilePage.ID) then - Result := not (TLoZROMFilePage.Values[0] = '') - else if (assigned(AdvnROMFilePage)) and (CurPageID = AdvnROMFilePage.ID) then - Result := not (AdvnROMFilePage.Values[0] = '') - else - Result := True; -end; - -function GetROMPath(Param: string): string; -begin - if Length(lttprom) > 0 then - Result := lttprom - else if Assigned(LttPRomFilePage) then - begin - R := CompareStr(GetSNESMD5OfFile(LttPROMFilePage.Values[0]), '03a63945398191337e896e5771f77173') - if R <> 0 then - MsgBox('ALttP ROM validation failed. Very likely wrong file.', mbInformation, MB_OK); - - Result := LttPROMFilePage.Values[0] - end - else - Result := ''; - end; - -function GetSMROMPath(Param: string): string; -begin - if Length(smrom) > 0 then - Result := smrom - else if Assigned(SMRomFilePage) then - begin - R := CompareStr(GetSNESMD5OfFile(SMROMFilePage.Values[0]), '21f3e98df4780ee1c667b84e57d88675') - if R <> 0 then - MsgBox('Super Metroid ROM validation failed. Very likely wrong file.', mbInformation, MB_OK); - - Result := SMROMFilePage.Values[0] - end - else - Result := ''; - end; - -function GetDKC3ROMPath(Param: string): string; -begin - if Length(dkc3rom) > 0 then - Result := dkc3rom - else if Assigned(DKC3RomFilePage) then - begin - R := CompareStr(GetSNESMD5OfFile(DKC3ROMFilePage.Values[0]), '120abf304f0c40fe059f6a192ed4f947') - if R <> 0 then - MsgBox('Donkey Kong Country 3 ROM validation failed. Very likely wrong file.', mbInformation, MB_OK); - - Result := DKC3ROMFilePage.Values[0] - end - else - Result := ''; - end; - -function GetSMWROMPath(Param: string): string; -begin - if Length(smwrom) > 0 then - Result := smwrom - else if Assigned(SMWRomFilePage) then - begin - R := CompareStr(GetSNESMD5OfFile(SMWROMFilePage.Values[0]), 'cdd3c8c37322978ca8669b34bc89c804') - if R <> 0 then - MsgBox('Super Mario World ROM validation failed. Very likely wrong file.', mbInformation, MB_OK); - - Result := SMWROMFilePage.Values[0] - end - else - Result := ''; - end; - -function GetSoEROMPath(Param: string): string; -begin - if Length(soerom) > 0 then - Result := soerom - else if Assigned(SoERomFilePage) then - begin - R := CompareStr(GetSNESMD5OfFile(SoEROMFilePage.Values[0]), '6e9c94511d04fac6e0a1e582c170be3a') - if R <> 0 then - MsgBox('Secret of Evermore ROM validation failed. Very likely wrong file.', mbInformation, MB_OK); - - Result := SoEROMFilePage.Values[0] - end - else - Result := ''; - end; - -function GetOoTROMPath(Param: string): string; -begin - if Length(ootrom) > 0 then - Result := ootrom - else if Assigned(OoTROMFilePage) then - begin - R := CompareStr(GetMD5OfFile(OoTROMFilePage.Values[0]), '5bd1fe107bf8106b2ab6650abecd54d6') * CompareStr(GetMD5OfFile(OoTROMFilePage.Values[0]), '6697768a7a7df2dd27a692a2638ea90b') * CompareStr(GetMD5OfFile(OoTROMFilePage.Values[0]), '05f0f3ebacbc8df9243b6148ffe4792f'); - if R <> 0 then - MsgBox('OoT ROM validation failed. Very likely wrong file.', mbInformation, MB_OK); - - Result := OoTROMFilePage.Values[0] - end - else - Result := ''; -end; - -function GetL2ACROMPath(Param: string): string; -begin - if Length(l2acrom) > 0 then - Result := l2acrom - else if Assigned(L2ACROMFilePage) then - begin - R := CompareStr(GetSNESMD5OfFile(L2ACROMFilePage.Values[0]), '6efc477d6203ed2b3b9133c1cd9e9c5d') - if R <> 0 then - MsgBox('Lufia II ROM validation failed. Very likely wrong file.', mbInformation, MB_OK); - - Result := L2ACROMFilePage.Values[0] - end - else - Result := ''; -end; - -function GetZlROMPath(Param: string): string; -begin - if Length(zlrom) > 0 then - Result := zlrom - else if Assigned(ZlROMFilePage) then - begin - R := CompareStr(GetMD5OfFile(ZlROMFilePage.Values[0]), 'd4bf9e7bcf9a48da53785d2ae7bc4270'); - if R <> 0 then - MsgBox('Zillion ROM validation failed. Very likely wrong file.', mbInformation, MB_OK); - - Result := ZlROMFilePage.Values[0] - end - else - Result := ''; -end; - -function GetRedROMPath(Param: string): string; -begin - if Length(redrom) > 0 then - Result := redrom - else if Assigned(RedROMFilePage) then - begin - R := CompareStr(GetMD5OfFile(RedROMFilePage.Values[0]), '3d45c1ee9abd5738df46d2bdda8b57dc') - if R <> 0 then - MsgBox('Pokemon Red ROM validation failed. Very likely wrong file.', mbInformation, MB_OK); - - Result := RedROMFilePage.Values[0] - end - else - Result := ''; - end; - -function GetBlueROMPath(Param: string): string; -begin - if Length(bluerom) > 0 then - Result := bluerom - else if Assigned(BlueROMFilePage) then - begin - R := CompareStr(GetMD5OfFile(BlueROMFilePage.Values[0]), '50927e843568814f7ed45ec4f944bd8b') - if R <> 0 then - MsgBox('Pokemon Blue ROM validation failed. Very likely wrong file.', mbInformation, MB_OK); - - Result := BlueROMFilePage.Values[0] - end - else - Result := ''; - end; - -function GetTLoZROMPath(Param: string): string; -begin - if Length(tlozrom) > 0 then - Result := tlozrom - else if Assigned(TLoZROMFilePage) then - begin - R := CompareStr(GetMD5OfFile(TLoZROMFilePage.Values[0]), '337bd6f1a1163df31bf2633665589ab0'); - if R <> 0 then - MsgBox('The Legend of Zelda ROM validation failed. Very likely wrong file.', mbInformation, MB_OK); - - Result := TLoZROMFilePage.Values[0] - end - else - Result := ''; -end; - -function GetLADXROMPath(Param: string): string; -begin - if Length(ladxrom) > 0 then - Result := ladxrom - else if Assigned(LADXROMFilePage) then - begin - R := CompareStr(GetMD5OfFile(LADXROMFilePage.Values[0]), '07c211479386825042efb4ad31bb525f') - if R <> 0 then - MsgBox('Link''s Awakening DX ROM validation failed. Very likely wrong file.', mbInformation, MB_OK); - - Result := LADXROMFilePage.Values[0] - end - else - Result := ''; - end; - -function GetAdvnROMPath(Param: string): string; -begin - if Length(advnrom) > 0 then - Result := advnrom - else if Assigned(AdvnROMFilePage) then - begin - R := CompareStr(GetMD5OfFile(AdvnROMFilePage.Values[0]), '157bddb7192754a45372be196797f284'); - if R <> 0 then - MsgBox('Adventure ROM validation failed. Very likely wrong file.', mbInformation, MB_OK); - - Result := AdvnROMFilePage.Values[0] - end - else - Result := ''; -end; - -function GetBN3ROMPath(Param: string): string; -begin - if Length(bn3rom) > 0 then - Result := bn3rom - else if Assigned(BN3ROMFilePage) then - begin - R := CompareStr(GetMD5OfFile(BN3ROMFilePage.Values[0]), '6fe31df0144759b34ad666badaacc442') - if R <> 0 then - MsgBox('MegaMan Battle Network 3 Blue ROM validation failed. Very likely wrong file.', mbInformation, MB_OK); - - Result := BN3ROMFilePage.Values[0] - end - else - Result := ''; - end; - -procedure InitializeWizard(); -begin - AddOoTRomPage(); - - lttprom := CheckRom('Zelda no Densetsu - Kamigami no Triforce (Japan).sfc', '03a63945398191337e896e5771f77173'); - if Length(lttprom) = 0 then - LttPROMFilePage:= AddRomPage('Zelda no Densetsu - Kamigami no Triforce (Japan).sfc'); - - smrom := CheckRom('Super Metroid (JU).sfc', '21f3e98df4780ee1c667b84e57d88675'); - if Length(smrom) = 0 then - SMRomFilePage:= AddRomPage('Super Metroid (JU).sfc'); - - dkc3rom := CheckRom('Donkey Kong Country 3 - Dixie Kong''s Double Trouble! (USA) (En,Fr).sfc', '120abf304f0c40fe059f6a192ed4f947'); - if Length(dkc3rom) = 0 then - DKC3RomFilePage:= AddRomPage('Donkey Kong Country 3 - Dixie Kong''s Double Trouble! (USA) (En,Fr).sfc'); - - smwrom := CheckRom('Super Mario World (USA).sfc', 'cdd3c8c37322978ca8669b34bc89c804'); - if Length(smwrom) = 0 then - SMWRomFilePage:= AddRomPage('Super Mario World (USA).sfc'); - - soerom := CheckRom('Secret of Evermore (USA).sfc', '6e9c94511d04fac6e0a1e582c170be3a'); - if Length(soerom) = 0 then - SoEROMFilePage:= AddRomPage('Secret of Evermore (USA).sfc'); - - zlrom := CheckSMSRom('Zillion (UE) [!].sms', 'd4bf9e7bcf9a48da53785d2ae7bc4270'); - if Length(zlrom) = 0 then - ZlROMFilePage:= AddSMSRomPage('Zillion (UE) [!].sms'); - - redrom := CheckRom('Pokemon Red (UE) [S][!].gb','3d45c1ee9abd5738df46d2bdda8b57dc'); - if Length(redrom) = 0 then - RedROMFilePage:= AddGBRomPage('Pokemon Red (UE) [S][!].gb'); - - bluerom := CheckRom('Pokemon Blue (UE) [S][!].gb','50927e843568814f7ed45ec4f944bd8b'); - if Length(bluerom) = 0 then - BlueROMFilePage:= AddGBRomPage('Pokemon Blue (UE) [S][!].gb'); - - bn3rom := CheckRom('Mega Man Battle Network 3 - Blue Version (USA).gba','6fe31df0144759b34ad666badaacc442'); - if Length(bn3rom) = 0 then - BN3ROMFilePage:= AddGBARomPage('Mega Man Battle Network 3 - Blue Version (USA).gba'); - - ladxrom := CheckRom('Legend of Zelda, The - Link''s Awakening DX (USA, Europe) (SGB Enhanced).gbc','07c211479386825042efb4ad31bb525f'); - if Length(ladxrom) = 0 then - LADXROMFilePage:= AddGBRomPage('Legend of Zelda, The - Link''s Awakening DX (USA, Europe) (SGB Enhanced).gbc'); - - l2acrom := CheckRom('Lufia II - Rise of the Sinistrals (USA).sfc', '6efc477d6203ed2b3b9133c1cd9e9c5d'); - if Length(l2acrom) = 0 then - L2ACROMFilePage:= AddRomPage('Lufia II - Rise of the Sinistrals (USA).sfc'); - - tlozrom := CheckNESROM('Legend of Zelda, The (U) (PRG0) [!].nes', '337bd6f1a1163df31bf2633665589ab0'); - if Length(tlozrom) = 0 then - TLoZROMFilePage:= AddNESRomPage('Legend of Zelda, The (U) (PRG0) [!].nes'); - - advnrom := CheckSMSRom('ADVNTURE.BIN', '157bddb7192754a45372be196797f284'); - if Length(advnrom) = 0 then - AdvnROMFilePage:= AddA26Page('ADVNTURE.BIN'); -end; - - -function ShouldSkipPage(PageID: Integer): Boolean; -begin - Result := False; - if (assigned(LttPROMFilePage)) and (PageID = LttPROMFilePage.ID) then - Result := not (WizardIsComponentSelected('client/sni/lttp') or WizardIsComponentSelected('generator/lttp')); - if (assigned(SMROMFilePage)) and (PageID = SMROMFilePage.ID) then - Result := not (WizardIsComponentSelected('client/sni/sm') or WizardIsComponentSelected('generator/sm')); - if (assigned(DKC3ROMFilePage)) and (PageID = DKC3ROMFilePage.ID) then - Result := not (WizardIsComponentSelected('client/sni/dkc3') or WizardIsComponentSelected('generator/dkc3')); - if (assigned(SMWROMFilePage)) and (PageID = SMWROMFilePage.ID) then - Result := not (WizardIsComponentSelected('client/sni/smw') or WizardIsComponentSelected('generator/smw')); - if (assigned(L2ACROMFilePage)) and (PageID = L2ACROMFilePage.ID) then - Result := not (WizardIsComponentSelected('client/sni/l2ac') or WizardIsComponentSelected('generator/l2ac')); - if (assigned(SoEROMFilePage)) and (PageID = SoEROMFilePage.ID) then - Result := not (WizardIsComponentSelected('generator/soe')); - if (assigned(OoTROMFilePage)) and (PageID = OoTROMFilePage.ID) then - Result := not (WizardIsComponentSelected('generator/oot') or WizardIsComponentSelected('client/oot')); - if (assigned(ZlROMFilePage)) and (PageID = ZlROMFilePage.ID) then - Result := not (WizardIsComponentSelected('generator/zl') or WizardIsComponentSelected('client/zl')); - if (assigned(RedROMFilePage)) and (PageID = RedROMFilePage.ID) then - Result := not (WizardIsComponentSelected('generator/pkmn_r') or WizardIsComponentSelected('client/pkmn/red')); - if (assigned(BlueROMFilePage)) and (PageID = BlueROMFilePage.ID) then - Result := not (WizardIsComponentSelected('generator/pkmn_b') or WizardIsComponentSelected('client/pkmn/blue')); - if (assigned(BN3ROMFilePage)) and (PageID = BN3ROMFilePage.ID) then - Result := not (WizardIsComponentSelected('generator/mmbn3') or WizardIsComponentSelected('client/mmbn3')); - if (assigned(LADXROMFilePage)) and (PageID = LADXROMFilePage.ID) then - Result := not (WizardIsComponentSelected('generator/ladx') or WizardIsComponentSelected('client/ladx')); - if (assigned(TLoZROMFilePage)) and (PageID = TLoZROMFilePage.ID) then - Result := not (WizardIsComponentSelected('generator/tloz') or WizardIsComponentSelected('client/tloz')); - if (assigned(AdvnROMFilePage)) and (PageID = AdvnROMFilePage.ID) then - Result := not (WizardIsComponentSelected('client/advn')); -end; From 8109d4a1af1b0c10acabebb4e5613ddee1f9fd64 Mon Sep 17 00:00:00 2001 From: el-u <109771707+el-u@users.noreply.github.com> Date: Mon, 23 Oct 2023 22:20:27 +0200 Subject: [PATCH 142/144] lufia2ac: prevent "door stairs" and "rare stairs" (#2341) --- worlds/lufia2ac/basepatch/basepatch.asm | 47 ++++++++++++++++++ worlds/lufia2ac/basepatch/basepatch.bsdiff4 | Bin 8555 -> 8638 bytes .../lufia2ac/docs/en_Lufia II Ancient Cave.md | 2 +- 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/worlds/lufia2ac/basepatch/basepatch.asm b/worlds/lufia2ac/basepatch/basepatch.asm index 923ee6a226..f298a1129d 100644 --- a/worlds/lufia2ac/basepatch/basepatch.asm +++ b/worlds/lufia2ac/basepatch/basepatch.asm @@ -1127,6 +1127,53 @@ pullpc +; door stairs fix +pushpc +org $839453 + ; DB=$7F, x=0, m=1 + JSL DoorStairsFix ; overwrites JSR $9B18 : JSR $9D11 + NOP #2 +pullpc + +DoorStairsFix: + CLC + LDY.w #$0000 +--: LDX.w #$00FF ; loop through floor layout starting from the bottom right +-: LDA $EA00,X ; read node contents + BEQ + ; always skip empty nodes + BCC ++ ; 1st pass: skip all blocked nodes (would cause door stairs or rare stairs) + LDA $E9F0,X ; 2nd pass: skip only if the one above is also blocked (would cause door stairs) +++: BMI + + INY ; count usable nodes ++: DEX + BPL - + TYA + BNE ++ ; all nodes blocked? + SEC ; set up 2nd, less restrictive pass + BRA -- +++: JSL $8082C7 ; advance RNG + STA $00211B + TDC + STA $00211B ; M7A; first factor = random number from 0 to 255 + TYA + STA $00211C ; M7B; second factor = number of possible stair positions + LDA $002135 ; MPYM; calculate random number from 0 to number of possible stair positions - 1 + TAY + LDX.w #$00FF ; loop through floor layout starting from the bottom right +-: LDA $EA00,X ; read node contents + BEQ + ; always skip empty nodes + BCC ++ ; if 1st pass was sufficient: skip all blocked nodes (prevent door stairs and rare stairs) + LDA $E9F0,X ; if 2nd pass was needed: skip only if the one above is also blocked (prevent door stairs) +++: BMI + + DEY ; count down to locate the (Y+1)th usable node + BMI ++ ++: DEX + BPL - +++: TXA ; return selected stair node coordinate + RTL + + + ; equipment text fix pushpc org $81F2E3 diff --git a/worlds/lufia2ac/basepatch/basepatch.bsdiff4 b/worlds/lufia2ac/basepatch/basepatch.bsdiff4 index aee1c7125ddac1ca5ccd03a8caf5fa563f2ccee9..4ed1815039a04c4f3c1ce8a6c5a28ddfda86f96e 100644 GIT binary patch literal 8638 zcmZ{pWmFX0x5j6PVJK;&yE_IXM260hZV(uWA*B%zq+_H9iJ@yq0civzr8@;Aq(MRn zDUtiW|GVz`f4a|yv(H-3`S7g0&e|XLPtibGLsb$N+qX;bkctlZ(0K!56b%18kNoP83 z{1lWzN+hCxYL=`Jr5c?VrGY^tNTRs3b(E*3lgCvlfFOkeCG`Z3Ssjfq8bti8^2>Qp zUL0n`DKG5;Jd-G=jT>bEg&Uw)Kj`1DxSQyGzjm?m{_(;b@G3 zy`(ByjinpVtL>^kY2HB0;sqjG6!*!fJ~#o z!-wJq3ZY?f#ZWn}EIPX(AZiY=oItNTjZw`4V{@N+9er`1gNT2BqJ`xBqNudMN}!2ST0F)@rp$9LgUd+$)G$88i9Zi zi@|A$!Z2y^m?>U1CG0<1lE* zFy`Ic(GUoHc^&JSe8UxSqweHUhUVg*)tY5MWfT2x^0n_|q007?>HO=IjcGevEW=38 zS$z_195Hq}_Vjwm;;XP8&E1G~$S3fi|7WKSg{Vxx`=V#fscyQ^pEWiN}r z^ToUnmDsy+iMjp6a(xN?u(9>^z0Sr|tc7I|IExxzw!~yVHe2~UWr_VXj9ErbZ^rpN zweXeVse&{uQC0{7>K2y}K*}OHfDRhKZO1-ujQ6W?El8gaTM$_lC9Jafh{in-rTWU% zgNN(~g_Tw#uwz{Qd}OJa!SSpn)rXsmiI_$Wir_Q~jMf3V;pD=mSd}wCK3CEYreOl< zED9iwsD;K*^24uT_(Joc_`xP0RY72 z&fU!U=&3nrsfmPVD*|mH9918wUG-YORy9?<;*IjCs7j>BP4zS355R>2*)-@1n}^Rz zK0R@Sc@FA%@0(F2LeiBatgcX_s)iw9<8x(Os-I?-UKjB0P{{7c<9QQ^Ix{g{Zjh@aE3&4rkby zSfaG_SLCryKF~n@eZkmAQrnjdfRu4Cn|rU+Fe|5;(!FC5An(gaT`q(qCZ~9&b>}|`1LY;aizO``VFD`lb4+%=EQ~v0dm-|8YU)8mzfqH zLgQ`W`-l|xCiJbOd^|UAw2CDaR!;Ttgw6WiwDj#wCB%752JaE7a^7=V6tl(8h$@2eE?_}$w(ANJmZflrfA^!L8*`uey!IPqEd{LC4VoO+RRy)=? zT67z_jV7BpG=3cQrH2J2a-%LywH3Mk*zTE=wR318FSV#7ZkX18w|e_5d+%ZQ zVtv+_UyIV@dux*&HV&!##kG)EVMVs1>w=DuGJ-PHYFyi0ZF-O1w|PU-#UD!SsNb@x zV`gaANvG$pIer$di8pV3`W*92zCNnM?QEdsEl&2#PWhzBlCQ_lX>yYC6V?$sLM$nT zH#3Q2Ox3J{uP=?EJ)gl?MhEVp28coP#oBRmSlm;>BnXK_DTwXTDEFJ?XjVWJw&D(Dp zsBh4fItkK*)|)b=&UXPaJ8NzCILry=otA43H~SJakmvZMaZETiq(pvHJ%*balrhOO zJi15d_`t|zdfIK*HCIbC8mv$Zu@VG_UoS-)Q#KcktS-elCM3d?=Y$+lS$Qb?dc;sG zk9HOkv~S)e{bpg>@`ws7T+2&cYb0!SHr@ROoNO*kHPE%crlFW+b4%DUfd^BaPov=g zUh!UNM@zlb`P=S)3C@Vn->cS-~%A%@7a(7!AW0$pv{;&vMw7#}Jy`ug5!ltbBn~e%h ztZ}62VW{`L`R8Z@LVm?|tESjbpfKE}HNvPi9c>J9O0e^)=K5swirJhE31bkhO3?fZ zU8cG*O%?lKWVfp>V#35Ac3KngIZ>*(T9>p%f2I@2Djki4mSyvIpcPr7cnKCA<>9yX zEJD6}-3&)A#F1eg8rNnWTG=bZdQ%s<%-%!lCN4?Z=8xdCj;k77r|gysMfkz*XrN#} zoN4BQ9cwjGYkx?!!GAga0gT%#0=WV>mFs-cv+9h+(2xaa3Ny znN@ej!{g!1oVxyzmbhGLOoF5vJSaST>^miGwZ(#>yRRL{*-%!ny})2_Mt^>{wKm_S zsF5Q`<}CPlTq2z+vG08ai-SsSshX{(PkSM(m7>goEqdE|gPC5hwZr-{a1>|Hz$UJ! zE`ZgAG)Jo{o_|GfK1RcCJyMTwYlMqVM?pSzr%C=rpoF0Z+tAJDnC+yGfpWEsEm!Cp zebWytSgR!w15PZNim^yVd^Oq2lgv`Nve>Rh;fuO7s=VO5n~CaPiH=o58tqpRpF2mO zq(JuT3nsSbk4W@>ggSEi?Y%akGHtzj_h<|&hQs}v|GYrSji`a}Vwf4vdDe}8#r50W zGhwOV#Gj8%(XJtX9QM9#y;MwLzIb0Qk~~}}O=KzSP%Y0BlX7_RYVBEX64&(hgZsJ( zV}qGMdf(+uz(j^;>4{^p@Sh7&J-8bqA-5zJ5UU1G!}B=9m1pVpadirls1G%B&vFT% z@JqNX)Aw!TZEmnj4Djl4E`&hx&go6NpLNLJyhGGVstEJE(Op(`vwv~KiA~b}>n^+% zU!e!%kn0`~fq^f5;|?~L?Vfnzee)A+2Xv-pMe|RoBJc8DmM5i(!$h<9oAj@knd6G} z?2iPsfABhWzt^aB4j)i>>dViG& zX!9OF_|2NRv2xTfp(`0bD9Os((?VNU6b29xW**B=H#&w7a&Tutj18uO$QbQTIpOmG znv?Jj*{+n)v`@2zfAG2l#k2GzIc%T_b<#gGyc1Htnee%RHB(P^AW028GQNHZzC9^F zdbsoynwlUd7ee;iG&kuRsbNN3+wT5@1v!ED_+{i^cY)S}HD#&K4ycsrH2*KFbvm3h zy5=k*rp0A7Z6S}+g_1ASG(X4avDC5bH@MSm@rG8vEpt-(0MD9p4@xm6FIRFx$q`E6 zhweKn?jc_m46;1q*7|dGM%)5M%?&qXo(8pCTrn|IVjXcy);1xA$s%qN8~r+V==Dgg z9W{z&b)2+xNw(|Yx&G15=NEIkYX^^uIoNEyEgZtuxnn2@M7k3|GrtpfzI$~2s!rZi zug=X4jPTJCGV%}mV4MFrCvU7VO+!Cb)J*EUBK+6nip{GGSN3_bQ!iaCU4!q!mzQ^j zk1<#vja#AI7V+gZ`yKv{D_@fC0WCnu9X_I8{dd9i5yMGu`cuSMsx|fGT2N8!cJuKp zKx}4H)>p0OBn@7+{z4Kx>qHRz>!5fom4VD2XU?~@fUUdX8RuKHzJQ(*<6kB^(!s?@d2Gg%-ZpfW+Veh^e?`X6W1%akKD73Uhz^yx0pHvyt@z#w z4A+bCR7{nnR=cfDi17sf`Xh_XVBfDRoxqwwg}hL-;eN*sTXj{JGv-sE@b1S~kg#H$ zjDZ#`@;O<}3scPd`sNRa!{uGcWtUXSyX_cRdK6#ry{t;0W%9A|^|uQ7g1S7m$%pO9m-~# zzEfxqivazrvwnZ?@)G{)I5UHFRm+VA!iG*#1Y0ADhmzTMCb3_S3sywK7qRe^=z&x9^`JL&O_9 zX+j3&wsmJ#lo*KU6N-fK&oV^Z+!NISIm_w4%frr_uhN6B>}lV?{qK7$&r=4C=I~6R zxr#bo$w#gZ!Bkn23kKr2N40lt!3TbpP{omKTBXN9X(<)&gSMT1U+Zv`bq_J&6G4hT z%KU%KN~m=*1&&2s_vEpEHSOTQhp(Z=zO3Z=xbu)6pKmApT{%EUI7ZZrA_KW9Dd zy@oI-HML#nJ1p8kuz{k!rrs(MZt#2RCuv*f_H*v~GZ0~wn1hCJEXpCU3~%_e6Em%0 z&e*3;t}}^NF$ga0m>|!wTi^SLl5CDR(P{z9=hT`Ulic>Oz>Ru_s(xS)%+c3&`W1a;2v{aKZGyUeXjxS$7 zZ0LkoeQASYly_^Zd_`%;p7}>H?wB&n)-s1Vw0y1aw@toe!J)b=ySU3e##E*g1J2Q# zC)GluXN)M(o~m}j@;cCqE;rlL#c)1JNJr>>i94x`uGKa(UbiurQ&uqS)5OxA>T z`yi6Tq&SA1-rTE6&gf;Q=e-yQ5~dGG1ow{fri98;?dmjLu7KW%N-t?4YUPW^mgkUm zl8gRsmFfEHSal1f0rIb$wU#E*+}tMH!h5|z&qLK%n1!yilt;u@%VDG8#nP`C3*n$g zLuX>&-mEM5{z(Xbu|?OvR&7nZ*VP_M8r0M@U}aZ>w1s9ja7X<^{s5^PAh%1>>X$^uDnvC+Mq+ zqIOZ_YlDmp8gs<{yI;62e<22O0-gG8RrDcSB?MHu6NOBjI6P<@e;Yot%){=~&s| zuhu$m1!BDjHNscO97UyyLf7}_;^rLGyHPmKqc{_$!SOqziK~AZ6whQAXpjGfHU^=+pn9OR|#I`1{_w@AtLd_2dhJ^uF? zqk)3&i99+fXtHo<)Xi4hM;iXC1Kp>c+5|w9FIIYEN2#roV;m)lhE*zb(sOhWNtP9- z*}S1oiutvriF4YQ?=2pE9k@1=w(W3aHJ{>i#hPX*dXW6`scyRjV@2+5>O^(-PjB+N z5Z@;>O2jeB8+S{`E~e&Zz8R=xXY6@5cOm{&xQ_aJqu0nqJ{9P6VZJhUX%r830Xd68 zi0M{5^z-G-o4r&%5Vm!E|7p|PjImtT$?(ti5rTJbWJ2Hx8Y8w+SDbtHP38q8z>LuLI?d%KlNt{F#i!qu0`On2zjU~_beR=ZEg9UsH zlHq3hu6yJ?T-cx3(IKNPSbKP{(b>}#=0*B~z6M99r5ECwowGT6wN#qpw53~^sm#1EBL*tsUTF>bf>bxz_k z2Ovd-M~o$z`ct2VS&ssJ3ybP$3}ZEs$fF2&1Gb`WY{O_-d}A?RN%C6?;WP1b%@h7( zeiA@YN;?my%=e>fZQ%=NtRQ;_5_|3Ny_&CcDoeiw<&!c|9k2LKaZlAFOy+1Bd)mTz zm-YIls*({%`8?euaNHhoK6dA#JK`_hk_CZ5Qk20e0h%yg!3Bo46oj@R6#T3f7$i#f zBkcN;5dZ+$o1sAvlBj>QPXkkRblUMJ1o?_-bE~Wtamrh(N!RAg1=hm_@nV^AEg01Z z|7vc0nx=&4#bCdm4PR|B#$_*?e2zB>?x#sfX{y+{Xb5PGC$$@_``iC2;OYMdGO04 zQgM@QZcgcw!3QJjk`?z-5gdwkCq)qz1$V5mG<9WWQGf@zyO9c#NG31Cq&dLzM`v{b zEziW0r#ZW}p73WgSv=2i98z6R4AZ-M_P+@z=u(+{T-R?MqxzZ?qTg@HA+@77+rTBK zqPp+r3VMSCkBmjxHqobUH&!yHG(xBen2dNZ5{;V3n-`Q`LxxBCx?yCQ%oyM8$RvnK zbCMTezOKF_84c+Y$Xn$Lvud0^mW~i zp$!I4Uc^UlgW8AyT045?ckxkiz_bWrhWGH;OGQdI`Cnl2UwqOQ>7)xF75QIC;(PUQ z_3)72^!DN5<~C^fVeMh};rgN9`0C+weG2Ci-F~N(V%2|&L%}6eSviCbSXe+ugQizkrqHN- zn~aix6}e*2^vaiVI=KIk3gDp|j6``FaSSN)w^OoMAs59n zfoQ}9cg4O!L0~8y@DSistfTuc2^sqLJpdH?FBSoascXmWqo&000Mv@1{Of<{>@NsHw8R|{1Z!z&WubkG8xD*BcQ=TuFZ!`xV>cbD8L7#I{Pav zUV8O!RYctrw#UJY6D=^~;!hJM`6YinFpvR({A%ri8D9y^oU}w5Uow5E3hz$^a*Pky z(;q33ZZq}-SW}UCl6%u{kXZLb(S)g1w4n)w(B;@%f5Rcl{QSMObO*|zW=+^zCW9U{ zN*m!{*?4*Jhl=3g3bGv?*T0_t0q?M|_$j#ce?JvB5$Ha(n*F5yR_Tp9LH5sB#Hcsm zo5k_NYD(qa`J96s!AzCbHw~zDnFG10&cvrAhF?E-5#&4PlOiYLP@57I2Wd|yyYzQn zFqTE%>c_qmpXU;{LE0$KM*I)w*C1-BQ(D%S`tmnOKSg~75kY$jtr)2+N1oEH+Cky| zaAPwpFcv?f!dQNx#|{4Q&zr(YmEOkD*$?v}`*pUXK~*mjQ8D_bUp064*E-)=MiZc20QbpJ+|&b=9zauZP0paqX%3EG-RwnwO~#1cuwxn}!d1-lWeS@xH^a zeY|<&RjCWHg^3(wIM<~q`;ORWtx*tw$w)gQYEu8MahDsA5fX};qST*E7Q2UUN(eOm zh^>&`o%{l=|FNOm!8^$5DgOsYpbCQEv{#SVINr0=tW z%=9;iu6Om}@v3vs;PN{EK*B0};YTBHLUs^MI;+25TGAhvEdPL~vqDUH3KuniY-h;2He~I=G1(H!*;yQ&cQP`LOw;liy zF|(tZ3r#(hp=|89@jS7yi5nNoYp?w8X2z#m3^@ibbLzvZJGuV9ud4h0Ce&L)P?8LBE>n_WJJ;vuj1|SklLB zNUv7U>z7TdZYy6JQLl`9O#d*Di&s}xfQ8dZskoXSwdiqD=x||DRW{!34VwNC+K&Fr zc>|rb3Q3^45L{q<46pCKeMBf7SZSJ?+A7$8 ztR*>BnlZ=!O|;S;@7HWw!T~rH0cMz^K(YGHi}zCv;=@-cp4M@M&bbj*PMf2nKDF_- ze`+V=S0@Kfp=t6d%Pqe>Hm7!Y5yTnE5N3p#(Pus^uc(y$ zS<@dL8AlzV;mpMUlVm#pldAvw`n|tP5A;eaSe!GA3v_7Y8SQ&o`D4`~%mEcYQ}prlao5zJudM zP8L=SP~PW{>5$~_5?AE)VQ;70FHeh(u7}JD^o!$T`;y0Hyo&SdZ@gYz!}Yt#c%cXj zrz;!B7ML5C63cQI7R%TvPR6L$JO3)#7>@?u&S{= zif=zm3$o?MU#LkPZ)fKDLGOu;Pw*{-rm{?Izp3T;{n%&9-=GJPWA9{l<90#fz|wKG zXdMG7Xv*Ifo-l=oZZy3aba?<6@g$Z$r(R~yx~X*pY6ShNRN(%#f2Hellg=vH2?#$d zTjssz<|8;I>6_{SKe3cBr#RElI>92is+^6HyXcShMaGvx2;Zy$r+W%fop=~ODFAm&&-~gwf4onm`53@s->od9qtNn_ zaC0!y90hL31OPT~Z*M!WJKXh!#AsC*Apq8@1EU2b(gouH5}}L|w4uR(IzS7l1bJ{K zD*2LCqke-x;)UtqUqiV3~3>oOhGX1lpAt5 z082x3TJFmpLMB;kV{5f?$za&Rww}o>?i=q$-~`;neC!b-W_ ze``_lgTD~~NPhFL82?|b{J+&z22n1+1I)$Ok&esfua~l3LBr!~KfDvJq*w`*U3nv%7k_=NiVOi+u zvxSgzh&45|7d%lQlN_M>M;NO*HBp+CfuCMLQvAabcT|apHUN2g_k?+qt8K5CDEi#N*~#Yg%i_3p-wpA zb$x1~bJnZ?dzrArfBR!9fa!CYB4;S|zcWPK3$Xww6odvi;XqIT6n_pNz!8Ci|Cz2( zCgd3?Wx;88LJ4`UuY=SVLZ8LnK~6d)yBRF#PfwpiU1N%L0aMdc@GaJ`Y=d(sLD4iP z4j&LKNi+c>NSN5C{&yi1dRGev)HmQqkmE7|x(vhCPNgwf=!v9&EawOItKUOjfOF1I5pDD-ENI_#=n&+J(aV!7!$4@51)7 z1N)T~miy;6wMU_U!Mt)cg&JsOh0B;U&olE~cB5;=a;&XBB5c7(HD^mXm9z@RV1f#6 z?{&h^WRow{vjxor{^W_-Jo1=*yC|9Aw1jVJR?maPV-4xbeU_4)+Es;(y-=ND)okp826>|cg zdKZbhK!%NPuef}$K$(MoFv%2cVyZR8L@^<1o6IBzmag4P#@(CHF``Kz_LG)J;KU4& zbX&vEtvwdWqutBT{R2cyNNi@`o4^vwpBUUdlRwh2yt8>$!#lHHPqz+Z0spi^O2RKY z&S+%g!inYQ=q37GP%~9v6)j_*3@ap+albVZ*ln1+_?Zn4_B?{*R103`Nra72a7Ou;*{TRfH>l*~eSi5;Y zOC}#TUx$%9-o##5{1owQG}0e?sYV4sT=Vw~WQf>R=gK=~AZ|oTG?SKf;Wf8E(qyXX z0R(LCIGJ3IWX=jOC=zf=tc&Y|{WU*3GKp$W?ivW*RP6DgO^VlJylR(oEGtE&>KMrl z9gwLwnEt~WvHqMEkElR{_vZmFS_)B%9twue@7_kgWO?Ch5ZCuw`ZTc$ddWf&`t)Sd zuJ=I}hquCvDLW>^m*MlqGA#sv`z}Y#FOrS!5T2cZ8kkE7WDj5tp;&G@WrC{TUjUS4j z)}j7t67DL7vv5{AFO9cf+!EE(&Kerv-kY^kF*xw{YDknnpUW_kNOD^~0sDTK$V9PL z*2APo`%WX^wO_mz8B*$#EcG`Ce7w1I`gZ$UXQa* z3}O&6i}1R^_Q468-75N79ifRPDX$E`wj^O2gVfiaDSAp%`ZB3HI*gZ_iX!_rpK59$ zhAgv~syN}nFxNtpUm4;yM?S5Lb7UA2Yib2pP4lFOPLC|0!1Y~2f_+BTWcmz=%~)IO zezcI_e*VFIUgesu@}*WIiZ#zBHXMZW_R6lYJL9yct$MK8A_ypK8w}Gr7|y>BbqTX? zd)pID#d)^!>Gdp#$uKQ_pM0rNza-zZr}fI+eMQ_o?$+_^`!q&etCaI7jG4P@a#Pf- zm#@6R#ES2%R=_j)t+kOIM3Ga#pZ$k#^Yu00>6tL_M^iA<0IVY9z*jGWEDG?FxHsTe zWtD1lQ|{^7pc?|NlPzk3d}6llN(&2Jkgo_VzlG5vtR1U;68YR9&_nbGp zT&%=a*@}XrjB+b2ZK+a_2jJ+6QkFkQtH%Vo1soB6>xs`_xQ!>zcB3d>JPf@9TWk@S zh@a&R$S_#d%zx{P!?Aek@Y)*S2+8gjM#UM7#X{2pNf;Ac+wJevtj&uZa!y9t4bhe` zjb`DATp0-vsx?>o6Ab~ZtGQ`##wg8|yg0+<3fmYRh}5eyY?i=#hboj1gQFK6*3A_x z&`i6K`&O&YCFX%asp9kJFO25OLelX|R@=WSeO-&Y{&fULJ+UOiP|Uo8VC7PZCZp@= zQJLJTOHl~Vp+o;u(A`6jrY#~&+)*(qNKmB=t7*OKh0WMjhsdR(v$ z??hGy-nCWAZkMOE*KJI@Ypo%SO6*y_o4v48@~mGHZ(I-=pN0{$k}qhcEXMhXsR+#~ zUZz|~TLN*_vK7KCQV}oQlu!|RPx_6BaVIZglA~M*`XX&9-g=dBFwqvRP0^6@hyDv) zt{-T*A3Te;RVMr;<@j^u;EXkAbj_5dtS#m(Pfmz;9eiq28I{wdeZxZIm)D-Bm8&i< z)o~VDWEpM7%r6FM*AEBhyZkIfY89Oxd+a~ezKc|=ic)u~GBJ2oM3Qv8mKV6QdPC0l zx-}A{XnKPtIP-0-#x-*S5keygD2% zDk0ZD7A$^ZQOlxsu>yWs178FOjXzk(ss(0X0`Ij)8jxI#73+1gIha1`>3cLOoZ|aU za|8Vy^ZdxQYLqTqlRIRq?^)Vs5wVZp)*PChn0sVThARei9kPMGuT3Sl=?u2Kqb~?s?TeR?tEcwHIZSTx_w9wOqHPTG`b)^$ z*7T>zXFg78hY|<3~((?9)jnB;`+9+7v zb?nvASUM2DAMvZem4j%MpaEo_&&(_afDy-IEf2#bJiadr)3`e}$)?(v_civKv362d zn}6HQ?M=dNs%bnY{F3xUKbg(2-cYlsO?y2OiIQ4&&x6ioYgF3<`&rIg z%p8rgLdlAr9HT1boTDX2PqN$qLSFe}v|s;9y(bTm#{LMvj^_M|EuqR&v(yN>%I+TF z;r8HiF6;FEo#t4#xPpH8`Acs=lfgmj_|qKb9yVvoWXGy%56iYwb^S!e3!#aR z{={jzp*$Hzs!{(n&+}?M^rjj9sG91z=O14GZzYinv8zuO)N9XWn*?4XdB5GbI7ofr z@Nx=yOV#G%{Ipl3Lq}>tVkLCm@3~VkPZae!;q6dbC@O)(xzEK%E~#sK-RsN56+9$EQh{w~T;BTbE){srds$Gj>W+!@Agr?Di0I&E^7BPIlRDrUT%g`i0^5~bym zIy#{N?^Sq9`6E8N&3NZ0U5bHo5ZyTWy}-@_t+39yM(wqsgL}h#DRJ&| zvBwhi3P=p>Z*!v(po#l#)hjb8Z00*|ws}~A>4feCy$CQLB7>Fq$&1(-!lHAlf|5_%qrN^~3|(r&1^o=bOdC|gB&i=Vuj zvyqSsx_ownC7pUHGj>gmKTV%{ez~3U)Sj*3<9c&ewed{LA+OM4(6h6jB1Olx?6@u) z>6i8by|==BvGH1AA7XRv$uq@C?`{9=$q2V^UB8#Mr3boVk^~)B(9gLTTC07$Q6Q`; z>+!AN;$tab=`Yp2<{rGR5b^-hYbDG1M^quqZNLLNU&JVFnJK zExEWE9;NaIk|xkGU}qedS+8qOd19jM3aMWyq!>raJa|qWb@!Lt)SQ4|fp0QGV!P&@ zO~WJl0*%O#`f*BFUUA;falsdEZtVv$y@cF5h$`uKUCZ2F{=I@Ifuc>O5imm6GVh^X znrm`2s43PG4k#LdOE=peJ89E>Eb1E`*5%_K_#6(5tLsIAo88_BSPA0#I&&=*d+HP$ z%d!dgK~J`NpnBNd8@q*~568}Gr2${I5@QQn5RPIw3eJK~jXHSrGZ^{ITC*!1(RPiv z%bWMAr>r4gVxnKAMapu8a1;=lCnLY8e5kiMt{8xJNz^^!MUsYIY&*(g>{;TIItq5q z&sn9%u2@i4N1@Lh<@iUBE5n|1V(erijC^y)#BC~5>NHPNj409$EhGntr-`q{zGNX1 zWbJpn4}CUIcIW|gb_?FcjoD9D0nZBeTi^ zVABla^jni6-eR3a9&{nh zh~BxODjyOpm@3qm&A2UimVh>uTVVO|=joE@%+lX*_N$l?Gq7pbiw>jBueQ9ybcq_q zmcm9k|2ncdT}X6?b2m%`a+nb-Ye%@HK>;h8qoO(K$JaipkW`E#~plP@Ja(uxN< z)SKcI!AxNIl}jydSV?b}t3XF5eTxeAq{WuVlxbdWyWc)(d0Ay(f={>EC}>+xF%#-%Goa`C05>O(Pd&!f>5#{H~!2(oX_s3b+-YVXzCdD)^49#5C~>jbOf z7#7FwvI;q&jB;82d`QDIR&}oaCC%nCPOmlh=P*exEHyYcLJF;mQYpSQGQGrW-ue2Bx)5pmP_ zoaXeAE+@tp667C(N)?!^2Kynel0rp2)l@rRGr!x-=C8BC7apL4RjMC>cP4kUjLrW2 zJREUtmEU;&5GAw@XFkZO4jxB63WKFBdgsq0$0l1Mve}zDK?MqY2c8U1wCLx_iTplT zvs)#DthkHXfTpa>#6_5Q;jBfjAsYOp`(~yp9dy?XMQ04W$Wk6=Gn>c#Sl8avKScEU~?T)Zo1Oz+TF618-_<;d#n?ikCmPY+Cd3B&Oa!dNxYVTbxsb$hWF zwewPOzc@aI7XSF_vf#s|_@bd^WI`xfN6t1o5jM!Thbjscy8GFGqG_nyn=(?wXgfM9 zbyfzXIU?`NNJjPTptzfyaxeOcExEG3B9g#A%iL{isY$8YFr+ueTYRV3lA6|TmPV47 zayyDsrR73UVSY%ipJ443eaAabg>v}N7z4=zhslzof!xXuS(AEsWlnBxCL7^O!g4dL z6lvSqa4bxAChX%rJ zPTM*~q$BUee2l*}F6f94jq6#*n2k$pHt5*TIrJVbR?nc@0&X7TA~)gV^5xAu4;D zM8(Ig#zF0;kzwVWSx%ehR@d;%QERM?>~Oy6mFOnmFw;Pu1@P!Srf22sH0ne7kJtS> zttq?aqn-vZDil}i3!CchlDX%g9QzB90!6C!EKrg%|E$_ndez45yAz7dcJ?RP>dI;G z`TMqySW}i`7jFaKmox1_?GM_5xV}|?j;rU%GIxN}d+liyXOV3!KfiUh!SMH9Yw`vB z7LBuF+Y{E+I?8(B82!gWZiu2d%)9A zsALxjlR&ZrJ7&$;)P)5Vr(naJGClXlq->d9ldQ0bfP=4N->=$22Vyz&+I!~qeeFIK z%}xdex^aV+sYphp0h3K6-FB6Fp$#^x_ z2H{lYJj#LDEh@EgSE>$*BWj>Ve#v|YZQhaGb5Av2VSu<8lZr9L<~&&Xhlo$+!0-ap z)QZYt$)Cg6mDykF9ydofoy9`z`=B~ZUrMZ`@&i?=F*Z=Vy07Q0mlcBzDD&${u4KQ} z!Wy%=W`7)wup3)x*vLM5d&{Cwz8_S|4FK5i!?`v1NP0PjbALQxdLeM5w^*~$ukQ3s zRm;@b*gurBP#OL9EDn~WJw;+TcYVjMd54$rj`tkrYwX`WD}Ip&DYM#76;>_YPgpPw z^Jbxj+XYJfrdL-CrEL5Hskvp#>$^+!bn;k65~80S8oOk^uzFr-*?`F*1BC9{VgL^E z!TA`EZL3zMj`W9nN>RQ>kx4716j*y@`)VOSg{_9hk3L@^Is^pQ*#^HN7KU~D;^7iufHvj#<+4V2Qq1)x#?LSwyy(X7`f2`n% zmzggi-#Dmh6{nF=A(IYRO=$^dH|Ugl@^k=*wo_l(8V%9~*huIImIn|NLVKmOaGe0O zvmH-GVq_CAau+|Pm5k=ii^v4(f@hQ%hTU|gbk)&1u}Zp1&{$5~KMX+x|D`$w(7~$c z0;mD_LDsOCU_2WDh}DHo5y~X&J4FU(5d5utvtO?BkAM+c|1$vLKN!aFzrp{CJeWJm zDdl$pC`)*+P*M_vc7v*`X988gSQU6#IWP=^15kqi0AMfxO$1~6_XdEVfCsvu70Jnq zKGio+g6|P`6-eTq0SFTC;0!`3SOywT1jbGRpf>-g1^}Q#r2m0xK9Lp>5p9W6h0)Pz zD8vpUzY+r?2ziB#ipGxJvDg69*znoA<2IlC{4@NO-SH)Ny{6c?m0i2&3mSnCer#|p zBq8Eoz9`Xe?84E@ou2xbo6dG1Nhm2A7Rn4|ATvz&=`)(qtA5;9XJeb~D^tHYGDZm< zIZ)#Jn^rB2a%g=@<=-}zGAG-?a->$c73V_6#7yh?eC{sc13SL^zk9CJ>IHDYLNUi( zqHv&mt(zjayUFmc6&-!Dz!#QB#rr4s-f&AN^j6O~@_Y4U&fax&ZkIUfN{|x1n+&5n zRkbUZq-IsP2oU9@6E1(AUQgD-wpy^H_moRa-A3VDVm@jtq8iandayki+>O*Dh^N4y z7q5*|ih$srjL`w!sMzu_wZxTEm4%jM%|3-HTRELnpQ)*-xAJ51{u$4Hy_ z*zrROmHp>SD^vSR%Cz>KWB-_Jg^iau5+nvCq;IL}S5ty?VJSOvUol+Ie%7o)iiDKj zeg1>HF5U;qz4F?a5Zsqj^RxJQwzLsJNXTo82rRvt%;w10&3QCtLAgewqxdj{MFLdm zYM)kPz_g*>~>zMgdW|97`R`c2k*?`*VJy95AROJ3ya(r?qyV{%dr|tKu z2Q(lj1-UBDzy16JZAt`YEbWu z5G1m*uU39XW?COZ?Q4KAxtwmYT{jEt-pG9^x2%CT5WGpC+83cRa`}1j*L1=)_Fzm; zLd+;te6p-Vrq4|&gI>$NOHWuseteL~AqHvfT_S8B(P>M_E0{|uMNm0rP)-&w2>eZ;s7ZMhM)iIQ$rV7H;QW);=Z%uI1Q932DAGKCDj3sjSn3i{T@Te0dx9Y?DecG2 zY%Upc8*!Ai%e=huO;;`t}DoI5z z5fPEnI7Sxn1sjYfi+FqRnE(ivJi8>vynCP(9ieYhYqvC_m&%J-`8sfU@1k0U3Dcw+ zU?V_?70;V;R|pt67%2R`KrsR|N?*&gMzI0dX-|k<817D;=h0@oMs!+#5qUF@KLjxHWpWG_`_NaLBFg3r4jQQLtNTr~781>!K(B|Ex%Q;yXRe{iH zLPlAri7I}L==Tj(v~q~tfjW>MKJU$i#K=d2<%>U0M~Bog81JlqvWtHt4PW`{9@E0k zkGc3BKHm8;c`NwgCP!5e@h3yVv~Y-^vwv39uyd_pRGw_*#q{1Zw7TY9MPr?F;^8{k zf7;MLsk=!GxwNs#Y0VW~8*Xbgyt&}d_9)zA8AVGPd)t2d zZ2~h*1TWIKH)MlLLC(Zy48z#7tHrhoz`arQIFpewbX#|DKA|aIy>T~5hb}C#1btf@ z7uuv!o%ojMdds|pUO#wscxY@#s8UlfU^QeeP^v*C*vzoY6I99wZ)70|kdwjGtq|Qg zqLe$MtnxxNI%0Et>c91FD&Xg}%|}$*Epuvyr5<_+m0o}Mz_zNB`tEXVT9+EBj(U6w zji-~z=NMi3yntVPs!$NkKk+S&!lTy#Y&|~s3^L4?*>o{kNLw|Z(j$t)L(ot8D1h2f zDacE7QNGjL#y92?7yLik;{)|Sk=|MHH??&8985q5!j diff --git a/worlds/lufia2ac/docs/en_Lufia II Ancient Cave.md b/worlds/lufia2ac/docs/en_Lufia II Ancient Cave.md index 849a9f9c9d..d24c4ef9f9 100644 --- a/worlds/lufia2ac/docs/en_Lufia II Ancient Cave.md +++ b/worlds/lufia2ac/docs/en_Lufia II Ancient Cave.md @@ -76,7 +76,7 @@ Your Party Leader will hold up the item they received when not in a fight or in ###### Bug fixes: -- Vanilla game bugs that could result in softlocks or save file corruption have been fixed +- Vanilla game bugs that could result in anomalous floors, softlocks, or save file corruption have been fixed - (optional) Bugfix for the algorithm that determines the item pool for red chest gear. Enabling this allows the cave to generate shields, headgear, rings, and jewels in red chests even after floor B9 - (optional) Bugfix for the outlandish cravings of capsule monsters in the US version. Enabling this makes feeding work From 12c73acb20cccfef53d1d205dbf6b8e8d70c1890 Mon Sep 17 00:00:00 2001 From: Justus Lind Date: Tue, 24 Oct 2023 06:39:37 +1000 Subject: [PATCH 143/144] Muse Dash: Make which .net to download more explicit in setup guides. (#2328) --- worlds/musedash/docs/setup_en.md | 4 ++-- worlds/musedash/docs/setup_es.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/worlds/musedash/docs/setup_en.md b/worlds/musedash/docs/setup_en.md index 1ab61ff22a..ebf165c7dd 100644 --- a/worlds/musedash/docs/setup_en.md +++ b/worlds/musedash/docs/setup_en.md @@ -8,10 +8,10 @@ - Windows 8 or Newer. - Muse Dash: [Available on Steam](https://store.steampowered.com/app/774171/Muse_Dash/) - - \[Optional\] [Just as Planned] DLC: [Also Available on Steam](https://store.steampowered.com/app/1055810/Muse_Dash__Just_as_planned/) + - \[Optional\] [Muse Plus] DLC: [Also Available on Steam](https://store.steampowered.com/app/2593750/Muse_Dash__Muse_Plus/) - Melon Loader: [GitHub](https://github.com/LavaGang/MelonLoader/releases/latest) - .Net Framework 4.8 may be needed for the installer: [Download](https://dotnet.microsoft.com/en-us/download/dotnet-framework/net48) -- .Net 6.0 (If not already installed): [Download](https://dotnet.microsoft.com/en-us/download/dotnet/6.0#runtime-6.0.15) +- .NET Desktop Runtime 6.0.XX (If not already installed): [Download](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) - Muse Dash Archipelago Mod: [GitHub](https://github.com/DeamonHunter/ArchipelagoMuseDash/releases/latest) ## Installing the Archipelago mod to Muse Dash diff --git a/worlds/musedash/docs/setup_es.md b/worlds/musedash/docs/setup_es.md index 21fc69e7eb..0d737c26d7 100644 --- a/worlds/musedash/docs/setup_es.md +++ b/worlds/musedash/docs/setup_es.md @@ -8,10 +8,10 @@ - Windows 8 o más reciente. - Muse Dash: [Disponible en Steam](https://store.steampowered.com/app/774171/Muse_Dash/) - - \[Opcional\] [Just as Planned] DLC: [tambien disponible on Steam](https://store.steampowered.com/app/1055810/Muse_Dash__Just_as_planned/) + - \[Opcional\] [Muse Plus] DLC: [tambien disponible on Steam](https://store.steampowered.com/app/2593750/Muse_Dash__Muse_Plus/) - Melon Loader: [GitHub](https://github.com/LavaGang/MelonLoader/releases/latest) - - .Net Framework 4.8 podría ser necesario para el instalador: [Descarga](https://dotnet.microsoft.com/en-us/download/dotnet-framework/net48) -- .Net 6.0 (si aún no está instalado): [Descarga](https://dotnet.microsoft.com/en-us/download/dotnet/6.0#runtime-6.0.15) + - .Net Framework 4.8 podría ser necesario para el instalador: [Descarga](https://dotnet.microsoft.com/es-es/download/dotnet-framework/net48) +- Entorno de ejecución de escritorio de .NET 6.0.XX (si aún no está instalado): [Descarga](https://dotnet.microsoft.com/es-es/download/dotnet/6.0) - Muse Dash Archipelago Mod: [GitHub](https://github.com/DeamonHunter/ArchipelagoMuseDash/releases/latest) ## Instalar el mod de Archipelago en Muse Dash From 764128568e0411a032179f7a8515baa006bdc51f Mon Sep 17 00:00:00 2001 From: Aaron Wagener Date: Mon, 23 Oct 2023 19:20:08 -0500 Subject: [PATCH 144/144] WebHost: consistent naming for player options (#2037) * WebHost: unify references to options * it was just an extra s the whole time... * grammar * redirect from old pages * redirect stuff correctly * use url_for * use " for modified strings * remove redirect cache * player_settings * update site map --- Generate.py | 2 +- WebHostLib/misc.py | 28 ++- WebHostLib/options.py | 34 ++-- .../{player-settings.js => player-options.js} | 180 +++++++++--------- ...ighted-settings.js => weighted-options.js} | 2 +- ...player-settings.css => player-options.css} | 68 +++---- ...hted-settings.css => weighted-options.css} | 0 ...ayer-settings.html => player-options.html} | 20 +- WebHostLib/templates/siteMap.html | 6 +- WebHostLib/templates/supportedGames.html | 8 +- ...ed-settings.html => weighted-options.html} | 14 +- worlds/AutoWorld.py | 2 +- worlds/bk_sudoku/__init__.py | 2 +- worlds/ff1/__init__.py | 2 +- 14 files changed, 190 insertions(+), 178 deletions(-) rename WebHostLib/static/assets/{player-settings.js => player-options.js} (63%) rename WebHostLib/static/assets/{weighted-settings.js => weighted-options.js} (99%) rename WebHostLib/static/styles/{player-settings.css => player-options.css} (67%) rename WebHostLib/static/styles/{weighted-settings.css => weighted-options.css} (100%) rename WebHostLib/templates/{player-settings.html => player-options.html} (75%) rename WebHostLib/templates/{weighted-settings.html => weighted-options.html} (82%) diff --git a/Generate.py b/Generate.py index 08fe2b9083..34a0084e8d 100644 --- a/Generate.py +++ b/Generate.py @@ -169,7 +169,7 @@ def main(args=None, callback=ERmain): for player in range(1, args.multi + 1): player_path_cache[player] = player_files.get(player, args.weights_file_path) name_counter = Counter() - erargs.player_settings = {} + erargs.player_options = {} player = 1 while player <= args.multi: diff --git a/WebHostLib/misc.py b/WebHostLib/misc.py index e3111ed5b5..ee04e56fd7 100644 --- a/WebHostLib/misc.py +++ b/WebHostLib/misc.py @@ -37,17 +37,29 @@ def start_playing(): return render_template(f"startPlaying.html") -@app.route('/weighted-settings') -@cache.cached() +# TODO for back compat. remove around 0.4.5 +@app.route("/weighted-settings") def weighted_settings(): - return render_template(f"weighted-settings.html") + return redirect("weighted-options", 301) -# Player settings pages -@app.route('/games//player-settings') +@app.route("/weighted-options") @cache.cached() -def player_settings(game): - return render_template(f"player-settings.html", game=game, theme=get_world_theme(game)) +def weighted_options(): + return render_template("weighted-options.html") + + +# TODO for back compat. remove around 0.4.5 +@app.route("/games//player-settings") +def player_settings(game: str): + return redirect(url_for("player_options", game=game), 301) + + +# Player options pages +@app.route("/games//player-options") +@cache.cached() +def player_options(game: str): + return render_template("player-options.html", game=game, theme=get_world_theme(game)) # Game Info Pages @@ -181,6 +193,6 @@ def get_sitemap(): available_games: List[Dict[str, Union[str, bool]]] = [] for game, world in AutoWorldRegister.world_types.items(): if not world.hidden: - has_settings: bool = isinstance(world.web.settings_page, bool) and world.web.settings_page + has_settings: bool = isinstance(world.web.options_page, bool) and world.web.options_page available_games.append({ 'title': game, 'has_settings': has_settings }) return render_template("siteMap.html", games=available_games) diff --git a/WebHostLib/options.py b/WebHostLib/options.py index 18a28045ee..785785cde0 100644 --- a/WebHostLib/options.py +++ b/WebHostLib/options.py @@ -25,7 +25,7 @@ def create(): return "Please document me!" return "\n".join(line.strip() for line in option_type.__doc__.split("\n")).strip() - weighted_settings = { + weighted_options = { "baseOptions": { "description": "Generated by https://archipelago.gg/", "name": "Player", @@ -38,8 +38,8 @@ def create(): all_options: typing.Dict[str, Options.AssembleOptions] = world.options_dataclass.type_hints - # Generate JSON files for player-settings pages - player_settings = { + # Generate JSON files for player-options pages + player_options = { "baseOptions": { "description": f"Generated by https://archipelago.gg/ for {game_name}", "game": game_name, @@ -117,17 +117,17 @@ def create(): } else: - logging.debug(f"{option} not exported to Web Settings.") + logging.debug(f"{option} not exported to Web options.") - player_settings["gameOptions"] = game_options + player_options["gameOptions"] = game_options - os.makedirs(os.path.join(target_folder, 'player-settings'), exist_ok=True) + os.makedirs(os.path.join(target_folder, 'player-options'), exist_ok=True) - with open(os.path.join(target_folder, 'player-settings', game_name + ".json"), "w") as f: - json.dump(player_settings, f, indent=2, separators=(',', ': ')) + with open(os.path.join(target_folder, 'player-options', game_name + ".json"), "w") as f: + json.dump(player_options, f, indent=2, separators=(',', ': ')) - if not world.hidden and world.web.settings_page is True: - # Add the random option to Choice, TextChoice, and Toggle settings + if not world.hidden and world.web.options_page is True: + # Add the random option to Choice, TextChoice, and Toggle options for option in game_options.values(): if option["type"] == "select": option["options"].append({"name": "Random", "value": "random"}) @@ -135,11 +135,11 @@ def create(): if not option["defaultValue"]: option["defaultValue"] = "random" - weighted_settings["baseOptions"]["game"][game_name] = 0 - weighted_settings["games"][game_name] = {} - weighted_settings["games"][game_name]["gameSettings"] = game_options - weighted_settings["games"][game_name]["gameItems"] = tuple(world.item_names) - weighted_settings["games"][game_name]["gameLocations"] = tuple(world.location_names) + weighted_options["baseOptions"]["game"][game_name] = 0 + weighted_options["games"][game_name] = {} + weighted_options["games"][game_name]["gameSettings"] = game_options + weighted_options["games"][game_name]["gameItems"] = tuple(world.item_names) + weighted_options["games"][game_name]["gameLocations"] = tuple(world.location_names) - with open(os.path.join(target_folder, 'weighted-settings.json'), "w") as f: - json.dump(weighted_settings, f, indent=2, separators=(',', ': ')) + with open(os.path.join(target_folder, 'weighted-options.json'), "w") as f: + json.dump(weighted_options, f, indent=2, separators=(',', ': ')) diff --git a/WebHostLib/static/assets/player-settings.js b/WebHostLib/static/assets/player-options.js similarity index 63% rename from WebHostLib/static/assets/player-settings.js rename to WebHostLib/static/assets/player-options.js index 4ebec1adbf..727e0f63b9 100644 --- a/WebHostLib/static/assets/player-settings.js +++ b/WebHostLib/static/assets/player-options.js @@ -1,41 +1,41 @@ let gameName = null; window.addEventListener('load', () => { - gameName = document.getElementById('player-settings').getAttribute('data-game'); + gameName = document.getElementById('player-options').getAttribute('data-game'); // Update game name on page document.getElementById('game-name').innerText = gameName; - fetchSettingData().then((results) => { - let settingHash = localStorage.getItem(`${gameName}-hash`); - if (!settingHash) { + fetchOptionData().then((results) => { + let optionHash = localStorage.getItem(`${gameName}-hash`); + if (!optionHash) { // If no hash data has been set before, set it now - settingHash = md5(JSON.stringify(results)); - localStorage.setItem(`${gameName}-hash`, settingHash); + optionHash = md5(JSON.stringify(results)); + localStorage.setItem(`${gameName}-hash`, optionHash); localStorage.removeItem(gameName); } - if (settingHash !== md5(JSON.stringify(results))) { - showUserMessage("Your settings are out of date! Click here to update them! Be aware this will reset " + + if (optionHash !== md5(JSON.stringify(results))) { + showUserMessage("Your options are out of date! Click here to update them! Be aware this will reset " + "them all to default."); - document.getElementById('user-message').addEventListener('click', resetSettings); + document.getElementById('user-message').addEventListener('click', resetOptions); } // Page setup - createDefaultSettings(results); + createDefaultOptions(results); buildUI(results); adjustHeaderWidth(); // Event listeners - document.getElementById('export-settings').addEventListener('click', () => exportSettings()); + document.getElementById('export-options').addEventListener('click', () => exportOptions()); document.getElementById('generate-race').addEventListener('click', () => generateGame(true)); document.getElementById('generate-game').addEventListener('click', () => generateGame()); // Name input field - const playerSettings = JSON.parse(localStorage.getItem(gameName)); + const playerOptions = JSON.parse(localStorage.getItem(gameName)); const nameInput = document.getElementById('player-name'); - nameInput.addEventListener('keyup', (event) => updateBaseSetting(event)); - nameInput.value = playerSettings.name; + nameInput.addEventListener('keyup', (event) => updateBaseOption(event)); + nameInput.value = playerOptions.name; }).catch((e) => { console.error(e); const url = new URL(window.location.href); @@ -43,13 +43,13 @@ window.addEventListener('load', () => { }) }); -const resetSettings = () => { +const resetOptions = () => { localStorage.removeItem(gameName); localStorage.removeItem(`${gameName}-hash`) window.location.reload(); }; -const fetchSettingData = () => new Promise((resolve, reject) => { +const fetchOptionData = () => new Promise((resolve, reject) => { const ajax = new XMLHttpRequest(); ajax.onreadystatechange = () => { if (ajax.readyState !== 4) { return; } @@ -60,54 +60,54 @@ const fetchSettingData = () => new Promise((resolve, reject) => { try{ resolve(JSON.parse(ajax.responseText)); } catch(error){ reject(error); } }; - ajax.open('GET', `${window.location.origin}/static/generated/player-settings/${gameName}.json`, true); + ajax.open('GET', `${window.location.origin}/static/generated/player-options/${gameName}.json`, true); ajax.send(); }); -const createDefaultSettings = (settingData) => { +const createDefaultOptions = (optionData) => { if (!localStorage.getItem(gameName)) { - const newSettings = { + const newOptions = { [gameName]: {}, }; - for (let baseOption of Object.keys(settingData.baseOptions)){ - newSettings[baseOption] = settingData.baseOptions[baseOption]; + for (let baseOption of Object.keys(optionData.baseOptions)){ + newOptions[baseOption] = optionData.baseOptions[baseOption]; } - for (let gameOption of Object.keys(settingData.gameOptions)){ - newSettings[gameName][gameOption] = settingData.gameOptions[gameOption].defaultValue; + for (let gameOption of Object.keys(optionData.gameOptions)){ + newOptions[gameName][gameOption] = optionData.gameOptions[gameOption].defaultValue; } - localStorage.setItem(gameName, JSON.stringify(newSettings)); + localStorage.setItem(gameName, JSON.stringify(newOptions)); } }; -const buildUI = (settingData) => { +const buildUI = (optionData) => { // Game Options const leftGameOpts = {}; const rightGameOpts = {}; - Object.keys(settingData.gameOptions).forEach((key, index) => { - if (index < Object.keys(settingData.gameOptions).length / 2) { leftGameOpts[key] = settingData.gameOptions[key]; } - else { rightGameOpts[key] = settingData.gameOptions[key]; } + Object.keys(optionData.gameOptions).forEach((key, index) => { + if (index < Object.keys(optionData.gameOptions).length / 2) { leftGameOpts[key] = optionData.gameOptions[key]; } + else { rightGameOpts[key] = optionData.gameOptions[key]; } }); document.getElementById('game-options-left').appendChild(buildOptionsTable(leftGameOpts)); document.getElementById('game-options-right').appendChild(buildOptionsTable(rightGameOpts)); }; -const buildOptionsTable = (settings, romOpts = false) => { - const currentSettings = JSON.parse(localStorage.getItem(gameName)); +const buildOptionsTable = (options, romOpts = false) => { + const currentOptions = JSON.parse(localStorage.getItem(gameName)); const table = document.createElement('table'); const tbody = document.createElement('tbody'); - Object.keys(settings).forEach((setting) => { + Object.keys(options).forEach((option) => { const tr = document.createElement('tr'); // td Left const tdl = document.createElement('td'); const label = document.createElement('label'); - label.textContent = `${settings[setting].displayName}: `; - label.setAttribute('for', setting); + label.textContent = `${options[option].displayName}: `; + label.setAttribute('for', option); const questionSpan = document.createElement('span'); questionSpan.classList.add('interactive'); - questionSpan.setAttribute('data-tooltip', settings[setting].description); + questionSpan.setAttribute('data-tooltip', options[option].description); questionSpan.innerText = '(?)'; label.appendChild(questionSpan); @@ -120,36 +120,36 @@ const buildOptionsTable = (settings, romOpts = false) => { const randomButton = document.createElement('button'); - switch(settings[setting].type){ + switch(options[option].type){ case 'select': element = document.createElement('div'); element.classList.add('select-container'); let select = document.createElement('select'); - select.setAttribute('id', setting); - select.setAttribute('data-key', setting); + select.setAttribute('id', option); + select.setAttribute('data-key', option); if (romOpts) { select.setAttribute('data-romOpt', '1'); } - settings[setting].options.forEach((opt) => { + options[option].options.forEach((opt) => { const option = document.createElement('option'); option.setAttribute('value', opt.value); option.innerText = opt.name; - if ((isNaN(currentSettings[gameName][setting]) && - (parseInt(opt.value, 10) === parseInt(currentSettings[gameName][setting]))) || - (opt.value === currentSettings[gameName][setting])) + if ((isNaN(currentOptions[gameName][option]) && + (parseInt(opt.value, 10) === parseInt(currentOptions[gameName][option]))) || + (opt.value === currentOptions[gameName][option])) { option.selected = true; } select.appendChild(option); }); - select.addEventListener('change', (event) => updateGameSetting(event.target)); + select.addEventListener('change', (event) => updateGameOption(event.target)); element.appendChild(select); // Randomize button randomButton.innerText = '🎲'; randomButton.classList.add('randomize-button'); - randomButton.setAttribute('data-key', setting); + randomButton.setAttribute('data-key', option); randomButton.setAttribute('data-tooltip', 'Toggle randomization for this option!'); randomButton.addEventListener('click', (event) => toggleRandomize(event, select)); - if (currentSettings[gameName][setting] === 'random') { + if (currentOptions[gameName][option] === 'random') { randomButton.classList.add('active'); select.disabled = true; } @@ -163,30 +163,30 @@ const buildOptionsTable = (settings, romOpts = false) => { let range = document.createElement('input'); range.setAttribute('type', 'range'); - range.setAttribute('data-key', setting); - range.setAttribute('min', settings[setting].min); - range.setAttribute('max', settings[setting].max); - range.value = currentSettings[gameName][setting]; + range.setAttribute('data-key', option); + range.setAttribute('min', options[option].min); + range.setAttribute('max', options[option].max); + range.value = currentOptions[gameName][option]; range.addEventListener('change', (event) => { - document.getElementById(`${setting}-value`).innerText = event.target.value; - updateGameSetting(event.target); + document.getElementById(`${option}-value`).innerText = event.target.value; + updateGameOption(event.target); }); element.appendChild(range); let rangeVal = document.createElement('span'); rangeVal.classList.add('range-value'); - rangeVal.setAttribute('id', `${setting}-value`); - rangeVal.innerText = currentSettings[gameName][setting] !== 'random' ? - currentSettings[gameName][setting] : settings[setting].defaultValue; + rangeVal.setAttribute('id', `${option}-value`); + rangeVal.innerText = currentOptions[gameName][option] !== 'random' ? + currentOptions[gameName][option] : options[option].defaultValue; element.appendChild(rangeVal); // Randomize button randomButton.innerText = '🎲'; randomButton.classList.add('randomize-button'); - randomButton.setAttribute('data-key', setting); + randomButton.setAttribute('data-key', option); randomButton.setAttribute('data-tooltip', 'Toggle randomization for this option!'); randomButton.addEventListener('click', (event) => toggleRandomize(event, range)); - if (currentSettings[gameName][setting] === 'random') { + if (currentOptions[gameName][option] === 'random') { randomButton.classList.add('active'); range.disabled = true; } @@ -200,11 +200,11 @@ const buildOptionsTable = (settings, romOpts = false) => { // Build the select element let specialRangeSelect = document.createElement('select'); - specialRangeSelect.setAttribute('data-key', setting); - Object.keys(settings[setting].value_names).forEach((presetName) => { + specialRangeSelect.setAttribute('data-key', option); + Object.keys(options[option].value_names).forEach((presetName) => { let presetOption = document.createElement('option'); presetOption.innerText = presetName; - presetOption.value = settings[setting].value_names[presetName]; + presetOption.value = options[option].value_names[presetName]; const words = presetOption.innerText.split("_"); for (let i = 0; i < words.length; i++) { words[i] = words[i][0].toUpperCase() + words[i].substring(1); @@ -217,8 +217,8 @@ const buildOptionsTable = (settings, romOpts = false) => { customOption.value = 'custom'; customOption.selected = true; specialRangeSelect.appendChild(customOption); - if (Object.values(settings[setting].value_names).includes(Number(currentSettings[gameName][setting]))) { - specialRangeSelect.value = Number(currentSettings[gameName][setting]); + if (Object.values(options[option].value_names).includes(Number(currentOptions[gameName][option]))) { + specialRangeSelect.value = Number(currentOptions[gameName][option]); } // Build range element @@ -226,17 +226,17 @@ const buildOptionsTable = (settings, romOpts = false) => { specialRangeWrapper.classList.add('special-range-wrapper'); let specialRange = document.createElement('input'); specialRange.setAttribute('type', 'range'); - specialRange.setAttribute('data-key', setting); - specialRange.setAttribute('min', settings[setting].min); - specialRange.setAttribute('max', settings[setting].max); - specialRange.value = currentSettings[gameName][setting]; + specialRange.setAttribute('data-key', option); + specialRange.setAttribute('min', options[option].min); + specialRange.setAttribute('max', options[option].max); + specialRange.value = currentOptions[gameName][option]; // Build rage value element let specialRangeVal = document.createElement('span'); specialRangeVal.classList.add('range-value'); - specialRangeVal.setAttribute('id', `${setting}-value`); - specialRangeVal.innerText = currentSettings[gameName][setting] !== 'random' ? - currentSettings[gameName][setting] : settings[setting].defaultValue; + specialRangeVal.setAttribute('id', `${option}-value`); + specialRangeVal.innerText = currentOptions[gameName][option] !== 'random' ? + currentOptions[gameName][option] : options[option].defaultValue; // Configure select event listener specialRangeSelect.addEventListener('change', (event) => { @@ -244,18 +244,18 @@ const buildOptionsTable = (settings, romOpts = false) => { // Update range slider specialRange.value = event.target.value; - document.getElementById(`${setting}-value`).innerText = event.target.value; - updateGameSetting(event.target); + document.getElementById(`${option}-value`).innerText = event.target.value; + updateGameOption(event.target); }); // Configure range event handler specialRange.addEventListener('change', (event) => { // Update select element specialRangeSelect.value = - (Object.values(settings[setting].value_names).includes(parseInt(event.target.value))) ? + (Object.values(options[option].value_names).includes(parseInt(event.target.value))) ? parseInt(event.target.value) : 'custom'; - document.getElementById(`${setting}-value`).innerText = event.target.value; - updateGameSetting(event.target); + document.getElementById(`${option}-value`).innerText = event.target.value; + updateGameOption(event.target); }); element.appendChild(specialRangeSelect); @@ -266,12 +266,12 @@ const buildOptionsTable = (settings, romOpts = false) => { // Randomize button randomButton.innerText = '🎲'; randomButton.classList.add('randomize-button'); - randomButton.setAttribute('data-key', setting); + randomButton.setAttribute('data-key', option); randomButton.setAttribute('data-tooltip', 'Toggle randomization for this option!'); randomButton.addEventListener('click', (event) => toggleRandomize( event, specialRange, specialRangeSelect) ); - if (currentSettings[gameName][setting] === 'random') { + if (currentOptions[gameName][option] === 'random') { randomButton.classList.add('active'); specialRange.disabled = true; specialRangeSelect.disabled = true; @@ -281,7 +281,7 @@ const buildOptionsTable = (settings, romOpts = false) => { break; default: - console.error(`Ignoring unknown setting type: ${settings[setting].type} with name ${setting}`); + console.error(`Ignoring unknown option type: ${options[option].type} with name ${option}`); return; } @@ -311,35 +311,35 @@ const toggleRandomize = (event, inputElement, optionalSelectElement = null) => { optionalSelectElement.disabled = true; } } - updateGameSetting(active ? inputElement : randomButton); + updateGameOption(active ? inputElement : randomButton); }; -const updateBaseSetting = (event) => { +const updateBaseOption = (event) => { const options = JSON.parse(localStorage.getItem(gameName)); options[event.target.getAttribute('data-key')] = isNaN(event.target.value) ? event.target.value : parseInt(event.target.value); localStorage.setItem(gameName, JSON.stringify(options)); }; -const updateGameSetting = (settingElement) => { +const updateGameOption = (optionElement) => { const options = JSON.parse(localStorage.getItem(gameName)); - if (settingElement.classList.contains('randomize-button')) { + if (optionElement.classList.contains('randomize-button')) { // If the event passed in is the randomize button, then we know what we must do. - options[gameName][settingElement.getAttribute('data-key')] = 'random'; + options[gameName][optionElement.getAttribute('data-key')] = 'random'; } else { - options[gameName][settingElement.getAttribute('data-key')] = isNaN(settingElement.value) ? - settingElement.value : parseInt(settingElement.value, 10); + options[gameName][optionElement.getAttribute('data-key')] = isNaN(optionElement.value) ? + optionElement.value : parseInt(optionElement.value, 10); } localStorage.setItem(gameName, JSON.stringify(options)); }; -const exportSettings = () => { - const settings = JSON.parse(localStorage.getItem(gameName)); - if (!settings.name || settings.name.toLowerCase() === 'player' || settings.name.trim().length === 0) { +const exportOptions = () => { + const options = JSON.parse(localStorage.getItem(gameName)); + if (!options.name || options.name.toLowerCase() === 'player' || options.name.trim().length === 0) { return showUserMessage('You must enter a player name!'); } - const yamlText = jsyaml.safeDump(settings, { noCompatMode: true }).replaceAll(/'(\d+)':/g, (x, y) => `${y}:`); + const yamlText = jsyaml.safeDump(options, { noCompatMode: true }).replaceAll(/'(\d+)':/g, (x, y) => `${y}:`); download(`${document.getElementById('player-name').value}.yaml`, yamlText); }; @@ -355,14 +355,14 @@ const download = (filename, text) => { }; const generateGame = (raceMode = false) => { - const settings = JSON.parse(localStorage.getItem(gameName)); - if (!settings.name || settings.name.toLowerCase() === 'player' || settings.name.trim().length === 0) { + const options = JSON.parse(localStorage.getItem(gameName)); + if (!options.name || options.name.toLowerCase() === 'player' || options.name.trim().length === 0) { return showUserMessage('You must enter a player name!'); } axios.post('/api/generate', { - weights: { player: settings }, - presetData: { player: settings }, + weights: { player: options }, + presetData: { player: options }, playerCount: 1, spoiler: 3, race: raceMode ? '1' : '0', diff --git a/WebHostLib/static/assets/weighted-settings.js b/WebHostLib/static/assets/weighted-options.js similarity index 99% rename from WebHostLib/static/assets/weighted-settings.js rename to WebHostLib/static/assets/weighted-options.js index 2cd61d2e6e..bdd121eff5 100644 --- a/WebHostLib/static/assets/weighted-settings.js +++ b/WebHostLib/static/assets/weighted-options.js @@ -23,7 +23,7 @@ window.addEventListener('load', () => { adjustHeaderWidth(); // Event listeners - document.getElementById('export-settings').addEventListener('click', () => settings.export()); + document.getElementById('export-options').addEventListener('click', () => settings.export()); document.getElementById('generate-race').addEventListener('click', () => settings.generateGame(true)); document.getElementById('generate-game').addEventListener('click', () => settings.generateGame()); diff --git a/WebHostLib/static/styles/player-settings.css b/WebHostLib/static/styles/player-options.css similarity index 67% rename from WebHostLib/static/styles/player-settings.css rename to WebHostLib/static/styles/player-options.css index e6e0c29292..2f5481d285 100644 --- a/WebHostLib/static/styles/player-settings.css +++ b/WebHostLib/static/styles/player-options.css @@ -4,7 +4,7 @@ html{ background-size: 650px 650px; } -#player-settings{ +#player-options{ box-sizing: border-box; max-width: 1024px; margin-left: auto; @@ -15,14 +15,14 @@ html{ color: #eeffeb; } -#player-settings #player-settings-button-row{ +#player-options #player-options-button-row{ display: flex; flex-direction: row; justify-content: space-between; margin-top: 15px; } -#player-settings code{ +#player-options code{ background-color: #d9cd8e; border-radius: 4px; padding-left: 0.25rem; @@ -30,7 +30,7 @@ html{ color: #000000; } -#player-settings #user-message{ +#player-options #user-message{ display: none; width: calc(100% - 8px); background-color: #ffe86b; @@ -40,12 +40,12 @@ html{ text-align: center; } -#player-settings #user-message.visible{ +#player-options #user-message.visible{ display: block; cursor: pointer; } -#player-settings h1{ +#player-options h1{ font-size: 2.5rem; font-weight: normal; width: 100%; @@ -53,7 +53,7 @@ html{ text-shadow: 1px 1px 4px #000000; } -#player-settings h2{ +#player-options h2{ font-size: 40px; font-weight: normal; width: 100%; @@ -62,22 +62,22 @@ html{ text-shadow: 1px 1px 2px #000000; } -#player-settings h3, #player-settings h4, #player-settings h5, #player-settings h6{ +#player-options h3, #player-options h4, #player-options h5, #player-options h6{ text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5); } -#player-settings input:not([type]){ +#player-options input:not([type]){ border: 1px solid #000000; padding: 3px; border-radius: 3px; min-width: 150px; } -#player-settings input:not([type]):focus{ +#player-options input:not([type]):focus{ border: 1px solid #ffffff; } -#player-settings select{ +#player-options select{ border: 1px solid #000000; padding: 3px; border-radius: 3px; @@ -85,72 +85,72 @@ html{ background-color: #ffffff; } -#player-settings #game-options, #player-settings #rom-options{ +#player-options #game-options, #player-options #rom-options{ display: flex; flex-direction: row; } -#player-settings .left, #player-settings .right{ +#player-options .left, #player-options .right{ flex-grow: 1; } -#player-settings .left{ +#player-options .left{ margin-right: 10px; } -#player-settings .right{ +#player-options .right{ margin-left: 10px; } -#player-settings table{ +#player-options table{ margin-bottom: 30px; width: 100%; } -#player-settings table .select-container{ +#player-options table .select-container{ display: flex; flex-direction: row; } -#player-settings table .select-container select{ +#player-options table .select-container select{ min-width: 200px; flex-grow: 1; } -#player-settings table select:disabled{ +#player-options table select:disabled{ background-color: lightgray; } -#player-settings table .range-container{ +#player-options table .range-container{ display: flex; flex-direction: row; } -#player-settings table .range-container input[type=range]{ +#player-options table .range-container input[type=range]{ flex-grow: 1; } -#player-settings table .range-value{ +#player-options table .range-value{ min-width: 20px; margin-left: 0.25rem; } -#player-settings table .special-range-container{ +#player-options table .special-range-container{ display: flex; flex-direction: column; } -#player-settings table .special-range-wrapper{ +#player-options table .special-range-wrapper{ display: flex; flex-direction: row; margin-top: 0.25rem; } -#player-settings table .special-range-wrapper input[type=range]{ +#player-options table .special-range-wrapper input[type=range]{ flex-grow: 1; } -#player-settings table .randomize-button { +#player-options table .randomize-button { max-height: 24px; line-height: 16px; padding: 2px 8px; @@ -160,23 +160,23 @@ html{ border-radius: 3px; } -#player-settings table .randomize-button.active { +#player-options table .randomize-button.active { background-color: #ffef00; /* Same as .interactive in globalStyles.css */ } -#player-settings table .randomize-button[data-tooltip]::after { +#player-options table .randomize-button[data-tooltip]::after { left: unset; right: 0; } -#player-settings table label{ +#player-options table label{ display: block; min-width: 200px; margin-right: 4px; cursor: default; } -#player-settings th, #player-settings td{ +#player-options th, #player-options td{ border: none; padding: 3px; font-size: 17px; @@ -184,17 +184,17 @@ html{ } @media all and (max-width: 1024px) { - #player-settings { + #player-options { border-radius: 0; } - #player-settings #game-options{ + #player-options #game-options{ justify-content: flex-start; flex-wrap: wrap; } - #player-settings .left, - #player-settings .right { + #player-options .left, + #player-options .right { margin: 0; } diff --git a/WebHostLib/static/styles/weighted-settings.css b/WebHostLib/static/styles/weighted-options.css similarity index 100% rename from WebHostLib/static/styles/weighted-settings.css rename to WebHostLib/static/styles/weighted-options.css diff --git a/WebHostLib/templates/player-settings.html b/WebHostLib/templates/player-options.html similarity index 75% rename from WebHostLib/templates/player-settings.html rename to WebHostLib/templates/player-options.html index 50b9e3cbb1..701b4e5861 100644 --- a/WebHostLib/templates/player-settings.html +++ b/WebHostLib/templates/player-options.html @@ -1,26 +1,26 @@ {% extends 'pageWrapper.html' %} {% block head %} - {{ game }} Settings + {{ game }} Options - + - + {% endblock %} {% block body %} {% include 'header/'+theme+'Header.html' %} -
    + -
    - +
    +
    diff --git a/WebHostLib/templates/siteMap.html b/WebHostLib/templates/siteMap.html index 562dd3b71b..231ec83e24 100644 --- a/WebHostLib/templates/siteMap.html +++ b/WebHostLib/templates/siteMap.html @@ -24,7 +24,7 @@
  • Supported Games Page
  • Tutorials Page
  • User Content
  • -
  • Weighted Settings Page
  • +
  • Weighted Options Page
  • Game Statistics
  • Glossary
  • @@ -46,11 +46,11 @@ {% endfor %} -

    Game Settings Pages

    +

    Game Options Pages

    diff --git a/WebHostLib/templates/supportedGames.html b/WebHostLib/templates/supportedGames.html index f1514d8353..3252b16ad4 100644 --- a/WebHostLib/templates/supportedGames.html +++ b/WebHostLib/templates/supportedGames.html @@ -51,12 +51,12 @@ | Setup Guides {% endif %} - {% if world.web.settings_page is string %} + {% if world.web.options_page is string %} | - Settings Page - {% elif world.web.settings_page %} + Options Page + {% elif world.web.options_page %} | - Settings Page + Options Page {% endif %} {% if world.web.bug_report_page %} | diff --git a/WebHostLib/templates/weighted-settings.html b/WebHostLib/templates/weighted-options.html similarity index 82% rename from WebHostLib/templates/weighted-settings.html rename to WebHostLib/templates/weighted-options.html index 9ce097c37f..032a4eeb90 100644 --- a/WebHostLib/templates/weighted-settings.html +++ b/WebHostLib/templates/weighted-options.html @@ -1,26 +1,26 @@ {% extends 'pageWrapper.html' %} {% block head %} - {{ game }} Settings + {{ game }} Options - + - + {% endblock %} {% block body %} {% include 'header/grassHeader.html' %}
    -

    Weighted Settings

    -

    Weighted Settings allows you to choose how likely a particular option is to be used in game generation. +

    Weighted Options

    +

    Weighted options allow you to choose how likely a particular option is to be used in game generation. The higher an option is weighted, the more likely the option will be chosen. Think of them like entries in a raffle.

    Choose the games and options you would like to play with! You may generate a single-player game from - this page, or download a settings file you can use to participate in a MultiWorld.

    + this page, or download an options file you can use to participate in a MultiWorld.

    A list of all games you have generated can be found on the User Content page.

    @@ -40,7 +40,7 @@
    - +
    diff --git a/worlds/AutoWorld.py b/worlds/AutoWorld.py index 9a8b6a56ef..d4fe0f49a2 100644 --- a/worlds/AutoWorld.py +++ b/worlds/AutoWorld.py @@ -149,7 +149,7 @@ def call_stage(multiworld: "MultiWorld", method_name: str, *args: Any) -> None: class WebWorld: """Webhost integration""" - settings_page: Union[bool, str] = True + options_page: Union[bool, str] = True """display a settings page. Can be a link to a specific page or external tool.""" game_info_languages: List[str] = ['en'] diff --git a/worlds/bk_sudoku/__init__.py b/worlds/bk_sudoku/__init__.py index f914baf066..36d863bb44 100644 --- a/worlds/bk_sudoku/__init__.py +++ b/worlds/bk_sudoku/__init__.py @@ -5,7 +5,7 @@ from ..AutoWorld import WebWorld, World class Bk_SudokuWebWorld(WebWorld): - settings_page = "games/Sudoku/info/en" + options_page = "games/Sudoku/info/en" theme = 'partyTime' tutorials = [ Tutorial( diff --git a/worlds/ff1/__init__.py b/worlds/ff1/__init__.py index 432467399e..16905cc6da 100644 --- a/worlds/ff1/__init__.py +++ b/worlds/ff1/__init__.py @@ -14,7 +14,7 @@ class FF1Settings(settings.Group): class FF1Web(WebWorld): - settings_page = "https://finalfantasyrandomizer.com/" + options_page = "https://finalfantasyrandomizer.com/" tutorials = [Tutorial( "Multiworld Setup Guide", "A guide to playing Final Fantasy multiworld. This guide only covers playing multiworld.",