More fixes to xyzmaps. Add goto
This commit is contained in:
parent
de66313ec9
commit
5edda10e81
6 changed files with 144 additions and 38 deletions
|
|
@ -7,38 +7,15 @@ the commands with XYZ-aware equivalents.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from evennia import InterruptCommand
|
from evennia import InterruptCommand
|
||||||
from evennia import default_cmds, CmdSet
|
from evennia import default_cmds, CmdSet
|
||||||
from evennia.commands.default import building, general
|
from evennia.commands.default import building
|
||||||
from evennia.contrib.xyzgrid.xyzroom import XYZRoom
|
from evennia.contrib.xyzgrid.xyzroom import XYZRoom
|
||||||
from evennia.utils.utils import inherits_from
|
from evennia.contrib.xyzgrid.xyzgrid import get_xyzgrid
|
||||||
|
from evennia.utils.utils import list_to_string, class_from_module, make_iter
|
||||||
|
|
||||||
|
COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS)
|
||||||
class CmdXYZLook(general.CmdLook):
|
|
||||||
|
|
||||||
character = '@'
|
|
||||||
visual_range = 2
|
|
||||||
map_mode = 'nodes' # or 'scan'
|
|
||||||
|
|
||||||
def func(self):
|
|
||||||
"""
|
|
||||||
Add xymap display before the normal look command.
|
|
||||||
|
|
||||||
"""
|
|
||||||
location = self.caller.location
|
|
||||||
if inherits_from(location, XYZRoom):
|
|
||||||
xyz = location.xyz
|
|
||||||
xymap = location.xyzgrid.get_map(xyz[2])
|
|
||||||
map_display = xymap.get_visual_range(
|
|
||||||
(xyz[0], xyz[1]),
|
|
||||||
dist=self.visual_range,
|
|
||||||
mode=self.map_mode)
|
|
||||||
maxw = min(xymap.max_x, self.client_width())
|
|
||||||
sep = "~" * maxw
|
|
||||||
map_display = f"|x{sep}|n\n{map_display}\n|x{sep}"
|
|
||||||
self.msg((map_display, {"type": "xymap"}), options=None)
|
|
||||||
# now run the normal look command
|
|
||||||
super().func()
|
|
||||||
|
|
||||||
|
|
||||||
class CmdXYZTeleport(building.CmdTeleport):
|
class CmdXYZTeleport(building.CmdTeleport):
|
||||||
|
|
@ -184,6 +161,106 @@ class CmdXYZOpen(building.CmdOpen):
|
||||||
self.exit_typeclass = self.lhs_objs[0]["option"]
|
self.exit_typeclass = self.lhs_objs[0]["option"]
|
||||||
|
|
||||||
|
|
||||||
|
class CmdGoto(COMMAND_DEFAULT_CLASS):
|
||||||
|
"""
|
||||||
|
Go to a named location in this area.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
goto <location> - get path and start walking
|
||||||
|
path <location> - just check the path
|
||||||
|
goto - abort current goto
|
||||||
|
path - show current path
|
||||||
|
|
||||||
|
This will find the shortest route to a location in your current area and
|
||||||
|
start automatically walk you there. Builders can also specify a specific grid
|
||||||
|
coordinate (X,Y).
|
||||||
|
|
||||||
|
"""
|
||||||
|
key = "goto"
|
||||||
|
aliases = "path"
|
||||||
|
help_category = "General"
|
||||||
|
locks = "cmd:all()"
|
||||||
|
|
||||||
|
def _search_by_xyz(self, inp, xyz_start):
|
||||||
|
inp = inp.strip("()")
|
||||||
|
X, Y = inp.split(",", 2)
|
||||||
|
Z = xyz_start[2]
|
||||||
|
# search by coordinate
|
||||||
|
X, Y, Z = str(X).strip(), str(Y).strip(), str(Z).strip()
|
||||||
|
try:
|
||||||
|
return XYZRoom.objects.get_xyz(xyz=(X, Y, Z))
|
||||||
|
except XYZRoom.DoesNotExist:
|
||||||
|
self.caller.msg(f"Could not find a room at ({X},{Y}) (Z={Z}).")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _search_by_key_and_alias(self, inp, xyz_start):
|
||||||
|
Z = xyz_start[2]
|
||||||
|
candidates = list(XYZRoom.objects.filter_xyz(xyz=('*', '*', Z)))
|
||||||
|
return self.caller.search(inp, candidates=candidates)
|
||||||
|
|
||||||
|
def func(self):
|
||||||
|
"""
|
||||||
|
Implement command
|
||||||
|
"""
|
||||||
|
|
||||||
|
caller = self.caller
|
||||||
|
|
||||||
|
current_target, *current_path = make_iter(caller.ndb.xy_current_goto)
|
||||||
|
goto_mode = self.cmdname == 'goto'
|
||||||
|
|
||||||
|
if not self.args:
|
||||||
|
if current_target:
|
||||||
|
if goto_mode:
|
||||||
|
caller.ndb.xy_current_goto_target = None
|
||||||
|
caller.msg("Aborted goto.")
|
||||||
|
else:
|
||||||
|
caller.msg(f"Remaining steps: {list_to_string(current_path)}")
|
||||||
|
else:
|
||||||
|
caller.msg("Usage: goto <location>")
|
||||||
|
return
|
||||||
|
|
||||||
|
xyzgrid = get_xyzgrid()
|
||||||
|
try:
|
||||||
|
xyz_start = caller.location.xyz
|
||||||
|
except AttributeError:
|
||||||
|
self.caller.msg("Cannot path-find since the current location is not on the grid.")
|
||||||
|
return
|
||||||
|
|
||||||
|
allow_xyz_query = caller.locks.check_lockstring(caller, "perm(Builder)")
|
||||||
|
if allow_xyz_query and all(char in self.args for char in ("(", ")", ",")):
|
||||||
|
# search by (X,Y)
|
||||||
|
target = self._search_by_xyz(self.args, xyz_start)
|
||||||
|
if not target:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
# search by normal key/alias
|
||||||
|
target = self._search_by_key_and_alias(self.args, xyz_start)
|
||||||
|
if not target:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
xyz_end = target.xyz
|
||||||
|
except AttributeError:
|
||||||
|
self.caller.msg("Target location is not on the grid and cannot be auto-walked to.")
|
||||||
|
return
|
||||||
|
|
||||||
|
xymap = xyzgrid.get_map(xyz_start[2])
|
||||||
|
# we only need the xy coords once we have the map
|
||||||
|
xy_start = xyz_start[:2]
|
||||||
|
xy_end = xyz_end[:2]
|
||||||
|
shortest_path, _ = xymap.get_shortest_path(xy_start, xy_end)
|
||||||
|
|
||||||
|
caller.msg(f"There are {len(shortest_path)} steps to {target.get_display_name(caller)}: "
|
||||||
|
f"|w{list_to_string(shortest_path, endsep='|nand finally|w')}|n")
|
||||||
|
|
||||||
|
# store for use by the return_appearance hook on the XYZRoom
|
||||||
|
caller.ndb.xy_current_goto = (xy_end, shortest_path)
|
||||||
|
|
||||||
|
if self.cmdname == "goto":
|
||||||
|
# start actually walking right away
|
||||||
|
self.msg("Walking ... eventually")
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class XYZGridCmdSet(CmdSet):
|
class XYZGridCmdSet(CmdSet):
|
||||||
"""
|
"""
|
||||||
Cmdset for easily adding the above cmds to the character cmdset.
|
Cmdset for easily adding the above cmds to the character cmdset.
|
||||||
|
|
@ -194,3 +271,4 @@ class XYZGridCmdSet(CmdSet):
|
||||||
def at_cmdset_creation(self):
|
def at_cmdset_creation(self):
|
||||||
self.add(CmdXYZTeleport())
|
self.add(CmdXYZTeleport())
|
||||||
self.add(CmdXYZOpen())
|
self.add(CmdXYZOpen())
|
||||||
|
self.add(CmdGoto())
|
||||||
|
|
|
||||||
|
|
@ -320,7 +320,7 @@ def _option_delete(*suboptions):
|
||||||
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."
|
||||||
"\nThis can't be undone. Are you sure you want to continue? Y/[N]?")
|
"\nThis can't be undone. Are you sure you want to continue? Y/[N]? ")
|
||||||
if repl.lower() not in ('yes', 'y'):
|
if repl.lower() not in ('yes', 'y'):
|
||||||
print("Aborted.")
|
print("Aborted.")
|
||||||
return
|
return
|
||||||
|
|
@ -342,7 +342,7 @@ def _option_delete(*suboptions):
|
||||||
repl = input("This will delete map(s) {', '.join(zcoords)} and wipe all corresponding\n"
|
repl = input("This will delete map(s) {', '.join(zcoords)} and wipe all corresponding\n"
|
||||||
"rooms/exits!"
|
"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."
|
||||||
"\nThis can't be undone. Are you sure you want to continue? Y/[N]?")
|
"\nThis can't be undone. Are you sure you want to continue? Y/[N]? ")
|
||||||
if repl.lower() not in ('yes', 'y'):
|
if repl.lower() not in ('yes', 'y'):
|
||||||
print("Aborted.")
|
print("Aborted.")
|
||||||
return
|
return
|
||||||
|
|
@ -378,3 +378,6 @@ def xyzcommand(*args):
|
||||||
_option_initpath(*suboptions)
|
_option_initpath(*suboptions)
|
||||||
elif option == 'delete':
|
elif option == 'delete':
|
||||||
_option_delete(*suboptions)
|
_option_delete(*suboptions)
|
||||||
|
else:
|
||||||
|
print(f"Unknown option '{option}'. Use 'evennia xyzgrid help' for valid arguments.")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -326,8 +326,9 @@ class MapNode:
|
||||||
|
|
||||||
maplinks = {}
|
maplinks = {}
|
||||||
for direction, link in self.first_links.items():
|
for direction, link in self.first_links.items():
|
||||||
|
|
||||||
key, *aliases = (
|
key, *aliases = (
|
||||||
make_iter(link.spawn_aliases)
|
link.spawn_aliases.get(direction, ('unknown',))
|
||||||
if link.spawn_aliases
|
if link.spawn_aliases
|
||||||
else self.direction_spawn_defaults.get(direction, ('unknown',))
|
else self.direction_spawn_defaults.get(direction, ('unknown',))
|
||||||
)
|
)
|
||||||
|
|
@ -368,7 +369,7 @@ class MapNode:
|
||||||
if err:
|
if err:
|
||||||
raise RuntimeError(err)
|
raise RuntimeError(err)
|
||||||
linkobjs[key.lower()] = exi
|
linkobjs[key.lower()] = exi
|
||||||
self.log(f" spawning/updating exit xyz={xyz}, direction={direction}")
|
self.log(f" spawning/updating exit xyz={xyz}, direction={key}")
|
||||||
|
|
||||||
# apply prototypes to catch any changes
|
# apply prototypes to catch any changes
|
||||||
for key, linkobj in linkobjs.items():
|
for key, linkobj in linkobjs.items():
|
||||||
|
|
@ -1175,7 +1176,7 @@ class DownMapLink(UpMapLink):
|
||||||
# all movement over this link is 'down', regardless of where on the xygrid we move.
|
# all movement over this link is 'down', regardless of where on the xygrid we move.
|
||||||
direction_aliases = {'n': symbol, 'ne': symbol, 'e': symbol, 'se': symbol,
|
direction_aliases = {'n': symbol, 'ne': symbol, 'e': symbol, 'se': symbol,
|
||||||
's': symbol, 'sw': symbol, 'w': symbol, 'nw': symbol}
|
's': symbol, 'sw': symbol, 'w': symbol, 'nw': symbol}
|
||||||
spawn_aliases = {direction: ("down", "do") for direction in direction_aliases}
|
spawn_aliases = {direction: ("down", "d") for direction in direction_aliases}
|
||||||
prototype = "xyz_exit"
|
prototype = "xyz_exit"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -772,7 +772,8 @@ class XYMap:
|
||||||
|
|
||||||
def get_visual_range(self, xy, 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,
|
||||||
indent=0,
|
indent=0,
|
||||||
return_str=True):
|
return_str=True):
|
||||||
|
|
@ -797,7 +798,7 @@ class XYMap:
|
||||||
(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
|
||||||
marked according to `target_path_style`.
|
marked according to `target_path_style`.
|
||||||
target_path_style (str or callable, optional): This is use for marking the path
|
target_path_style (str or callable, optional): This is use for marking the path
|
||||||
found when `path_to_coord` is given. If a string, it accepts a formatting marker
|
found when `target` is given. If a string, it accepts a formatting marker
|
||||||
`display_symbol` which will be filled with the `display_symbol` of each node/link
|
`display_symbol` which will be filled with the `display_symbol` of each node/link
|
||||||
the path passes through. This allows e.g. to color the path. If a callable, this
|
the path passes through. This allows e.g. to color the path. If a callable, this
|
||||||
will receive the MapNode or MapLink object for every step of the path and and
|
will receive the MapNode or MapLink object for every step of the path and and
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,9 @@ used as stand-alone XYZ-coordinate-aware rooms.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.conf import settings
|
|
||||||
from evennia.objects.objects import DefaultRoom, DefaultExit
|
from evennia.objects.objects import DefaultRoom, DefaultExit
|
||||||
from evennia.objects.manager import ObjectManager
|
from evennia.objects.manager import ObjectManager
|
||||||
|
from evennia.utils.utils import make_iter
|
||||||
|
|
||||||
# name of all tag categories. Note that the Z-coordinate is
|
# name of all tag categories. Note that the Z-coordinate is
|
||||||
# the `map_name` of the XYZgrid
|
# the `map_name` of the XYZgrid
|
||||||
|
|
@ -328,6 +328,25 @@ class XYZRoom(DefaultRoom):
|
||||||
|
|
||||||
return DefaultRoom.create(key, account=account, tags=tags, typeclass=cls, **kwargs)
|
return DefaultRoom.create(key, account=account, tags=tags, typeclass=cls, **kwargs)
|
||||||
|
|
||||||
|
def get_display_name(self, looker, **kwargs):
|
||||||
|
"""
|
||||||
|
Shows both the #dbref and the xyz coord to staff.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
looker (TypedObject): The object or account that is looking
|
||||||
|
at/getting inforamtion for this object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
name (str): A string containing the name of the object,
|
||||||
|
including the DBREF and XYZ coord if this user is
|
||||||
|
privileged to control the room.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if self.locks.check_lockstring(looker, "perm(Builder)"):
|
||||||
|
x, y, z = self.xyz
|
||||||
|
return f"{self.name}[#{self.id}({x},{y},{z})]"
|
||||||
|
return self.name
|
||||||
|
|
||||||
def return_appearance(self, looker, **kwargs):
|
def return_appearance(self, looker, **kwargs):
|
||||||
"""
|
"""
|
||||||
Displays the map in addition to the room description
|
Displays the map in addition to the room description
|
||||||
|
|
@ -411,12 +430,16 @@ class XYZRoom(DefaultRoom):
|
||||||
elif map_align == 'c':
|
elif map_align == 'c':
|
||||||
map_indent = max(0, (display_width - map_width) // 2)
|
map_indent = max(0, (display_width - map_width) // 2)
|
||||||
|
|
||||||
|
goto_target, *current_path = make_iter(looker.ndb.xy_current_goto)
|
||||||
|
|
||||||
# get visual range display from map
|
# get visual range display from map
|
||||||
map_display = xymap.get_visual_range(
|
map_display = xymap.get_visual_range(
|
||||||
(xyz[0], xyz[1]),
|
(xyz[0], xyz[1]),
|
||||||
dist=visual_range,
|
dist=visual_range,
|
||||||
mode=map_mode,
|
mode=map_mode,
|
||||||
character=character_symbol,
|
target=goto_target,
|
||||||
|
target_path_style="|y{display_symbol}|n",
|
||||||
|
character=f"|g{character_symbol}|n",
|
||||||
max_size=(display_width, None),
|
max_size=(display_width, None),
|
||||||
indent=map_indent
|
indent=map_indent
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1260,7 +1260,7 @@ class DbHolder:
|
||||||
_GA(self, _GA(self, "name")).remove(attrname)
|
_GA(self, _GA(self, "name")).remove(attrname)
|
||||||
|
|
||||||
def get_all(self):
|
def get_all(self):
|
||||||
return _GA(self, _GA(self, "name")).get_all_attributes()
|
return _GA(self, _GA(self, "name")).backend.get_all_attributes()
|
||||||
|
|
||||||
all = property(get_all)
|
all = property(get_all)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue