diff --git a/evennia/commands/default/general.py b/evennia/commands/default/general.py index b5cec9930..edb2e69b9 100644 --- a/evennia/commands/default/general.py +++ b/evennia/commands/default/general.py @@ -452,13 +452,15 @@ class CmdWhisper(COMMAND_DEFAULT_CLASS): return speech = self.rhs + # Call a hook to change the speech before whispering + speech = caller.at_before_whisper(receiver, speech) - # Feedback for the object doing the talking. - caller.msg('You whisper to %s, "%s|n"' % (receiver.key, speech)) + # If the speech is empty, abort the command + if not speech: + return - # Build the string to emit to receiver. - emit_string = '%s whispers, "%s|n"' % (caller.name, speech) - receiver.msg(emit_string, from_obj=caller) + # Call the at_after_whisper hook for feedback + caller.at_after_whisper(receiver, speech) class CmdPose(COMMAND_DEFAULT_CLASS): diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index 39f2f8841..e99e12396 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -128,7 +128,7 @@ class TestGeneral(CommandTest): self.call(general.CmdSay(), "Testing", "You say, \"Testing\"") def test_whisper(self): - self.call(general.CmdWhisper(), "Obj = Testing", "You whisper to Obj, \"Testing\"") + self.call(general.CmdWhisper(), "Obj = Testing", "You whisper to Obj, \"Testing\"", caller=self.char2) def test_access(self): self.call(general.CmdAccess(), "", "Permission Hierarchy (climbing):") diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index cba11842c..1c8d8c132 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -1509,7 +1509,6 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)): }) self_mapping = {k: v.get_display_name(self) if hasattr( v, "get_display_name") else str(v) for k, v in mapping.items()} - print self_mapping self.msg(msg_self.format(**self_mapping)) self.location.msg_contents(msg_location, exclude=(self, ), mapping=mapping) @@ -1533,6 +1532,79 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)): """ return message + def at_before_whisper(self, receiver, speech): + """ + Before the object whispers something to receiver. + + This hook is called by the 'whisper' command on the object itself + (probably a character). It is called before the actual whisper, + and can be used to control the content of the text to be whispered, + prevent whispering altogether or perform some alternative checks. + This hook should return the modified speech. If this return + value is empty (like "" or None), the command is aborted. + + Args: + receiver (Object): the object to whisper to. + speech (str): the text to be whispered by self. + + Returns: + speech (str): the text to be whispered (can be modified). + + """ + return speech + + def at_after_whisper(self, receiver, speech, msg_self=None, + msg_receiver=None, mapping=None): + """ + Display the actual whisper of self. + + This hook should display the actual whisper of the object to + receiver. It should both alert the object (self) and the + receiver. You can also notify the location if you want to, + to indicate to others that a message was whispered but you + can't hear it. The overriding of messages or + `mapping` allows for simple customization of the hook without + re-writing it completely. + + Args: + receiver (Objecvt): the object to whisper to. + speech (str): the text to be said by self. + msg_self (str, optional): the replacement message to say to self. + msg_receiver (str, optional): the replacement message to say + to receiver. + mapping (dict, optional): Additional mapping in messages. + + Both `msg_self` and `msg_receiver` should contain references + to other objects between braces, the way `locaiton.msg_contents` + would allow. For instance: + msg_self = 'You whisper to {receiver}, "{speech}"|n' + msg_receiver = '{object} whispers: "{speech}"|n' + + The following mappings can be used in both messages: + object: the object whispering. + receiver: the object whispered to. + speech: the text spoken by self. + + You can use additional mappings if you want to add other + information in your messages. + + """ + msg_self = msg_self or 'You whisper to {receiver}, "{speech}"|n' + msg_receiver = msg_receiver or '{object} whispers: "{speech}"|n' + mapping = mapping or {} + mapping.update({ + "object": self, + "receiver": receiver, + "speech": speech, + }) + self_mapping = {k: v.get_display_name(self) if hasattr( + v, "get_display_name") else str(v) for k, v in mapping.items()} + receiver_mapping = {k: v.get_display_name(receiver) if hasattr( + v, "get_display_name") else str(v) for k, v in mapping.items()} + self.msg(msg_self.format(**self_mapping)) + receiver.msg(msg_receiver.format(**receiver_mapping)) + + # # Base Character object