mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Start writing docs
This commit is contained in:
parent
0a75577929
commit
cf3cbed5d2
2 changed files with 290 additions and 164 deletions
255
docs/source/Contribs/XYZGrid.md
Normal file
255
docs/source/Contribs/XYZGrid.md
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
# XYZGrid contribution
|
||||
|
||||
This contrib adds a 'coordinate grid' to the game, with rooms that are aware of
|
||||
their X, Y, Z coordinate. It also adds shortest-path pathfinding, auto-stepping
|
||||
and map visualization.
|
||||
|
||||
The system consists of 2D (XY) maps, where the Z
|
||||
coordinate is the name of the map. Each map can be linked together.
|
||||
|
||||
The grid supports 8 cardinal directions (north, northeast, east etc) as well as
|
||||
a 'fake' up/down directions (you enter up/down in-game but are actually moving to
|
||||
a different XY position on the same map).
|
||||
|
||||
An (XY) map is defined as a Python string, using a simple syntax;
|
||||
|
||||
```
|
||||
#-#-#-# #
|
||||
| / d
|
||||
#-# | #
|
||||
\ u |\
|
||||
o---#--------#---+-#-#
|
||||
| ^ |/
|
||||
| | #
|
||||
v |
|
||||
#-#-#-#-#----#
|
||||
|x|x|
|
||||
#-#-#-#
|
||||
|
||||
```
|
||||
|
||||
(descriptions of components are found below).
|
||||
|
||||
Basically the system will parse this string together with a _legend_ (a mapping
|
||||
describing how each symbol of the map behaves) to generate a representation of
|
||||
the map that can then be _spawned_ into actual rooms and exits. The map can also
|
||||
contain links to other maps this (map transitions).
|
||||
|
||||
The generation and maintenance of the map topology (how rooms are placed and
|
||||
relate to each other) are maintained entirely from outside of the game.
|
||||
|
||||
## Installation
|
||||
|
||||
1. Import and add the `evennia.contrib.commands.XYZGridCmdSet` to the
|
||||
`CharacterCmdset` cmdset in `mygame/commands.default_cmds.py`. Reload
|
||||
the server. This makes the `map`, `goto/path` and modified `teleport` and
|
||||
`open` commands available in-game.
|
||||
2. Edit `mygame/server/conf/settings.py` and set
|
||||
|
||||
EXTRA_LAUNCHER_COMMANDS['xyzgrid'] = 'evennia.contrib.launchcmd.xyzcommand'
|
||||
|
||||
3. Run the new `evennia xyzgrid help` for instructions on how to build the grid.
|
||||
|
||||
## Example usage
|
||||
|
||||
After installation, do the following (from your command line, where the
|
||||
`evennia` command is available) to install an example grid:
|
||||
|
||||
evennia xyzgrid init
|
||||
evennia xyzgrid add evennia.contrib.xyzgrid.map_example
|
||||
evennia xyzgrid list
|
||||
evennia xyzgrid show "the large tree"
|
||||
evennia xyzgrid show "the small cave"
|
||||
evennia xyzgrid build
|
||||
evennia reload
|
||||
|
||||
(remember to reload the server after build operations).
|
||||
|
||||
Now you can log into the
|
||||
server and do `teleport (3,0,the large tree)` to teleport into the map.
|
||||
|
||||
You can use `open togrid = (3, 0, the large tree)` to open a permanent (one-way)
|
||||
exit from your current location into the grid. To make a way back to a non-grid
|
||||
location just stand in a grid room and open a new exit out of it:
|
||||
`open tolimbo = #2`.
|
||||
|
||||
Try `goto view` to go to the top of the tree and `goto dungeon` to go down to
|
||||
the dungeon entrance at the bottom of the tree.
|
||||
|
||||
## A note on 3D = 2D + 1D
|
||||
|
||||
Since actual 3D movement usually is impractical to visualize in text, most
|
||||
action in this contrib takes place on 2-dimenstional XY-planes we refer to as
|
||||
_Maps_. Changing Z-coordinate means moving to another Map. Maps does not need
|
||||
be the same size as one another and there is no (enforced) concept of moving
|
||||
'up' or 'down' between maps - instead you basically 'teleport' between them,
|
||||
which means you can have your characters end up anywhere you want in the next
|
||||
map, regardless of which XY coordinate they were leaving from.
|
||||
|
||||
If you really want an actual 3D coordinate system, you could make all maps the
|
||||
same size and name them `0`, `1`, `2` etc. But most users will want to just
|
||||
treat each map as a location, and name the "Z-coordinate" things like `Dungeon
|
||||
of Doom`, `The ice queen's palace` or `City of Blackhaven`.
|
||||
|
||||
Whereas the included rooms and exits can be used for 'true' 3D movement, the more
|
||||
advanced tools like pathfinding will only operate within each XY `Map`.
|
||||
|
||||
## Components of the XYgrid
|
||||
|
||||
1. The Map String - describes the topology of a single
|
||||
map/location/Z-coordinate.
|
||||
2. The Map Legend - describes how to parse each symbol in the map string to a
|
||||
topological relation, such as 'a room' or 'a two-way link east-west'.
|
||||
3. The XYMap - combines the Map String and Legend into a parsed object with
|
||||
pathfinding and visual-range handling.
|
||||
4. The MultiMap - tracks multiple maps
|
||||
5. Rooms, Exits and Prototypes - custom Typeclasses that understands XYZ coordinates.
|
||||
The prototype describes how to build a database-entity from a given
|
||||
Map Legend component.
|
||||
6. The Grid - the combination of prototype-built rooms/exits with Maps for
|
||||
pathfinding and visualization. This is kept in-sync with changes to Map
|
||||
Strings.
|
||||
|
||||
|
||||
### The Map string
|
||||
|
||||
A `Map` represents one Z-coordinate/location. The Map starts out as an text
|
||||
string visually laying out one 2D map (so one Z-position). It is created
|
||||
manually by the developer/builder outside of the game. The string-map
|
||||
has one character per node(room) and descibe how rooms link together. Each
|
||||
symbol is linked to a particular abstract Python class which helps parse
|
||||
the map-string. While the contrib comes with a large number of nodes and links,
|
||||
one can also make one's own.
|
||||
|
||||
```
|
||||
MAP = r"""
|
||||
|
||||
+ 0 1 2 3
|
||||
|
||||
3 #-#---o
|
||||
v |
|
||||
2 #-# |
|
||||
|x| ^
|
||||
1 #-#-#
|
||||
/ \
|
||||
0 # #d#
|
||||
|
||||
+ 0 1 2 3
|
||||
|
||||
"""
|
||||
|
||||
```
|
||||
Above, only the two `+`-characters in the upper-left and bottom-left are
|
||||
required to mark the start of grid area - the numbered axes are optional but
|
||||
recommended for readability! Note that the coordinate system has (0, 0) in the
|
||||
bottom left - this means that +Y movement is 'upwards' in the string as
|
||||
expected.
|
||||
|
||||
### Map Legend
|
||||
|
||||
The map legend is a mapping of one-character symbols to Python classes
|
||||
representing _Nodes_ (usually equivalent to in-game rooms) or _Links_ (which
|
||||
usually start as an Exit, but the length of the link only describes the
|
||||
target-destination and has no in-game representation otherwise). These 'map
|
||||
components' are Python classes that can be inherited and modified as needed.
|
||||
|
||||
The default legend support nearly 20 different symbols, including:
|
||||
|
||||
- Nodes (rooms) are always on XY coordinates (not between) and
|
||||
- 8 two-way cardinal directions (n, ne etc)
|
||||
- up/down - this is a 'fake' up-down using XY coordinates for ease of
|
||||
visualization (the exit is just called 'up' or 'down', unless you display the
|
||||
actual coordinate to the user they'll never know the difference).
|
||||
- One-way links in 4 cardinal directions (not because it's hard to add more,
|
||||
but because there are no obvious ASCII characters for the diaginal movements ...)
|
||||
- Multi-step links are by default considered as one step in-game.
|
||||
- 'Invisible' symbols that are used to block or act as deterrent for the
|
||||
pathfinder to use certain routes (like a hidden entrance you should be
|
||||
auto-pathing through).
|
||||
- 'Points of interest' are nodes or links where the auto-stepper will always
|
||||
stop, even if it can see what's behind. This is great for places where you
|
||||
expect to have a door or a guard (which are not represented on the map).
|
||||
- Teleporter-links, for jumping from one edge of the map to the other without
|
||||
having to draw an actual link across. Good for maps that 'wrap around'.
|
||||
- Transitional links between maps/locations/Z-positions. Note that pathfinding
|
||||
will _not_ work across map transitions.
|
||||
|
||||
### Map
|
||||
|
||||
All `Map strings` are combined with their `Map Legends` to be parsed into a `Map`
|
||||
object. The `Map` object has the relations between all nodes stored in a very
|
||||
efficient matrix for quick lookup. This allows for:
|
||||
|
||||
- Shortest-path finding. The shortest-path from one coordinate to another
|
||||
is calculated using an optimized implementation of the
|
||||
[Dijkstra algorithm](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm).
|
||||
While large solutions can be slow (up to 20 seconds for a 10 000 node map
|
||||
with 8 cardinal links between every room), the solution is cached to disk -
|
||||
meaning that as long as the map-string does not change, subsequent
|
||||
path-finding operations are all very fast (<0.1s, usually much faster).
|
||||
Once retrieving the route, the player can easily be auto-stepped along it.
|
||||
- Visual-range - useful for displaying the map to the user as they move. The
|
||||
system can either show everything within a certain grid-distance, or a
|
||||
certain number of connected nodes away from the character's position.
|
||||
- Visualize-paths. The system can highlight the path to a target on the grid.
|
||||
Useful for showing where the pathfinder is moving you.
|
||||
- The Map parser can be customized so as to give different weight to longer
|
||||
links - by default a 3-step long link has the same 'weight' for the
|
||||
pathfinder as two nodes next to each other. This could be combined with
|
||||
some measure of it taking longer to traverse such exits.
|
||||
|
||||
|
||||
### MultiMap
|
||||
|
||||
Multiple `Maps` (or 'Z coordinates') are combined together in the `MultiMap`
|
||||
handler. The MultiMap knows all maps in the game because it must be possible to
|
||||
transition from one to the other by giving the name/Z-coordinate to jump to.
|
||||
|
||||
It's worth pointing out that neither `Maps`, nor `MultiMap` has any direct
|
||||
link to the game at this point - these are all just abstract _representations_
|
||||
of the game world stored in memory.
|
||||
|
||||
|
||||
### Rooms and Exits
|
||||
|
||||
The component (_Nodes_ and _Links_ both) of each `Map` can have a `prototype`
|
||||
dict associated with it. This is the default used when converting this node/link
|
||||
into something actually visible in-game. It is also possible to override
|
||||
the default on a per-XY coordinate level.
|
||||
|
||||
- For _Nodes_, the `evennia.contrib.xyzgrid.room.XYZRoom` typeclass (or a child
|
||||
thereof) should be used. This sets up storage of the XYZ-coordinates correctly
|
||||
(using `Tags`).
|
||||
- For _Links_, one uses the `evennia.contrib.xyzgrid.room.XYZExit` typeclass
|
||||
(or a child thereof). This also sets up the proper coordinates, both for the
|
||||
location they are in and for the exit's destination.
|
||||
|
||||
|
||||
### The Grid
|
||||
|
||||
The combination of `MultiMaps` and `prototypes` are used to create the `Grid`, a
|
||||
series of coordinate-bound rooms/exits actually present in the game world. Each
|
||||
node of this grid has a unique `(X, Y, Z)` position (no duplicates).
|
||||
|
||||
Once the prototypes have been used to create the grid, it must be kept in-sync
|
||||
with any changes in map-strings `Map` structures - otherwise pathfinding and
|
||||
visual-range displays will not work (or at least be confusingly inaccurate). So
|
||||
changes should _only_ be done on the Map-string outside of the game, _not_ by
|
||||
digging new XYZRooms manually!
|
||||
|
||||
The contrib provides a sync mechanism. This compares the stored `Map` with
|
||||
the current topology and rebuilds/removes any nodes/rooms/links/exits that
|
||||
has changed. Since this process can be slow, you need to run this manually when
|
||||
you know you've made a change.
|
||||
|
||||
Remember that syncing is only necesssary for topological changes! That is,
|
||||
changes visible in the map string. Fixing the `desc` of a room or adding a new
|
||||
enemy does not require any re-sync. Also, things not visible on the
|
||||
map (like a secret entrance) should not be available to the pathfinder anyway.
|
||||
|
||||
You can dig non-XYZRoom objects and link them to `XYZRooms` with no issues -
|
||||
they will work like normal in-game. But such rooms (and exits leading to/from
|
||||
them) are _not_ considered part of the grid for the purposes of pathfinding etc.
|
||||
Exactly how to organize this depends on your game.
|
||||
|
||||
|
||||
|
|
@ -13,179 +13,50 @@ This contrib forces each room to exist on a 3-dimensional XYZ grid and also
|
|||
implements very efficient pathfinding along with tools for displaying
|
||||
your current visual-range and a lot of related features.
|
||||
|
||||
## A note on 3D = 2D + 1D
|
||||
The rooms of the grid are entirely controlled from outside the game, using
|
||||
python modules with strings and dicts defining the map(s) of the game. It's
|
||||
possible to combine grid- with non-grid rooms, and you can decorate
|
||||
grid rooms as much as you like in-game, but you cannot build new grid
|
||||
rooms without editing the map files outside of the game.
|
||||
|
||||
Since actual 3D movement usually is impractical to visualize in text, most
|
||||
action in this contrib takes place on 2-dimenstional XY-planes we refer to as
|
||||
_Maps_. Changing Z-coordinate means moving to another Map. Maps does not need
|
||||
be the same size as one another and there is no (enforced) concept of moving
|
||||
'up' or 'down' between maps - instead you basically 'teleport' between them,
|
||||
which means you can have your characters end up anywhere you want in the next
|
||||
map, regardless of which XY coordinate they were leaving from.
|
||||
The full docs are found as
|
||||
[Contribs/XYZGrid](https://evennia.com/docs/latest/Contributions/XYZGrid.html)
|
||||
in the docs.
|
||||
|
||||
If you really want an actual 3D coordinate system, you could make all maps the
|
||||
same size and name them `0`, `1`, `2` etc. But most users will want to just
|
||||
treat each map as a location, and name the "Z-coordinate" things like `Dungeon
|
||||
of Doom`, `The ice queen's palace` or `City of Blackhaven`.
|
||||
## Installation
|
||||
|
||||
Whereas the included rooms and exits can be used for 'true' 3D movement, the more
|
||||
advanced tools like pathfinding will only operate within each XY `Map`.
|
||||
1. Import and add the `evennia.contrib.commands.XYZGridCmdSet` to the
|
||||
`CharacterCmdset` cmdset in `mygame/commands.default_cmds.py`. Reload
|
||||
the server. This makes the `map`, `goto/path` and modified `teleport` and
|
||||
`open` commands available in-game.
|
||||
2. Edit `mygame/server/conf/settings.py` and set
|
||||
|
||||
## Components of the XYgrid
|
||||
EXTRA_LAUNCHER_COMMANDS['xyzgrid'] = 'evennia.contrib.launchcmd.xyzcommand'
|
||||
|
||||
1. The Map String - describes the topology of a single
|
||||
map/location/Z-coordinate.
|
||||
2. The Map Legend - describes how to parse each symbol in the map string to a
|
||||
topological relation, such as 'a room' or 'a two-way link east-west'.
|
||||
3. The XYMap - combines the Map String and Legend into a parsed object with
|
||||
pathfinding and visual-range handling.
|
||||
4. The MultiMap - tracks multiple maps
|
||||
5. Rooms, Exits and Prototypes - custom Typeclasses that understands XYZ coordinates.
|
||||
The prototype describes how to build a database-entity from a given
|
||||
Map Legend component.
|
||||
6. The Grid - the combination of prototype-built rooms/exits with Maps for
|
||||
pathfinding and visualization. This is kept in-sync with changes to Map
|
||||
Strings.
|
||||
3. Run the new `evennia xyzgrid help` for instructions on how to build the grid.
|
||||
|
||||
## Example usage
|
||||
|
||||
### The Map string
|
||||
After installation, do the following (from your command line, where the
|
||||
`evennia` command is available) to install an example grid:
|
||||
|
||||
A `Map` represents one Z-coordinate/location. The Map starts out as an text
|
||||
string visually laying out one 2D map (so one Z-position). It is created
|
||||
manually by the developer/builder outside of the game. The string-map
|
||||
has one character per node(room) and descibe how rooms link together. Each
|
||||
symbol is linked to a particular abstract Python class which helps parse
|
||||
the map-string. While the contrib comes with a large number of nodes and links,
|
||||
one can also make one's own.
|
||||
evennia xyzgrid init
|
||||
evennia xyzgrid add evennia.contrib.xyzgrid.map_example
|
||||
evennia xyzgrid list
|
||||
evennia xyzgrid show "the large tree"
|
||||
evennia xyzgrid show "the small cave"
|
||||
evennia xyzgrid build
|
||||
evennia reload
|
||||
|
||||
```
|
||||
MAP = r"""
|
||||
(remember to reload the server after build operations).
|
||||
|
||||
+ 0 1 2 3
|
||||
Now you can log into the
|
||||
server and do `teleport (3,0,the large tree)` to teleport into the map.
|
||||
|
||||
3 #-#---o
|
||||
v |
|
||||
2 #-# |
|
||||
|x| ^
|
||||
1 #-#-#
|
||||
/ \
|
||||
0 # #d#
|
||||
|
||||
+ 0 1 2 3
|
||||
|
||||
"""
|
||||
|
||||
```
|
||||
Above, only the two `+`-characters in the upper-left and bottom-left are
|
||||
required to mark the start of grid area - the numbered axes are optional but
|
||||
recommended for readability! Note that the coordinate system has (0, 0) in the
|
||||
bottom left - this means that +Y movement is 'upwards' in the string as
|
||||
expected.
|
||||
|
||||
### Map Legend
|
||||
|
||||
The map legend is a mapping of one-character symbols to Python classes
|
||||
representing _Nodes_ (usually equivalent to in-game rooms) or _Links_ (which
|
||||
usually start as an Exit, but the length of the link only describes the
|
||||
target-destination and has no in-game representation otherwise). These 'map
|
||||
components' are Python classes that can be inherited and modified as needed.
|
||||
|
||||
The default legend support nearly 20 different symbols, including:
|
||||
|
||||
- Nodes (rooms) are always on XY coordinates (not between) and
|
||||
- 8 two-way cardinal directions (n, ne etc)
|
||||
- up/down - this is a 'fake' up-down using XY coordinates for ease of
|
||||
visualization (the exit is just called 'up' or 'down', unless you display the
|
||||
actual coordinate to the user they'll never know the difference).
|
||||
- One-way links in 4 cardinal directions (not because it's hard to add more,
|
||||
but because there are no obvious ASCII characters for the diaginal movements ...)
|
||||
- Multi-step links are by default considered as one step in-game.
|
||||
- 'Invisible' symbols that are used to block or act as deterrent for the
|
||||
pathfinder to use certain routes (like a hidden entrance you should be
|
||||
auto-pathing through).
|
||||
- 'Points of interest' are nodes or links where the auto-stepper will always
|
||||
stop, even if it can see what's behind. This is great for places where you
|
||||
expect to have a door or a guard (which are not represented on the map).
|
||||
- Teleporter-links, for jumping from one edge of the map to the other without
|
||||
having to draw an actual link across. Good for maps that 'wrap around'.
|
||||
- Transitional links between maps/locations/Z-positions. Note that pathfinding
|
||||
will _not_ work across map transitions.
|
||||
|
||||
### Map
|
||||
|
||||
All `Map strings` are combined with their `Map Legends` to be parsed into a `Map`
|
||||
object. The `Map` object has the relations between all nodes stored in a very
|
||||
efficient matrix for quick lookup. This allows for:
|
||||
|
||||
- Shortest-path finding. The shortest-path from one coordinate to another
|
||||
is calculated using an optimized implementation of the
|
||||
[Dijkstra algorithm](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm).
|
||||
While large solutions can be slow (up to 20 seconds for a 10 000 node map
|
||||
with 8 cardinal links between every room), the solution is cached to disk -
|
||||
meaning that as long as the map-string does not change, subsequent
|
||||
path-finding operations are all very fast (<0.1s, usually much faster).
|
||||
Once retrieving the route, the player can easily be auto-stepped along it.
|
||||
- Visual-range - useful for displaying the map to the user as they move. The
|
||||
system can either show everything within a certain grid-distance, or a
|
||||
certain number of connected nodes away from the character's position.
|
||||
- Visualize-paths. The system can highlight the path to a target on the grid.
|
||||
Useful for showing where the pathfinder is moving you.
|
||||
- The Map parser can be customized so as to give different weight to longer
|
||||
links - by default a 3-step long link has the same 'weight' for the
|
||||
pathfinder as two nodes next to each other. This could be combined with
|
||||
some measure of it taking longer to traverse such exits.
|
||||
|
||||
|
||||
### MultiMap
|
||||
|
||||
Multiple `Maps` (or 'Z coordinates') are combined together in the `MultiMap`
|
||||
handler. The MultiMap knows all maps in the game because it must be possible to
|
||||
transition from one to the other by giving the name/Z-coordinate to jump to.
|
||||
|
||||
It's worth pointing out that neither `Maps`, nor `MultiMap` has any direct
|
||||
link to the game at this point - these are all just abstract _representations_
|
||||
of the game world stored in memory.
|
||||
|
||||
|
||||
### Rooms and Exits
|
||||
|
||||
The component (_Nodes_ and _Links_ both) of each `Map` can have a `prototype`
|
||||
dict associated with it. This is the default used when converting this node/link
|
||||
into something actually visible in-game. It is also possible to override
|
||||
the default on a per-XY coordinate level.
|
||||
|
||||
- For _Nodes_, the `evennia.contrib.xyzgrid.room.XYZRoom` typeclass (or a child
|
||||
thereof) should be used. This sets up storage of the XYZ-coordinates correctly
|
||||
(using `Tags`).
|
||||
- For _Links_, one uses the `evennia.contrib.xyzgrid.room.XYZExit` typeclass
|
||||
(or a child thereof). This also sets up the proper coordinates, both for the
|
||||
location they are in and for the exit's destination.
|
||||
|
||||
|
||||
### The Grid
|
||||
|
||||
The combination of `MultiMaps` and `prototypes` are used to create the `Grid`, a
|
||||
series of coordinate-bound rooms/exits actually present in the game world. Each
|
||||
node of this grid has a unique `(X, Y, Z)` position (no duplicates).
|
||||
|
||||
Once the prototypes have been used to create the grid, it must be kept in-sync
|
||||
with any changes in map-strings `Map` structures - otherwise pathfinding and
|
||||
visual-range displays will not work (or at least be confusingly inaccurate). So
|
||||
changes should _only_ be done on the Map-string outside of the game, _not_ by
|
||||
digging new XYZRooms manually!
|
||||
|
||||
The contrib provides a sync mechanism. This compares the stored `Map` with
|
||||
the current topology and rebuilds/removes any nodes/rooms/links/exits that
|
||||
has changed. Since this process can be slow, you need to run this manually when
|
||||
you know you've made a change.
|
||||
|
||||
Remember that syncing is only necesssary for topological changes! That is,
|
||||
changes visible in the map string. Fixing the `desc` of a room or adding a new
|
||||
enemy does not require any re-sync. Also, things not visible on the
|
||||
map (like a secret entrance) should not be available to the pathfinder anyway.
|
||||
|
||||
You can dig non-XYZRoom objects and link them to `XYZRooms` with no issues -
|
||||
they will work like normal in-game. But such rooms (and exits leading to/from
|
||||
them) are _not_ considered part of the grid for the purposes of pathfinding etc.
|
||||
Exactly how to organize this depends on your game.
|
||||
You can use `open togrid = (3, 0, the large tree)` to open a permanent (one-way)
|
||||
exit from your current location into the grid. To make a way back to a non-grid
|
||||
location just stand in a grid room and open a new exit out of it:
|
||||
`open tolimbo = #2`.
|
||||
|
||||
Try `goto view` to go to the top of the tree and `goto dungeon` to go down to
|
||||
the dungeon entrance at the bottom of the tree.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue