Create new dynamic say command

This also includes a 'gm' command that should make GMing a game
better.
This commit is contained in:
Howard Abrams 2025-04-25 15:55:20 -07:00
parent 600542b0d4
commit 6b8a9e946b
10 changed files with 374 additions and 264 deletions

View file

@ -19,7 +19,8 @@ from evennia.contrib.grid import extended_room
from evennia.contrib.game_systems.gendersub import SetGender
from evennia.contrib.rpg.rpsystem import RPSystemCmdSet
from commands.sittables import CmdNoSitStand
from commands.everyone import CmdTake, CmdThink
from commands.everyone import CmdTake, CmdThink, CmdSay
from commands.wizards import CmdGM
class CharacterCmdSet(default_cmds.CharacterCmdSet):
"""
@ -41,6 +42,8 @@ class CharacterCmdSet(default_cmds.CharacterCmdSet):
self.add(SetGender())
self.add(RPSystemCmdSet())
self.add(extended_room.ExtendedRoomCmdSet)
self.add(CmdSay())
self.add(CmdGM)
class AccountCmdSet(default_cmds.AccountCmdSet):

View file

@ -1,13 +1,92 @@
#!/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
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:
- yell : To replace 'says' with 'yells'
- scream : To replace 'says' with 'screams'
- ask : To replace 'says' with 'asks'
- to : Takes one or more characters in the same area, and directs the statement to them. Note that 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", "scream", "ask"]
priority = 0
locks = "cmd:all()"
rhs_split = (",", "=")
def func(self):
"""
Implements the new 'say' command with switches.
"""
def characters(chars):
return [c if c.startswith('/') else '/'+c for c in split(r"[ ,]+", chars)]
if not self.args:
self.caller.msg("Say what?")
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
logger.info(f"CmdSayIt: {self.cmdstring}")
adverb = ''
for switch in self.switches:
if switch.endswith('ly'):
adverb = switch + ' '
if 'yell' in self.switches or self.cmdstring == 'yell' or speech.endswith('!'):
omsg_type = adverb + 'yells'
elif 'scream' in self.switches or self.cmdstring == 'scream':
omsg_type = adverb + 'screams'
elif 'ask' in self.switches or self.cmdstring == 'ask' or speech.endswith('?'):
omsg_type = adverb + 'asks'
else:
omsg_type = adverb + "says"
if 'to' in self.switches:
to_whom = ' to ' if omsg_type.endswith('says') else ' ' + \
iter_to_str(characters(self.lhs), endsep='and')
else:
to_whom = ''
full_speech = f"/Me {omsg_type}{to_whom}, \"{speech}\""
targets = self.caller.location.contents
send_emote(self.caller, targets, full_speech, msg_type="say", anonymous_add=None)
class CmdThink(Command, NumberedTargetCommand):
"""Think a thought out loud.

View file

@ -1,7 +1,10 @@
#!/usr/bin/env python
from .command import Command
from evennia import CmdSet
from evennia.utils import logger
from evennia.commands.default.muxcommand import MuxCommand
from .command import Command
class CmdFly(Command):
@ -18,12 +21,46 @@ class CmdFly(Command):
"""
key = "^fly"
locks = "call:all()"
def func(self):
self.caller.do_fly(self.args.strip())
class CmdSetWizardSpells(CmdSet):
class CmdSetWand(CmdSet):
"""
All wizard spells are tied to a 'wand' that might be flavored.
"""
key = "Wand"
def at_cmdset_creation(self):
super().at_cmdset_creation()
self.add(CmdFly)
class CmdGM(MuxCommand):
"""
The gm command allows anything to be emoted into a room.
"""
key = "gm"
aliases = ["#"]
locks = "cmd:perm(gm) or perm(Admin)"
def func(self):
send_to = []
for switch in self.switches:
o = self.caller.search(switch, global_search=True)
if o:
send_to = send_to + [o]
if not send_to:
send_to = [self.caller.location]
for o in send_to:
if o.is_typeclass('typeclasses.rooms.Room'):
o.msg_contents(self.args)
elif o.is_typeclass('typeclasses.characters.Character'):
o.msg(self.args)
logger.info(f"switches = {self.switches} lhs={self.lhs} rhs={self.rhs} / args={self.args}")

View file

@ -10,13 +10,11 @@ creation commands.
from re import match
from evennia.objects.objects import DefaultCharacter
from evennia.contrib.game_systems.gendersub import GenderCharacter
from evennia.contrib.rpg.rpsystem import ContribRPCharacter
from evennia.prototypes.spawner import spawn
from evennia.utils import delay, logger
from evennia.utils import delay # , logger
from commands.wizards import CmdSetWizardSpells
from utils.word_list import routput
from .objects import Object
from .tutorial import TutorBird, TutorialState
@ -95,6 +93,9 @@ class Character(Object, GenderCharacter, ContribRPCharacter):
letter.db.inside = READ_LETTER.format(self.name.capitalize())
letter.location = self
def get_display_things(self, looker, *args, **kwargs):
return super().get_display_things(looker, pose=False)
def do_take(self, to_take, from_whom):
"""
A character has a _steal_command. What are the limitations?
@ -112,7 +113,7 @@ class Character(Object, GenderCharacter, ContribRPCharacter):
self.msg(f"{victim.key} doesn't have a {to_take} you can take.")
def at_pre_move(self, destination, **kwargs):
def at_pre_move(self, destination, *args, **kwargs):
"""
Called by self.move_to when trying to move somewhere. If this returns
False, the move is immediately canceled.
@ -181,19 +182,7 @@ class Character(Object, GenderCharacter, ContribRPCharacter):
# function call.
return super().at_look(target)
class Wizard(Character):
"""
Upgrade a character with the following features:
@update self = typeclasses.character.Wizard
"""
def at_object_creation(self):
"""
Called when character is getting promoted to wizardy.
"""
self.cmdset.add(CmdSetWizardSpells)
def spell_sequence(self, location, messages, time_delay):
def spell_sequence(self, location, messages, time_delay=1):
"""
Send one or more messages to 'location' with a delay.
"""

View file

@ -13,6 +13,7 @@ from evennia.commands.command import Command
from evennia.utils import utils
from .objects import ObjectParent
from utils.word_list import choices
from commands.misc import CmdSetKnock
MOVE_DELAY = {"stroll": 6, "walk": 4, "run": 2, "sprint": 1}
@ -56,7 +57,7 @@ class Exit(ObjectParent, DefaultExit):
if pre_check:
moving = SPEED_DESCS[move_speed]
if self.db.traverse_msg:
msg = f"\n{self.db.traverse_msg}\n".format(move_speed, moving)
msg = choices(f"\n{self.db.traverse_msg}\n", move_speed, moving)
else:
if self.key in ['north', 'south', 'east', 'west']:
msg = f"You start {moving} {self.key}."

View file

@ -1,11 +1,4 @@
#!/usr/bin/env python
import datetime
from evennia import gametime
from evennia.utils import delay, logger
from evennia.contrib.rpg.rpsystem import ContribRPObject
"""
Object
@ -15,8 +8,8 @@ Use the ObjectParent class to implement common features for *all* entities
with a location in the game world (like Characters, Rooms, Exits).
"""
from evennia.objects.objects import DefaultObject
from evennia.contrib.rpg.rpsystem.rpsystem import ContribRPObject
from evennia.utils import delay
# from evennia.utils import delay, logger, search
@ -30,6 +23,8 @@ class ObjectParent:
take precedence.
"""
def get_display_footer(self, _, **kwargs):
return "\n"
class Object(ObjectParent, ContribRPObject):

View file

@ -73,7 +73,6 @@ class Pet(Object):
TICKER_HANDLER.add(interval=60, callback=self.update_state)
super().at_object_creation()
def hunger(self):
if self.db.hunger_level < Hunger.RAVENOUS.value:
return Hunger.RAVENOUS

View file

@ -64,8 +64,13 @@ class Room(ObjectParent, ExtendedRoom, ContribRPRoom):
def get_display_characters(self, looker, *args, **kwargs):
num_chars = len(self.contents_get(content_type="character"))
char_list = super().get_display_characters(looker, pose=True)
if len(self.filter_visible(self.contents_get(content_type="object"), looker, **kwargs)) > 0:
also = 'also '
else:
also = ''
if num_chars > 2:
return "You notice the following characters:\n" + char_list
return f"You {also}see the following characters:\n" + char_list
if num_chars > 1:
character = char_list.strip()
if character.startswith('|'):
@ -74,15 +79,19 @@ class Room(ObjectParent, ExtendedRoom, ContribRPRoom):
comp_char = character.lower()
if match(r"an? |the ", comp_char):
return "You notice " + character
return f"You {also}see {character}"
elif match(r"[aeiou]", comp_char):
return "You notice an " + character
return f"You {also}see an {character}"
else:
return "You notice a " + character
return f"You {also}see a {character}"
return ''
def get_display_things(self, looker, *args, **kwargs):
return super().get_display_things(looker, pose=False)
std_msg = super().get_display_things(looker, pose=False)
if std_msg:
return std_msg.replace('|wYou see:|n', 'You notice')
else:
return ''
class DabblersRoom(Room):
def get_display_desc(self, looker):

View file

@ -7,6 +7,7 @@ from evennia import create_script
from evennia.utils import logger, delay
from commands.misc import CmdSetPuddle, CmdSetStick, CmdSetKnock
from commands.wizards import CmdSetWand
from utils.word_list import routput, choices
from .scripts import KnockScript
from .objects import Object
@ -168,6 +169,11 @@ class Stick(Object):
thrower.msg("I think you should be outside or a place with more room before you throw that stick around.")
class Wand(Stick):
def at_object_creation(self):
self.cmdset.add_default(CmdSetWand)
class Puddle(Object):
def at_object_creation(self):
self.cmdset.add_default(CmdSetPuddle)
@ -430,7 +436,7 @@ class Knocker(Object):
curr_state = Knocker_Convo(talker.db.knocker_conversation_state or 0)
# message will be on the form `<Person> says, "say_text"`
# we want to get only say_text without the quotes and any spaces
message = message.split('says, ')[1].strip(' "')
message = message.split('"')[1].strip()
# Let's see if a keyword gives a good response:
for [regex, convo_state, responses, new_state] in self.all_responses:

File diff suppressed because it is too large Load diff