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.game_systems.gendersub import SetGender
from evennia.contrib.rpg.rpsystem import RPSystemCmdSet from evennia.contrib.rpg.rpsystem import RPSystemCmdSet
from commands.sittables import CmdNoSitStand 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): class CharacterCmdSet(default_cmds.CharacterCmdSet):
""" """
@ -41,6 +42,8 @@ class CharacterCmdSet(default_cmds.CharacterCmdSet):
self.add(SetGender()) self.add(SetGender())
self.add(RPSystemCmdSet()) self.add(RPSystemCmdSet())
self.add(extended_room.ExtendedRoomCmdSet) self.add(extended_room.ExtendedRoomCmdSet)
self.add(CmdSay())
self.add(CmdGM)
class AccountCmdSet(default_cmds.AccountCmdSet): class AccountCmdSet(default_cmds.AccountCmdSet):

View file

@ -1,13 +1,92 @@
#!/usr/bin/env python #!/usr/bin/env python
from random import random from random import random
from re import split
from commands.command import Command from commands.command import Command
from evennia.commands.default.general import NumberedTargetCommand 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 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): class CmdThink(Command, NumberedTargetCommand):
"""Think a thought out loud. """Think a thought out loud.

View file

@ -1,7 +1,10 @@
#!/usr/bin/env python #!/usr/bin/env python
from .command import Command
from evennia import CmdSet from evennia import CmdSet
from evennia.utils import logger
from evennia.commands.default.muxcommand import MuxCommand
from .command import Command
class CmdFly(Command): class CmdFly(Command):
@ -18,12 +21,46 @@ class CmdFly(Command):
""" """
key = "^fly" key = "^fly"
locks = "call:all()"
def func(self): def func(self):
self.caller.do_fly(self.args.strip()) 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): def at_cmdset_creation(self):
super().at_cmdset_creation() super().at_cmdset_creation()
self.add(CmdFly) 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 re import match
from evennia.objects.objects import DefaultCharacter
from evennia.contrib.game_systems.gendersub import GenderCharacter from evennia.contrib.game_systems.gendersub import GenderCharacter
from evennia.contrib.rpg.rpsystem import ContribRPCharacter from evennia.contrib.rpg.rpsystem import ContribRPCharacter
from evennia.prototypes.spawner import spawn 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 utils.word_list import routput
from .objects import Object from .objects import Object
from .tutorial import TutorBird, TutorialState from .tutorial import TutorBird, TutorialState
@ -95,6 +93,9 @@ class Character(Object, GenderCharacter, ContribRPCharacter):
letter.db.inside = READ_LETTER.format(self.name.capitalize()) letter.db.inside = READ_LETTER.format(self.name.capitalize())
letter.location = self 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): def do_take(self, to_take, from_whom):
""" """
A character has a _steal_command. What are the limitations? 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.") 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 Called by self.move_to when trying to move somewhere. If this returns
False, the move is immediately canceled. False, the move is immediately canceled.
@ -181,19 +182,7 @@ class Character(Object, GenderCharacter, ContribRPCharacter):
# function call. # function call.
return super().at_look(target) return super().at_look(target)
class Wizard(Character): def spell_sequence(self, location, messages, time_delay=1):
"""
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):
""" """
Send one or more messages to 'location' with a delay. 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 evennia.utils import utils
from .objects import ObjectParent from .objects import ObjectParent
from utils.word_list import choices
from commands.misc import CmdSetKnock from commands.misc import CmdSetKnock
MOVE_DELAY = {"stroll": 6, "walk": 4, "run": 2, "sprint": 1} MOVE_DELAY = {"stroll": 6, "walk": 4, "run": 2, "sprint": 1}
@ -56,7 +57,7 @@ class Exit(ObjectParent, DefaultExit):
if pre_check: if pre_check:
moving = SPEED_DESCS[move_speed] moving = SPEED_DESCS[move_speed]
if self.db.traverse_msg: 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: else:
if self.key in ['north', 'south', 'east', 'west']: if self.key in ['north', 'south', 'east', 'west']:
msg = f"You start {moving} {self.key}." msg = f"You start {moving} {self.key}."

View file

@ -1,11 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python
import datetime
from evennia import gametime
from evennia.utils import delay, logger
from evennia.contrib.rpg.rpsystem import ContribRPObject
""" """
Object 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). with a location in the game world (like Characters, Rooms, Exits).
""" """
from evennia.contrib.rpg.rpsystem.rpsystem import ContribRPObject
from evennia.objects.objects import DefaultObject from evennia.utils import delay
# from evennia.utils import delay, logger, search # from evennia.utils import delay, logger, search
@ -30,6 +23,8 @@ class ObjectParent:
take precedence. take precedence.
""" """
def get_display_footer(self, _, **kwargs):
return "\n"
class Object(ObjectParent, ContribRPObject): class Object(ObjectParent, ContribRPObject):

View file

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

View file

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

View file

@ -7,6 +7,7 @@ from evennia import create_script
from evennia.utils import logger, delay from evennia.utils import logger, delay
from commands.misc import CmdSetPuddle, CmdSetStick, CmdSetKnock from commands.misc import CmdSetPuddle, CmdSetStick, CmdSetKnock
from commands.wizards import CmdSetWand
from utils.word_list import routput, choices from utils.word_list import routput, choices
from .scripts import KnockScript from .scripts import KnockScript
from .objects import Object 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.") 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): class Puddle(Object):
def at_object_creation(self): def at_object_creation(self):
self.cmdset.add_default(CmdSetPuddle) self.cmdset.add_default(CmdSetPuddle)
@ -430,7 +436,7 @@ class Knocker(Object):
curr_state = Knocker_Convo(talker.db.knocker_conversation_state or 0) curr_state = Knocker_Convo(talker.db.knocker_conversation_state or 0)
# message will be on the form `<Person> says, "say_text"` # message will be on the form `<Person> says, "say_text"`
# we want to get only say_text without the quotes and any spaces # 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: # Let's see if a keyword gives a good response:
for [regex, convo_state, responses, new_state] in self.all_responses: for [regex, convo_state, responses, new_state] in self.all_responses:

File diff suppressed because it is too large Load diff