Automated puppets react on triggers
This commit is contained in:
parent
428fa8334b
commit
2e69558b3f
5 changed files with 187 additions and 42 deletions
|
|
@ -123,32 +123,14 @@ class CmdSay(MuxCommand):
|
|||
"""
|
||||
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}")
|
||||
speaker = self.caller
|
||||
|
||||
if not self.args:
|
||||
self.caller.msg("Say what?")
|
||||
speaker.msg("Say what?")
|
||||
return
|
||||
|
||||
if 'to' in self.switches and not self.rhs:
|
||||
self.caller.msg(paragraph("""
|
||||
speaker.msg(paragraph("""
|
||||
When attempting to say something to one or more
|
||||
characters, use the '=' character to identify what you
|
||||
want to say.
|
||||
|
|
@ -161,7 +143,7 @@ class CmdSay(MuxCommand):
|
|||
speech = self.args
|
||||
|
||||
# If speech is empty, stop here
|
||||
if not self.caller.at_pre_say(speech):
|
||||
if not speaker.at_pre_say(speech):
|
||||
return
|
||||
|
||||
adverb = ''
|
||||
|
|
@ -188,27 +170,50 @@ class CmdSay(MuxCommand):
|
|||
|
||||
for_me, for_others, verb = \
|
||||
speech_effect(speech, verb,
|
||||
self.caller,
|
||||
self.caller.attributes.get(category="effect",
|
||||
speaker,
|
||||
speaker.attributes.get(category="effect",
|
||||
return_obj=True,
|
||||
return_list=True))
|
||||
|
||||
to_who = to_whom = ''
|
||||
|
||||
if 'to' not in self.switches:
|
||||
for char in speaker.location.contents:
|
||||
if hasattr(char, 'other_say') and callable(char.other_say):
|
||||
char.other_say(speaker, for_others)
|
||||
else:
|
||||
# Send the message to 'puppets' that have the special 'other_sayto'
|
||||
# method (if they are the object of the message):
|
||||
targets = split(r" *, *", self.lhs)
|
||||
to_chars = [speaker.search(c, quiet=True)[0] for c in targets]
|
||||
for char in to_chars:
|
||||
if hasattr(char, 'other_sayto') and callable(char.other_sayto):
|
||||
logger.info(f"Found {char.key}: {for_others}")
|
||||
char.other_sayto(speaker, for_others)
|
||||
|
||||
who = iter_to_str([c.get_display_name(speaker) for c in to_chars])
|
||||
whom = iter_to_str([f"/{c}" for c in targets])
|
||||
|
||||
if verb == "ask":
|
||||
to_who = " " + who
|
||||
to_whom = " " + whom
|
||||
else:
|
||||
to_who = " to " + who
|
||||
to_whom = " to " + whom
|
||||
|
||||
# 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)
|
||||
full_speech = f"You {adverb}{verb}{to_who}, \"{for_me}\""
|
||||
speaker.msg(full_speech, from_obj=speaker)
|
||||
|
||||
# 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 ''
|
||||
targets = [item for item in speaker.location.contents if item != speaker]
|
||||
full_speech = f"/me {adverb}{verb}s{to_whom}, \"{for_others}\""
|
||||
send_emote(self.caller, targets, full_speech, msg_type="say", anonymous_add=None)
|
||||
send_emote(speaker, targets, full_speech, msg_type="say", anonymous_add=None)
|
||||
|
||||
|
||||
class CmdThink(Command):
|
||||
|
|
|
|||
|
|
@ -97,7 +97,9 @@ class Character(Object, GenderCharacter, ContribRPCharacter):
|
|||
This just looks better to me.
|
||||
"""
|
||||
def capitalize_line(line):
|
||||
if match(r"^\|[bm][a-z].*", line):
|
||||
if not line or line.strip() == "":
|
||||
return line
|
||||
elif match(r"^\|[bm][a-z].*", line):
|
||||
return "The " + line
|
||||
elif match(r"^\|[A-z][a-z].*", line):
|
||||
return line[0:1] + line[2].upper() + line[3:]
|
||||
|
|
@ -326,3 +328,26 @@ class Character(Object, GenderCharacter, ContribRPCharacter):
|
|||
self.spell_sequence(self.location,
|
||||
self.db.reappear_msg.split(';;'),
|
||||
self.db.appear_delay or 2)
|
||||
|
||||
# Hooks to the puppets:
|
||||
|
||||
def puppets_here(self):
|
||||
"""
|
||||
Return a list of puppets in the current location.
|
||||
Only used for calling hooks on the animatronic dolls.
|
||||
"""
|
||||
return [puppet for puppet in
|
||||
self.search("", typeclass="typeclasses.puppets.Puppet",
|
||||
location=self.location, quiet=True)
|
||||
if puppet != self]
|
||||
|
||||
def at_post_move(self, past_location, move_type="move", **kwargs):
|
||||
super().at_post_move(past_location, move_type)
|
||||
for puppet in self.puppets_here():
|
||||
puppet.other_arrive(self)
|
||||
|
||||
def at_pre_move(self, destination, move_type="move", **kwargs):
|
||||
super().at_pre_move(destination, move_type)
|
||||
for puppet in self.puppets_here():
|
||||
puppet.other_leave(self)
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -8,11 +8,16 @@ Use the ObjectParent class to implement common features for *all* entities
|
|||
with a location in the game world (like Characters, Rooms, Exits).
|
||||
|
||||
"""
|
||||
|
||||
from re import split, match
|
||||
|
||||
# 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
|
||||
# from evennia.utils import delay, logger, search
|
||||
|
||||
from utils.word_list import routput
|
||||
|
||||
|
||||
class ObjectParent:
|
||||
"""
|
||||
|
|
@ -245,6 +250,50 @@ class Object(ObjectParent, ContribRPObject):
|
|||
|
||||
return None
|
||||
|
||||
def delay_sequence(self, sequence_str, time_delay=1, *args):
|
||||
"""Run a sequence of messages or commands with a delay.
|
||||
|
||||
The 'sequence_str' is a number of commands separated by ';;'
|
||||
character sequence. The command can by standard things like
|
||||
'say' or 'give', but can be a message delivered to the room
|
||||
location, if it begins with a # or 'gm'.
|
||||
|
||||
The command can also be a number, in which case, the next
|
||||
command (and all subsequent commands), will be delayed by that
|
||||
number of seconds.
|
||||
|
||||
For instance:
|
||||
|
||||
'say Hello there, want a drink? ;; 8 ;; # He works on shaking a cocktail. ;; 2 ;; shake whisky = avatar'
|
||||
|
||||
Which could show:
|
||||
|
||||
Blonde elf says, "Hello there, want a drink?"
|
||||
<8 seconds pass>
|
||||
He works on shaking a cocktail.
|
||||
<2 seconds pass>
|
||||
You now have a whisky.
|
||||
"""
|
||||
def convert(x):
|
||||
try:
|
||||
return int(x)
|
||||
except ValueError:
|
||||
return x
|
||||
|
||||
lines = [convert(line) for line in split(r" *;; *", sequence_str)]
|
||||
pause = 0
|
||||
for line in lines:
|
||||
if isinstance(line, int):
|
||||
time_delay = line
|
||||
else:
|
||||
pause = pause + time_delay
|
||||
|
||||
m = match(r"(# *|gm +)(.*)", line)
|
||||
if m:
|
||||
delay(pause, self.location.msg_contents, routput(m.group(2), *args))
|
||||
else:
|
||||
delay(pause, self.execute_cmd, routput(line, *args))
|
||||
|
||||
def at_post_move(self, source_location, move_type="move", **kwargs):
|
||||
"""
|
||||
Delete ourselves if we are living inside a pet.
|
||||
|
|
@ -252,3 +301,11 @@ class Object(ObjectParent, ContribRPObject):
|
|||
super().at_post_move(source_location, move_type)
|
||||
if self.location.is_typeclass("typeclasses.pets.Pet"):
|
||||
delay(5, self.delete)
|
||||
|
||||
def at_give(self, giver, getter, **kwargs):
|
||||
"""
|
||||
Call a puppet's 'other_given' method, if defined.
|
||||
"""
|
||||
super().at_give(giver, getter)
|
||||
if hasattr(getter, 'other_given') and callable(getter.other_given):
|
||||
getter.other_given(giver, self)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from re import split, match, IGNORECASE
|
||||
from evennia.utils import logger
|
||||
|
||||
from typeclasses.characters import Character
|
||||
from utils.word_list import routput
|
||||
|
||||
|
|
@ -25,7 +28,7 @@ class Puppet(Character):
|
|||
puppeting this Object.
|
||||
|
||||
"""
|
||||
self.msg("\nYou are puppeting |c{name}|n.".format(name=self.key))
|
||||
self.msg(f"\nYou are puppeting |c{self.key}|n.")
|
||||
self.msg((self.at_look(self.location), {"type": "look"}), options=None)
|
||||
|
||||
def at_pre_unpuppet(self, **kwargs):
|
||||
|
|
@ -51,7 +54,7 @@ class Puppet(Character):
|
|||
overriding the call (unused by default).
|
||||
|
||||
"""
|
||||
self.msg("\nNo longer puppeting |c{name}|n.\n".format(name=self.key))
|
||||
self.msg(f"\nNo longer puppeting |c{self.key}|n.\n")
|
||||
|
||||
def get_display_desc(self, _, **kwargs):
|
||||
"""
|
||||
|
|
@ -68,3 +71,62 @@ class Puppet(Character):
|
|||
return self.db.desc_puppeted if self.db.desc_puppeted else self.db.desc
|
||||
else:
|
||||
return self.db.desc_unpuppeted if self.db.desc_unpuppeted else self.db.desc
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# TRIGGERS
|
||||
|
||||
def get_character_label(self, character):
|
||||
return character.get_display_name(self).split(' ')[-1]
|
||||
|
||||
def trigger_sequence(self, seq, character, *other_stuff):
|
||||
self.delay_sequence(seq, 1, character.key, character.get_display_name(self),
|
||||
self.get_character_label(character), *other_stuff)
|
||||
|
||||
def get_attribute_trigger(self, label, character):
|
||||
"""
|
||||
Return the attribute where 'key' is the label, and the
|
||||
category can by either the 'character' or its display name.
|
||||
"""
|
||||
name = self.get_character_label(character)
|
||||
|
||||
return (self.attributes.get(key=label, category=character.key) or
|
||||
self.attributes.get(key=label, category=name) or
|
||||
self.attributes.get(key=label))
|
||||
|
||||
def move_triggers(self, label, character):
|
||||
"""
|
||||
Return a list of triggers matching 'label' and 'character'.
|
||||
"""
|
||||
seq = self.get_attribute_trigger(label, character)
|
||||
if seq:
|
||||
self.trigger_sequence(seq, character)
|
||||
|
||||
def other_arrive(self, character):
|
||||
"""
|
||||
Execute a command when a character arrives in the same location.
|
||||
"""
|
||||
self.move_triggers('arrive', character)
|
||||
|
||||
def other_leave(self, character):
|
||||
"""
|
||||
Execute a command when a character arrives in the same location.
|
||||
"""
|
||||
self.move_triggers('leave', character)
|
||||
|
||||
def say_triggers(self, label, character, speech):
|
||||
trigs = self.get_attribute_trigger(label, character)
|
||||
if trigs:
|
||||
for _, trigger in enumerate(dict(trigs)):
|
||||
seq = trigs[trigger]
|
||||
if match(trigger, speech, IGNORECASE):
|
||||
self.trigger_sequence(seq, character)
|
||||
|
||||
def other_say(self, speaker, speech):
|
||||
self.say_triggers('say', speaker, speech)
|
||||
|
||||
def other_sayto(self, speaker, speech):
|
||||
self.say_triggers('sayto', speaker, speech)
|
||||
|
||||
def other_given(self, giver, obj):
|
||||
target = giver.get_display_name(self).split(' ')[-1]
|
||||
self.execute_cmd(f"emote /Me says to /{target}, \"Thanks for the {obj.name}.\"")
|
||||
|
|
|
|||
|
|
@ -115,14 +115,10 @@ def choices(text, *substitutions):
|
|||
Note that text can already be separated as a list or tuple.
|
||||
"""
|
||||
if isinstance(text, str):
|
||||
selections = split(r"\s*;;\s*", text)
|
||||
elif isinstance(text, (tuple, list)):
|
||||
selections = text
|
||||
else:
|
||||
selections = None
|
||||
text = split(r"\s*;;\s*", text)
|
||||
|
||||
if selections:
|
||||
return routput(choice(selections), *substitutions)
|
||||
if text:
|
||||
return routput(choice(text), *substitutions)
|
||||
|
||||
|
||||
def split_party_msg(viewer, msg, *substitutions):
|
||||
|
|
|
|||
Loading…
Reference in a new issue