moss-n-puddles/commands/say.py
Howard Abrams b33f6ca7df Can make pets into familiars
Refactor get_name to a top-level place on the Object.
2026-02-24 21:44:09 -08:00

291 lines
9.6 KiB
Python
Executable file

#!/usr/bin/env python
from random import random
from re import match, split, sub, MULTILINE
from evennia.contrib.rpg.rpsystem import send_emote
from evennia.utils import logger
from evennia.utils.verb_conjugation.pronouns import PRONOUN_MAPPING
from commands.command import Command
from utils.word_list import routput, paragraph, choices
ADVERBS = [
"aloud",
"abruptly", "actually", "angrily", "animatedly", "bitterly", "boldly", "cheerfully", "clearly",
"confidently", "continuously", "cruelly", "defiantly", "desperately", "earnestly", "elaborately",
"emphatically", "excitedly", "frantically", "frequently", "gently", "gravely", "hesitantly",
"impatiently", "insistently", "intelligently", "jokingly", "joyfully", "loudly", "mockingly",
"nonchalantly", "passionately", "playfully", "quietly", "reassuringly", "reluctantly", "seriously",
"sharply", "sharply", "simply", "silently", "sincerely", "softly", "suddenly", "surprisingly",
"thoughtfully", "unexpectedly", "voraciously", "whimsically", "wistfully",
]
def get_adverb(phrase):
"""Return tuple of initial adverb (or None) and the phrase."""
m = match(r"(\w+)[, ]+(.*)", phrase)
if m:
first_word = m.group(1)
rest_words = m.group(2)
if first_word in ADVERBS:
return first_word, rest_words
return None, phrase
def get_say_direction(phrase):
"""Return tuple of object of communication and the rest of the phrase."""
m = match(r"to (\w+)[, ]+(.*)", phrase)
if m:
first_word = m.group(1)
rest_words = m.group(2)
if first_word.endswith("self"):
return "self", rest_words
else:
return first_word, rest_words
return None, phrase
def speech_effect(speech, verb, target, effects):
"""
Return speech after applying 'effects'.
Return a tuple for what a user thinks he said, and what
others actually here. If no effect, just return speech twice.
Effects:
- mute ... can't talk
- donkied ... target and others hear the value of effect
- sloshed ... slurred speech
To administer:
@set woman/donkied:effect = "Heehaw! ;; Heehaw, heehaw!"
"""
for effect in effects:
if effect:
if effect.db_key == 'donkied':
msg = choices(effect.value)
return (msg, msg, "bray")
return (speech, speech, verb)
class CmdSay(Command):
"""Say something to the characters in the same area.
Usage:
|gsay [[<adverb>] [to <character>],] <message>|n
This command has a couple optional strings. as you see, the text
within brackets, [...] are optional, while text in <...> are to be
replaced, and this technical jargon probably means little, so
perhaps examples would be better:
|gsay Good evening.|n
|wYou say, "Good evening."|n
Or:
|gsay loudly, Good evening!|n
|wYou loudly exclaim, "Good evening!"|n
In the second example, you gave an |wadverb|n that ends in
|w-ly|n. Also, the exclamation point changes the 'say' to an
'exclaim'. A question mark changes 'say' to 'ask'.
If you start your message with an adverb, but want that adverb
|win|n the message and not |waffecting|n the message, surround it
with quotes. For instance:
|gsay actually, it is pronounced gah-nome.
|wYou say actually, "it is pronounced gah-nome."
|gsay "actually, it is pronounced gah-nome."
|wYou say, "actually, it is pronounced gah-nome."|n
Of course, adverbs that affect the message are lowercase, and
messages usually begin with an uppercase, as in:
|gsay Actually, it is pronounced Gah-nome.
|wYou say, "Actually, it is pronounced Gah-nome."|n
Because this command is so common, you can also use ", as in:
|g"What's going on?|n
You can also direct your message to a particular person (or
thing), as in:
|gsay to heron, How do you do?
|wYou ask the purple heron, "How do you do?"|n
"What can I do for you?" asks the purple heron.
Since you might be having a conversation with a character, you can
use the |gsayto|n to target the same person. For instance:
|gsay to pet Who's a widdle cutie?
|wYou ask wee beastie, "Who's a widdle cutie?"
|gsayto You are!
|wYou exclaim to wee beastie, "You are!"|n
You can replace the 'say' command with any of the following:
- shout
- scream
- respond
- reply
- yell
- ask
"""
key = "say"
verb = "say"
aliases = ["says", "speak", "shout", "yell", "exclaim", "scream",
"ask", "reply", "respond", '"', "'", "sayto"]
locks = "cmd:all()"
# don't require a space after `say/'/"`
arg_regex = None
adverb = None
target = None
def parse(self):
"""Parse the input into speech parts."""
self.phrase = self.args.strip()
m = match(r".+\?.*", self.phrase)
if m:
self.verb = "ask"
m = match(r".+!.*", self.phrase)
if m:
self.verb = "exclaim"
m = match(r'.*".+".*', self.phrase)
if not m:
# No overriding quotes? Process adverbs...
self.adverb, self.phrase = get_adverb(self.phrase)
target, self.phrase = get_say_direction(self.phrase)
if target:
if target == "self":
self.target = self.caller
else:
self.target = self.caller.search(target)
self.caller.db.say_target = self.target
# Final quotes can be trimmed:
self.phrase = self.phrase.strip('"').strip("'")
def func(self):
"""Implement the new 'say' command with switches."""
speaker = self.caller
if not self.phrase or self.phrase == "":
speaker.msg("Say what?")
return
# If speech is empty, stop here
if not speaker.at_pre_say(self.phrase):
return
if self.cmdstring == 'sayto':
self.target = self.caller.db.say_target
if self.cmdstring == 'scream':
self.verb = 'scream'
elif self.cmdstring == 'shout':
self.verb = 'shout'
elif self.cmdstring == 'respond':
self.verb = 'respond'
elif self.cmdstring == 'reply':
self.verb = 'reply'
elif self.cmdstring == 'yell':
self.verb = 'yell'
elif self.cmdstring == 'ask':
self.verb = 'ask'
for_me, for_others, verb = \
speech_effect(self.phrase, self.verb, speaker,
speaker.attributes.get(category="effect",
return_obj=True,
return_list=True))
self.to_puppets(speaker, for_others, self.target)
self.to_you(speaker, verb, for_me)
self.to_others(speaker, verb, for_others)
def to_you(self, speaker, verb, phrase):
"""Send 'say' message to the speaker.
The `send_emote` is _global_, so if we want to say to 'Bob',
'You say ...', we have to both send him a message with the
'You' as well as everyone else with the 'send_emote'.
"""
to_who = ""
if self.target:
if self.target == self.caller:
who = "yourself"
else:
who = self.target.get_display_name(speaker)
if verb == "ask":
to_who = " " + who
else:
to_who = " to " + who
verb = " " + verb
adverb = " " + self.adverb if self.adverb else ""
if adverb.endswith("ly") and random() < 0.5:
full_speech = f"You{adverb}{verb}{to_who}, \"{phrase}\""
else:
full_speech = f"You{verb}{adverb}{to_who}, \"{phrase}\""
speaker.msg(full_speech, from_obj=speaker)
def to_others(self, speaker, verb, phrase):
"""Use the 'send_emote' to have speaker 'say' something to others.
We use /me to be replaced by the user's appearance to others.
We also need to deal with weird English usage.
"""
to_whom = ""
if self.target:
if self.target == self.caller:
whom = PRONOUN_MAPPING['3rd person']['reflexive pronoun'][self.caller.db.gender]
else:
who = self.target.get_display_name(speaker)
whom = f"/{self.target.key}"
if verb == "ask":
to_whom = " " + whom
else:
to_whom = " to " + whom
# English is weird...
verb = ' replie' if verb == 'reply' else " " + verb
adverb = " " + self.adverb if self.adverb else ""
targets = [item for item in speaker.location.contents
if item != speaker]
if adverb.endswith("ly") and random() < 0.5:
full_speech = f"/me{adverb}{verb}s{to_whom}, \"{phrase}\""
else:
full_speech = f"/me{verb}s{adverb}{to_whom}, \"{phrase}\""
send_emote(speaker, targets, full_speech, msg_type="say",
anonymous_add=None)
def to_puppets(self, speaker, phrase, target=None):
"""Send phrase to puppet, target.
If target is None, then send to all puppets.
"""
# Speak to everyone in the room:
if not target:
for char in speaker.location.contents:
if hasattr(char, 'other_say') and callable(char.other_say):
char.other_say(speaker, phrase)
else:
if hasattr(target, 'other_sayto') and callable(target.other_sayto):
logger.info(f"Found {target.key}: {phrase}")
target.other_sayto(speaker, phrase)