| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | # Archipelago API
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | This document tries to explain some aspects of the Archipelago World API used when implementing the generation logic of | 
					
						
							|  |  |  | a game. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | Client implementation is out of scope of this document. Please refer to an existing game that provides a similar API to | 
					
						
							|  |  |  | yours, and the following documents: | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | * [network protocol.md](https://github.com/ArchipelagoMW/Archipelago/blob/main/docs/network%20protocol.md) | 
					
						
							|  |  |  | * [adding games.md](https://github.com/ArchipelagoMW/Archipelago/blob/main/docs/adding%20games.md) | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | Archipelago will be abbreviated as "AP" from now on. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | ## Language
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 11:06:41 +02:00
										 |  |  | AP worlds are written in python3. | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | Clients that connect to the server to sync items can be in any language that allows using WebSockets. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ## Coding style
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | AP follows a [style guide](https://github.com/ArchipelagoMW/Archipelago/blob/main/docs/style.md). | 
					
						
							|  |  |  | When in doubt, use an IDE with a code-style linter, for example PyCharm Community Edition. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ## Docstrings
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | Docstrings are strings attached to an object in Python that describe what the object is supposed to be. Certain | 
					
						
							|  |  |  | docstrings will be picked up and used by AP. They are assigned by writing a string without any assignment right below a | 
					
						
							|  |  |  | definition. The string must be a triple-quoted string, and should | 
					
						
							|  |  |  | follow [reST style](https://peps.python.org/pep-0287/). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | Example: | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | ```python | 
					
						
							| 
									
										
										
										
											2021-11-04 13:23:13 +01:00
										 |  |  | from worlds.AutoWorld import World | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | class MyGameWorld(World): | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |     """This is the description of My Game that will be displayed on the AP website.""" | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Definitions
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | This section covers various classes and objects you can use for your world. While some of the attributes and methods | 
					
						
							|  |  |  | are mentioned here, not all of them are, but you can find them in | 
					
						
							|  |  |  | [`BaseClasses.py`](https://github.com/ArchipelagoMW/Archipelago/blob/main/BaseClasses.py). | 
					
						
							| 
									
										
										
										
											2022-03-24 08:21:08 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ### World Class
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | A `World` is the class with all the specifics of a certain game that is to be included. A new instance will be created | 
					
						
							|  |  |  | for each player of the game for any given generated multiworld. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-24 08:21:08 -05:00
										 |  |  | ### WebWorld Class
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | A `WebWorld` class contains specific attributes and methods that can be modified for your world specifically on the | 
					
						
							|  |  |  | webhost: | 
					
						
							| 
									
										
										
										
											2022-04-12 14:37:05 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | * `options_page` can be changed to a link instead of an AP-generated options page. | 
					
						
							| 
									
										
										
										
											2022-04-12 14:37:05 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | * `theme` to be used for your game-specific AP pages. Available themes: | 
					
						
							| 
									
										
										
										
											2023-07-09 18:04:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |   | dirt                                       | grass (default)                             | grassFlowers                                       | ice                                       | jungle                                       | ocean                                       | partyTime                                       | stone                                       | | 
					
						
							|  |  |  |   |--------------------------------------------|---------------------------------------------|----------------------------------------------------|-------------------------------------------|----------------------------------------------|---------------------------------------------|-------------------------------------------------|---------------------------------------------| | 
					
						
							|  |  |  |   | <img src="img/theme_dirt.JPG" width="100"> | <img src="img/theme_grass.JPG" width="100"> | <img src="img/theme_grassFlowers.JPG" width="100"> | <img src="img/theme_ice.JPG" width="100"> | <img src="img/theme_jungle.JPG" width="100"> | <img src="img/theme_ocean.JPG" width="100"> | <img src="img/theme_partyTime.JPG" width="100"> | <img src="img/theme_stone.JPG" width="100"> | | 
					
						
							| 
									
										
										
										
											2022-03-24 08:21:08 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | * `bug_report_page` (optional) can be a link to a bug reporting page, most likely a GitHub issue page, that will be | 
					
						
							|  |  |  |   placed by the site to help users report bugs. | 
					
						
							| 
									
										
										
										
											2022-04-12 14:37:05 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | * `tutorials` list of `Tutorial` classes where each class represents a guide to be generated on the webhost. | 
					
						
							| 
									
										
										
										
											2022-05-18 14:23:27 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | * `game_info_languages` (optional) list of strings for defining the existing game info pages your game supports. The | 
					
						
							|  |  |  |   documents must be prefixed with the same string as defined here. Default already has 'en'. | 
					
						
							| 
									
										
										
										
											2022-05-18 14:23:27 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | * `options_presets` (optional) `Dict[str, Dict[str, Any]]` where the keys are the names of the presets and the values | 
					
						
							|  |  |  |   are the options to be set for that preset. The options are defined as a `Dict[str, Any]` where the keys are the names | 
					
						
							|  |  |  |   of the options and the values are the values to be set for that option. These presets will be available for users to | 
					
						
							|  |  |  |   select from on the game's options page. | 
					
						
							| 
									
										
										
										
											2023-11-16 04:37:06 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | Note: The values must be a non-aliased value for the option type and can only include the following option types: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | * If you have a `Range`/`NamedRange` option, the value should be an `int` between the `range_start` and `range_end` | 
					
						
							|  |  |  |   values. | 
					
						
							|  |  |  |     * If you have a `NamedRange` option, the value can alternatively be a `str` that is one of the | 
					
						
							| 
									
										
										
										
											2023-11-16 04:37:06 -06:00
										 |  |  |       `special_range_names` keys. | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | * If you have a `Choice` option, the value should be a `str` that is one of the `option_<name>` values. | 
					
						
							|  |  |  | * If you have a `Toggle`/`DefaultOnToggle` option, the value should be a `bool`. | 
					
						
							|  |  |  | * `random` is also a valid value for any of these option types. | 
					
						
							| 
									
										
										
										
											2023-11-16 04:37:06 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | `OptionDict`, `OptionList`, `OptionSet`, `FreeText`, or custom `Option`-derived classes are not supported for presets on | 
					
						
							|  |  |  | the webhost at this time. | 
					
						
							| 
									
										
										
										
											2023-11-16 04:37:06 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | Here is an example of a defined preset: | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-16 04:37:06 -06:00
										 |  |  | ```python | 
					
						
							|  |  |  | # presets.py
 | 
					
						
							|  |  |  | options_presets = { | 
					
						
							|  |  |  |     "Limited Potential": { | 
					
						
							|  |  |  |         "progression_balancing":    0, | 
					
						
							|  |  |  |         "fairy_chests_per_zone":    2, | 
					
						
							|  |  |  |         "starting_class":           "random", | 
					
						
							|  |  |  |         "chests_per_zone":          30, | 
					
						
							|  |  |  |         "vendors":                  "normal", | 
					
						
							|  |  |  |         "architect":                "disabled", | 
					
						
							|  |  |  |         "gold_gain_multiplier":     "half", | 
					
						
							|  |  |  |         "number_of_children":       2, | 
					
						
							|  |  |  |         "free_diary_on_generation": False, | 
					
						
							|  |  |  |         "health_pool":              10, | 
					
						
							|  |  |  |         "mana_pool":                10, | 
					
						
							|  |  |  |         "attack_pool":              10, | 
					
						
							|  |  |  |         "magic_damage_pool":        10, | 
					
						
							|  |  |  |         "armor_pool":               5, | 
					
						
							|  |  |  |         "equip_pool":               10, | 
					
						
							|  |  |  |         "crit_chance_pool":         5, | 
					
						
							|  |  |  |         "crit_damage_pool":         5, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-16 04:37:06 -06:00
										 |  |  | # __init__.py
 | 
					
						
							|  |  |  | class RLWeb(WebWorld): | 
					
						
							|  |  |  |     options_presets = options_presets | 
					
						
							|  |  |  |     # ... | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ### MultiWorld Object
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | The `MultiWorld` object references the whole multiworld (all items and locations for all players) and is accessible | 
					
						
							|  |  |  | through `self.multiworld` from your `World` object. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | ### Player
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | The player is just an `int` in AP and is accessible through `self.player` from your `World` object. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ### Player Options
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | Options are provided by the user as part of the generation process, intended to affect how their randomizer experience | 
					
						
							|  |  |  | should play out. These can control aspects such as what locations should be shuffled, what items are in the itempool, | 
					
						
							|  |  |  | etc. Players provide the customized options for their World in the form of yamls. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | By convention, options are defined in `options.py` and will be used when parsing the players' yaml files. Each option | 
					
						
							|  |  |  | has its own class, which inherits from a base option type, a docstring to describe it, and a `display_name` property | 
					
						
							|  |  |  | shown on the website and in spoiler logs. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The available options are defined by creating a `dataclass`, which must be a subclass of `PerGameCommonOptions`. It has | 
					
						
							|  |  |  | defined fields for the option names used in the player yamls and used for options access, with their types matching the | 
					
						
							|  |  |  | appropriate Option class. By convention, the strings that define your option names should be in `snake_case`. The | 
					
						
							|  |  |  | `dataclass` is then assigned to your `World` by defining its `options_dataclass`. Option results are then automatically | 
					
						
							|  |  |  | added to the `World` object for easy access, between `World` creation and `generate_early`. These are accessible through | 
					
						
							|  |  |  | `self.options.<option_name>`, and you can get a dictionary with option values | 
					
						
							|  |  |  | via `self.options.as_dict(<option_names>)`, | 
					
						
							|  |  |  | passing the desired option names as strings. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Common option types are `Toggle`, `DefaultOnToggle`, `Choice`, and `Range`. | 
					
						
							|  |  |  | For more information, see the [options api doc](options%20api.md). | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-09 18:04:24 +02:00
										 |  |  | ### World Settings
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | Settings are set by the user outside the generation process. They can be used for those settings that may affect | 
					
						
							|  |  |  | generation or client behavior, but should remain static between generations, such as the path to a ROM file. | 
					
						
							|  |  |  | These settings are accessible through `self.settings.<setting_name>` or `cls.settings.<setting_name>`. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | Users can set these in their `host.yaml` file. Some settings may automatically open a file browser if a file is missing. | 
					
						
							| 
									
										
										
										
											2023-07-05 22:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | Refer to [settings api.md](https://github.com/ArchipelagoMW/Archipelago/blob/main/docs/settings%20api.md) for details. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | ### Locations
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | Locations are places where items can be located in your game. This may be chests or boss drops for RPG-like games, but | 
					
						
							|  |  |  | could also be progress in a research tree, or even something more abstract like a level up. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | Each location has a `name` and an `address` (hereafter referred to as an `id`), is placed in a Region, has access rules, | 
					
						
							|  |  |  | and has a classification. The name needs to be unique within each game and must not be numeric (must contain least 1 | 
					
						
							|  |  |  | letter or symbol). The ID needs to be unique across all games, and is best kept in the same range as the item IDs. | 
					
						
							| 
									
										
										
										
											2024-02-28 19:55:55 +01:00
										 |  |  | Locations and items can share IDs, so typically a game's locations and items start at the same ID. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | World-specific IDs must be in the range 1 to 2<sup>53</sup>-1; IDs ≤ 0 are global and reserved. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 00:47:23 +01:00
										 |  |  | Classification is one of `LocationProgressType.DEFAULT`, `PRIORITY` or `EXCLUDED`. | 
					
						
							| 
									
										
										
										
											2023-05-20 14:04:26 -04:00
										 |  |  | 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. | 
					
						
							| 
									
										
										
										
											2022-10-31 00:47:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-10 22:06:54 -08:00
										 |  |  | #### Documenting Locations
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | 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 Weighted Options page. Extra | 
					
						
							| 
									
										
										
										
											2023-11-10 22:06:54 -08:00
										 |  |  | indentation and single newlines will be collapsed into spaces. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | # locations.py
 | 
					
						
							| 
									
										
										
										
											2023-11-10 22:06:54 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | location_descriptions = { | 
					
						
							|  |  |  |     "Red Potion #6": "In a secret destructible block under the second stairway", | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |     "L2 Spaceship": | 
					
						
							|  |  |  |         """ | 
					
						
							|  |  |  |         The group of all items in the spaceship in Level 2. | 
					
						
							| 
									
										
										
										
											2023-11-10 22:06:54 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |         This doesn't include the item on the spaceship door, since it can be accessed without the Spaceship Key. | 
					
						
							|  |  |  |         """ | 
					
						
							| 
									
										
										
										
											2023-11-10 22:06:54 -08:00
										 |  |  | } | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							|  |  |  | # __init__.py
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from worlds.AutoWorld import World | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | from .locations import location_descriptions | 
					
						
							| 
									
										
										
										
											2023-11-10 22:06:54 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MyGameWorld(World): | 
					
						
							|  |  |  |     location_descriptions = location_descriptions | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | ### Items
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | Items are all things that can "drop" for your game. This may be RPG items like weapons, or technologies you normally | 
					
						
							|  |  |  | research in a research tree. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | Each item has a `name`, a `code` (hereafter referred to as `id`), and a classification. | 
					
						
							|  |  |  | The most important classification is `progression`. Progression items are items which a player *may* require to progress | 
					
						
							|  |  |  | in their world. If an item can possibly be considered for logic (it's referenced in a location's rules) it *must* be | 
					
						
							|  |  |  | progression. Progression items will be assigned to locations with higher priority, and moved around to meet defined rules | 
					
						
							|  |  |  | and satisfy progression balancing. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | The name needs to be unique within each game, meaning if you need to create multiple items with the same name, they | 
					
						
							|  |  |  | will all have the same ID. Name must not be numeric (must contain at least 1 letter or symbol). | 
					
						
							| 
									
										
										
										
											2022-09-20 08:08:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | Other classifications include: | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-09 18:04:24 +02:00
										 |  |  | * `filler`: a regular item or trash item | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | * `useful`: generally quite useful, but not required for anything logical. Cannot be placed on excluded locations | 
					
						
							| 
									
										
										
										
											2023-07-09 18:04:24 +02:00
										 |  |  | * `trap`: negative impact on the player | 
					
						
							| 
									
										
										
										
											2023-07-22 16:56:00 +02:00
										 |  |  | * `skip_balancing`: denotes that an item should not be moved to an earlier sphere for the purpose of balancing (to be | 
					
						
							|  |  |  |   combined with `progression`; see below) | 
					
						
							|  |  |  | * `progression_skip_balancing`: the combination of `progression` and `skip_balancing`, i.e., a progression item that | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |   will not be moved around by progression balancing; used, e.g., for currency or tokens, to not flood early spheres | 
					
						
							| 
									
										
										
										
											2022-06-19 15:19:46 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-10 22:06:54 -08:00
										 |  |  | #### Documenting Items
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | 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 Weighted Options page. Extra indentation and | 
					
						
							|  |  |  | single newlines will be collapsed into spaces. | 
					
						
							| 
									
										
										
										
											2023-11-10 22:06:54 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | # items.py
 | 
					
						
							| 
									
										
										
										
											2023-11-10 22:06:54 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | item_descriptions = { | 
					
						
							|  |  |  |     "Red Potion": "A standard health potion", | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |     "Spaceship Key": | 
					
						
							|  |  |  |         """ | 
					
						
							|  |  |  |         The key to the spaceship in Level 2. | 
					
						
							| 
									
										
										
										
											2023-11-10 22:06:54 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |         This is necessary to get to the Star Realm. | 
					
						
							|  |  |  |         """ | 
					
						
							| 
									
										
										
										
											2023-11-10 22:06:54 -08:00
										 |  |  | } | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							|  |  |  | # __init__.py
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from worlds.AutoWorld import World | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | from .items import item_descriptions | 
					
						
							| 
									
										
										
										
											2023-11-10 22:06:54 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MyGameWorld(World): | 
					
						
							|  |  |  |     item_descriptions = item_descriptions | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | ### Events
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | An Event is a special combination of a Location and an Item, with both having an `id` of `None`. These can be used to | 
					
						
							|  |  |  | track certain logic interactions, with the Event Item being required for access in other locations or regions, but not | 
					
						
							|  |  |  | being "real". Since the item and location have no ID, they get dropped at the end of generation and so the server is | 
					
						
							|  |  |  | never made aware of them and these locations can never be checked, nor can the items be received during play. | 
					
						
							|  |  |  | They may also be used for making the spoiler log look nicer, i.e. by having a `"Victory"` Event Item, that | 
					
						
							|  |  |  | is required to finish the game. This makes it very clear when the player finishes, rather than only seeing their last | 
					
						
							|  |  |  | relevant Item. Events function just like any other Location, and can still have their own access rules, etc. | 
					
						
							|  |  |  | By convention, the Event "pair" of Location and Item typically have the same name, though this is not a requirement. | 
					
						
							|  |  |  | They must not exist in the `name_to_id` lookups, as they have no ID. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The most common way to create an Event pair is to create and place the Item on the Location as soon as it's created: | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | ```python | 
					
						
							|  |  |  | from worlds.AutoWorld import World | 
					
						
							|  |  |  | from BaseClasses import ItemClassification | 
					
						
							|  |  |  | from .subclasses import MyGameLocation, MyGameItem | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | class MyGameWorld(World): | 
					
						
							|  |  |  |     victory_loc = MyGameLocation(self.player, "Victory", None) | 
					
						
							|  |  |  |     victory_loc.place_locked_item(MyGameItem("Victory", ItemClassification.progression, None, self.player)) | 
					
						
							|  |  |  | ``` | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | ### Regions
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | Regions are logical containers that typically hold locations that share some common access rules. If location logic is | 
					
						
							|  |  |  | written from scratch, using regions greatly simplifies the requirements and can help with implementing things | 
					
						
							|  |  |  | like entrance randomization in logic. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | Regions have a list called `exits`, containing `Entrance` objects representing transitions to other regions. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | There must be one special region, "Menu", from which the logic unfolds. AP assumes that a player will always be able to | 
					
						
							|  |  |  | return to the "Menu" region by resetting the game ("Save and quit"). | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | ### Entrances
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | An `Entrance` has a `parent_region` and `connected_region`, where it is in the `exits` of its parent, and the | 
					
						
							|  |  |  | `entrances` of its connected region. The `Entrance` then has rules assigned to it to determine if it can be passed | 
					
						
							|  |  |  | through, making the connected region accessible. They can be static (regular logic) or be defined/connected during | 
					
						
							|  |  |  | generation (entrance randomization). | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ### Access Rules
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | An access rule is a function that returns `True` or `False` for a `Location` or `Entrance` based on the current `state` | 
					
						
							|  |  |  | (items that have been collected). | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ### Item Rules
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | An item rule is a function that returns `True` or `False` for a `Location` based on a single item. It can be used to | 
					
						
							|  |  |  | reject the placement of an item there. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | ## Implementation
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Your World
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | All code for your world implementation should be placed in a python package in the `/worlds` directory. The starting | 
					
						
							|  |  |  | point for the package is `__init__.py`. Conventionally, your `World` class is placed in that file. | 
					
						
							| 
									
										
										
										
											2021-10-09 11:28:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | World classes must inherit from the `World` class in `/worlds/AutoWorld.py`, which can be imported as | 
					
						
							|  |  |  | `from worlds.AutoWorld import World` from your package. | 
					
						
							| 
									
										
										
										
											2021-10-09 11:28:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | AP will pick up your world automatically due to the `AutoWorld` implementation. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | ### Requirements
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | If your world needs specific python packages, they can be listed in `worlds/<world_name>/requirements.txt`. | 
					
						
							|  |  |  | ModuleUpdate.py will automatically pick up and install them. | 
					
						
							| 
									
										
										
										
											2022-09-20 09:09:13 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | See [pip documentation](https://pip.pypa.io/en/stable/cli/pip_install/#requirements-file-format). | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ### Relative Imports
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | AP will only import the `__init__.py`. Depending on code size, it may make sense to use multiple files and use relative | 
					
						
							|  |  |  | imports to access them. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | e.g. `from .options import MyGameOptions` from your `__init__.py` will load `world/[world_name]/options.py` and make | 
					
						
							|  |  |  | its `MyGameOptions` accessible. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | When imported names pile up, it may be easier to use `from . import options` and access the variable as | 
					
						
							|  |  |  | `options.MyGameOptions`. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | Imports from directories outside your world should use absolute imports. Correct use of relative / absolute imports is | 
					
						
							|  |  |  | required for zipped worlds to function, see [apworld specification.md](apworld%20specification.md). | 
					
						
							| 
									
										
										
										
											2022-09-18 00:00:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ### Your Item Type
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | Each world uses its own subclass of `BaseClasses.Item`. The constructor can be overridden to attach additional data to | 
					
						
							|  |  |  | it, e.g. "price in shop". Since the constructor is only ever called from your code, you can add whatever arguments you | 
					
						
							|  |  |  | like to the constructor. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | In its simplest form, we only set the game name and use the default constructor: | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							|  |  |  | from BaseClasses import Item | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | class MyGameItem(Item): | 
					
						
							|  |  |  |     game: str = "My Game" | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | By convention, this class definition will either be placed in your `__init__.py` or your `items.py`. For a more | 
					
						
							|  |  |  | elaborate example see | 
					
						
							|  |  |  | [`worlds/oot/Items.py`](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/oot/Items.py). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Your Location Type
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The same thing we did for items above, we will now do for locations: | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							| 
									
										
										
										
											2021-11-04 13:23:13 +01:00
										 |  |  | from BaseClasses import Location | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | class MyGameLocation(Location): | 
					
						
							|  |  |  |     game: str = "My Game" | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | in your `__init__.py` or your `locations.py`. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ### A World Class Skeleton
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							|  |  |  | # world/mygame/__init__.py
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-05 22:39:35 +02:00
										 |  |  | import settings | 
					
						
							|  |  |  | import typing | 
					
						
							| 
									
										
										
										
											2023-11-15 10:07:42 -06:00
										 |  |  | from .options import MyGameOptions  # the options we defined earlier | 
					
						
							|  |  |  | from .items import mygame_items  # data used below to add items to the World | 
					
						
							|  |  |  | from .locations import mygame_locations  # same as above | 
					
						
							| 
									
										
										
										
											2022-09-18 00:00:54 +02:00
										 |  |  | from worlds.AutoWorld import World | 
					
						
							| 
									
										
										
										
											2022-06-19 15:19:46 +02:00
										 |  |  | from BaseClasses import Region, Location, Entrance, Item, RegionType, ItemClassification | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-05 22:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | class MyGameItem(Item):  # or from Items import MyGameItem | 
					
						
							|  |  |  |     game = "My Game"  # name of the game/world this item is from | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-05 22:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | class MyGameLocation(Location):  # or from Locations import MyGameLocation | 
					
						
							|  |  |  |     game = "My Game"  # name of the game/world this location is in | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-05 22:39:35 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | class MyGameSettings(settings.Group): | 
					
						
							|  |  |  |     class RomFile(settings.SNESRomPath): | 
					
						
							|  |  |  |         """Insert help text for host.yaml here.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     rom_file: RomFile = RomFile("MyGame.sfc") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | class MyGameWorld(World): | 
					
						
							|  |  |  |     """Insert description of the world/game here.""" | 
					
						
							| 
									
										
										
										
											2023-03-20 11:01:08 -05:00
										 |  |  |     game = "My Game"  # name of the game/world | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  |     options_dataclass = MyGameOptions  # options the player can set | 
					
						
							|  |  |  |     options: MyGameOptions  # typing hints for option results | 
					
						
							| 
									
										
										
										
											2023-07-05 22:39:35 +02:00
										 |  |  |     settings: typing.ClassVar[MyGameSettings]  # will be automatically assigned from type hint | 
					
						
							| 
									
										
										
										
											2023-03-20 11:01:08 -05:00
										 |  |  |     topology_present = True  # show path to required location checks in spoiler | 
					
						
							| 
									
										
										
										
											2021-10-09 14:29:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # ID of first item and location, could be hard-coded but code may be easier | 
					
						
							| 
									
										
										
										
											2023-07-09 18:04:24 +02:00
										 |  |  |     # to read with this as a property. | 
					
						
							| 
									
										
										
										
											2021-10-09 14:29:52 +02:00
										 |  |  |     base_id = 1234 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |     # instead of dynamic numbering, IDs could be part of data | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # The following two dicts are required for the generation to know which | 
					
						
							|  |  |  |     # items exist. They could be generated from json or something else. They can | 
					
						
							|  |  |  |     # include events, but don't have to since events will be placed manually. | 
					
						
							|  |  |  |     item_name_to_id = {name: id for | 
					
						
							| 
									
										
										
										
											2021-10-09 14:29:52 +02:00
										 |  |  |                        id, name in enumerate(mygame_items, base_id)} | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  |     location_name_to_id = {name: id for | 
					
						
							| 
									
										
										
										
											2021-10-09 14:29:52 +02:00
										 |  |  |                            id, name in enumerate(mygame_locations, base_id)} | 
					
						
							| 
									
										
										
										
											2021-10-09 13:00:50 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Items can be grouped using their names to allow easy checking if any item | 
					
						
							|  |  |  |     # from that group has been collected. Group names can also be used for !hint | 
					
						
							|  |  |  |     item_name_groups = { | 
					
						
							| 
									
										
										
										
											2023-07-09 18:04:24 +02:00
										 |  |  |         "weapons": {"sword", "lance"}, | 
					
						
							| 
									
										
										
										
											2021-10-09 13:00:50 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Generation
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | The world has to provide the following things for generation: | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | * the properties mentioned above | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | * additions to the item pool | 
					
						
							|  |  |  | * additions to the regions list: at least one called "Menu" | 
					
						
							|  |  |  | * locations placed inside those regions | 
					
						
							| 
									
										
										
										
											2022-12-11 02:59:17 +01:00
										 |  |  | * a `def create_item(self, item: str) -> MyGameItem` to create any item on demand | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | * applying `self.multiworld.push_precollected` for world-defined start inventory | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | In addition, the following methods can be implemented and are called in this order during generation: | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | * `stage_assert_generate(cls, multiworld: MultiWorld)` | 
					
						
							|  |  |  |   a class method called at the start of generation to check for the existence of prerequisite files, usually a ROM for | 
					
						
							| 
									
										
										
										
											2023-02-19 16:16:56 -06:00
										 |  |  |   games which require one. | 
					
						
							| 
									
										
										
										
											2023-11-15 10:07:42 -06:00
										 |  |  | * `generate_early(self)` | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |   called per player before any items or locations are created. You can set properties on your | 
					
						
							|  |  |  |   world here. Already has access to player options and RNG. This is the earliest step where the world should start | 
					
						
							|  |  |  |   setting up for the current multiworld, as the multiworld itself is still setting up before this point. | 
					
						
							| 
									
										
										
										
											2023-11-15 10:07:42 -06:00
										 |  |  | * `create_regions(self)` | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |   called to place player's regions and their locations into the MultiWorld's regions list. | 
					
						
							|  |  |  |   If it's hard to separate, this can be done during `generate_early` or `create_items` as well. | 
					
						
							| 
									
										
										
										
											2023-11-15 10:07:42 -06:00
										 |  |  | * `create_items(self)` | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |   called to place player's items into the MultiWorld's itempool. After this step all regions | 
					
						
							|  |  |  |   and items have to be in the MultiWorld's regions and itempool, and these lists should not be modified afterward. | 
					
						
							| 
									
										
										
										
											2023-11-15 10:07:42 -06:00
										 |  |  | * `set_rules(self)` | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |   called to set access and item rules on locations and entrances. | 
					
						
							| 
									
										
										
										
											2023-11-15 10:07:42 -06:00
										 |  |  | * `generate_basic(self)` | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |   player-specific randomization that does not affect logic can be done here. | 
					
						
							|  |  |  | * `pre_fill(self)`, `fill_hook(self)` and `post_fill(self)` | 
					
						
							|  |  |  |   called to modify item placement before, during, and after the regular fill process; all finishing before | 
					
						
							|  |  |  |   `generate_output`. Any items that need to be placed during `pre_fill` should not exist in the itempool, and if there | 
					
						
							|  |  |  |   are any items that need to be filled this way, but need to be in state while you fill other items, they can be | 
					
						
							|  |  |  |   returned from `get_prefill_items`. | 
					
						
							|  |  |  | * `generate_output(self, output_directory: str)` | 
					
						
							|  |  |  |   creates the output files if there is output to be generated. When this is called, | 
					
						
							|  |  |  |   `self.multiworld.get_locations(self.player)` has all locations for the player, with attribute `item` pointing to the | 
					
						
							|  |  |  |   item. `location.item.player` can be used to see if it's a local item. | 
					
						
							| 
									
										
										
										
											2023-11-15 10:07:42 -06:00
										 |  |  | * `fill_slot_data(self)` and `modify_multidata(self, multidata: Dict[str, Any])` can be used to modify the data that | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  |   will be used by the server to host the MultiWorld. | 
					
						
							| 
									
										
										
										
											2023-02-19 16:16:56 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | All instance methods can, optionally, have a class method defined which will be called after all instance methods are | 
					
						
							|  |  |  | finished running, by defining a method with `stage_` in front of the method name. These class methods will have the | 
					
						
							|  |  |  | args `(cls, multiworld: MultiWorld)`, followed by any other args that the relevant instance method has. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #### generate_early
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							| 
									
										
										
										
											2022-04-28 09:03:44 -07:00
										 |  |  | def generate_early(self) -> None: | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |     # read player options to world instance | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  |     self.final_boss_hp = self.options.final_boss_hp.value | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | #### create_regions
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							|  |  |  | def create_regions(self) -> None: | 
					
						
							|  |  |  |     # Add regions to the multiworld. "Menu" is the required starting point. | 
					
						
							|  |  |  |     # Arguments to Region() are name, player, multiworld, and optionally hint_text | 
					
						
							|  |  |  |     menu_region = Region("Menu", self.player, self.multiworld) | 
					
						
							|  |  |  |     self.multiworld.regions.append(menu_region)  # or use += [menu_region...] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     main_region = Region("Main Area", self.player, self.multiworld) | 
					
						
							|  |  |  |     # add main area's locations to main area (all but final boss) | 
					
						
							|  |  |  |     main_region.add_locations(main_region_locations, MyGameLocation) | 
					
						
							|  |  |  |     # or  | 
					
						
							|  |  |  |     # main_region.locations = \ | 
					
						
							|  |  |  |     #   [MyGameLocation(self.player, location_name, self.location_name_to_id[location_name], main_region] | 
					
						
							|  |  |  |     self.multiworld.regions.append(main_region) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     boss_region = Region("Boss Room", self.player, self.multiworld) | 
					
						
							|  |  |  |     # add event to Boss Room | 
					
						
							|  |  |  |     boss_region.locations.append(MyGameLocation(self.player, "Final Boss", None, boss_region)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # if entrances are not randomized, they should be connected here, otherwise they can also be connected at a later stage | 
					
						
							|  |  |  |     # create Entrances and connect the Regions | 
					
						
							|  |  |  |     menu_region.connect(main_region)  # connects the "Menu" and "Main Area", can also pass a rule | 
					
						
							|  |  |  |     # or | 
					
						
							|  |  |  |     main_region.add_exits({"Boss Room": "Boss Door"}, {"Boss Room": lambda state: state.has("Sword", self.player)}) | 
					
						
							|  |  |  |     # connects the "Main Area" and "Boss Room" regions, and places a rule requiring the "Sword" item to traverse | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # if setting location access rules from data is easier here, set_rules can possibly be omitted | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | #### create_item
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | # we need a way to know if an item provides progress in the game ("key item") this can be part of the items definition,
 | 
					
						
							|  |  |  | # or depend on recipe randomization
 | 
					
						
							| 
									
										
										
										
											2023-11-15 10:07:42 -06:00
										 |  |  | from .items import is_progression  # this is just a dummy | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-15 10:07:42 -06:00
										 |  |  | def create_item(self, item: str) -> MyGameItem: | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |     # this is called when AP wants to create an item by name (for plando) or when you call it from your own code | 
					
						
							|  |  |  |     classification = ItemClassification.progression if is_progression(item) else | 
					
						
							|  |  |  |     ItemClassification.filler | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | return MyGameItem(item, classification, self.item_name_to_id[item], | 
					
						
							|  |  |  |                   self.player) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-15 10:07:42 -06:00
										 |  |  | def create_event(self, event: str) -> MyGameItem: | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |     # while we are at it, we can also add a helper to create events | 
					
						
							|  |  |  |     return MyGameItem(event, True, None, self.player) | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #### create_items
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							| 
									
										
										
										
											2022-04-28 09:03:44 -07:00
										 |  |  | def create_items(self) -> None: | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  |     # Add items to the Multiworld. | 
					
						
							|  |  |  |     # If there are two of the same item, the item has to be twice in the pool. | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |     # Which items are added to the pool may depend on player options, e.g. custom win condition like triforce hunt. | 
					
						
							| 
									
										
										
										
											2021-10-10 13:08:23 +02:00
										 |  |  |     # Having an item in the start inventory won't remove it from the pool. | 
					
						
							|  |  |  |     # If an item can't have duplicates it has to be excluded manually. | 
					
						
							| 
									
										
										
										
											2021-10-10 18:39:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # List of items to exclude, as a copy since it will be destroyed below | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     exclude = [item for item in self.multiworld.precollected_items[self.player]] | 
					
						
							| 
									
										
										
										
											2021-10-10 13:08:23 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for item in map(self.create_item, mygame_items): | 
					
						
							|  |  |  |         if item in exclude: | 
					
						
							|  |  |  |             exclude.remove(item)  # this is destructive. create unique list above | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             self.multiworld.itempool.append(self.create_item("nothing")) | 
					
						
							| 
									
										
										
										
											2021-10-10 13:08:23 +02:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |             self.multiworld.itempool.append(item) | 
					
						
							| 
									
										
										
										
											2021-10-10 13:08:23 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # itempool and number of locations should match up. | 
					
						
							|  |  |  |     # If this is not the case we want to fill the itempool with junk. | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |     junk = 0  # calculate this based on player options | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     self.multiworld.itempool += [self.create_item("nothing") for _ in range(junk)] | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ### Setting Rules
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							| 
									
										
										
										
											2023-11-24 00:35:37 +01:00
										 |  |  | from worlds.generic.Rules import add_rule, set_rule, forbid_item, add_item_rule | 
					
						
							| 
									
										
										
										
											2023-11-15 10:07:42 -06:00
										 |  |  | from .items import get_item_type | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-28 09:03:44 -07:00
										 |  |  | def set_rules(self) -> None: | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  |     # For some worlds this step can be omitted if either a Logic mixin  | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |     # (see below) is used, it's easier to apply the rules from data during | 
					
						
							|  |  |  |     # location generation or everything is in generate_basic | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # set a simple rule for an region | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     set_rule(self.multiworld.get_entrance("Boss Door", self.player), | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |              lambda state: state.has("Boss Key", self.player)) | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |     # location.access_rule = ... is likely to be a bit faster | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |     # combine rules to require two items | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     add_rule(self.multiworld.get_location("Chest2", self.player), | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |              lambda state: state.has("Sword", self.player)) | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     add_rule(self.multiworld.get_location("Chest2", self.player), | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |              lambda state: state.has("Shield", self.player)) | 
					
						
							|  |  |  |     # or simply combine yourself | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     set_rule(self.multiworld.get_location("Chest2", self.player), | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |              lambda state: state.has("Sword", self.player) and | 
					
						
							|  |  |  |                            state.has("Shield", self.player)) | 
					
						
							|  |  |  |     # require two of an item | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     set_rule(self.multiworld.get_location("Chest3", self.player), | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |              lambda state: state.has("Key", self.player, 2)) | 
					
						
							| 
									
										
										
										
											2021-10-09 11:06:41 +02:00
										 |  |  |     # require one item from an item group | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     add_rule(self.multiworld.get_location("Chest3", self.player), | 
					
						
							| 
									
										
										
										
											2021-10-09 11:06:41 +02:00
										 |  |  |              lambda state: state.has_group("weapons", self.player)) | 
					
						
							| 
									
										
										
										
											2023-11-24 00:35:37 +01:00
										 |  |  |     # state also has .count() for items, .has_any() and .has_all() for multiple | 
					
						
							| 
									
										
										
										
											2021-10-09 11:06:41 +02:00
										 |  |  |     # and .count_group() for groups | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |     # set_rule is likely to be a bit faster than add_rule | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # disallow placing a specific local item at a specific location | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     forbid_item(self.multiworld.get_location("Chest4", self.player), "Sword") | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |     # disallow placing items with a specific property | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     add_item_rule(self.multiworld.get_location("Chest5", self.player), | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |                   lambda item: get_item_type(item) == "weapon") | 
					
						
							|  |  |  |     # get_item_type needs to take player/world into account | 
					
						
							|  |  |  |     # if MyGameItem has a type property, a more direct implementation would be | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     add_item_rule(self.multiworld.get_location("Chest5", self.player), | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |                   lambda item: item.player != self.player or | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |                                item.my_type == "weapon") | 
					
						
							|  |  |  |     # location.item_rule = ... is likely to be a bit faster | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |     # place "Victory" at "Final Boss" and set collection as win condition | 
					
						
							|  |  |  |     self.multiworld.get_location("Final Boss", self.player).place_locked_item(self.create_event("Victory")) | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |     self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # for debugging purposes, you may want to visualize the layout of your world. Uncomment the following code to
 | 
					
						
							|  |  |  | # write a PlantUML diagram to the file "my_world.puml" that can help you see whether your regions and locations
 | 
					
						
							|  |  |  | # are connected and placed as desired
 | 
					
						
							|  |  |  | # from Utils import visualize_regions
 | 
					
						
							|  |  |  | # visualize_regions(self.multiworld.get_region("Menu", self.player), "my_world.puml")
 | 
					
						
							|  |  |  | ``` | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | ### Custom Logic Rules
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Custom methods can be defined for your logic rules. The access rule that ultimately gets assigned to the Location or | 
					
						
							|  |  |  | Entrance should be | 
					
						
							|  |  |  | a [`CollectionRule`](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/generic/Rules.py#L9). | 
					
						
							|  |  |  | Typically, this is done by defining a lambda expression on demand at the relevant bit, typically calling other | 
					
						
							|  |  |  | functions, but this can also be achieved by defining a method with the appropriate format and assigning it directly. | 
					
						
							|  |  |  | For an example, see [The Messenger](/worlds/messenger/rules.py). | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							| 
									
										
										
										
											2023-11-15 10:07:42 -06:00
										 |  |  | # logic.py
 | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | from BaseClasses import CollectionState | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | def mygame_has_key(self, state: CollectionState, player: int) -> bool: | 
					
						
							|  |  |  |     # More arguments above are free to choose, since you can expect this is only called in your world | 
					
						
							|  |  |  |     # MultiWorld can be accessed through state.multiworld. | 
					
						
							|  |  |  |     # Explicitly passing in MyGameWorld instance for easy options access is also a valid approach, but it's generally | 
					
						
							|  |  |  |     # better to check options before rule assignment since the individual functions can be called thousands of times | 
					
						
							|  |  |  |     return state.has("key", player)  # or whatever | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | ``` | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | ```python | 
					
						
							|  |  |  | # __init__.py
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-18 00:00:54 +02:00
										 |  |  | from worlds.generic.Rules import set_rule | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | from . import logic | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | class MyGameWorld(World): | 
					
						
							|  |  |  |     # ... | 
					
						
							| 
									
										
										
										
											2023-11-15 10:07:42 -06:00
										 |  |  |     def set_rules(self) -> None: | 
					
						
							| 
									
										
										
										
											2023-07-09 18:04:24 +02:00
										 |  |  |         set_rule(self.multiworld.get_location("A Door", self.player), | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |                  lambda state: logic.mygame_has_key(state, self.player)) | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Logic Mixin
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | While lambdas and events can do pretty much anything, more complex logic can be handled in logic mixins. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | When importing a file that defines a class that inherits from `worlds.AutoWorld.LogicMixin`, the `CollectionState` class | 
					
						
							|  |  |  | is automatically extended by the mixin's members. These members should be prefixed with the name of the implementing | 
					
						
							|  |  |  | world since the namespace is shared with all other logic mixins. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Some uses could be to add additional variables to the state object, or to have a custom state machine that gets modified | 
					
						
							|  |  |  | with the state. | 
					
						
							|  |  |  | Please do this with caution and only when necessary. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #### pre_fill
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							|  |  |  | def pre_fill(self) -> None: | 
					
						
							|  |  |  |     # place item Herb into location Chest1 for some reason | 
					
						
							|  |  |  |     item = self.create_item("Herb") | 
					
						
							|  |  |  |     self.multiworld.get_location("Chest1", self.player).place_locked_item(item) | 
					
						
							|  |  |  |     # in most cases it's better to do this at the same time the itempool is | 
					
						
							|  |  |  |     # filled to avoid accidental duplicates, such as manually placed and still in the itempool | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ### Generate Output
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							| 
									
										
										
										
											2023-11-15 10:07:42 -06:00
										 |  |  | from .mod import generate_mod | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-15 10:07:42 -06:00
										 |  |  | def generate_output(self, output_directory: str) -> None: | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |     # How to generate the mod or ROM highly depends on the game. | 
					
						
							|  |  |  |     # If the mod is written in Lua, Jinja can be used to fill a template. | 
					
						
							|  |  |  |     # If the mod reads a json file, `json.dump()` can be used to generate that. | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |     # code below is a dummy | 
					
						
							|  |  |  |     data = { | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |         "seed": self.multiworld.seed_name,  # to verify the server's multiworld | 
					
						
							|  |  |  |         "slot": self.multiworld.player_name[self.player],  # to connect to server | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |         "items": {location.name: location.item.name | 
					
						
							|  |  |  |                   if location.item.player == self.player else "Remote" | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |                   for location in self.multiworld.get_filled_locations(self.player)}, | 
					
						
							| 
									
										
										
										
											2021-10-09 14:29:52 +02:00
										 |  |  |         # store start_inventory from player's .yaml | 
					
						
							| 
									
										
										
										
											2022-12-11 14:14:27 +01:00
										 |  |  |         # make sure to mark as not remote_start_inventory when connecting if stored in rom/mod | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |         "starter_items": [item.name for item in self.multiworld.precollected_items[self.player]], | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-11-15 10:07:42 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # add needed option results to the dictionary | 
					
						
							|  |  |  |     data.update(self.options.as_dict("final_boss_hp", "difficulty", "fix_xyz_glitch")) | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |     # point to a ROM specified by the installation | 
					
						
							| 
									
										
										
										
											2023-07-05 22:39:35 +02:00
										 |  |  |     src = self.settings.rom_file | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |     # or point to worlds/mygame/data/mod_template | 
					
						
							|  |  |  |     src = os.path.join(os.path.dirname(__file__), "data", "mod_template") | 
					
						
							|  |  |  |     # generate output path | 
					
						
							| 
									
										
										
										
											2023-07-09 18:04:24 +02:00
										 |  |  |     mod_name = self.multiworld.get_out_file_name_base(self.player) | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |     out_file = os.path.join(output_directory, mod_name + ".zip") | 
					
						
							|  |  |  |     # generate the file | 
					
						
							|  |  |  |     generate_mod(src, out_file, data) | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  | ### Slot Data
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | If the game client needs to know information about the generated seed, a preferred method of transferring the data | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | is through the slot data. This is filled with the `fill_slot_data` method of your world by returning | 
					
						
							| 
									
										
										
										
											2024-02-28 17:22:42 -08:00
										 |  |  | 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). | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | 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. | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							| 
									
										
										
										
											2023-11-15 10:07:42 -06:00
										 |  |  | def fill_slot_data(self) -> Dict[str, Any]: | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |     # In order for our game client to handle the generated seed correctly we need to know what the user selected | 
					
						
							|  |  |  |     # for their difficulty and final boss HP. | 
					
						
							|  |  |  |     # A dictionary returned from this method gets set as the slot_data and will be sent to the client after connecting. | 
					
						
							|  |  |  |     # The options dataclass has a method to return a `Dict[str, Any]` of each option name provided and the relevant | 
					
						
							|  |  |  |     # option's value. | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  |     return self.options.as_dict("difficulty", "final_boss_hp") | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-19 16:16:56 -06:00
										 |  |  | ### Documentation
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Each world implementation should have a tutorial and a game info page. These are both rendered on the website by reading | 
					
						
							|  |  |  | the `.md` files in your world's `/docs` directory. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #### Game Info
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-19 16:16:56 -06:00
										 |  |  | The game info page is for a short breakdown of what your game is and how it works in Archipelago. Any additional | 
					
						
							|  |  |  | information that may be useful to the player when learning your randomizer should also go here. The file name format | 
					
						
							|  |  |  | is `<language key>_<game name>.md`. While you can write these docs for multiple languages, currently only the english | 
					
						
							|  |  |  | version is displayed on the website. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #### Tutorials
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-19 16:16:56 -06:00
										 |  |  | Your game can have as many tutorials in as many languages as you like, with each one having a relevant `Tutorial` | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | defined in the `WebWorld`. The file name you use isn't particularly important, but it should be descriptive of what | 
					
						
							|  |  |  | the tutorial covers, and the name of the file must match the relative URL provided in the `Tutorial`. Currently, | 
					
						
							| 
									
										
										
										
											2023-02-19 16:16:56 -06:00
										 |  |  | the JS that determines this ignores the provided file name and will search for `game/document_lang.md`, where | 
					
						
							|  |  |  | `game/document/lang` is the provided URL. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Tests
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Each world is expected to include unit tests that cover its logic, to ensure no logic bug regressions occur. This can be | 
					
						
							|  |  |  | done by creating a `/test` package within your world package. The `__init__.py` within this folder is where the world's | 
					
						
							|  |  |  | TestBase should be defined. This can be inherited from the main TestBase, which will automatically set up a solo | 
					
						
							|  |  |  | multiworld for each test written using it. Within subsequent modules, classes should be defined which inherit the world | 
					
						
							|  |  |  | TestBase, and can then define options to test in the class body, and run tests in each test method. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Example `__init__.py` | 
					
						
							| 
									
										
										
										
											2023-10-22 06:00:27 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-19 16:16:56 -06:00
										 |  |  | ```python | 
					
						
							| 
									
										
										
										
											2023-12-01 03:26:27 -06:00
										 |  |  | from test.bases import WorldTestBase | 
					
						
							| 
									
										
										
										
											2023-02-19 16:16:56 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MyGameTestBase(WorldTestBase): | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |     game = "My Game" | 
					
						
							| 
									
										
										
										
											2023-02-19 16:16:56 -06:00
										 |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | Next, using the rules defined in the above `set_rules` we can test that the chests have the correct access rules. | 
					
						
							| 
									
										
										
										
											2023-02-19 16:16:56 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-01 03:26:27 -06:00
										 |  |  | Example `test_chest_access.py` | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-19 16:16:56 -06:00
										 |  |  | ```python | 
					
						
							|  |  |  | from . import MyGameTestBase | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class TestChestAccess(MyGameTestBase): | 
					
						
							| 
									
										
										
										
											2023-11-15 10:07:42 -06:00
										 |  |  |     def test_sword_chests(self) -> None: | 
					
						
							| 
									
										
										
										
											2023-02-19 16:16:56 -06:00
										 |  |  |         """Test locations that require a sword""" | 
					
						
							|  |  |  |         locations = ["Chest1", "Chest2"] | 
					
						
							|  |  |  |         items = [["Sword"]] | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |         # this will test that each location can't be accessed without the "Sword", but can be accessed once obtained | 
					
						
							| 
									
										
										
										
											2023-02-19 16:16:56 -06:00
										 |  |  |         self.assertAccessDependency(locations, items) | 
					
						
							| 
									
										
										
										
											2023-07-09 18:04:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-15 10:07:42 -06:00
										 |  |  |     def test_any_weapon_chests(self) -> None: | 
					
						
							| 
									
										
										
										
											2023-02-19 16:16:56 -06:00
										 |  |  |         """Test locations that require any weapon""" | 
					
						
							|  |  |  |         locations = [f"Chest{i}" for i in range(3, 6)] | 
					
						
							|  |  |  |         items = [["Sword"], ["Axe"], ["Spear"]] | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  |         # this will test that chests 3-5 can't be accessed without any weapon, but can be with just one of them | 
					
						
							| 
									
										
										
										
											2023-02-19 16:16:56 -06:00
										 |  |  |         self.assertAccessDependency(locations, items) | 
					
						
							|  |  |  | ``` | 
					
						
							| 
									
										
										
										
											2023-12-01 03:26:27 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-30 14:42:33 -06:00
										 |  |  | For more information on tests, check the [tests doc](tests.md). |