moss-n-puddles/commands/everyone.py
2025-05-13 21:00:50 -07:00

262 lines
8.3 KiB
Python
Executable file

#!/usr/bin/env python
from random import random
from re import split
from commands.command import Command
from evennia.commands.default.general import NumberedTargetCommand
from evennia.commands.default.muxcommand import MuxCommand
from evennia.contrib.rpg.rpsystem import send_emote
from evennia.utils import iter_to_str, logger
from utils.word_list import routput, paragraph, choices
def speech_effect(speech, verb, target, effects):
"""
Returns 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 CmdWhisper(MuxCommand):
"""
Speak privately as your character to another
Usage:
whisper <character> = <message>
whisper <char1>, <char2> = <message>
Talk privately to one or more characters in your current location, without
others in the room being informed.
"""
key = "whisper"
priority = 0
locks = "cmd:all()"
rhs_split = ("=")
def func(self):
"""
Implements the new 'whisper' command.
"""
if not self.args:
self.caller.msg("What are you whispering?")
return
if not self.rhs:
self.caller.msg("Usage: whisper <character> = <message>")
return
targets = [self.caller.search(target) for target in split(r" *, *", self.lhs)]
full_speech = f"/Me whispers to you, \"{self.rhs}\""
send_emote(self.caller, targets, full_speech, msg_type="say", anonymous_add=None, quiet=True)
to_list = [target.get_display_name(self) for target in targets]
full_speech = f"You whisper to {iter_to_str(to_list, endsep='and')}, \"{self.rhs}\""
self.caller.msg(full_speech, from_obj=self.caller)
class CmdSay(MuxCommand):
"""
Say something to the characters in the same area.
Usage:
say phrase
say/to char1, [char2 ...] = phrase
say[/switches] phrase
Where switches can be any of the following:
- exclaim : To replace 'says' with 'exclaims'
- yell : To replace 'says' with 'yells'
- scream : To replace 'says' with 'screams'
- ask : To replace 'says' with 'asks'
- to : Directs phrase to one or more characters
in the same area. Note others can still hear
the statement (see the 'whisper' command).
- adverb : Any adverb-like word that ends in '-ly'
is added to the say command, for instance:
say/quietly Hi there.
Shows as:
You quietly say, "Hi there."
"""
key = "say"
aliases = ["speak", "yell", "exclaim", "scream", "ask", "reply", "respond", "\"", "'"]
priority = 0
locks = "cmd:all()"
rhs_split = ("=")
arg_regex = None
def func(self):
"""
Implements the new 'say' command with switches.
"""
def charname(name):
try:
results = self.caller.search(name, quiet=True)
return results.get_display_name(self)
except:
return name
def chars_for_self(chars):
return [charname(c) for c in split(r"[ ,]+", chars)]
def chars_for_others(chars):
return [c if c.startswith('/') else '/'+c for c in split(r"[ ,]+", chars)]
def chars_list(chars, verb, for_rp=True):
char_lst = chars_for_others(chars) if for_rp else chars_for_self(chars)
return (' to ' if verb == 'say' else ' ') + \
iter_to_str(char_lst, endsep='and')
# logger.info(f"CmdSayIt: {self.cmdstring} lhs={self.lhs} switches={self.switches}")
if not self.args:
self.caller.msg("Say what?")
return
if 'to' in self.switches and not self.rhs:
self.caller.msg(paragraph("""
When attempting to say something to one or more
characters, use the '=' character to identify what you
want to say.
"""))
return
if self.rhs:
speech = self.rhs
else:
speech = self.args
# If speech is empty, stop here
if not self.caller.at_pre_say(speech):
return
adverb = ''
for switch in self.switches:
if switch.endswith('ly'):
adverb = switch + ' '
if 'scream' in self.switches or self.cmdstring == 'scream':
verb = 'scream'
elif 'respond' in self.switches or self.cmdstring == 'respond':
verb = 'respond'
elif 'reply' in self.switches or self.cmdstring == 'reply':
verb = 'reply'
elif 'yell' in self.switches or self.cmdstring == 'yell':
verb = 'yell'
elif 'exclaim' in self.switches or self.cmdstring == 'exclaim' or speech.endswith('!'):
verb = 'exclaim'
elif 'ask' in self.switches or self.cmdstring == 'ask' or speech.endswith('?'):
verb = 'ask'
else:
verb = "say"
for_me, for_others, verb = \
speech_effect(speech, verb,
self.caller,
self.caller.attributes.get(category="effect",
return_obj=True,
return_list=True))
# 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_whom = chars_list(self.lhs, verb, for_rp=False) if 'to' in self.switches else ''
full_speech = f"You {adverb}{verb}{to_whom}, \"{for_me}\""
self.caller.msg(full_speech, from_obj=self.caller)
# English is weird...
if verb == 'reply':
verb = 'replie'
targets = [item for item in self.caller.location.contents if item != self.caller]
to_whom = chars_list(self.lhs, verb) if 'to' in self.switches else ''
full_speech = f"/Me {adverb}{verb}s{to_whom}, \"{for_others}\""
# logger.info(f"send_emote('{self.caller}', {targets}, '{full_speech}', quiet=True")
send_emote(self.caller, targets, full_speech, msg_type="say", anonymous_add=None)
class CmdThink(Command, NumberedTargetCommand):
"""Think a thought out loud.
Usage:
think <message>
Similar to the 'say' or 'pose' commands, this communicates an
inner monologue to other characters in the same area.
"""
key = "think"
aliases = ["thinks", "("]
arg_regex = None
def func(self):
"""
Implements the think out loud command.
"""
if not self.args:
self.caller.msg("What do you want to think out loud?")
else:
thought = self.args.strip()
if (self.caller.db.thinking_count or 0) < 3 or random() < 0.4:
msg = routput(
f"""{self.caller.name}
<< thinks ^ wonders >> << out loud ^ aloud >>
o O ( {thought} )
"""
)
else:
msg = f"{self.caller.name} . o O ( {thought} )"
self.caller.db.thinking_count = (self.caller.db.thinking_count or 0) + 1
self.caller.location.msg_contents(msg)
class CmdTake(Command, NumberedTargetCommand):
"""
Take an object from another character or NPC.
Usage:
take <thing> from <character>
Note that only some things can be stolen.
For instance, the brass ring from the door knocker.
"""
key = "take"
aliases = ["steal"]
rhs_split = ("=", " from ")
def func(self):
"""
Implements the take command. Since this command is designed
to work on the object, we operate only on self.obj.
"""
if not self.args:
self.caller.msg("What do you want to take?")
elif not self.rhs:
self.caller.msg(f"You want to take {self.lhs}, but from whom?")
else:
self.caller.do_take(self.lhs, self.rhs)