Continue with doc writing for xyzgrid

This commit is contained in:
Griatch 2021-07-21 01:35:57 +02:00
parent cf3cbed5d2
commit a140c68247
10 changed files with 1285 additions and 264 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
# Toc
- [API root](api/evennia-api.rst)
- [Coding/Coding Introduction](Coding/Coding-Introduction)
- [Coding/Coding Overview](Coding/Coding-Overview)
- [Coding/Continuous Integration](Coding/Continuous-Integration)
@ -82,6 +82,7 @@
- [Contribs/Dialogues in events](Contribs/Dialogues-in-events)
- [Contribs/Dynamic In Game Map](Contribs/Dynamic-In-Game-Map)
- [Contribs/Static In Game Map](Contribs/Static-In-Game-Map)
- [Contribs/XYZGrid](Contribs/XYZGrid)
- [./Contributing](./Contributing)
- [./Contributing Docs](./Contributing-Docs)
- [./Evennia API](./Evennia-API)

View file

@ -16,7 +16,7 @@ your current visual-range and a lot of related features.
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
grid rooms as much as you like in-game, but you cannot spawn new grid
rooms without editing the map files outside of the game.
The full docs are found as
@ -33,7 +33,7 @@ in the docs.
EXTRA_LAUNCHER_COMMANDS['xyzgrid'] = 'evennia.contrib.launchcmd.xyzcommand'
3. Run the new `evennia xyzgrid help` for instructions on how to build the grid.
3. Run the new `evennia xyzgrid help` for instructions on how to spawn the grid.
## Example usage
@ -41,14 +41,14 @@ 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 add evennia.contrib.xyzgrid.example
evennia xyzgrid list
evennia xyzgrid show "the large tree"
evennia xyzgrid show "the small cave"
evennia xyzgrid build
evennia xyzgrid spawn
evennia reload
(remember to reload the server after build operations).
(remember to reload the server after spawn operations).
Now you can log into the
server and do `teleport (3,0,the large tree)` to teleport into the map.

View file

@ -21,15 +21,16 @@ from evennia.contrib.xyzgrid import xymap_legend
# default prototype parent. It's important that
# the typeclass inherits from the XYZRoom (or XYZExit)
# the xymap_legend.XYZROOM_PARENT and XYZEXIT_PARENTS can also
# be used as a shortcut.
# if adding the evennia.contrib.xyzgrid.prototypes to
# settings.PROTOTYPE_MODULES, one could just set the
# prototype_parent to 'xyz_room' and 'xyz_exit' respectively
# instead.
PARENT = {
"key": "An empty room",
"prototype_key": "xyzmap_room_map1",
"typeclass": "evennia.contrib.xyzgrid.xyzroom.XYZRoom",
"desc": "An empty room.",
"options": {}
}
@ -66,7 +67,7 @@ MAP1 = r"""
"""
class TransitionToCave(xymap_legend.MapTransitionMapNode):
class TransitionToCave(xymap_legend.TransitionMapNode):
"""
A transition from 'the large tree' to 'the small cave' map. This node is never spawned
into a room but only acts as a target for finding the exit's destination.
@ -169,7 +170,7 @@ MAP2 = r"""
"""
# custom map node
class TransitionToLargeTree(xymap_legend.MapTransitionMapNode):
class TransitionToLargeTree(xymap_legend.TransitionMapNode):
"""
A transition from 'the small cave' to 'the large tree' map. This node is never spawned
into a room by only acts as a target for finding the exit's destination.

View file

@ -1,6 +1,6 @@
"""
Custom Evennia launcher command option for building/rebuilding the grid in a separate process than
the main server (since this can be slow).
Custom Evennia launcher command option for maintaining the grid in a separate process than the main
server (since this can be slow).
To use, add to the settings:
::
@ -22,7 +22,7 @@ from evennia.utils import ansi
from evennia.contrib.xyzgrid.xyzgrid import get_xyzgrid
_HELP_SHORT = """
evennia xyzgrid help|list|init|add|build|initpath|delete [<options>]
evennia xyzgrid help|list|init|add|spawn|initpath|delete [<options>]
Manages the XYZ grid. Use 'xyzgrid help <option>' for documentation.
"""
@ -34,7 +34,7 @@ help <command> - get help about each command:
list - show list
init - initialize grid (only one time)
add - add new maps to grid
build - build added maps into actual db-rooms/exits
spawn - spawn added maps into actual db-rooms/exits
initpath - (re)creates pathfinder matrices
delete - delete part or all of grid
"""
@ -78,38 +78,38 @@ add <path.to.xymap.module> [<path> <path>,...]
{"map": mapstring, "zcoord": mapname/zcoord, "legend": dict, "prototypes": dict}
describing one single XYmap, or
- a XYMAP_DATA_LIST - a list of multiple dicts on the XYMAP_DATA form. This allows for
embedding multiple maps in the same module. See evennia/contrib/xyzgrid/map_example.py
embedding multiple maps in the same module. See evennia/contrib/xyzgrid/example.py
for an example of how this looks.
Note that adding a map does *not* build it. If maps are linked to one another, you should
add all linked maps before running 'build', or you'll get errors when creating transitional
Note that adding a map does *not* spawn it. If maps are linked to one another, you should
add all linked maps before running 'spawn', or you'll get errors when creating transitional
exits between maps.
Examples:
evennia xyzgrid add evennia.contrib.xyzgrid.map_example
evennia xyzgrid add evennia.contrib.xyzgrid.example
evennia xyzgrid add world.mymap1 world.mymap2 world.mymap3
"""
_HELP_BUILD = """
build
_HELP_SPAWN = """
spawn
Builds/updates the entire database grid based on the added maps. For a new grid, this will
spawns/updates the entire database grid based on the added maps. For a new grid, this will
spawn all new rooms/exits (and may take a good while!). For updating, rooms may be
removed/spawned if a map changed since the last build.
removed/spawned if a map changed since the last spawn.
build "(X,Y,Z|mapname)"
spawn "(X,Y,Z|mapname)"
Builds/updates only a part of the grid. Remember the quotes around the coordinate (this
spawns/updates only a part of the grid. Remember the quotes around the coordinate (this
is mostly because shells don't like them)! Use '*' as a wild card for XY coordinates.
This should usually only be used if the full grid has already been built once - otherwise
inter-map transitions may fail! Z is the name/z-coordinate of the map to build.
inter-map transitions may fail! Z is the name/z-coordinate of the map to spawn.
Examples:
evennia xyzgrid build - build all
evennia xyzgrid "(*, *, mymap1)" - build everything of map/zcoord mymap1
evennia xyzgrid "(12, 5, mymap1)" - build only coordinate (12, 5) on map/zcoord mymap1
evennia xyzgrid spawn - spawn all
evennia xyzgrid "(*, *, mymap1)" - spawn everything of map/zcoord mymap1
evennia xyzgrid "(12, 5, mymap1)" - spawn only coordinate (12, 5) on map/zcoord mymap1
"""
_HELP_INITPATH = """
@ -118,7 +118,7 @@ initpath
Recreates the pathfinder matrices for the entire grid. These are used for all shortest-path
calculations. The result will be cached to disk (in mygame/server/.cache/). If not run, each
map will run this automatically first time it's used. Running this will always force to
rebuild the cache.
respawn the cache.
initpath Z|mapname
@ -156,7 +156,7 @@ _TOPICS_MAP = {
"list": _HELP_LIST,
"init": _HELP_INIT,
"add": _HELP_ADD,
"build": _HELP_BUILD,
"spawn": _HELP_SPAWN,
"initpath": _HELP_INITPATH,
"delete": _HELP_DELETE
}
@ -216,14 +216,14 @@ def _option_list(*suboptions):
"to another map), so the 'missing room(s)' may just be from such nodes.")
elif nrooms > nnodes:
print(f"{nrooms} / {nnodes} rooms are spawned\n"
"Note: Maybe some rooms were removed from map. Run 'build' to re-sync.")
"Note: Maybe some rooms were removed from map. Run 'spawn' to re-sync.")
else:
print(f"{nrooms} / {nnodes} rooms are spawned\n")
if checkwarning:
print("Note: This check is not complete; it does not consider changed map "
"topology\nlike relocated nodes/rooms and new/removed links/exits - this "
"is calculated only during a build.")
"is calculated only during a spawn.")
print("\nDisplayed map (as appearing in-game):\n\n" + ansi.parse_ansi(str(xymap)))
print("\nRaw map string (including axes and invisible nodes/links):\n"
+ str(xymap.mapstring))
@ -268,9 +268,9 @@ def _option_add(*suboptions):
print(f"Added (or readded) {len(xymap_data_list)} XYMaps to grid.")
def _option_build(*suboptions):
def _option_spawn(*suboptions):
"""
Build the grid or part of it.
spawn the grid or part of it.
"""
grid = get_xyzgrid()
@ -286,14 +286,14 @@ def _option_build(*suboptions):
try:
x, y, z = (part.strip() for part in opts.split(","))
except ValueError:
print("Build coordinate must be given as (X, Y, Z) tuple, where '*' act "
print("spawn coordinate must be given as (X, Y, Z) tuple, where '*' act "
"wild cards and Z is the mapname/z-coord of the map to load.")
return
else:
x, y, z = '*', '*', '*'
if x == y == z == '*':
inp = input("This will (re)build the entire grid. If it was built before, it may spawn \n"
inp = input("This will (re)spawn the entire grid. If it was built before, it may spawn \n"
"new rooms or delete rooms that no longer matches the grid.\nDo you want to "
"continue? [Y]/N? ")
else:
@ -303,9 +303,9 @@ def _option_build(*suboptions):
print("Aborted.")
return
print("Starting build ...")
print("Starting spawn ...")
grid.spawn(xyz=(x, y, z))
print("... build complete!\nIt's recommended to reload the server to refresh caches if this "
print("... spawn complete!\nIt's recommended to reload the server to refresh caches if this "
"modified an existing grid.")

View file

@ -14,19 +14,34 @@ and/or
{'prototype_parent': 'xyz_exit', ...}
"""
from django.conf import settings
# required by the prototype importer
PROTOTYPE_LIST = [
{
'prototype_key': 'xyz_room',
'typeclass': 'evennia.contrib.xyzgrid.xyzroom.XYZRoom',
'prototype_tags': ("xyzroom", ),
'key': "A room",
'desc': "An empty room."
}, {
'prototype_key': 'xyz_exit',
'prototype_tags': ("xyzexit", ),
'typeclass': 'evennia.contrib.xyzgrid.xyzroom.XYZExit',
'desc': "An exit."
}
]
try:
room_override = settings.XYZROOM_PROTOTYPE_OVERRIDE
except AttributeError:
room_override = {}
try:
exit_override = settings.XYZEXIT_PROTOTYPE_OVERRIDE
except AttributeError:
exit_override = {}
room_prototype = {
'prototype_key': 'xyz_room',
'typeclass': 'evennia.contrib.xyzgrid.xyzroom.XYZRoom',
'prototype_tags': ("xyzroom", ),
'key': "A room",
'desc': "An empty room."
}
room_prototype.update(room_override)
exit_prototype = {
'prototype_key': 'xyz_exit',
'typeclass': 'evennia.contrib.xyzgrid.xyzroom.XYZExit',
'prototype_tags': ("xyzexit", ),
'desc': "An exit."
}
exit_prototype.update(exit_override)
# accessed by the prototype importer
PROTOTYPE_LIST = [room_prototype, exit_prototype]

View file

@ -1182,12 +1182,12 @@ class TestXYZGrid(EvenniaTest):
# map transitions
class Map12aTransition(xymap_legend.MapTransitionMapNode):
class Map12aTransition(xymap_legend.TransitionMapNode):
symbol = "T"
target_map_xyz = (1, 0, "map12b")
class Map12bTransition(xymap_legend.MapTransitionMapNode):
class Map12bTransition(xymap_legend.TransitionMapNode):
symbol = "T"
target_map_xyz = (0, 1, "map12a")
@ -1251,7 +1251,7 @@ class TestXYZGridTransition(EvenniaTest):
class TestBuildExampleGrid(EvenniaTest):
"""
Test building the map_example (this takes about 30s)
Test building the map-example (this takes about 30s)
"""
def setUp(self):
@ -1271,7 +1271,7 @@ class TestBuildExampleGrid(EvenniaTest):
Build the map example.
"""
mapdatas = self.grid.maps_from_module("evennia.contrib.xyzgrid.map_example")
mapdatas = self.grid.maps_from_module("evennia.contrib.xyzgrid.example")
self.assertEqual(len(mapdatas), 2)
self.grid.add_maps(*mapdatas)

View file

@ -88,7 +88,7 @@ The nodes and links can be customized by add your own implementation of `MapNode
the LEGEND dict, mapping them to a particular character symbol. A `MapNode` can only be added
on an even XY coordinate while `MapLink`s can be added anywhere on the xygrid.
See `./map_example.py` for some empty grid areas to start from.
See `./example.py` for a full grid example.
----
"""
@ -456,11 +456,12 @@ class XYMap:
xygrid[ix][iy] = XYgrid[iX][iY] = node_index_map[node_index] = \
mapnode_or_link_class(x=ix, y=iy, Z=self.Z,
node_index=node_index, xymap=self)
node_index=node_index, symbol=char, xymap=self)
else:
# we have a link at this xygrid position (this is ok everywhere)
xygrid[ix][iy] = mapnode_or_link_class(x=ix, y=iy, Z=self.Z, xymap=self)
xygrid[ix][iy] = mapnode_or_link_class(x=ix, y=iy, Z=self.Z, symbol=char,
xymap=self)
# store the symbol mapping for transition lookups
symbol_map[char].append(xygrid[ix][iy])

View file

@ -45,14 +45,13 @@ class MapNode:
- `display_symbol` (str or `None`) - This is what is used to visualize this node later. This
symbol must still only have a visual size of 1, but you could e.g. use some fancy unicode
character (be aware of encodings to different clients though) or, commonly, add color
tags around it. For further customization, the `.get_display_symbol` method receives
the full grid and can return a dynamically determined display symbol. If set to `None`,
the `symbol` is used.
tags around it. For further customization, the `.get_display_symbol` method
can return a dynamically determined display symbol. If set to `None`, the `symbol` is used.
- `interrupt_path` (bool): If this is set, the shortest-path algorithm will include this
node as normally, but stop when reaching it, even if not having reached its target yet. This
is useful for marking 'points of interest' along a route, or places where you are not
expected to be able to continue without some further in-game action not covered by the map
(such as a guard or locked gate etc).
node as normally, but the auto-stepper will stop when reaching it, even if not having reached
its target yet. This is useful for marking 'points of interest' along a route, or places where
you are not expected to be able to continue without some further in-game action not covered by
the map (such as a guard or locked gate etc).
- `prototype` (dict) - The default `prototype` dict to use for reproducing this map component
on the game grid. This is used if not overridden specifically for this coordinate. If this
is not given, nothing will be spawned for this coordinate (a 'virtual' node can be useful
@ -88,7 +87,7 @@ class MapNode:
'u': ('up', 'u'),
}
def __init__(self, x, y, Z, node_index=0, xymap=None):
def __init__(self, x, y, Z, node_index=0, symbol=None, xymap=None):
"""
Initialize the mapnode.
@ -99,6 +98,8 @@ class MapNode:
node_index (int): This identifies this node with a running
index number required for pathfinding. This is used
internally and should not be set manually.
symbol (str, optional): Set during parsing - allows to override
the symbol based on what's set in the legend.
xymap (XYMap, optional): The map object this sits on.
"""
@ -115,6 +116,8 @@ class MapNode:
self.Z = Z
self.node_index = node_index
if symbol is not None:
self.symbol = symbol
# this indicates linkage in 8 cardinal directions on the string-map,
# n,ne,e,se,s,sw,w,nw and link that to a node (always)
@ -501,8 +504,8 @@ class MapLink:
symbol must still only have a visual size of 1, but you could e.g. use some fancy unicode
character (be aware of encodings to different clients though) or, commonly, add color
tags around it. For further customization, the `.get_display_symbol` can be used.
- `default_weight` (int) - Each link direction covered by this link can have its seprate weight,
this is used if none is specified in a particular direction. This value must be >= 1,
- `default_weight` (int) - Each link direction covered by this link can have its separate
weight, this is used if none is specified in a particular direction. This value must be >= 1,
and can be higher than 1 if a link should be less favored.
- `directions` (dict) - this specifies which link edge to which other link-edge this link
is connected; A link connecting the link's sw edge to its easted edge would be written
@ -577,7 +580,7 @@ class MapLink:
# will be used.
spawn_aliases = {}
def __init__(self, x, y, Z, xymap=None):
def __init__(self, x, y, Z, symbol=None, xymap=None):
"""
Initialize the link.
@ -585,6 +588,8 @@ class MapLink:
x (int): The xygrid x coordinate
y (int): The xygrid y coordinate.
X (int or str): The name/Z-coord of this map we are on.
symbol (str, optional): Set during parsing, allows to override
the default symbol with the one set in the legend.
xymap (XYMap, optional): The map object this sits on.
"""
@ -597,6 +602,9 @@ class MapLink:
self.Y = y / 2
self.Z = Z
if symbol is not None:
self.symbol = symbol
def __str__(self):
return f"<LinkNode '{self.symbol}' XY=({self.X:g},{self.Y:g})>"
@ -1117,7 +1125,7 @@ class InterruptMapNode(MapNode):
interrupt_path = True
prototype = "xyz_room"
class MapTransitionMapNode(TransitionMapNode):
class MapTransitionNode(TransitionMapNode):
"""Transition-target node to other map. This is not actually spawned in-game."""
symbol = "T"
display_symbol = " "
@ -1256,7 +1264,7 @@ class TeleporterMapLink(SmartTeleporterMapLink):
LEGEND = {
# nodes
"#": BasicMapNode,
"T": MapTransitionMapNode,
"T": MapTransitionNode,
"I": InterruptMapNode,
# links
"|": NSMapLink,

View file

@ -241,6 +241,9 @@ class XYZRoom(DefaultRoom):
map_area_client (bool): If True, map area will always fill the entire
client width. If False, the map area's width will vary with the
width of the currently displayed location description.
map_fill_all (bool): I the map area should fill the client width or not.
map_separator_char (str): The char to use to separate the map area from
the room description.
"""
@ -255,6 +258,7 @@ class XYZRoom(DefaultRoom):
map_align = 'c'
map_target_path_style = "|y{display_symbol}|n"
map_fill_all = True
map_separator_char = "|x~|n"
def __str__(self):
return repr(self)
@ -431,6 +435,9 @@ class XYZRoom(DefaultRoom):
xymap.options.get("map_target_path_style", self.map_target_path_style))
map_area_client = kwargs.get(
"map_fill_all", xymap.options.get("map_fill_all", self.map_fill_all))
map_separator_char = kwargs.get(
"map_separator_char",
xymap.options.get("map_separator_char", self.map_separator_char))
client_width, _ = looker.sessions.get()[0].get_client_size()
@ -465,8 +472,8 @@ class XYZRoom(DefaultRoom):
max_size=(display_width, None),
indent=map_indent
)
sep = "~" * sep_width
map_display = f"|x{sep}|n\n{map_display}\n|x{sep}"
sep = map_separator_char * sep_width
map_display = f"{sep}|n\n{map_display}\n{sep}"
# echo directly to make easier to separate in client
looker.msg(text=(map_display, {"type": "xymap"}), options=None)