From 1ae0a9b76f994bd4fc8a7e5e7e50856b7bd58d5a Mon Sep 17 00:00:00 2001 From: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> Date: Wed, 22 May 2024 19:58:06 -0400 Subject: [PATCH 01/12] WebHost: Fixing default values for LocationSets (#3374) * Update macros.html * Update macros.html --- WebHostLib/templates/playerOptions/macros.html | 2 +- WebHostLib/templates/weightedOptions/macros.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/WebHostLib/templates/playerOptions/macros.html b/WebHostLib/templates/playerOptions/macros.html index c4d97255d85e..b34ac79a029e 100644 --- a/WebHostLib/templates/playerOptions/macros.html +++ b/WebHostLib/templates/playerOptions/macros.html @@ -141,7 +141,7 @@ {% for group_name in world.location_name_groups.keys()|sort %} {% if group_name != "Everywhere" %}
- +
{% endif %} diff --git a/WebHostLib/templates/weightedOptions/macros.html b/WebHostLib/templates/weightedOptions/macros.html index 91474d76960e..5b8944a43887 100644 --- a/WebHostLib/templates/weightedOptions/macros.html +++ b/WebHostLib/templates/weightedOptions/macros.html @@ -142,7 +142,7 @@ {% for group_name in world.location_name_groups.keys()|sort %} {% if group_name != "Everywhere" %}
- +
{% endif %} From b4fec93c820eeb7d093821fc5f58740f7afdba41 Mon Sep 17 00:00:00 2001 From: Scipio Wright Date: Wed, 22 May 2024 20:00:06 -0400 Subject: [PATCH 02/12] Update guide with some linux instructions (#3330) --- worlds/tunic/docs/setup_en.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/worlds/tunic/docs/setup_en.md b/worlds/tunic/docs/setup_en.md index 94a8a0384191..58cc1bcf25f2 100644 --- a/worlds/tunic/docs/setup_en.md +++ b/worlds/tunic/docs/setup_en.md @@ -31,6 +31,8 @@ Download [BepInEx](https://github.com/BepInEx/BepInEx/releases/download/v6.0.0-p If playing on Steam Deck, follow this [guide to set up BepInEx via Proton](https://docs.bepinex.dev/articles/advanced/proton_wine.html). +If playing on Linux, you may be able to add `WINEDLLOVERRIDES="winhttp=n,b" %command%` to your Steam launch options. If this does not work, follow the guide for Steam Deck above. + Extract the contents of the BepInEx .zip file into your TUNIC game directory:
- **Steam**: Steam\steamapps\common\TUNIC
- **PC Game Pass**: XboxGames\Tunic\Content
From 93f63a3e31fb51a97e931ce36d528405478e5b95 Mon Sep 17 00:00:00 2001 From: Bryce Wilson Date: Wed, 22 May 2024 17:01:27 -0700 Subject: [PATCH 03/12] Pokemon Emerald: Fix broken Markdown in spanish setup guide (#3320) * Pokemon Emerald: Fix broken Markdown in spanish setup guide * Pokemon Emerald: Minor formatting in spanish setup guide * oops --- worlds/pokemon_emerald/docs/setup_es.md | 36 ++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/worlds/pokemon_emerald/docs/setup_es.md b/worlds/pokemon_emerald/docs/setup_es.md index 28c3a4a01a65..1d3721862a4f 100644 --- a/worlds/pokemon_emerald/docs/setup_es.md +++ b/worlds/pokemon_emerald/docs/setup_es.md @@ -14,51 +14,51 @@ Una vez que hayas instalado BizHawk, abre `EmuHawk.exe` y cambia las siguientes `NLua+KopiLua` a `Lua+LuaInterface`, luego reinicia EmuHawk. (Si estás usando BizHawk 2.9, puedes saltar este paso.) - En `Config > Customize`, activa la opción "Run in background" para prevenir desconexiones del cliente mientras la aplicación activa no sea EmuHawk. -- Abre el archivo `.gba` en EmuHawk y luego ve a `Config > Controllers…` para configurar los controles. Si no puedes +- Abre el archivo `.gba` en EmuHawk y luego ve a `Config > Controllers…` para configurar los controles. Si no puedes hacer clic en `Controllers…`, debes abrir cualquier ROM `.gba` primeramente. -- Considera limpiar tus macros y atajos en `Config > Hotkeys…` si no quieres usarlas de manera intencional. Para +- Considera limpiar tus macros y atajos en `Config > Hotkeys…` si no quieres usarlas de manera intencional. Para limpiarlas, selecciona el atajo y presiona la tecla Esc. ## Software Opcional -- [Pokémon Emerald AP Tracker](https://github.com/seto10987/Archipelago-Emerald-AP-Tracker/releases/latest), para usar con -[PopTracker](https://github.com/black-sliver/PopTracker/releases) +- [Pokémon Emerald AP Tracker](https://github.com/seto10987/Archipelago-Emerald-AP-Tracker/releases/latest), para usar +con [PopTracker](https://github.com/black-sliver/PopTracker/releases) ## Generando y Parcheando el Juego -1. Crea tu archivo de configuración (YAML). Puedes hacerlo en +1. Crea tu archivo de configuración (YAML). Puedes hacerlo en [Página de Opciones de Pokémon Emerald](../../../games/Pokemon%20Emerald/player-options). -2. Sigue las instrucciones generales de Archipelago para [Generar un juego] -(../../Archipelago/setup/en#generating-a-game). Esto generará un archivo de salida (output file) para ti. Tu archivo -de parche tendrá la extensión de archivo`.apemerald`. +2. Sigue las instrucciones generales de Archipelago para +[Generar un juego](../../Archipelago/setup/en#generating-a-game). Esto generará un archivo de salida (output file) para +ti. Tu archivo de parche tendrá la extensión de archivo `.apemerald`. 3. Abre `ArchipelagoLauncher.exe` 4. Selecciona "Open Patch" en el lado derecho y elige tu archivo de parcheo. 5. Si esta es la primera vez que vas a parchear, se te pedirá que selecciones la ROM sin parchear. 6. Un archivo parcheado con extensión `.gba` será creado en el mismo lugar que el archivo de parcheo. -7. La primera vez que abras un archivo parcheado con el BizHawk Client, se te preguntará donde está localizado +7. La primera vez que abras un archivo parcheado con el BizHawk Client, se te preguntará donde está localizado `EmuHawk.exe` en tu instalación de BizHawk. -Si estás jugando una seed Single-Player y no te interesa el auto-tracking o las pistas, puedes parar aquí, cierra el -cliente, y carga la ROM ya parcheada en cualquier emulador. Pero para partidas multi-worlds y para otras -implementaciones de Archipelago, continúa usando BizHawk como tu emulador +Si estás jugando una seed Single-Player y no te interesa el auto-tracking o las pistas, puedes parar aquí, cierra el +cliente, y carga la ROM ya parcheada en cualquier emulador. Pero para partidas multi-worlds y para otras +implementaciones de Archipelago, continúa usando BizHawk como tu emulador. ## Conectando con el Servidor -Por defecto, al abrir un archivo parcheado, se harán de manera automática 1-5 pasos. Aun así, ten en cuenta lo +Por defecto, al abrir un archivo parcheado, se harán de manera automática 1-5 pasos. Aun así, ten en cuenta lo siguiente en caso de que debas cerrar y volver a abrir la ventana en mitad de la partida por algún motivo. -1. Pokémon Emerald usa el Archipelago BizHawk Client. Si el cliente no se encuentra abierto al abrir la rom +1. Pokémon Emerald usa el Archipelago BizHawk Client. Si el cliente no se encuentra abierto al abrir la rom parcheada, puedes volver a abrirlo desde el Archipelago Launcher. 2. Asegúrate que EmuHawk está corriendo la ROM parcheada. 3. En EmuHawk, ve a `Tools > Lua Console`. Debes tener esta ventana abierta mientras juegas. 4. En la ventana de Lua Console, ve a `Script > Open Script…`. 5. Ve a la carpeta donde está instalado Archipelago y abre `data/lua/connector_bizhawk_generic.lua`. -6. El emulador y el cliente eventualmente se conectarán uno con el otro. La ventana de BizHawk Client indicará que te +6. El emulador y el cliente eventualmente se conectarán uno con el otro. La ventana de BizHawk Client indicará que te has conectado y reconocerá Pokémon Emerald. -7. Para conectar el cliente con el servidor, ingresa la dirección y el puerto de la sala (ej. `archipelago.gg:38281`) +7. Para conectar el cliente con el servidor, ingresa la dirección y el puerto de la sala (ej. `archipelago.gg:38281`) en el campo de texto que se encuentra en la parte superior del cliente y haz click en Connect. -Ahora deberías poder enviar y recibir ítems. Debes seguir estos pasos cada vez que quieras reconectarte. Es seguro +Ahora deberías poder enviar y recibir ítems. Debes seguir estos pasos cada vez que quieras reconectarte. Es seguro jugar de manera offline; se sincronizará todo cuando te vuelvas a conectar. ## Tracking Automático @@ -70,5 +70,5 @@ Pokémon Emerald tiene un Map Tracker completamente funcional que soporta auto-t 2. Coloca la carpeta del Tracker en la carpeta packs/ dentro de la carpeta de instalación del PopTracker. 3. Abre PopTracker, y carga el Pack de Pokémon Emerald Map Tracker. 4. Para utilizar el auto-tracking, haz click en el símbolo "AP" que se encuentra en la parte superior. -5. Entra la dirección del Servidor de Archipelago (la misma a la que te conectaste para jugar), nombre del jugador, y +5. Entra la dirección del Servidor de Archipelago (la misma a la que te conectaste para jugar), nombre del jugador, y contraseña (deja vacío este campo en caso de no utilizar contraseña). From cd160842ba13c935e2824b80d746f9cce92d05a9 Mon Sep 17 00:00:00 2001 From: Bryce Wilson Date: Wed, 22 May 2024 17:03:42 -0700 Subject: [PATCH 04/12] BizHawkClient: Linting/style (#3335) --- worlds/_bizhawk/__init__.py | 2 +- worlds/_bizhawk/client.py | 14 ++++++-------- worlds/_bizhawk/context.py | 8 ++++---- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/worlds/_bizhawk/__init__.py b/worlds/_bizhawk/__init__.py index 94a9ce1ddf04..74f2954b984b 100644 --- a/worlds/_bizhawk/__init__.py +++ b/worlds/_bizhawk/__init__.py @@ -103,7 +103,7 @@ async def connect(ctx: BizHawkContext) -> bool: return True except (TimeoutError, ConnectionRefusedError): continue - + # No ports worked ctx.streams = None ctx.connection_status = ConnectionStatus.NOT_CONNECTED diff --git a/worlds/_bizhawk/client.py b/worlds/_bizhawk/client.py index 32a6e3704e1e..00370c277a17 100644 --- a/worlds/_bizhawk/client.py +++ b/worlds/_bizhawk/client.py @@ -2,7 +2,6 @@ A module containing the BizHawkClient base class and metaclass """ - from __future__ import annotations import abc @@ -12,14 +11,13 @@ if TYPE_CHECKING: from .context import BizHawkClientContext -else: - BizHawkClientContext = object def launch_client(*args) -> None: from .context import launch launch_subprocess(launch, name="BizHawkClient") + component = Component("BizHawk Client", "BizHawkClient", component_type=Type.CLIENT, func=launch_client, file_identifier=SuffixIdentifier()) components.append(component) @@ -56,7 +54,7 @@ def __new__(cls, name: str, bases: Tuple[type, ...], namespace: Dict[str, Any]) return new_class @staticmethod - async def get_handler(ctx: BizHawkClientContext, system: str) -> Optional[BizHawkClient]: + 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(): @@ -77,7 +75,7 @@ class BizHawkClient(abc.ABC, metaclass=AutoBizHawkClientRegister): """The file extension(s) this client is meant to open and patch (e.g. ".apz3")""" @abc.abstractmethod - async def validate_rom(self, ctx: BizHawkClientContext) -> bool: + 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. @@ -86,18 +84,18 @@ async def validate_rom(self, ctx: BizHawkClientContext) -> bool: as necessary (such as setting `ctx.game = self.game`, modifying `ctx.items_handling`, etc...).""" ... - async def set_auth(self, ctx: BizHawkClientContext) -> None: + 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: + 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: + def on_package(self, ctx: "BizHawkClientContext", cmd: str, args: dict) -> None: """For handling packages from the server. Called from `BizHawkClientContext.on_package`.""" pass diff --git a/worlds/_bizhawk/context.py b/worlds/_bizhawk/context.py index 05bee23412d5..0a28a47894d4 100644 --- a/worlds/_bizhawk/context.py +++ b/worlds/_bizhawk/context.py @@ -3,7 +3,6 @@ checking or launching the client, otherwise it will probably cause circular import issues. """ - import asyncio import enum import subprocess @@ -77,7 +76,7 @@ def on_package(self, cmd, args): if self.client_handler is not None: self.client_handler.on_package(self, cmd, args) - async def server_auth(self, password_requested: bool = False): + async def server_auth(self, password_requested: bool=False): self.password_requested = password_requested if self.bizhawk_ctx.connection_status != ConnectionStatus.CONNECTED: @@ -103,7 +102,7 @@ async def server_auth(self, password_requested: bool = False): await self.send_connect() self.auth_status = AuthStatus.PENDING - async def disconnect(self, allow_autoreconnect: bool = False): + async def disconnect(self, allow_autoreconnect: bool=False): self.auth_status = AuthStatus.NOT_AUTHENTICATED await super().disconnect(allow_autoreconnect) @@ -148,7 +147,8 @@ async def _game_watcher(ctx: BizHawkClientContext): 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.") + logger.info(f"Connector script is incompatible. Expected version {EXPECTED_SCRIPT_VERSION} but " + f"got {script_version}. Disconnecting.") disconnect(ctx.bizhawk_ctx) continue From 02d3fdf2a6f3ccdf7abdbd540fe57a1acc2568e9 Mon Sep 17 00:00:00 2001 From: Scipio Wright Date: Wed, 22 May 2024 20:05:21 -0400 Subject: [PATCH 05/12] Update options to look better on webhost after update, also give death link a description (#3329) --- worlds/noita/options.py | 57 +++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/worlds/noita/options.py b/worlds/noita/options.py index f2ccbfbc4d3b..0fdd62365a5a 100644 --- a/worlds/noita/options.py +++ b/worlds/noita/options.py @@ -3,11 +3,13 @@ class PathOption(Choice): - """Choose where you would like Hidden Chest and Pedestal checks to be placed. + """ + Choose where you would like Hidden Chest and Pedestal checks to be placed. Main Path includes the main 7 biomes you typically go through to get to the final boss. Side Path includes the Lukki Lair and Fungal Caverns. 9 biomes total. Main World includes the full world (excluding parallel worlds). 15 biomes total. - Note: The Collapsed Mines have been combined into the Mines as the biome is tiny.""" + Note: The Collapsed Mines have been combined into the Mines as the biome is tiny. + """ display_name = "Path Option" option_main_path = 1 option_side_path = 2 @@ -16,7 +18,9 @@ class PathOption(Choice): class HiddenChests(Range): - """Number of hidden chest checks added to the applicable biomes.""" + """ + Number of hidden chest checks added to the applicable biomes. + """ display_name = "Hidden Chests per Biome" range_start = 0 range_end = 20 @@ -24,7 +28,9 @@ class HiddenChests(Range): class PedestalChecks(Range): - """Number of checks that will spawn on pedestals in the applicable biomes.""" + """ + Number of checks that will spawn on pedestals in the applicable biomes. + """ display_name = "Pedestal Checks per Biome" range_start = 0 range_end = 20 @@ -32,15 +38,19 @@ class PedestalChecks(Range): class Traps(DefaultOnToggle): - """Whether negative effects on the Noita world are added to the item pool.""" + """ + Whether negative effects on the Noita world are added to the item pool. + """ display_name = "Traps" class OrbsAsChecks(Choice): - """Decides whether finding the orbs that naturally spawn in the world count as checks. + """ + Decides whether finding the orbs that naturally spawn in the world count as checks. The Main Path option includes only the Floating Island and Abyss Orb Room orbs. The Side Path option includes the Main Path, Magical Temple, Lukki Lair, and Lava Lake orbs. - The Main World option includes all 11 orbs.""" + The Main World option includes all 11 orbs. + """ display_name = "Orbs as Location Checks" option_no_orbs = 0 option_main_path = 1 @@ -50,10 +60,12 @@ class OrbsAsChecks(Choice): class BossesAsChecks(Choice): - """Makes bosses count as location checks. The boss only needs to die, you do not need the kill credit. + """ + Makes bosses count as location checks. The boss only needs to die, you do not need the kill credit. The Main Path option includes Gate Guardian, Suomuhauki, and Kolmisilmä. The Side Path option includes the Main Path bosses, Sauvojen Tuntija, and Ylialkemisti. - The All Bosses option includes all 15 bosses.""" + The All Bosses option includes all 15 bosses. + """ display_name = "Bosses as Location Checks" option_no_bosses = 0 option_main_path = 1 @@ -65,11 +77,13 @@ class BossesAsChecks(Choice): # Note: the Sampo is an item that is picked up to trigger the boss fight at the normal ending location. # The sampo is required for every ending (having orbs and bringing the sampo to a different spot changes the ending). class VictoryCondition(Choice): - """Greed is to get to the bottom, beat the boss, and win the game. + """ + Greed is to get to the bottom, beat the boss, and win the game. Pure is to get 11 orbs, grab the sampo, and bring it to the mountain altar. Peaceful is to get all 33 orbs, grab the sampo, and bring it to the mountain altar. Orbs will be added to the randomizer pool based on which victory condition you chose. - The base game orbs will not count towards these victory conditions.""" + The base game orbs will not count towards these victory conditions. + """ display_name = "Victory Condition" option_greed_ending = 0 option_pure_ending = 1 @@ -78,9 +92,11 @@ class VictoryCondition(Choice): class ExtraOrbs(Range): - """Add extra orbs to your item pool, to prevent you from needing to wait as long for the last orb you need for your victory condition. + """ + Add extra orbs to your item pool, to prevent you from needing to wait as long for the last orb you need for your victory condition. Extra orbs received past your victory condition's amount will be received as hearts instead. - Can be turned on for the Greed Ending goal, but will only really make it harder.""" + Can be turned on for the Greed Ending goal, but will only really make it harder. + """ display_name = "Extra Orbs" range_start = 0 range_end = 10 @@ -88,8 +104,10 @@ class ExtraOrbs(Range): class ShopPrice(Choice): - """Reduce the costs of Archipelago items in shops. - By default, the price of Archipelago items matches the price of wands at that shop.""" + """ + Reduce the costs of Archipelago items in shops. + By default, the price of Archipelago items matches the price of wands at that shop. + """ display_name = "Shop Price Reduction" option_full_price = 100 option_25_percent_off = 75 @@ -98,10 +116,17 @@ class ShopPrice(Choice): default = 100 +class NoitaDeathLink(DeathLink): + """ + When you die, everyone dies. Of course, the reverse is true too. + You can disable this in the in-game mod options. + """ + + @dataclass class NoitaOptions(PerGameCommonOptions): start_inventory_from_pool: StartInventoryPool - death_link: DeathLink + death_link: NoitaDeathLink bad_effects: Traps victory_condition: VictoryCondition path_option: PathOption From 893a157b23ab131b59b007dfc1f6ef0f28aba34e Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Wed, 22 May 2024 20:09:52 -0400 Subject: [PATCH 06/12] Lingo: Minor logic fixes (part 2) (#3250) * Lingo: Minor logic fixes (part 2) * Update the datafile * Renamed Fearless Mastery * Move Rhyme Room LEAP into upper room * Rename Artistic achievement location * Fix broken wondrous painting * Added a test for the Wondrous painting thing --- worlds/lingo/data/LL1.yaml | 13 +++++++++---- worlds/lingo/data/generated.dat | Bin 135088 -> 136017 bytes worlds/lingo/data/ids.yaml | 2 +- worlds/lingo/datatypes.py | 1 + worlds/lingo/locations.py | 2 +- worlds/lingo/utils/pickle_static_data.py | 9 ++++++--- worlds/lingo/utils/validate_config.rb | 9 ++++++++- 7 files changed, 26 insertions(+), 10 deletions(-) diff --git a/worlds/lingo/data/LL1.yaml b/worlds/lingo/data/LL1.yaml index c33cad393bba..4d6771a7350d 100644 --- a/worlds/lingo/data/LL1.yaml +++ b/worlds/lingo/data/LL1.yaml @@ -2052,6 +2052,7 @@ door: Rhyme Room Entrance Art Gallery: warp: True + Roof: True # by parkouring through the Bearer shortcut panels: RED: id: Color Arrow Room/Panel_red_afar @@ -2333,6 +2334,7 @@ # This is the MASTERY on the other side of THE FEARLESS. It can only be # accessed by jumping from the top of the tower. id: Master Room/Panel_mastery_mastery8 + location_name: The Fearless - MASTERY tag: midwhite hunt: True required_door: @@ -4098,6 +4100,7 @@ Number Hunt: room: Number Hunt door: Door to Directional Gallery + Roof: True # through ceiling of sunwarp panels: PEPPER: id: Backside Room/Panel_pepper_salt @@ -5390,6 +5393,7 @@ - The Artistic (Apple) - The Artistic (Lattice) check: True + location_name: The Artistic - Achievement achievement: The Artistic FINE: id: Ceiling Room/Panel_yellow_top_5 @@ -6046,7 +6050,7 @@ paintings: - id: symmetry_painting_a_5 orientation: east - - id: symmetry_painting_a_5 + - id: symmetry_painting_b_5 disable: True The Wondrous (Window): entrances: @@ -6814,9 +6818,6 @@ tag: syn rhyme subtag: bot link: rhyme FALL - LEAP: - id: Double Room/Panel_leap_leap - tag: midwhite doors: Exit: id: Double Room Area Doors/Door_room_exit @@ -7065,6 +7066,9 @@ tag: syn rhyme subtag: bot link: rhyme CREATIVE + LEAP: + id: Double Room/Panel_leap_leap + tag: midwhite doors: Door to Cross: id: Double Room Area Doors/Door_room_4a @@ -7272,6 +7276,7 @@ MASTERY: id: Master Room/Panel_mastery_mastery tag: midwhite + hunt: True required_door: room: Orange Tower Seventh Floor door: Mastery diff --git a/worlds/lingo/data/generated.dat b/worlds/lingo/data/generated.dat index 304109ca2840005f25efda7653198ff8ddb96092..6c8c925138aa5ac61edb22d194cfb8e9b9e4a492 100644 GIT binary patch literal 136017 zcmd?S4V+}xRUas;tE<1~Qy-%bmDp>uvzUmHeKS6YXy!<*OJebl|7U%!7i-Ps)ZbU6H`VRXW&>FlsGJKs6cnFGK}RX_%z9v4uj9jLpmwR%_D`!QCR z4i9d$d$Pv6I&-M(rOtFTLr4_&uPhxL^oH*rM$@b9E7#hE_0|jRVRTZXUTAar$U6)$WN} zUmw?cceB$jY#r=h=^YIEg?YmKx&4F7507l)#P#<6_RenH!dpefkHi(9s0|MLJ6GBT zYGC1DcQn?gF7FQ7do4y}ITq*QI`86jZth=cqd&d&=$h|r-)#3U-E8lI#4*Bh@V+9h z{7znZ@mjClZe89TJ#MoHHw+O34HlxA)txKg7R8gqZF-dF_P`4<3IcpNnp_=R4#1x1 z@9pdw;7gLim1u6M-MUgJ?O!Prd+io6kg0zbJ+-T^M$?PEgMPnv(7MtWz@0OlS~OX@ ziE(f3?g}Q1Cxg@EIf3D3r)D)Wb)(%sywtkdlFamUG~F2N9QFg~+z}$YsPn&!66kHU z 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�ZH!Qzm&t;Y3EA~7>rC03v zGMmsnBcr1hRa3GI4||~wFjfahmiIp^`Ja{br+%cm097iw1V<5|xO9zh?&Zjy2 z&DNEiLbTT0F@FxW|$_y7O^ literal 135088 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 VN|2s@APVa$lY!lU^DCFP{9o7VyI24K diff --git a/worlds/lingo/data/ids.yaml b/worlds/lingo/data/ids.yaml index 918af7aba923..1fa06d24254f 100644 --- a/worlds/lingo/data/ids.yaml +++ b/worlds/lingo/data/ids.yaml @@ -766,7 +766,6 @@ panels: BOUNCE: 445010 SCRAWL: 445011 PLUNGE: 445012 - LEAP: 445013 Rhyme Room (Circle): BIRD: 445014 LETTER: 445015 @@ -790,6 +789,7 @@ panels: GEM: 445031 INNOVATIVE (Top): 445032 INNOVATIVE (Bottom): 445033 + LEAP: 445013 Room Room: DOOR (1): 445034 DOOR (2): 445035 diff --git a/worlds/lingo/datatypes.py b/worlds/lingo/datatypes.py index e466558f87ff..36141daa4106 100644 --- a/worlds/lingo/datatypes.py +++ b/worlds/lingo/datatypes.py @@ -63,6 +63,7 @@ class Panel(NamedTuple): exclude_reduce: bool achievement: bool non_counting: bool + location_name: Optional[str] class Painting(NamedTuple): diff --git a/worlds/lingo/locations.py b/worlds/lingo/locations.py index 5ffedee36799..c527e522fb06 100644 --- a/worlds/lingo/locations.py +++ b/worlds/lingo/locations.py @@ -39,7 +39,7 @@ def load_location_data(): for room_name, panels in PANELS_BY_ROOM.items(): for panel_name, panel in panels.items(): - location_name = f"{room_name} - {panel_name}" + location_name = f"{room_name} - {panel_name}" if panel.location_name is None else panel.location_name classification = LocationClassification.insanity if panel.check: diff --git a/worlds/lingo/utils/pickle_static_data.py b/worlds/lingo/utils/pickle_static_data.py index 10ec69be3537..e40c21ce3e6a 100644 --- a/worlds/lingo/utils/pickle_static_data.py +++ b/worlds/lingo/utils/pickle_static_data.py @@ -150,8 +150,6 @@ def process_entrance(source_room, doors, room_obj): def process_panel(room_name, panel_name, panel_data): global PANELS_BY_ROOM - full_name = f"{room_name} - {panel_name}" - # required_room can either be a single room or a list of rooms. if "required_room" in panel_data: if isinstance(panel_data["required_room"], list): @@ -229,8 +227,13 @@ def process_panel(room_name, panel_name, panel_data): else: non_counting = False + if "location_name" in panel_data: + location_name = panel_data["location_name"] + else: + location_name = None + panel_obj = Panel(required_rooms, required_doors, required_panels, colors, check, event, exclude_reduce, - achievement, non_counting) + achievement, non_counting, location_name) PANELS_BY_ROOM[room_name][panel_name] = panel_obj diff --git a/worlds/lingo/utils/validate_config.rb b/worlds/lingo/utils/validate_config.rb index 831fee2ad312..498980bb719a 100644 --- a/worlds/lingo/utils/validate_config.rb +++ b/worlds/lingo/utils/validate_config.rb @@ -39,11 +39,12 @@ mentioned_panels = Set[] mentioned_sunwarp_entrances = Set[] mentioned_sunwarp_exits = Set[] +mentioned_paintings = Set[] door_groups = {} directives = Set["entrances", "panels", "doors", "paintings", "sunwarps", "progression"] -panel_directives = Set["id", "required_room", "required_door", "required_panel", "colors", "check", "exclude_reduce", "tag", "link", "subtag", "achievement", "copy_to_sign", "non_counting", "hunt"] +panel_directives = Set["id", "required_room", "required_door", "required_panel", "colors", "check", "exclude_reduce", "tag", "link", "subtag", "achievement", "copy_to_sign", "non_counting", "hunt", "location_name"] door_directives = Set["id", "painting_id", "panels", "item_name", "item_group", "location_name", "skip_location", "skip_item", "door_group", "include_reduce", "event", "warp_id"] painting_directives = Set["id", "enter_only", "exit_only", "orientation", "required_door", "required", "required_when_no_doors", "move", "req_blocked", "req_blocked_when_no_doors"] @@ -257,6 +258,12 @@ unless paintings.include? painting["id"] then puts "#{room_name} :::: Invalid Painting ID #{painting["id"]}" end + + if mentioned_paintings.include?(painting["id"]) then + puts "Painting #{painting["id"]} is mentioned more than once" + else + mentioned_paintings.add(painting["id"]) + end else puts "#{room_name} :::: Painting is missing an ID" end From 92392c0e65854fd01d9fd7253aa1ce8ab8ae96a3 Mon Sep 17 00:00:00 2001 From: Justus Lind Date: Thu, 23 May 2024 10:11:27 +1000 Subject: [PATCH 07/12] Update Song List to Muse Dash 4.3.0 (#3216) --- worlds/musedash/MuseDashData.txt | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/worlds/musedash/MuseDashData.txt b/worlds/musedash/MuseDashData.txt index 0a8beba37b44..b0f3b80c997a 100644 --- a/worlds/musedash/MuseDashData.txt +++ b/worlds/musedash/MuseDashData.txt @@ -538,10 +538,16 @@ Reality Show|71-2|Valentine Stage|False|5|7|10| SIG feat.Tobokegao|71-3|Valentine Stage|True|3|6|8| Rose Love|71-4|Valentine Stage|True|2|4|7| Euphoria|71-5|Valentine Stage|True|1|3|6| -P E R O P E R O Brother Dance|72-0|Legends of Muse Warriors|False|0|?|0| +P E R O P E R O Brother Dance|72-0|Legends of Muse Warriors|True|0|?|0| PA PPA PANIC|72-1|Legends of Muse Warriors|False|4|8|10| -How To Make Music Game Song!|72-2|Legends of Muse Warriors|False|6|8|10|11 -Re Re|72-3|Legends of Muse Warriors|False|7|9|11|12 -Marmalade Twins|72-4|Legends of Muse Warriors|False|5|8|10| -DOMINATOR|72-5|Legends of Muse Warriors|False|7|9|11| -Teshikani TESHiKANi|72-6|Legends of Muse Warriors|False|5|7|9| +How To Make Music Game Song!|72-2|Legends of Muse Warriors|True|6|8|10|11 +Re Re|72-3|Legends of Muse Warriors|True|7|9|11|12 +Marmalade Twins|72-4|Legends of Muse Warriors|True|5|8|10| +DOMINATOR|72-5|Legends of Muse Warriors|True|7|9|11| +Teshikani TESHiKANi|72-6|Legends of Muse Warriors|True|5|7|9| +Urban Magic|73-0|Happy Otaku Pack Vol.19|True|3|5|7| +Maid's Prank|73-1|Happy Otaku Pack Vol.19|True|5|7|10| +Dance Dance Good Night Dance|73-2|Happy Otaku Pack Vol.19|True|2|4|7| +Ops Limone|73-3|Happy Otaku Pack Vol.19|True|5|8|11| +NOVA|73-4|Happy Otaku Pack Vol.19|True|6|8|10| +Heaven's Gradius|73-5|Happy Otaku Pack Vol.19|True|6|8|10| From a43e29478646ff608c46238ce5c20dfe4d30e687 Mon Sep 17 00:00:00 2001 From: Scipio Wright Date: Wed, 22 May 2024 20:12:59 -0400 Subject: [PATCH 08/12] TUNIC: Add option presets (#3377) * Add option presets * why the hell is there an s here * entrance rando yes --- worlds/tunic/__init__.py | 3 ++- worlds/tunic/options.py | 23 ++++++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/worlds/tunic/__init__.py b/worlds/tunic/__init__.py index 8e8957144de6..cff8c39c9fea 100644 --- a/worlds/tunic/__init__.py +++ b/worlds/tunic/__init__.py @@ -8,7 +8,7 @@ from .regions import tunic_regions from .er_scripts import create_er_regions from .er_data import portal_mapping -from .options import TunicOptions, EntranceRando, tunic_option_groups +from .options import TunicOptions, EntranceRando, tunic_option_groups, tunic_option_presets from worlds.AutoWorld import WebWorld, World from worlds.generic import PlandoConnection from decimal import Decimal, ROUND_HALF_UP @@ -28,6 +28,7 @@ class TunicWeb(WebWorld): theme = "grassFlowers" game = "TUNIC" option_groups = tunic_option_groups + options_presets = tunic_option_presets class TunicItem(Item): diff --git a/worlds/tunic/options.py b/worlds/tunic/options.py index 1f12b5053dc9..a45ee71b0557 100644 --- a/worlds/tunic/options.py +++ b/worlds/tunic/options.py @@ -1,5 +1,5 @@ from dataclasses import dataclass - +from typing import Dict, Any from Options import (DefaultOnToggle, Toggle, StartInventoryPool, Choice, Range, TextChoice, PerGameCommonOptions, OptionGroup) @@ -199,3 +199,24 @@ class TunicOptions(PerGameCommonOptions): Maskless, ]) ] + +tunic_option_presets: Dict[str, Dict[str, Any]] = { + "Sync": { + "ability_shuffling": True, + }, + "Async": { + "progression_balancing": 0, + "ability_shuffling": True, + "shuffle_ladders": True, + "laurels_location": "10_fairies", + }, + "Glace Mode": { + "accessibility": "minimal", + "ability_shuffling": True, + "entrance_rando": "yes", + "fool_traps": "onslaught", + "logic_rules": "unrestricted", + "maskless": True, + "lanternless": True, + }, +} From 56d01f391331357485bf40ab59157f0c40ad143e Mon Sep 17 00:00:00 2001 From: LiquidCat64 <74896918+LiquidCat64@users.noreply.github.com> Date: Wed, 22 May 2024 18:16:13 -0600 Subject: [PATCH 09/12] CV64: Add option groups (#3360) * Add the option groups. * Get rid of all mid-sentence line breaks. --- worlds/cv64/__init__.py | 4 +- worlds/cv64/options.py | 302 ++++++++++++++++++++++++++-------------- 2 files changed, 203 insertions(+), 103 deletions(-) diff --git a/worlds/cv64/__init__.py b/worlds/cv64/__init__.py index 84bf03ff27aa..2f483cd4d919 100644 --- a/worlds/cv64/__init__.py +++ b/worlds/cv64/__init__.py @@ -8,7 +8,7 @@ from .items import CV64Item, filler_item_names, get_item_info, get_item_names_to_ids, get_item_counts from .locations import CV64Location, get_location_info, verify_locations, get_location_names_to_ids, base_id from .entrances import verify_entrances, get_warp_entrances -from .options import CV64Options, CharacterStages, DraculasCondition, SubWeaponShuffle +from .options import CV64Options, cv64_option_groups, CharacterStages, DraculasCondition, SubWeaponShuffle from .stages import get_locations_from_stage, get_normal_stage_exits, vanilla_stage_order, \ shuffle_stages, generate_warps, get_region_names from .regions import get_region_info @@ -45,6 +45,8 @@ class CV64Web(WebWorld): ["Liquid Cat"] )] + option_groups = cv64_option_groups + class CV64World(World): """ diff --git a/worlds/cv64/options.py b/worlds/cv64/options.py index da2b9f949662..93b417ad26fd 100644 --- a/worlds/cv64/options.py +++ b/worlds/cv64/options.py @@ -1,10 +1,11 @@ from dataclasses import dataclass -from Options import Choice, DefaultOnToggle, Range, Toggle, PerGameCommonOptions, StartInventoryPool +from Options import OptionGroup, Choice, DefaultOnToggle, Range, Toggle, PerGameCommonOptions, StartInventoryPool class CharacterStages(Choice): - """Whether to include Reinhardt-only stages, Carrie-only stages, or both with or without branching paths at the end - of Villa and Castle Center.""" + """ + Whether to include Reinhardt-only stages, Carrie-only stages, or both with or without branching paths at the end of Villa and Castle Center. + """ display_name = "Character Stages" option_both = 0 option_branchless_both = 1 @@ -14,14 +15,18 @@ class CharacterStages(Choice): class StageShuffle(Toggle): - """Shuffles which stages appear in which stage slots. Villa and Castle Center will never appear in any character - stage slots if Character Stages is set to Both; they can only be somewhere on the main path. - Castle Keep will always be at the end of the line.""" + """ + Shuffles which stages appear in which stage slots. + Villa and Castle Center will never appear in any character stage slots if Character Stages is set to Both; they can only be somewhere on the main path. + Castle Keep will always be at the end of the line. + """ display_name = "Stage Shuffle" class StartingStage(Choice): - """Which stage to start at if Stage Shuffle is turned on.""" + """ + Which stage to start at if Stage Shuffle is turned on. + """ display_name = "Starting Stage" option_forest_of_silence = 0 option_castle_wall = 1 @@ -39,8 +44,9 @@ class StartingStage(Choice): class WarpOrder(Choice): - """Arranges the warps in the warp menu in whichever stage order chosen, - thereby changing the order they are unlocked in.""" + """ + Arranges the warps in the warp menu in whichever stage order chosen, thereby changing the order they are unlocked in. + """ display_name = "Warp Order" option_seed_stage_order = 0 option_vanilla_stage_order = 1 @@ -49,7 +55,9 @@ class WarpOrder(Choice): class SubWeaponShuffle(Choice): - """Shuffles all sub-weapons in the game within each other in their own pool or in the main item pool.""" + """ + Shuffles all sub-weapons in the game within each other in their own pool or in the main item pool. + """ display_name = "Sub-weapon Shuffle" option_off = 0 option_own_pool = 1 @@ -58,8 +66,10 @@ class SubWeaponShuffle(Choice): class SpareKeys(Choice): - """Puts an additional copy of every non-Special key item in the pool for every key item that there is. - Chance gives each key item a 50% chance of having a duplicate instead of guaranteeing one for all of them.""" + """ + Puts an additional copy of every non-Special key item in the pool for every key item that there is. + Chance gives each key item a 50% chance of having a duplicate instead of guaranteeing one for all of them. + """ display_name = "Spare Keys" option_off = 0 option_on = 1 @@ -68,14 +78,17 @@ class SpareKeys(Choice): class HardItemPool(Toggle): - """Replaces some items in the item pool with less valuable ones, to make the item pool sort of resemble Hard Mode - in the PAL version.""" + """ + Replaces some items in the item pool with less valuable ones, to make the item pool sort of resemble Hard Mode in the PAL version. + """ display_name = "Hard Item Pool" class Special1sPerWarp(Range): - """Sets how many Special1 jewels are needed per warp menu option unlock. - This will decrease until the number x 7 is less than or equal to the Total Specail1s if it isn't already.""" + """ + Sets how many Special1 jewels are needed per warp menu option unlock. + This will decrease until the number x 7 is less than or equal to the Total Specail1s if it isn't already. + """ range_start = 1 range_end = 10 default = 1 @@ -83,7 +96,9 @@ class Special1sPerWarp(Range): class TotalSpecial1s(Range): - """Sets how many Speical1 jewels are in the pool in total.""" + """ + Sets how many Speical1 jewels are in the pool in total. + """ range_start = 7 range_end = 70 default = 7 @@ -91,11 +106,13 @@ class TotalSpecial1s(Range): class DraculasCondition(Choice): - """Sets the requirement for unlocking and opening the door to Dracula's chamber. + """ + Sets the requirement for unlocking and opening the door to Dracula's chamber. None: No requirement. Door is unlocked from the start. Crystal: Activate the big crystal in Castle Center's basement. Neither boss afterwards has to be defeated. Bosses: Kill a specified number of bosses with health bars and claim their Trophies. - Specials: Find a specified number of Special2 jewels shuffled in the main item pool.""" + Specials: Find a specified number of Special2 jewels shuffled in the main item pool. + """ display_name = "Dracula's Condition" option_none = 0 option_crystal = 1 @@ -105,7 +122,9 @@ class DraculasCondition(Choice): class PercentSpecial2sRequired(Range): - """Percentage of Special2s required to enter Dracula's chamber when Dracula's Condition is Special2s.""" + """ + Percentage of Special2s required to enter Dracula's chamber when Dracula's Condition is Special2s. + """ range_start = 1 range_end = 100 default = 80 @@ -113,7 +132,9 @@ class PercentSpecial2sRequired(Range): class TotalSpecial2s(Range): - """How many Speical2 jewels are in the pool in total when Dracula's Condition is Special2s.""" + """ + How many Speical2 jewels are in the pool in total when Dracula's Condition is Special2s. + """ range_start = 1 range_end = 70 default = 25 @@ -121,58 +142,70 @@ class TotalSpecial2s(Range): class BossesRequired(Range): - """How many bosses need to be defeated to enter Dracula's chamber when Dracula's Condition is set to Bosses. - This will automatically adjust if there are fewer available bosses than the chosen number.""" + """ + How many bosses need to be defeated to enter Dracula's chamber when Dracula's Condition is set to Bosses. + This will automatically adjust if there are fewer available bosses than the chosen number. + """ range_start = 1 range_end = 16 - default = 14 + default = 12 display_name = "Bosses Required" class CarrieLogic(Toggle): - """Adds the 2 checks inside Underground Waterway's crawlspace to the pool. + """ + Adds the 2 checks inside Underground Waterway's crawlspace to the pool. If you (and everyone else if racing the same seed) are planning to only ever play Reinhardt, don't enable this. - Can be combined with Hard Logic to include Carrie-only tricks.""" + Can be combined with Hard Logic to include Carrie-only tricks. + """ display_name = "Carrie Logic" class HardLogic(Toggle): - """Properly considers sequence break tricks in logic (i.e. maze skip). Can be combined with Carrie Logic to include - Carrie-only tricks. - See the Game Page for a full list of tricks and glitches that may be logically required.""" + """ + Properly considers sequence break tricks in logic (i.e. maze skip). Can be combined with Carrie Logic to include Carrie-only tricks. + See the Game Page for a full list of tricks and glitches that may be logically required. + """ display_name = "Hard Logic" class MultiHitBreakables(Toggle): - """Adds the items that drop from the objects that break in three hits to the pool. There are 18 of these throughout - the game, adding up to 79 or 80 checks (depending on sub-weapons - being shuffled anywhere or not) in total with all stages. - The game will be modified to - remember exactly which of their items you've picked up instead of simply whether they were broken or not.""" + """ + Adds the items that drop from the objects that break in three hits to the pool. + There are 18 of these throughout the game, adding up to 79 or 80 checks (depending on sub-weapons being shuffled anywhere or not) in total with all stages. + The game will be modified to remember exactly which of their items you've picked up instead of simply whether they were broken or not. + """ display_name = "Multi-hit Breakables" class EmptyBreakables(Toggle): - """Adds 9 check locations in the form of breakables that normally have nothing (all empty Forest coffins, etc.) - and some additional Red Jewels and/or moneybags into the item pool to compensate.""" + """ + Adds 9 check locations in the form of breakables that normally have nothing (all empty Forest coffins, etc.) and some additional Red Jewels and/or moneybags into the item pool to compensate. + """ display_name = "Empty Breakables" class LizardLockerItems(Toggle): - """Adds the 6 items inside Castle Center 2F's Lizard-man generators to the pool. - Picking up all of these can be a very tedious luck-based process, so they are off by default.""" + """ + Adds the 6 items inside Castle Center 2F's Lizard-man generators to the pool. + Picking up all of these can be a very tedious luck-based process, so they are off by default. + """ display_name = "Lizard Locker Items" class Shopsanity(Toggle): - """Adds 7 one-time purchases from Renon's shop into the location pool. After buying an item from a slot, it will - revert to whatever it is in the vanilla game.""" + """ + Adds 7 one-time purchases from Renon's shop into the location pool. + After buying an item from a slot, it will revert to whatever it is in the vanilla game. + """ display_name = "Shopsanity" class ShopPrices(Choice): - """Randomizes the amount of gold each item costs in Renon's shop. - Use the below options to control how much or little an item can cost.""" + """ + Randomizes the amount of gold each item costs in Renon's shop. + Use the Minimum and Maximum Gold Price options to control how much or how little an item can cost. + """ display_name = "Shop Prices" option_vanilla = 0 option_randomized = 1 @@ -180,7 +213,9 @@ class ShopPrices(Choice): class MinimumGoldPrice(Range): - """The lowest amount of gold an item can cost in Renon's shop, divided by 100.""" + """ + The lowest amount of gold an item can cost in Renon's shop, divided by 100. + """ display_name = "Minimum Gold Price" range_start = 1 range_end = 50 @@ -188,7 +223,9 @@ class MinimumGoldPrice(Range): class MaximumGoldPrice(Range): - """The highest amount of gold an item can cost in Renon's shop, divided by 100.""" + """ + The highest amount of gold an item can cost in Renon's shop, divided by 100. + """ display_name = "Maximum Gold Price" range_start = 1 range_end = 50 @@ -196,8 +233,9 @@ class MaximumGoldPrice(Range): class PostBehemothBoss(Choice): - """Sets which boss is fought in the vampire triplets' room in Castle Center by which characters after defeating - Behemoth.""" + """ + Sets which boss is fought in the vampire triplets' room in Castle Center by which characters after defeating Behemoth. + """ display_name = "Post-Behemoth Boss" option_vanilla = 0 option_inverted = 1 @@ -207,7 +245,9 @@ class PostBehemothBoss(Choice): class RoomOfClocksBoss(Choice): - """Sets which boss is fought at Room of Clocks by which characters.""" + """ + Sets which boss is fought at Room of Clocks by which characters. + """ display_name = "Room of Clocks Boss" option_vanilla = 0 option_inverted = 1 @@ -217,7 +257,9 @@ class RoomOfClocksBoss(Choice): class RenonFightCondition(Choice): - """Sets the condition on which the Renon fight will trigger.""" + """ + Sets the condition on which the Renon fight will trigger. + """ display_name = "Renon Fight Condition" option_never = 0 option_spend_30k = 1 @@ -226,7 +268,9 @@ class RenonFightCondition(Choice): class VincentFightCondition(Choice): - """Sets the condition on which the vampire Vincent fight will trigger.""" + """ + Sets the condition on which the vampire Vincent fight will trigger. + """ display_name = "Vincent Fight Condition" option_never = 0 option_wait_16_days = 1 @@ -235,7 +279,9 @@ class VincentFightCondition(Choice): class BadEndingCondition(Choice): - """Sets the condition on which the currently-controlled character's Bad Ending will trigger.""" + """ + Sets the condition on which the currently-controlled character's Bad Ending will trigger. + """ display_name = "Bad Ending Condition" option_never = 0 option_kill_vincent = 1 @@ -244,24 +290,32 @@ class BadEndingCondition(Choice): class IncreaseItemLimit(DefaultOnToggle): - """Increases the holding limit of usable items from 10 to 99 of each item.""" + """ + Increases the holding limit of usable items from 10 to 99 of each item. + """ display_name = "Increase Item Limit" class NerfHealingItems(Toggle): - """Decreases the amount of health healed by Roast Chickens to 25%, Roast Beefs to 50%, and Healing Kits to 80%.""" + """ + Decreases the amount of health healed by Roast Chickens to 25%, Roast Beefs to 50%, and Healing Kits to 80%. + """ display_name = "Nerf Healing Items" class LoadingZoneHeals(DefaultOnToggle): - """Whether end-of-level loading zones restore health and cure status aliments or not. - Recommended off for those looking for more of a survival horror experience!""" + """ + Whether end-of-level loading zones restore health and cure status aliments or not. + Recommended off for those looking for more of a survival horror experience! + """ display_name = "Loading Zone Heals" class InvisibleItems(Choice): - """Sets which items are visible in their locations and which are invisible until picked up. - 'Chance' gives each item a 50/50 chance of being visible or invisible.""" + """ + Sets which items are visible in their locations and which are invisible until picked up. + 'Chance' gives each item a 50/50 chance of being visible or invisible. + """ display_name = "Invisible Items" option_vanilla = 0 option_reveal_all = 1 @@ -271,21 +325,25 @@ class InvisibleItems(Choice): class DropPreviousSubWeapon(Toggle): - """When receiving a sub-weapon, the one you had before will drop behind you, so it can be taken back if desired.""" + """ + When receiving a sub-weapon, the one you had before will drop behind you, so it can be taken back if desired. + """ display_name = "Drop Previous Sub-weapon" class PermanentPowerUps(Toggle): - """Replaces PowerUps with PermaUps, which upgrade your B weapon level permanently and will stay even after - dying and/or continuing. - To compensate, only two will be in the pool overall, and they will not drop from any enemy or projectile.""" + """ + Replaces PowerUps with PermaUps, which upgrade your B weapon level permanently and will stay even after dying and/or continuing. + To compensate, only two will be in the pool overall, and they will not drop from any enemy or projectile. + """ display_name = "Permanent PowerUps" class IceTrapPercentage(Range): - """Replaces a percentage of junk items with Ice Traps. - These will be visibly disguised as other items, and receiving one will freeze you - as if you were hit by Camilla's ice cloud attack.""" + """ + Replaces a percentage of junk items with Ice Traps. + These will be visibly disguised as other items, and receiving one will freeze you as if you were hit by Camilla's ice cloud attack. + """ display_name = "Ice Trap Percentage" range_start = 0 range_end = 100 @@ -293,7 +351,9 @@ class IceTrapPercentage(Range): class IceTrapAppearance(Choice): - """What items Ice Traps can possibly be disguised as.""" + """ + What items Ice Traps can possibly be disguised as. + """ display_name = "Ice Trap Appearance" option_major_only = 0 option_junk_only = 1 @@ -302,31 +362,34 @@ class IceTrapAppearance(Choice): class DisableTimeRestrictions(Toggle): - """Disables the restriction on every event and door that requires the current time - to be within a specific range, so they can be triggered at any time. + """ + Disables the restriction on every event and door that requires the current time to be within a specific range, so they can be triggered at any time. This includes all sun/moon doors and, in the Villa, the meeting with Rosa and the fountain pillar. - The Villa coffin is not affected by this.""" + The Villa coffin is not affected by this. + """ display_name = "Disable Time Requirements" class SkipGondolas(Toggle): - """Makes jumping on and activating a gondola in Tunnel instantly teleport you - to the other station, thereby skipping the entire three-minute ride. - The item normally at the gondola transfer point is moved to instead be - near the red gondola at its station.""" + """ + Makes jumping on and activating a gondola in Tunnel instantly teleport you to the other station, thereby skipping the entire three-minute ride. + The item normally at the gondola transfer point is moved to instead be near the red gondola at its station. + """ display_name = "Skip Gondolas" class SkipWaterwayBlocks(Toggle): - """Opens the door to the third switch in Underground Waterway from the start so that the jumping across floating - brick platforms won't have to be done. Shopping at the Contract on the other side of them may still be logically - required if Shopsanity is on.""" + """ + Opens the door to the third switch in Underground Waterway from the start so that the jumping across floating brick platforms won't have to be done. + Shopping at the Contract on the other side of them may still be logically required if Shopsanity is on. + """ display_name = "Skip Waterway Blocks" class Countdown(Choice): - """Displays, near the HUD clock and below the health bar, the number of unobtained progression-marked items - or the total check locations remaining in the stage you are currently in.""" + """ + Displays, near the HUD clock and below the health bar, the number of unobtained progression-marked items or the total check locations remaining in the stage you are currently in. + """ display_name = "Countdown" option_none = 0 option_majors = 1 @@ -335,19 +398,21 @@ class Countdown(Choice): class BigToss(Toggle): - """Makes every non-immobilizing damage source launch you as if you got hit by Behemoth's charge. + """ + Makes every non-immobilizing damage source launch you as if you got hit by Behemoth's charge. Press A while tossed to cancel the launch momentum and avoid being thrown off ledges. Hold Z to have all incoming damage be treated as it normally would. - Any tricks that might be possible with it are NOT considered in logic by any options.""" + Any tricks that might be possible with it are not in logic. + """ display_name = "Big Toss" class PantherDash(Choice): - """Hold C-right at any time to sprint way faster. Any tricks that might be - possible with it are NOT considered in logic by any options and any boss - fights with boss health meters, if started, are expected to be finished - before leaving their arenas if Dracula's Condition is bosses. Jumpless will - prevent jumping while moving at the increased speed to ensure logic cannot be broken with it.""" + """ + Hold C-right at any time to sprint way faster. + Any tricks that are possible with it are not in logic and any boss fights with boss health meters, if started, are expected to be finished before leaving their arenas if Dracula's Condition is bosses. + Jumpless will prevent jumping while moving at the increased speed to make logic harder to break with it. + """ display_name = "Panther Dash" option_off = 0 option_on = 1 @@ -356,19 +421,25 @@ class PantherDash(Choice): class IncreaseShimmySpeed(Toggle): - """Increases the speed at which characters shimmy left and right while hanging on ledges.""" + """ + Increases the speed at which characters shimmy left and right while hanging on ledges. + """ display_name = "Increase Shimmy Speed" class FallGuard(Toggle): - """Removes fall damage from landing too hard. Note that falling for too long will still result in instant death.""" + """ + Removes fall damage from landing too hard. Note that falling for too long will still result in instant death. + """ display_name = "Fall Guard" class BackgroundMusic(Choice): - """Randomizes or disables the music heard throughout the game. + """ + Randomizes or disables the music heard throughout the game. Randomized music is split into two pools: songs that loop and songs that don't. - The "lead-in" versions of some songs will be paired accordingly.""" + The "lead-in" versions of some songs will be paired accordingly. + """ display_name = "Background Music" option_normal = 0 option_disabled = 1 @@ -377,8 +448,10 @@ class BackgroundMusic(Choice): class MapLighting(Choice): - """Randomizes the lighting color RGB values on every map during every time of day to be literally anything. - The colors and/or shading of the following things are affected: fog, maps, player, enemies, and some objects.""" + """ + Randomizes the lighting color RGB values on every map during every time of day to be literally anything. + The colors and/or shading of the following things are affected: fog, maps, player, enemies, and some objects. + """ display_name = "Map Lighting" option_normal = 0 option_randomized = 1 @@ -386,12 +459,16 @@ class MapLighting(Choice): class CinematicExperience(Toggle): - """Enables an unused film reel effect on every cutscene in the game. Purely cosmetic.""" + """ + Enables an unused film reel effect on every cutscene in the game. Purely cosmetic. + """ display_name = "Cinematic Experience" class WindowColorR(Range): - """The red value for the background color of the text windows during gameplay.""" + """ + The red value for the background color of the text windows during gameplay. + """ display_name = "Window Color R" range_start = 0 range_end = 15 @@ -399,7 +476,9 @@ class WindowColorR(Range): class WindowColorG(Range): - """The green value for the background color of the text windows during gameplay.""" + """ + The green value for the background color of the text windows during gameplay. + """ display_name = "Window Color G" range_start = 0 range_end = 15 @@ -407,7 +486,9 @@ class WindowColorG(Range): class WindowColorB(Range): - """The blue value for the background color of the text windows during gameplay.""" + """ + The blue value for the background color of the text windows during gameplay. + """ display_name = "Window Color B" range_start = 0 range_end = 15 @@ -415,7 +496,9 @@ class WindowColorB(Range): class WindowColorA(Range): - """The alpha value for the background color of the text windows during gameplay.""" + """ + The alpha value for the background color of the text windows during gameplay. + """ display_name = "Window Color A" range_start = 0 range_end = 15 @@ -423,9 +506,10 @@ class WindowColorA(Range): class DeathLink(Choice): - """When you die, everyone dies. Of course the reverse is true too. - Explosive: Makes received DeathLinks kill you via the Magical Nitro explosion - instead of the normal death animation.""" + """ + When you die, everyone dies. Of course the reverse is true too. + Explosive: Makes received DeathLinks kill you via the Magical Nitro explosion instead of the normal death animation. + """ display_name = "DeathLink" option_off = 0 alias_no = 0 @@ -437,6 +521,7 @@ class DeathLink(Choice): @dataclass class CV64Options(PerGameCommonOptions): + start_inventory_from_pool: StartInventoryPool character_stages: CharacterStages stage_shuffle: StageShuffle starting_stage: StartingStage @@ -479,13 +564,26 @@ class CV64Options(PerGameCommonOptions): big_toss: BigToss panther_dash: PantherDash increase_shimmy_speed: IncreaseShimmySpeed - background_music: BackgroundMusic - map_lighting: MapLighting - fall_guard: FallGuard - cinematic_experience: CinematicExperience window_color_r: WindowColorR window_color_g: WindowColorG window_color_b: WindowColorB window_color_a: WindowColorA + background_music: BackgroundMusic + map_lighting: MapLighting + fall_guard: FallGuard + cinematic_experience: CinematicExperience death_link: DeathLink - start_inventory_from_pool: StartInventoryPool + + +cv64_option_groups = [ + OptionGroup("gameplay tweaks", [ + HardItemPool, ShopPrices, MinimumGoldPrice, MaximumGoldPrice, PostBehemothBoss, RoomOfClocksBoss, + RenonFightCondition, VincentFightCondition, BadEndingCondition, IncreaseItemLimit, NerfHealingItems, + LoadingZoneHeals, InvisibleItems, DropPreviousSubWeapon, PermanentPowerUps, IceTrapPercentage, + IceTrapAppearance, DisableTimeRestrictions, SkipGondolas, SkipWaterwayBlocks, Countdown, BigToss, PantherDash, + IncreaseShimmySpeed, FallGuard, DeathLink + ]), + OptionGroup("cosmetics", [ + WindowColorR, WindowColorG, WindowColorB, WindowColorA, BackgroundMusic, MapLighting, CinematicExperience + ]) +] From 89d0dae299d35e8fd8bd0dae4dc10a84f32ccd34 Mon Sep 17 00:00:00 2001 From: agilbert1412 Date: Thu, 23 May 2024 03:22:28 +0300 Subject: [PATCH 10/12] Stardew valley: Create Option Groups (#3376) * - Fix link in Stardew Setup Guide * - Create option groups for Stardew Valley * - Cleaned up the imports * - Fixed double quotes and trailing comma * - Improve order in the multipliers category --- worlds/stardew_valley/__init__.py | 2 + worlds/stardew_valley/option_groups.py | 65 ++++++++++++++++++++++++++ worlds/stardew_valley/options.py | 14 +++--- 3 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 worlds/stardew_valley/option_groups.py diff --git a/worlds/stardew_valley/__init__.py b/worlds/stardew_valley/__init__.py index 6a82a2a26dd8..dafb1c64730f 100644 --- a/worlds/stardew_valley/__init__.py +++ b/worlds/stardew_valley/__init__.py @@ -13,6 +13,7 @@ from .logic.bundle_logic import BundleLogic from .logic.logic import StardewLogic from .logic.time_logic import MAX_MONTHS +from .option_groups import sv_option_groups from .options import StardewValleyOptions, SeasonRandomization, Goal, BundleRandomization, BundlePrice, NumberOfLuckBuffs, NumberOfMovementBuffs, \ BackpackProgression, BuildingProgression, ExcludeGingerIsland, TrapItems, EntranceRandomization from .presets import sv_options_presets @@ -39,6 +40,7 @@ class StardewWebWorld(WebWorld): theme = "dirt" bug_report_page = "https://github.com/agilbert1412/StardewArchipelago/issues/new?labels=bug&title=%5BBug%5D%3A+Brief+Description+of+bug+here" options_presets = sv_options_presets + option_groups = sv_option_groups tutorials = [ Tutorial( diff --git a/worlds/stardew_valley/option_groups.py b/worlds/stardew_valley/option_groups.py new file mode 100644 index 000000000000..50709c10fd49 --- /dev/null +++ b/worlds/stardew_valley/option_groups.py @@ -0,0 +1,65 @@ +from Options import OptionGroup, DeathLink, ProgressionBalancing, Accessibility +from .options import (Goal, StartingMoney, ProfitMargin, BundleRandomization, BundlePrice, + EntranceRandomization, SeasonRandomization, Cropsanity, BackpackProgression, + ToolProgression, ElevatorProgression, SkillProgression, BuildingProgression, + FestivalLocations, ArcadeMachineLocations, SpecialOrderLocations, + QuestLocations, Fishsanity, Museumsanity, Friendsanity, FriendsanityHeartSize, + NumberOfMovementBuffs, NumberOfLuckBuffs, ExcludeGingerIsland, TrapItems, + MultipleDaySleepEnabled, MultipleDaySleepCost, ExperienceMultiplier, + FriendshipMultiplier, DebrisMultiplier, QuickStart, Gifting, FarmType, + Monstersanity, Shipsanity, Cooksanity, Chefsanity, Craftsanity, Mods) + +sv_option_groups = [ + OptionGroup("General", [ + Goal, + FarmType, + BundleRandomization, + BundlePrice, + EntranceRandomization, + ExcludeGingerIsland, + ]), + OptionGroup("Major Unlocks", [ + SeasonRandomization, + Cropsanity, + BackpackProgression, + ToolProgression, + ElevatorProgression, + SkillProgression, + BuildingProgression, + ]), + OptionGroup("Extra Shuffling", [ + FestivalLocations, + ArcadeMachineLocations, + SpecialOrderLocations, + QuestLocations, + Fishsanity, + Museumsanity, + Friendsanity, + FriendsanityHeartSize, + Monstersanity, + Shipsanity, + Cooksanity, + Chefsanity, + Craftsanity, + ]), + OptionGroup("Multipliers and Buffs", [ + StartingMoney, + ProfitMargin, + ExperienceMultiplier, + FriendshipMultiplier, + DebrisMultiplier, + NumberOfMovementBuffs, + NumberOfLuckBuffs, + TrapItems, + MultipleDaySleepEnabled, + MultipleDaySleepCost, + QuickStart, + ]), + OptionGroup("Advanced Options", [ + Gifting, + DeathLink, + Mods, + ProgressionBalancing, + Accessibility, + ]), +] diff --git a/worlds/stardew_valley/options.py b/worlds/stardew_valley/options.py index 191a634496e4..ba1ebfb9c177 100644 --- a/worlds/stardew_valley/options.py +++ b/worlds/stardew_valley/options.py @@ -697,8 +697,6 @@ class Mods(OptionSet): class StardewValleyOptions(PerGameCommonOptions): goal: Goal farm_type: FarmType - starting_money: StartingMoney - profit_margin: ProfitMargin bundle_randomization: BundleRandomization bundle_price: BundlePrice entrance_randomization: EntranceRandomization @@ -722,16 +720,18 @@ class StardewValleyOptions(PerGameCommonOptions): craftsanity: Craftsanity friendsanity: Friendsanity friendsanity_heart_size: FriendsanityHeartSize + exclude_ginger_island: ExcludeGingerIsland + quick_start: QuickStart + starting_money: StartingMoney + profit_margin: ProfitMargin + experience_multiplier: ExperienceMultiplier + friendship_multiplier: FriendshipMultiplier + debris_multiplier: DebrisMultiplier movement_buff_number: NumberOfMovementBuffs luck_buff_number: 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 From 8b6eae0a1433c0b53cf8d51147460b166b63f017 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Wed, 22 May 2024 20:22:39 -0400 Subject: [PATCH 11/12] Lingo: Add option groups (#3352) * Lingo: Add option groups * Touched up option docstrings --- worlds/lingo/__init__.py | 3 +- worlds/lingo/options.py | 60 ++++++++++++++++++++++++++++++---------- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/worlds/lingo/__init__.py b/worlds/lingo/__init__.py index 113c3928d21c..fa24fdc3bc63 100644 --- a/worlds/lingo/__init__.py +++ b/worlds/lingo/__init__.py @@ -9,12 +9,13 @@ from .datatypes import Room, RoomEntrance from .items import ALL_ITEM_TABLE, ITEMS_BY_GROUP, TRAP_ITEMS, LingoItem from .locations import ALL_LOCATION_TABLE, LOCATIONS_BY_GROUP -from .options import LingoOptions +from .options import LingoOptions, lingo_option_groups from .player_logic import LingoPlayerLogic from .regions import create_regions class LingoWebWorld(WebWorld): + option_groups = lingo_option_groups theme = "grass" tutorials = [Tutorial( "Multiworld Setup Guide", diff --git a/worlds/lingo/options.py b/worlds/lingo/options.py index 65f27269f2c8..1c1f645b8613 100644 --- a/worlds/lingo/options.py +++ b/worlds/lingo/options.py @@ -2,7 +2,8 @@ from schema import And, Schema -from Options import Toggle, Choice, DefaultOnToggle, Range, PerGameCommonOptions, StartInventoryPool, OptionDict +from Options import Toggle, Choice, DefaultOnToggle, Range, PerGameCommonOptions, StartInventoryPool, OptionDict, \ + OptionGroup from .items import TRAP_ITEMS @@ -32,8 +33,8 @@ class ProgressiveColorful(DefaultOnToggle): class LocationChecks(Choice): - """On "normal", there will be a location check for each panel set that would ordinarily open a door, as well as for - achievement panels and a small handful of other panels. + """Determines what locations are available. + On "normal", there will be a location check for each panel set that would ordinarily open a door, as well as for achievement panels and a small handful of other panels. On "reduced", many of the locations that are associated with opening doors are removed. On "insanity", every individual panel in the game is a location check.""" display_name = "Location Checks" @@ -43,8 +44,10 @@ class LocationChecks(Choice): class ShuffleColors(DefaultOnToggle): - """If on, an item is added to the pool for every puzzle color (besides White). - You will need to unlock the requisite colors in order to be able to solve puzzles of that color.""" + """ + If on, an item is added to the pool for every puzzle color (besides White). + You will need to unlock the requisite colors in order to be able to solve puzzles of that color. + """ display_name = "Shuffle Colors" @@ -62,20 +65,25 @@ class ShufflePaintings(Toggle): class EnablePilgrimage(Toggle): - """If on, you are required to complete a pilgrimage in order to access the Pilgrim Antechamber. + """Determines how the pilgrimage works. + If on, you are required to complete a pilgrimage in order to access the Pilgrim Antechamber. If off, the pilgrimage will be deactivated, and the sun painting will be added to the pool, even if door shuffle is off.""" display_name = "Enable Pilgrimage" class PilgrimageAllowsRoofAccess(DefaultOnToggle): - """If on, you may use the Crossroads roof access during a pilgrimage (and you may be expected to do so). - Otherwise, pilgrimage will be deactivated when going up the stairs.""" + """ + If on, you may use the Crossroads roof access during a pilgrimage (and you may be expected to do so). + Otherwise, pilgrimage will be deactivated when going up the stairs. + """ display_name = "Allow Roof Access for Pilgrimage" class PilgrimageAllowsPaintings(DefaultOnToggle): - """If on, you may use paintings during a pilgrimage (and you may be expected to do so). - Otherwise, pilgrimage will be deactivated when going through a painting.""" + """ + If on, you may use paintings during a pilgrimage (and you may be expected to do so). + Otherwise, pilgrimage will be deactivated when going through a painting. + """ display_name = "Allow Paintings for Pilgrimage" @@ -137,8 +145,10 @@ class Level2Requirement(Range): class EarlyColorHallways(Toggle): - """When on, a painting warp to the color hallways area will appear in the starting room. - This lets you avoid being trapped in the starting room for long periods of time when door shuffle is on.""" + """ + When on, a painting warp to the color hallways area will appear in the starting room. + This lets you avoid being trapped in the starting room for long periods of time when door shuffle is on. + """ display_name = "Early Color Hallways" @@ -151,8 +161,10 @@ class TrapPercentage(Range): class TrapWeights(OptionDict): - """Specify the distribution of traps that should be placed into the pool. - If you don't want a specific type of trap, set the weight to zero.""" + """ + Specify the distribution of traps that should be placed into the pool. + If you don't want a specific type of trap, set the weight to zero. + """ display_name = "Trap Weights" schema = Schema({trap_name: And(int, lambda n: n >= 0) for trap_name in TRAP_ITEMS}) default = {trap_name: 1 for trap_name in TRAP_ITEMS} @@ -171,6 +183,26 @@ class DeathLink(Toggle): display_name = "Death Link" +lingo_option_groups = [ + OptionGroup("Pilgrimage", [ + EnablePilgrimage, + PilgrimageAllowsRoofAccess, + PilgrimageAllowsPaintings, + SunwarpAccess, + ShuffleSunwarps, + ]), + OptionGroup("Fine-tuning", [ + ProgressiveOrangeTower, + ProgressiveColorful, + MasteryAchievements, + Level2Requirement, + TrapPercentage, + TrapWeights, + PuzzleSkipPercentage, + ]) +] + + @dataclass class LingoOptions(PerGameCommonOptions): shuffle_doors: ShuffleDoors From e1ff5073b5fbfc378b800b102b83afa7b7be6dcf Mon Sep 17 00:00:00 2001 From: Zach Parks Date: Thu, 23 May 2024 02:08:08 -0500 Subject: [PATCH 12/12] WebHost, Core: Move item and location descriptions to `WebWorld` responsibilities. (#2508) Co-authored-by: Doug Hoskisson Co-authored-by: el-u <109771707+el-u@users.noreply.github.com> --- docs/world api.md | 107 +++++++++++++----------------- test/general/test_items.py | 9 --- test/general/test_locations.py | 9 --- test/webhost/test_descriptions.py | 23 +++++++ worlds/AutoWorld.py | 49 +++----------- worlds/dark_souls_3/Items.py | 6 +- worlds/dark_souls_3/__init__.py | 4 +- 7 files changed, 83 insertions(+), 124 deletions(-) create mode 100644 test/webhost/test_descriptions.py diff --git a/docs/world api.md b/docs/world api.md index 6714fa3a21fb..37638c3c66cc 100644 --- a/docs/world api.md +++ b/docs/world api.md @@ -121,6 +121,53 @@ class RLWeb(WebWorld): # ... ``` +* `location_descriptions` (optional) WebWorlds can provide a map that contains human-friendly descriptions of locations +or location groups. + + ```python + # locations.py + location_descriptions = { + "Red Potion #6": "In a secret destructible block under the second stairway", + "L2 Spaceship": """ + The group of all items in the spaceship in Level 2. + + This doesn't include the item on the spaceship door, since it can be + accessed without the Spaceship Key. + """ + } + + # __init__.py + from worlds.AutoWorld import WebWorld + from .locations import location_descriptions + + + class MyGameWeb(WebWorld): + location_descriptions = location_descriptions + ``` + +* `item_descriptions` (optional) WebWorlds can provide a map that contains human-friendly descriptions of items or item +groups. + + ```python + # items.py + item_descriptions = { + "Red Potion": "A standard health potion", + "Spaceship Key": """ + The key to the spaceship in Level 2. + + This is necessary to get to the Star Realm. + """, + } + + # __init__.py + from worlds.AutoWorld import WebWorld + from .items import item_descriptions + + + class MyGameWeb(WebWorld): + item_descriptions = item_descriptions + ``` + ### MultiWorld Object The `MultiWorld` object references the whole multiworld (all items and locations for all players) and is accessible @@ -178,36 +225,6 @@ Classification is one of `LocationProgressType.DEFAULT`, `PRIORITY` or `EXCLUDED The Fill algorithm will force progression items to be placed at priority locations, giving a higher chance of them being required, and will prevent progression and useful items from being placed at excluded locations. -#### Documenting Locations - -Worlds can optionally provide a `location_descriptions` map which contains human-friendly descriptions of locations and -location groups. These descriptions will show up in location-selection options on the options pages. - -```python -# locations.py - -location_descriptions = { - "Red Potion #6": "In a secret destructible block under the second stairway", - "L2 Spaceship": - """ - The group of all items in the spaceship in Level 2. - - This doesn't include the item on the spaceship door, since it can be accessed without the Spaceship Key. - """ -} -``` - -```python -# __init__.py - -from worlds.AutoWorld import World -from .locations import location_descriptions - - -class MyGameWorld(World): - location_descriptions = location_descriptions -``` - ### Items Items are all things that can "drop" for your game. This may be RPG items like weapons, or technologies you normally @@ -232,36 +249,6 @@ Other classifications include: * `progression_skip_balancing`: the combination of `progression` and `skip_balancing`, i.e., a progression item that will not be moved around by progression balancing; used, e.g., for currency or tokens, to not flood early spheres -#### Documenting Items - -Worlds can optionally provide an `item_descriptions` map which contains human-friendly descriptions of items and item -groups. These descriptions will show up in item-selection options on the options pages. - -```python -# items.py - -item_descriptions = { - "Red Potion": "A standard health potion", - "Spaceship Key": - """ - The key to the spaceship in Level 2. - - This is necessary to get to the Star Realm. - """ -} -``` - -```python -# __init__.py - -from worlds.AutoWorld import World -from .items import item_descriptions - - -class MyGameWorld(World): - item_descriptions = item_descriptions -``` - ### Events An Event is a special combination of a Location and an Item, with both having an `id` of `None`. These can be used to diff --git a/test/general/test_items.py b/test/general/test_items.py index 7c0b7050c670..9cc91a1b00ef 100644 --- a/test/general/test_items.py +++ b/test/general/test_items.py @@ -64,15 +64,6 @@ def test_items_in_datapackage(self): for item in multiworld.itempool: self.assertIn(item.name, world_type.item_name_to_id) - def test_item_descriptions_have_valid_names(self): - """Ensure all item descriptions match an item name or item group name""" - for game_name, world_type in AutoWorldRegister.world_types.items(): - valid_names = world_type.item_names.union(world_type.item_name_groups) - for name in world_type.item_descriptions: - with self.subTest("Name should be valid", game=game_name, item=name): - self.assertIn(name, valid_names, - "All item descriptions must match defined item names") - def test_itempool_not_modified(self): """Test that worlds don't modify the itempool after `create_items`""" gen_steps = ("generate_early", "create_regions", "create_items") diff --git a/test/general/test_locations.py b/test/general/test_locations.py index 2ac059312c17..4b95ebd22c90 100644 --- a/test/general/test_locations.py +++ b/test/general/test_locations.py @@ -66,12 +66,3 @@ def test_location_group(self): for location in locations: self.assertIn(location, world_type.location_name_to_id) self.assertNotIn(group_name, world_type.location_name_to_id) - - def test_location_descriptions_have_valid_names(self): - """Ensure all location descriptions match a location name or location group name""" - for game_name, world_type in AutoWorldRegister.world_types.items(): - valid_names = world_type.location_names.union(world_type.location_name_groups) - for name in world_type.location_descriptions: - with self.subTest("Name should be valid", game=game_name, location=name): - self.assertIn(name, valid_names, - "All location descriptions must match defined location names") diff --git a/test/webhost/test_descriptions.py b/test/webhost/test_descriptions.py new file mode 100644 index 000000000000..70f375b51cf0 --- /dev/null +++ b/test/webhost/test_descriptions.py @@ -0,0 +1,23 @@ +import unittest + +from worlds.AutoWorld import AutoWorldRegister + + +class TestWebDescriptions(unittest.TestCase): + def test_item_descriptions_have_valid_names(self) -> None: + """Ensure all item descriptions match an item name or item group name""" + for game_name, world_type in AutoWorldRegister.world_types.items(): + valid_names = world_type.item_names.union(world_type.item_name_groups) + for name in world_type.web.item_descriptions: + with self.subTest("Name should be valid", game=game_name, item=name): + self.assertIn(name, valid_names, + "All item descriptions must match defined item names") + + def test_location_descriptions_have_valid_names(self) -> None: + """Ensure all location descriptions match a location name or location group name""" + for game_name, world_type in AutoWorldRegister.world_types.items(): + valid_names = world_type.location_names.union(world_type.location_name_groups) + for name in world_type.web.location_descriptions: + with self.subTest("Name should be valid", game=game_name, location=name): + self.assertIn(name, valid_names, + "All location descriptions must match defined location names") diff --git a/worlds/AutoWorld.py b/worlds/AutoWorld.py index b564932eb9b2..32a84f5d577f 100644 --- a/worlds/AutoWorld.py +++ b/worlds/AutoWorld.py @@ -3,13 +3,12 @@ import hashlib import logging import pathlib -from random import Random -import re import sys import time +from random import Random from dataclasses import make_dataclass -from typing import (Any, Callable, ClassVar, Dict, FrozenSet, List, Mapping, - Optional, Set, TextIO, Tuple, TYPE_CHECKING, Type, Union) +from typing import (Any, Callable, ClassVar, Dict, FrozenSet, List, Mapping, Optional, Set, TextIO, Tuple, + TYPE_CHECKING, Type, Union) from Options import ( ExcludeLocations, ItemLinks, LocalItems, NonLocalItems, OptionGroup, PerGameCommonOptions, @@ -55,17 +54,12 @@ def __new__(mcs, name: str, bases: Tuple[type, ...], dct: Dict[str, Any]) -> Aut dct["item_name_groups"] = {group_name: frozenset(group_set) for group_name, group_set in dct.get("item_name_groups", {}).items()} dct["item_name_groups"]["Everything"] = dct["item_names"] - dct["item_descriptions"] = {name: _normalize_description(description) for name, description - in dct.get("item_descriptions", {}).items()} - dct["item_descriptions"]["Everything"] = "All items in the entire game." + dct["location_names"] = frozenset(dct["location_name_to_id"]) dct["location_name_groups"] = {group_name: frozenset(group_set) for group_name, group_set in dct.get("location_name_groups", {}).items()} dct["location_name_groups"]["Everywhere"] = dct["location_names"] dct["all_item_and_group_names"] = frozenset(dct["item_names"] | set(dct.get("item_name_groups", {}))) - dct["location_descriptions"] = {name: _normalize_description(description) for name, description - in dct.get("location_descriptions", {}).items()} - dct["location_descriptions"]["Everywhere"] = "All locations in the entire game." # move away from get_required_client_version function if "game" in dct: @@ -226,6 +220,12 @@ class WebWorld(metaclass=WebWorldRegister): option_groups: ClassVar[List[OptionGroup]] = [] """Ordered list of option groupings. Any options not set in a group will be placed in a pre-built "Game Options".""" + location_descriptions: Dict[str, str] = {} + """An optional map from location names (or location group names) to brief descriptions for users.""" + + item_descriptions: Dict[str, str] = {} + """An optional map from item names (or item group names) to brief descriptions for users.""" + class World(metaclass=AutoWorldRegister): """A World object encompasses a game's Items, Locations, Rules and additional data or functionality required. @@ -252,23 +252,9 @@ class World(metaclass=AutoWorldRegister): item_name_groups: ClassVar[Dict[str, Set[str]]] = {} """maps item group names to sets of items. Example: {"Weapons": {"Sword", "Bow"}}""" - item_descriptions: ClassVar[Dict[str, str]] = {} - """An optional map from item names (or item group names) to brief descriptions for users. - - Individual newlines and indentation will be collapsed into spaces before these descriptions are - displayed. This may cover only a subset of items. - """ - location_name_groups: ClassVar[Dict[str, Set[str]]] = {} """maps location group names to sets of locations. Example: {"Sewer": {"Sewer Key Drop 1", "Sewer Key Drop 2"}}""" - location_descriptions: ClassVar[Dict[str, str]] = {} - """An optional map from location names (or location group names) to brief descriptions for users. - - Individual newlines and indentation will be collapsed into spaces before these descriptions are - displayed. This may cover only a subset of locations. - """ - data_version: ClassVar[int] = 0 """ Increment this every time something in your world's names/id mappings changes. @@ -572,18 +558,3 @@ def data_package_checksum(data: "GamesPackage") -> str: assert sorted(data) == list(data), "Data not ordered" from NetUtils import encode return hashlib.sha1(encode(data).encode()).hexdigest() - - -def _normalize_description(description): - """ - Normalizes a description in item_descriptions or location_descriptions. - - This allows authors to write descritions with nice indentation and line lengths in their world - definitions without having it affect the rendered format. - """ - # First, collapse the whitespace around newlines and the ends of the description. - description = re.sub(r' *\n *', '\n', description.strip()) - # Next, condense individual newlines into spaces. - description = re.sub(r'(? dict: ]] item_descriptions = { - "Cinders": """ - All four Cinders of a Lord. - - Once you have these four, you can fight Soul of Cinder and win the game. - """, + "Cinders": "All four Cinders of a Lord.\n\nOnce you have these four, you can fight Soul of Cinder and win the game.", } _all_items = _vanilla_items + _dlc_items diff --git a/worlds/dark_souls_3/__init__.py b/worlds/dark_souls_3/__init__.py index b4c231cdea1b..c4b2232b32dc 100644 --- a/worlds/dark_souls_3/__init__.py +++ b/worlds/dark_souls_3/__init__.py @@ -35,6 +35,8 @@ class DarkSouls3Web(WebWorld): tutorials = [setup_en, setup_fr] + item_descriptions = item_descriptions + class DarkSouls3World(World): """ @@ -61,8 +63,6 @@ class DarkSouls3World(World): "Cinders of a Lord - Lothric Prince" } } - item_descriptions = item_descriptions - def __init__(self, multiworld: MultiWorld, player: int): super().__init__(multiworld, player)