Docs: document world docs and tests (#1463)
* Docs: document world docs and tests * regions and items shouldn't be created after `create_items` * Changes from review * Restructure game info section Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> * w * urls can have extension probably * reorder the methods by call order * fix grammar mistake in ordered method list --------- Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									ecd2675ea8
								
							
						
					
					
						commit
						53e2232f29
					
				| 
						 | 
				
			
			@ -402,40 +402,43 @@ 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.world.push_precollected` for start inventory
 | 
			
		||||
* a `def generate_output(self, output_directory: str)` that creates the output
 | 
			
		||||
  files if there is output to be generated. When this is
 | 
			
		||||
  called, `self.world.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.
 | 
			
		||||
* applying `self.multiworld.push_precollected` for 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.
 | 
			
		||||
 | 
			
		||||
In addition, the following methods can be implemented and attributes can be set
 | 
			
		||||
In addition, the following methods can be implemented and are called in this order during generation
 | 
			
		||||
 | 
			
		||||
* `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)`
 | 
			
		||||
  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 `basic` as well.
 | 
			
		||||
  hard to separate, this can be done during `generate_early` or `create_items` as well.
 | 
			
		||||
* `def create_items(self)`
 | 
			
		||||
  called to place player's items into the MultiWorld's itempool.
 | 
			
		||||
  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)`
 | 
			
		||||
  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)`
 | 
			
		||||
  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.
 | 
			
		||||
  randomizations can be done here.
 | 
			
		||||
* `pre_fill`, `fill_hook` and `post_fill` 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
 | 
			
		||||
  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
 | 
			
		||||
  will be used by the server to host the MultiWorld.
 | 
			
		||||
* `required_client_version: Tuple(int, int, int)`
 | 
			
		||||
  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.
 | 
			
		||||
* `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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#### generate_early
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -680,3 +683,60 @@ def generate_output(self, output_directory: str):
 | 
			
		|||
    generate_mod(src, out_file, data)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### 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
 | 
			
		||||
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
 | 
			
		||||
Your game can have as many tutorials in as many languages as you like, with each one having a relevant `Tutorial`
 | 
			
		||||
defined in the `WebWorld`. The file name you use aren't particularly important, but it should be descriptive of what
 | 
			
		||||
the tutorial is covering, and the name of the file must match the relative URL provided in the `Tutorial`. Currently,
 | 
			
		||||
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`
 | 
			
		||||
```python
 | 
			
		||||
from test.TestBase import WorldTestBase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MyGameTestBase(WorldTestBase):
 | 
			
		||||
  game = "My Game"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Next using the rules defined in the above `set_rules` we can test that the chests have the correct access rules.
 | 
			
		||||
 | 
			
		||||
Example `testChestAccess.py`
 | 
			
		||||
```python
 | 
			
		||||
from . import MyGameTestBase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestChestAccess(MyGameTestBase):
 | 
			
		||||
    def testSwordChests(self):
 | 
			
		||||
        """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 testAnyWeaponChests(self):
 | 
			
		||||
        """Test locations that require any weapon"""
 | 
			
		||||
        locations = [f"Chest{i}" for i in range(3, 6)]
 | 
			
		||||
        items = [["Sword"], ["Axe"], ["Spear"]]
 | 
			
		||||
        # this will test that chests 3-5 can't be accessed without any weapon, but can be with just one of them.
 | 
			
		||||
        self.assertAccessDependency(locations, items)
 | 
			
		||||
```
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue