mirror of
https://github.com/evennia/evennia.git
synced 2026-03-22 07:46:30 +01:00
First working function of map-spawning
This commit is contained in:
parent
5ba16cf63f
commit
cc820beb98
5 changed files with 327 additions and 208 deletions
|
|
@ -73,7 +73,7 @@ class MapNode:
|
|||
direction_spawn_defaults = {
|
||||
'n': ('north', 'n'),
|
||||
'ne': ('northeast', 'ne', 'north-east'),
|
||||
'e': ('east',),
|
||||
'e': ('east', 'e'),
|
||||
'se': ('southeast', 'se', 'south-east'),
|
||||
's': ('south', 's'),
|
||||
'sw': ('southwest', 'sw', 'south-west'),
|
||||
|
|
@ -231,7 +231,7 @@ class MapNode:
|
|||
"""
|
||||
return self.symbol if self.display_symbol is None else self.display_symbol
|
||||
|
||||
def get_spawn_coords(self):
|
||||
def get_spawn_xyz(self):
|
||||
"""
|
||||
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
|
||||
|
|
@ -260,15 +260,15 @@ class MapNode:
|
|||
# a 'virtual' node.
|
||||
return
|
||||
|
||||
coord = self.get_spawn_coords()
|
||||
xyz = self.get_spawn_xyz()
|
||||
|
||||
try:
|
||||
nodeobj = NodeTypeclass.objects.get_xyz(coord=coord)
|
||||
nodeobj = NodeTypeclass.objects.get_xyz(xyz=xyz)
|
||||
except NodeTypeclass.DoesNotExist:
|
||||
# create a new entity with proper coordinates etc
|
||||
nodeobj, err = NodeTypeclass.create(
|
||||
self.prototype.get('key', 'An Empty room'),
|
||||
coord=coord
|
||||
xyz=xyz
|
||||
)
|
||||
if err:
|
||||
raise RuntimeError(err)
|
||||
|
|
@ -277,12 +277,12 @@ class MapNode:
|
|||
spawner.batch_update_objects_with_prototype(
|
||||
self.prototype, objects=[nodeobj], exact=False)
|
||||
|
||||
def spawn_links(self, only_directions=None):
|
||||
def spawn_links(self, directions=None):
|
||||
"""
|
||||
Build actual in-game exits based on the links out of this room.
|
||||
|
||||
Args:
|
||||
only_directions (list, optional): If given, this should be a list of supported
|
||||
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.
|
||||
|
||||
|
|
@ -290,7 +290,12 @@ class MapNode:
|
|||
the entire XYZgrid. This creates/syncs all exits to their locations and destinations.
|
||||
|
||||
"""
|
||||
coord = (self.X, self.Y, self.Z)
|
||||
if not self.prototype:
|
||||
# no exits to spawn out of a 'virtual' node.
|
||||
return
|
||||
|
||||
xyz = (self.X, self.Y, self.Z)
|
||||
direction_limits = directions
|
||||
|
||||
global ExitTypeclass
|
||||
if not ExitTypeclass:
|
||||
|
|
@ -308,7 +313,7 @@ class MapNode:
|
|||
# we need to search for exits in all directions since some
|
||||
# may have been removed since last sync
|
||||
linkobjs = {exi.db_key.lower(): exi
|
||||
for exi in ExitTypeclass.objects.filter_xyz(coord=coord)}
|
||||
for exi in ExitTypeclass.objects.filter_xyz(xyz=xyz)}
|
||||
|
||||
# figure out if the topology changed between grid and map (will always
|
||||
# build all exits first run)
|
||||
|
|
@ -321,20 +326,25 @@ class MapNode:
|
|||
else:
|
||||
# missing in linkobjs - create a new exit
|
||||
key, aliases, direction, link = maplinks[differing_key]
|
||||
exitnode = self.links[direction]
|
||||
|
||||
linkobjs[direction] = ExitTypeclass.create(
|
||||
# either get name from the prototype or use our custom set
|
||||
if direction_limits and direction not in direction_limits:
|
||||
continue
|
||||
|
||||
exitnode = self.links[direction]
|
||||
exi, err = ExitTypeclass.create(
|
||||
key,
|
||||
coord=coord,
|
||||
destination_coord=exitnode.get_spawn_coords(),
|
||||
xyz=xyz,
|
||||
xyz_destination=exitnode.get_spawn_xyz(),
|
||||
aliases=aliases,
|
||||
)
|
||||
if err:
|
||||
raise RuntimeError(err)
|
||||
linkobjs[key.lower()] = exi
|
||||
|
||||
# apply prototypes to catch any changes
|
||||
for direction, linkobj in linkobjs:
|
||||
for key, linkobj in linkobjs.items():
|
||||
spawner.batch_update_objects_with_prototype(
|
||||
maplinks[direction].prototype, objects=[linkobj], exact=False)
|
||||
maplinks[key.lower()][3].prototype, objects=[linkobj], exact=False)
|
||||
|
||||
def unspawn(self):
|
||||
"""
|
||||
|
|
@ -345,8 +355,10 @@ class MapNode:
|
|||
if not NodeTypeclass:
|
||||
from .room import XYZRoom as NodeTypeclass
|
||||
|
||||
xyz = (self.X, self.Y, self.Z)
|
||||
|
||||
try:
|
||||
nodeobj = NodeTypeclass.objects.get_xyz(coord=coord)
|
||||
nodeobj = NodeTypeclass.objects.get_xyz(xyz=xyz)
|
||||
except NodeTypeclass.DoesNotExist:
|
||||
# no object exists
|
||||
pass
|
||||
|
|
@ -364,7 +376,7 @@ class TransitionMapNode(MapNode):
|
|||
to this node.
|
||||
|
||||
Properties:
|
||||
- `target_map_coord` (tuple) - the (X, Y, Z) coordinate of a node on the other map to teleport
|
||||
- `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).
|
||||
|
||||
|
|
@ -380,16 +392,21 @@ class TransitionMapNode(MapNode):
|
|||
"""
|
||||
symbol = 'T'
|
||||
display_symbol = ' '
|
||||
# X,Y,Z coordinates of target node (not a transitionalmapnode)
|
||||
taget_map_coord = (None, None, None)
|
||||
# X,Y,Z coordinates of target node
|
||||
taget_map_xyz = (None, None, None)
|
||||
|
||||
def get_spawn_coords(self):
|
||||
def get_spawn_xyz(self):
|
||||
"""
|
||||
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).
|
||||
|
||||
"""
|
||||
return self.target_map_coord
|
||||
if any(True for coord in self.target_map_xyz if coord in (None, 'unset')):
|
||||
raise MapParserError(f"(Z={self.xymap.Z}) has not defined its "
|
||||
"`.target_map_xyz` property. It must point "
|
||||
"to another valid xymap (Z coordinate).", self)
|
||||
|
||||
return self.target_map_xyz
|
||||
|
||||
def build_links(self):
|
||||
"""Check so we don't have too many links"""
|
||||
|
|
@ -449,7 +466,7 @@ class MapLink:
|
|||
of the link is only used to determine its destination). This can be overridden on a
|
||||
per-direction basis.
|
||||
- `spawn_aliases` (list): A list of [key, alias, alias, ...] for the node to use when spawning
|
||||
exits from this link. If not given, a sane set of defaults (n=north etc) will be used. This
|
||||
exits from this link. If not given, a sane set of defaults ((north, n) etc) will be used. This
|
||||
is required if you use any custom directions outside of the cardinal directions + up/down.
|
||||
|
||||
"""
|
||||
|
|
@ -1017,8 +1034,8 @@ class MapTransitionMapNode(TransitionMapNode):
|
|||
"""Transition-target to other map"""
|
||||
symbol = "T"
|
||||
display_symbol = " "
|
||||
target_map_coords = (0, 0, 'unset') # must be changed
|
||||
prototype = None # important!
|
||||
prototype = None # important to leave None!
|
||||
target_map_xyz = (None, None, None) # must be set manually
|
||||
|
||||
|
||||
class InterruptMapNode(MapNode):
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from random import randint
|
|||
from unittest import TestCase
|
||||
from parameterized import parameterized
|
||||
from django.test import override_settings
|
||||
from . import xymap, xyzgrid, map_legend
|
||||
from . import xymap, xyzgrid, map_legend, xyzroom
|
||||
|
||||
|
||||
MAP1 = """
|
||||
|
|
@ -341,14 +341,36 @@ MAP12b = r"""
|
|||
"""
|
||||
|
||||
|
||||
class TestMap1(TestCase):
|
||||
class _MapTest(TestCase):
|
||||
"""
|
||||
Parent for map tests
|
||||
|
||||
"""
|
||||
map_data = {
|
||||
'map': MAP1,
|
||||
'zcoord': "map1",
|
||||
|
||||
}
|
||||
map_display = MAP1_DISPLAY
|
||||
|
||||
def setUp(self):
|
||||
"""Set up grid and map"""
|
||||
self.grid, err = xyzgrid.XYZGrid.create("testgrid")
|
||||
self.grid.add_maps(self.map_data)
|
||||
self.map = self.grid.get(self.map_data['zcoord'])
|
||||
|
||||
def tearDown(self):
|
||||
self.grid.delete()
|
||||
|
||||
|
||||
class TestMap1(_MapTest):
|
||||
"""
|
||||
Test the Map class with a simple 4-node map
|
||||
|
||||
"""
|
||||
def setUp(self):
|
||||
self.map = xymap.XYMap({"map": MAP1}, Z="testmap")
|
||||
self.map.parse()
|
||||
# def setUp(self):
|
||||
# self.map = xymap.XYMap({"map": MAP1}, Z="testmap")
|
||||
# self.map.parse()
|
||||
|
||||
def test_str_output(self):
|
||||
"""Check the display_map"""
|
||||
|
|
@ -1057,17 +1079,23 @@ class TestXYZGrid(TestCase):
|
|||
def test_spawn(self):
|
||||
"""Spawn objects for the grid"""
|
||||
self.grid.spawn()
|
||||
# import sys
|
||||
# sys.stderr.write("\nrooms: " + repr(xyzroom.XYZRoom.objects.all()))
|
||||
# sys.stderr.write("\n\nexits: " + repr(xyzroom.XYZExit.objects.all()) + "\n")
|
||||
|
||||
self.assertEqual(xyzroom.XYZRoom.objects.all().count(), 4)
|
||||
self.assertEqual(xyzroom.XYZExit.objects.all().count(), 8)
|
||||
|
||||
|
||||
# map transitions
|
||||
class Map12aTransition(map_legend.MapTransitionMapNode):
|
||||
symbol = "T"
|
||||
target_map_coords = (1, 0, "map12b")
|
||||
target_map_xyz = (1, 0, "map12b")
|
||||
|
||||
|
||||
class Map12bTransition(map_legend.MapTransitionMapNode):
|
||||
symbol = "T"
|
||||
target_map_coords = (0, 1, "map12a")
|
||||
target_map_xyz = (0, 1, "map12a")
|
||||
|
||||
|
||||
class TestXYZGridTransition(TestCase):
|
||||
|
|
@ -1112,4 +1140,17 @@ class TestXYZGridTransition(TestCase):
|
|||
Spawn the two maps into actual objects.
|
||||
|
||||
"""
|
||||
# from evennia import set_trace;set_trace()
|
||||
self.grid.spawn()
|
||||
|
||||
self.assertEqual(xyzroom.XYZRoom.objects.all().count(), 6)
|
||||
self.assertEqual(xyzroom.XYZExit.objects.all().count(), 10)
|
||||
|
||||
room1 = xyzroom.XYZRoom.objects.get_xyz(xyz=(0, 1, 'map12a'))
|
||||
room2 = xyzroom.XYZRoom.objects.get_xyz(xyz=(1, 0, 'map12b'))
|
||||
east_exit = [exi for exi in room1.exits if exi.db_key == 'east'][0]
|
||||
west_exit = [exi for exi in room2.exits if exi.db_key == 'west'][0]
|
||||
|
||||
# make sure exits traverse the maps
|
||||
self.assertEqual(east_exit.db_destination, room2)
|
||||
self.assertEqual(west_exit.db_destination, room1)
|
||||
|
|
|
|||
|
|
@ -272,7 +272,8 @@ class XYMap:
|
|||
return "\n".join("".join(line) for line in self.display_map[::-1])
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Map {self.max_X + 1}x{self.max_Y + 1}, {len(self.node_index_map)} nodes>"
|
||||
return (f"<XYMap(Z={self.Z}), {self.max_X + 1}x{self.max_Y + 1}, "
|
||||
f"{len(self.node_index_map)} nodes>")
|
||||
|
||||
def reload(self, map_module_or_dict=None):
|
||||
"""
|
||||
|
|
@ -498,20 +499,20 @@ class XYMap:
|
|||
# store
|
||||
self.display_map = display_map
|
||||
|
||||
def _get_topology_around_coord(self, coord, dist=2):
|
||||
def _get_topology_around_coord(self, xy, dist=2):
|
||||
"""
|
||||
Get all links and nodes up to a certain distance from an XY coordinate.
|
||||
|
||||
Args:
|
||||
coord (tuple), the X,Y coordinate of the center point.
|
||||
xy (tuple), the X,Y coordinate of the center point.
|
||||
dist (int): How many nodes away from center point to find paths for.
|
||||
|
||||
Returns:
|
||||
tuple: A tuple of 5 elements `(coords, xmin, xmax, ymin, ymax)`, where the
|
||||
tuple: A tuple of 5 elements `(xy_coords, xmin, xmax, ymin, ymax)`, where the
|
||||
first element is a list of xy-coordinates (on xygrid) for all linked nodes within
|
||||
range. This is meant to be used with the xygrid for extracting a subset
|
||||
for display purposes. The others are the minimum size of the rectangle
|
||||
surrounding the area containing `coords`.
|
||||
surrounding the area containing `xy_coords`.
|
||||
|
||||
Notes:
|
||||
This performs a depth-first pass down the the given dist.
|
||||
|
|
@ -542,7 +543,7 @@ class XYMap:
|
|||
|
||||
return points, xmin, xmax, ymin, ymax
|
||||
|
||||
center_node = self.get_node_from_coord(coord)
|
||||
center_node = self.get_node_from_coord(xy)
|
||||
points, xmin, xmax, ymin, ymax = _scan_neighbors(center_node, [], dist=dist)
|
||||
return list(set(points)), xmin, xmax, ymin, ymax
|
||||
|
||||
|
|
@ -591,7 +592,7 @@ class XYMap:
|
|||
pickle.dump((self.mapstring, self.dist_matrix, self.pathfinding_routes),
|
||||
fil, protocol=4)
|
||||
|
||||
def spawn_nodes(self, coord=(None, None)):
|
||||
def spawn_nodes(self, 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*
|
||||
|
|
@ -599,58 +600,61 @@ class XYMap:
|
|||
to exist. It's also possible to only spawn a subset of the map
|
||||
|
||||
Args:
|
||||
coord (tuple, optional): An (X,Y) coordinate of node(s). `None` acts as a wildcard.
|
||||
xy (tuple, optional): An (X,Y) coordinate of node(s). `'*'` acts as a wildcard.
|
||||
|
||||
Examples:
|
||||
- `coord=(1, 3) - spawn (1,3) coordinate only.
|
||||
- `coord=(None, 1) - spawn all nodes in the first row of the map only.
|
||||
- `coord=(None, None)` - spawn all nodes
|
||||
- `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.
|
||||
|
||||
"""
|
||||
x, y = coord
|
||||
|
||||
x, y = xy
|
||||
wildcard = '*'
|
||||
spawned = []
|
||||
|
||||
for node in self.node_index_map.values():
|
||||
if (x is None or x == node.X) and (y is None or y == node.Y):
|
||||
if (x in (wildcard, node.X)) and (y in (wildcard, node.Y)):
|
||||
node.spawn()
|
||||
spawned.append(node)
|
||||
return spawned
|
||||
|
||||
def spawn_links(self, coord=(None, None), nodes=None, only_directions=None):
|
||||
def spawn_links(self, 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
|
||||
|
||||
Args:
|
||||
coord (tuple, optional): An (X,Y) coordinate of node(s). `None` acts as a wildcard.
|
||||
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 `coords`, so that if there are no nodes of given coords in `nodes`, no
|
||||
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 (`coords` limits which links out of which
|
||||
nodes should be considered). `None` acts as a wildcard.
|
||||
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:
|
||||
- `coord=(1, 3 )`, `direction='ne'` - sync only the north-eastern exit
|
||||
- `xy=(1, 3 )`, `direction='ne'` - sync only the north-eastern exit
|
||||
out of the (1, 3) node.
|
||||
|
||||
"""
|
||||
x, y = coord
|
||||
x, y = xy
|
||||
wildcard = '*'
|
||||
|
||||
if not nodes:
|
||||
nodes = self.node_index_map.values()
|
||||
|
||||
for node in nodes:
|
||||
if (x is None or x == node.X) and (y is None or y == node.Y):
|
||||
node.spawn_links(only_directions=only_directions)
|
||||
if (x in (wildcard, node.X)) and (y in (wildcard, node.Y)):
|
||||
node.spawn_links(directions=directions)
|
||||
|
||||
def get_node_from_coord(self, coords):
|
||||
def get_node_from_coord(self, xy):
|
||||
"""
|
||||
Get a MapNode from a coordinate.
|
||||
|
||||
Args:
|
||||
coords (tuple): X,Y coordinates on XYgrid.
|
||||
xy (tuple): X,Y coordinate on XYgrid.
|
||||
|
||||
Returns:
|
||||
MapNode: The node found at the given coordinates. Returns
|
||||
|
|
@ -664,12 +668,12 @@ class XYMap:
|
|||
if not self.XYgrid:
|
||||
self.parse()
|
||||
|
||||
iX, iY = coords
|
||||
iX, iY = xy
|
||||
if not ((0 <= iX <= self.max_X) and (0 <= iY <= self.max_Y)):
|
||||
raise MapError(f"get_node_from_coord got coordinate {coords} which is "
|
||||
raise MapError(f"get_node_from_coord got coordinate {xy} which is "
|
||||
f"outside the grid size of (0,0) - ({self.max_X}, {self.max_Y}).")
|
||||
try:
|
||||
return self.XYgrid[coords[0]][coords[1]]
|
||||
return self.XYgrid[iX][iY]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
|
|
@ -686,14 +690,14 @@ class XYMap:
|
|||
"""
|
||||
return self.symbol_map.get(symbol, [])
|
||||
|
||||
def get_shortest_path(self, startcoord, endcoord):
|
||||
def get_shortest_path(self, start_xy, end_xy):
|
||||
"""
|
||||
Get the shortest route between two points on the grid.
|
||||
|
||||
Args:
|
||||
startcoord (tuple): A starting (X,Y) coordinate on the XYgrid (in-game coordinate) for
|
||||
start_xy (tuple): A starting (X,Y) coordinate on the XYgrid (in-game coordinate) for
|
||||
where we start from.
|
||||
endcoord (tuple or MapNode): The end (X,Y) coordinate on the XYgrid (in-game coordinate)
|
||||
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:
|
||||
|
|
@ -702,8 +706,8 @@ class XYMap:
|
|||
the full path including the start- and end-node.
|
||||
|
||||
"""
|
||||
startnode = self.get_node_from_coord(startcoord)
|
||||
endnode = self.get_node_from_coord(endcoord)
|
||||
startnode = self.get_node_from_coord(start_xy)
|
||||
endnode = self.get_node_from_coord(end_xy)
|
||||
|
||||
if not (startnode and endnode):
|
||||
# no node at given coordinate. No path is possible.
|
||||
|
|
@ -751,7 +755,7 @@ class XYMap:
|
|||
|
||||
return directions, path
|
||||
|
||||
def get_visual_range(self, coord, dist=2, mode='nodes',
|
||||
def get_visual_range(self, xy, dist=2, mode='nodes',
|
||||
character='@',
|
||||
target=None, target_path_style="|y{display_symbol}|n",
|
||||
max_size=None,
|
||||
|
|
@ -761,7 +765,7 @@ class XYMap:
|
|||
of nodes or grid points in every direction.
|
||||
|
||||
Args:
|
||||
coord (tuple): (X,Y) in-world coordinate location. If this is not the location
|
||||
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
|
||||
|
|
@ -771,7 +775,7 @@ class XYMap:
|
|||
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 `coord` position
|
||||
character (str, optional): Place this symbol at the `xy` position
|
||||
of the displayed map. The center node' 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
|
||||
|
|
@ -823,7 +827,7 @@ class XYMap:
|
|||
# @-#
|
||||
|
||||
"""
|
||||
iX, iY = coord
|
||||
iX, iY = xy
|
||||
# convert inputs to xygrid
|
||||
width, height = self.max_x + 1, self.max_y + 1
|
||||
ix, iy = max(0, min(iX * 2, width)), max(0, min(iY * 2, height))
|
||||
|
|
@ -835,14 +839,14 @@ class XYMap:
|
|||
gridmap = self.display_map
|
||||
ixc, iyc = ix, iy
|
||||
|
||||
elif dist is None or dist <= 0 or not self.get_node_from_coord(coord):
|
||||
elif dist is None or dist <= 0 or not self.get_node_from_coord(xy):
|
||||
# There is no node at these coordinates. Show
|
||||
# nothing but ourselves or emptiness
|
||||
return character if character else self.empty_symbol
|
||||
|
||||
elif mode == 'nodes':
|
||||
# dist measures only full, reachable nodes.
|
||||
points, xmin, xmax, ymin, ymax = self._get_topology_around_coord(coord, dist=dist)
|
||||
points, xmin, xmax, ymin, ymax = self._get_topology_around_coord(xy, dist=dist)
|
||||
|
||||
ixc, iyc = ix - xmin, iy - ymin
|
||||
# note - override width/height here since our grid is
|
||||
|
|
@ -878,7 +882,7 @@ class XYMap:
|
|||
else:
|
||||
_target_path_style = _default_callable
|
||||
|
||||
_, path = self.get_shortest_path(coord, target)
|
||||
_, path = self.get_shortest_path(xy, target)
|
||||
|
||||
maxstep = dist if mode == 'nodes' else dist / 2
|
||||
nsteps = 0
|
||||
|
|
|
|||
|
|
@ -106,12 +106,15 @@ class XYZGrid(DefaultScript):
|
|||
remove_objects (bool, optional): If the synced database objects (rooms/exits) should
|
||||
be removed alongside this map.
|
||||
"""
|
||||
# from evennia import set_trace;set_trace()
|
||||
for zcoord in zcoords:
|
||||
if zcoord in self.db.map_data:
|
||||
self.db.map_data.pop(zcoord)
|
||||
if remove_objects:
|
||||
# this should also remove all exits automatically
|
||||
XYZRoom.objects.filter_xyz(coord=(None, None, zcoord)).delete()
|
||||
# we can't batch-delete because we want to run the .delete
|
||||
# method that also wipes exits and moves content to save locations
|
||||
for xyzroom in XYZRoom.objects.filter_xyz(xyz=('*', '*', zcoord)):
|
||||
xyzroom.delete()
|
||||
self.reload()
|
||||
|
||||
def delete(self):
|
||||
|
|
@ -121,41 +124,42 @@ class XYZGrid(DefaultScript):
|
|||
"""
|
||||
self.remove_map(*(zcoord for zcoord in self.db.map_data), remove_objects=True)
|
||||
|
||||
def spawn(self, coord=(None, None, None), only_directions=None):
|
||||
def spawn(self, xyz=('*', '*', '*'), directions=None):
|
||||
"""
|
||||
Create/recreate/update the in-game grid based on the stored Maps or for a specific Map
|
||||
or coordinate.
|
||||
|
||||
Args:
|
||||
coord (tuple, optional): An (X,Y,Z) coordinate, where Z is the name of the map. `None`
|
||||
xyz (tuple, optional): An (X,Y,Z) coordinate, where Z is the name of the map. `'*'`
|
||||
acts as a wildcard.
|
||||
only_directions (list, optional): A list of cardinal directions ('n', 'ne' etc).
|
||||
If given, spawn exits only the given direction. `None` acts as a wildcard.
|
||||
directions (list, optional): A list of cardinal directions ('n', 'ne' etc).
|
||||
Spawn exits only the given direction. If unset, all needed directions are spawned.
|
||||
|
||||
Examples:
|
||||
- `coord=(1, 3, 'foo')` - sync a specific element of map 'foo' only.
|
||||
- `coord=(None, None, 'foo') - sync all elements of map 'foo'
|
||||
- `coord=(1, 3, None) - sync all (1,3) coordinates on all maps (rarely useful)
|
||||
- `coord=(None, None, None)` - sync all maps.
|
||||
- `coord=(1, 3, 'foo')`, `direction='ne'` - sync only the north-eastern exit
|
||||
- `xyz=('*', '*', '*')` (default) - spawn/update all maps.
|
||||
- `xyz=(1, 3, 'foo')` - sync a specific element of map 'foo' only.
|
||||
- `xyz=('*', '*', 'foo') - sync all elements of map 'foo'
|
||||
- `xyz=(1, 3, '*') - sync all (1,3) coordinates on all maps (rarely useful)
|
||||
- `xyz=(1, 3, 'foo')`, `direction='ne'` - sync only the north-eastern exit
|
||||
out of the specific node on map 'foo'.
|
||||
|
||||
"""
|
||||
x, y, z = coord
|
||||
x, y, z = xyz
|
||||
wildcard = '*'
|
||||
|
||||
if z is None:
|
||||
if z == wildcard:
|
||||
xymaps = self.grid
|
||||
elif z in self.ndb.grid:
|
||||
xymaps = [self.grid[z]]
|
||||
elif self.ndb.grid and z in self.ndb.grid:
|
||||
xymaps = {z: self.grid[z]}
|
||||
else:
|
||||
raise RuntimeError(f"The 'z' coordinate/name '{z}' is not found on the grid.")
|
||||
|
||||
# first build all nodes/rooms
|
||||
for zcoord, xymap in xymaps.items():
|
||||
logger.log_info(f"[grid] spawning/updating nodes for {zcoord} ...")
|
||||
xymap.spawn_nodes(coord=(x, y))
|
||||
xymap.spawn_nodes(xy=(x, y))
|
||||
|
||||
# next build all links between nodes (including between maps)
|
||||
for zcoord, xymap in xymaps.items():
|
||||
logger.log_info(f"[grid] spawning/updating links for {zcoord} ...")
|
||||
xymap.spawn_links(coord=(x, y), only_directions=only_directions)
|
||||
xymap.spawn_links(xy=(x, y), directions=directions)
|
||||
|
|
|
|||
|
|
@ -30,16 +30,15 @@ class XYZManager(ObjectManager):
|
|||
efficiently querying the room in the database based on XY coordinates.
|
||||
|
||||
"""
|
||||
def filter_xyz(self, coord=(None, None, 'map'), **kwargs):
|
||||
def filter_xyz(self, xyz=('*', '*', '*'), **kwargs):
|
||||
"""
|
||||
Filter queryset based on map as well as x- or y-coordinate, or both. The map-name is
|
||||
required but not the coordinates - if only one coordinate is given, multiple rooms may be
|
||||
returned from the same coordinate row/column. If both coordinates are omitted (set to
|
||||
`None`), then all rooms of a given map is returned.
|
||||
Filter queryset based on XYZ position on the grid. The Z-position is the name of the XYMap
|
||||
Set a coordinate to `'*'` to act as a wildcard (setting all coords to `*` will thus find
|
||||
*all* XYZ rooms). This will also find children of XYZRooms on the given coordinates.
|
||||
|
||||
Kwargs:
|
||||
coord (tuple, optional): A tuple (X, Y, Z) where each element is either
|
||||
an `int`, `str` or `None`. `None` acts as a wild card. Note that
|
||||
xyz (tuple, optional): A coordinate tuple (X, Y, Z) where each element is either
|
||||
an `int` or `str`. The character `'*'` acts as a wild card. Note that
|
||||
the `Z`-coordinate is the name of the map (case-sensitive) in the XYZgrid contrib.
|
||||
**kwargs: All other kwargs are passed on to the query.
|
||||
|
||||
|
|
@ -48,25 +47,34 @@ class XYZManager(ObjectManager):
|
|||
with further filtering.
|
||||
|
||||
"""
|
||||
x, y, z = coord
|
||||
x, y, z = xyz
|
||||
wildcard = '*'
|
||||
|
||||
return self.filter_family(
|
||||
(Q() if x is None else Q(db_tags__db_key=str(x),
|
||||
db_tags__db_category=MAP_X_TAG_CATEGORY)),
|
||||
(Q() if y is None else Q(db_tags__db_key=str(y),
|
||||
db_tags__db_category=MAP_Y_TAG_CATEGORY)),
|
||||
(Q() if z is None else Q(db_tags__db_key=str(z),
|
||||
db_tags__db_category=MAP_Z_TAG_CATEGORY)),
|
||||
**kwargs
|
||||
|
||||
|
||||
return (
|
||||
self
|
||||
.filter_family(**kwargs)
|
||||
.filter(
|
||||
Q() if x == wildcard
|
||||
else Q(db_tags__db_key=str(x), db_tags__db_category=MAP_X_TAG_CATEGORY))
|
||||
.filter(
|
||||
Q() if y == wildcard
|
||||
else Q(db_tags__db_key=str(y), db_tags__db_category=MAP_Y_TAG_CATEGORY))
|
||||
.filter(
|
||||
Q() if z == wildcard
|
||||
else Q(db_tags__db_key=str(z), db_tags__db_category=MAP_Z_TAG_CATEGORY))
|
||||
)
|
||||
|
||||
def get_xyz(self, coord=(0, 0, 'map'), **kwargs):
|
||||
def get_xyz(self, xyz=(0, 0, 'map'), **kwargs):
|
||||
"""
|
||||
Always return a single matched entity directly.
|
||||
Always return a single matched entity directly. This accepts no `*`-wildcards.
|
||||
This will also find children of XYZRooms on the given coordinates.
|
||||
|
||||
Kwargs:
|
||||
coord (tuple): A tuple of `int` or `str` (not `None`). The `Z`-coordinate
|
||||
acts as the name (case-sensitive) of the map in the XYZgrid contrib.
|
||||
xyz (tuple): A coordinate tuple of `int` or `str` (not `'*'`, no wildcards are
|
||||
allowed in get). The `Z`-coordinate acts as the name (case-sensitive) of the map in
|
||||
the XYZgrid contrib.
|
||||
**kwargs: All other kwargs are passed on to the query.
|
||||
|
||||
Returns:
|
||||
|
|
@ -78,13 +86,27 @@ class XYZManager(ObjectManager):
|
|||
possible with a unique combination of x,y,z).
|
||||
|
||||
"""
|
||||
x, y, z = coord
|
||||
return self.get_family(
|
||||
Q(db_tags__db_key=str(x), db_tags__db_category=MAP_X_TAG_CATEGORY),
|
||||
Q(db_tags__db_key=str(y), db_tags__db_category=MAP_Y_TAG_CATEGORY),
|
||||
Q(db_tags__db_key=str(z), db_tags__db_category=MAP_Z_TAG_CATEGORY),
|
||||
**kwargs
|
||||
)
|
||||
x, y, z = xyz
|
||||
|
||||
# mimic get_family
|
||||
paths = [self.model.path] + [
|
||||
"%s.%s" % (cls.__module__, cls.__name__) for cls in self._get_subclasses(self.model)
|
||||
]
|
||||
kwargs["db_typeclass_path__in"] = paths
|
||||
|
||||
try:
|
||||
return (
|
||||
self
|
||||
.filter(db_tags__db_key=str(x), db_tags__db_category=MAP_X_TAG_CATEGORY)
|
||||
.filter(db_tags__db_key=str(y), db_tags__db_category=MAP_Y_TAG_CATEGORY)
|
||||
.filter(db_tags__db_key=str(z), db_tags__db_category=MAP_Z_TAG_CATEGORY)
|
||||
.get(**kwargs)
|
||||
)
|
||||
except self.model.DoesNotExist:
|
||||
inp = (f"xyz=({x},{y},{z}), " +
|
||||
",".join(f"{key}={val}" for key, val in kwargs.items()))
|
||||
raise self.model.DoesNotExist(f"{self.model.__name__} "
|
||||
f"matching query {inp} does not exist.")
|
||||
|
||||
|
||||
class XYZExitManager(XYZManager):
|
||||
|
|
@ -94,17 +116,19 @@ class XYZExitManager(XYZManager):
|
|||
|
||||
"""
|
||||
|
||||
def filter_xyz_exit(self, coord=(None, None, 'map'),
|
||||
destination_coord=(None, None, 'map'), **kwargs):
|
||||
def filter_xyz_exit(self, xyz=('*', '*', '*'),
|
||||
xyz_destination=('*', '*', '*'), **kwargs):
|
||||
"""
|
||||
Used by exits (objects with a source and -destination property).
|
||||
Find all exits out of a source or to a particular destination.
|
||||
Find all exits out of a source or to a particular destination. This will also find
|
||||
children of XYZExit on the given coords..
|
||||
|
||||
Kwargs:
|
||||
coord (tuple, optional): A tuple (X, Y, Z) for the source location. Each
|
||||
element is either an `int`, `str` or `None`. `None` acts as a wild card. Note that
|
||||
xyz (tuple, optional): A coordinate (X, Y, Z) for the source location. Each
|
||||
element is either an `int` or `str`. The character `'*'` is used as a wildcard -
|
||||
so setting all coordinates to the wildcard will return *all* XYZExits.
|
||||
the `Z`-coordinate is the name of the map (case-sensitive) in the XYZgrid contrib.
|
||||
destination_coord (tuple, optional): Same as the `coord` but for the destination of the
|
||||
xyz_destination (tuple, optional): Same as `xyz` but for the destination of the
|
||||
exit.
|
||||
**kwargs: All other kwargs are passed on to the query.
|
||||
|
||||
|
|
@ -113,42 +137,52 @@ class XYZExitManager(XYZManager):
|
|||
with further filtering.
|
||||
|
||||
Notes:
|
||||
Depending on what coordinates are set to `None`, this can be used to
|
||||
Depending on what coordinates are set to `*`, this can be used to
|
||||
e.g. find all exits in a room, or leading to a room or even to rooms
|
||||
in a particular X/Y row/column.
|
||||
|
||||
In the XYZgrid, `z != zdest` means a _transit_ between different maps.
|
||||
In the XYZgrid, `z_source != z_destination` means a _transit_ between different maps.
|
||||
|
||||
"""
|
||||
x, y, z = coord
|
||||
xdest, ydest, zdest = destination_coord
|
||||
x, y, z = xyz
|
||||
xdest, ydest, zdest = xyz_destination
|
||||
wildcard = '*'
|
||||
|
||||
return self.filter_family(
|
||||
(Q() if x is None else Q(db_tags__db_key=str(x),
|
||||
db_tags__db_category=MAP_X_TAG_CATEGORY)),
|
||||
(Q() if y is None else Q(db_tags__db_key=str(y),
|
||||
db_tags__db_category=MAP_Y_TAG_CATEGORY)),
|
||||
(Q() if z is None else Q(db_tags__db_key=str(z),
|
||||
db_tags__db_category=MAP_Z_TAG_CATEGORY)),
|
||||
(Q() if xdest is None else Q(db_tags__db_key=str(xdest),
|
||||
db_tags__db_category=MAP_XDEST_TAG_CATEGORY)),
|
||||
(Q() if ydest is None else Q(db_tags__db_key=str(ydest),
|
||||
db_tags__db_category=MAP_YDEST_TAG_CATEGORY)),
|
||||
(Q() if zdest is None else Q(db_tags__db_key=str(zdest),
|
||||
db_tags__db_category=MAP_ZDEST_TAG_CATEGORY)),
|
||||
return (
|
||||
self
|
||||
.filter_family(**kwargs)
|
||||
.filter(
|
||||
Q() if x == wildcard
|
||||
else Q(db_tags__db_key=str(x), db_tags__db_category=MAP_X_TAG_CATEGORY))
|
||||
.filter(
|
||||
Q() if y == wildcard
|
||||
else Q(db_tags__db_key=str(y), db_tags__db_category=MAP_Y_TAG_CATEGORY))
|
||||
.filter(
|
||||
Q() if z == wildcard
|
||||
else Q(db_tags__db_key=str(z), db_tags__db_category=MAP_Z_TAG_CATEGORY))
|
||||
.filter(
|
||||
Q() if xdest == wildcard
|
||||
else Q(db_tags__db_key=str(xdest), db_tags__db_category=MAP_XDEST_TAG_CATEGORY))
|
||||
.filter(
|
||||
Q() if ydest == wildcard
|
||||
else Q(db_tags__db_key=str(ydest), db_tags__db_category=MAP_YDEST_TAG_CATEGORY))
|
||||
.filter(
|
||||
Q() if zdest == wildcard
|
||||
else Q(db_tags__db_key=str(zdest), db_tags__db_category=MAP_ZDEST_TAG_CATEGORY))
|
||||
)
|
||||
|
||||
def get_xyz_exit(self, coord=(0, 0, 'map'), destination_coord=(0, 0, 'map'), **kwargs):
|
||||
def get_xyz_exit(self, xyz=(0, 0, 'map'), xyz_destination=(0, 0, 'map'), **kwargs):
|
||||
"""
|
||||
Used by exits (objects with a source and -destination property). Get a single
|
||||
exit. All source/destination coordinates (as well as the map's name) are required.
|
||||
This will also find children of XYZExits on the given coords.
|
||||
|
||||
Kwargs:
|
||||
coord (tuple, optional): A tuple (X, Y, Z) for the source location. Each
|
||||
element is either an `int` or `str` (not `None`).
|
||||
xyz (tuple, optional): A coordinate (X, Y, Z) for the source location. Each
|
||||
element is either an `int` or `str` (not `*`, no wildcards are allowed for get).
|
||||
the `Z`-coordinate is the name of the map (case-sensitive) in the XYZgrid contrib.
|
||||
destination_coord (tuple, optional): Same as the `coord` but for the destination of the
|
||||
exit.
|
||||
xyz_destination_coord (tuple, optional): Same as the `xyz` but for the destination of
|
||||
the exit.
|
||||
**kwargs: All other kwargs are passed on to the query.
|
||||
|
||||
Returns:
|
||||
|
|
@ -157,29 +191,41 @@ class XYZExitManager(XYZManager):
|
|||
Raises:
|
||||
DoesNotExist: If no matching query was found.
|
||||
MultipleObjectsReturned: If more than one match was found (which should not
|
||||
possible with a unique combination of x,y,x).
|
||||
be possible with a unique combination of x,y,x).
|
||||
|
||||
Notes:
|
||||
All coordinates are required.
|
||||
|
||||
"""
|
||||
x, y, z = coord
|
||||
xdest, ydest, zdest = destination_coord
|
||||
x, y, z = xyz
|
||||
xdest, ydest, zdest = xyz_destination
|
||||
# mimic get_family
|
||||
paths = [self.model.path] + [
|
||||
"%s.%s" % (cls.__module__, cls.__name__) for cls in self._get_subclasses(self.model)
|
||||
]
|
||||
kwargs["db_typeclass_path__in"] = paths
|
||||
|
||||
return self.get_family(
|
||||
Q(db_tags__db_key=str(z), db_tags__db_category=MAP_Z_TAG_CATEGORY),
|
||||
Q(db_tags__db_key=str(x), db_tags__db_category=MAP_X_TAG_CATEGORY),
|
||||
Q(db_tags__db_key=str(y), db_tags__db_category=MAP_Y_TAG_CATEGORY),
|
||||
Q(db_tags__db_key=str(xdest), db_tags__db_category=MAP_XDEST_TAG_CATEGORY),
|
||||
Q(db_tags__db_key=str(ydest), db_tags__db_category=MAP_YDEST_TAG_CATEGORY),
|
||||
Q(db_tags__db_key=str(zdest), db_tags__db_category=MAP_ZDEST_TAG_CATEGORY),
|
||||
**kwargs
|
||||
)
|
||||
try:
|
||||
return (
|
||||
self
|
||||
.filter(db_tags__db_key=str(z), db_tags__db_category=MAP_Z_TAG_CATEGORY)
|
||||
.filter(db_tags__db_key=str(x), db_tags__db_category=MAP_X_TAG_CATEGORY)
|
||||
.filter(db_tags__db_key=str(y), db_tags__db_category=MAP_Y_TAG_CATEGORY)
|
||||
.filter(db_tags__db_key=str(xdest), db_tags__db_category=MAP_XDEST_TAG_CATEGORY)
|
||||
.filter(db_tags__db_key=str(ydest), db_tags__db_category=MAP_YDEST_TAG_CATEGORY)
|
||||
.filter(db_tags__db_key=str(zdest), db_tags__db_category=MAP_ZDEST_TAG_CATEGORY)
|
||||
.get(**kwargs)
|
||||
)
|
||||
except self.model.DoesNotExist:
|
||||
inp = (f"xyz=({x},{y},{z}),xyz_destination=({xdest},{ydest},{zdest})," +
|
||||
",".join(f"{key}={val}" for key, val in kwargs.items()))
|
||||
raise self.model.DoesNotExist(f"{self.model.__name__} "
|
||||
f"matching query {inp} does not exist.")
|
||||
|
||||
|
||||
class XYZRoom(DefaultRoom):
|
||||
"""
|
||||
A game location aware of its XY-coordinate and map.
|
||||
A game location aware of its XYZ-position.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -190,28 +236,32 @@ class XYZRoom(DefaultRoom):
|
|||
return repr(self)
|
||||
|
||||
def __repr__(self):
|
||||
x, y, z = self.xyzcoords
|
||||
x, y, z = self.xyz
|
||||
return f"<XYZRoom '{self.db_key}', XYZ=({x},{y},{z})>"
|
||||
|
||||
@property
|
||||
def xyzcoords(self):
|
||||
if not hasattr(self, "_xyzcoords"):
|
||||
def xyz(self):
|
||||
if not hasattr(self, "_xyz"):
|
||||
x = self.tags.get(category=MAP_X_TAG_CATEGORY, return_list=False)
|
||||
y = self.tags.get(category=MAP_Y_TAG_CATEGORY, return_list=False)
|
||||
z = self.tags.get(category=MAP_Z_TAG_CATEGORY, return_list=False)
|
||||
self._xyzcoords = (x, y, z)
|
||||
return self._xyzcoords
|
||||
if x is None or y is None or z is None:
|
||||
# don't cache unfinished coordinate
|
||||
return (x, y, z)
|
||||
# cache result
|
||||
self._xyz = (x, y, z)
|
||||
return self._xyz
|
||||
|
||||
@classmethod
|
||||
def create(cls, key, account=None, coord=(0, 0, 'map'), **kwargs):
|
||||
def create(cls, key, account=None, xyz=(0, 0, 'map'), **kwargs):
|
||||
"""
|
||||
Creation method aware of coordinates.
|
||||
Creation method aware of XYZ coordinates.
|
||||
|
||||
Args:
|
||||
key (str): New name of object to create.
|
||||
account (Account, optional): Any Account to tie to this entity (usually not used for
|
||||
rooms).
|
||||
coords (tuple, optional): A 3D coordinate (X, Y, Z) for this room's location on a
|
||||
xyz (tuple, optional): A 3D coordinate (X, Y, Z) for this room's location on a
|
||||
map grid. Each element can theoretically be either `int` or `str`, but for the
|
||||
XYZgrid, the X, Y are always integers while the `Z` coordinate is used for the
|
||||
map's name.
|
||||
|
|
@ -227,15 +277,15 @@ class XYZRoom(DefaultRoom):
|
|||
|
||||
"""
|
||||
try:
|
||||
x, y, z = coord
|
||||
x, y, z = xyz
|
||||
except ValueError:
|
||||
return None, [f"XYRroom.create got `coord={coord}` - needs a valid (X,Y,Z) "
|
||||
return None, [f"XYRroom.create got `xyz={xyz}` - needs a valid (X,Y,Z) "
|
||||
"coordinate of ints/strings."]
|
||||
|
||||
existing_query = cls.objects.filter_xyz(coord=(x, y, z))
|
||||
existing_query = cls.objects.filter_xyz(xyz=(x, y, z))
|
||||
if existing_query.exists():
|
||||
existing_room = existing_query.first()
|
||||
return None, [f"XYRoom XYZ={coord} already exists "
|
||||
return None, [f"XYRoom XYZ=({x},{y},{z}) already exists "
|
||||
f"(existing room is named '{existing_room.db_key}')!"]
|
||||
|
||||
tags = (
|
||||
|
|
@ -249,7 +299,7 @@ class XYZRoom(DefaultRoom):
|
|||
|
||||
class XYZExit(DefaultExit):
|
||||
"""
|
||||
An exit that is aware of the XY coordinate system.
|
||||
An exit that is aware of the XYZ coordinate system.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -259,30 +309,38 @@ class XYZExit(DefaultExit):
|
|||
return repr(self)
|
||||
|
||||
def __repr__(self):
|
||||
x, y, z = self.xyzcoords
|
||||
xd, yd, zd = self.xyzdestcoords
|
||||
x, y, z = self.xyz
|
||||
xd, yd, zd = self.xyz_destination
|
||||
return f"<XYZExit '{self.db_key}', XYZ=({x},{y},{z})->({xd},{yd},{zd})>"
|
||||
|
||||
@property
|
||||
def xyzcoords(self):
|
||||
if not hasattr(self, "_xyzcoords"):
|
||||
def xyz(self):
|
||||
if not hasattr(self, "_xyz"):
|
||||
x = self.tags.get(category=MAP_X_TAG_CATEGORY, return_list=False)
|
||||
y = self.tags.get(category=MAP_Y_TAG_CATEGORY, return_list=False)
|
||||
z = self.tags.get(category=MAP_Z_TAG_CATEGORY, return_list=False)
|
||||
self._xyzcoords = (x, y, z)
|
||||
return self._xyzcoords
|
||||
if x is None or y is None or z is None:
|
||||
# don't cache yet unfinished coordinate
|
||||
return (x, y, z)
|
||||
# cache result
|
||||
self._xyz = (x, y, z)
|
||||
return self._xyz
|
||||
|
||||
@property
|
||||
def xyzdestcoords(self):
|
||||
if not hasattr(self, "_xyzdestcoords"):
|
||||
def xyz_destination(self):
|
||||
if not hasattr(self, "_xyz_destination"):
|
||||
xd = self.tags.get(category=MAP_XDEST_TAG_CATEGORY, return_list=False)
|
||||
yd = self.tags.get(category=MAP_YDEST_TAG_CATEGORY, return_list=False)
|
||||
zd = self.tags.get(category=MAP_ZDEST_TAG_CATEGORY, return_list=False)
|
||||
self._xyzdestcoords = (xd, yd, zd)
|
||||
return self._xyzdestcoords
|
||||
if xd is None or yd is None or zd is None:
|
||||
# don't cache unfinished coordinate
|
||||
return (xd, yd, zd)
|
||||
# cache result
|
||||
self._xyz_destination = (xd, yd, zd)
|
||||
return self._xyz_destination
|
||||
|
||||
@classmethod
|
||||
def create(cls, key, account=None, coord=(0, 0, 'map'), destination_coord=(0, 0, 'map'),
|
||||
def create(cls, key, account=None, xyz=(0, 0, 'map'), xyz_destination=(0, 0, 'map'),
|
||||
location=None, destination=None, **kwargs):
|
||||
"""
|
||||
Creation method aware of coordinates.
|
||||
|
|
@ -290,19 +348,17 @@ class XYZExit(DefaultExit):
|
|||
Args:
|
||||
key (str): New name of object to create.
|
||||
account (Account, optional): Any Account to tie to this entity (unused for exits).
|
||||
coords (tuple or None, optional): A 3D coordinate (X, Y, Z) for this room's location
|
||||
xyz (tuple or None, optional): A 3D coordinate (X, Y, Z) for this room's location
|
||||
on a map grid. Each element can theoretically be either `int` or `str`, but for the
|
||||
XYZgrid contrib, the X, Y are always integers while the `Z` coordinate is used for
|
||||
the map's name. Set to `None` if instead using a direct room reference with
|
||||
`location`. destination_coord (tuple or None, optional): Works as `coords`, but for
|
||||
destination of
|
||||
the exit. Set to `None` if using the `destination` kwarg to point to room directly.
|
||||
destination_coord (tuple, optional): The XYZ coordinate of the place the exit
|
||||
`location`.
|
||||
xyz_destination (tuple, optional): The XYZ coordinate of the place the exit
|
||||
leads to. Will be ignored if `destination` is given directly.
|
||||
location (Object, optional): Only used if `coord` is not given. This can be used
|
||||
location (Object, optional): If given, overrides `xyz` coordinate. This can be used
|
||||
to place this exit in any room, including non-XYRoom type rooms.
|
||||
destination (Object, optional): If given, overrides `destination_coord`. This can
|
||||
be any room (including non-XYRooms) and is not checked for XY coordinates.
|
||||
destination (Object, optional): If given, overrides `xyz_destination`. This can
|
||||
be any room (including non-XYRooms) and is not checked for XYZ coordinates.
|
||||
**kwargs: Will be passed into the normal `DefaultRoom.create` method.
|
||||
|
||||
Returns:
|
||||
|
|
@ -311,35 +367,32 @@ class XYZExit(DefaultExit):
|
|||
|
||||
"""
|
||||
tags = []
|
||||
try:
|
||||
x, y, z = coord
|
||||
except ValueError:
|
||||
if not location:
|
||||
return None, ["XYExit.create need either a `coord` or a `location`."]
|
||||
if location:
|
||||
source = location
|
||||
else:
|
||||
print("rooms:", XYZRoom.objects.all().count(), XYZRoom.objects.all())
|
||||
print("exits:", XYZExit.objects.all().count(), XYZExit.objects.all())
|
||||
source = XYZRoom.objects.get_xyz(coord=(x, y, z))
|
||||
tags.extend(((str(x), MAP_X_TAG_CATEGORY),
|
||||
(str(y), MAP_Y_TAG_CATEGORY),
|
||||
(str(z), MAP_Z_TAG_CATEGORY)))
|
||||
try:
|
||||
x, y, z = xyz
|
||||
except ValueError:
|
||||
return None, ["XYExit.create need either `xyz=(X,Y,Z)` coordinate or a `location`."]
|
||||
else:
|
||||
source = XYZRoom.objects.get_xyz(xyz=(x, y, z))
|
||||
tags.extend(((str(x), MAP_X_TAG_CATEGORY),
|
||||
(str(y), MAP_Y_TAG_CATEGORY),
|
||||
(str(z), MAP_Z_TAG_CATEGORY)))
|
||||
if destination:
|
||||
dest = destination
|
||||
else:
|
||||
try:
|
||||
xdest, ydest, zdest = destination_coord
|
||||
xdest, ydest, zdest = xyz_destination
|
||||
except ValueError:
|
||||
if not destination:
|
||||
return None, ["XYExit.create need either a `destination_coord` or "
|
||||
"a `destination`."]
|
||||
dest = destination
|
||||
return None, ["XYExit.create need either `xyz_destination=(X,Y,Z)` coordinate "
|
||||
"or a `destination`."]
|
||||
else:
|
||||
dest = XYZRoom.objects.get_xyz(coord=(xdest, ydest, zdest))
|
||||
dest = XYZRoom.objects.get_xyz(xyz=(xdest, ydest, zdest))
|
||||
tags.extend(((str(xdest), MAP_XDEST_TAG_CATEGORY),
|
||||
(str(ydest), MAP_YDEST_TAG_CATEGORY),
|
||||
(str(zdest), MAP_ZDEST_TAG_CATEGORY)))
|
||||
|
||||
return DefaultExit.create(
|
||||
key, source, dest, account=account,
|
||||
location=location, tags=tags, typeclass=cls, **kwargs)
|
||||
key, source, dest,
|
||||
account=account, tags=tags, typeclass=cls, **kwargs)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue