From 5c710ad032eedb6bd79ea2436490423c5a41aee0 Mon Sep 17 00:00:00 2001 From: Ixrec Date: Mon, 16 Jun 2025 13:36:12 +0100 Subject: [PATCH] Docs: Rework the "Events" Section of `world api.md` (#5012) Co-authored-by: Scipio Wright Co-authored-by: qwint Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- docs/world api.md | 86 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 27 deletions(-) diff --git a/docs/world api.md b/docs/world api.md index 013b02cc..833d379b 100644 --- a/docs/world api.md +++ b/docs/world api.md @@ -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)`