Make numbered_names use get_display_name; make dbref display separate method
This commit is contained in:
parent
d893cfd46e
commit
cbe3d4c738
14 changed files with 242 additions and 122 deletions
16
CHANGELOG.md
16
CHANGELOG.md
|
|
@ -2,16 +2,30 @@
|
||||||
|
|
||||||
## Evennia Main branch
|
## Evennia Main branch
|
||||||
|
|
||||||
|
- Feature: *Backwards incompatible*: `DefaultObject.get_numbered_name` now gets object's
|
||||||
|
name via `.get_display_name` for better compatibility with recog systems.
|
||||||
|
- Feature: *Backwards incompatible*: Removed the (#dbref) display from
|
||||||
|
`DefaultObject.get_display_name`, instead using new `.get_extra_display_name_info`
|
||||||
|
method for getting this info. The Object's display template was extended for
|
||||||
|
optionally adding this information. This makes showing extra object info to
|
||||||
|
admins an explicit action and opens up `get_display_name` for general use.
|
||||||
- Feature: Add `ON_DEMAND_HANDLER.set_dt(key, category, dt)` and
|
- Feature: Add `ON_DEMAND_HANDLER.set_dt(key, category, dt)` and
|
||||||
`.set_stage(key, category, stage)` to allow manual tweaking of task timings,
|
`.set_stage(key, category, stage)` to allow manual tweaking of task timings,
|
||||||
for example for a spell speeding a plant's growth (Griatch)
|
for example for a spell speeding a plant's growth (Griatch)
|
||||||
- Feature: Add `use_assertequal` kwarg to the `EvenniaCommandTestMixin` testing
|
- Feature: Add `use_assertequal` kwarg to the `EvenniaCommandTestMixin` testing
|
||||||
class; this uses django's `assertEqual` over the default more lenient checker,
|
class; this uses django's `assertEqual` over the default more lenient checker,
|
||||||
which can be useful for testing table whitespace (Griatch)
|
which can be useful for testing table whitespace (Griatch)
|
||||||
|
- Feature: New `utils.group_objects_by_key_and_desc` for grouping a list of
|
||||||
|
objects based on the visible key and desc. Useful for inventory listings (Griatch)
|
||||||
|
- Feature: Add `DefaultObject.get_numbered_name` `return_string` bool kwarg, for only
|
||||||
|
returning singular/plural based on count instead of a tuple with both (Griatch)
|
||||||
|
- Fix: `DefaultObject.get_numbered_name` used `.name` instead of
|
||||||
|
`.get_display_name` which broke recog systems. May lead to object's #dbref
|
||||||
|
will show for admins in some more places (Griatch)
|
||||||
- [Fix][pull3420]: Refactor Clothing contrib's inventory command align with
|
- [Fix][pull3420]: Refactor Clothing contrib's inventory command align with
|
||||||
Evennia core's version (michaelfaith84, Griatch)
|
Evennia core's version (michaelfaith84, Griatch)
|
||||||
- Fix: Resolve a bug when loading on-demand-handler data from database (Griatch)
|
- Fix: Resolve a bug when loading on-demand-handler data from database (Griatch)
|
||||||
- Doc fixes (iLPdev, Griatch)
|
- Doc fixes (iLPdev, Griatch, CloudKeeper)
|
||||||
|
|
||||||
[pull3420]: https://github.com/evennia/evennia/pull/3420
|
[pull3420]: https://github.com/evennia/evennia/pull/3420
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,8 @@ General Character commands usually available to all characters
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
import evennia
|
import evennia
|
||||||
|
from django.conf import settings
|
||||||
from evennia.typeclasses.attributes import NickTemplateInvalid
|
from evennia.typeclasses.attributes import NickTemplateInvalid
|
||||||
from evennia.utils import utils
|
from evennia.utils import utils
|
||||||
|
|
||||||
|
|
@ -370,11 +369,10 @@ class CmdInventory(COMMAND_DEFAULT_CLASS):
|
||||||
from evennia.utils.ansi import raw as raw_ansi
|
from evennia.utils.ansi import raw as raw_ansi
|
||||||
|
|
||||||
table = self.styled_table(border="header")
|
table = self.styled_table(border="header")
|
||||||
for item in items:
|
for key, desc, _ in utils.group_objects_by_key_and_desc(items, caller=self.caller):
|
||||||
singular, _ = item.get_numbered_name(1, self.caller)
|
|
||||||
table.add_row(
|
table.add_row(
|
||||||
f"|C{singular}|n",
|
f"|C{key}|n",
|
||||||
"{}|n".format(utils.crop(raw_ansi(item.db.desc or ""), width=50) or ""),
|
"{}|n".format(utils.crop(raw_ansi(desc or ""), width=50) or ""),
|
||||||
)
|
)
|
||||||
string = f"|wYou are carrying:\n{table}"
|
string = f"|wYou are carrying:\n{table}"
|
||||||
self.msg(text=(string, {"type": "inventory"}))
|
self.msg(text=(string, {"type": "inventory"}))
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,10 @@ main test suite started with
|
||||||
import datetime
|
import datetime
|
||||||
from unittest.mock import MagicMock, Mock, patch
|
from unittest.mock import MagicMock, Mock, patch
|
||||||
|
|
||||||
|
import evennia
|
||||||
from anything import Anything
|
from anything import Anything
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.test import override_settings
|
from django.test import override_settings
|
||||||
from parameterized import parameterized
|
|
||||||
from twisted.internet import task
|
|
||||||
|
|
||||||
import evennia
|
|
||||||
from evennia import (
|
from evennia import (
|
||||||
DefaultCharacter,
|
DefaultCharacter,
|
||||||
DefaultExit,
|
DefaultExit,
|
||||||
|
|
@ -32,14 +29,7 @@ from evennia import (
|
||||||
from evennia.commands import cmdparser
|
from evennia.commands import cmdparser
|
||||||
from evennia.commands.cmdset import CmdSet
|
from evennia.commands.cmdset import CmdSet
|
||||||
from evennia.commands.command import Command, InterruptCommand
|
from evennia.commands.command import Command, InterruptCommand
|
||||||
from evennia.commands.default import (
|
from evennia.commands.default import account, admin, batchprocess, building, comms, general
|
||||||
account,
|
|
||||||
admin,
|
|
||||||
batchprocess,
|
|
||||||
building,
|
|
||||||
comms,
|
|
||||||
general,
|
|
||||||
)
|
|
||||||
from evennia.commands.default import help as help_module
|
from evennia.commands.default import help as help_module
|
||||||
from evennia.commands.default import syscommands, system, unloggedin
|
from evennia.commands.default import syscommands, system, unloggedin
|
||||||
from evennia.commands.default.cmdset_character import CharacterCmdSet
|
from evennia.commands.default.cmdset_character import CharacterCmdSet
|
||||||
|
|
@ -48,6 +38,8 @@ from evennia.prototypes import prototypes as protlib
|
||||||
from evennia.utils import create, gametime, utils
|
from evennia.utils import create, gametime, utils
|
||||||
from evennia.utils.test_resources import BaseEvenniaCommandTest # noqa
|
from evennia.utils.test_resources import BaseEvenniaCommandTest # noqa
|
||||||
from evennia.utils.test_resources import BaseEvenniaTest, EvenniaCommandTest
|
from evennia.utils.test_resources import BaseEvenniaTest, EvenniaCommandTest
|
||||||
|
from parameterized import parameterized
|
||||||
|
from twisted.internet import task
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
# Command testing
|
# Command testing
|
||||||
|
|
@ -116,13 +108,13 @@ class TestGeneral(BaseEvenniaCommandTest):
|
||||||
self.call(general.CmdNick(), "/list", "Defined Nicks:")
|
self.call(general.CmdNick(), "/list", "Defined Nicks:")
|
||||||
|
|
||||||
def test_get_and_drop(self):
|
def test_get_and_drop(self):
|
||||||
self.call(general.CmdGet(), "Obj", "You pick up an Obj.")
|
self.call(general.CmdGet(), "Obj", "You pick up an Obj")
|
||||||
self.call(general.CmdDrop(), "Obj", "You drop an Obj.")
|
self.call(general.CmdDrop(), "Obj", "You drop an Obj")
|
||||||
|
|
||||||
def test_give(self):
|
def test_give(self):
|
||||||
self.call(general.CmdGive(), "Obj to Char2", "You aren't carrying Obj.")
|
self.call(general.CmdGive(), "Obj to Char2", "You aren't carrying Obj.")
|
||||||
self.call(general.CmdGive(), "Obj = Char2", "You aren't carrying Obj.")
|
self.call(general.CmdGive(), "Obj = Char2", "You aren't carrying Obj.")
|
||||||
self.call(general.CmdGet(), "Obj", "You pick up an Obj.")
|
self.call(general.CmdGet(), "Obj", "You pick up an Obj")
|
||||||
self.call(general.CmdGive(), "Obj to Char2", "You give")
|
self.call(general.CmdGive(), "Obj to Char2", "You give")
|
||||||
self.call(general.CmdGive(), "Obj = Char", "You give", caller=self.char2)
|
self.call(general.CmdGive(), "Obj = Char", "You give", caller=self.char2)
|
||||||
|
|
||||||
|
|
@ -569,7 +561,7 @@ class TestAdmin(BaseEvenniaCommandTest):
|
||||||
self.call(
|
self.call(
|
||||||
admin.CmdForce(),
|
admin.CmdForce(),
|
||||||
"Char2=say test",
|
"Char2=say test",
|
||||||
'Char2(#{}) says, "test"|You have forced Char2 to: say test'.format(cid),
|
'Char2 says, "test"|You have forced Char2 to: say test',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -781,17 +773,14 @@ class TestBuilding(BaseEvenniaCommandTest):
|
||||||
self.call(building.CmdExamine(), "*TestAccount")
|
self.call(building.CmdExamine(), "*TestAccount")
|
||||||
|
|
||||||
def test_set_obj_alias(self):
|
def test_set_obj_alias(self):
|
||||||
oid = self.obj1.id
|
|
||||||
self.call(building.CmdSetObjAlias(), "Obj =", "Cleared aliases from Obj")
|
self.call(building.CmdSetObjAlias(), "Obj =", "Cleared aliases from Obj")
|
||||||
self.call(
|
self.call(
|
||||||
building.CmdSetObjAlias(),
|
building.CmdSetObjAlias(), "Obj = TestObj1b", "Alias(es) for 'Obj' set to 'testobj1b'."
|
||||||
"Obj = TestObj1b",
|
|
||||||
"Alias(es) for 'Obj(#{})' set to 'testobj1b'.".format(oid),
|
|
||||||
)
|
)
|
||||||
self.call(building.CmdSetObjAlias(), "", "Usage: ")
|
self.call(building.CmdSetObjAlias(), "", "Usage: ")
|
||||||
self.call(building.CmdSetObjAlias(), "NotFound =", "Could not find 'NotFound'.")
|
self.call(building.CmdSetObjAlias(), "NotFound =", "Could not find 'NotFound'.")
|
||||||
|
|
||||||
self.call(building.CmdSetObjAlias(), "Obj", "Aliases for Obj(#{}): 'testobj1b'".format(oid))
|
self.call(building.CmdSetObjAlias(), "Obj", "Aliases for Obj: 'testobj1b'")
|
||||||
self.call(building.CmdSetObjAlias(), "Obj2 =", "Cleared aliases from Obj2")
|
self.call(building.CmdSetObjAlias(), "Obj2 =", "Cleared aliases from Obj2")
|
||||||
self.call(building.CmdSetObjAlias(), "Obj2 =", "No aliases to clear.")
|
self.call(building.CmdSetObjAlias(), "Obj2 =", "No aliases to clear.")
|
||||||
|
|
||||||
|
|
@ -1228,9 +1217,7 @@ class TestBuilding(BaseEvenniaCommandTest):
|
||||||
|
|
||||||
def test_desc(self):
|
def test_desc(self):
|
||||||
oid = self.obj2.id
|
oid = self.obj2.id
|
||||||
self.call(
|
self.call(building.CmdDesc(), "Obj2=TestDesc", "The description was set on Obj2.")
|
||||||
building.CmdDesc(), "Obj2=TestDesc", "The description was set on Obj2(#{}).".format(oid)
|
|
||||||
)
|
|
||||||
self.call(building.CmdDesc(), "", "Usage: ")
|
self.call(building.CmdDesc(), "", "Usage: ")
|
||||||
|
|
||||||
with patch("evennia.commands.default.building.EvEditor") as mock_ed:
|
with patch("evennia.commands.default.building.EvEditor") as mock_ed:
|
||||||
|
|
@ -1251,7 +1238,7 @@ class TestBuilding(BaseEvenniaCommandTest):
|
||||||
oid = self.obj2.id
|
oid = self.obj2.id
|
||||||
o2d = self.obj2.db.desc
|
o2d = self.obj2.db.desc
|
||||||
r1d = self.room1.db.desc
|
r1d = self.room1.db.desc
|
||||||
self.call(building.CmdDesc(), "Obj2=", "The description was set on Obj2(#{}).".format(oid))
|
self.call(building.CmdDesc(), "Obj2=", "The description was set on Obj2.")
|
||||||
assert self.obj2.db.desc == "" and self.obj2.db.desc != o2d
|
assert self.obj2.db.desc == "" and self.obj2.db.desc != o2d
|
||||||
assert self.room1.db.desc == r1d
|
assert self.room1.db.desc == r1d
|
||||||
|
|
||||||
|
|
@ -1260,7 +1247,7 @@ class TestBuilding(BaseEvenniaCommandTest):
|
||||||
rid = self.room1.id
|
rid = self.room1.id
|
||||||
o2d = self.obj2.db.desc
|
o2d = self.obj2.db.desc
|
||||||
r1d = self.room1.db.desc
|
r1d = self.room1.db.desc
|
||||||
self.call(building.CmdDesc(), "Obj2", "The description was set on Room(#{}).".format(rid))
|
self.call(building.CmdDesc(), "Obj2", "The description was set on Room.")
|
||||||
assert self.obj2.db.desc == o2d
|
assert self.obj2.db.desc == o2d
|
||||||
assert self.room1.db.desc == "Obj2" and self.room1.db.desc != r1d
|
assert self.room1.db.desc == "Obj2" and self.room1.db.desc != r1d
|
||||||
|
|
||||||
|
|
@ -1283,16 +1270,11 @@ class TestBuilding(BaseEvenniaCommandTest):
|
||||||
building.CmdDestroy(), settings.DEFAULT_HOME, "You are trying to delete"
|
building.CmdDestroy(), settings.DEFAULT_HOME, "You are trying to delete"
|
||||||
) # DEFAULT_HOME should not be deleted
|
) # DEFAULT_HOME should not be deleted
|
||||||
self.char2.location = self.room2
|
self.char2.location = self.room2
|
||||||
charid = self.char2.id
|
|
||||||
room1id = self.room1.id
|
|
||||||
room2id = self.room2.id
|
|
||||||
self.call(
|
self.call(
|
||||||
building.CmdDestroy(),
|
building.CmdDestroy(),
|
||||||
self.room2.dbref,
|
self.room2.dbref,
|
||||||
"Char2(#{}) arrives to Room(#{}) from Room2(#{}).|Room2 was destroyed.".format(
|
"Char2 arrives to Room from Room2.|Room2 was destroyed.",
|
||||||
charid, room1id, room2id
|
),
|
||||||
),
|
|
||||||
)
|
|
||||||
building.CmdDestroy.confirm = confirm
|
building.CmdDestroy.confirm = confirm
|
||||||
|
|
||||||
def test_destroy_sequence(self):
|
def test_destroy_sequence(self):
|
||||||
|
|
@ -1640,9 +1622,6 @@ class TestBuilding(BaseEvenniaCommandTest):
|
||||||
self.assertFalse(script3.pk)
|
self.assertFalse(script3.pk)
|
||||||
|
|
||||||
def test_teleport(self):
|
def test_teleport(self):
|
||||||
oid = self.obj1.id
|
|
||||||
rid = self.room1.id
|
|
||||||
rid2 = self.room2.id
|
|
||||||
self.call(building.CmdTeleport(), "", "Usage: ")
|
self.call(building.CmdTeleport(), "", "Usage: ")
|
||||||
self.call(building.CmdTeleport(), "Obj = Room", "Obj is already at Room.")
|
self.call(building.CmdTeleport(), "Obj = Room", "Obj is already at Room.")
|
||||||
self.call(
|
self.call(
|
||||||
|
|
@ -1653,9 +1632,7 @@ class TestBuilding(BaseEvenniaCommandTest):
|
||||||
self.call(
|
self.call(
|
||||||
building.CmdTeleport(),
|
building.CmdTeleport(),
|
||||||
"Obj = Room2",
|
"Obj = Room2",
|
||||||
"Obj(#{}) is leaving Room(#{}), heading for Room2(#{}).|Teleported Obj -> Room2.".format(
|
"Obj is leaving Room, heading for Room2.|Teleported Obj -> Room2.",
|
||||||
oid, rid, rid2
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
self.call(building.CmdTeleport(), "NotFound = Room", "Could not find 'NotFound'.")
|
self.call(building.CmdTeleport(), "NotFound = Room", "Could not find 'NotFound'.")
|
||||||
self.call(
|
self.call(
|
||||||
|
|
@ -1663,7 +1640,7 @@ class TestBuilding(BaseEvenniaCommandTest):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.call(building.CmdTeleport(), "/tonone Obj2", "Teleported Obj2 -> None-location.")
|
self.call(building.CmdTeleport(), "/tonone Obj2", "Teleported Obj2 -> None-location.")
|
||||||
self.call(building.CmdTeleport(), "/quiet Room2", "Room2(#{})".format(rid2))
|
self.call(building.CmdTeleport(), "/quiet Room2", "Room2")
|
||||||
self.call(
|
self.call(
|
||||||
building.CmdTeleport(),
|
building.CmdTeleport(),
|
||||||
"/t", # /t switch is abbreviated form of /tonone
|
"/t", # /t switch is abbreviated form of /tonone
|
||||||
|
|
@ -1777,7 +1754,8 @@ class TestBuilding(BaseEvenniaCommandTest):
|
||||||
self.call(
|
self.call(
|
||||||
building.CmdSpawn(),
|
building.CmdSpawn(),
|
||||||
"{'prototype_key':'GOBLIN', 'typeclass':'evennia.objects.objects.DefaultCharacter', "
|
"{'prototype_key':'GOBLIN', 'typeclass':'evennia.objects.objects.DefaultCharacter', "
|
||||||
"'key':'goblin', 'location':'%s'}" % spawnLoc.dbref,
|
"'key':'goblin', 'location':'%s'}"
|
||||||
|
% spawnLoc.dbref,
|
||||||
"Spawned goblin",
|
"Spawned goblin",
|
||||||
)
|
)
|
||||||
goblin = get_object(self, "goblin")
|
goblin = get_object(self, "goblin")
|
||||||
|
|
@ -1825,7 +1803,8 @@ class TestBuilding(BaseEvenniaCommandTest):
|
||||||
self.call(
|
self.call(
|
||||||
building.CmdSpawn(),
|
building.CmdSpawn(),
|
||||||
"/noloc {'prototype_parent':'TESTBALL', 'key': 'Ball', 'prototype_key': 'foo',"
|
"/noloc {'prototype_parent':'TESTBALL', 'key': 'Ball', 'prototype_key': 'foo',"
|
||||||
" 'location':'%s'}" % spawnLoc.dbref,
|
" 'location':'%s'}"
|
||||||
|
% spawnLoc.dbref,
|
||||||
"Spawned Ball",
|
"Spawned Ball",
|
||||||
)
|
)
|
||||||
ball = get_object(self, "Ball")
|
ball = get_object(self, "Ball")
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ class TestEvscaperoomCommands(BaseEvenniaCommandTest):
|
||||||
self.call(
|
self.call(
|
||||||
commands.CmdEmote(),
|
commands.CmdEmote(),
|
||||||
"/me smiles to /obj",
|
"/me smiles to /obj",
|
||||||
f"Char(#{self.char1.id}) smiles to Obj(#{self.obj1.id})",
|
f"Char smiles to Obj.",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_focus_interaction(self):
|
def test_focus_interaction(self):
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,15 @@ from collections import defaultdict
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from evennia import DefaultCharacter, DefaultObject, default_cmds
|
from evennia import DefaultCharacter, DefaultObject, default_cmds
|
||||||
from evennia.commands.default.muxcommand import MuxCommand
|
from evennia.commands.default.muxcommand import MuxCommand
|
||||||
from evennia.utils import at_search_result, crop, evtable, inherits_from, int2str, iter_to_str
|
from evennia.utils import (
|
||||||
|
at_search_result,
|
||||||
|
crop,
|
||||||
|
evtable,
|
||||||
|
group_objects_by_key_and_desc,
|
||||||
|
inherits_from,
|
||||||
|
int2str,
|
||||||
|
iter_to_str,
|
||||||
|
)
|
||||||
from evennia.utils.ansi import raw as raw_ansi
|
from evennia.utils.ansi import raw as raw_ansi
|
||||||
|
|
||||||
# Options start here.
|
# Options start here.
|
||||||
|
|
@ -660,11 +668,10 @@ class CmdInventory(MuxCommand):
|
||||||
carried = [obj for obj in items if not obj.db.worn]
|
carried = [obj for obj in items if not obj.db.worn]
|
||||||
carry_table = self.styled_table(border="header")
|
carry_table = self.styled_table(border="header")
|
||||||
|
|
||||||
for item in carried:
|
for key, desc, _ in group_objects_by_key_and_desc(carried, caller=self.caller):
|
||||||
singular, _ = item.get_numbered_name(1, self.caller)
|
|
||||||
carry_table.add_row(
|
carry_table.add_row(
|
||||||
f"{singular}|n",
|
f"{key}|n",
|
||||||
"{}|n".format(crop(raw_ansi(item.db.desc or ""), width=50) or ""),
|
"{}|n".format(crop(raw_ansi(desc or ""), width=50) or ""),
|
||||||
)
|
)
|
||||||
message_list.extend(
|
message_list.extend(
|
||||||
["|wYou are carrying:|n", str(carry_table) if carry_table.nrows > 0 else " Nothing."]
|
["|wYou are carrying:|n", str(carry_table) if carry_table.nrows > 0 else " Nothing."]
|
||||||
|
|
@ -674,18 +681,17 @@ class CmdInventory(MuxCommand):
|
||||||
worn = [obj for obj in items if obj.db.worn]
|
worn = [obj for obj in items if obj.db.worn]
|
||||||
wear_table = self.styled_table(border="header")
|
wear_table = self.styled_table(border="header")
|
||||||
|
|
||||||
for item in worn:
|
for key, desc, _ in group_objects_by_key_and_desc(worn, caller=self.caller):
|
||||||
singular, _ = item.get_numbered_name(1, self.caller)
|
|
||||||
wear_table.add_row(
|
wear_table.add_row(
|
||||||
f"{singular}|n",
|
f"{key}|n",
|
||||||
"{}|n".format(crop(raw_ansi(item.db.desc or ""), width=50) or ""),
|
"{}|n".format(crop(raw_ansi(desc or ""), width=50) or ""),
|
||||||
)
|
)
|
||||||
message_list.extend(
|
message_list.extend(
|
||||||
["You are wearing:|n", str(wear_table) if wear_table.nrows > 0 else " Nothing."]
|
["You are wearing:|n", str(wear_table) if wear_table.nrows > 0 else " Nothing."]
|
||||||
)
|
)
|
||||||
|
|
||||||
# return the composite message
|
# return the composite message
|
||||||
self.caller.msg("\n".join(message_list))
|
self.caller.msg(text=("\n".join(message_list), {"type": "inventory"}))
|
||||||
|
|
||||||
|
|
||||||
class ClothedCharacterCmdSet(default_cmds.CharacterCmdSet):
|
class ClothedCharacterCmdSet(default_cmds.CharacterCmdSet):
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,11 @@ class TestClothingCmd(BaseEvenniaCommandTest):
|
||||||
self.wearer.location = self.room
|
self.wearer.location = self.room
|
||||||
# Make a test hat
|
# Make a test hat
|
||||||
self.test_hat = create_object(clothing.ContribClothing, key="test hat")
|
self.test_hat = create_object(clothing.ContribClothing, key="test hat")
|
||||||
|
self.test_hat.db.desc = "A test hat."
|
||||||
self.test_hat.db.clothing_type = "hat"
|
self.test_hat.db.clothing_type = "hat"
|
||||||
# Make a test scarf
|
# Make a test scarf
|
||||||
self.test_scarf = create_object(clothing.ContribClothing, key="test scarf")
|
self.test_scarf = create_object(clothing.ContribClothing, key="test scarf")
|
||||||
|
self.test_scarf.db.desc = "A test scarf."
|
||||||
self.test_scarf.db.clothing_type = "accessory"
|
self.test_scarf.db.clothing_type = "accessory"
|
||||||
|
|
||||||
def test_clothingcommands(self):
|
def test_clothingcommands(self):
|
||||||
|
|
@ -40,7 +42,10 @@ class TestClothingCmd(BaseEvenniaCommandTest):
|
||||||
self.call(
|
self.call(
|
||||||
clothing.CmdInventory(),
|
clothing.CmdInventory(),
|
||||||
"",
|
"",
|
||||||
"You are carrying:\n a test scarf \n a test hat \nYou are wearing:\n Nothing.",
|
(
|
||||||
|
"You are carrying:\n a test hat A test hat. \n a test scarf A test"
|
||||||
|
" scarf. \nYou are wearing:\n Nothing."
|
||||||
|
),
|
||||||
caller=self.wearer,
|
caller=self.wearer,
|
||||||
use_assertequal=True,
|
use_assertequal=True,
|
||||||
)
|
)
|
||||||
|
|
@ -71,7 +76,10 @@ class TestClothingCmd(BaseEvenniaCommandTest):
|
||||||
self.call(
|
self.call(
|
||||||
clothing.CmdInventory(),
|
clothing.CmdInventory(),
|
||||||
"",
|
"",
|
||||||
"You are carrying:\n Nothing.\nYou are wearing:\n a test scarf \n a test hat ",
|
(
|
||||||
|
"You are carrying:\n Nothing.\nYou are wearing:\n a test hat A test hat. \n"
|
||||||
|
" a test scarf A test scarf. "
|
||||||
|
),
|
||||||
caller=self.wearer,
|
caller=self.wearer,
|
||||||
use_assertequal=True,
|
use_assertequal=True,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from evennia import create_object
|
from evennia import create_object
|
||||||
from evennia.utils.test_resources import BaseEvenniaCommandTest, BaseEvenniaTest # noqa
|
from evennia.utils.test_resources import BaseEvenniaCommandTest # noqa
|
||||||
|
from evennia.utils.test_resources import BaseEvenniaTest
|
||||||
|
|
||||||
from .containers import CmdContainerGet, CmdContainerLook, CmdPut, ContribContainer
|
from .containers import CmdContainerGet, CmdContainerLook, CmdPut, ContribContainer
|
||||||
|
|
||||||
|
|
@ -40,9 +41,17 @@ class TestContainerCmds(BaseEvenniaCommandTest):
|
||||||
# get normally
|
# get normally
|
||||||
self.call(CmdContainerGet(), "Obj", "You pick up an Obj.")
|
self.call(CmdContainerGet(), "Obj", "You pick up an Obj.")
|
||||||
# put in the container
|
# put in the container
|
||||||
self.call(CmdPut(), "obj in box", "You put an Obj in a Box.")
|
self.call(
|
||||||
|
CmdPut(),
|
||||||
|
"obj in box",
|
||||||
|
"You put an Obj in a Box.",
|
||||||
|
)
|
||||||
# get from the container
|
# get from the container
|
||||||
self.call(CmdContainerGet(), "obj from box", "You get an Obj from a Box.")
|
self.call(
|
||||||
|
CmdContainerGet(),
|
||||||
|
"obj from box",
|
||||||
|
"You get an Obj from a Box.",
|
||||||
|
)
|
||||||
|
|
||||||
def test_locked_get_put(self):
|
def test_locked_get_put(self):
|
||||||
# lock container
|
# lock container
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,10 @@ Testing of ExtendedRoom contrib
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from mock import Mock, patch
|
|
||||||
from parameterized import parameterized
|
|
||||||
|
|
||||||
from evennia import create_object
|
from evennia import create_object
|
||||||
from evennia.utils.test_resources import BaseEvenniaCommandTest, EvenniaTestCase
|
from evennia.utils.test_resources import BaseEvenniaCommandTest, EvenniaTestCase
|
||||||
|
from mock import Mock, patch
|
||||||
|
from parameterized import parameterized
|
||||||
|
|
||||||
from . import extended_room
|
from . import extended_room
|
||||||
|
|
||||||
|
|
@ -195,7 +194,7 @@ class TestExtendedRoomCommands(BaseEvenniaCommandTest):
|
||||||
extended_room.CmdExtendedRoomDesc(),
|
extended_room.CmdExtendedRoomDesc(),
|
||||||
"",
|
"",
|
||||||
f"""
|
f"""
|
||||||
Room Room(#{self.room1.id}) Season: autumn. Time: afternoon. States: None
|
Room Room Season: autumn. Time: afternoon. States: None
|
||||||
|
|
||||||
Room state (default) (active):
|
Room state (default) (active):
|
||||||
Base room description.
|
Base room description.
|
||||||
|
|
@ -218,7 +217,7 @@ Base room description.
|
||||||
extended_room.CmdExtendedRoomDesc(),
|
extended_room.CmdExtendedRoomDesc(),
|
||||||
"",
|
"",
|
||||||
f"""
|
f"""
|
||||||
Room Room(#{self.room1.id}) Season: autumn. Time: afternoon. States: None
|
Room Room Season: autumn. Time: afternoon. States: None
|
||||||
|
|
||||||
Room state burning:
|
Room state burning:
|
||||||
Burning description.
|
Burning description.
|
||||||
|
|
@ -235,8 +234,10 @@ Base room description.
|
||||||
self.call(
|
self.call(
|
||||||
extended_room.CmdExtendedRoomDesc(),
|
extended_room.CmdExtendedRoomDesc(),
|
||||||
"/del/burning/spring",
|
"/del/burning/spring",
|
||||||
"The burning-description was deleted, if it existed.|The spring-description was"
|
(
|
||||||
" deleted, if it existed",
|
"The burning-description was deleted, if it existed.|The spring-description was"
|
||||||
|
" deleted, if it existed"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
# add autumn, which should be active
|
# add autumn, which should be active
|
||||||
self.call(
|
self.call(
|
||||||
|
|
@ -248,7 +249,7 @@ Base room description.
|
||||||
extended_room.CmdExtendedRoomDesc(),
|
extended_room.CmdExtendedRoomDesc(),
|
||||||
"",
|
"",
|
||||||
f"""
|
f"""
|
||||||
Room Room(#{self.room1.id}) Season: autumn. Time: afternoon. States: None
|
Room Room Season: autumn. Time: afternoon. States: None
|
||||||
|
|
||||||
Room state autumn (active):
|
Room state autumn (active):
|
||||||
Autumn description.
|
Autumn description.
|
||||||
|
|
@ -285,8 +286,8 @@ test: Test detail.
|
||||||
self.call(
|
self.call(
|
||||||
extended_room.CmdExtendedRoomDetail(),
|
extended_room.CmdExtendedRoomDetail(),
|
||||||
"",
|
"",
|
||||||
f"""
|
"""
|
||||||
The room Room(#{self.room1.id}) doesn't have any details.
|
The room Room doesn't have any details.
|
||||||
""".strip(),
|
""".strip(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -306,7 +307,7 @@ The room Room(#{self.room1.id}) doesn't have any details.
|
||||||
self.call(
|
self.call(
|
||||||
extended_room.CmdExtendedRoomState(),
|
extended_room.CmdExtendedRoomState(),
|
||||||
"",
|
"",
|
||||||
f"Room states (not counting automatic time/season) on Room(#{self.room1.id}):\n None",
|
"Room states (not counting automatic time/season) on Room:\n None",
|
||||||
)
|
)
|
||||||
|
|
||||||
# add room states
|
# add room states
|
||||||
|
|
@ -323,8 +324,7 @@ The room Room(#{self.room1.id}) doesn't have any details.
|
||||||
self.call(
|
self.call(
|
||||||
extended_room.CmdExtendedRoomState(),
|
extended_room.CmdExtendedRoomState(),
|
||||||
"",
|
"",
|
||||||
f"Room states (not counting automatic time/season) on Room(#{self.room1.id}):\n "
|
f"Room states (not counting automatic time/season) on Room:\n 'burning' and 'windy'",
|
||||||
"'burning' and 'windy'",
|
|
||||||
)
|
)
|
||||||
# toggle windy
|
# toggle windy
|
||||||
self.call(
|
self.call(
|
||||||
|
|
@ -335,8 +335,7 @@ The room Room(#{self.room1.id}) doesn't have any details.
|
||||||
self.call(
|
self.call(
|
||||||
extended_room.CmdExtendedRoomState(),
|
extended_room.CmdExtendedRoomState(),
|
||||||
"",
|
"",
|
||||||
f"Room states (not counting automatic time/season) on Room(#{self.room1.id}):\n "
|
f"Room states (not counting automatic time/season) on Room:\n 'burning'",
|
||||||
"'burning'",
|
|
||||||
)
|
)
|
||||||
# add a autumn state and make sure we override it
|
# add a autumn state and make sure we override it
|
||||||
self.room1.add_desc("Autumn description.", room_state="autumn")
|
self.room1.add_desc("Autumn description.", room_state="autumn")
|
||||||
|
|
@ -387,13 +386,17 @@ The room Room(#{self.room1.id}) doesn't have any details.
|
||||||
self.call(
|
self.call(
|
||||||
extended_room.CmdExtendedRoomLook(),
|
extended_room.CmdExtendedRoomLook(),
|
||||||
"",
|
"",
|
||||||
f"Room(#{self.room1.id})\nThis is a nice autumnal forest. The afternoon sun is"
|
(
|
||||||
" shining through the trees.",
|
f"Room(#{self.room1.id})\nThis is a nice autumnal forest. The afternoon sun is"
|
||||||
|
" shining through the trees."
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self.room1.add_room_state("burning")
|
self.room1.add_room_state("burning")
|
||||||
self.call(
|
self.call(
|
||||||
extended_room.CmdExtendedRoomLook(),
|
extended_room.CmdExtendedRoomLook(),
|
||||||
"",
|
"",
|
||||||
f"Room(#{self.room1.id})\nThis is a nice autumnal forest. The afternoon sun is"
|
(
|
||||||
" shining through the trees and this place is on fire!",
|
f"Room(#{self.room1.id})\nThis is a nice autumnal forest. The afternoon sun is"
|
||||||
|
" shining through the trees and this place is on fire!"
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -154,18 +154,12 @@ from string import punctuation
|
||||||
|
|
||||||
import inflect
|
import inflect
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from evennia.commands.cmdset import CmdSet
|
from evennia.commands.cmdset import CmdSet
|
||||||
from evennia.commands.command import Command
|
from evennia.commands.command import Command
|
||||||
from evennia.objects.models import ObjectDB
|
from evennia.objects.models import ObjectDB
|
||||||
from evennia.objects.objects import DefaultCharacter, DefaultObject
|
from evennia.objects.objects import DefaultCharacter, DefaultObject
|
||||||
from evennia.utils import ansi, logger
|
from evennia.utils import ansi, logger
|
||||||
from evennia.utils.utils import (
|
from evennia.utils.utils import iter_to_str, lazy_property, make_iter, variable_from_module
|
||||||
iter_to_str,
|
|
||||||
lazy_property,
|
|
||||||
make_iter,
|
|
||||||
variable_from_module,
|
|
||||||
)
|
|
||||||
|
|
||||||
_INFLECT = inflect.engine()
|
_INFLECT = inflect.engine()
|
||||||
|
|
||||||
|
|
@ -1343,13 +1337,15 @@ class ContribRPObject(DefaultObject):
|
||||||
# in eventual error reporting later (not their keys). Doing
|
# in eventual error reporting later (not their keys). Doing
|
||||||
# it like this e.g. allows for use of the typeclass kwarg
|
# it like this e.g. allows for use of the typeclass kwarg
|
||||||
# limiter.
|
# limiter.
|
||||||
results.extend([obj for obj in search_obj(candidate.key) if obj not in results])
|
results.extend(
|
||||||
|
[obj for obj in search_obj(candidate.key, **kwargs) if obj not in results]
|
||||||
|
)
|
||||||
|
|
||||||
if not results and is_builder:
|
if not results and is_builder:
|
||||||
# builders get a chance to search only by key+alias
|
# builders get to do a global search by key+alias
|
||||||
results = search_obj(searchdata, candidates=candidates, **kwargs)
|
results = search_obj(searchdata, **kwargs)
|
||||||
else:
|
else:
|
||||||
# global searches / #drefs end up here. Global searches are
|
# global searches with #drefs end up here. Global searches are
|
||||||
# only done in code, so is controlled, #dbrefs are turned off
|
# only done in code, so is controlled, #dbrefs are turned off
|
||||||
# for non-Builders.
|
# for non-Builders.
|
||||||
results = search_obj(searchdata, **kwargs)
|
results = search_obj(searchdata, **kwargs)
|
||||||
|
|
@ -1409,10 +1405,6 @@ class ContribRPObject(DefaultObject):
|
||||||
# use own sdesc as a fallback
|
# use own sdesc as a fallback
|
||||||
sdesc = self.sdesc.get()
|
sdesc = self.sdesc.get()
|
||||||
|
|
||||||
# add dbref is looker has control access and `noid` is not set
|
|
||||||
if self.access(looker, access_type="control") and not kwargs.get("noid", False):
|
|
||||||
sdesc = f"{sdesc}(#{self.id})"
|
|
||||||
|
|
||||||
return self.get_posed_sdesc(sdesc) if kwargs.get("pose", False) else sdesc
|
return self.get_posed_sdesc(sdesc) if kwargs.get("pose", False) else sdesc
|
||||||
|
|
||||||
def get_display_characters(self, looker, pose=True, **kwargs):
|
def get_display_characters(self, looker, pose=True, **kwargs):
|
||||||
|
|
@ -1545,10 +1537,6 @@ class ContribRPCharacter(DefaultCharacter, ContribRPObject):
|
||||||
# use own sdesc as a fallback
|
# use own sdesc as a fallback
|
||||||
sdesc = self.sdesc.get()
|
sdesc = self.sdesc.get()
|
||||||
|
|
||||||
# add dbref is looker has control access and `noid` is not set
|
|
||||||
if self.access(looker, access_type="control") and not kwargs.get("noid", False):
|
|
||||||
sdesc = f"{sdesc}(#{self.id})"
|
|
||||||
|
|
||||||
return self.get_posed_sdesc(sdesc) if kwargs.get("pose", False) else sdesc
|
return self.get_posed_sdesc(sdesc) if kwargs.get("pose", False) else sdesc
|
||||||
|
|
||||||
def at_object_creation(self):
|
def at_object_creation(self):
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ Tests for RP system
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from anything import Anything
|
from anything import Anything
|
||||||
|
|
||||||
from evennia import DefaultObject, create_object, default_cmds
|
from evennia import DefaultObject, create_object, default_cmds
|
||||||
from evennia.commands.default.tests import BaseEvenniaCommandTest
|
from evennia.commands.default.tests import BaseEvenniaCommandTest
|
||||||
from evennia.utils.test_resources import BaseEvenniaTest
|
from evennia.utils.test_resources import BaseEvenniaTest
|
||||||
|
|
@ -414,14 +413,14 @@ class TestRPSystemCommands(BaseEvenniaCommandTest):
|
||||||
|
|
||||||
expected_first_call = [
|
expected_first_call = [
|
||||||
"More than one match for 'Mushroom' (please narrow target):",
|
"More than one match for 'Mushroom' (please narrow target):",
|
||||||
f" Mushroom({mushroom1.dbref})-1 []",
|
f" Mushroom-1 []",
|
||||||
f" Mushroom({mushroom2.dbref})-2 []",
|
f" Mushroom-2 []",
|
||||||
]
|
]
|
||||||
|
|
||||||
self.call(default_cmds.CmdLook(), "Mushroom", "\n".join(expected_first_call)) # PASSES
|
self.call(default_cmds.CmdLook(), "Mushroom", "\n".join(expected_first_call)) # PASSES
|
||||||
|
|
||||||
expected_second_call = f"Mushroom({mushroom1.dbref})\nThe first mushroom is brown."
|
expected_second_call = f"Mushroom(#{mushroom1.id})\nThe first mushroom is brown."
|
||||||
self.call(default_cmds.CmdLook(), "Mushroom-1", expected_second_call) # FAILS
|
self.call(default_cmds.CmdLook(), "Mushroom-1", expected_second_call) # FAILS
|
||||||
|
|
||||||
expected_third_call = f"Mushroom({mushroom2.dbref})\nThe second mushroom is red."
|
expected_third_call = f"Mushroom(#{mushroom2.id})\nThe second mushroom is red."
|
||||||
self.call(default_cmds.CmdLook(), "Mushroom-2", expected_third_call) # FAILS
|
self.call(default_cmds.CmdLook(), "Mushroom-2", expected_third_call) # FAILS
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ from django.conf import settings
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.core.validators import validate_comma_separated_integer_list
|
from django.core.validators import validate_comma_separated_integer_list
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from evennia.objects.manager import ObjectDBManager
|
from evennia.objects.manager import ObjectDBManager
|
||||||
from evennia.typeclasses.models import TypedObject
|
from evennia.typeclasses.models import TypedObject
|
||||||
from evennia.utils import logger
|
from evennia.utils import logger
|
||||||
|
|
|
||||||
|
|
@ -219,7 +219,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
|
||||||
# populated by `return_appearance`
|
# populated by `return_appearance`
|
||||||
appearance_template = """
|
appearance_template = """
|
||||||
{header}
|
{header}
|
||||||
|c{name}|n
|
|c{name}{extra_name_info}|n
|
||||||
{desc}
|
{desc}
|
||||||
{exits}{characters}{things}
|
{exits}{characters}{things}
|
||||||
{footer}
|
{footer}
|
||||||
|
|
@ -316,7 +316,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
|
||||||
"obj.location to move an object here.".format(self.__class__)
|
"obj.location to move an object here.".format(self.__class__)
|
||||||
)
|
)
|
||||||
|
|
||||||
contents = property(contents_get, contents_set, contents_set)
|
contents = property(contents_get, contents_set, contents_set, contents_set)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def exits(self):
|
def exits(self):
|
||||||
|
|
@ -827,6 +827,16 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
|
||||||
for session in sessions:
|
for session in sessions:
|
||||||
session.data_out(**kwargs)
|
session.data_out(**kwargs)
|
||||||
|
|
||||||
|
def get_contents_unique(self, caller=None):
|
||||||
|
"""
|
||||||
|
Get a mapping of contents that are visually unique to the caller, along with
|
||||||
|
how many of each there are.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
caller (Object, optional): The object to check visibility from. If not given,
|
||||||
|
the current object will be used.
|
||||||
|
"""
|
||||||
|
|
||||||
def for_contents(self, func, exclude=None, **kwargs):
|
def for_contents(self, func, exclude=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Runs a function on every object contained within this one.
|
Runs a function on every object contained within this one.
|
||||||
|
|
@ -1436,10 +1446,28 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
|
||||||
and is expected to produce something useful for builders.
|
and is expected to produce something useful for builders.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if looker and self.locks.check_lockstring(looker, "perm(Builder)"):
|
|
||||||
return "{}(#{})".format(self.name, self.id)
|
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
def get_extra_display_name_info(self, looker=None, **kwargs):
|
||||||
|
"""
|
||||||
|
Adds any extra display information to the object's name. By default this is is the
|
||||||
|
object's dbref in parentheses, if the looker has permission to see it.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
looker (Object): The object looking at this object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The dbref of this object, if the looker has permission to see it. Otherwise, an
|
||||||
|
empty string is returned.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
By default, this becomes a string (#dbref) attached to the object's name.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if looker and self.locks.check_lockstring(looker, "perm(Builder)"):
|
||||||
|
return f"(#{self.id})"
|
||||||
|
return ""
|
||||||
|
|
||||||
def get_numbered_name(self, count, looker, **kwargs):
|
def get_numbered_name(self, count, looker, **kwargs):
|
||||||
"""
|
"""
|
||||||
Return the numbered (singular, plural) forms of this object's key. This is by default called
|
Return the numbered (singular, plural) forms of this object's key. This is by default called
|
||||||
|
|
@ -1453,8 +1481,10 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
|
||||||
looker (Object): Onlooker. Not used by default.
|
looker (Object): Onlooker. Not used by default.
|
||||||
|
|
||||||
Keyword Args:
|
Keyword Args:
|
||||||
key (str): Optional key to pluralize. If not given, the object's `.name` property is
|
key (str): Optional key to pluralize. If not given, the object's `.get_display_name()`
|
||||||
used.
|
method is used.
|
||||||
|
return_string (bool): If `True`, return only the singular form if count is 0,1 or
|
||||||
|
the plural form otherwise. If `False` (default), return both forms as a tuple.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
tuple: This is a tuple `(str, str)` with the singular and plural forms of the key
|
tuple: This is a tuple `(str, str)` with the singular and plural forms of the key
|
||||||
|
|
@ -1466,7 +1496,8 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
plural_category = "plural_key"
|
plural_category = "plural_key"
|
||||||
key = kwargs.get("key", self.name)
|
key = kwargs.get("key", self.get_display_name(looker))
|
||||||
|
raw_key = self.name
|
||||||
key = ansi.ANSIString(key) # this is needed to allow inflection of colored names
|
key = ansi.ANSIString(key) # this is needed to allow inflection of colored names
|
||||||
try:
|
try:
|
||||||
plural = _INFLECT.plural(key, count)
|
plural = _INFLECT.plural(key, count)
|
||||||
|
|
@ -1482,6 +1513,10 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
|
||||||
# save the singular form as an alias here too so we can display "an egg" and also
|
# save the singular form as an alias here too so we can display "an egg" and also
|
||||||
# look at 'an egg'.
|
# look at 'an egg'.
|
||||||
self.aliases.add(singular, category=plural_category)
|
self.aliases.add(singular, category=plural_category)
|
||||||
|
|
||||||
|
if kwargs.get("return_string"):
|
||||||
|
return singular if count in (0, 1) else plural
|
||||||
|
|
||||||
return singular, plural
|
return singular, plural
|
||||||
|
|
||||||
def get_display_header(self, looker, **kwargs):
|
def get_display_header(self, looker, **kwargs):
|
||||||
|
|
@ -1645,6 +1680,7 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
|
||||||
return self.format_appearance(
|
return self.format_appearance(
|
||||||
self.appearance_template.format(
|
self.appearance_template.format(
|
||||||
name=self.get_display_name(looker, **kwargs),
|
name=self.get_display_name(looker, **kwargs),
|
||||||
|
extra_name_info=self.get_extra_display_name_info(looker, **kwargs),
|
||||||
desc=self.get_display_desc(looker, **kwargs),
|
desc=self.get_display_desc(looker, **kwargs),
|
||||||
header=self.get_display_header(looker, **kwargs),
|
header=self.get_display_header(looker, **kwargs),
|
||||||
footer=self.get_display_footer(looker, **kwargs),
|
footer=self.get_display_footer(looker, **kwargs),
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,11 @@ from datetime import datetime, timedelta
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from parameterized import parameterized
|
|
||||||
from twisted.internet import task
|
|
||||||
|
|
||||||
from evennia.utils import utils
|
from evennia.utils import utils
|
||||||
from evennia.utils.ansi import ANSIString
|
from evennia.utils.ansi import ANSIString
|
||||||
from evennia.utils.test_resources import BaseEvenniaTest
|
from evennia.utils.test_resources import BaseEvenniaTest
|
||||||
|
from parameterized import parameterized
|
||||||
|
from twisted.internet import task
|
||||||
|
|
||||||
|
|
||||||
class TestIsIter(TestCase):
|
class TestIsIter(TestCase):
|
||||||
|
|
@ -775,6 +774,54 @@ class TestJustify(TestCase):
|
||||||
self.assertIn(ANSI_RED, str(result))
|
self.assertIn(ANSI_RED, str(result))
|
||||||
|
|
||||||
|
|
||||||
|
class TestGroupObjectsByKeyAndDesc(TestCase):
|
||||||
|
"""
|
||||||
|
Test the utils.group_objects_by_key_and_desc function.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
class MockObject:
|
||||||
|
def __init__(self, key, desc):
|
||||||
|
self.key = key
|
||||||
|
self.desc = desc
|
||||||
|
|
||||||
|
def get_display_name(self, looker, **kwargs):
|
||||||
|
return self.key + f" (looker: {looker.key})"
|
||||||
|
|
||||||
|
def get_display_desc(self, looker, **kwargs):
|
||||||
|
return self.desc + f" (looker: {looker.key})"
|
||||||
|
|
||||||
|
def get_numbered_name(self, count, looker, **kwargs):
|
||||||
|
return f"{count} {self.key} (looker: {looker.key})"
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"MockObject({self.key}, {self.desc})"
|
||||||
|
|
||||||
|
def test_group_by_key_and_desc(self):
|
||||||
|
ma1 = self.MockObject("itemA", "descA")
|
||||||
|
ma2 = self.MockObject("itemA", "descA")
|
||||||
|
ma3 = self.MockObject("itemA", "descA")
|
||||||
|
ma4 = self.MockObject("itemA", "descA")
|
||||||
|
|
||||||
|
mb1 = self.MockObject("itemB", "descB")
|
||||||
|
mb2 = self.MockObject("itemB", "descB")
|
||||||
|
mb3 = self.MockObject("itemB", "descB")
|
||||||
|
|
||||||
|
me = self.MockObject("Looker", "DescLooker")
|
||||||
|
|
||||||
|
result = utils.group_objects_by_key_and_desc([ma1, ma2, ma3, ma4, mb1, mb2, mb3], caller=me)
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
list(result),
|
||||||
|
[
|
||||||
|
("4 itemA (looker: Looker)", "descA (looker: Looker)", [ma1, ma2, ma3, ma4]),
|
||||||
|
("3 itemB (looker: Looker)", "descB (looker: Looker)", [mb1, mb2, mb3]),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create a list of objects
|
||||||
|
|
||||||
|
|
||||||
class TestMatchIP(TestCase):
|
class TestMatchIP(TestCase):
|
||||||
"""
|
"""
|
||||||
test utils.match_ip
|
test utils.match_ip
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ from os.path import join as osjoin
|
||||||
from string import punctuation
|
from string import punctuation
|
||||||
from unicodedata import east_asian_width
|
from unicodedata import east_asian_width
|
||||||
|
|
||||||
|
import evennia
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||||
|
|
@ -35,14 +36,12 @@ from django.core.validators import validate_email as django_validate_email
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.html import strip_tags
|
from django.utils.html import strip_tags
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
from evennia.utils import logger
|
||||||
from simpleeval import simple_eval
|
from simpleeval import simple_eval
|
||||||
from twisted.internet import reactor, threads
|
from twisted.internet import reactor, threads
|
||||||
from twisted.internet.defer import returnValue # noqa - used as import target
|
from twisted.internet.defer import returnValue # noqa - used as import target
|
||||||
from twisted.internet.task import deferLater
|
from twisted.internet.task import deferLater
|
||||||
|
|
||||||
import evennia
|
|
||||||
from evennia.utils import logger
|
|
||||||
|
|
||||||
_MULTIMATCH_TEMPLATE = settings.SEARCH_MULTIMATCH_TEMPLATE
|
_MULTIMATCH_TEMPLATE = settings.SEARCH_MULTIMATCH_TEMPLATE
|
||||||
_EVENNIA_DIR = settings.EVENNIA_DIR
|
_EVENNIA_DIR = settings.EVENNIA_DIR
|
||||||
_GAME_DIR = settings.GAME_DIR
|
_GAME_DIR = settings.GAME_DIR
|
||||||
|
|
@ -1767,6 +1766,41 @@ def string_partial_matching(alternatives, inp, ret_index=True):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def group_objects_by_key_and_desc(objects, caller=None, **kwargs):
|
||||||
|
"""
|
||||||
|
Groups a list of objects by their key and description. This is used to group
|
||||||
|
visibly identical objects together, for example for inventory listings.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
objects (list): A list of objects to group. These must be DefaultObject.
|
||||||
|
|
||||||
|
caller (Object, optional): The object looking at the objects, used to get the
|
||||||
|
description and key of each object.
|
||||||
|
**kwargs: Passed into each object's `get_display_name/desc` methods.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
iterable: An iterable of tuples, where each tuple is on the form
|
||||||
|
`(numbered_name, description, [objects])`.
|
||||||
|
|
||||||
|
"""
|
||||||
|
key_descs = defaultdict(list)
|
||||||
|
return_string = kwargs.pop("return_string", True)
|
||||||
|
|
||||||
|
for obj in objects:
|
||||||
|
key_descs[
|
||||||
|
(obj.get_display_name(caller, **kwargs), obj.get_display_desc(caller, **kwargs))
|
||||||
|
].append(obj)
|
||||||
|
|
||||||
|
return (
|
||||||
|
(
|
||||||
|
objs[0].get_numbered_name(len(objs), caller, return_string=return_string, **kwargs),
|
||||||
|
desc,
|
||||||
|
objs,
|
||||||
|
)
|
||||||
|
for (key, desc), objs in sorted(key_descs.items(), key=lambda tup: tup[0][0])
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def format_table(table, extra_space=1):
|
def format_table(table, extra_space=1):
|
||||||
"""
|
"""
|
||||||
Format a 2D array of strings into a multi-column table.
|
Format a 2D array of strings into a multi-column table.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue