mirror of
https://github.com/evennia/evennia.git
synced 2026-03-30 04:27:16 +02:00
First stepping around xyzgrid, still issues
This commit is contained in:
parent
8c9f7e58ef
commit
d06ae0119a
8 changed files with 159 additions and 16 deletions
|
|
@ -83,7 +83,10 @@ class CmdLook(COMMAND_DEFAULT_CLASS):
|
|||
target = caller.search(self.args)
|
||||
if not target:
|
||||
return
|
||||
self.msg((caller.at_look(target), {"type": "look"}), options=None)
|
||||
desc = caller.at_look(target)
|
||||
# add the type=look to the outputfunc to make it
|
||||
# easy to separate this output in client.
|
||||
self.msg(text=(desc, {"type": "look"}), options=None)
|
||||
|
||||
|
||||
class CmdNick(COMMAND_DEFAULT_CLASS):
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class CmdXYZLook(general.CmdLook):
|
|||
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)
|
||||
self.msg((map_display, {"type": "xymap"}), options=None)
|
||||
# now run the normal look command
|
||||
super().func()
|
||||
|
||||
|
|
@ -55,6 +55,7 @@ class CmdXYZTeleport(building.CmdTeleport):
|
|||
tel/tonone box
|
||||
tel (3, 3, the small cave)
|
||||
tel (4, 1) # on the same map
|
||||
tel/map Z|mapname
|
||||
|
||||
Switches:
|
||||
quiet - don't echo leave/arrive messages to the source/target
|
||||
|
|
@ -67,6 +68,7 @@ class CmdXYZTeleport(building.CmdTeleport):
|
|||
an object from a None location is by direct #dbref
|
||||
reference. A puppeted object cannot be moved to None.
|
||||
loc - teleport object to the target's location instead of its contents
|
||||
map - show coordinate map of given Zcoord/mapname.
|
||||
|
||||
Teleports an object somewhere. If no object is given, you yourself are
|
||||
teleported to the target location. If (X,Y) or (X,Y,Z) coordinates
|
||||
|
|
@ -74,6 +76,7 @@ class CmdXYZTeleport(building.CmdTeleport):
|
|||
|
||||
"""
|
||||
def _search_by_xyz(self, inp):
|
||||
inp = inp.strip("()")
|
||||
X, Y, *Z = inp.split(",", 2)
|
||||
if Z:
|
||||
# Z was specified
|
||||
|
|
@ -93,7 +96,7 @@ class CmdXYZTeleport(building.CmdTeleport):
|
|||
try:
|
||||
self.destination = XYZRoom.objects.get_xyz(xyz=(X, Y, Z))
|
||||
except XYZRoom.DoesNotExist:
|
||||
self.caller.msg("Found no target XYZRoom at ({X},{Y},{Y}).")
|
||||
self.caller.msg(f"Found no target XYZRoom at ({X},{Y},{Z}).")
|
||||
raise InterruptCommand
|
||||
|
||||
def parse(self):
|
||||
|
|
@ -114,7 +117,7 @@ class CmdXYZTeleport(building.CmdTeleport):
|
|||
self.destination = self.caller.search(self.rhs, global_search=True)
|
||||
|
||||
elif self.lhs:
|
||||
if all(char in self.rhs for char in ("(", ")", ",")):
|
||||
if all(char in self.lhs for char in ("(", ")", ",")):
|
||||
self._search_by_xyz(self.lhs)
|
||||
else:
|
||||
self.destination = self.caller.search(self.lhs, global_search=True)
|
||||
|
|
@ -189,6 +192,5 @@ class XYZGridCmdSet(CmdSet):
|
|||
key = "xyzgrid_cmdset"
|
||||
|
||||
def at_cmdset_creation(self):
|
||||
self.add(CmdXYZLook())
|
||||
self.add(CmdXYZTeleport())
|
||||
self.add(CmdXYZOpen())
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ Use `evennia xyzgrid help` for usage help.
|
|||
|
||||
from os.path import join as pathjoin
|
||||
from django.conf import settings
|
||||
from evennia.utils import ansi
|
||||
from evennia.contrib.xyzgrid.xyzgrid import get_xyzgrid
|
||||
|
||||
_HELP_SHORT = """
|
||||
|
|
@ -187,7 +188,7 @@ def _option_list(*suboptions):
|
|||
print("XYMaps stored in grid:")
|
||||
for zcoord, xymap in sorted(xymap_data.items(), key=lambda tup: tup[0]):
|
||||
print("\n" + str(repr(xymap)) + ":\n")
|
||||
print(str(xymap))
|
||||
print(ansi.parse_ansi(str(xymap)))
|
||||
return
|
||||
|
||||
for zcoord in suboptions:
|
||||
|
|
@ -216,7 +217,7 @@ def _option_list(*suboptions):
|
|||
print("Note: This check is not complete; it does not consider changed map "
|
||||
"topology\nlike relocated nodes/rooms and new/removed links/exits - this "
|
||||
"is calculated only during a build.")
|
||||
print("\nDisplayed map (as appearing in-game):\n\n" + 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"
|
||||
+ str(xymap.mapstring))
|
||||
legend = []
|
||||
|
|
|
|||
|
|
@ -1088,6 +1088,7 @@ class MapTransitionMapNode(TransitionMapNode):
|
|||
class NSMapLink(MapLink):
|
||||
"""Two-way, North-South link"""
|
||||
symbol = "|"
|
||||
display_symbol = "||"
|
||||
directions = {"n": "s", "s": "n"}
|
||||
prototype = "xyz_exit"
|
||||
|
||||
|
|
@ -1164,6 +1165,7 @@ class UpMapLink(SmartMapLink):
|
|||
# all movement over this link is 'up', regardless of where on the xygrid we move.
|
||||
direction_aliases = {'n': symbol, 'ne': symbol, 'e': symbol, 'se': symbol,
|
||||
's': symbol, 'sw': symbol, 'w': symbol, 'nw': symbol}
|
||||
spawn_aliases = {direction: ("up", "u") for direction in direction_aliases}
|
||||
prototype = "xyz_exit"
|
||||
|
||||
|
||||
|
|
@ -1173,6 +1175,7 @@ class DownMapLink(UpMapLink):
|
|||
# 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,
|
||||
's': symbol, 'sw': symbol, 'w': symbol, 'nw': symbol}
|
||||
spawn_aliases = {direction: ("down", "do") for direction in direction_aliases}
|
||||
prototype = "xyz_exit"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -774,6 +774,7 @@ class XYMap:
|
|||
character='@',
|
||||
target=None, target_path_style="|y{display_symbol}|n",
|
||||
max_size=None,
|
||||
indent=0,
|
||||
return_str=True):
|
||||
"""
|
||||
Get a part of the grid centered on a specific point and extended a certain number
|
||||
|
|
@ -791,7 +792,7 @@ class XYMap:
|
|||
nodes are reachable or not. If 'nodes', distance measure how many linked nodes
|
||||
away from the center coordinate to display.
|
||||
character (str, optional): Place this symbol at the `xy` position
|
||||
of the displayed map. The center node' symbol is shown if this is falsy.
|
||||
of the displayed map. The center node's symbol is shown if this is falsy.
|
||||
target (tuple, optional): A target XY coordinate to go to. The path to this
|
||||
(or the beginning of said path, if outside of visual range) will be
|
||||
marked according to `target_path_style`.
|
||||
|
|
@ -802,8 +803,11 @@ class XYMap:
|
|||
will receive the MapNode or MapLink object for every step of the path and and
|
||||
must return the suitable string to display at the position of the node/link.
|
||||
max_size (tuple, optional): A max `(width, height)` to crop the displayed
|
||||
return to. Make both odd numbers to get a perfect center.
|
||||
If unset, display-size can grow up to the full size of the grid.
|
||||
return to. Make both odd numbers to get a perfect center. Set either of
|
||||
the tuple values to `None` to make that coordinate unlimited. Set entire
|
||||
tuple to None let display-size able to grow up to full size of grid.
|
||||
indent (int, optional): How far to the right to indent the map area (only
|
||||
applies to `return_str=True`).
|
||||
return_str (bool, optional): Return result as an already formatted string
|
||||
or a 2D list.
|
||||
|
||||
|
|
@ -914,12 +918,15 @@ class XYMap:
|
|||
if max_size:
|
||||
# crop grid to make sure it doesn't grow too far
|
||||
max_x, max_y = max_size
|
||||
max_x = self.max_x if max_x is None else max_x
|
||||
max_y = self.max_y if max_y is None else max_y
|
||||
xmin, xmax = max(0, ixc - max_x // 2), min(width, ixc + max_x // 2 + 1)
|
||||
ymin, ymax = max(0, iyc - max_y // 2), min(height, iyc + max_y // 2 + 1)
|
||||
gridmap = [line[xmin:xmax] for line in gridmap[ymin:ymax]]
|
||||
|
||||
if return_str:
|
||||
# we must flip the y-axis before returning the string
|
||||
return "\n".join("".join(line) for line in gridmap[::-1])
|
||||
indent = indent * " "
|
||||
return indent + f"\n{indent}".join("".join(line) for line in gridmap[::-1])
|
||||
else:
|
||||
return gridmap
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ class XYZGrid(DefaultScript):
|
|||
|
||||
"""
|
||||
self.db.map_data = {}
|
||||
self.desc = "Manages maps for XYZ-grid"
|
||||
|
||||
@property
|
||||
def grid(self):
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ used as stand-alone XYZ-coordinate-aware rooms.
|
|||
"""
|
||||
|
||||
from django.db.models import Q
|
||||
from django.conf import settings
|
||||
from evennia.objects.objects import DefaultRoom, DefaultExit
|
||||
from evennia.objects.manager import ObjectManager
|
||||
|
||||
|
|
@ -226,11 +227,34 @@ class XYZRoom(DefaultRoom):
|
|||
"""
|
||||
A game location aware of its XYZ-position.
|
||||
|
||||
Special properties:
|
||||
map_display (bool): If the return_appearance of the room should
|
||||
show the map or not.
|
||||
map_mode (str): One of 'nodes' or 'scan'. See `return_apperance`
|
||||
for examples of how they differ.
|
||||
map_visual_range (int): How far on the map one can see. This is a
|
||||
fixed value here, but could also be dynamic based on skills,
|
||||
light etc.
|
||||
map_character_symbol (str): The character symbol to use to show
|
||||
the character position. Can contain color info. Default is
|
||||
the @-character.
|
||||
map_area_client (bool): If True, map area will always fill the entire
|
||||
client width. If False, the map area's width will vary with the
|
||||
width of the currently displayed location description.
|
||||
|
||||
"""
|
||||
|
||||
# makes the `room.objects.filter_xymap` available
|
||||
objects = XYZManager()
|
||||
|
||||
# default settings for map visualization
|
||||
map_display = True
|
||||
map_mode = 'nodes' # or 'scan'
|
||||
map_visual_range = 2
|
||||
map_character_symbol = "@"
|
||||
map_align = 'c'
|
||||
map_area_client = True
|
||||
|
||||
def __str__(self):
|
||||
return repr(self)
|
||||
|
||||
|
|
@ -252,10 +276,12 @@ class XYZRoom(DefaultRoom):
|
|||
y = self.tags.get(category=MAP_Y_TAG_CATEGORY, return_list=False)
|
||||
z = self.tags.get(category=MAP_Z_TAG_CATEGORY, return_list=False)
|
||||
if x is None or y is None or z is None:
|
||||
# don't cache unfinished coordinate
|
||||
return (x, y, z)
|
||||
# cache result
|
||||
self._xyz = (x, y, z)
|
||||
# don't cache unfinished coordinate (probably tags have not finished saving)
|
||||
return tuple(int(coord) if coord is not None and coord.isdigit() else coord
|
||||
for coord in (x, y, z))
|
||||
# cache result, convert to correct types (tags are strings)
|
||||
self._xyz = tuple(int(coord) if coord.isdigit() else coord for coord in (x, y, z))
|
||||
|
||||
return self._xyz
|
||||
|
||||
@classmethod
|
||||
|
|
@ -302,6 +328,106 @@ class XYZRoom(DefaultRoom):
|
|||
|
||||
return DefaultRoom.create(key, account=account, tags=tags, typeclass=cls, **kwargs)
|
||||
|
||||
def return_appearance(self, looker, **kwargs):
|
||||
"""
|
||||
Displays the map in addition to the room description
|
||||
|
||||
Args:
|
||||
looker (Object): The one looking.
|
||||
|
||||
Keyword Args:
|
||||
map_display (bool): Turn on/off map display.
|
||||
map_visual_range (int): How 'far' one can see on the map. For
|
||||
'nodes' mode, this is how many connected nodes away, for
|
||||
'scan' mode, this is number of characters away on the map.
|
||||
Default is a visual range of 2 (nodes).
|
||||
map_mode (str): One of 'node' (default) or 'scan'.
|
||||
map_character_symbol (str): The character symbol to use. Defaults to '@'.
|
||||
This can also be colored with standard color tags. Set to `None`
|
||||
to just show the current node.
|
||||
|
||||
Examples:
|
||||
|
||||
Assume this is the full map (where '@' is the character location):
|
||||
::
|
||||
#----------------#
|
||||
| |
|
||||
| |
|
||||
# @------------#-#
|
||||
| |
|
||||
#----------------#
|
||||
|
||||
This is how it will look in 'nodes' mode with `visual_range=2`:
|
||||
::
|
||||
@------------#-#
|
||||
|
||||
And in 'scan' mode with `visual_range=2`:
|
||||
::
|
||||
|
|
||||
|
|
||||
# @--
|
||||
|
|
||||
#----
|
||||
|
||||
Notes:
|
||||
The map kwargs default to values with the same names set on the
|
||||
XYZRoom class; these can be changed by overriding the room.
|
||||
|
||||
We return the map display as a separate msg() call here, in order
|
||||
to make it easier to break this out into a client pane etc. The
|
||||
map is tagged with type='xymap'.
|
||||
|
||||
"""
|
||||
|
||||
# normal get_appearance of a room
|
||||
room_desc = super().return_appearance(looker, **kwargs)
|
||||
|
||||
if kwargs.get('map_display', self.map_display):
|
||||
# show the near-area map.
|
||||
|
||||
character_symbol = kwargs.get('map_character_symbol', self.map_character_symbol)
|
||||
visual_range = kwargs.get("visual_range", self.map_visual_range)
|
||||
map_mode = kwargs.get("map_mode", self.map_mode)
|
||||
map_align = kwargs.get("map_align", self.map_align)
|
||||
map_area_client = kwargs.get("fill_width", self.map_area_client)
|
||||
client_width, _ = looker.sessions.get()[0].get_client_size()
|
||||
|
||||
# get current xymap
|
||||
xyz = self.xyz
|
||||
xymap = self.xyzgrid.get_map(xyz[2])
|
||||
map_width = xymap.max_x
|
||||
|
||||
if map_area_client:
|
||||
display_width = client_width
|
||||
else:
|
||||
display_width = max(map_width,
|
||||
max(len(line) for line in room_desc.split("\n")))
|
||||
|
||||
# align map
|
||||
map_indent = 0
|
||||
sep_width = display_width
|
||||
if map_align == 'r':
|
||||
map_indent = max(0, display_width - map_width)
|
||||
elif map_align == 'c':
|
||||
map_indent = max(0, (display_width - map_width) // 2)
|
||||
|
||||
# get visual range display from map
|
||||
map_display = xymap.get_visual_range(
|
||||
(xyz[0], xyz[1]),
|
||||
dist=visual_range,
|
||||
mode=map_mode,
|
||||
character=character_symbol,
|
||||
max_size=(display_width, None),
|
||||
indent=map_indent
|
||||
)
|
||||
sep = "~" * sep_width
|
||||
map_display = f"|x{sep}|n\n{map_display}\n|x{sep}"
|
||||
|
||||
# echo directly to make easier to separate in client
|
||||
looker.msg(text=(map_display, {"type": "xymap"}), options=None)
|
||||
|
||||
return room_desc
|
||||
|
||||
|
||||
class XYZExit(DefaultExit):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -2269,7 +2269,7 @@ class DefaultCharacter(DefaultObject):
|
|||
|
||||
"""
|
||||
if self.location.access(self, "view"):
|
||||
self.msg(self.at_look(self.location))
|
||||
self.msg(text=(self.at_look(self.location), {"type": "look"}))
|
||||
|
||||
def at_pre_puppet(self, account, session=None, **kwargs):
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue