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