mirror of
https://github.com/evennia/evennia.git
synced 2026-03-30 12:37:16 +02:00
Add xyzgrid support commands
This commit is contained in:
parent
1c6fffeff2
commit
578e6d63e1
9 changed files with 345 additions and 100 deletions
|
|
@ -4,6 +4,7 @@ Building and world design commands
|
|||
import re
|
||||
from django.conf import settings
|
||||
from django.db.models import Q, Min, Max
|
||||
from evennia import InterruptCommand
|
||||
from evennia.objects.models import ObjectDB
|
||||
from evennia.locks.lockhandler import LockException
|
||||
from evennia.commands.cmdhandler import get_and_merge_cmdsets
|
||||
|
|
@ -1487,40 +1488,33 @@ class CmdOpen(ObjManipCommand):
|
|||
caller.msg(string)
|
||||
return exit_obj
|
||||
|
||||
def parse(self):
|
||||
super().parse()
|
||||
self.location = self.caller.location
|
||||
if not self.args or not self.rhs:
|
||||
self.caller.msg("Usage: open <new exit>[;alias...][:typeclass]"
|
||||
"[,<return exit>[;alias..][:typeclass]]] "
|
||||
"= <destination>")
|
||||
raise InterruptCommand
|
||||
if not self.location:
|
||||
self.caller.msg("You cannot create an exit from a None-location.")
|
||||
raise InterruptCommand
|
||||
self.destination = self.caller.search(self.rhs, global_search=True)
|
||||
if not self.destination:
|
||||
raise InterruptCommand
|
||||
self.exit_name = self.lhs_objs[0]["name"]
|
||||
self.exit_aliases = self.lhs_objs[0]["aliases"]
|
||||
self.exit_typeclass = self.lhs_objs[0]["option"]
|
||||
|
||||
def func(self):
|
||||
"""
|
||||
This is where the processing starts.
|
||||
Uses the ObjManipCommand.parser() for pre-processing
|
||||
as well as the self.create_exit() method.
|
||||
"""
|
||||
caller = self.caller
|
||||
|
||||
if not self.args or not self.rhs:
|
||||
string = "Usage: open <new exit>[;alias...][:typeclass][,<return exit>[;alias..][:typeclass]]] "
|
||||
string += "= <destination>"
|
||||
caller.msg(string)
|
||||
return
|
||||
|
||||
# We must have a location to open an exit
|
||||
location = caller.location
|
||||
if not location:
|
||||
caller.msg("You cannot create an exit from a None-location.")
|
||||
return
|
||||
|
||||
# obtain needed info from cmdline
|
||||
|
||||
exit_name = self.lhs_objs[0]["name"]
|
||||
exit_aliases = self.lhs_objs[0]["aliases"]
|
||||
exit_typeclass = self.lhs_objs[0]["option"]
|
||||
dest_name = self.rhs
|
||||
|
||||
# first, check so the destination exists.
|
||||
destination = caller.search(dest_name, global_search=True)
|
||||
if not destination:
|
||||
return
|
||||
|
||||
# Create exit
|
||||
ok = self.create_exit(exit_name, location, destination, exit_aliases, exit_typeclass)
|
||||
ok = self.create_exit(self.exit_name, self.location, self.destination,
|
||||
self.exit_aliases, self.exit_typeclass)
|
||||
if not ok:
|
||||
# an error; the exit was not created, so we quit.
|
||||
return
|
||||
|
|
@ -1529,9 +1523,8 @@ class CmdOpen(ObjManipCommand):
|
|||
back_exit_name = self.lhs_objs[1]["name"]
|
||||
back_exit_aliases = self.lhs_objs[1]["aliases"]
|
||||
back_exit_typeclass = self.lhs_objs[1]["option"]
|
||||
self.create_exit(
|
||||
back_exit_name, destination, location, back_exit_aliases, back_exit_typeclass
|
||||
)
|
||||
self.create_exit(back_exit_name, self.destination, self.location, back_exit_aliases,
|
||||
back_exit_typeclass)
|
||||
|
||||
|
||||
def _convert_from_string(cmd, strobj):
|
||||
|
|
@ -2981,28 +2974,31 @@ class CmdTeleport(COMMAND_DEFAULT_CLASS):
|
|||
locks = "cmd:perm(teleport) or perm(Builder)"
|
||||
help_category = "Building"
|
||||
|
||||
def parse(self):
|
||||
"""
|
||||
Breaking out searching here to make this easier to override.
|
||||
|
||||
"""
|
||||
super().parse()
|
||||
self.obj_to_teleport = self.caller
|
||||
self.destination = None
|
||||
if self.lhs:
|
||||
self.obj_to_teleport = self.caller.search(self.lhs, global_search=True)
|
||||
if not self.obj_to_teleport:
|
||||
self.caller.msg("Did not find object to teleport.")
|
||||
raise InterruptCommand
|
||||
if self.rhs:
|
||||
self.destination = self.caller.search(self.rhs, global_search=True)
|
||||
|
||||
def func(self):
|
||||
"""Performs the teleport"""
|
||||
|
||||
caller = self.caller
|
||||
args = self.args
|
||||
lhs, rhs = self.lhs, self.rhs
|
||||
switches = self.switches
|
||||
obj_to_teleport = self.obj_to_teleport
|
||||
destination = self.destination
|
||||
|
||||
# setting switches
|
||||
tel_quietly = "quiet" in switches
|
||||
to_none = "tonone" in switches
|
||||
to_loc = "loc" in switches
|
||||
|
||||
if to_none:
|
||||
if "tonone" in self.switches:
|
||||
# teleporting to None
|
||||
if not args:
|
||||
obj_to_teleport = caller
|
||||
else:
|
||||
obj_to_teleport = caller.search(lhs, global_search=True)
|
||||
if not obj_to_teleport:
|
||||
caller.msg("Did not find object to teleport.")
|
||||
return
|
||||
if obj_to_teleport.has_account:
|
||||
caller.msg(
|
||||
"Cannot teleport a puppeted object "
|
||||
|
|
@ -3011,53 +3007,44 @@ class CmdTeleport(COMMAND_DEFAULT_CLASS):
|
|||
)
|
||||
return
|
||||
caller.msg("Teleported %s -> None-location." % obj_to_teleport)
|
||||
if obj_to_teleport.location and not tel_quietly:
|
||||
if obj_to_teleport.location and "quiet" not in self.switches:
|
||||
obj_to_teleport.location.msg_contents(
|
||||
"%s teleported %s into nothingness." % (caller, obj_to_teleport), exclude=caller
|
||||
)
|
||||
obj_to_teleport.location = None
|
||||
return
|
||||
|
||||
# not teleporting to None location
|
||||
if not args and not to_none:
|
||||
caller.msg("Usage: teleport[/switches] [<obj> =] <target_loc>||home")
|
||||
return
|
||||
|
||||
if rhs:
|
||||
obj_to_teleport = caller.search(lhs, global_search=True)
|
||||
destination = caller.search(rhs, global_search=True)
|
||||
else:
|
||||
obj_to_teleport = caller
|
||||
destination = caller.search(lhs, global_search=True)
|
||||
if not obj_to_teleport:
|
||||
caller.msg("Did not find object to teleport.")
|
||||
if not self.args:
|
||||
caller.msg("Usage: teleport[/switches] [<obj> =] <target or (X,Y,Z)>||home")
|
||||
return
|
||||
|
||||
if not destination:
|
||||
caller.msg("Destination not found.")
|
||||
return
|
||||
if to_loc:
|
||||
|
||||
if "loc" in self.switches:
|
||||
destination = destination.location
|
||||
if not destination:
|
||||
caller.msg("Destination has no location.")
|
||||
return
|
||||
|
||||
if obj_to_teleport == destination:
|
||||
caller.msg("You can't teleport an object inside of itself!")
|
||||
return
|
||||
|
||||
if obj_to_teleport == destination.location:
|
||||
caller.msg("You can't teleport an object inside something it holds!")
|
||||
return
|
||||
|
||||
if obj_to_teleport.location and obj_to_teleport.location == destination:
|
||||
caller.msg("%s is already at %s." % (obj_to_teleport, destination))
|
||||
return
|
||||
use_destination = True
|
||||
if "intoexit" in self.switches:
|
||||
use_destination = False
|
||||
|
||||
# try the teleport
|
||||
if obj_to_teleport.move_to(
|
||||
destination, quiet=tel_quietly, emit_to_obj=caller, use_destination=use_destination
|
||||
):
|
||||
destination, quiet="quiet" in self.switches,
|
||||
emit_to_obj=caller, use_destination="intoexit" not in self.switches):
|
||||
|
||||
if obj_to_teleport == caller:
|
||||
caller.msg("Teleported to %s." % destination)
|
||||
else:
|
||||
|
|
|
|||
187
evennia/contrib/xyzgrid/commands.py
Normal file
187
evennia/contrib/xyzgrid/commands.py
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
"""
|
||||
|
||||
XYZ-aware commands
|
||||
|
||||
Just add the XYZGridCmdSet to the default character cmdset to override
|
||||
the commands with XYZ-aware equivalents.
|
||||
|
||||
"""
|
||||
|
||||
from evennia import InterruptCommand
|
||||
from evennia import MuxCommand, CmdSet
|
||||
from evennia.commands.default import building, general
|
||||
from evennia.contrib.xyzgrid.xyzroom import XYZRoom
|
||||
from evennia.utils.utils import inherits_from
|
||||
|
||||
|
||||
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):
|
||||
"""
|
||||
teleport object to another location
|
||||
|
||||
Usage:
|
||||
tel/switch [<object> to||=] <target location>
|
||||
tel/switch [<object> to||=] (X,Y[,Z])
|
||||
|
||||
Examples:
|
||||
tel Limbo
|
||||
tel/quiet box = Limbo
|
||||
tel/tonone box
|
||||
tel (3, 3, the small cave)
|
||||
tel (4, 1) # on the same map
|
||||
|
||||
Switches:
|
||||
quiet - don't echo leave/arrive messages to the source/target
|
||||
locations for the move.
|
||||
intoexit - if target is an exit, teleport INTO
|
||||
the exit object instead of to its destination
|
||||
tonone - if set, teleport the object to a None-location. If this
|
||||
switch is set, <target location> is ignored.
|
||||
Note that the only way to retrieve
|
||||
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
|
||||
|
||||
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
|
||||
are given, the target is a location on the XYZGrid.
|
||||
|
||||
"""
|
||||
|
||||
def parse(self):
|
||||
MuxCommand.parse(self)
|
||||
self.obj_to_teleport = self.caller
|
||||
self.destination = None
|
||||
rhs = self.rhs
|
||||
if self.lhs:
|
||||
self.obj_to_teleport = self.caller.search(self.lhs, global_search=True)
|
||||
if not self.obj_to_teleport:
|
||||
self.caller.msg("Did not find object to teleport.")
|
||||
raise InterruptCommand
|
||||
if rhs:
|
||||
if all(char in rhs for char in ("(", ")", ",")):
|
||||
# search by (X,Y) or (X,Y,Z)
|
||||
X, Y, *Z = rhs.split(",", 2)
|
||||
if Z:
|
||||
# Z was specified
|
||||
Z = Z[0]
|
||||
else:
|
||||
# use current location's Z, if it exists
|
||||
try:
|
||||
xyz = self.caller.xyz
|
||||
except AttributeError:
|
||||
self.caller.msg("Z-coordinate is also required since you are not currently "
|
||||
"in a room with a Z coordinate of its own.")
|
||||
raise InterruptCommand
|
||||
else:
|
||||
Z = xyz[2]
|
||||
# search by coordinate
|
||||
X, Y, Z = str(X).strip(), str(Y).strip(), str(Z).strip()
|
||||
try:
|
||||
self.obj_to_teleport = XYZRoom.objects.get_xyz(xyz=(X, Y, Z))
|
||||
except XYZRoom.DoesNotExist:
|
||||
self.caller.msg("Found no target XYZRoom at ({X},{Y},{Y}).")
|
||||
raise InterruptCommand
|
||||
else:
|
||||
# regular search
|
||||
self.destination = self.caller.search(rhs, global_search=True)
|
||||
|
||||
|
||||
class CmdXYZOpen(building.CmdOpen):
|
||||
"""
|
||||
open a new exit from the current room
|
||||
|
||||
Usage:
|
||||
open <new exit>[;alias;..][:typeclass] [,<return exit>[;alias;..][:typeclass]]] = <destination>
|
||||
open <new exit>[;alias;..][:typeclass] [,<return exit>[;alias;..][:typeclass]]] = (X,Y,Z)
|
||||
|
||||
Handles the creation of exits. If a destination is given, the exit
|
||||
will point there. The destination can also be given as an (X,Y,Z) coordinate on the
|
||||
XYZGrid - this command is used to link non-grid rooms to the grid and vice-versa.
|
||||
|
||||
The <return exit> argument sets up an exit at the destination leading back to the current room.
|
||||
Apart from (X,Y,Z) coordinate, destination name can be given both as a #dbref and a name, if
|
||||
that name is globally unique.
|
||||
|
||||
Examples:
|
||||
open kitchen = Kitchen
|
||||
open north, south = Town Center
|
||||
open cave mouth;cave = (3, 4, the small cave)
|
||||
|
||||
"""
|
||||
|
||||
def parse(self):
|
||||
building.ObjManipCommand.parse(self)
|
||||
|
||||
self.location = self.caller.location
|
||||
if not self.args or not self.rhs:
|
||||
self.caller.msg("Usage: open <new exit>[;alias...][:typeclass]"
|
||||
"[,<return exit>[;alias..][:typeclass]]] "
|
||||
"= <destination>")
|
||||
raise InterruptCommand
|
||||
if not self.location:
|
||||
self.caller.msg("You cannot create an exit from a None-location.")
|
||||
raise InterruptCommand
|
||||
|
||||
if all(char in self.rhs for char in ("(", ")", ",")):
|
||||
# search by (X,Y) or (X,Y,Z)
|
||||
X, Y, *Z = self.rhs.split(",", 2)
|
||||
if not Z:
|
||||
self.caller.msg("A full (X,Y,Z) coordinate must be given for the destination.")
|
||||
raise InterruptCommand
|
||||
Z = Z[0]
|
||||
# search by coordinate
|
||||
X, Y, Z = str(X).strip(), str(Y).strip(), str(Z).strip()
|
||||
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}).")
|
||||
raise InterruptCommand
|
||||
else:
|
||||
# regular search query
|
||||
self.destination = self.caller.search(self.rhs, global_search=True)
|
||||
if not self.destination:
|
||||
raise InterruptCommand
|
||||
|
||||
self.exit_name = self.lhs_objs[0]["name"]
|
||||
self.exit_aliases = self.lhs_objs[0]["aliases"]
|
||||
self.exit_typeclass = self.lhs_objs[0]["option"]
|
||||
|
||||
|
||||
class XYZGridCmdSet(CmdSet):
|
||||
"""
|
||||
Cmdset for easily adding the above cmds to the character cmdset.
|
||||
|
||||
"""
|
||||
key = "xyzgrid_cmdset"
|
||||
|
||||
def at_cmdset_creation(self):
|
||||
self.add(CmdXYZLook())
|
||||
self.add(CmdXYZTeleport())
|
||||
self.add(CmdXYZOpen())
|
||||
|
|
@ -31,7 +31,10 @@ PARENT = {
|
|||
"desc": "An empty room."
|
||||
}
|
||||
|
||||
# -------------------- map 1 - the large tree
|
||||
|
||||
# ---------------------------------------- map1
|
||||
# The large tree
|
||||
#
|
||||
# this exemplifies the various map symbols
|
||||
# but is not heavily prototyped
|
||||
|
||||
|
|
@ -39,11 +42,11 @@ MAP1 = r"""
|
|||
1
|
||||
+ 0 1 2 3 4 5 6 7 8 9 0
|
||||
|
||||
9 #-------#-#-------I
|
||||
\ /
|
||||
8 #-#---# #-t
|
||||
8 #-------#-#-------I
|
||||
\ /
|
||||
7 #-#---# #-t
|
||||
|\ |
|
||||
7 #i#-#b--#-t
|
||||
6 #i#-#b--#-t
|
||||
| |
|
||||
5 o-#---#
|
||||
\ /
|
||||
|
|
@ -68,7 +71,7 @@ class TransitionToCave(map_legend.MapTransitionMapNode):
|
|||
|
||||
"""
|
||||
symbol = 'T'
|
||||
target_map_xyz = (2, 3, 'small cave')
|
||||
target_map_xyz = (1, 0, 'the small cave')
|
||||
|
||||
|
||||
# extends the default legend
|
||||
|
|
@ -110,15 +113,15 @@ PROTOTYPES_MAP1 = {
|
|||
"key": "Dense foilage",
|
||||
"desc": "The foilage to the east is extra dense. It will take forever to get through it."
|
||||
},
|
||||
(5, 7): {
|
||||
(5, 6): {
|
||||
"key": "On a huge branch",
|
||||
"desc": "To the east is a glowing light, may be a teleporter."
|
||||
},
|
||||
(9, 8): {
|
||||
(9, 7): {
|
||||
"key": "On an enormous branch",
|
||||
"desc": "To the east is a glowing light, may be a teleporter."
|
||||
},
|
||||
(10, 9): {
|
||||
(10, 8): {
|
||||
"key": "A gorgeous view",
|
||||
"desc": "The view from here is breathtaking, showing the forest stretching far and wide."
|
||||
},
|
||||
|
|
@ -144,7 +147,8 @@ XYMAP_DATA_MAP1 = {
|
|||
"prototypes": PROTOTYPES_MAP1
|
||||
}
|
||||
|
||||
# ------------- map2 definitions - small cave
|
||||
# -------------------------------------- map2
|
||||
# The small cave
|
||||
# this gives prototypes for every room
|
||||
|
||||
MAP2 = r"""
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ except ImportError as err:
|
|||
f"{err}\nThe XYZgrid contrib requires "
|
||||
"the SciPy package. Install with `pip install scipy'.")
|
||||
|
||||
import uuid
|
||||
from evennia.prototypes import spawner
|
||||
from evennia.utils.utils import make_iter
|
||||
from .utils import MAPSCAN, REVERSE_DIRECTIONS, MapParserError, BIGVAL
|
||||
|
|
@ -22,6 +23,9 @@ NodeTypeclass = None
|
|||
ExitTypeclass = None
|
||||
|
||||
|
||||
UUID_XYZ_NAMESPACE = uuid.uuid5(uuid.UUID(int=0), "xyzgrid")
|
||||
|
||||
|
||||
# Nodes/Links
|
||||
|
||||
class MapNode:
|
||||
|
|
@ -135,6 +139,18 @@ class MapNode:
|
|||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
def log(self, msg):
|
||||
"""log messages using the xygrid parent"""
|
||||
self.xymap.xyzgrid.log(msg)
|
||||
|
||||
def generate_prototype_key(self):
|
||||
"""
|
||||
Generate a deterministic prototype key to allow for users to apply prototypes without
|
||||
needing a separate new name for every one.
|
||||
|
||||
"""
|
||||
return str(uuid.uuid5(UUID_XYZ_NAMESPACE, str((self.X, self.Y, self.Z))))
|
||||
|
||||
def build_links(self):
|
||||
"""
|
||||
This is called by the map parser when this node is encountered. It tells the node
|
||||
|
|
@ -261,20 +277,26 @@ class MapNode:
|
|||
return
|
||||
|
||||
xyz = self.get_spawn_xyz()
|
||||
print("xyz:", xyz, self.node_index)
|
||||
|
||||
self.log(f" spawning/updating room at xyz={xyz}")
|
||||
try:
|
||||
nodeobj = NodeTypeclass.objects.get_xyz(xyz=xyz)
|
||||
except NodeTypeclass.DoesNotExist:
|
||||
# create a new entity with proper coordinates etc
|
||||
nodeobj, err = NodeTypeclass.create(
|
||||
self.prototype.get('key', 'An Empty room'),
|
||||
self.prototype.get('key', 'An empty room'),
|
||||
xyz=xyz
|
||||
)
|
||||
if err:
|
||||
raise RuntimeError(err)
|
||||
|
||||
if not self.prototype.get('prototype_key'):
|
||||
# make sure there is a prototype_key in prototype
|
||||
self.prototype['prototype_key'] = self.generate_prototype_key()
|
||||
|
||||
# apply prototype to node. This will not override the XYZ tags since
|
||||
# these are not in the prototype and exact=False
|
||||
|
||||
spawner.batch_update_objects_with_prototype(
|
||||
self.prototype, objects=[nodeobj], exact=False)
|
||||
|
||||
|
|
@ -309,6 +331,9 @@ class MapNode:
|
|||
if link.spawn_aliases
|
||||
else self.direction_spawn_defaults.get(direction, ('unknown',))
|
||||
)
|
||||
if not link.prototype.get('prototype_key'):
|
||||
# generate a deterministic prototype_key if it doesn't exist
|
||||
link.prototype['prototype_key'] = self.generate_prototype_key()
|
||||
maplinks[key.lower()] = (key, aliases, direction, link)
|
||||
|
||||
# we need to search for exits in all directions since some
|
||||
|
|
@ -323,6 +348,8 @@ class MapNode:
|
|||
|
||||
if differing_key not in maplinks:
|
||||
# an exit without a maplink - delete the exit-object
|
||||
self.log(f" deleting exit at xyz={xyz}, direction={direction}")
|
||||
|
||||
linkobjs.pop(differing_key).delete()
|
||||
else:
|
||||
# missing in linkobjs - create a new exit
|
||||
|
|
@ -341,6 +368,7 @@ class MapNode:
|
|||
if err:
|
||||
raise RuntimeError(err)
|
||||
linkobjs[key.lower()] = exi
|
||||
self.log(f" spawning/updating exit xyz={xyz}, direction={direction}")
|
||||
|
||||
# apply prototypes to catch any changes
|
||||
for key, linkobj in linkobjs.items():
|
||||
|
|
@ -537,6 +565,17 @@ class MapLink:
|
|||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
def generate_prototype_key(self, *args):
|
||||
"""
|
||||
Generate a deterministic prototype key to allow for users to apply prototypes without
|
||||
needing a separate new name for every one.
|
||||
|
||||
Args:
|
||||
*args (str): These are used to further seed the key.
|
||||
|
||||
"""
|
||||
return str(uuid.uuid5(UUID_XYZ_NAMESPACE, str((self.X, self.Y, self.Z, *args))))
|
||||
|
||||
def traverse(self, start_direction, _weight=0, _linklen=1, _steps=None):
|
||||
"""
|
||||
Recursively traverse the links out of this LinkNode.
|
||||
|
|
|
|||
|
|
@ -355,8 +355,15 @@ class _MapTest(TestCase):
|
|||
self.grid.add_maps(self.map_data)
|
||||
self.map = self.grid.get_map(self.map_data['zcoord'])
|
||||
|
||||
# output to console
|
||||
# def _log(msg):
|
||||
# print(msg)
|
||||
# self.grid.log = _log
|
||||
|
||||
def tearDown(self):
|
||||
self.grid.delete()
|
||||
xyzroom.XYZRoom.objects.all().delete()
|
||||
xyzroom.XYZExit.objects.all().delete()
|
||||
|
||||
|
||||
class TestMap1(_MapTest):
|
||||
|
|
@ -1055,7 +1062,6 @@ class TestMapStressTest(TestCase):
|
|||
"""
|
||||
Xmax, Ymax = gridsize
|
||||
grid = self._get_grid(Xmax, Ymax)
|
||||
# print(f"\n\n{grid}\n")
|
||||
t0 = time()
|
||||
mapobj = xymap.XYMap({'map': grid}, Z="testmap")
|
||||
mapobj.parse()
|
||||
|
|
@ -1239,13 +1245,17 @@ class TestXYZGridTransition(TestCase):
|
|||
|
||||
class TestBuildExampleGrid(TestCase):
|
||||
"""
|
||||
Test building the map_example
|
||||
Test building the map_example (this takes about 30s)
|
||||
|
||||
"""
|
||||
def setUp(self):
|
||||
# build and populate grid
|
||||
self.grid, err = xyzgrid.XYZGrid.create("testgrid")
|
||||
|
||||
def _log(msg):
|
||||
print(msg)
|
||||
self.grid.log = _log
|
||||
|
||||
def tearDown(self):
|
||||
self.grid.delete()
|
||||
|
||||
|
|
@ -1262,15 +1272,15 @@ class TestBuildExampleGrid(TestCase):
|
|||
|
||||
# testing
|
||||
room1a = xyzroom.XYZRoom.objects.get_xyz(xyz=(3, 0, 'the large tree'))
|
||||
room1b = xyzroom.XYZRoom.objects.get_xyz(xyz=(10, 9, 'the large tree'))
|
||||
room2a = xyzroom.XYZRoom.objects.get_xyz(xyz=(1, 0, 'small cave'))
|
||||
room2b = xyzroom.XYZRoom.objects.get_xyz(xyz=(1, 3, 'small cave'))
|
||||
room1b = xyzroom.XYZRoom.objects.get_xyz(xyz=(10, 8, 'the large tree'))
|
||||
room2a = xyzroom.XYZRoom.objects.get_xyz(xyz=(1, 0, 'the small cave'))
|
||||
room2b = xyzroom.XYZRoom.objects.get_xyz(xyz=(1, 3, 'the small cave'))
|
||||
|
||||
self.assertEqual(room1a.key, "Dungeon Entrance")
|
||||
self.assertTrue(room1a.desc.startswith("To the west"))
|
||||
self.assertTrue(room1a.db.desc.startswith("To the west"))
|
||||
self.assertEqual(room1b.key, "A gorgeous view")
|
||||
self.assertTrue(room1b.desc.startswith("The view from here is breathtaking."))
|
||||
self.assertTrue(room1b.db.desc.startswith("The view from here is breathtaking,"))
|
||||
self.assertEqual(room2a.key, "The entrance")
|
||||
self.assertTrue(room2a.desc.startswith("This is the entrance to"))
|
||||
self.assertTrue(room2a.db.desc.startswith("This is the entrance to"))
|
||||
self.assertEqual(room2b.key, "North-west corner of the atrium")
|
||||
self.assertTrue(room2b.desc.startswith("Sunlight sifts down"))
|
||||
self.assertTrue(room2b.db.desc.startswith("Sunlight sifts down"))
|
||||
|
|
|
|||
|
|
@ -494,17 +494,19 @@ class XYMap:
|
|||
for iy, node_or_link in ydct.items():
|
||||
display_map[iy][ix] = node_or_link.get_display_symbol()
|
||||
|
||||
# validate and make sure all nodes/links have prototypes
|
||||
for node in node_index_map.values():
|
||||
node_coord = (node.X, node.Y)
|
||||
# load prototype from override, or use default
|
||||
node.prototype = self.prototypes.get(
|
||||
node_coord, self.prototypes.get(('*', '*'), node.prototype))
|
||||
# do the same for links (x, y, direction) coords
|
||||
for direction, maplink in node.first_links.items():
|
||||
maplink.prototype = self.prototypes.get(
|
||||
node_coord + (direction,),
|
||||
self.prototypes.get(('*', '*', '*'), maplink.prototype))
|
||||
# override node-prototypes, ignore if no prototype
|
||||
# is defined (some nodes should not be spawned)
|
||||
if node.prototype:
|
||||
node_coord = (node.X, node.Y)
|
||||
# load prototype from override, or use default
|
||||
node.prototype = self.prototypes.get(
|
||||
node_coord, self.prototypes.get(('*', '*'), node.prototype))
|
||||
# do the same for links (x, y, direction) coords
|
||||
for direction, maplink in node.first_links.items():
|
||||
maplink.prototype = self.prototypes.get(
|
||||
node_coord + (direction,),
|
||||
self.prototypes.get(('*', '*', '*'), maplink.prototype))
|
||||
|
||||
# store
|
||||
self.display_map = display_map
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ MAP_XDEST_TAG_CATEGORY = "exit_dest_x_coordinate"
|
|||
MAP_YDEST_TAG_CATEGORY = "exit_dest_y_coordinate"
|
||||
MAP_ZDEST_TAG_CATEGORY = "exit_dest_z_coordinate"
|
||||
|
||||
GET_XYZGRID = None
|
||||
|
||||
|
||||
class XYZManager(ObjectManager):
|
||||
"""
|
||||
|
|
@ -236,6 +238,13 @@ class XYZRoom(DefaultRoom):
|
|||
x, y, z = self.xyz
|
||||
return f"<XYZRoom '{self.db_key}', XYZ=({x},{y},{z})>"
|
||||
|
||||
@property
|
||||
def xyzgrid(self):
|
||||
global GET_XYZGRID
|
||||
if not GET_XYZGRID:
|
||||
from evennia.contrib.xyzgrid.xyzgrid import get_xyzgrid as GET_XYZGRID
|
||||
return GET_XYZGRID()
|
||||
|
||||
@property
|
||||
def xyz(self):
|
||||
if not hasattr(self, "_xyz"):
|
||||
|
|
@ -310,6 +319,13 @@ class XYZExit(DefaultExit):
|
|||
xd, yd, zd = self.xyz_destination
|
||||
return f"<XYZExit '{self.db_key}', XYZ=({x},{y},{z})->({xd},{yd},{zd})>"
|
||||
|
||||
@property
|
||||
def xyzgrid(self):
|
||||
global GET_XYZGRID
|
||||
if not GET_XYZGRID:
|
||||
from evennia.contrib.xyzgrid.xyzgrid import get_xyzgrid as GET_XYZGRID
|
||||
return GET_XYZGRID()
|
||||
|
||||
@property
|
||||
def xyz(self):
|
||||
if not hasattr(self, "_xyz"):
|
||||
|
|
|
|||
|
|
@ -92,8 +92,8 @@ def homogenize_prototype(prototype, custom_keys=None):
|
|||
homogenizations like adding missing prototype_keys and setting a default typeclass.
|
||||
|
||||
"""
|
||||
if not prototype or not isinstance(prototype, dict):
|
||||
return {}
|
||||
if not prototype or isinstance(prototype, str):
|
||||
return prototype
|
||||
|
||||
reserved = _PROTOTYPE_RESERVED_KEYS + (custom_keys or ())
|
||||
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ COMMAND_RATE_WARNING = "You entered commands too fast. Wait a moment and try aga
|
|||
# custom, extra commands to add to the `evennia` launcher. This is a dict
|
||||
# of {'cmdname': 'path.to.callable', ...}, where the callable will be passed
|
||||
# any extra args given on the command line. For example `evennia cmdname foo bar`.
|
||||
CUSTOM_LAUNCHER_COMMANDS = {}
|
||||
EXTRA_LAUNCHER_COMMANDS = {}
|
||||
|
||||
# Determine how large of a string can be sent to the server in number
|
||||
# of characters. If they attempt to enter a string over this character
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue