Docs: Rework the "Events" Section of world api.md (#5012)

Co-authored-by: Scipio Wright <scipiowright@gmail.com>
Co-authored-by: qwint <qwint.42@gmail.com>
Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>
This commit is contained in:
Ixrec
2025-06-16 13:36:12 +01:00
committed by GitHub
parent dda5a05cbb
commit 5c710ad032

View File

@@ -258,31 +258,6 @@ another flag like "progression", it means "an especially useful progression item
* `progression_skip_balancing`: the combination of `progression` and `skip_balancing`, i.e., a progression item that
will not be moved around by progression balancing; used, e.g., for currency or tokens, to not flood early spheres
### Events
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:
```python
from worlds.AutoWorld import World
from BaseClasses import ItemClassification
from .subclasses import MyGameLocation, MyGameItem
class MyGameWorld(World):
victory_loc = MyGameLocation(self.player, "Victory", None)
victory_loc.place_locked_item(MyGameItem("Victory", ItemClassification.progression, None, self.player))
```
### Regions
Regions are logical containers that typically hold locations that share some common access rules. If location logic is
@@ -339,6 +314,63 @@ avoiding the need for indirect conditions at the expense of performance.
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.
### Events (or "generation-only items/locations")
An event item or location is one that only exists during multiworld generation; the server is never made aware of them.
Event locations can never be checked by the player, and event items cannot be received during play.
Events are used to represent in-game actions (that aren't regular Archipelago locations) when either:
* We want to show in the spoiler log when the player is expected to perform the in-game action.
* It's the cleanest way to represent how that in-game action impacts logic.
Typical examples include completing the goal, defeating a boss, or flipping a switch that affects multiple areas.
To be precise: the term "event" on its own refers to the special combination of an "event item" placed on an "event
location". Event items and locations are created the same way as normal items and locations, except that they have an
`id` of `None`, and an event item must be placed on an event location
(and vice versa). Finally, although events are often described as "fake" items and locations, it's important to
understand that they are perfectly real during generation.
The most common way to create an event is to create the event item and the event location, then immediately call
`Location.place_locked_item()`:
```python
victory_loc = MyGameLocation(self.player, "Defeat the Final Boss", None, final_boss_arena_region)
victory_loc.place_locked_item(MyGameItem("Victory", ItemClassification.progression, None, self.player))
self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
set_rule(victory_loc, lambda state: state.has("Boss Defeating Sword", self.player))
```
Requiring an event to finish the game will make the spoiler log display an additional
`Defeat the Final Boss: Victory` line when the player is expected to finish, rather than only showing their last
relevant item. But events aren't just about the spoiler log; a more substantial example of using events to structure
your logic might be:
```python
water_loc = MyGameLocation(self.player, "Water Level Switch", None, pump_station_region)
water_loc.place_locked_item(MyGameItem("Lowered Water Level", ItemClassification.progression, None, self.player))
pump_station_region.locations.append(water_loc)
set_rule(water_loc, lambda state: state.has("Double Jump", self.player)) # the switch is really high up
...
basement_loc = MyGameLocation(self.player, "Flooded House - Basement Chest", None, flooded_house_region)
flooded_house_region.locations += [upstairs_loc, ground_floor_loc, basement_loc]
...
set_rule(basement_loc, lambda state: state.has("Lowered Water Level", self.player))
```
This creates a "Lowered Water Level" event and a regular location whose access rule depends on that
event being reachable. If you made several more locations the same way, this would ensure all of those locations can
only become reachable when the event location is reachable (i.e. when the water level can be lowered), without
copy-pasting the event location's access rule and then repeatedly re-evaluating it. Also, the spoiler log will show
`Water Level Switch: Lowered Water Level` when the player is expected to do this.
To be clear, this example could also be modeled with a second Region (perhaps "Un-Flooded House"). Or you could modify
the game so flipping that switch checks a regular AP location in addition to lowering the water level.
Events are never required, but it may be cleaner to use an event if e.g. flipping that switch affects the logic in
dozens of half-flooded areas that would all otherwise need additional Regions, and you don't want it to be a regular
location. It depends on the game.
## Implementation
### Your World
@@ -488,8 +520,8 @@ In addition, the following methods can be implemented and are called in this ord
If it's hard to separate, this can be done during `generate_early` or `create_items` as well.
* `create_items(self)`
called to place player's items into the MultiWorld's itempool. By the end of this step all regions, locations and
items have to be in the MultiWorld's regions and itempool. You cannot add or remove items, locations, or regions
after this step. Locations cannot be moved to different regions after this step.
items have to be in the MultiWorld's regions and itempool. You cannot add or remove items, locations, or regions after
this step. Locations cannot be moved to different regions after this step. This includes event items and locations.
* `set_rules(self)`
called to set access and item rules on locations and entrances.
* `connect_entrances(self)`