moss-n-puddles/commands/wizards.py
2025-05-27 18:39:22 -07:00

281 lines
8 KiB
Python
Executable file

#!/usr/bin/env python
from random import choice
from re import match
from evennia import CmdSet, create_script
from evennia.utils import delay, logger
from evennia.commands.default.muxcommand import MuxCommand
from evennia.contrib.rpg.rpsystem import send_emote
from .command import Command
from typeclasses.scripts import DonkeyHeadSpell
from typeclasses.drinkables import Cocktail
from utils.word_list import routput
class CmdFly(Command):
"""Cast the 'fly' spell.
Make sure you set the following properties, for instance:
@set self/disappear_msg = "The wizard disappears in a puff of smoke."
@set self/reappear_msg = "A plume of <<white ^ light blue ^ gray ^ >>
smoke appears... ;; When the smoke clears, a
wizard <<emerges ^ materializes>>."
@set self/appear_delay = 3
The last setting is the number of seconds between message segments
(those are separated by double semicolons).
"""
key = "fly"
locks = "cmd:holds()"
def func(self):
"""
Call the 'do_fly' method on the caller.
"""
self.caller.do_fly(self.args.strip())
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 CmdMakeCocktail(MuxCommand):
"""
For the 'Bartender' especially.
Usage:
shake |wcocktail|n = |wpatron|n
If patron is not given or not found, the drink will be in your
inventory, and you can call |ggive|n to pass it along.
If cocktail name isn't given (or matches anything), a random
one will be created.
"""
key = 'shake'
locks = "cmd:perm(gm) or perm(Admin)"
def func(self):
dest = self.caller
if self.rhs:
dest = self.caller.search(self.rhs)
Cocktail.make(dest, self.lhs)
class CmdGM(MuxCommand):
"""
The gm command allows anything to be emoted into a room.
Usage:
gm A bat flies into the room!
gm/gnome You hear a distant ringing
"""
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]
me = self.caller
msg = routput(self.args)
for o in send_to:
if o.is_typeclass('typeclasses.rooms.Room'):
chars = o.contents_get(None, 'character')
send_emote(me, chars, msg, 'say', None)
elif o.is_typeclass('typeclasses.characters.Character'):
o.msg(msg)
class CmdSpell(Command):
"""
Cast one of the few spells we've created that affect others.
Usage:
spell donkey on lizardman
"""
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):
"""The trigger command kicks off a series of named events.
Usage:
trigger trigger-name
trigger :game trigger-name
trigger/character trigger-name
trigger/character:game trigger-name
Where 'game' defaults to the value previously set:
@set npc/currentgame = "session1"
Triggers are typically set on the NPC (which would be in the room
with the PCs) or the room. Using the command:
@set npc/triggers:session1 = {"darkness": {"desc": "Make the
room go black", "timer": 1, "events": [ "The room gets dark",
"And then pitch-black.", ("You can't help it, but scream!", "You
hear a scream!")]}}
The 'set' command, as a complicated data structure, should be set
in a batchcommand.
"""
key = "trigger"
aliases = ["trig"]
locks = "cmd:perm(gm) or perm(Admin)"
def parse(self):
m = match(r" *(/[^: ]+)?(:[^ ]+)? *(.*)", self.args)
if m:
self.name = m.group(3)
if m.group(2):
self.game = m.group(2)[1:]
else:
self.game = None
if m.group(1):
self.switches = m.group(1)[1:].split(',')
else:
self.switches = []
else:
self.caller.msg("Usage trigger/dest:game trigger")
return False
def func(self):
npc = self.caller
self.send_to = []
for switch in self.switches:
o = npc.search(switch)
if o:
self.send_to = self.send_to + [o]
else:
return
game = self.game or npc.db.currentgame
if not game:
npc.msg("Specify the game, or set a default with |g@set self/currentgame = <game>")
return
triggers = dict(npc.attributes.get(key='triggers', category=game))
if not self.name:
npc.msg("What event do you want to trigger?")
for name, details in triggers.items():
npc.msg(f" - {name} : {details['desc']}")
return
trigger = triggers[self.name]
if trigger:
npc.msg(f"Triggering: |w{trigger['desc']}")
self.trigger(trigger['events'],
trigger.get('timer', 5),
self.send_to)
else:
npc.msg(f"Didn't find '{self.name}' to trigger.")
def trigger(self, events, time_delay, dests=[]):
"""
Given a list of events, send each events at an interval.
If an event is a tuple instead of a string, the first
element goes to 'dests' and the second goes to the room
(based on the caller's location).
"""
target = None
if len(self.send_to) > 0:
target = self.send_to[0]
else:
targets = self.caller.characters_here()
if targets:
target = choice(targets)
if target:
name = target.db._sdesc or target.key
else:
name = ''
for idx, event in enumerate(events):
if isinstance(event, str):
if event.startswith("@"):
self.caller.execute_cmd(event[1:])
else:
if target:
msg = target.gendered_text(routput(event, name))
else:
msg = routput(event)
delay(time_delay * idx,
self.caller.location.msg_contents,
"\n" + msg)
else:
char = target.gendered_text(routput(event[0], name))
room = target.gendered_text(routput(event[1], name))
if room:
delay(time_delay * idx,
self.caller.location.msg_contents,
"\n" + routput(room),
exclude=dests)
if char:
for dest in dests:
delay(time_delay * idx,
dest.msg,
"\n" + routput(char))