# evennia.contrib.grid.xyzgrid.xymap

## XYMap

The XYMap class represents one XY-grid of interconnected map-legend components. It’s built from an ASCII representation, where unique characters represents each type of component. The Map parses the map into an internal graph that can be efficiently used for pathfinding the shortest route between any two nodes (rooms).

Each room (MapNode) can have exits (links) in 8 cardinal directions (north, northwest etc) as well as up and down. These are indicated in code as ‘n’, ‘ne’, ‘e’, ‘se’, ‘s’, ‘sw’, ‘w’, ‘nw’, ‘u’ and ‘d’.

    # in module passed to 'Map' class

    MAP = r'''
                           1
     + 0 1 2 3 4 5 6 7 8 9 0

    10 #   # # #     #-I-#
        \  i i i     d
     9   #-#-#-#     |
         |\    |     u
     8   #-#-#-#-----#b----o
         |     |           |
     7   #-#---#-#-#-#-#   |
         |         |x|x|   |
     6   o-#-#-#   #-#-#-#b#
            \      |x|x|
     5   o---#-#<--#-#-#
        /    |
     4 #-----+-# #---#
        \    | |  \ /
     3   #b#-#-#   x   #
             | |  / \ u
     2       #-#-#---#
             ^       d
     1       #-#     #
             |
     0 #-#---o

     + 0 1 2 3 4 5 6 7 8 9 1
                           0

    '''

    LEGEND = {'#': xyzgrid.MapNode, '|': xyzgrid.NSMapLink,...}

    # read by parser if XYMAP_DATA_LIST doesn't exist
    XYMAP_DATA = {
        "map": MAP,
        "legend": LEGEND,
        "zcoord": "City of Foo",
        "prototypes": {
            (0,1): { ... },
            (1,3): { ... },
            ...
        }

    }

    # will be parsed first, allows for multiple map-data dicts from one module
    XYMAP_DATA_LIST = [
        XYMAP_DATA
    ]

The two + signs in the upper/lower left corners are required and marks the edge of the map area. The origo of the grid is always two steps right and two up from the bottom test marker and the grid extends to two lines below the top-left marker. Anything outside the grid is ignored, so numbering the coordinate axes is optional but recommended for readability.

The XY positions represent coordinates positions in the game world. When existing, they are usually represented by Rooms in-game. The links between nodes would normally represent Exits, but the length of links on the map have no in-game equivalence except that traversing a multi-step link will place you in a location with an XY coordinate different from what you’d expect by a single step (most games don’t relay the XY position to the player anyway).

In the map string, every XY coordinate must have exactly one spare space/line between them - this is used for node linkings. This finer grid which has 2x resolution of the XYgrid is only used by the mapper and is referred to as the xygrid (small xy) internally. Note that an XY position can also be held by a link (for example a passthrough).

The nodes and links can be customized by add your own implementation of MapNode or MapLink to 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 ./example.py for a full grid example.

class `evennia.contrib.grid.xyzgrid.xymap.``XYMap`(map_module_or_dict, Z='map', xyzgrid=None)

  Bases: `object`

  This represents a single map of interconnected nodes/rooms, parsed from a ASCII map representation.

  Each room is connected to each other as a directed graph with optional ‘weights’ between the the connections. It is created from a map string with symbols describing the topological layout. It also provides pathfinding using the Dijkstra algorithm.

  The map-string is read from a string or from a module. The grid area of the string is marked by two + characters - one in the top left of the area and the other in the bottom left. The grid starts two spaces/lines in from the ‘open box’ created by these two markers and extend any width to the right. Any other markers or comments can be added outside of the grid - they will be ignored. Every grid coordinate must always be separated by exactly one space/line since the space between are used for links.

      '''
                             1 1 1
       + 0 1 2 3 4 5 6 7 8 9 0 1 2 ...

       4       #         #   #
               |          \ /
       3     #-#-#     #   #
             |          \ /
       2     #-#-#       #
             |x|x|       |
       1     #-#-#-#-#-#-#
            /
       0 #-#

       + 0 1 2 3 4 5 6 7 8 9 1 1 1 ...
                             0 1 2
      '''

  So origo (0,0) is in the bottom-left and north is +y movement, south is -y movement while east/west is +/- x movement as expected. Adding numbers to axes is optional but recommended for readability!

  `mapcorner_symbol` = '+'
  `max_pathfinding_length` = 500
  `empty_symbol` = ' '
  `legend_key_exceptions` = '\\'
  `__init__`(map_module_or_dict, Z='map', xyzgrid=None)

    Initialize the map parser by feeding it the map.

    Parameters

      - map_module_or_dict (str, module or dict) – Path or module pointing to a map. If a dict, this should be a dict with a MAP_DATA key ‘map’ and optionally a ‘legend’ dicts to specify the map structure.

      - Z (int or str, optional) – Name or Z-coord for for this map. Needed if the game uses more than one map. If not given, it can also be embedded in the map_module_or_dict. Used when referencing this map during map transitions, baking of pathfinding matrices etc.

      - xyzgrid (xyzgrid.XYZgrid) – A top-level grid this map is a part of.

    Notes

    Interally, the map deals with two sets of coordinate systems: - grid-coordinates x,y are the character positions in the map string. - world-coordinates X,Y are the in-world coordinates of nodes/rooms.

    > There are fewer of these since they ignore the ‘link’ spaces between the nodes in the grid, s

    > > X = x // 2 Y = y // 2

    - The Z-coordinate, if given, is only used when transitioning between maps on the supplied grid.

  `log`(msg)
  `reload`(map_module_or_dict=None)

    (Re)Load a map.

    Parameters

      - map_module_or_dict (str, module or dict, optional) – See description for the variable in the class’ __init__ function. If given, replace the already loaded map with a new one. If not given, the existing one given on class creation will be reloaded.

      - parse (bool, optional) – If set, auto-run .parse() on the newly loaded data.

    Notes

    This will both (re)load the data and parse it into a new map structure, replacing any existing one. The valid mapstructure is:

        {
            "map": <str>,
            "zcoord": <int or str>, # optional
            "legend": <dict>,       # optional
            "prototypes": <dict>    # optional
            "options": <dict>       # optional
        }

  `parse`()

    Parses the numerical grid from the string. The first pass means parsing out all nodes. The linking-together of nodes is not happening until the second pass (the reason for this is that maps can also link to other maps, so all maps need to have gone through their first parsing-passes before they can be linked together).

    See the class docstring for details of how the grid should be defined.

    Notes

    In this parsing, the ‘xygrid’ is the full range of chraracters read from the string. The XYgrid is used to denote the game-world coordinates (which doesn’t include the links)

  `calculate_path_matrix`(force=False)

    Solve the pathfinding problem using Dijkstra’s algorithm. This will try to load the solution from disk if possible.

    Parameters

      force (bool, optional) – If the cache should always be rebuilt.

  `spawn_nodes`(xy='*', '*')

    Convert the nodes of this XYMap into actual in-world rooms by spawning their related prototypes in the correct coordinate positions. This must be done first before spawning links (with spawn_links because exits require the target destination to exist. It’s also possible to only spawn a subset of the map

    Parameters

      xy (tuple, optional) – An (X,Y) coordinate of node(s). ‘*’ acts as a wildcard.

    Examples

    - **xy=(1, 3) - spawn (1,3) coordinate only.

    - **xy=(‘*’, 1) - spawn all nodes in the first row of the map only.

    - xy=(‘*’, ‘*’) - spawn all nodes

    Returns

      list – A list of nodes that were spawned.

  `spawn_links`(xy='*', '*', nodes=None, directions=None)

    Convert links of this XYMap into actual in-game exits by spawning their related prototypes. It’s possible to only spawn a specic exit by specifying the node and

    Parameters

      - xy (tuple, optional) – An (X,Y) coordinate of node(s). ‘*’ acts as a wildcard.

      - nodes (list, optional) – If given, only consider links out of these nodes. This also affects xy, so that if there are no nodes of given coords in nodes, no links will be spawned at all.

      - directions (list, optional) – A list of cardinal directions (‘n’, ‘ne’ etc). If given, sync only the exit in the given directions (xy limits which links out of which nodes should be considered). If unset, there are no limits to directions.

    Examples

    - xy=(1, 3 ), direction=’ne’ - sync only the north-eastern exit

        out of the (1, 3) node.

  `get_node_from_coord`(xy)

    Get a MapNode from a coordinate.

    Parameters

      xy (tuple) – X,Y coordinate on XYgrid.

    Returns

      MapNode –

      The node found at the given coordinates. Returns

        None if there is no mapnode at the given coordinate.

    Raises

      MapError – If trying to specify an iX,iY outside of the grid’s maximum bounds.

  `get_components_with_symbol`(symbol)

    Find all map components (nodes, links) with a given symbol in this map.

    Parameters

      symbol (char) – A single character-symbol to search for.

    Returns

      list – A list of MapNodes and/or MapLinks found with the matching symbol.

  `get_shortest_path`(start_xy, end_xy)

    Get the shortest route between two points on the grid.

    Parameters

      - start_xy (tuple) – A starting (X,Y) coordinate on the XYgrid (in-game coordinate) for where we start from.

      - end_xy (tuple or MapNode) – The end (X,Y) coordinate on the XYgrid (in-game coordinate) we want to find the shortest route to.

    Returns

      tuple – Two lists, first containing the list of directions as strings (n, ne etc) and the second is a mixed list of MapNodes and all MapLinks in a sequence describing the full path including the start- and end-node.

  `get_visual_range`(xy, dist=2, mode='nodes', character='@', target=None, target_path_style='|y{display_symbol}|n', max_size=None, indent=0, return_str=True)

    Get a part of the grid centered on a specific point and extended a certain number of nodes or grid points in every direction.

    Parameters

      - xy (tuple) – (X,Y) in-world coordinate location. If this is not the location of a node on the grid, the character or the empty-space symbol (by default an empty space) will be shown.

      - dist (int, optional) – Number of gridpoints distance to show. Which grid to use depends on the setting of only_nodes. Set to None to always show the entire grid.

      - mode (str, optional) – One of ‘scan’ or ‘nodes’. In ‘scan’ mode, dist measure number of xy grid points in all directions and doesn’t care about if visible nodes are reachable or not. If ‘nodes’, distance measure how many linked nodes away from the center coordinate to display.

      - character (str, optional) – Place this symbol at the xy position of the displayed map. The center node’s symbol is shown if this is falsy.

      - target (tuple, optional) – A target XY coordinate to go to. The path to this (or the beginning of said path, if outside of visual range) will be marked according to target_path_style.

      - target_path_style (str or callable, optional) – This is use for marking the path found when target is given. If a string, it accepts a formatting marker display_symbol which will be filled with the display_symbol of each node/link the path passes through. This allows e.g. to color the path. If a callable, this will receive the MapNode or MapLink object for every step of the path and and must return the suitable string to display at the position of the node/link.

      - max_size (tuple, optional) – A max (width, height) to crop the displayed return to. Make both odd numbers to get a perfect center. Set either of the tuple values to None to make that coordinate unlimited. Set entire tuple to None let display-size able to grow up to full size of grid.

      - indent (int, optional) – How far to the right to indent the map area (only applies to return_str=True).

      - return_str (bool, optional) – Return result as an already formatted string or a 2D list.

    Returns

      str or list –

      Depending on value of return_str. If a list,

        this is 2D grid of lines, [[str,str,str,…], […]] where each element is a single character in the display grid. To extract a character at (ix,iy) coordinate from it, use indexing outlist[iy][ix] in that order.

    Notes

    If outputting a list, the y-axis must first be reversed before printing since printing happens top-bottom and the y coordinate system goes bottom-up. This can be done simply with this before building the final string to send/print.

    > printable_order_list = outlist[::-1]

    If mode=’nodes’, a dist of 2 will give the following result in a row of nodes:

    > #-#-@———-#-#

    This display may thus visually grow much bigger than expected (both horizontally and vertically). consider setting max_size if wanting to restrict the display size. Also note that link ‘weights’ are included in this estimate, so if links have weights > 1, fewer nodes may be found for a given dist.

    If mode=**scan**, a dist of 2 on the above example would instead give

    > #-@–

    This mode simply shows a cut-out subsection of the map you are on. The dist is measured on xygrid, so two steps per XY coordinate. It does not consider links or weights and may also show nodes not actually reachable at the moment:

    > |

    > # @-#