Fix remaining map contrib issues
This commit is contained in:
parent
25781b27d7
commit
a3995f5b67
8 changed files with 165 additions and 59 deletions
|
|
@ -191,6 +191,7 @@ class CmdGoto(COMMAND_DEFAULT_CLASS):
|
||||||
|
|
||||||
# how quickly to step (seconds)
|
# how quickly to step (seconds)
|
||||||
auto_step_delay = 2
|
auto_step_delay = 2
|
||||||
|
default_xyz_path_interrupt_msg = "Pathfinding interrupted here."
|
||||||
|
|
||||||
def _search_by_xyz(self, inp, xyz_start):
|
def _search_by_xyz(self, inp, xyz_start):
|
||||||
inp = inp.strip("()")
|
inp = inp.strip("()")
|
||||||
|
|
@ -286,10 +287,14 @@ class CmdGoto(COMMAND_DEFAULT_CLASS):
|
||||||
step_sequence=step_sequence,
|
step_sequence=step_sequence,
|
||||||
task=None
|
task=None
|
||||||
)
|
)
|
||||||
|
# the map can itself tell the stepper to stop the auto-step prematurely
|
||||||
|
interrupt_node_or_link = None
|
||||||
|
|
||||||
# pop any extra links up until the next node - these are
|
# pop any extra links up until the next node - these are
|
||||||
# not useful when dealing with exits
|
# not useful when dealing with exits
|
||||||
while step_sequence:
|
while step_sequence:
|
||||||
|
if not interrupt_node_or_link and step_sequence[0].interrupt_path:
|
||||||
|
interrupt_node_or_link = step_sequence[0]
|
||||||
if hasattr(step_sequence[0], "node_index"):
|
if hasattr(step_sequence[0], "node_index"):
|
||||||
break
|
break
|
||||||
step_sequence.pop(0)
|
step_sequence.pop(0)
|
||||||
|
|
@ -298,13 +303,28 @@ class CmdGoto(COMMAND_DEFAULT_CLASS):
|
||||||
exit_name, *_ = first_link.spawn_aliases.get(
|
exit_name, *_ = first_link.spawn_aliases.get(
|
||||||
direction, current_node.direction_spawn_defaults.get(direction, ('unknown', )))
|
direction, current_node.direction_spawn_defaults.get(direction, ('unknown', )))
|
||||||
|
|
||||||
if not caller.search(exit_name):
|
exit_obj = caller.search(exit_name)
|
||||||
|
if not exit_obj:
|
||||||
# extra safety measure to avoid trying to walk over and over
|
# extra safety measure to avoid trying to walk over and over
|
||||||
# if there's something wrong with the exit's name
|
# if there's something wrong with the exit's name
|
||||||
caller.msg(f"No exit '{exit_name}' found at current location. Aborting goto.")
|
caller.msg(f"No exit '{exit_name}' found at current location. Aborting goto.")
|
||||||
caller.ndb.xy_path_data = None
|
caller.ndb.xy_path_data = None
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if interrupt_node_or_link:
|
||||||
|
# premature stop of pathfind-step because of map node/link of interrupt type
|
||||||
|
if hasattr(interrupt_node_or_link, "node_index"):
|
||||||
|
message = exit_obj.destination.attributes.get(
|
||||||
|
"xyz_path_interrupt_msg", default=self.default_xyz_path_interrupt_msg)
|
||||||
|
# we move into the node/room and then stop
|
||||||
|
caller.execute_cmd(exit_name, session=session)
|
||||||
|
else:
|
||||||
|
# if the link is interrupted we don't cross it at all
|
||||||
|
message = exit_obj.attributes.get(
|
||||||
|
"xyz_path_interrupt_msg", default=self.default_xyz_path_interrupt_msg)
|
||||||
|
caller.msg(message)
|
||||||
|
return
|
||||||
|
|
||||||
# do the actual move - we use the command to allow for more obvious overrides
|
# do the actual move - we use the command to allow for more obvious overrides
|
||||||
caller.execute_cmd(exit_name, session=session)
|
caller.execute_cmd(exit_name, session=session)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,14 @@ def _option_list(*suboptions):
|
||||||
List/view grid.
|
List/view grid.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
xyzgrid = get_xyzgrid()
|
xyzgrid = get_xyzgrid()
|
||||||
|
|
||||||
|
# override grid's logger to echo directly to console
|
||||||
|
def _log(msg):
|
||||||
|
print(msg)
|
||||||
|
xyzgrid.log = _log
|
||||||
|
|
||||||
xymap_data = xyzgrid.grid
|
xymap_data = xyzgrid.grid
|
||||||
if not xymap_data:
|
if not xymap_data:
|
||||||
print("The XYZgrid is currently empty. Use 'add' to add paths to your map data.")
|
print("The XYZgrid is currently empty. Use 'add' to add paths to your map data.")
|
||||||
|
|
@ -220,6 +227,7 @@ def _option_list(*suboptions):
|
||||||
print("\nDisplayed map (as appearing in-game):\n\n" + ansi.parse_ansi(str(xymap)))
|
print("\nDisplayed map (as appearing in-game):\n\n" + ansi.parse_ansi(str(xymap)))
|
||||||
print("\nRaw map string (including axes and invisible nodes/links):\n"
|
print("\nRaw map string (including axes and invisible nodes/links):\n"
|
||||||
+ str(xymap.mapstring))
|
+ str(xymap.mapstring))
|
||||||
|
print(f"\nCustom map options: {xymap.options}\n")
|
||||||
legend = []
|
legend = []
|
||||||
for key, node_or_link in xymap.legend.items():
|
for key, node_or_link in xymap.legend.items():
|
||||||
legend.append(f"{key} - {node_or_link.__doc__.strip()}")
|
legend.append(f"{key} - {node_or_link.__doc__.strip()}")
|
||||||
|
|
@ -241,6 +249,12 @@ def _option_add(*suboptions):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
grid = get_xyzgrid()
|
grid = get_xyzgrid()
|
||||||
|
|
||||||
|
# override grid's logger to echo directly to console
|
||||||
|
def _log(msg):
|
||||||
|
print(msg)
|
||||||
|
grid.log = _log
|
||||||
|
|
||||||
xymap_data_list = []
|
xymap_data_list = []
|
||||||
for path in suboptions:
|
for path in suboptions:
|
||||||
maps = grid.maps_from_module(path)
|
maps = grid.maps_from_module(path)
|
||||||
|
|
@ -291,7 +305,8 @@ def _option_build(*suboptions):
|
||||||
|
|
||||||
print("Starting build ...")
|
print("Starting build ...")
|
||||||
grid.spawn(xyz=(x, y, z))
|
grid.spawn(xyz=(x, y, z))
|
||||||
print("... build complete!")
|
print("... build complete!\nIt's recommended to reload the server to refresh caches if this "
|
||||||
|
"modified an existing grid.")
|
||||||
|
|
||||||
|
|
||||||
def _option_initpath(*suboptions):
|
def _option_initpath(*suboptions):
|
||||||
|
|
@ -300,6 +315,12 @@ def _option_initpath(*suboptions):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
grid = get_xyzgrid()
|
grid = get_xyzgrid()
|
||||||
|
|
||||||
|
# override grid's logger to echo directly to console
|
||||||
|
def _log(msg):
|
||||||
|
print(msg)
|
||||||
|
grid.log = _log
|
||||||
|
|
||||||
xymaps = grid.all_maps()
|
xymaps = grid.all_maps()
|
||||||
nmaps = len(xymaps)
|
nmaps = len(xymaps)
|
||||||
for inum, xymap in enumerate(xymaps):
|
for inum, xymap in enumerate(xymaps):
|
||||||
|
|
@ -317,6 +338,12 @@ def _option_delete(*suboptions):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
grid = get_xyzgrid()
|
grid = get_xyzgrid()
|
||||||
|
|
||||||
|
# override grid's logger to echo directly to console
|
||||||
|
def _log(msg):
|
||||||
|
print(msg)
|
||||||
|
grid.log = _log
|
||||||
|
|
||||||
if not suboptions:
|
if not suboptions:
|
||||||
repl = input("WARNING: This will delete the ENTIRE Grid and wipe all rooms/exits!"
|
repl = input("WARNING: This will delete the ENTIRE Grid and wipe all rooms/exits!"
|
||||||
"\nObjects/Chars inside deleted rooms will be moved to their home locations."
|
"\nObjects/Chars inside deleted rooms will be moved to their home locations."
|
||||||
|
|
@ -326,7 +353,8 @@ def _option_delete(*suboptions):
|
||||||
return
|
return
|
||||||
print("Deleting grid ...")
|
print("Deleting grid ...")
|
||||||
grid.delete()
|
grid.delete()
|
||||||
print("... done.")
|
print("... done.\nPlease reload the server now; otherwise "
|
||||||
|
"removed rooms may linger in cache.")
|
||||||
return
|
return
|
||||||
|
|
||||||
zcoords = (part.strip() for part in suboptions)
|
zcoords = (part.strip() for part in suboptions)
|
||||||
|
|
@ -349,7 +377,8 @@ def _option_delete(*suboptions):
|
||||||
|
|
||||||
print("Deleting selected xymaps ...")
|
print("Deleting selected xymaps ...")
|
||||||
grid.remove_map(*zcoords, remove_objects=True)
|
grid.remove_map(*zcoords, remove_objects=True)
|
||||||
print("... done. Remember to remove any links from remaining maps pointing to deleted maps.")
|
print("... done.\nPlease reload the server to refresh room caches."
|
||||||
|
"\nAlso remember to remove any links from remaining maps pointing to deleted maps.")
|
||||||
|
|
||||||
|
|
||||||
def xyzcommand(*args):
|
def xyzcommand(*args):
|
||||||
|
|
|
||||||
|
|
@ -45,13 +45,13 @@ MAP1 = r"""
|
||||||
|
|
||||||
8 #-------#-#-------I
|
8 #-------#-#-------I
|
||||||
\ /
|
\ /
|
||||||
7 #-#---# #-t
|
7 #-#---# t-#
|
||||||
|\ |
|
|\ |
|
||||||
6 #i#-#b--#-t
|
6 #i#-#b--#-t
|
||||||
| |
|
| |
|
||||||
5 o-#---#
|
5 o-#---#
|
||||||
\ /
|
\ /
|
||||||
4 o-o-#-#
|
4 o---#-#
|
||||||
/ d
|
/ d
|
||||||
3 #-----+-------#
|
3 #-----+-------#
|
||||||
| d
|
| d
|
||||||
|
|
@ -59,7 +59,7 @@ MAP1 = r"""
|
||||||
v u
|
v u
|
||||||
1 #---#>#-#
|
1 #---#>#-#
|
||||||
/
|
/
|
||||||
0 T-#
|
0 #-T
|
||||||
|
|
||||||
+ 0 1 2 3 4 5 6 7 8 9 0
|
+ 0 1 2 3 4 5 6 7 8 9 0
|
||||||
1
|
1
|
||||||
|
|
@ -87,11 +87,11 @@ PROTOTYPES_MAP1 = {
|
||||||
# node/room prototypes
|
# node/room prototypes
|
||||||
(3, 0): {
|
(3, 0): {
|
||||||
"key": "Dungeon Entrance",
|
"key": "Dungeon Entrance",
|
||||||
"desc": "To the west, a narrow opening leads into darkness."
|
"desc": "To the east, a narrow opening leads into darkness."
|
||||||
},
|
},
|
||||||
(4, 1): {
|
(4, 1): {
|
||||||
"key": "Under the foilage of a giant tree",
|
"key": "Under the foilage of a giant tree",
|
||||||
"desc": "High above the branches of a giant tree blocs out the sunlight. A slide "
|
"desc": "High above the branches of a giant tree blocks out the sunlight. A slide "
|
||||||
"leading down from the upper branches ends here."
|
"leading down from the upper branches ends here."
|
||||||
},
|
},
|
||||||
(4, 4): {
|
(4, 4): {
|
||||||
|
|
@ -117,11 +117,11 @@ PROTOTYPES_MAP1 = {
|
||||||
},
|
},
|
||||||
(5, 6): {
|
(5, 6): {
|
||||||
"key": "On a huge branch",
|
"key": "On a huge branch",
|
||||||
"desc": "To the east is a glowing light, may be a teleporter."
|
"desc": "To the east is a glowing light, may be a teleporter to a higher branch."
|
||||||
},
|
},
|
||||||
(9, 7): {
|
(9, 7): {
|
||||||
"key": "On an enormous branch",
|
"key": "On an enormous branch",
|
||||||
"desc": "To the east is a glowing light, may be a teleporter."
|
"desc": "To the west is a glowing light. It may be a teleporter to a lower branch."
|
||||||
},
|
},
|
||||||
(10, 8): {
|
(10, 8): {
|
||||||
"key": "A gorgeous view",
|
"key": "A gorgeous view",
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ except ImportError as err:
|
||||||
"the SciPy package. Install with `pip install scipy'.")
|
"the SciPy package. Install with `pip install scipy'.")
|
||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
from collections import defaultdict
|
||||||
from evennia.prototypes import spawner
|
from evennia.prototypes import spawner
|
||||||
from evennia.utils.utils import make_iter
|
from evennia.utils.utils import make_iter
|
||||||
from .utils import MAPSCAN, REVERSE_DIRECTIONS, MapParserError, BIGVAL
|
from .utils import MAPSCAN, REVERSE_DIRECTIONS, MapParserError, BIGVAL
|
||||||
|
|
@ -141,7 +142,7 @@ class MapNode:
|
||||||
|
|
||||||
def log(self, msg):
|
def log(self, msg):
|
||||||
"""log messages using the xygrid parent"""
|
"""log messages using the xygrid parent"""
|
||||||
self.xymap.xyzgrid.log(msg)
|
self.xymap.log(msg)
|
||||||
|
|
||||||
def generate_prototype_key(self):
|
def generate_prototype_key(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -301,17 +302,19 @@ class MapNode:
|
||||||
|
|
||||||
xyz = self.get_spawn_xyz()
|
xyz = self.get_spawn_xyz()
|
||||||
|
|
||||||
self.log(f" spawning/updating room at xyz={xyz}")
|
|
||||||
try:
|
try:
|
||||||
nodeobj = NodeTypeclass.objects.get_xyz(xyz=xyz)
|
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
|
||||||
|
self.log(f" spawning room at xyz={xyz}")
|
||||||
nodeobj, err = NodeTypeclass.create(
|
nodeobj, err = NodeTypeclass.create(
|
||||||
self.prototype.get('key', 'An empty room'),
|
self.prototype.get('key', 'An empty room'),
|
||||||
xyz=xyz
|
xyz=xyz
|
||||||
)
|
)
|
||||||
if err:
|
if err:
|
||||||
raise RuntimeError(err)
|
raise RuntimeError(err)
|
||||||
|
else:
|
||||||
|
self.log(f" updating existing room (if changed) at xyz={xyz}")
|
||||||
|
|
||||||
if not self.prototype.get('prototype_key'):
|
if not self.prototype.get('prototype_key'):
|
||||||
# make sure there is a prototype_key in prototype
|
# make sure there is a prototype_key in prototype
|
||||||
|
|
@ -356,6 +359,17 @@ class MapNode:
|
||||||
link.prototype['prototype_key'] = self.generate_prototype_key()
|
link.prototype['prototype_key'] = self.generate_prototype_key()
|
||||||
maplinks[key.lower()] = (key, aliases, direction, link)
|
maplinks[key.lower()] = (key, aliases, direction, link)
|
||||||
|
|
||||||
|
# if xyz == (8, 1, 'the large tree'):
|
||||||
|
# from evennia import set_trace;set_trace()
|
||||||
|
# remove duplicates
|
||||||
|
linkobjs = defaultdict(list)
|
||||||
|
for exitobj in ExitTypeclass.objects.filter_xyz(xyz=xyz):
|
||||||
|
linkobjs[exitobj.key].append(exitobj)
|
||||||
|
for exitkey, exitobjs in linkobjs.items():
|
||||||
|
for exitobj in exitobjs[1:]:
|
||||||
|
self.log(f" deleting duplicate {exitkey}")
|
||||||
|
exitobj.delete()
|
||||||
|
|
||||||
# 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
|
||||||
|
|
@ -365,10 +379,11 @@ class MapNode:
|
||||||
# build all exits first run)
|
# build all exits first run)
|
||||||
differing_keys = set(maplinks.keys()).symmetric_difference(set(linkobjs.keys()))
|
differing_keys = set(maplinks.keys()).symmetric_difference(set(linkobjs.keys()))
|
||||||
for differing_key in differing_keys:
|
for differing_key in differing_keys:
|
||||||
|
# from evennia import set_trace;set_trace()
|
||||||
|
|
||||||
if differing_key not in maplinks:
|
if differing_key not in maplinks:
|
||||||
# an exit without a maplink - delete the exit-object
|
# an exit without a maplink - delete the exit-object
|
||||||
self.log(f" deleting exit at xyz={xyz}, direction={direction}")
|
self.log(f" deleting exit at xyz={xyz}, direction={differing_key}")
|
||||||
|
|
||||||
linkobjs.pop(differing_key).delete()
|
linkobjs.pop(differing_key).delete()
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -115,9 +115,10 @@ from . import map_legend
|
||||||
|
|
||||||
_CACHE_DIR = settings.CACHE_DIR
|
_CACHE_DIR = settings.CACHE_DIR
|
||||||
_LOADED_PROTOTYPES = None
|
_LOADED_PROTOTYPES = None
|
||||||
|
_XYZROOMCLASS = None
|
||||||
|
|
||||||
MAP_DATA_KEYS = [
|
MAP_DATA_KEYS = [
|
||||||
"zcoord", "map", "legend", "prototypes", "options"
|
"zcoord", "map", "legend", "prototypes", "options", "module_path"
|
||||||
]
|
]
|
||||||
|
|
||||||
# these are all symbols used for x,y coordinate spots
|
# these are all symbols used for x,y coordinate spots
|
||||||
|
|
@ -280,6 +281,12 @@ class XYMap:
|
||||||
return (f"<XYMap(Z={self.Z}), {self.max_X + 1}x{self.max_Y + 1}, "
|
return (f"<XYMap(Z={self.Z}), {self.max_X + 1}x{self.max_Y + 1}, "
|
||||||
f"{len(self.node_index_map)} nodes>")
|
f"{len(self.node_index_map)} nodes>")
|
||||||
|
|
||||||
|
def log(self, msg):
|
||||||
|
if self.xyzgrid:
|
||||||
|
self.xyzgrid.log(msg)
|
||||||
|
else:
|
||||||
|
logger.log_info(msg)
|
||||||
|
|
||||||
def reload(self, map_module_or_dict=None):
|
def reload(self, map_module_or_dict=None):
|
||||||
"""
|
"""
|
||||||
(Re)Load a map.
|
(Re)Load a map.
|
||||||
|
|
@ -629,10 +636,23 @@ class XYMap:
|
||||||
list: A list of nodes that were spawned.
|
list: A list of nodes that were spawned.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
global _XYZROOMCLASS
|
||||||
|
if not _XYZROOMCLASS:
|
||||||
|
from evennia.contrib.xyzgrid.xyzroom import XYZRoom as _XYZROOMCLASS
|
||||||
x, y = xy
|
x, y = xy
|
||||||
wildcard = '*'
|
wildcard = '*'
|
||||||
spawned = []
|
spawned = []
|
||||||
|
|
||||||
|
# find existing nodes, in case some rooms need to be removed
|
||||||
|
map_coords = ((node.X, node.Y) for node in
|
||||||
|
sorted(self.node_index_map.values(), key=lambda n: (n.Y, n.X)))
|
||||||
|
for existing_room in _XYZROOMCLASS.objects.filter_xyz(xyz=(x, y, self.Z)):
|
||||||
|
roomX, roomY, _ = existing_room.xyz
|
||||||
|
if (roomX, roomY) not in map_coords:
|
||||||
|
self.log(f" deleting room at {existing_room.xyz} (not found on map).")
|
||||||
|
existing_room.delete()
|
||||||
|
|
||||||
|
# (re)build nodes (will not build already existing rooms)
|
||||||
for node in sorted(self.node_index_map.values(), key=lambda n: (n.Y, n.X)):
|
for node in sorted(self.node_index_map.values(), key=lambda n: (n.Y, n.X)):
|
||||||
if (x in (wildcard, node.X)) and (y in (wildcard, node.Y)):
|
if (x in (wildcard, node.X)) and (y in (wildcard, node.Y)):
|
||||||
node.spawn()
|
node.spawn()
|
||||||
|
|
@ -758,15 +778,6 @@ class XYMap:
|
||||||
directions.append(shortest_route_to[0])
|
directions.append(shortest_route_to[0])
|
||||||
path.extend(shortest_route_to[1][::-1] + [nextnode])
|
path.extend(shortest_route_to[1][::-1] + [nextnode])
|
||||||
|
|
||||||
if any(1 for step in shortest_route_to[1] if step.interrupt_path):
|
|
||||||
# detected an interrupt in linkage - discard what we have so far
|
|
||||||
directions = []
|
|
||||||
path = [nextnode]
|
|
||||||
|
|
||||||
if nextnode.interrupt_path and nextnode is not startnode:
|
|
||||||
directions = []
|
|
||||||
path = [nextnode]
|
|
||||||
|
|
||||||
# we have the path - reverse to get the correct order
|
# we have the path - reverse to get the correct order
|
||||||
directions = directions[::-1]
|
directions = directions[::-1]
|
||||||
path = path[::-1]
|
path = path[::-1]
|
||||||
|
|
|
||||||
|
|
@ -111,51 +111,77 @@ class XYZGrid(DefaultScript):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def reload(self):
|
def maps_from_module(self, module_path):
|
||||||
"""
|
|
||||||
Reload and rebuild the grid. This is done on a server reload and is also necessary if adding
|
|
||||||
a new map since this may introduce new between-map traversals.
|
|
||||||
|
|
||||||
"""
|
|
||||||
self.log("(Re)loading grid ...")
|
|
||||||
self.ndb.grid = {}
|
|
||||||
nmaps = 0
|
|
||||||
# generate all Maps - this will also initialize their components
|
|
||||||
# and bake any pathfinding paths (or load from disk-cache)
|
|
||||||
for zcoord, mapdata in self.db.map_data.items():
|
|
||||||
|
|
||||||
self.log(f"Loading map '{zcoord}'...")
|
|
||||||
xymap = XYMap(dict(mapdata), Z=zcoord, xyzgrid=self)
|
|
||||||
xymap.parse()
|
|
||||||
xymap.calculate_path_matrix()
|
|
||||||
self.ndb.grid[zcoord] = xymap
|
|
||||||
nmaps += 1
|
|
||||||
|
|
||||||
# store
|
|
||||||
self.log(f"Loaded and linked {nmaps} map(s).")
|
|
||||||
self.ndb.loaded = True
|
|
||||||
|
|
||||||
def maps_from_module(self, module):
|
|
||||||
"""
|
"""
|
||||||
Load map data from module. The loader will look for a dict XYMAP_DATA or a list of
|
Load map data from module. The loader will look for a dict XYMAP_DATA or a list of
|
||||||
XYMAP_DATA_LIST (a list of XYMAP_DATA dicts). Each XYMAP_DATA dict should contain
|
XYMAP_DATA_LIST (a list of XYMAP_DATA dicts). Each XYMAP_DATA dict should contain
|
||||||
`{"xymap": mapstring, "zcoord": mapname/zcoord, "legend": dict, "prototypes": dict}`.
|
`{"xymap": mapstring, "zcoord": mapname/zcoord, "legend": dict, "prototypes": dict}`.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
module (module or str): A module or python-path to a module containing
|
module_path (module_path): A python-path to a module containing
|
||||||
map data as either `XYMAP_DATA` or `XYMAP_DATA_LIST` variables.
|
map data as either `XYMAP_DATA` or `XYMAP_DATA_LIST` variables.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list: List of zero, one or more xy-map data dicts loaded from the module.
|
list: List of zero, one or more xy-map data dicts loaded from the module.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
map_data_list = variable_from_module(module, "XYMAP_DATA_LIST")
|
map_data_list = variable_from_module(module_path, "XYMAP_DATA_LIST")
|
||||||
if not map_data_list:
|
if not map_data_list:
|
||||||
map_data_list = variable_from_module(module, "XYMAP_DATA")
|
map_data_list = variable_from_module(module_path, "XYMAP_DATA")
|
||||||
if map_data_list:
|
if map_data_list:
|
||||||
map_data_list = make_iter(map_data_list)
|
map_data_list = make_iter(map_data_list)
|
||||||
|
# inject the python path in the map data
|
||||||
|
for mapdata in map_data_list:
|
||||||
|
mapdata['module_path'] = module_path
|
||||||
return map_data_list
|
return map_data_list
|
||||||
|
|
||||||
|
def reload(self):
|
||||||
|
"""
|
||||||
|
Reload and rebuild the grid. This is done on a server reload.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.log("(Re)loading grid ...")
|
||||||
|
self.ndb.grid = {}
|
||||||
|
nmaps = 0
|
||||||
|
loaded_mapdata = {}
|
||||||
|
changed = []
|
||||||
|
|
||||||
|
# generate all Maps - this will also initialize their components
|
||||||
|
# and bake any pathfinding paths (or load from disk-cache)
|
||||||
|
for zcoord, old_mapdata in self.db.map_data.items():
|
||||||
|
|
||||||
|
self.log(f"Loading map '{zcoord}'...")
|
||||||
|
|
||||||
|
# we reload the map from module
|
||||||
|
new_mapdata = loaded_mapdata.get(zcoord)
|
||||||
|
if not new_mapdata:
|
||||||
|
if 'module_path' in old_mapdata:
|
||||||
|
for mapdata in self.maps_from_module(old_mapdata['module_path']):
|
||||||
|
loaded_mapdata[mapdata['zcoord']] = mapdata
|
||||||
|
else:
|
||||||
|
# nowhere to reload from - use what we have
|
||||||
|
loaded_mapdata[zcoord] = old_mapdata
|
||||||
|
|
||||||
|
new_mapdata = loaded_mapdata.get(zcoord)
|
||||||
|
|
||||||
|
if new_mapdata != old_mapdata:
|
||||||
|
self.log(f" XYMap data for Z='{zcoord}' has changed.")
|
||||||
|
changed.append(zcoord)
|
||||||
|
|
||||||
|
xymap = XYMap(dict(new_mapdata), Z=zcoord, xyzgrid=self)
|
||||||
|
xymap.parse()
|
||||||
|
xymap.calculate_path_matrix()
|
||||||
|
self.ndb.grid[zcoord] = xymap
|
||||||
|
nmaps += 1
|
||||||
|
|
||||||
|
# re-store changed data
|
||||||
|
for zcoord in changed:
|
||||||
|
self.db.map_data[zcoord] = loaded_mapdata['zcoord']
|
||||||
|
|
||||||
|
# store
|
||||||
|
self.log(f"Loaded and linked {nmaps} map(s).")
|
||||||
|
self.ndb.loaded = True
|
||||||
|
|
||||||
def add_maps(self, *mapdatas):
|
def add_maps(self, *mapdatas):
|
||||||
"""
|
"""
|
||||||
Add map or maps to the grid.
|
Add map or maps to the grid.
|
||||||
|
|
@ -163,9 +189,10 @@ class XYZGrid(DefaultScript):
|
||||||
Args:
|
Args:
|
||||||
*mapdatas (dict): Each argument is a dict structure
|
*mapdatas (dict): Each argument is a dict structure
|
||||||
`{"map": <mapstr>, "legend": <legenddict>, "name": <name>,
|
`{"map": <mapstr>, "legend": <legenddict>, "name": <name>,
|
||||||
"prototypes": <dict-of-dicts>}`. The `prototypes are
|
"prototypes": <dict-of-dicts>, "module_path": <str>}`. The `prototypes are
|
||||||
coordinate-specific overrides for nodes/links on the map, keyed with their
|
coordinate-specific overrides for nodes/links on the map, keyed with their
|
||||||
(X,Y) coordinate within that map.
|
(X,Y) coordinate within that map. The `module_path` is injected automatically
|
||||||
|
by self.maps_from_module.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
RuntimeError: If mapdata is malformed.
|
RuntimeError: If mapdata is malformed.
|
||||||
|
|
|
||||||
|
|
@ -484,7 +484,7 @@ def search_prototype(key=None, tags=None, require_single=False, return_iterators
|
||||||
if key:
|
if key:
|
||||||
if key in mod_matches:
|
if key in mod_matches:
|
||||||
# exact match
|
# exact match
|
||||||
module_prototypes = [mod_matches[key]]
|
module_prototypes = [mod_matches[key].copy()]
|
||||||
allow_fuzzy = False
|
allow_fuzzy = False
|
||||||
else:
|
else:
|
||||||
# fuzzy matching
|
# fuzzy matching
|
||||||
|
|
@ -494,7 +494,9 @@ def search_prototype(key=None, tags=None, require_single=False, return_iterators
|
||||||
if key in prototype_key
|
if key in prototype_key
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
module_prototypes = [match for match in mod_matches.values()]
|
# note - we return a copy of the prototype dict, otherwise using this with e.g.
|
||||||
|
# prototype_from_object will modify the base prototype for every object
|
||||||
|
module_prototypes = [match.copy() for match in mod_matches.values()]
|
||||||
|
|
||||||
# search db-stored prototypes
|
# search db-stored prototypes
|
||||||
|
|
||||||
|
|
@ -1053,7 +1055,8 @@ def value_to_obj(value, force=True):
|
||||||
stype = type(value)
|
stype = type(value)
|
||||||
if is_iter(value):
|
if is_iter(value):
|
||||||
if stype == dict:
|
if stype == dict:
|
||||||
return {value_to_obj_or_any(key): value_to_obj_or_any(val) for key, val in value.iter()}
|
return {value_to_obj_or_any(key): value_to_obj_or_any(val)
|
||||||
|
for key, val in value.items()}
|
||||||
else:
|
else:
|
||||||
return stype([value_to_obj_or_any(val) for val in value])
|
return stype([value_to_obj_or_any(val) for val in value])
|
||||||
return dbid_to_obj(value, ObjectDB)
|
return dbid_to_obj(value, ObjectDB)
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,8 @@ from evennia.prototypes.prototypes import (
|
||||||
|
|
||||||
|
|
||||||
_CREATE_OBJECT_KWARGS = ("key", "location", "home", "destination")
|
_CREATE_OBJECT_KWARGS = ("key", "location", "home", "destination")
|
||||||
_PROTOTYPE_META_NAMES = ("prototype_key", "prototype_desc", "prototype_tags", "prototype_locks")
|
_PROTOTYPE_META_NAMES = ("prototype_key", "prototype_desc", "prototype_tags",
|
||||||
|
"prototype_locks", "prototype_parent")
|
||||||
_PROTOTYPE_ROOT_NAMES = (
|
_PROTOTYPE_ROOT_NAMES = (
|
||||||
"typeclass",
|
"typeclass",
|
||||||
"key",
|
"key",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue