Casting spells ... first, is donkey head

Spells can affect a characters speech and their appearance.
All spells should create attributes with a 'effect' category,
and spell scripts will clean up after they are stopped.
This commit is contained in:
Howard Abrams 2025-05-10 21:57:12 -07:00
parent 0a955fe2b6
commit 951d35356d
8 changed files with 411 additions and 247 deletions

View file

@ -21,7 +21,7 @@ from evennia.contrib.rpg.rpsystem import RPSystemCmdSet
from evennia.contrib.rpg.character_creator.character_creator import ContribChargenCmdSet from evennia.contrib.rpg.character_creator.character_creator import ContribChargenCmdSet
from commands.sittables import CmdNoSitStand from commands.sittables import CmdNoSitStand
from commands.everyone import CmdTake, CmdThink, CmdSay, CmdWhisper from commands.everyone import CmdTake, CmdThink, CmdSay, CmdWhisper
from commands.wizards import CmdGM, CmdGMTrigger from commands.wizards import CmdGM, CmdSpell, CmdGMTrigger
class CharacterCmdSet(default_cmds.CharacterCmdSet): class CharacterCmdSet(default_cmds.CharacterCmdSet):
""" """
@ -46,6 +46,7 @@ class CharacterCmdSet(default_cmds.CharacterCmdSet):
self.add(CmdSay) self.add(CmdSay)
self.add(CmdWhisper) self.add(CmdWhisper)
self.add(CmdGM) self.add(CmdGM)
self.add(CmdSpell)
self.add(CmdGMTrigger) self.add(CmdGMTrigger)

View file

@ -9,7 +9,29 @@ from evennia.commands.default.muxcommand import MuxCommand
from evennia.contrib.rpg.rpsystem import send_emote from evennia.contrib.rpg.rpsystem import send_emote
from evennia.utils import iter_to_str, logger from evennia.utils import iter_to_str, logger
from utils.word_list import routput, paragraph 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): class CmdWhisper(MuxCommand):
@ -146,12 +168,19 @@ class CmdSay(MuxCommand):
else: else:
verb = "say" 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', # 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 say ...', we have to both send him a message with the
# 'You' as well as everyone else with the 'send_emote'. # '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 '' to_whom = chars_list(self.lhs, verb, for_rp=False) if 'to' in self.switches else ''
full_speech = f"You {adverb}{verb}{to_whom}, \"{speech}\"" full_speech = f"You {adverb}{verb}{to_whom}, \"{for_me}\""
self.caller.msg(full_speech, from_obj=self.caller) self.caller.msg(full_speech, from_obj=self.caller)
# English is weird... # English is weird...
@ -160,7 +189,7 @@ class CmdSay(MuxCommand):
targets = [item for item in self.caller.location.contents if item != self.caller] 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 '' to_whom = chars_list(self.lhs, verb) if 'to' in self.switches else ''
full_speech = f"/Me {adverb}{verb}s{to_whom}, \"{speech}\"" full_speech = f"/Me {adverb}{verb}s{to_whom}, \"{for_others}\""
send_emote(self.caller, targets, full_speech, msg_type="say", anonymous_add=None, quiet=True) send_emote(self.caller, targets, full_speech, msg_type="say", anonymous_add=None, quiet=True)

View file

@ -2,11 +2,13 @@
from re import match from re import match
from evennia import CmdSet from evennia import CmdSet, create_script
from evennia.utils import delay, logger from evennia.utils import delay, logger
from evennia.commands.default.muxcommand import MuxCommand from evennia.commands.default.muxcommand import MuxCommand
from evennia.contrib.rpg.rpsystem import send_emote
from .command import Command from .command import Command
from typeclasses.scripts import DonkeyHeadSpell
from utils.word_list import routput from utils.word_list import routput
class CmdFly(Command): class CmdFly(Command):
@ -65,8 +67,42 @@ class CmdGM(MuxCommand):
elif o.is_typeclass('typeclasses.characters.Character'): elif o.is_typeclass('typeclasses.characters.Character'):
o.msg(self.args) o.msg(self.args)
logger.info(f"switches = {self.switches} lhs={self.lhs} rhs={self.rhs} / args={self.args}")
class CmdSpell(Command):
key = "spell"
aliases = ['cast']
locks = "cmd:perm(gm) or perm(Admin)"
def parse(self):
self.spell = None
self.target = None
m = match(r"([^ ]+)( +on +(.+))?", self.args.strip())
if m:
self.spell = m.group(1)
self.target = m.group(3)
def func(self):
caster = self.caller
if not self.spell:
caster.msg('Usage: cast <spell> [on <target>]')
return
char = None
if self.target:
char = caster.search(self.target)
if not char:
return
if self.spell == 'donkey' and char:
create_script(key="donkey_head",
typeclass=DonkeyHeadSpell,
interval=130,
start_delay=True,
attributes=[("target", char)])
caster.msg(f"You cast |wHead of Donkey|n on {char}")
else:
caster.msg(f"You fail to cast {self.spell}")
class CmdGMTrigger(Command): class CmdGMTrigger(Command):
"""The trigger command kicks off a series of named events. """The trigger command kicks off a series of named events.

View file

@ -8,7 +8,7 @@ creation commands.
""" """
from re import match from re import match, compile
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
@ -16,7 +16,7 @@ from evennia.contrib.rpg.rpsystem import send_emote
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 utils.word_list import routput from utils.word_list import routput, choices
from .objects import Object from .objects import Object
from .tutorial import TutorBird, TutorialState from .tutorial import TutorBird, TutorialState
@ -115,6 +115,51 @@ 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 pronoun_subjective(self, uppercase=False):
gender = self.attributes.get('gender')
if gender == "male":
results = 'He'
elif gender == "female":
results = 'She'
elif gender == "neutral":
results = 'It'
else:
results = 'They'
return results if uppercase else results.lower()
def gendered_text(self, text):
"""
Replace entries, like |s and |p with pronouns.
Like 'he' and 'her'
"""
gender_rx = compile(r"(?<!\|)\|(?!\|)[sSoOpPaA]")
return gender_rx.sub(lambda x: self._get_pronoun(x), text)
def return_appearance(self, looker, **kwargs):
"""
Can be overridden or appended with an effect.
Does the looker affect this?
"""
# To replace, temporarily, a description:
# @set woman/temp_desc:effect = "They fade away from view."
new_desc = self.attributes.get(category="effect",
key="temp_desc")
if new_desc:
return self.gendered_text(new_desc)
pre_desc = self.attributes.get(category="effect",
key="pre_desc") or ""
post_desc = self.attributes.get(category="effect",
key="post_desc") or ""
reg_desc = super().return_appearance(looker)
pronoun = self.pronoun_subjective(True)
return self.gendered_text(pre_desc + \
reg_desc.replace('|wYou see:|n',
'|S has') + \
post_desc)
def at_pre_move(self, destination, *args, **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

View file

@ -551,7 +551,7 @@ class BHB(Friendly):
self.adjust_character(feeder, 40) self.adjust_character(feeder, 40)
feeder.has('berries').delete() feeder.has('berries').delete()
else: else:
msg = f"{noun} doesn't appear interesting in anything you have." msg = f"{noun} doesn't appear interested in anything you have."
feeder.msg(msg) feeder.msg(msg)

View file

@ -25,7 +25,7 @@ class Puppet(Character):
puppeting this Object. puppeting this Object.
""" """
self.msg("\nYou are puppeting |c{name}|n.\n".format(name=self.key)) self.msg("\nYou are puppeting |c{name}|n.".format(name=self.key))
self.msg((self.at_look(self.location), {"type": "look"}), options=None) self.msg((self.at_look(self.location), {"type": "look"}), options=None)
def at_post_unpuppet(self, account=None, session=None, **kwargs): def at_post_unpuppet(self, account=None, session=None, **kwargs):

View file

@ -145,3 +145,28 @@ Well, by sticky, we mean, wizardly-sticky...an absolutely amazing looking stick.
})[0] })[0]
stick.location = woods stick.location = woods
class Spell(Script):
"""
A script to clean up the effects of a spell.
"""
def at_stop(self, **kwargs):
target = self.attributes.get("target")
target.attributes.clear(category="effect")
self.delete()
class DonkeyHeadSpell(Spell):
def at_start(self, **kwargs):
target = self.attributes.get("target")
target.attributes.add(
"donkied",
"Heehaw! ;; Heehaw, heehaw!",
category="effect")
target.attributes.add(
"post_desc",
"\nOh, and |s has a donkey's head!",
category="effect")
target.msg("You suddenly feel quite peculiar.")
def at_repeat(self, **kwargs):
self.stop()

File diff suppressed because it is too large Load diff