diff --git a/data/options.yaml b/data/options.yaml index b9bacaa0d103..30bd328f99a0 100644 --- a/data/options.yaml +++ b/data/options.yaml @@ -17,10 +17,10 @@ # A. This is a .yaml file. You are allowed to use most characters. # To test if your yaml is valid or not, you can use this website: # http://www.yamllint.com/ -# You can also verify your Archipelago settings are valid at this site: +# You can also verify that your Archipelago options are valid at this site: # https://archipelago.gg/check -# Your name in-game. Spaces will be replaced with underscores and there is a 16-character limit. +# Your name in-game, limited to 16 characters. # {player} will be replaced with the player's slot number. # {PLAYER} will be replaced with the player's slot number, if that slot number is greater than 1. # {number} will be replaced with the counter value of the name. diff --git a/docs/network protocol.md b/docs/network protocol.md index c6d6cf6887e6..9f2c07883b9d 100644 --- a/docs/network protocol.md +++ b/docs/network protocol.md @@ -345,7 +345,7 @@ Sent to the server to update on the sender's status. Examples include readiness #### Arguments | Name | Type | Notes | | ---- | ---- | ----- | -| status | ClientStatus\[int\] | One of [Client States](#Client-States). Send as int. Follow the link for more information. | +| status | ClientStatus\[int\] | One of [Client States](#ClientStatus). Send as int. Follow the link for more information. | ### Say Basic chat command which sends text to the server to be distributed to other clients. diff --git a/docs/world api.md b/docs/world api.md index fd8e0988e567..f82ef40a98f8 100644 --- a/docs/world api.md +++ b/docs/world api.md @@ -738,8 +738,9 @@ def generate_output(self, output_directory: str) -> None: 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 is filled with the `fill_slot_data` method of your world by returning -a `Dict[str, Any]`, but, to not waste resources, should be limited to data that is absolutely necessary. Slot data is -sent to your client once it has successfully [connected](network%20protocol.md#connected). +a `dict` with `str` keys that can be serialized with json. +But, to not waste resources, it should be limited to data that is absolutely necessary. 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 sending option results that the client needs to be aware of. diff --git a/worlds/AutoWorld.py b/worlds/AutoWorld.py index b282c7deb8bd..dd0f46f6a6d1 100644 --- a/worlds/AutoWorld.py +++ b/worlds/AutoWorld.py @@ -7,8 +7,8 @@ import sys import time from dataclasses import make_dataclass -from typing import Any, Callable, ClassVar, Dict, Set, Tuple, FrozenSet, List, Optional, TYPE_CHECKING, TextIO, Type, \ - Union +from typing import (Any, Callable, ClassVar, Dict, FrozenSet, List, Mapping, + Optional, Set, TextIO, Tuple, TYPE_CHECKING, Type, Union) from Options import PerGameCommonOptions from BaseClasses import CollectionState @@ -365,13 +365,19 @@ def generate_output(self, output_directory: str) -> None: If you need any last-second randomization, use self.random instead.""" pass - def fill_slot_data(self) -> Dict[str, Any]: # json of WebHostLib.models.Slot - """Fill in the `slot_data` field in the `Connected` network package. + def fill_slot_data(self) -> Mapping[str, Any]: # json of WebHostLib.models.Slot + """What is returned from this function will be in the `slot_data` field + in the `Connected` network package. + It should be a `dict` with `str` keys, and should be serializable with json. + This is a way the generator can give custom data to the client. The client will receive this as JSON in the `Connected` response. The generation does not wait for `generate_output` to complete before calling this. `threading.Event` can be used if you need to wait for something from `generate_output`.""" + # The reason for the `Mapping` type annotation, rather than `dict` + # is so that type checkers won't worry about the mutability of `dict`, + # so you can have more specific typing in your world implementation. return {} def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]): diff --git a/worlds/blasphemous/docs/setup_en.md b/worlds/blasphemous/docs/setup_en.md index cc238a492eb3..070d1ca4964b 100644 --- a/worlds/blasphemous/docs/setup_en.md +++ b/worlds/blasphemous/docs/setup_en.md @@ -15,7 +15,6 @@ Optional: - Quick Prie Dieu warp mod: [GitHub](https://github.com/BadMagic100/Blasphemous-PrieWarp) - Boots of Pleading mod: [GitHub](https://github.com/BrandenEK/Blasphemous-Boots-of-Pleading) - Double Jump mod: [GitHub](https://github.com/BrandenEK/Blasphemous-Double-Jump) -- PopTracker pack: [GitHub](https://github.com/sassyvania/Blasphemous-Randomizer-Maptracker) ## Mod Installer (Recommended) diff --git a/worlds/dark_souls_3/__init__.py b/worlds/dark_souls_3/__init__.py index 6efe4e4bc961..b4c231cdea1b 100644 --- a/worlds/dark_souls_3/__init__.py +++ b/worlds/dark_souls_3/__init__.py @@ -14,6 +14,7 @@ class DarkSouls3Web(WebWorld): bug_report_page = "https://github.com/Marechal-L/Dark-Souls-III-Archipelago-client/issues" + theme = "stone" setup_en = Tutorial( "Multiworld Setup Guide", "A guide to setting up the Archipelago Dark Souls III randomizer on your computer.", diff --git a/worlds/dark_souls_3/docs/setup_en.md b/worlds/dark_souls_3/docs/setup_en.md index 7a3ca4e9bd86..72c665af9507 100644 --- a/worlds/dark_souls_3/docs/setup_en.md +++ b/worlds/dark_souls_3/docs/setup_en.md @@ -11,7 +11,7 @@ ## General Concept - + **This mod can ban you permanently from the FromSoftware servers if used online.** The Dark Souls III AP Client is a dinput8.dll triggered when launching Dark Souls III. This .dll file will launch a command diff --git a/worlds/dark_souls_3/docs/setup_fr.md b/worlds/dark_souls_3/docs/setup_fr.md index 6ad86c4aff13..769d331bb98d 100644 --- a/worlds/dark_souls_3/docs/setup_fr.md +++ b/worlds/dark_souls_3/docs/setup_fr.md @@ -12,7 +12,7 @@ permettant de lire des informations de la partie et écrire des commandes pour i ## Procédures d'installation - + **Il y a des risques de bannissement permanent des serveurs FromSoftware si ce mod est utilisé en ligne.** Ce client a été testé sur la version Steam officielle du jeu (v1.15/1.35), peu importe les DLCs actuellement installés. diff --git a/worlds/pokemon_emerald/__init__.py b/worlds/pokemon_emerald/__init__.py index 95e549a32ef0..4d40dd196688 100644 --- a/worlds/pokemon_emerald/__init__.py +++ b/worlds/pokemon_emerald/__init__.py @@ -36,6 +36,7 @@ class PokemonEmeraldWebWorld(WebWorld): Webhost info for Pokemon Emerald """ theme = "ocean" + setup_en = Tutorial( "Multiworld Setup Guide", "A guide to playing Pokémon Emerald with Archipelago.", @@ -45,7 +46,16 @@ class PokemonEmeraldWebWorld(WebWorld): ["Zunawe"] ) - tutorials = [setup_en] + setup_es = Tutorial( + "Guía de configuración para Multiworld", + "Una guía para jugar Pokémon Emerald en Archipelago", + "Español", + "setup_es.md", + "setup/es", + ["nachocua"] + ) + + tutorials = [setup_en, setup_es] class PokemonEmeraldSettings(settings.Group): diff --git a/worlds/pokemon_emerald/docs/rom changes.md b/worlds/pokemon_emerald/docs/rom changes.md new file mode 100644 index 000000000000..9b189d08e76a --- /dev/null +++ b/worlds/pokemon_emerald/docs/rom changes.md @@ -0,0 +1,75 @@ +## QoL + +- The catch tutorial and cutscenes during your first visit to Petalburg are skipped +- The match call tutorial after you leave Devon Corp is skipped +- Cycling and running is allowed in every map (some exceptions like Fortree and Pacifidlog) +- When you run out of Repel steps, you'll be prompted to use another one if you have more in your bag +- Text is always rendered in its entirety on the first frame (instant text) +- With an option set, text will advance if A is held +- The message explaining that the trainer is about to send out a new pokemon is shortened to fit on two lines so that +you can still read the species when deciding whether to change pokemon +- The Pokemon Center Nurse dialogue is entirely removed except for the final text box +- When receiving TMs and HMs, the move that it teaches is consistently displayed in the "received item" message (by +default, certain ways of receiving items would only display the TM/HM number) +- The Pokedex starts in national mode +- The Oldale Pokemart sells Poke Balls at the start of the game +- Pauses during battles (e.g. the ~1 second pause at the start of a turn before an opponent uses a potion) are shorter +by 62.5% +- The sliding animation for trainers and wild pokemon at the start of a battle runs at double speed. +- Bag space was greatly expanded (there is room for one stack of every unique item in every pocket, plus a little bit +extra for some pockets) + - Save data format was changed as a result of this. Shrank some unused space and removed some multiplayer phrases from + the save data. + - Pretty much any code that checks for bag space is ignored or bypassed (this sounds dangerous, but with expanded bag + space you should pretty much never have a full bag unless you're trying to fill it up, and skipping those checks + greatly simplifies detecting when items are picked up) +- Pokemon are never disobedient +- When moving in the overworld, set the input priority based on the most recently pressed direction rather than by some +predetermined priority +- Shoal cave changes state every time you reload the map and is no longer tied to the RTC +- Increased safari zone steps from 500 to 50000 +- Trainers will not approach the player if the blind trainers option is set +- Changed trade evolutions to be possible without trading: + - Politoed: Use King's Rock in bag menu + - Alakazam: Level 37 + - Machamp: Level 37 + - Golem: Level 37 + - Slowking: Use King's Rock in bag menu + - Gengar: Level 37 + - Steelix: Use Metal Coat in bag menu + - Kingdra: Use Dragon Scale in bag menu + - Scizor: Use Metal Coat in bag menu + - Porygon2: Use Up-Grade in bag menu + - Milotic: Level 30 + - Huntail: Use Deep Sea Tooth in bag menu + - Gorebyss: Use Deep Sea Scale in bag menu + +## Game State Changes/Softlock Prevention + +- Mr. Briney never disappears or stops letting you use his ferry +- Prevent the player from flying or surfing until they have received the Pokedex +- The S.S. Tidal will be available at all times if you have the option enabled +- Some NPCs or tiles are removed on the creation of a new save file based on player options +- Ensured that every species has some damaging move by level 5 +- Route 115 may have strength boulders between the beach and cave entrance based on player options +- The Petalburg Gym is set up based on your player options rather than after the first 4 gyms +- The E4 guards will actually check all your badges (or gyms beaten based on your options) instead of just the Feather +Badge +- Steven cuts the conversation short in Granite Cave if you don't have the Letter +- Dock checks that you have the Devon Goods before asking you to deliver them (and thus opening the museum) +- Rydel gives you both bikes at the same time +- The man in Pacifidlog who gives you Frustration and Return will give you both at the same time, does not check +friendship first, and no longer has any behavior related to the RTC +- The woman who gives you the Soothe Bell in Slateport does not check friendship +- When trading the Scanner with Captain Stern, you will receive both the Deep Sea Tooth and Deep Sea Scale + +## Misc + +- You can no longer try to switch bikes in the bike shop +- The Seashore House only rewards you with 1 Soda Pop instead of 6 +- Many small changes that make it possible to swap single battles to double battles + - Includes some safeguards against two trainers seeing you and initiating a battle while one or both of them are + "single trainer double battles" +- Game now properly waits on vblank instead of spinning in a while loop +- Misc small changes to text for consistency +- Many bugfixes to the vanilla game code diff --git a/worlds/pokemon_emerald/docs/setup_es.md b/worlds/pokemon_emerald/docs/setup_es.md new file mode 100644 index 000000000000..65a74a9ddc70 --- /dev/null +++ b/worlds/pokemon_emerald/docs/setup_es.md @@ -0,0 +1,74 @@ +# Guía de Configuración para Pokémon Emerald + +## Software Requerido + +- [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases) +- Una ROM de Pokémon Emerald en Inglés. La comunidad de Archipelago no puede proveerla. +- [BizHawk](https://tasvideos.org/BizHawk/ReleaseHistory) 2.7 o posterior + +### Configuración de BizHawk + +Una vez que hayas instalado BizHawk, abre `EmuHawk.exe` y cambia las siguientes configuraciones: + +- Si estás usando BizHawk 2.7 o 2.8, ve a `Config > Customize`. En la pestaña Advanced, cambia el Lua Core de +`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 +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 +limpiarlas, selecciona el atajo y presiona la tecla Esc. + +## Software Opcional + +- [Pokémon Emerald AP Tracker](https://github.com/AliceMousie/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 +[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`. +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 +`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 + +## 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 +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 +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 +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`) +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 +jugar de manera offline; se sincronizará todo cuando te vuelvas a conectar. + +## Tracking Automático + +Pokémon Emerald tiene un Map Tracker completamente funcional que soporta auto-tracking. + +1. Descarga [Pokémon Emerald AP Tracker](https://github.com/AliceMousie/emerald-ap-tracker/releases/latest) y +[PopTracker](https://github.com/black-sliver/PopTracker/releases). +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 +contraseña (deja vacío este campo en caso de no utilizar contraseña). diff --git a/worlds/witness/__init__.py b/worlds/witness/__init__.py index c38898b33d4e..e985dde353aa 100644 --- a/worlds/witness/__init__.py +++ b/worlds/witness/__init__.py @@ -44,10 +44,6 @@ class WitnessWorld(World): """ game = "The Witness" topology_present = False - - StaticWitnessLogic() - StaticWitnessLocations() - StaticWitnessItems() web = WitnessWebWorld() options_dataclass = TheWitnessOptions diff --git a/worlds/witness/items.py b/worlds/witness/items.py index 41bc3c1bb8da..6802fd2a21b5 100644 --- a/worlds/witness/items.py +++ b/worlds/witness/items.py @@ -176,9 +176,14 @@ def get_filler_items(self, quantity: int) -> Dict[str, int]: # Read trap configuration data. trap_weight = self._world.options.trap_percentage / 100 - filler_weight = 1 - trap_weight + trap_items = self._world.options.trap_weights.value + + if not sum(trap_items.values()): + trap_weight = 0 # Add filler items to the list. + filler_weight = 1 - trap_weight + filler_items: Dict[str, float] filler_items = {name: data.definition.weight if isinstance(data.definition, WeightedItemDefinition) else 1 for (name, data) in self.item_data.items() if data.definition.category is ItemCategory.FILLER} @@ -187,8 +192,6 @@ def get_filler_items(self, quantity: int) -> Dict[str, int]: # Add trap items. if trap_weight > 0: - trap_items = {name: data.definition.weight if isinstance(data.definition, WeightedItemDefinition) else 1 - for (name, data) in self.item_data.items() if data.definition.category is ItemCategory.TRAP} filler_items.update({name: base_weight * trap_weight / sum(trap_items.values()) for name, base_weight in trap_items.items() if base_weight > 0}) @@ -267,3 +270,6 @@ def get_progressive_item_ids_in_pool(self) -> Dict[int, List[int]]: output[item.ap_code] = [StaticWitnessItems.item_data[child_item].ap_code for child_item in item.definition.child_item_names] return output + + +StaticWitnessItems() diff --git a/worlds/witness/locations.py b/worlds/witness/locations.py index d38cf9025806..cd6d71f46911 100644 --- a/worlds/witness/locations.py +++ b/worlds/witness/locations.py @@ -569,3 +569,6 @@ def add_location_late(self, entity_name: str): entity_hex = StaticWitnessLogic.ENTITIES_BY_NAME[entity_name]["entity_hex"] self.CHECK_LOCATION_TABLE[entity_hex] = entity_name self.CHECK_PANELHEX_TO_ID[entity_hex] = StaticWitnessLocations.get_id(entity_hex) + + +StaticWitnessLocations() diff --git a/worlds/witness/options.py b/worlds/witness/options.py index 68a4ac7fc231..18aa76d95ae9 100644 --- a/worlds/witness/options.py +++ b/worlds/witness/options.py @@ -1,5 +1,10 @@ from dataclasses import dataclass -from Options import Toggle, DefaultOnToggle, Range, Choice, PerGameCommonOptions + +from schema import Schema, And, Optional + +from Options import Toggle, DefaultOnToggle, Range, Choice, PerGameCommonOptions, OptionDict + +from worlds.witness.static_logic import WeightedItemDefinition, ItemCategory, StaticWitnessLogic class DisableNonRandomizedPuzzles(Toggle): @@ -172,6 +177,24 @@ class TrapPercentage(Range): default = 20 +class TrapWeights(OptionDict): + """Specify the weights determining how many copies of each trap item will be in your itempool. + If you don't want a specific type of trap, you can set the weight for it to 0 (Do not delete the entry outright!). + If you set all trap weights to 0, you will get no traps, bypassing the "Trap Percentage" option.""" + + display_name = "Trap Weights" + schema = Schema({ + trap_name: And(int, lambda n: n >= 0) + for trap_name, item_definition in StaticWitnessLogic.all_items.items() + if isinstance(item_definition, WeightedItemDefinition) and item_definition.category is ItemCategory.TRAP + }) + default = { + trap_name: item_definition.weight + for trap_name, item_definition in StaticWitnessLogic.all_items.items() + if isinstance(item_definition, WeightedItemDefinition) and item_definition.category is ItemCategory.TRAP + } + + class PuzzleSkipAmount(Range): """Adds this number of Puzzle Skips into the pool, if there is room. Puzzle Skips let you skip one panel. Works on most panels in the game - The only big exception is The Challenge.""" @@ -237,6 +260,7 @@ class TheWitnessOptions(PerGameCommonOptions): early_caves: EarlyCaves elevators_come_to_you: ElevatorsComeToYou trap_percentage: TrapPercentage + trap_weights: TrapWeights puzzle_skip_amount: PuzzleSkipAmount hint_amount: HintAmount area_hint_percentage: AreaHintPercentage diff --git a/worlds/witness/static_logic.py b/worlds/witness/static_logic.py index 5a3e8b1b580e..3efab4915e69 100644 --- a/worlds/witness/static_logic.py +++ b/worlds/witness/static_logic.py @@ -295,3 +295,6 @@ def __init__(self): self.EP_TO_OBELISK_SIDE.update(self.sigma_normal.EP_TO_OBELISK_SIDE) self.ENTITY_ID_TO_NAME.update(self.sigma_normal.ENTITY_ID_TO_NAME) + + +StaticWitnessLogic()