Create a recorder
These objects record the goings-on of a room into an HTML file.
This commit is contained in:
parent
b33f6ca7df
commit
be13c8cb6e
8 changed files with 329 additions and 107 deletions
|
|
@ -1,17 +1,19 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
from random import choice
|
from random import choice
|
||||||
from re import match
|
from re import match, findall
|
||||||
|
|
||||||
from evennia import CmdSet, create_script
|
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 evennia.contrib.rpg.rpsystem import send_emote
|
||||||
|
from evennia.prototypes.spawner import spawn
|
||||||
|
from evennia.utils.utils import int2str
|
||||||
|
|
||||||
from .command import Command
|
from .command import Command
|
||||||
from typeclasses.scripts import DonkeyHeadSpell
|
from typeclasses.scripts import DonkeyHeadSpell
|
||||||
from typeclasses.drinkables import Cocktail
|
from typeclasses.drinkables import Cocktail
|
||||||
from utils.word_list import routput
|
from utils.word_list import routput, pluralize
|
||||||
|
|
||||||
|
|
||||||
class CmdFly(Command):
|
class CmdFly(Command):
|
||||||
|
|
@ -88,6 +90,98 @@ class CmdMagic(Command):
|
||||||
wizard.spell_sequence(None, msgs, wizard.db.magic_delay or 3)
|
wizard.spell_sequence(None, msgs, wizard.db.magic_delay or 3)
|
||||||
|
|
||||||
|
|
||||||
|
class CmdMakeItem(Command):
|
||||||
|
"""
|
||||||
|
Create one or more items from thin air.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
make [ number ] name [ description ]
|
||||||
|
|
||||||
|
Where 'effects' is a statement of what happens after the magic erupts.
|
||||||
|
Note that you can have multiple effects separated by two semicolons.
|
||||||
|
|
||||||
|
You can tailor the effects to your character with the following:
|
||||||
|
|
||||||
|
- |y$you()|n: Replaced by "you" and your name for others, like "old gnome"
|
||||||
|
- |y$your()|n: Replaced by "your" and your possessive name for others, like "old gnome's"
|
||||||
|
- |y$conj(verb)|n: Replaced by "verb" for you, and "verbs" for others.
|
||||||
|
- |y$pron(you)|n: Replaced by "you" for you, but "he" or "she" for others.
|
||||||
|
- |y$pron(your)|n: Replaced by "your" for you, but "his" or "her" for others.
|
||||||
|
|
||||||
|
While flexible, you would use this complex replacement in either
|
||||||
|
|gnicks|n or as part of a standard magical prefix, by setting the
|
||||||
|
property:
|
||||||
|
|
||||||
|
@set self/magic_msg = "$You() $conj(shake) $pron(your) necklace of bones!"
|
||||||
|
"""
|
||||||
|
key = "make"
|
||||||
|
locks = "cmd:holds()"
|
||||||
|
|
||||||
|
def parse(self):
|
||||||
|
"""
|
||||||
|
Allows the following phrases:
|
||||||
|
|
||||||
|
candy
|
||||||
|
2 candy Piece of Turkish Delight.
|
||||||
|
candy Piece of Turkish Delight.
|
||||||
|
"turkish delight" Piece of Turkish Delight.
|
||||||
|
4 "turkish delight" Piece of Turkish Delight.
|
||||||
|
"""
|
||||||
|
pattern = r'"(.*?)"|(\S+)'
|
||||||
|
matches = findall(pattern, self.args.strip())
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.item_number = int(matches[0][0] or matches[0][1])
|
||||||
|
self.item_name = matches[1][0] or matches[1][1]
|
||||||
|
start = 2
|
||||||
|
except ValueError:
|
||||||
|
self.item_number = 1
|
||||||
|
self.item_name = matches[0][0] or matches[0][1]
|
||||||
|
start = 1
|
||||||
|
|
||||||
|
words = [m[0] or m[1] for m in matches[start:]]
|
||||||
|
self.item_desc = " ".join(words)
|
||||||
|
|
||||||
|
def func(self):
|
||||||
|
"""
|
||||||
|
Call the 'do_magic' method on the caller.
|
||||||
|
"""
|
||||||
|
wizard = self.caller
|
||||||
|
msgs = wizard.db.make_msg or wizard.db.magic_msg
|
||||||
|
if msgs:
|
||||||
|
msgs = msgs.split(';;')
|
||||||
|
else:
|
||||||
|
msgs = [
|
||||||
|
"$You() $conj(snap) $pron(your) fingers.",
|
||||||
|
]
|
||||||
|
|
||||||
|
if self.item_number == 1:
|
||||||
|
if match(r"^[aeiou]"):
|
||||||
|
name = f"an {self.item_name}"
|
||||||
|
else:
|
||||||
|
name = f"a {self.item_name}"
|
||||||
|
else:
|
||||||
|
name = int2str(self.item_number) + " " + \
|
||||||
|
pluralize(self.item_name)
|
||||||
|
|
||||||
|
# from evennia.utils.utils import int2str ; print(int2str("2"))
|
||||||
|
msgs = msgs + [ name + " appear in $pron(your) hand." ]
|
||||||
|
|
||||||
|
wizard.spell_sequence(None, msgs, wizard.db.magic_delay or 3)
|
||||||
|
|
||||||
|
# FIXME So that the cat will eat it and it can't be littered:
|
||||||
|
if not self.item_desc or self.item_desc == "":
|
||||||
|
self.item_desc = f"Conjured by {wizard.get_name()}."
|
||||||
|
|
||||||
|
for _ in range(self.item_number):
|
||||||
|
item = spawn({
|
||||||
|
"typeclass": "typeclasses.consumables.Litterable",
|
||||||
|
"key": self.item_name,
|
||||||
|
"desc": self.item_desc
|
||||||
|
})[0]
|
||||||
|
item.location = wizard
|
||||||
|
|
||||||
class CmdSetWand(CmdSet):
|
class CmdSetWand(CmdSet):
|
||||||
"""
|
"""
|
||||||
All wizard spells are tied to a 'wand' that might be flavored.
|
All wizard spells are tied to a 'wand' that might be flavored.
|
||||||
|
|
@ -96,6 +190,7 @@ class CmdSetWand(CmdSet):
|
||||||
super().at_cmdset_creation()
|
super().at_cmdset_creation()
|
||||||
self.add(CmdFly)
|
self.add(CmdFly)
|
||||||
self.add(CmdMagic)
|
self.add(CmdMagic)
|
||||||
|
self.add(CmdMakeItem)
|
||||||
|
|
||||||
|
|
||||||
class CmdMakeCocktail(MuxCommand):
|
class CmdMakeCocktail(MuxCommand):
|
||||||
|
|
|
||||||
48
transcripts/cozy-header-template.html
Normal file
48
transcripts/cozy-header-template.html
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Wyldwood Bar Transcript</title>
|
||||||
|
<style type="text/css" media="screen">
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&family=Goudy+Bookletter+1911&family=Merienda:wght@300..900&display=swap');
|
||||||
|
|
||||||
|
html {
|
||||||
|
background-color: #FCF5E5;
|
||||||
|
color: #26201a;
|
||||||
|
|
||||||
|
font-family: "Goudy Bookletter 1911", serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 21px;
|
||||||
|
line-height: 1.4em;
|
||||||
|
|
||||||
|
padding: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: auto;
|
||||||
|
margin-bottom: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
blockquote i {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.special {
|
||||||
|
margin: 20px;
|
||||||
|
padding: 2px 20px;
|
||||||
|
border: 2px solid brown;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
.special .title {
|
||||||
|
font-variant-caps: small-caps;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heavy {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Another Cozy Day</h1>
|
||||||
|
|
@ -493,9 +493,15 @@ class Character(Object, GenderCharacter, ContribRPCharacter):
|
||||||
"""
|
"""
|
||||||
# All this does is add the character's gender to the message.
|
# All this does is add the character's gender to the message.
|
||||||
# So $pron(you,op) will render "you" or "him"
|
# So $pron(you,op) will render "you" or "him"
|
||||||
newmsg = sub(r"\$pron\((.*?),(.*?)\)", f"$pron(\\1,\\2 {self.db.gender})", message)
|
newmsg = sub(r"\$pron\(([^\)]*?),([^\)]*?)\)",
|
||||||
|
f"$pron(\\1,\\2 {self.db.gender})",
|
||||||
|
message)
|
||||||
|
|
||||||
# While $pron(you) will render "you" and "he":
|
# While $pron(you) will render "you" and "he":
|
||||||
newmsg = sub(r"\$pron\(([^,]*?)\)", f"$pron(\\1, {self.db.gender})", newmsg)
|
newmsg = sub(r"\$pron\(([^,\)]*?)\)",
|
||||||
|
f"$pron(\\1, {self.db.gender})",
|
||||||
|
newmsg)
|
||||||
|
|
||||||
choose = choices(newmsg)
|
choose = choices(newmsg)
|
||||||
logger.info(choose)
|
logger.info(choose)
|
||||||
self.location.msg_contents(f"{choose}", from_obj=self, exclude=exclude)
|
self.location.msg_contents(f"{choose}", from_obj=self, exclude=exclude)
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,12 @@ with a location in the game world (like Characters, Rooms, Exits).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from os import makedirs
|
||||||
|
from os.path import dirname, exists
|
||||||
from re import split, match, sub, IGNORECASE
|
from re import split, match, sub, IGNORECASE
|
||||||
from random import randint
|
from shutil import copyfile
|
||||||
|
from random import randint, choice
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
@ -719,3 +723,116 @@ class Listener:
|
||||||
|
|
||||||
self.msg(f"You can't give '{gift}' to {receiver.key}... {gifts.keys()}")
|
self.msg(f"You can't give '{gift}' to {receiver.key}... {gifts.keys()}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
class Recorder(Object):
|
||||||
|
"""Mixin for recording the events in the current room.
|
||||||
|
|
||||||
|
Classes that include this mixin, need, in their msg()
|
||||||
|
should call 'record_msg', for instance:
|
||||||
|
|
||||||
|
def msg(self, text=None, from_obj=None, session=None, **kwargs):
|
||||||
|
self.record_msg(text, from_obj)
|
||||||
|
|
||||||
|
Objects should set the following properties:
|
||||||
|
|
||||||
|
@set scribe/transcripts_path = "transcripts/%Y-%m-%d.html"
|
||||||
|
@set scribe/header_file = "transcripts/header-template.html"
|
||||||
|
|
||||||
|
The 'directory' is really just a prefix to a file that includes
|
||||||
|
today's date.
|
||||||
|
"""
|
||||||
|
def capitalize_msg(self, text, msg_type=None):
|
||||||
|
"""
|
||||||
|
If text is lowercase, capitalize it.
|
||||||
|
|
||||||
|
Maybe prepend a 'The' to the front.
|
||||||
|
"""
|
||||||
|
logger.info(f"capitalize: {text} / {msg_type}")
|
||||||
|
if msg_type and msg_type in ('traverse', 'teleport'):
|
||||||
|
if match(r"^(\|[A-z])?[aeiou]", text):
|
||||||
|
return "An " + text
|
||||||
|
else:
|
||||||
|
return "A " + text
|
||||||
|
|
||||||
|
# If the line is colored and lowercase, it probably assumes a
|
||||||
|
# character's sdesc, so let's add a 'the' to the front:
|
||||||
|
# Do we need to only do this on 'say'?
|
||||||
|
elif match(r"^\|[mb][a-z]", text):
|
||||||
|
return "The " + text
|
||||||
|
elif match(r"^(\|[A-z])[a-z]", text) and len(text) > 2:
|
||||||
|
return text[0:2] + text[2].upper() + text[3:]
|
||||||
|
elif match(r"^[a-z]", text) and len(text) > 2:
|
||||||
|
return text[0].upper() + text[1:]
|
||||||
|
return text
|
||||||
|
|
||||||
|
def to_html(self, text, msg_type=None):
|
||||||
|
"""
|
||||||
|
Convert the 'text' to an HTML formatted string.
|
||||||
|
"""
|
||||||
|
# Yellow text should be italics:
|
||||||
|
text = sub(r"\|y(.*?)\|n", '<i>\\1</i>', text)
|
||||||
|
# Bold and tag the titles:
|
||||||
|
text = sub(r"\|[cb](.*?)\|n", '<b class="title">\\1</b>', text)
|
||||||
|
# Bold anything white:
|
||||||
|
text = sub(r"\|w(.*?)\|n", '<b class="heavy">\\1</b>', text)
|
||||||
|
# Remove the rest:
|
||||||
|
text = sub(r"\|[A-z]", '', text)
|
||||||
|
# Remove initial or final carriage returns:
|
||||||
|
text = sub(r"^\n", '', text)
|
||||||
|
text = sub(r"\n$", '', text)
|
||||||
|
# Convert the carriage returns:
|
||||||
|
text = sub(r"\n", ' <br/>\n', text)
|
||||||
|
|
||||||
|
if msg_type and msg_type == 'look':
|
||||||
|
text = text[0].upper() + text[1:]
|
||||||
|
return f"<div class=\"special\"><p>{text}</p></div>\n"
|
||||||
|
if msg_type and msg_type == 'note':
|
||||||
|
return f"<blockquote>{text}</blockquote>"
|
||||||
|
return f"<p>{text}</p>\n"
|
||||||
|
|
||||||
|
|
||||||
|
def find_actor(self, text, default=None):
|
||||||
|
"""
|
||||||
|
Extract the character performing the action from the text.
|
||||||
|
"""
|
||||||
|
if default:
|
||||||
|
return default
|
||||||
|
|
||||||
|
m = match(r".*\|b(.*?)\|n.*", text)
|
||||||
|
if m:
|
||||||
|
return m.group(1)
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
|
# Make things a little more interesting by substituting the
|
||||||
|
# name...
|
||||||
|
names = actor and actor.db.alt_names
|
||||||
|
if names:
|
||||||
|
logger.info("lets change this")
|
||||||
|
msg = sub(f"^{actor.sdesc.get()}", choice(names),
|
||||||
|
msg, flags=IGNORECASE)
|
||||||
|
|
||||||
|
actor = self.find_actor(msg, actor)
|
||||||
|
|
||||||
|
msg = self.to_html(msg, msg_type)
|
||||||
|
filename = datetime.today().strftime(self.db.transcripts_path)
|
||||||
|
|
||||||
|
if not exists(filename):
|
||||||
|
makedirs(dirname(filename), exist_ok=True)
|
||||||
|
copyfile(self.db.header_file, 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}")
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# Emacs environement
|
|
||||||
# (setq python-shell-interpreter "/Users/howard/src/moss-n-puddles/.venv/bin/ipython")
|
|
||||||
"""
|
"""
|
||||||
Pets
|
Pets
|
||||||
|
|
||||||
|
|
@ -16,7 +14,7 @@ from time import time
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from evennia import TICKER_HANDLER
|
from evennia import TICKER_HANDLER
|
||||||
from evennia.utils import logger
|
from evennia.utils import logger, delay
|
||||||
from evennia.utils.gametime import schedule
|
from evennia.utils.gametime import schedule
|
||||||
from evennia.utils.search import search_object
|
from evennia.utils.search import search_object
|
||||||
|
|
||||||
|
|
@ -482,9 +480,10 @@ class WeeBeastie(Friendly, Familiar, Listener):
|
||||||
"Override to return a string in response to message."
|
"Override to return a string in response to message."
|
||||||
owner = self.search("Dabbler")
|
owner = self.search("Dabbler")
|
||||||
if owner:
|
if owner:
|
||||||
owner.announce_action(f"$Your() {name} purrs.")
|
delay(3, owner.announce_action,
|
||||||
|
f"$Your() {self.get_name()} purrs.")
|
||||||
else:
|
else:
|
||||||
self.execute_cmd(f"emote /me purrs.")
|
delay(3, self.execute_cmd, f"emote /me purrs.")
|
||||||
|
|
||||||
def feed(self, feeder, item=None):
|
def feed(self, feeder, item=None):
|
||||||
"""
|
"""
|
||||||
|
|
@ -492,9 +491,14 @@ class WeeBeastie(Friendly, Familiar, Listener):
|
||||||
the character has, and go with that...
|
the character has, and go with that...
|
||||||
"""
|
"""
|
||||||
# Categorize items that can be used to feed the beast:
|
# Categorize items that can be used to feed the beast:
|
||||||
def is_flower(item):
|
def is_some(item, name):
|
||||||
return (not item and feeder.has("yellow flower")) or \
|
return (not item and feeder.has(name)) or \
|
||||||
(item and item.key == 'yellow flower')
|
(item and item.key == name)
|
||||||
|
|
||||||
|
def is_edible(item):
|
||||||
|
return is_some(item, "yellow flower") or \
|
||||||
|
is_some(item, "candy") or \
|
||||||
|
is_some(item, "turkish delight")
|
||||||
|
|
||||||
# Based on the reaction to the feeder, the adjectives may alter:
|
# Based on the reaction to the feeder, the adjectives may alter:
|
||||||
noun = "The " + random.choice(["wee", "furry", "white", "adorable"]) + " beastie"
|
noun = "The " + random.choice(["wee", "furry", "white", "adorable"]) + " beastie"
|
||||||
|
|
@ -521,10 +525,11 @@ class WeeBeastie(Friendly, Familiar, Listener):
|
||||||
"rubs its wee widdle head under $pron(you,op) chin in gratitude",
|
"rubs its wee widdle head under $pron(you,op) chin in gratitude",
|
||||||
])
|
])
|
||||||
|
|
||||||
if is_flower(item):
|
edible = is_edible(item)
|
||||||
msg = f"{noun} {how_sniff} sniffs $your() << hand holding a ^>> flower. It {how_eat} eats it, and {and_then}."
|
if edible:
|
||||||
|
msg = f"{noun} {how_sniff} sniffs $your() << hand holding a ^>> {edible}. It {how_eat} eats it, and {and_then}."
|
||||||
self.adjust_character(feeder, 100)
|
self.adjust_character(feeder, 100)
|
||||||
feeder.has('yellow flower').delete()
|
edible.delete()
|
||||||
else:
|
else:
|
||||||
msg = f"{noun} doesn't appear interested in anything you have."
|
msg = f"{noun} doesn't appear interested in anything you have."
|
||||||
|
|
||||||
|
|
@ -654,7 +659,6 @@ class BHB(Friendly):
|
||||||
|
|
||||||
if msg:
|
if msg:
|
||||||
feeder.announce_action(msg)
|
feeder.announce_action(msg)
|
||||||
# feeder.msg(msg)
|
|
||||||
|
|
||||||
def thrown_stick(self, thrower):
|
def thrown_stick(self, thrower):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,13 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
from os.path import exists
|
|
||||||
from datetime import datetime
|
|
||||||
from re import split, match, sub, IGNORECASE
|
from re import split, match, sub, IGNORECASE
|
||||||
from shutil import copyfile
|
|
||||||
|
|
||||||
from evennia import CmdSet
|
from evennia import CmdSet
|
||||||
from evennia.utils import logger
|
from evennia.utils import logger
|
||||||
|
|
||||||
from commands.command import Command
|
from commands.command import Command
|
||||||
from typeclasses.characters import Character
|
from typeclasses.characters import Character
|
||||||
from typeclasses.objects import Listener
|
from typeclasses.objects import Listener, Recorder
|
||||||
from utils.word_list import routput
|
from utils.word_list import routput
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -101,7 +98,7 @@ class CmdSetShrubSay(CmdSet):
|
||||||
self.add(CmdShrubSay)
|
self.add(CmdShrubSay)
|
||||||
|
|
||||||
|
|
||||||
class Shrub(Puppet):
|
class Shrub(Puppet, Recorder):
|
||||||
"""
|
"""
|
||||||
The 'Shrub' has its own way of communicating.
|
The 'Shrub' has its own way of communicating.
|
||||||
"""
|
"""
|
||||||
|
|
@ -145,89 +142,6 @@ class Shrub(Puppet):
|
||||||
self.db.inside = f"The chalkboard reads: |w{new_text}|n"
|
self.db.inside = f"The chalkboard reads: |w{new_text}|n"
|
||||||
self.record_msg(self.db.inside)
|
self.record_msg(self.db.inside)
|
||||||
|
|
||||||
def find_actor(self, text, default=None):
|
|
||||||
"""
|
|
||||||
Extract the character performing the action from the text.
|
|
||||||
"""
|
|
||||||
if default:
|
|
||||||
return default
|
|
||||||
|
|
||||||
m = match(r".*\|b(.*?)\|n.*", text)
|
|
||||||
if m:
|
|
||||||
return m.group(1)
|
|
||||||
|
|
||||||
def capitalize_msg(self, text, msg_type=None):
|
|
||||||
"""
|
|
||||||
If text is lowercase, capitalize it.
|
|
||||||
|
|
||||||
Maybe prepend a 'The' to the front.
|
|
||||||
"""
|
|
||||||
logger.info(f"capitalize: {text} / {msg_type}")
|
|
||||||
if msg_type and msg_type in ('traverse', 'teleport'):
|
|
||||||
if match(r"^(\|[A-z])?[aeiou]", text):
|
|
||||||
return "An " + text
|
|
||||||
else:
|
|
||||||
return "A " + text
|
|
||||||
# If the line is colored and lowercase, it probably assumes a
|
|
||||||
# character's sdesc, so let's add a 'the' to the front:
|
|
||||||
# Do we need to only do this on 'say'?
|
|
||||||
elif match(r"^\|[mb][a-z]", text):
|
|
||||||
return "The " + text
|
|
||||||
elif match(r"^(\|[A-z])[a-z]", text) and len(text) > 2:
|
|
||||||
return text[0:2] + text[2].upper() + text[3:]
|
|
||||||
elif match(r"^[a-z]", text) and len(text) > 2:
|
|
||||||
return text[0].upper() + text[1:]
|
|
||||||
return text
|
|
||||||
|
|
||||||
def to_html(self, text, msg_type=None):
|
|
||||||
"""
|
|
||||||
Convert the 'text' to an HTML formatted string.
|
|
||||||
"""
|
|
||||||
# Yellow text should be italics:
|
|
||||||
text = sub(r"\|y(.*?)\|n", '<i>\\1</i>', text)
|
|
||||||
# Bold and tag the titles:
|
|
||||||
text = sub(r"\|[cb](.*?)\|n", '<b class="title">\\1</b>', text)
|
|
||||||
# Bold anything white:
|
|
||||||
text = sub(r"\|w(.*?)\|n", '<b class="heavy">\\1</b>', text)
|
|
||||||
# Remove the rest:
|
|
||||||
text = sub(r"\|[A-z]", '', text)
|
|
||||||
# Remove initial or final carriage returns:
|
|
||||||
text = sub(r"^\n", '', text)
|
|
||||||
text = sub(r"\n$", '', text)
|
|
||||||
# Convert the carriage returns:
|
|
||||||
text = sub(r"\n", ' <br/>\n', text)
|
|
||||||
|
|
||||||
if msg_type and msg_type == 'look':
|
|
||||||
text = text[0].upper() + text[1:]
|
|
||||||
return f"<div class=\"special\"><p>{text}</p></div>\n"
|
|
||||||
if msg_type and msg_type == 'note':
|
|
||||||
return f"<blockquote>{text}</blockquote>"
|
|
||||||
return f"<p>{text}</p>\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):
|
def msg(self, text=None, from_obj=None, session=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Record everything that happens in the room for a transcript.
|
Record everything that happens in the room for a transcript.
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ from commands.wizards import CmdSetWand
|
||||||
from utils.word_list import routput, choices, paragraph
|
from utils.word_list import routput, choices, paragraph
|
||||||
from utils.scoring import Scores
|
from utils.scoring import Scores
|
||||||
from typeclasses.consumables import Litterable
|
from typeclasses.consumables import Litterable
|
||||||
from typeclasses.objects import Object
|
from typeclasses.objects import Object, Recorder
|
||||||
from typeclasses.scripts import KnockScript
|
from typeclasses.scripts import KnockScript
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -936,7 +936,7 @@ class BagofJunk(Object):
|
||||||
self.db.latest_item = item
|
self.db.latest_item = item
|
||||||
where = self.db._sdesc or self.name
|
where = self.db._sdesc or self.name
|
||||||
|
|
||||||
msg = routput(f"$You() << $conj(scrounge) ^ $conj(rummage) ^ $conj(fish) ^ $conj(rifle) ^ $conj(put) $pron(your) hand >> << around ^ >> in $pron(your) {where}, and << $conj(pull) out ^ $conj(find) ^ $conj(stare) at >> |w{item}|n.")
|
msg = routput(f"$You() << $conj(scrounge) ^ $conj(rummage) ^ $conj(fish) ^ $conj(rifle) ^ $conj(put) $pron(your) hand >> << around ^ >> in $pron(your) {where}, and << $conj(pull) out ^ $conj(find) ^ $conj(stare) at >>|w{item}|n.")
|
||||||
owner.announce_action(msg)
|
owner.announce_action(msg)
|
||||||
|
|
||||||
def do_keep(self, keeper, description=None):
|
def do_keep(self, keeper, description=None):
|
||||||
|
|
@ -1142,3 +1142,16 @@ class GlobalAlarmClock(Object):
|
||||||
if start:
|
if start:
|
||||||
chan.msg(start)
|
chan.msg(start)
|
||||||
delay(seconds, chan.msg, finish)
|
delay(seconds, chan.msg, finish)
|
||||||
|
|
||||||
|
|
||||||
|
class Scribe(Recorder):
|
||||||
|
"""
|
||||||
|
Probably invisible script that records room events.
|
||||||
|
|
||||||
|
Set the following properties:
|
||||||
|
|
||||||
|
@set scribe/directory = "transcripts"
|
||||||
|
@set scribe/header = "transcripts/header-template.html"
|
||||||
|
"""
|
||||||
|
def msg(self, text=None, from_obj=None, session=None, **kwargs):
|
||||||
|
self.record_msg(text, from_obj)
|
||||||
|
|
|
||||||
|
|
@ -147,3 +147,28 @@ def choices(text, *substitutions):
|
||||||
|
|
||||||
if text:
|
if text:
|
||||||
return routput(choice(text), *substitutions)
|
return routput(choice(text), *substitutions)
|
||||||
|
|
||||||
|
|
||||||
|
def pluralize(noun):
|
||||||
|
"""Convert a singular noun to its plural form."""
|
||||||
|
# Basic pluralization rules
|
||||||
|
|
||||||
|
# Change 'y' to 'ies' if preceded by a consonant
|
||||||
|
if noun.endswith('y') and noun[-2] not in 'aeiou':
|
||||||
|
return noun[:-1] + 'ies'
|
||||||
|
|
||||||
|
# Add 'es' for words ending in 's', 'x', 'z', 'ch', or 'sh'
|
||||||
|
elif noun.endswith('s') or noun.endswith('x') or \
|
||||||
|
noun.endswith('z') or noun.endswith('ch') or \
|
||||||
|
noun.endswith('sh'):
|
||||||
|
return noun + 'es'
|
||||||
|
|
||||||
|
# Default case for regular nouns
|
||||||
|
else:
|
||||||
|
return noun + 's'
|
||||||
|
|
||||||
|
# print(pluralize("dog")) # Outputs: dogs
|
||||||
|
# print(pluralize("candy")) # Outputs: candies
|
||||||
|
# print(pluralize("box")) # Outputs: boxes
|
||||||
|
# print(pluralize("bush")) # Outputs: bushes
|
||||||
|
# print(pluralize("city")) # Outputs: cities
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue