#!/usr/bin/env python
from os.path import exists
from datetime import datetime
from re import split, match, sub, IGNORECASE
from shutil import copyfile
from evennia import CmdSet
from evennia.utils import logger
from commands.command import Command
from typeclasses.characters import Character
from utils.word_list import routput
class Puppet(Character):
"""
Special character that if not puppetable, stays put.
Perhaps responding or logging information to the GM.
"""
def at_post_puppet(self, **kwargs):
"""
Called just after puppeting has been completed and all
Account<->Object links have been established.
Args:
**kwargs (dict): Arbitrary, optional arguments for users
overriding the call (unused by default).
Notes:
You can use `self.account` and `self.sessions.get()` to get
account and sessions at this point; the last entry in the
list from `self.sessions.get()` is the latest Session
puppeting this Object.
"""
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):
"""
Reset our pose.
"""
super().at_pre_unpuppet()
self.execute_cmd("pose reset")
def at_post_unpuppet(self, account=None, session=None, **kwargs):
"""
Make the character object remain in the room.
Args:
account (DefaultAccount): The account object that just disconnected
from this object.
session (Session): Session controlling the connection that
just disconnected.
Keyword Args:
reason (str): If given, adds a reason for the unpuppet. This
is set when the user is auto-unpuppeted due to being link-dead.
**kwargs: Arbitrary, optional arguments for users
overriding the call (unused by default).
"""
self.msg(f"\nNo longer puppeting |c{self.key}|n.\n")
def get_display_desc(self, _, **kwargs):
"""
Return one of two appearances based on if it is being puppeted or not.
Set either or both:
@set me/desc_puppeted = "A clown bouncing up and down from a box."
@set me/desc_uppuppeted = "A colorful box with a closed lid."
If either is not specified, it uses the standard description.
"""
if self.tags.get(key='puppeted', category='account'):
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}.\"")
class CmdShrubSay(Command):
"""Erase and write on the shrub's chalkboard.
"""
key = "say"
aliases = ["write"]
def func(self):
here = self.caller.location
msg = routput("The shrub uses a branch to << brush ^ wipe >> << off ^ >> << the chalk from ^ >> its << small ^ >> chalkboard, and with another branch, << slowly ^ carefully ^ deliberately ^ >> starts to write << something ^ a message ^ >>.")
here.msg_contents(msg)
self.caller.db.inside = self.args.strip()
self.caller.record_msg(f"The chalkboard reads: |w{self.args}|n")
class CmdSetShrubSay(CmdSet):
"""
The shrub's version of the 'say' command.
"""
def at_cmdset_creation(self):
super().at_cmdset_creation()
self.add(CmdShrubSay)
class Shrub(Puppet):
"""
The 'Shrub' has its own way of communicating.
"""
def at_object_creation(self):
"Called when a character is first created."
self.cmdset.add(CmdSetShrubSay, persistent=True)
def find_actor(self, msg, default=None):
"""
Extract the character performing the action from the text.
"""
if default:
return default
m = match(r".*\|b(.*?)\|n.*", msg)
if m:
return m.group(1)
def capitalize_msg(self, msg, msg_type=None):
"""
If msg is lowercase, capitalize it.
Maybe prepend a 'The' to the front.
"""
if msg_type == 'traverse' or msg_type == 'teleport':
if match(r"^(\|[A-z])?[aeiou]", msg):
return "An " + msg
else:
return "A " + msg
elif msg_type == 'say' and match(r"^\|b[a-z]", msg):
return "The " + msg
elif match(r"^(\|[A-z])[a-z]", msg) and len(msg) > 2:
return msg[0:1] + msg[2].upper() + msg[3:]
elif match(r"^(\|[A-z])?[a-z]", msg) and len(msg) > 2:
return msg[0].upper() + msg[1:]
return msg
def to_html(self, msg, msg_type=None):
"""
Convert the 'msg' to an HTML formatted string.
"""
# Bold anything white:
msg = sub(r"\|w(.*?)\|n", '\\1', msg)
# Remove the rest:
msg = sub(r"\|[A-z]", '', msg)
# Remove initial or final carriage returns:
msg = sub(r"^\n", '', msg)
msg = sub(r"\n$", '', msg)
# Convert the carriage returns:
msg = sub(r"\n", '
\n', msg)
if msg_type == 'look':
msg = msg[0].upper + msg[1:]
return f"
{msg}
{msg}
\n" def record_msg(self, text, actor=None): msg_type = None if isinstance(text, tuple): if text[1] and isinstance(text[1], dict): msg_type = text[1]['type'] msg = self.capitalize_msg(text[0], msg_type) else: msg = self.capitalize_msg(str(text)) actor = self.find_actor(msg, actor) msg = self.to_html(msg, msg_type) filename = datetime.today().strftime('transcripts/%Y-%m-%d.html') if not exists(filename): copyfile("transcripts/header-template.html", filename) with open(filename, "a") as myfile: myfile.write(msg) # Follow-up Actions ... if match(r".* arrives .*", msg) and actor and \ msg_type in ('traverse', 'teleport'): self.execute_cmd(f"look {actor}") def msg(self, text=None, from_obj=None, session=None, **kwargs): """ Record everything that happens in the room for a transcript. Some key events may trigger more actions to get more information. """ self.record_msg(text, from_obj) super().msg(text, from_obj, session)