Docs: minor updates to recommend modern PEP8 (#2384)
* docs: update world api for modern PEP8 conventions * docs: update options api for modern PEP8 styling * missed a spot
This commit is contained in:
parent
2af5410301
commit
bf8432faa7
|
@ -29,6 +29,7 @@ The zip can contain arbitrary files in addition what was specified above.
|
|||
|
||||
## Caveats
|
||||
|
||||
Imports from other files inside the apworld have to use relative imports.
|
||||
Imports from other files inside the apworld have to use relative imports. e.g. `from .options import MyGameOptions`
|
||||
|
||||
Imports from AP base have to use absolute imports, e.g. Options.py and worlds/AutoWorld.py.
|
||||
Imports from AP base have to use absolute imports, e.g. `from Options import Toggle` or
|
||||
`from worlds.AutoWorld import World`
|
||||
|
|
|
@ -31,7 +31,7 @@ As an example, suppose we want an option that lets the user start their game wit
|
|||
create our option class (with a docstring), give it a `display_name`, and add it to our game's options dataclass:
|
||||
|
||||
```python
|
||||
# Options.py
|
||||
# options.py
|
||||
from dataclasses import dataclass
|
||||
|
||||
from Options import Toggle, PerGameCommonOptions
|
||||
|
|
|
@ -286,11 +286,11 @@ See [pip documentation](https://pip.pypa.io/en/stable/cli/pip_install/#requireme
|
|||
AP will only import the `__init__.py`. Depending on code size it makes sense to
|
||||
use multiple files and use relative imports to access them.
|
||||
|
||||
e.g. `from .Options import MyGameOptions` from your `__init__.py` will load
|
||||
`world/[world_name]/Options.py` and make its `MyGameOptions` accessible.
|
||||
e.g. `from .options import MyGameOptions` from your `__init__.py` will load
|
||||
`world/[world_name]/options.py` and make its `MyGameOptions` accessible.
|
||||
|
||||
When imported names pile up it may be easier to use `from . import Options`
|
||||
and access the variable as `Options.MyGameOptions`.
|
||||
When imported names pile up it may be easier to use `from . import options`
|
||||
and access the variable as `options.MyGameOptions`.
|
||||
|
||||
Imports from directories outside your world should use absolute imports.
|
||||
Correct use of relative / absolute imports is required for zipped worlds to
|
||||
|
@ -311,7 +311,7 @@ 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`.
|
||||
or your `items.py`. For a more elaborate example see `worlds/oot/Items.py`.
|
||||
|
||||
### Your location type
|
||||
|
||||
|
@ -323,15 +323,15 @@ class MyGameLocation(Location):
|
|||
game: str = "My Game"
|
||||
|
||||
# override constructor to automatically mark event locations as such
|
||||
def __init__(self, player: int, name = "", code = None, parent = None):
|
||||
def __init__(self, player: int, name = "", code = None, parent = None) -> None:
|
||||
super(MyGameLocation, self).__init__(player, name, code, parent)
|
||||
self.event = code is None
|
||||
```
|
||||
in your `__init__.py` or your `Locations.py`.
|
||||
in your `__init__.py` or your `locations.py`.
|
||||
|
||||
### Options
|
||||
|
||||
By convention options are defined in `Options.py` and will be used when parsing
|
||||
By convention options are defined in `options.py` and will be used when parsing
|
||||
the players' yaml files.
|
||||
|
||||
Each option has its own class, inherits from a base option type, has a docstring
|
||||
|
@ -347,7 +347,7 @@ For more see `Options.py` in AP's base directory.
|
|||
|
||||
#### Toggle, DefaultOnToggle
|
||||
|
||||
Those don't need any additional properties defined. After parsing the option,
|
||||
These don't need any additional properties defined. After parsing the option,
|
||||
its `value` will either be True or False.
|
||||
|
||||
#### Range
|
||||
|
@ -373,7 +373,7 @@ default = 0
|
|||
|
||||
#### Sample
|
||||
```python
|
||||
# Options.py
|
||||
# options.py
|
||||
|
||||
from dataclasses import dataclass
|
||||
from Options import Toggle, Range, Choice, PerGameCommonOptions
|
||||
|
@ -412,7 +412,7 @@ class MyGameOptions(PerGameCommonOptions):
|
|||
# __init__.py
|
||||
|
||||
from worlds.AutoWorld import World
|
||||
from .Options import MyGameOptions # import the options dataclass
|
||||
from .options import MyGameOptions # import the options dataclass
|
||||
|
||||
|
||||
class MyGameWorld(World):
|
||||
|
@ -429,9 +429,9 @@ class MyGameWorld(World):
|
|||
|
||||
import settings
|
||||
import typing
|
||||
from .Options import MyGameOptions # the options we defined earlier
|
||||
from .Items import mygame_items # data used below to add items to the World
|
||||
from .Locations import mygame_locations # same as above
|
||||
from .options import MyGameOptions # the options we defined earlier
|
||||
from .items import mygame_items # data used below to add items to the World
|
||||
from .locations import mygame_locations # same as above
|
||||
from worlds.AutoWorld import World
|
||||
from BaseClasses import Region, Location, Entrance, Item, RegionType, ItemClassification
|
||||
|
||||
|
@ -490,7 +490,7 @@ The world has to provide the following things for generation
|
|||
* additions to the regions list: at least one called "Menu"
|
||||
* locations placed inside those regions
|
||||
* a `def create_item(self, item: str) -> MyGameItem` to create any item on demand
|
||||
* applying `self.multiworld.push_precollected` for start inventory
|
||||
* applying `self.multiworld.push_precollected` for world defined start inventory
|
||||
* `required_client_version: Tuple[int, int, int]`
|
||||
Optional client version as tuple of 3 ints to make sure the client is compatible to
|
||||
this world (e.g. implements all required features) when connecting.
|
||||
|
@ -500,31 +500,32 @@ In addition, the following methods can be implemented and are called in this ord
|
|||
* `stage_assert_generate(cls, multiworld)` is a class method called at the start of
|
||||
generation to check the existence of prerequisite files, usually a ROM for
|
||||
games which require one.
|
||||
* `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)`
|
||||
* `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. This is the earliest step where the world should start setting up for the current
|
||||
multiworld as any steps before this, the multiworld itself is still getting set up
|
||||
* `create_regions(self)`
|
||||
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.
|
||||
* `def create_items(self)`
|
||||
* `create_items(self)`
|
||||
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 afterwards.
|
||||
* `def set_rules(self)`
|
||||
* `set_rules(self)`
|
||||
called to set access and item rules on locations and entrances.
|
||||
Locations have to be defined before this, or rule application can miss them.
|
||||
* `def generate_basic(self)`
|
||||
* `generate_basic(self)`
|
||||
called after the previous steps. Some placement and player specific
|
||||
randomizations can be done here.
|
||||
* `pre_fill`, `fill_hook` and `post_fill` are called to modify item placement
|
||||
* `pre_fill(self)`, `fill_hook(self)` and `post_fill(self)` are called to modify item placement
|
||||
before, during and after the regular fill process, before `generate_output`.
|
||||
If items need to be placed during pre_fill, these items can be determined
|
||||
and created using `get_prefill_items`
|
||||
* `def generate_output(self, output_directory: str)` that creates the output
|
||||
* `generate_output(self, output_directory: str)` that 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.
|
||||
* `fill_slot_data` and `modify_multidata` can be used to modify the data that
|
||||
* `fill_slot_data(self)` and `modify_multidata(self, multidata: Dict[str, Any])` can be used to modify the data that
|
||||
will be used by the server to host the MultiWorld.
|
||||
|
||||
|
||||
|
@ -541,9 +542,9 @@ def generate_early(self) -> None:
|
|||
```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
|
||||
from .Items import is_progression # this is just a dummy
|
||||
from .items import is_progression # this is just a dummy
|
||||
|
||||
def create_item(self, item: str):
|
||||
def create_item(self, item: str) -> MyGameItem:
|
||||
# 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 \
|
||||
|
@ -551,7 +552,7 @@ def create_item(self, item: str):
|
|||
return MyGameItem(item, classification, self.item_name_to_id[item],
|
||||
self.player)
|
||||
|
||||
def create_event(self, event: str):
|
||||
def create_event(self, event: str) -> MyGameItem:
|
||||
# while we are at it, we can also add a helper to create events
|
||||
return MyGameItem(event, True, None, self.player)
|
||||
```
|
||||
|
@ -644,7 +645,7 @@ def generate_basic(self) -> None:
|
|||
|
||||
```python
|
||||
from worlds.generic.Rules import add_rule, set_rule, forbid_item
|
||||
from Items import get_item_type
|
||||
from .items import get_item_type
|
||||
|
||||
|
||||
def set_rules(self) -> None:
|
||||
|
@ -713,12 +714,12 @@ Please do this with caution and only when necessary.
|
|||
#### Sample
|
||||
|
||||
```python
|
||||
# Logic.py
|
||||
# logic.py
|
||||
|
||||
from worlds.AutoWorld import LogicMixin
|
||||
|
||||
class MyGameLogic(LogicMixin):
|
||||
def mygame_has_key(self, player: int):
|
||||
def mygame_has_key(self, player: int) -> bool:
|
||||
# Arguments above are free to choose
|
||||
# MultiWorld can be accessed through self.multiworld, explicitly passing in
|
||||
# MyGameWorld instance for easy options access is also a valid approach
|
||||
|
@ -728,11 +729,11 @@ class MyGameLogic(LogicMixin):
|
|||
# __init__.py
|
||||
|
||||
from worlds.generic.Rules import set_rule
|
||||
import .Logic # apply the mixin by importing its file
|
||||
import .logic # apply the mixin by importing its file
|
||||
|
||||
class MyGameWorld(World):
|
||||
# ...
|
||||
def set_rules(self):
|
||||
def set_rules(self) -> None:
|
||||
set_rule(self.multiworld.get_location("A Door", self.player),
|
||||
lambda state: state.mygame_has_key(self.player))
|
||||
```
|
||||
|
@ -740,10 +741,10 @@ class MyGameWorld(World):
|
|||
### Generate Output
|
||||
|
||||
```python
|
||||
from .Mod import generate_mod
|
||||
from .mod import generate_mod
|
||||
|
||||
|
||||
def generate_output(self, output_directory: str):
|
||||
def generate_output(self, output_directory: str) -> None:
|
||||
# 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
|
||||
|
@ -758,12 +759,10 @@ def generate_output(self, output_directory: str):
|
|||
# make sure to mark as not remote_start_inventory when connecting if stored in rom/mod
|
||||
"starter_items": [item.name for item
|
||||
in self.multiworld.precollected_items[self.player]],
|
||||
"final_boss_hp": self.final_boss_hp,
|
||||
# store option name "easy", "normal" or "hard" for difficuly
|
||||
"difficulty": self.options.difficulty.current_key,
|
||||
# store option value True or False for fixing a glitch
|
||||
"fix_xyz_glitch": self.options.fix_xyz_glitch.value,
|
||||
}
|
||||
|
||||
# add needed option results to the dictionary
|
||||
data.update(self.options.as_dict("final_boss_hp", "difficulty", "fix_xyz_glitch"))
|
||||
# point to a ROM specified by the installation
|
||||
src = self.settings.rom_file
|
||||
# or point to worlds/mygame/data/mod_template
|
||||
|
@ -787,7 +786,7 @@ data already exists on the server. The most common usage of slot data is to send
|
|||
to be aware of.
|
||||
|
||||
```python
|
||||
def fill_slot_data(self):
|
||||
def fill_slot_data(self) -> Dict[str, Any]:
|
||||
# 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
|
||||
|
@ -839,14 +838,14 @@ from . import MyGameTestBase
|
|||
|
||||
|
||||
class TestChestAccess(MyGameTestBase):
|
||||
def test_sword_chests(self):
|
||||
def test_sword_chests(self) -> None:
|
||||
"""Test locations that require a sword"""
|
||||
locations = ["Chest1", "Chest2"]
|
||||
items = [["Sword"]]
|
||||
# this will test that each location can't be accessed without the "Sword", but can be accessed once obtained.
|
||||
self.assertAccessDependency(locations, items)
|
||||
|
||||
def test_any_weapon_chests(self):
|
||||
def test_any_weapon_chests(self) -> None:
|
||||
"""Test locations that require any weapon"""
|
||||
locations = [f"Chest{i}" for i in range(3, 6)]
|
||||
items = [["Sword"], ["Axe"], ["Spear"]]
|
||||
|
|
Loading…
Reference in New Issue