evennia.contrib.grid.xyzgrid.xymap_legend

Map legend components

Each map-legend component is either a ‘mapnode’ - something that represents and actual in-game location (usually a room) or a ‘maplink’ - something connecting nodes together. The start of a link usually shows as an Exit, but the length of the link has no in-game equivalent.


class evennia.contrib.grid.xyzgrid.xymap_legend.MapNode(x, y, Z, node_index=0, symbol=None, xymap=None)[source]

Bases: object

This represents a ‘room’ node on the map. Note that the map system deals with two grids, the finer xygrid, which is the per-character grid on the map, and the XYgrid which contains only the even-integer coordinates and also represents in-game coordinates/rooms. MapNodes are always located on even X,Y coordinates on the map grid and in-game.

MapNodes will also handle the syncing of themselves and all outgoing links to the grid.

Attributes on the node class:

  • symbol (str) - The character to parse from the map into this node. By default this is ‘#’ and must be a single character, with the exception of **

  • 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 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 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 for various reasons, mostly map-transitions).

display_symbol = None
interrupt_path = False
prototype = None
direction_spawn_defaults = {'d': ('down', 'd', 'do'), 'e': ('east', 'e'), 'n': ('north', 'n'), 'ne': ('northeast', 'ne', 'north-east'), 'nw': ('northwest', 'nw', 'north-west'), 's': ('south', 's'), 'se': ('southeast', 'se', 'south-east'), 'sw': ('southwest', 'sw', 'south-west'), 'u': ('up', 'u'), 'w': ('west', 'w')}
__init__(x, y, Z, node_index=0, symbol=None, xymap=None)[source]

Initialize the mapnode.

Parameters
  • x (int) – Coordinate on xygrid.

  • y (int) – Coordinate on xygrid.

  • Z (int or str) – Name/Z-pos of this map.

  • 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.

node_index = None
symbol = '#'
log(msg)[source]

log messages using the xygrid parent

generate_prototype_key()[source]

Generate a deterministic prototype key to allow for users to apply prototypes without needing a separate new name for every one.

This is called by the map parser when this node is encountered. It tells the node to scan in all directions and follow any found links to other nodes. Since there could be multiple steps to reach another node, the system will iterate down each path and store it once and for all.

Notes

This sets up all data needed for later use of this node in pathfinding and other operations. The method can’t run immediately when the node is created since a complete parsed xygrid is required.

linkweights(nnodes)[source]

Retrieve all the weights for the direct links to all other nodes. This is used for the efficient generation of shortest-paths.

Parameters

nnodes (int) – The total number of nodes

Returns

scipy.array

Array of weights of the direct links to other nodes.

The weight will be 0 for nodes not directly connected to one another.

Notes

A node can at most have 8 connections (the cardinal directions).

get_display_symbol()[source]

Hook to override for customizing how the display_symbol is determined.

Returns

str – The display-symbol to use. This must visually be a single character but could have color markers, use a unicode font etc.

Notes

By default, just setting .display_symbol is enough.

get_spawn_xyz()[source]

This should return the XYZ-coordinates for spawning this node. This normally the XYZ of the current map, but for traversal-nodes, it can also be the location on another map.

Returns

tuple – The (X, Y, Z) coords to spawn this node at.

get_exit_spawn_name(direction, return_aliases=True)[source]

Retrieve the spawn name for the exit being created by this link.

Parameters
  • direction (str) – The cardinal direction (n,ne etc) the want the exit name/aliases for.

  • return_aliases (bool, optional) – Also return all aliases.

Returns

str or tuple – The key of the spawned exit, or a tuple (key, alias, alias, …)

spawn()[source]

Build an actual in-game room from this node.

This should be called as part of the node-sync step of the map sync. The reason is that the exits (next step) requires all nodes to exist before they can link up to their destinations.

Build actual in-game exits based on the links out of this room.

Parameters

directions (list, optional) – If given, this should be a list of supported directions (n, ne, etc). Only links in these directions will be spawned for this node.

This should be called after all sync_node_to_grid operations have finished across the entire XYZgrid. This creates/syncs all exits to their locations and destinations.

unspawn()[source]

Remove all spawned objects related to this node and all links.

class evennia.contrib.grid.xyzgrid.xymap_legend.TransitionMapNode(x, y, Z, node_index=0, symbol=None, xymap=None)[source]

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.MapNode

This node acts as an end-node for a link that actually leads to a specific node on another map. It is not actually represented by a separate room in-game.

This teleportation is not understood by the pathfinder, so why it will be possible to pathfind to this node, it really represents a map transition. Only a single link must ever be connected to this node.

Properties: - target_map_xyz (tuple) - the (X, Y, Z) coordinate of a node on the other map to teleport

to when moving to this node. This should not be another TransitionMapNode (see below for how to make a two-way link).

Examples:

map1   map2

#-T    #-    - one-way transition from map1 -> map2.
#-T    T-#   - two-way. Both TransitionMapNodes links to the coords of the
               actual rooms (**#**) on the other map (NOT to the **T**s)!
symbol = 'T'
display_symbol = ' '
taget_map_xyz = (None, None, None)
get_spawn_xyz()[source]

Make sure to return the coord of the target - this will be used when building the exit to this node (since the prototype is None, this node itself will not be built).

Check so we don’t have too many links

Bases: object

This represents one or more links between an ‘incoming direction’ and an ‘outgoing direction’. It’s like a railway track between MapNodes. A Link can be placed on any location in the grid, but even when on an integer XY position they still don’t represent an actual in-game place but just a link between such places (the Nodes).

> Note that, if you want to create a child link-class that spawns onto the grid (usually an exit), you must set its .prototype. This parent class will not on its own spawn anything.

Each link has a ‘weight’ >=1, this indicates how ‘slow’ it is to traverse that link. This is used by the Dijkstra algorithm to find the ‘fastest’ route to a point. By default this weight is 1 for every link, but a locked door, terrain etc could increase this and have the shortest-path algorithm prefer to use another route.

Attributes on the link class:

  • symbol (str) - The character to parse from the map into this node. This must be a single character, with the exception of ****.

  • 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 can be used.

  • 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 as {‘sw’: ‘e’} and read ‘connects from southwest to east’. Note that if you want the link to go both ways, also the inverse (east to southwest) must also be added.

  • weights (dict) This maps a link’s start direction to a weight. So for the {‘sw’: ‘e’} link, a weight would be given as {‘sw’: 2}. If not given, a link will use the default_weight.

  • average_long_link_weights (bool): This applies to the first link out of a node only. When tracing links to another node, multiple links could be involved, each with a weight. So for a link chain with default weights, #—# would give a total weight of 3. With this setting, the weight will be 3 / 3 = 1. That is, for evenly weighted links, the length of the link doesn’t matter.

  • direction_aliases (dict): When displaying a direction during pathfinding, one may want to display a different ‘direction’ than the cardinal on-map one. For example ‘up’ may be visualized on the map as a ‘n’ movement, but the found path over this link should show as ‘u’. In that case, the alias would be {‘n’: ‘u’}.

  • multilink (bool): If set, this link accepts links from all directions. It will usually use a custom get_direction to determine what these are based on surrounding topology. This setting is necessary to avoid infinite loops when such multilinks are next to each other.

  • interrupt_path (bool): If set, a shortest-path solution will include this link as normal, but will stop short of actually moving past this link.

  • prototype (dict) - The default prototype dict to use for reproducing this map component on the game grid. This is only relevant for the first link out of a Node (the continuation of the link is only used to determine its destination). This can be overridden on a per-direction basis.

  • spawn_aliases (dict): A mapping {direction: (key, alias, alias, …) to use when spawning actual exits from this link. If not given, a sane set of defaults (n=(north, n) etc) will be used. This is required if you use any custom directions outside of the cardinal directions + up/down. The exit’s key (useful for auto-walk) is usually retrieved by calling node.get_exit_spawn_name(direction)

display_symbol = None
default_weight = 1
directions = {}
direction_aliases = {}
weights = {}
interrupt_path = False
prototype = None
spawn_aliases = {}
__init__(x, y, Z, symbol=None, xymap=None)[source]

Initialize the link.

Parameters
  • 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.

symbol = ''
generate_prototype_key(*args)[source]

Generate a deterministic prototype key to allow for users to apply prototypes without needing a separate new name for every one.

Parameters

*args (str) – These are used to further seed the key.

traverse(start_direction, _weight=0, _linklen=1, _steps=None)[source]

Recursively traverse the links out of this LinkNode.

Parameters

start_direction (str) – The direction (n, ne etc) from which this traversal originates for this link.

Kwargs:

_weight (int): Internal use. _linklen (int): Internal use. _steps (list): Internal use.

Returns

tuple

The (node, weight, links) result of the traversal, where links

is a list of directions (n, ne etc) that describes how to to get to the node on the grid. This includes the first direction.

Raises

MapParserError – If a link lead to nowhere.

get_linked_neighbors(directions=None)[source]

A helper to get all directions to which there appears to be a visual link/node. This does not trace the length of the link and check weights etc.

Parameters

directions (list, optional) – Only scan in these directions.

Returns

dict – Mapping {direction: node_or_link} wherever such was found.

at_empty_target(start_direction, end_direction)[source]

This is called by .traverse when it finds this link pointing to nowhere.

Parameters
  • start_direction (str) – The direction (n, ne etc) from which this traversal originates for this link.

  • end_direction (str) – The direction found from get_direction earlier.

Returns

MapNode, MapLink or None – The next target to go to from here. None if this is an error that should be reported.

Notes

This is usually a mapping error (returning None) but may have practical use, such as teleporting or transitioning to another map.

get_direction(start_direction, **kwargs)[source]

Hook to override for customizing how the directions are determined.

Parameters

start_direction (str) – The starting direction (n, ne etc).

Returns

str

The ‘out’ direction side of the link - where the link

leads to.

Example

With the default legend, if the link is a straght vertical link (|) and start_direction is s (link is approached from from the south side), then this function will return **n’.

get_weight(start_direction, current_weight, **kwargs)[source]

Hook to override for customizing how the weights are determined.

Parameters
  • start_direction (str) – The starting direction (n, ne etc).

  • current_weight (int) – This can have an existing value if we are progressing down a multi-step path.

Returns

int – The weight to use for a link from start_direction.

get_display_symbol()[source]

Hook to override for customizing how the display_symbol is determined. This is called after all other hooks, at map visualization.

Returns

str – The display-symbol to use. This must visually be a single character but could have color markers, use a unicode font etc.

Notes

By default, just setting .display_symbol is enough.

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.MapLink

A ‘smart’ link without visible direction, but which uses its topological surroundings to figure out how it connects. All such links are two-way. It can be used to create ‘knees’ and multi-crossings of links. Remember that this is still a link, so user will not ‘stop’ at it, even if placed on an XY position!

If there are links on cardinally opposite sites, these are considered pass-throughs, and If determining the path of a set of input/output directions this is not possible, or there is an uneven number of links, an MapParserError is raised.

Example with the RedirectLink:

  /
-o    - this is ok, there can only be one path, e-ne

 |
-o-   - equivalent to '+', one n-s and one w-e link crossing
 |

\|/
-o-   - all are passing straight through
/|\

-o-   - w-e pass straight through, other link is sw-s
/|

-o    - invalid; impossible to know which input goes to which output
/|
get_direction(start_direction)[source]

Dynamically determine the direction based on a source direction and grid topology.

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.MapLink

The teleport link works by connecting to nowhere - and will then continue on another teleport link with the same symbol elsewhere on the map. The teleport symbol must connect to only one other link (not to a node).

For this to work, there must be exactly one other teleport with the same .symbol on the map. The two teleports will always operate as two-way connections, but by making the ‘out-link’ on one side one-way, the effect will be that of a one-way teleport.

Example:

   t #
  /  |   -  moving ne from the left node will bring the user to the rightmost node
-#   t      as if the two teleporters were connected (two way).

-#-t t># - one-way teleport from left to right.

-#t       - invalid, may only connect to another link

-#-t-#    - invalid, only one connected link is allowed.
symbol = 't'
display_symbol = ' '
direction_name = 'teleport'
__init__(*args, **kwargs)[source]

Initialize the link.

Parameters
  • 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.

at_empty_target(start_direction, end_direction)[source]

Called during traversal, when finding an unknown direction out of the link (same as targeting a link at an empty spot on the grid). This will also search for a unique, matching teleport to send to.

Parameters

start_direction (str) – The direction (n, ne etc) from which this traversal originates for this link.

Returns

TeleporterMapLink – The paired teleporter.

Raises

MapParserError – We raise this explicitly rather than returning None if we don’t find another teleport. This avoids us getting the default (and in this case confusing) ‘pointing to an empty space’ error we’d get if returning None.

get_direction(start_direction)[source]

Figure out the connected link and paired teleport.

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.MapLink

A ‘smart’ link withot visible direction, but which uses its topological surroundings to figure out how it connects. Unlike the SmartRerouterMapLink, this link type is also a ‘direction’ of its own and can thus connect directly to nodes. It can only describe one transition and will prefer connecting two nodes if there are other possibilities. If the linking is unclear or there are more than two nodes directly neighboring, a MapParserError will be raised. If two nodes are not found, it will link to any combination of links- or nodes as long as it can un-ambiguously determine which direction they lead.

Placing a smart-link directly between two nodes/links will always be a two-way connection, whereas if it connects a node with another link, it will be a one-way connection in the direction of the link.

Example with the up-down directions:

#
u     - moving up in BOTH directions will bring you to the other node (two-way)
#

#
d     - this better represents the 'real' up/down behavior.
u
#

#
|     - one-way up from the lower node to the upper
u
#

#-#
u     - okay since the up-link prioritizes the nodes
#

#u#
u    - invalid since top-left node has two 'up' directions to go to
#

#     |
u# or u-   - invalid.
#     |
get_direction(start_direction)[source]

Figure out the direction from a specific source direction based on grid topology.

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.SmartMapLink

This is a smart maplink that does not show as such on the map - instead it will figure out how it should look had it been one of the ‘normal’ cardinal-direction links and display itself as that instead. This doesn’t change its functionality, only the symbol shown on the map display. This only works for cardinal-direction links.

It makes use of display_symbol_aliases mapping, which maps a sorted set of ((start, end), (end, start)) (two-way) or ((start, end),) (one-way) directions to a symbol in the current map legend - this is the symbol alias to use. The matching MapLink or MapNode will be initialized at the current position only for the purpose of getting its display_symbol.

Example

display_symbol_aliases = {((‘n’, ‘s’), (‘s’, n’)): ‘|’, …}

If no display_symbol_aliases are given, the regular display_symbol is used.

direction_aliases = {'e': 'e', 'n': 'n', 'ne': 'ne', 'nw': 'nw', 's': 's', 'se': 'se', 'sw': 'sw', 'w': 'w'}
display_symbol_aliases = {(('n', 's'), ('s', 'n')): '|', (('n', 's'),): 'v', ('s', 'n'): '^', (('e', 'w'), ('w', 'e')): '-', (('e', 'w'),): '>', (('w', 'e'),): '<', (('nw', 'se'), ('sw', 'ne')): '\\', (('ne', 'sw'), ('sw', 'ne')): '/'}
get_display_symbol()[source]

The SmartMapLink already calculated the directions before this, so we just need to figure out what to replace this with in order to make this ‘invisible’

Depending on how we are connected, we figure out how the ‘normal’ link should look and use that instead.

class evennia.contrib.grid.xyzgrid.xymap_legend.BasicMapNode(x, y, Z, node_index=0, symbol=None, xymap=None)[source]

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.MapNode

A map node/room

symbol = '#'
prototype = 'xyz_room'
class evennia.contrib.grid.xyzgrid.xymap_legend.InterruptMapNode(x, y, Z, node_index=0, symbol=None, xymap=None)[source]

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.MapNode

A point of interest node/room. Pathfinder will ignore but auto-stepper will stop here if passing through. Starting from here is fine.

symbol = 'I'
display_symbol = '#'
interrupt_path = True
prototype = 'xyz_room'
class evennia.contrib.grid.xyzgrid.xymap_legend.MapTransitionNode(x, y, Z, node_index=0, symbol=None, xymap=None)[source]

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.TransitionMapNode

Transition-target node to other map. This is not actually spawned in-game.

symbol = 'T'
display_symbol = ' '
prototype = None
target_map_xyz = (None, None, None)

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.MapLink

Two-way, North-South link

symbol = '|'
display_symbol = '||'
directions = {'n': 's', 's': 'n'}
prototype = 'xyz_exit'

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.MapLink

Two-way, East-West link

symbol = '-'
directions = {'e': 'w', 'w': 'e'}
prototype = 'xyz_exit'

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.MapLink

Two-way, NorthWest-SouthWest link

symbol = '/'
directions = {'ne': 'sw', 'sw': 'ne'}
prototype = 'xyz_exit'

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.MapLink

Two-way, SouthEast-NorthWest link

symbol = '\\'
directions = {'nw': 'se', 'se': 'nw'}
prototype = 'xyz_exit'

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.MapLink

Two-way, crossing North-South and East-West links

symbol = '+'
directions = {'e': 'w', 'n': 's', 's': 'n', 'w': 'e'}
prototype = 'xyz_exit'

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.MapLink

Two-way, crossing NorthEast-SouthWest and SouthEast-NorthWest links

symbol = 'x'
directions = {'ne': 'sw', 'nw': 'se', 'se': 'nw', 'sw': 'ne'}
prototype = 'xyz_exit'

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.MapLink

One-way North-South link

symbol = 'v'
directions = {'n': 's'}
prototype = 'xyz_exit'

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.MapLink

One-way South-North link

symbol = '^'
directions = {'s': 'n'}
prototype = 'xyz_exit'

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.MapLink

One-way East-West link

symbol = '<'
directions = {'e': 'w'}
prototype = 'xyz_exit'

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.MapLink

One-way West-East link

symbol = '>'
directions = {'w': 'e'}
prototype = 'xyz_exit'

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.SmartMapLink

Up direction. Note that this stays on the same z-coord so it’s a ‘fake’ up.

symbol = 'u'
direction_aliases = {'e': 'u', 'n': 'u', 'ne': 'u', 'nw': 'u', 's': 'u', 'se': 'u', 'sw': 'u', 'w': 'u'}
spawn_aliases = {'e': ('up', 'u'), 'n': ('up', 'u'), 'ne': ('up', 'u'), 'nw': ('up', 'u'), 's': ('up', 'u'), 'se': ('up', 'u'), 'sw': ('up', 'u'), 'w': ('up', 'u')}
prototype = 'xyz_exit'

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.UpMapLink

Down direction. Note that this stays on the same z-coord, so it’s a ‘fake’ down.

symbol = 'd'
direction_aliases = {'e': 'd', 'n': 'd', 'ne': 'd', 'nw': 'd', 's': 'd', 'se': 'd', 'sw': 'd', 'w': 'd'}
spawn_aliases = {'e': ('down', 'd'), 'n': ('down', 'd'), 'ne': ('down', 'd'), 'nw': ('down', 'd'), 's': ('down', 'd'), 'se': ('down', 'd'), 'sw': ('down', 'd'), 'w': ('down', 'd')}
prototype = 'xyz_exit'

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.InvisibleSmartMapLink

A (still passable) link. Pathfinder will treat this as any link, but auto-stepper will always abort before crossing this link - so this must be crossed manually.

symbol = 'i'
interrupt_path = True
prototype = 'xyz_exit'

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.InvisibleSmartMapLink

Causes the shortest-path algorithm to consider this a blocked path. The block will not show up in the map display (and exit can be traversed normally), pathfinder will just not include this link in any paths.

symbol = 'b'
weights = {'e': 999999999999, 'n': 999999999999, 'ne': 999999999999, 'nw': 999999999999, 's': 999999999999, 'se': 999999999999, 'sw': 999999999999, 'w': 999999999999}
prototype = 'xyz_exit'

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.SmartRerouterMapLink

A link that connects other links to build ‘knees’, pass-throughs etc.

symbol = 'o'

Bases: evennia.contrib.grid.xyzgrid.xymap_legend.SmartTeleporterMapLink

Teleporter links. Must appear in pairs on the same xy map. To make it one-way, add additional one-way link out of the teleporter on one side.

symbol = 't'