| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | # Archipelago API
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This document tries to explain some internals required to implement a game for | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | Archipelago's generation and server. Once a seed is generated, a client or mod is  | 
					
						
							|  |  |  | required to send and receive items between the game and server. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | Client implementation is out of scope of this document. Please refer to an | 
					
						
							| 
									
										
										
										
											2021-10-09 01:15:35 +02:00
										 |  |  | existing game that provides a similar API to yours. | 
					
						
							|  |  |  | Refer to the following documents as well: | 
					
						
							|  |  |  |     * [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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02: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. | 
					
						
							| 
									
										
										
										
											2021-10-09 02:05:55 +02: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
										 |  |  | 
 | 
					
						
							|  |  |  | AP follows all the PEPs. When in doubt use an IDE with coding style | 
					
						
							|  |  |  | linter, for example PyCharm Community Edition. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ## Docstrings
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02: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. | 
					
						
							|  |  |  | Example: | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | ```python | 
					
						
							| 
									
										
										
										
											2021-11-04 13:23:13 +01:00
										 |  |  | from worlds.AutoWorld import World | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | class MyGameWorld(World): | 
					
						
							|  |  |  |     """This is the description of My Game that will be displayed on the AP | 
					
						
							|  |  |  |        website.""" | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Definitions
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ### World Class
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | A `World` class is the class with all the specifics of a certain game to be | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | included. It will be instantiated for each player that rolls a seed for that | 
					
						
							|  |  |  | game. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ### MultiWorld Object
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | The `MultiWorld` object references the whole multiworld (all items and locations | 
					
						
							|  |  |  | for all players) and is accessible through `self.world` inside a `World` object. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | ### Player
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The player is just an integer in AP and is accessible through `self.player` | 
					
						
							|  |  |  | inside a World object. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ### Player Options
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | Players provide customized settings for their World in the form of yamls. | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | Those are accessible through `self.world.<option_name>[self.player]`. A dict | 
					
						
							|  |  |  | of valid options has to be provided in `self.options`. Options are automatically | 
					
						
							|  |  |  | added to the `World` object for easy access. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ### World Options
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | Any AP installation can provide settings for a world, for example a ROM file, | 
					
						
							|  |  |  | accessible through `Utils.get_options()['<world>_options']['<option>']`. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | Users can set those in their `host.yaml` file. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | ### Locations
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 11:06:41 +02:00
										 |  |  | Each location has a `name` and an `id` (a.k.a. "code" or "address"), is placed | 
					
						
							|  |  |  | in a Region and has access rules. | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | The name needs to be unique in each game, the ID needs to be unique across all | 
					
						
							|  |  |  | games and is best in the same range as the item IDs. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | Special locations with ID `None` can hold events. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | ### Items
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Items are all things that can "drop" for your game. This may be RPG items like | 
					
						
							|  |  |  | weapons, could as well be technologies you normally research in a research tree. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 02:05:55 +02:00
										 |  |  | Each item has a `name`, an `id` (can be known as "code"), and an `advancement` | 
					
						
							|  |  |  | flag. An advancement item is an item which a player may require to advance in | 
					
						
							|  |  |  | their world. Advancement items will be assigned to locations with higher | 
					
						
							|  |  |  | priority and moved around to meet defined rules and accomplish progression | 
					
						
							|  |  |  | balancing. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | Special items with ID `None` can mark events (read below). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Events
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | Events will mark some progress. You define an event location, an | 
					
						
							|  |  |  | event item, strap some rules to the location (i.e. hold certain | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | items) and manually place the event item at the event location. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Events can be used to either simplify the logic or to get better spoiler logs. | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | Events will show up in the spoiler playthrough but they do not represent actual | 
					
						
							|  |  |  | items or locations within the game. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | There is one special case for events: Victory. To get the win condition to show | 
					
						
							|  |  |  | up in the spoiler log, you create an event item and place it at an event | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | location with the `access_rules` for game completion. Once that's done, the | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | world's win condition can be as simple as checking for that item. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | By convention the victory event is called `"Victory"`. It can be placed at one | 
					
						
							|  |  |  | or more event locations based on player options. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Regions
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Regions are logical groups of locations that share some common access rules. If | 
					
						
							|  |  |  | location logic is written from scratch, using regions greatly simplifies the | 
					
						
							|  |  |  | definition and allow to somewhat easily implement things like entrance | 
					
						
							|  |  |  | randomizer in logic. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 11:06:41 +02:00
										 |  |  | Regions have a list called `exits` which are `Entrance` objects representing | 
					
						
							|  |  |  | transitions to other regions. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | There has to 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 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | resetting the game ("Save and quit"). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Entrances
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | An `Entrance` connects to a region, is assigned to region's exits and has rules | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | to define if it and thus the connected region is accessible. | 
					
						
							|  |  |  | They can be static (regular logic) or be defined/connected during generation | 
					
						
							|  |  |  | (entrance randomizer). | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ### Access Rules
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 11:06:41 +02:00
										 |  |  | An access rule is a function that returns `True` or `False` for a `Location` or | 
					
						
							|  |  |  | `Entrance` based on the the current `state` (items that can be 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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | An item rule is a function that returns `True` or `False` for a `Location` based | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | on a single item. It can be used to reject placement of an item there. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ## Implementation
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Your World
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 11:28:15 +02: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. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | World classes must inherit from the `World` class in `/worlds/AutoWorld.py`, | 
					
						
							|  |  |  | which can be imported as `..AutoWorld.World` from your package. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | AP will pick up your world automatically due to the `AutoWorld` implementation. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | ### Requirements
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | If your world needs specific python packages, they can be listed in | 
					
						
							|  |  |  | `world/[world_name]/requirements.txt`. | 
					
						
							|  |  |  | See [pip documentation](https://pip.pypa.io/en/stable/cli/pip_install/#requirements-file-format) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ### Relative Imports
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | AP will only import the `__init__.py`. Depending on code size it makes sense to | 
					
						
							|  |  |  | use multiple files and use relative imports to access them. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | e.g. `from .Options import mygame_options` from your `__init__.py` will load | 
					
						
							|  |  |  | `world/[world_name]/Options.py` and make its `mygame_options` accesible. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | When imported names pile up it may be easier to use `from . import Options` | 
					
						
							|  |  |  | and access the variable as `Options.mygame_options`. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ### Your Item Type
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | Each world uses its own subclass of `BaseClasses.Item`. The constuctor 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 constuctor | 
					
						
							|  |  |  | ```python | 
					
						
							|  |  |  | from BaseClasses import Item | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MyGameItem(Item): | 
					
						
							|  |  |  |     game: str = "My Game" | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 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`. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Your location type
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The same we have done for items above, we will do for locations | 
					
						
							|  |  |  | ```python | 
					
						
							| 
									
										
										
										
											2021-11-04 13:23:13 +01:00
										 |  |  | from BaseClasses import Location | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | class MyGameLocation(Location): | 
					
						
							|  |  |  |     game: str = "My Game" | 
					
						
							| 
									
										
										
										
											2021-10-10 14:03:33 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # override constructor to automatically mark event locations as such | 
					
						
							|  |  |  |     def __init__(self, player: int, name = '', code = None, parent = None): | 
					
						
							|  |  |  |         super(MyGameLocation, self).__init__(player, name, code, parent) | 
					
						
							|  |  |  |         self.event = code is None | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | ``` | 
					
						
							|  |  |  | in your `__init__.py` or your `Locations.py`. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Options
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | By convention options are defined in `Options.py` and will be used when parsing | 
					
						
							|  |  |  | the players' yaml files. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | Each option has its own class, inherits from a base option type, has a docstring  | 
					
						
							| 
									
										
										
										
											2021-10-09 13:00:50 +02:00
										 |  |  | to describe it and a `displayname` property for display on the website and in | 
					
						
							|  |  |  | spoiler logs. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 02:05:55 +02:00
										 |  |  | The actual name as used in the yaml is defined in a `dict[str, Option]`, that is | 
					
						
							|  |  |  | assigned to the world under `self.options`. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 11:06:41 +02:00
										 |  |  | Common option types are `Toggle`, `DefaultOnToggle`, `Choice`, `Range`. | 
					
						
							|  |  |  | For more see `Options.py` in AP's base directory. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #### Toggle, DefaultOnToggle
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Those don't need any additional properties defined. After parsing the option, | 
					
						
							|  |  |  | its `value` will either be True or False. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #### Range
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Define properties `range_start`, `range_end` and `default`. Ranges will be | 
					
						
							|  |  |  | displayed as sliders on the website and can be set to random in the yaml. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #### Choice
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Choices are like toggles, but have more options than just True and False. | 
					
						
							|  |  |  | Define a property `option_<name> = <number>` per selectable value and | 
					
						
							|  |  |  | `default = <number>` to set the default selection. Aliases can be set by | 
					
						
							|  |  |  | defining a property `alias_<name> = <same number>`. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | One special case where aliases are required is when option name is `yes`, `no`, | 
					
						
							|  |  |  | `on` or `off` because they parse to `True` or `False`: | 
					
						
							|  |  |  | ```python | 
					
						
							|  |  |  | option_off = 0 | 
					
						
							|  |  |  | option_on = 1 | 
					
						
							|  |  |  | option_some = 2 | 
					
						
							|  |  |  | alias_false = 0 | 
					
						
							|  |  |  | alias_true = 1 | 
					
						
							|  |  |  | default = 0 | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #### Sample
 | 
					
						
							|  |  |  | ```python | 
					
						
							|  |  |  | # Options.py
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-04 13:23:13 +01:00
										 |  |  | from Options import Toggle, Range, Choice, Option | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | import typing | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Difficulty(Choice): | 
					
						
							|  |  |  |     """Sets overall game difficulty.""" | 
					
						
							|  |  |  |     displayname = "Difficulty" | 
					
						
							|  |  |  |     option_easy = 0 | 
					
						
							|  |  |  |     option_normal = 1 | 
					
						
							|  |  |  |     option_hard = 2 | 
					
						
							|  |  |  |     alias_beginner = 0  # same as easy | 
					
						
							|  |  |  |     alias_expert = 2  # same as hard | 
					
						
							|  |  |  |     default = 1  # default to normal | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class FinalBossHP(Range): | 
					
						
							|  |  |  |     """Sets the HP of the final boss""" | 
					
						
							|  |  |  |     displayname = "Final Boss HP" | 
					
						
							|  |  |  |     range_start = 100 | 
					
						
							|  |  |  |     range_end = 10000 | 
					
						
							|  |  |  |     default = 2000 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class FixXYZGlitch(Toggle): | 
					
						
							|  |  |  |     """Fixes ABC when you do XYZ""" | 
					
						
							|  |  |  |     displayname = "Fix XYZ Glitch" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # By convention we call the options dict variable `<world>_options`.
 | 
					
						
							|  |  |  | mygame_options: typing.Dict[str, type(Option)] = { | 
					
						
							|  |  |  |     "difficulty": Difficulty, | 
					
						
							|  |  |  |     "final_boss_hp": FinalBossHP, | 
					
						
							|  |  |  |     "fix_xyz_glitch": FixXYZGlitch | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | ```python | 
					
						
							|  |  |  | # __init__.py
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from ..AutoWorld import World | 
					
						
							|  |  |  | from .Options import mygame_options  # import the options dict | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MyGameWorld(World): | 
					
						
							|  |  |  |     #... | 
					
						
							|  |  |  |     options = mygame_options  # assign the options dict to the world | 
					
						
							|  |  |  |     #... | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  | ### Local or Remote
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | A world with `remote_items` set to `True` gets all items items from the server | 
					
						
							|  |  |  | and no item from the local game. So for an RPG opening a chest would not add | 
					
						
							|  |  |  | any item to your inventory, instead the server will send you what was in that | 
					
						
							|  |  |  | chest. The advantage is that a generic mod can be used that does not need to | 
					
						
							|  |  |  | know anything about the seed. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | A world with `remote_items` set to `False` will locally reward its local items. | 
					
						
							|  |  |  | For console games this can remove delay and make script/animation/dialog flow | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | more natural. These games typically have been edited to 'bake in' the items. | 
					
						
							| 
									
										
										
										
											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
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from .Options import mygame_options  # the options we defined earlier | 
					
						
							|  |  |  | from .Items import mygame_items  # data used below to add items to the World | 
					
						
							|  |  |  | from .Locations import mygame_locations  # same as above | 
					
						
							|  |  |  | from ..AutoWorld import World | 
					
						
							|  |  |  | from BaseClasses import Region, Location, Entrance, Item | 
					
						
							|  |  |  | from Utils import get_options, output_path | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MyGameItem(Item):  # or from Items import MyGameItem | 
					
						
							|  |  |  |     game = "My Game"  # name of the game/world this item is from | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MyGameLocation(Location):  # or from Locations import MyGameLocation | 
					
						
							|  |  |  |     game = "My Game"  # name of the game/world this location is in | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MyGameWorld(World): | 
					
						
							|  |  |  |     """Insert description of the world/game here.""" | 
					
						
							|  |  |  |     game: str = "My Game"  # name of the game/world | 
					
						
							|  |  |  |     options = mygame_options  # options the player can set | 
					
						
							| 
									
										
										
										
											2021-10-09 13:00:50 +02:00
										 |  |  |     topology_present: bool = True  # show path to required location checks in spoiler | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  |     remote_items: bool = False  # True if all items come from the server | 
					
						
							| 
									
										
										
										
											2021-10-09 13:00:50 +02:00
										 |  |  |     remote_start_inventory: bool = False  # True if start inventory comes from the server | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 14:29:52 +02:00
										 |  |  |     # data_version is used to signal that items, locations or their names | 
					
						
							|  |  |  |     # changed. Set this to 0 during development so other games' clients do not | 
					
						
							|  |  |  |     # cache any texts, then increase by 1 for each release that makes changes. | 
					
						
							|  |  |  |     data_version = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # ID of first item and location, could be hard-coded but code may be easier | 
					
						
							|  |  |  |     # to read with this as a propery. | 
					
						
							|  |  |  |     base_id = 1234 | 
					
						
							|  |  |  |     # 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 = { | 
					
						
							|  |  |  |         "weapons": {"sword", "lance"} | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ### Generation
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The world has to provide the following things for generation | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | * the properties mentioned above  | 
					
						
							|  |  |  | * additions to the item pool | 
					
						
							|  |  |  | * additions to the regions list: at least one called "Menu" | 
					
						
							|  |  |  | * locations placed inside those regions | 
					
						
							|  |  |  | * a `def create_item(self, item: str) -> MyGameItem` for plando/manual placing | 
					
						
							| 
									
										
										
										
											2021-10-09 14:35:08 +02:00
										 |  |  | * applying `self.world.precollected_items` for plando/start inventory | 
					
						
							|  |  |  |   if not using a `remote_start_inventory` | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | * a `def generate_output(self, output_directory: str)` that creates the output | 
					
						
							| 
									
										
										
										
											2021-10-09 13:00:50 +02:00
										 |  |  |   if there is output to be generated. If only items are randomized and | 
					
						
							|  |  |  |   `remote_items = True` it is possible to have a generic mod and output | 
					
						
							|  |  |  |   generation can be skipped. In all other cases this is required. When this is | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  |   called, `self.world.get_locations()` has all locations for all players, with | 
					
						
							|  |  |  |   properties `item` pointing to the item and `player` identifying the player. | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |   `self.world.get_filled_locations(self.player)` will filter for this world. | 
					
						
							|  |  |  |   `item.player` can be used to see if it's a local item. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | In addition the following methods can be implemented | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | * `def generate_early(self)` | 
					
						
							|  |  |  |   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. | 
					
						
							|  |  |  | * `def create_regions(self)` | 
					
						
							|  |  |  |   called to place player's regions into the MultiWorld's regions list. If it's | 
					
						
							|  |  |  |   hard to separate, this can be done during `generate_early` or `basic` as well. | 
					
						
							|  |  |  | * `def create_items(self)` | 
					
						
							|  |  |  |   called to place player's items into the MultiWorld's itempool. | 
					
						
							|  |  |  | * `def set_rules(self)` | 
					
						
							|  |  |  |   called to set access and item rules on locations and entrances. | 
					
						
							|  |  |  | * `def generate_basic(self)` | 
					
						
							|  |  |  |   called after the previous steps. Some placement and player specific | 
					
						
							|  |  |  |   randomizations can be done here. After this step all regions and items have | 
					
						
							|  |  |  |   to be in the MultiWorld's regions and itempool. | 
					
						
							|  |  |  | * `pre_fill`, `fill_hook` and `post_fill` are called to modify item placement | 
					
						
							|  |  |  |   before, during and after the regular fill process, before `generate_output`. | 
					
						
							|  |  |  | * `fill_slot_data` and `modify_multidata` can be used to modify the data that | 
					
						
							|  |  |  |   will be used by the server to host the MultiWorld. | 
					
						
							|  |  |  | * `def get_required_client_version(self)` | 
					
						
							|  |  |  |   can return a tuple of 3 ints to make sure the client is compatible to this | 
					
						
							|  |  |  |   world (e.g. item IDs) when connecting. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #### generate_early
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							|  |  |  | def generate_early(self): | 
					
						
							|  |  |  |     # read player settings to world instance | 
					
						
							|  |  |  |     self.final_boss_hp = self.world.final_boss_hp[self.player].value | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #### create_item
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							|  |  |  | # 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
 | 
					
						
							| 
									
										
										
										
											2021-10-09 13:00:50 +02:00
										 |  |  | from .Items import is_progression  # this is just a dummy | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | def create_item(self, item: str): | 
					
						
							|  |  |  |     # This is called when AP wants to create an item by name (for plando) or | 
					
						
							|  |  |  |     # when you call it from your own code. | 
					
						
							|  |  |  |     return MyGameItem(item, is_progression(item), self.item_name_to_id[item], | 
					
						
							|  |  |  |                       self.player) | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | def create_event(self, event: str): | 
					
						
							|  |  |  |     # 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 | 
					
						
							|  |  |  | def create_items(self): | 
					
						
							|  |  |  |     # Add items to the Multiworld. | 
					
						
							|  |  |  |     # If there are two of the same item, the item has to be twice in the pool. | 
					
						
							|  |  |  |     # Which items are added to the pool may depend on player settings, | 
					
						
							|  |  |  |     # 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 | 
					
						
							|  |  |  |     exclude = [item for item in self.world.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 | 
					
						
							|  |  |  |             self.world.itempool.append(self.create_item('nothing')) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.world.itempool.append(item) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # itempool and number of locations should match up. | 
					
						
							|  |  |  |     # If this is not the case we want to fill the itempool with junk. | 
					
						
							|  |  |  |     junk = 0  # calculate this based on player settings | 
					
						
							|  |  |  |     self.world.itempool += [self.create_item('nothing') for _ in range(junk)] | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #### create_regions
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							|  |  |  | def create_regions(self): | 
					
						
							|  |  |  |     # Add regions to the multiworld. "Menu" is the required starting point. | 
					
						
							|  |  |  |     # Arguments to Region() are name, type, human_readable_name, player, world | 
					
						
							|  |  |  |     r = Region("Menu", None, "Menu", self.player, self.world) | 
					
						
							|  |  |  |     # Set Region.exits to a list of entrances that are reachable from region | 
					
						
							|  |  |  |     r.exits = [Entrance(self.player, "New game", r)]  # or use r.exits.append | 
					
						
							|  |  |  |     # Append region to MultiWorld's regions | 
					
						
							|  |  |  |     self.world.regions.append(r)  # or use += [r...] | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     r = Region("Main Area", None, "Main Area", self.player, self.world) | 
					
						
							|  |  |  |     # Add main area's locations to main area (all but final boss) | 
					
						
							|  |  |  |     r.locations = [MyGameLocation(self.player, location.name, | 
					
						
							|  |  |  |                    self.location_name_to_id[location.name], r)] | 
					
						
							|  |  |  |     r.exits = [Entrance(self.player, "Boss Door", r)] | 
					
						
							|  |  |  |     self.world.regions.append(r) | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     r = Region("Boss Room", None, "Boss Room", self.player, self.world) | 
					
						
							|  |  |  |     # add event to Boss Room | 
					
						
							|  |  |  |     r.locations = [MyGameLocation(self.player, "Final Boss", None, r)] | 
					
						
							|  |  |  |     self.world.regions.append(r) | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     # If entrances are not randomized, they should be connected here, otherwise | 
					
						
							|  |  |  |     # they can also be connected at a later stage. | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |     self.world.get_entrance("New Game", self.player)\ | 
					
						
							|  |  |  |         .connect(self.world.get_region("Main Area", self.player)) | 
					
						
							|  |  |  |     self.world.get_entrance("Boss Door", self.player)\ | 
					
						
							|  |  |  |         .connect(self.world.get_region("Boss Room", self.player)) | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  |      | 
					
						
							|  |  |  |     # If setting location access rules from data is easier here, set_rules can | 
					
						
							|  |  |  |     # possibly omitted. | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #### generate_basic
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							|  |  |  | def generate_basic(self): | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |     # place "Victory" at "Final Boss" and set collection as win condition | 
					
						
							|  |  |  |     self.world.get_location("Final Boss", self.player)\ | 
					
						
							|  |  |  |         .place_locked_item(self.create_event("Victory")) | 
					
						
							|  |  |  |     self.world.completion_condition[self.player] = \ | 
					
						
							|  |  |  |         lambda state: state.has("Victory", self.player) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # place item Herb into location Chest1 for some reason | 
					
						
							|  |  |  |     item = self.create_item("Herb") | 
					
						
							|  |  |  |     self.world.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: | 
					
						
							|  |  |  |     # manually placed and still in the itempool | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | from ..generic.Rules import add_rule, set_rule, forbid_item | 
					
						
							|  |  |  | from Items import get_item_type | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | def set_rules(self): | 
					
						
							|  |  |  |     # 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 | 
					
						
							| 
									
										
										
										
											2021-10-08 00:39:16 +02:00
										 |  |  |     set_rule(self.world.get_entrance("Boss Door", self.player), | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |              lambda state: state.has("Boss Key", self.player)) | 
					
						
							|  |  |  |     # combine rules to require two items | 
					
						
							| 
									
										
										
										
											2021-10-08 00:39:16 +02:00
										 |  |  |     add_rule(self.world.get_location("Chest2", self.player), | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |              lambda state: state.has("Sword", self.player)) | 
					
						
							| 
									
										
										
										
											2021-10-08 00:39:16 +02:00
										 |  |  |     add_rule(self.world.get_location("Chest2", self.player), | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |              lambda state: state.has("Shield", self.player)) | 
					
						
							|  |  |  |     # or simply combine yourself | 
					
						
							| 
									
										
										
										
											2021-10-08 00:39:16 +02:00
										 |  |  |     set_rule(self.world.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 | 
					
						
							| 
									
										
										
										
											2021-10-08 00:39:16 +02:00
										 |  |  |     set_rule(self.world.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 | 
					
						
							|  |  |  |     add_rule(self.world.get_location("Chest3", self.player), | 
					
						
							|  |  |  |              lambda state: state.has_group("weapons", self.player)) | 
					
						
							|  |  |  |     # state also has .item_count() for items, .has_any() and.has_all() for sets | 
					
						
							|  |  |  |     # 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 | 
					
						
							|  |  |  |     forbid_item(self.world.get_location("Chest4", self.player), "Sword") | 
					
						
							|  |  |  |     # disallow placing items with a specific property | 
					
						
							| 
									
										
										
										
											2021-10-08 00:39:16 +02:00
										 |  |  |     add_item_rule(self.world.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 | 
					
						
							| 
									
										
										
										
											2021-10-08 00:39:16 +02:00
										 |  |  |     add_item_rule(self.world.get_location("Chest5", self.player), | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |                   lambda item: item.player != self.player or\ | 
					
						
							|  |  |  |                                item.my_type == "weapon") | 
					
						
							|  |  |  |     # location.item_rule = ... is likely to be a bit faster | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ### Logic Mixin
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | While lambdas and events could do pretty much anything, by convention we | 
					
						
							| 
									
										
										
										
											2021-10-09 02:05:55 +02:00
										 |  |  | implement more complex logic in logic mixins, even if there is no need to add | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | properties to the `BaseClasses.CollectionState` state object. | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 02:05:55 +02:00
										 |  |  | When importing a file that defines a class that inherits from | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | `..AutoWorld.LogicMixin` the state object's class is automatically extended by | 
					
						
							| 
									
										
										
										
											2021-10-09 02:05:55 +02:00
										 |  |  | the mixin's members. These members should be prefixed with underscore following | 
					
						
							|  |  |  | the name of the implementing world. This is due to sharing a namespace with all | 
					
						
							|  |  |  | other logic mixins. | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | Typical uses are defining methods that are used instead of `state.has` | 
					
						
							|  |  |  | in lambdas, e.g.`state._mygame_has(custom, world, player)` or recurring checks | 
					
						
							|  |  |  | like `state._mygame_can_do_something(world, player)` to simplify lambdas. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | More advanced uses could be to add additional variables to the state object, | 
					
						
							|  |  |  | override `World.collect(self, state, item)` and `remove(self, state, item)` | 
					
						
							|  |  |  | to update the state object, and check those added variables in added methods. | 
					
						
							|  |  |  | Please do this with caution and only when neccessary. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #### Sample
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							|  |  |  | # Logic.py
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from ..AutoWorld import LogicMixin | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | class MyGameLogic(LogicMixin): | 
					
						
							|  |  |  |     def _mygame_has_key(self, world: MultiWorld, player: int): | 
					
						
							|  |  |  |         # Arguments above are free to choose | 
					
						
							|  |  |  |         # it may make sense to use World as argument instead of MultiWorld | 
					
						
							| 
									
										
										
										
											2021-10-08 00:39:16 +02:00
										 |  |  |         return self.has('key', player)  # or whatever | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | ``` | 
					
						
							|  |  |  | ```python | 
					
						
							|  |  |  | # __init__.py
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from ..generic.Rules import set_rule | 
					
						
							|  |  |  | import .Logic  # apply the mixin by importing its file | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MyGameWorld(World): | 
					
						
							|  |  |  |     # ... | 
					
						
							|  |  |  |     def set_rules(self): | 
					
						
							| 
									
										
										
										
											2021-10-08 00:39:16 +02:00
										 |  |  |         set_rule(self.world.get_location("A Door", self.player), | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |                  lamda state: state._myworld_has_key(self.world, self.player)) | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-09 00:49:47 +02:00
										 |  |  | ### Generate Output
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | ```python | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  | from .Mod import generate_mod | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-07 19:41:29 +02:00
										 |  |  | def generate_output(self, output_directory: str): | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02: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 | 
					
						
							|  |  |  |     # code below is a dummy | 
					
						
							|  |  |  |     data = { | 
					
						
							|  |  |  |         "seed": self.world.seed_name,  # to verify the server's multiworld | 
					
						
							|  |  |  |         "slot": self.world.player_name[self.player],  # to connect to server | 
					
						
							|  |  |  |         "items": {location.name: location.item.name | 
					
						
							|  |  |  |                   if location.item.player == self.player else "Remote" | 
					
						
							|  |  |  |                   for location in self.world.get_filled_locations(self.player)}, | 
					
						
							| 
									
										
										
										
											2021-10-09 14:29:52 +02:00
										 |  |  |         # store start_inventory from player's .yaml | 
					
						
							| 
									
										
										
										
											2021-10-10 18:39:03 +02:00
										 |  |  |         "starter_items": [item.name for item | 
					
						
							|  |  |  |                           in self.world.precollected_items[self.player]], | 
					
						
							| 
									
										
										
										
											2021-10-08 00:25:20 +02:00
										 |  |  |         "final_boss_hp": self.final_boss_hp, | 
					
						
							|  |  |  |         # store option name "easy", "normal" or "hard" for difficuly | 
					
						
							|  |  |  |         "difficulty": self.world.difficulty[self.player].current_key, | 
					
						
							|  |  |  |         # store option value True or False for fixing a glitch | 
					
						
							|  |  |  |         "fix_xyz_glitch": self.world.fix_xyz_glitch[self.player].value | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     # point to a ROM specified by the installation | 
					
						
							|  |  |  |     src = Utils.get_options()["mygame_options"]["rom_file"] | 
					
						
							|  |  |  |     # or point to worlds/mygame/data/mod_template | 
					
						
							|  |  |  |     src = os.path.join(os.path.dirname(__file__), "data", "mod_template") | 
					
						
							|  |  |  |     # generate output path | 
					
						
							|  |  |  |     mod_name = f"AP-{self.world.seed_name}-P{self.player}-{self.world.player_name[self.player]}" | 
					
						
							|  |  |  |     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
										 |  |  | ``` | 
					
						
							|  |  |  | 
 |