Attempt to update feed commands

This commit is contained in:
Howard Abrams 2025-04-07 22:44:56 -07:00
parent 542e140013
commit c731383915
15 changed files with 488 additions and 304 deletions

View file

@ -5,11 +5,54 @@ Commands describe the input the account can do to the game.
"""
import re
from evennia.commands.command import Command as BaseCommand
# from evennia import default_cmds
class TokenWords():
"""
Replacement for 'args' that includes a list without filler words and punctuation.
"""
def __init__(self, s):
# cleaned_words = re.sub(r"%s" % string.punctuation, "", s)
cleaned_words = re.sub(r"[\.,\?!'\":;^`\|%#\&\*<=>\(\)\[\]\{\}\+\/_-]*", "", s)
ignored_words = ["the", "a", "an", "of", "on", "that", "this"]
self.words = [word for word in cleaned_words.split() if word not in ignored_words]
def empty(self):
"""
Return true if args didn't have viable words.
"""
if len(self.words) == 0:
return True
return False
def lhs(self):
"""
Return first word after command, or None if no words available.
"""
return None if self.empty() else self.words[0]
def rhs(self):
"""
Return second word after command, or None if word wasn't given.
"""
return None if len(self.words) < 2 else self.words[1]
def contains(self, word):
"""
Return true if 'word' was contains in parsed arguments.
"""
if len(self.words) > 0:
result = [True for w in self.words if w.lower() == word.lower()]
if len(result) > 0:
return result[0]
return False
class Command(BaseCommand):
"""
Base command (you may see this if a child command had no help text defined)
@ -17,9 +60,7 @@ class Command(BaseCommand):
Note that the class's `__doc__` string is used by Evennia to create the
automatic help entry for the command, so make sure to document consistently
here. Without setting one, the parent's docstring will show (like now).
"""
# Each Command class implements the following methods, called in this order
# (only func() is actually required):
#
@ -30,7 +71,13 @@ class Command(BaseCommand):
# - at_post_cmd(): Extra actions, often things done after
# every command, like prompts.
#
pass
def words(self):
"""
Return an object of args that allows queries.
"""
if not hasattr(self, 'tokenized_words'):
self.tokenized_words = TokenWords(self.args.strip())
return self.tokenized_words
# -------------------------------------------------------------

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python
from evennia import Command, CmdSet
from re import match
from evennia import CmdSet
from commands.command import Command
class CmdMakeConsumable(Command):
@ -12,7 +12,7 @@ class CmdMakeConsumable(Command):
# aliases = "pick"
def func(self):
self.obj.do_make(self.caller, self.args)
self.obj.do_make(self.caller, words=self.words())
class CmdSetMakeConsumable(CmdSet):
@ -66,7 +66,7 @@ class CmdMakeTea(Command):
"""
Make a pot of tea. Optional accept the type of tea.
"""
self.obj.do_make_tea(self.caller, self.args.strip())
self.obj.do_make_tea(self.caller, self.words())
class CmdFillTeacup(Command):
@ -122,10 +122,27 @@ class CmdMakeScone(Command):
aliases = "grab scone"
def func(self):
self.obj.do_make(self.caller, self.args)
self.obj.do_make(self.caller, words=self.words())
class CmdGetWood(Command):
"""
Get wood from the woodbox on the hearth.
"""
auto_help = False
key = "get wood"
aliases = ["get log", "get logs", "get kindling"]
locks = "cmd:all()"
def func(self):
"""
Get wood from the hearth box.
"""
self.obj.produce_wood(self.caller)
class CmdSetTrolley(CmdSet):
def at_cmdset_creation(self):
self.add(CmdGetTeacup)
self.add(CmdMakeScone)
self.add(CmdGetWood)

View file

@ -1,25 +1,49 @@
#!/usr/bin/env python
from commands.command import Command
from evennia import CmdSet
from evennia.commands.default.general import CmdGive, NumberedTargetCommand
class CmdFeed(Command):
from commands.command import Command
# from typeclasses.pets import Pet
class CmdFeed(Command, NumberedTargetCommand):
"""
Feed or give something to an object that can eat.
Typically this is used to feed wood to a fire, or
berries to a beast.
"""
key = "feed"
rhs_split = ("=", " to ")
def func(self):
args = self.args.strip()
if not args:
"""
Implements the feed (or give) command.
"""
if not self.args:
self.caller.msg("Feed what?")
return
elif not self.rhs:
target = self.caller.search(self.lhs)
self.caller.msg(f"Feeding {target}")
if target:
target.feed(self.caller)
else:
self.caller.msg(f"Don't see a '{self.lhs}' to feed.")
else:
self.caller.msg(f"give {self.args}")
self.caller.execute_cmd("give " + self.args)
# # Pass this off to CmdGive?
# supercmd = CmdGive()
# supercmd.caller = self.caller
# supercmd.args = self.args
# supercmd.lhs = self.lhs
# supercmd.rhs = self.rhs
# supercmd.func()
print(self.obj.key)
self.obj.feed(self.caller, self.args)
class CmdFeedSet(CmdSet):
"""
Things that can be fed. Like a pet.
"""
def at_cmdset_creation(self):
self.add(CmdFeed)

View file

@ -3,6 +3,7 @@
from commands.command import Command
from evennia.commands.default.general import NumberedTargetCommand
class CmdTake(Command, NumberedTargetCommand):
"""
Steals an object from another.
@ -18,8 +19,8 @@ class CmdTake(Command, NumberedTargetCommand):
to work on the object, we operate only on self.obj.
"""
if not self.args:
self.msg("What do you want to take?")
self.caller.msg("What do you want to take?")
elif not self.rhs:
self.msg(f"You want to take {self.lhs}, but from whom?")
self.caller.msg(f"You want to take {self.lhs}, but from whom?")
else:
self.caller.do_take(self.lhs, self.rhs)

View file

@ -8,14 +8,15 @@ creation commands.
"""
from re import match
from evennia.objects.objects import DefaultCharacter
from evennia.prototypes.spawner import spawn
from utils.word_list import Token, routput
# from utils.word_list import routput
from .objects import Object
from .tutorial import TutorBird, TutorialState
from re import match
INTRO = """
As the surrounding mists dissipate, you find yourself in an ancient, halcyon forest dripping with moss. You see an envelope of parchment wedged under a scaly protrusion of bark...inside, a letter in familiar penmanship, personally addressed to you, which you pick up.
@ -35,6 +36,7 @@ READ_LETTER = """You read a letter with an oddly familiar penmanship:
(Type 'help start' for details on playing this game)"""
class Character(Object, DefaultCharacter):
"""
The Character just re-implements some of the Object's methods and hooks
@ -52,7 +54,7 @@ class Character(Object, DefaultCharacter):
self.create_letter()
TutorBird.do_start_tutorial(self)
def at_post_puppet(self):
def at_post_puppet(self, **kwargs):
if self.db.visited:
self.msg(f"""\n“Welcome back, {self.key.capitalize()}.”\n""")
self.execute_cmd("look")
@ -100,7 +102,7 @@ class Character(Object, DefaultCharacter):
if self.db.is_sitting:
self.msg("You stand up first...")
self.db.is_sitting = False;
self.db.is_sitting = False
# @lock thing = tethered:id(#19)
# @set thing/tethered_msg = "Let's put that back"

View file

@ -1,17 +1,16 @@
#!/usr/bin/env python
from evennia import Command, CmdSet, TICKER_HANDLER
from datetime import date
import random
from evennia import TICKER_HANDLER
from evennia.prototypes.spawner import spawn
from commands.consumables import (
CmdSetEat, CmdSetTrolley, CmdSetMakeConsumable
)
from typeclasses.objects import Object
from utils.word_list import routput
from datetime import date
from re import match
import random
from utils.word_list import routput # , choices
class Consumable(Object):
@ -36,9 +35,10 @@ class Consumable(Object):
self.db.eat_amount = self.eat_amount
def do_eat(self, eater):
tea_type = self.db.tea_type or "tea"
tea_details = self.db.tea_details
"""
Consume (eat) or destroy this object.
Indicate this with messages to eater.
"""
self.db.amount = self.db.amount - self.db.eat_amount
if self.db.eat_msgs:
msg = random.choice(self.db.eat_msgs)
@ -83,15 +83,21 @@ class Producer(Object):
# Either a single msg:
@set bush/make_eat_msg = "Sweet and slightly tart. [Delicious|Tangy|Mmmm]."
# Or many:
@set bush/make_eat_msgs = [ "Sweet and slightly tart.", "[Delicious|Tangy|Mmmm].", "Ooo...that one was sour.", "That was a bit ripe, but still good.", "So sweet with a hint of [orange|maple]." ]
@set bush/make_eat_msgs = [ "Sweet and slightly tart.", "[Delicious|Tangy|Mmmm].",
"Ooo...that one was sour.", "That was a bit ripe, but still good.",
"So sweet with a hint of [orange|maple]." ]
@set bush/make_finish_msg = "Those were [delicious|great]."
"""
def at_object_creation(self):
self.cmdset.add_default(CmdSetMakeConsumable)
def do_make(self, picker, args):
def do_make(self, picker, **kwargs):
"""
Create a configurable consumable, given to picker.
"""
consumable = spawn({
"typeclass": self.db.make_class or "typeclasses.consumables.Consumable",
"typeclass": self.db.make_class or kwargs['type']
if 'type' in kwargs else "typeclasses.consumables.Consumable",
"key": self.db.make_name,
"aliases": self.db.make_aliases,
"desc": routput(self.db.make_desc),
@ -122,6 +128,7 @@ TEACUP_DESCS = [
"A light jade green handle-less teacup. A good choice for a green tea.",
]
class Trolley(Producer):
"""
A special producer that has a plate of specific dishes for
@ -180,6 +187,9 @@ class Trolley(Producer):
callback=self.do_bake)
def produce_teacup(self, caller):
"""
Create and associate with 'caller', a newly created teacup.
"""
if caller.has("teacup"):
caller.msg("You already have a teacup.")
else:
@ -190,6 +200,21 @@ class Trolley(Producer):
cup.location = caller
caller.msg("You pick up a teacup.")
def produce_wood(self, caller):
"""
Create and give to 'caller' a log of wood.
"""
wood = spawn({
"creator_id": 1,
"typeclass": "typeclasses.things.Wood",
"key": "log",
"aliases": ["wood", "kindling", "logs"],
})[0]
wood.location = caller
caller.msg(routput("You [get|pick up|grab] "
"[some wood|couple logs|a log|a few pieces of wood] "
"[from the woodbox|from the box on the hearth|for the fireplace|]."))
def do_bake(self):
"""
Set the scone that we bake today.
@ -197,7 +222,7 @@ class Trolley(Producer):
todays_scone = self.scones[date.today().day % len(self.scones)]
name = todays_scone.get("name")
self.db.make_name = name
self.db.make_name = name
self.db.make_aliases = ["scone", "scones"]
self.db.make_desc = routput(f"A [tasty|yummy|delicious]-looking {name}")
self.db.make_amount = 3

View file

@ -2,7 +2,7 @@
from typeclasses.objects import Object
from commands.consumables import CmdSetTeapot, CmdSetTeacup
from utils.word_list import routput, Token
from utils.word_list import routput
import random
@ -85,8 +85,13 @@ class Teapot(Object):
"""
self.cmdset.add_default(CmdSetTeapot, persistent=True)
def do_make_tea(self, drinker, args):
words = Token(args)
def do_make_tea(self, drinker, words):
"""
Make tea.
If 'args', try to make a particular type of tea, otherwise,
pick one at random.
"""
tea_choice = []
if not words.empty():
tea_choice = [tea for tea in TEA_TYPES.keys() if words.contains(tea)]

View file

@ -1,5 +1,7 @@
#!/usr/bin/env python
from evennia.utils import logger
"""
Object
@ -11,7 +13,7 @@ with a location in the game world (like Characters, Rooms, Exits).
"""
from evennia.objects.objects import DefaultObject
from evennia.utils import delay, logger, search
# from evennia.utils import delay, logger, search
class ObjectParent:
@ -179,8 +181,8 @@ class Object(ObjectParent, DefaultObject):
at_object_leave(obj, target_location) - called when an object leaves
this object in any fashion
at_pre_object_receive(obj, source_location)
at_object_receive(obj, source_location, move_type="move", **kwargs) - called when this object receives
another object
at_object_receive(obj, source_location, move_type="move", **kwargs) -
called when this object receives another object
at_post_move(source_location, move_type="move", **kwargs)
at_traverse(traversing_object, target_location, **kwargs) - (exit-objects only)
@ -217,6 +219,9 @@ class Object(ObjectParent, DefaultObject):
"""
def global_search(self, searchdata):
"""
Search for something globally.
"""
return super().search(searchdata, global_search=True)
def has(self, item):
@ -230,7 +235,19 @@ class Object(ObjectParent, DefaultObject):
for i in self.contents:
if isinstance(item, str) and (i.key == item or i.aliases.get(item)):
return i
elif type(i) == item:
if item is type(i):
return i
elif i == item:
if i == item:
return i
return None
def at_post_move(self, source_location, move_type="move", **kwargs):
"""
Delete ourselves if we are living in the fireplace.
"""
logger.log_info("Can we delete?")
if self.location.is_typeclass("typeclasses.pets.Pet"):
self.delete()

View file

@ -7,21 +7,23 @@ Each level of pet requires more aspects for interaction.
"""
from typeclasses.objects import Object
from typeclasses.characters import Character
# from typeclasses.lightables import LightSource
from commands.feedables import CmdFeedSet
from utils.word_list import squish
from evennia import TICKER_HANDLER
from enum import Enum
from re import sub
from time import time
import random
from evennia import TICKER_HANDLER
from typeclasses.objects import Object
from typeclasses.characters import Character
# from typeclasses.lightables import LightSource
from commands.feedables import CmdFeedSet
from utils.word_list import squish, choices
class Hunger(Enum):
"States of being for a Pet."
RAVENOUS = 100
HUNGRY = 300
FED = 850
@ -54,6 +56,23 @@ class Pet(Object):
]
}
def at_object_creation(self):
"""
Called when object is first created.
We set up a ticker to update hunger levels regularly.
"""
self.cmdset.add_default(CmdFeedSet)
self.db.hunger_level = Hunger.FULL.value
# subscribe ourselves to a ticker to repeatedly call the hook
# "update_weather" on this object. The interval is randomized
# so as to not have all weather rooms update at the same time.
self.db.interval = random.randint(70, 90)
TICKER_HANDLER.add(interval=self.db.interval, callback=self.update_state)
super().at_object_creation()
def hunger(self):
if self.db.hunger_level < Hunger.RAVENOUS.value:
return Hunger.RAVENOUS
@ -67,26 +86,9 @@ class Pet(Object):
msgs = self.hunger_states.get(self.hunger())
if isinstance(msgs, str):
return f"{self.fullname or self.pers_pronoun} {msgs}"
else:
return f"{self.fullname or self.pers_pronoun} {random.choice(msgs)}"
return f"{self.fullname or self.pers_pronoun} {random.choice(msgs)}"
def at_object_creation(self):
"""
Called when object is first created.
We set up a ticker to update hunger levels regularly.
"""
self.db.hunger_level = Hunger.FULL.value
self.cmdset.add_default(CmdFeedSet)
# subscribe ourselves to a ticker to repeatedly call the hook
# "update_weather" on this object. The interval is randomized
# so as to not have all weather rooms update at the same time.
self.db.interval = random.randint(70, 90)
TICKER_HANDLER.add(interval=self.db.interval, callback=self.update_hunger)
super().at_object_creation()
def update_hunger(self, *args, **kwargs):
def update_state(self, *args, **kwargs):
"""
Called by the tickerhandler at regular intervals. Even so, we
only update at a hungry_rate of the time, picking a random weather message
@ -97,24 +99,10 @@ class Pet(Object):
curr = self.hunger()
amount = kwargs.get('amount', self.hungry_rate)
# self.location.msg_contents(f"Feeding a pet: {amount}
# {isinstance(amount, str)}-> {self.db.hunger_level} /
# {self.hungry_rate}")
self.db.hunger_level = self.db.hunger_level + amount
if self.db.hunger_level < 0:
self.db.hunger_level = 0
# if self.db.hunger_level < 5:
# self.db.is_giving_light = False
# else:
# self.db.is_giving_light = True
self.db.hunger_level = max(self.db.hunger_level + amount, 0)
if self.hunger() != curr:
self.location.msg_contents("|w%s|n\n" % self.hunger_appearance())
try:
self.location.check_light_state()
except AttributeError:
pass
self.location.msg_contents(f"|w{self.hunger_appearance()}|n\n")
def return_appearance(self, looker, **kwargs):
"""
@ -128,6 +116,8 @@ class Pet(Object):
"""
return f"{self.db.desc} {self.hunger_appearance()}"
def feed(self, giver, obj=None):
pass
# ------------------------------------------------------------
# Fireplace, both a pet that is hungry and consumes food,
@ -159,32 +149,50 @@ class Fire(Pet):
]
}
def feed(self, feeder, args):
def feed_msg(self, giver):
now = time()
last_fed = feeder.ndb.fire_last_fed # could be None
last_fed = giver.ndb.fire_last_fed # could be None
if last_fed and (now - last_fed < 30):
adj = "some more"
else:
adj = "some"
feeder.ndb.fire_last_fed = now
giver.ndb.fire_last_fed = now
get_up = ""
gets_up = ""
if feeder.db.is_sitting:
if giver.db.is_sitting:
get_up = "get up and"
gets_up = "gets up and"
if self.db.hunger_level < 5:
feeder.msg(squish(f"You {get_up} put {adj} wood in the "
f"fireplace, and start a fire."))
self.location.msg_contents(squish(f"{feeder.name} {gets_up} starts a fire."),
exclude=feeder)
giver.msg(squish(f"You {get_up} put {adj} wood in the "
f"fireplace, and start a fire."))
self.location.msg_contents(squish(f"{giver.name} {gets_up} starts a fire."),
exclude=giver)
else:
feeder.msg(squish(f"You {get_up} put {adj} wood on the "
f"fire in the fireplace."))
self.location.msg_contents(squish(f"{feeder.name} {gets_up} puts {adj} wood on the fire."),
exclude=feeder)
giver.msg(squish(f"You {get_up} put {adj} wood on the "
f"fire in the fireplace."))
self.location.msg_contents(squish(f"{giver.name} {gets_up} puts {adj} wood on the fire."),
exclude=giver)
def feed(self, giver, obj=None):
"""
Feed the fire the default object, wood.
"""
self.feed_msg(giver)
self.update_state(giver=giver, amount=300)
def at_pre_object_receive(self, moved_obj, giver, move_type="move", **kwargs):
"Burn something in the fireplace."
if moved_obj.is_typeclass("typeclasses.things.Wood"):
self.feed_msg(giver)
self.update_state(giver=giver, amount=400)
else:
giver.msg(f"You throw {moved_obj.name} in the fireplace, destroying it.")
return True
# def at_post_move(self, source_location, move_type, **kwargs):
self.update_hunger(feeder=feeder, amount=300)

View file

@ -6,7 +6,7 @@ from evennia.prototypes.spawner import spawn
from typeclasses.objects import Object
from typeclasses.rooms import DabblersRoom
from utils.word_list import routput, Token
from utils.word_list import routput
import random

View file

@ -11,10 +11,10 @@ Rather, each script tends to inherit from the base Script class and
just overloads its hooks to have it perform its function.
"""
from evennia.scripts.scripts import DefaultScript
from evennia.utils import logger
from utils.support import god_msg
from typeclasses.characters import Character
class Script(DefaultScript):
@ -101,7 +101,6 @@ class Script(DefaultScript):
at_server_start()
"""
pass
class KnockScript(Script):
@ -114,7 +113,10 @@ class KnockScript(Script):
room = knocker.global_search(self.attributes.get("room"))
if room:
room.msg_contents("Someone is knocking on the door...")
god_msg(f"With your seer stone, you see the knocker is {knocker.key}.")
god = Character.objects.search_object("#1")[0]
if god:
god.msg(f"With your seer stone, you see the knocker is {knocker.key}.")
def at_repeat(self):
waker = self.attributes.get("waker")

View file

@ -1,29 +1,43 @@
#!/usr/bin/env python
from random import choice, random
from evennia import create_script
from evennia.utils import logger, delay
from .scripts import KnockScript
from .objects import Object
from commands.misc import CmdSetPuddle, CmdSetStick, CmdSetKnock
from utils.word_list import routput
from .scripts import KnockScript
from .objects import Object
from random import choice, random
import re
class Ring(Object):
def move_to(self, destination, *args, **kwargs):
def move_to(self, destination, **kwargs):
"""
The ring should only go to the door knocker...
"""
if destination.is_typeclass("typeclasses.things.Knocker") or \
destination.is_typeclass("typeclasses.characters.Character"):
super().move_to(destination)
return True
super().move_to(destination)
return True
return False
class Wood(Object):
"An object to burn."
def at_object_creation(self):
self.db.singular = "a log"
self.db.plural = "some logs"
self.db.desc = routput(choice([
"Its log, its log, it's big, it's heavy, it's wood.",
"Some [logs|wood] for the fireplace.",
]))
class Stick(Object):
"An object to throw."
def at_object_creation(self):
self.cmdset.add_default(CmdSetStick)
@ -319,12 +333,14 @@ class Knocker(Object):
# get any feedback from the server (not even the results of look)
super().msg(text=text, from_obj=from_obj, **kwargs)
def at_object_leave(self, obj, target_location, **kwargs):
def at_object_leave(self, moved_obj, target_location, move_type="move", **kwargs):
"""
If the ring is removed, we make a comment.
"""
if obj.key == "brass ring" and target_location != self:
if moved_obj.key == "brass ring" and target_location != self:
self.at_say("Oh my, that feels better.")
return True
return False
def at_pre_object_receive(self, arriving_object,
source_location, **kwargs):

View file

@ -1,35 +0,0 @@
#!/usr/bin/env python
from typeclasses.characters import Character
def character_has(holder, item):
"""
Return true if character, holder, has an item.
Where item is probably a string name to match an item's key.
It can also be a type, for instance:
character_has(character, typeclasses.drinkables.TeaCup)
"""
for i in holder.contents:
if isinstance(item, str) and i.key == item:
return i
elif i.key == item:
return i
elif type(i) == item:
return i
elif i == item:
return i
def god_msg(msg, sender=None, location=None):
"""
Bit of a debugging tool that allows messages to my character.
"""
dabbler = Character.objects.search_object("#1")[0]
send_msg = ('from ' + sender.name) if sender else ''
loc_msg = ('at ' + location.name) if location else ''
dabbler.msg(f"{msg} {send_msg} {loc_msg}")
def debug(msg, sender=None, location=None):
god_msg(f"DEBUG: {msg}", sender, location)
# py from typeclasses.characters import Character; god = Character.objects.search_object("#1")[0]

View file

@ -2,29 +2,14 @@
import random
import re
import string
class Token():
def __init__(self, s):
# cleaned_words = re.sub(r"%s" % string.punctuation, "", s)
cleaned_words = re.sub(r"[\.,\?!'\":;^`\|%#\&\*<=>\(\)\[\]\{\}\+\/_-]*", "", s)
self.words = [word for word in cleaned_words.split() if word not in ["the", "a", "an"]]
def empty(self):
if len(self.words) == 0:
return True
def contains(self, word):
if len(self.words) > 0:
result = [True for w in self.words if w.lower() == word.lower()]
if len(result) > 0:
return result[0]
def squish(text):
"Remove series of spaces from the text."
return re.sub('[ \n\t]+', ' ', text).strip()
def routput(text):
def routput(text, substitution=None):
"""
Return string with internal word choices replaced randomly.
For instance, the string:
@ -39,7 +24,23 @@ def routput(text):
"""
acc = []
for s in text.split("["):
choices, *rest = s.split("]")
choice = random.choice(choices.split('|'))
selections, *rest = s.split("]")
choice = random.choice(selections.split('|'))
acc = acc + [choice] + rest
return squish(''.join(acc))
return squish(''.join(acc).format(substitution or ""))
def choices(text, substitution=None):
"""
Returns a section of 'text' based on separating semicolons.
"""
selections = text.split(';;')
return routput(random.choice(selections), substitution)
def split_party(text, viewer):
return text.sub(r"<you>", viewer.name)
# def searsonal(text, **kwargs):
# season = kwargs['season'] or kwargs['location'].get_season()
# time_of_day = kwargs['time_of_day'] or kwargs['location'].get_time_of_day()

View file

@ -2,7 +2,7 @@
# # -*- mode:ruby; -*-
# Shell:4 ends here
# Character: Dabble
# Can I rename myself?
@ -21,68 +21,7 @@
Spectacles perched precariously on the end of his hooked nose, wobble with his head. A jaunty crimson cap contrasts with his dark brown cloak.
# Character: Dabble:2 ends here
# Would love to have the following /introduction/. So edit [[file:~/src/moss-n-puddles/server/conf/connection_screens.py::CONNECTION_SCREEN = """][connection_screens.py]]:
# #+begin_example
# Wha...what is this place?
# You followed his instructions. Head to the city park, and on the woodsy side, turn left at the lamp post. Peer in the brambles and when you see the red handkerchief tied to branch, follow it ...
# Just keep following the red kerchiefs, until...
# #+end_example
# To give someone a special _log on_ message the /first time/, I have updated the =Character= [[file:~/src/moss-n-puddles/typeclasses/characters.py::def at_object_creation(self):][creation function]] to give them a letter.
# Or some help:
# [[file:../../../projects/mud.org::*World: Moss and Puddles][World: Moss and Puddles:1]]
@sethelp start;me start = Again, welcome to my cozy little game.
To |wplay this game|n, you typically type a |w<verb>|b for an action, or |w<verb> <object>|b combinations. For instance, type |blook|n to look around the area, and |blook tree|n to examine the trees in particular. The more you look, the more you explore.
What verbs are available depends on where you are and what you might be holding. Type |bhelp|n with so other option to get a list. Then type |bhelp look|n to get details on how to use the |wlook|n verb.
This is a multi-user game, you might run into other characters, and they may look at you, so let's use some special verbs to affect the world from outside the storybook.
Type:
|w@setdesc A frumpy, but spry person with large ears and a dark blue cloak.|n
You see what people see when they look at you by typing:
|wlook self|n
What is the goal of this game? Just to escape the chaos of the world and explore an idyllic setting. Feel free to find me to chat, but good luck finding me, as I may be hiding.
# renaming
When you created your account, my game creates a character with the same name. If you want to rename yourself, you create a new character. Follow these steps.
First, delete your introductory letter, using the |bburn|n command:
|wburn letter|n
Next, leave your current character by typing:
|wooc|n
Then delete your old character with:
|wchardelete <original-name>|n
This only deletes the character of that name, not your account with that name.
Next, create a new character by typing:
|wcharcreate Rambler = A frumpy, but spry person with large ears and dark blue cloak.|n
And now, assume that character by typing:
|wic Rambler|n
# World: Moss and Puddles:1 ends here
# The Forest
# Rename the Limbo (or starting place) with the name *Forest*. Note the term =mp01= as a global label that matches my map.
@ -126,7 +65,7 @@ And now, assume that character by typing:
@detail flower;flowers = Beautiful, but giant flowers look down on you and nod a friendly greeting.
# The Forest:5 ends here
# Puddles
# With my name, I need to have an object to go with it.
@ -154,12 +93,12 @@ And now, assume that character by typing:
@set puddle/get_err_msg = "As the water slips through your fingers, you realize the apt metaphor of attempting to grasp at your youth.
# Puddles:3 ends here
# Sticks
# I will have a script auto generate the sticks on a regular basis, but for now, lets just make one that I can drop.
# [[file:../../../projects/mud.org::*Sticks][Sticks:1]]
@create stick : typeclasses.things.Stick
@create/drop stick : typeclasses.things.Stick
# Sticks:1 ends here
@ -175,7 +114,7 @@ Well, by sticky, we mean, stick-like.
Maybe, by sticky, we mean, wizardly-sticky...an absolutely amazing looking stick...definitely a wizardly stick.
# Sticks:2 ends here
# Boulder
# More details about the room that describes aspects of the boulder in the =desc= above. First, the symbol:
# [[file:../../../projects/mud.org::*Boulder][Boulder:1]]
@ -245,7 +184,7 @@ Wait! You notice a foot hold, and then another. You can |bclimb|n this boulder!
@set boulder/traverse_msg = "You move some ivy out of the way and pick your way up the boulder from one hold to another..."
# Boulder:9 ends here
# Top of Boulder
# Lets jump to the top of the boulder to proceed:
@ -320,7 +259,7 @@ Wait! You notice a foot hold, and then another. You can |bclimb|n this boulder!
@set moss/get_err_msg = The moss is a bryophyte, and as such, has attached itself to boulder by rhizoids, which are one cell thick root structures. While this helps with water absorption, you didn't think you were going to get a biology lesson, did you? tl;dr You can't get it.
# Top of Boulder:10 ends here
# Field
# To the east, lets make a nice meadow. Start at the Forest:
# [[file:../../../projects/mud.org::*Field][Field:1]]
@ -336,60 +275,84 @@ Wait! You notice a foot hold, and then another. You can |bclimb|n this boulder!
# Field:2 ends here
# And a nice journey message to go east out of the forest:
# And some aliases:
# [[file:../../../projects/mud.org::*Field][Field:3]]
@set east/traverse_msg = "The mossy tree roots that borders this footpath begin to thin out to clover and flowers..."
@name east = east;e;meadow
# Field:3 ends here
# A description if they look east:
# [[file:../../../projects/mud.org::*Field][Field:4]]
@desc east = A winding footpath that may lead out of the forest... Is that a meadow?
# Field:4 ends here
# And a nice journey message to go east out of the forest:
# [[file:../../../projects/mud.org::*Field][Field:5]]
@set east/traverse_msg = "The mossy tree roots that borders this footpath begin to thin out to clover and flowers..."
# Field:5 ends here
# Before we label it, we follow =east=:
# [[file:../../../projects/mud.org::*Field][Field:4]]
# [[file:../../../projects/mud.org::*Field][Field:6]]
east
# Field:4 ends here
# Field:6 ends here
# And we give this area a name:
# [[file:../../../projects/mud.org::*Field][Field:5]]
# [[file:../../../projects/mud.org::*Field][Field:7]]
@name here = Meadow;mp05
# Field:5 ends here
# Field:7 ends here
# And a description that takes advantage of the time of day. We should get busy and get some seasonal description here.
# [[file:../../../projects/mud.org::*Field][Field:6]]
# [[file:../../../projects/mud.org::*Field][Field:8]]
@desc here = The giant trees ring this meadow full of tall grass and large yellow flowers, who nod their heads to you as you pass by in the <evening>darkening twilight</evening><morning>awakening dawn</morning><afternoon>lazy afternoon</afternoon><night>night sky</night>. A gap in the trees show how you can return to the footpath in the forest.
# Field:6 ends here
# Field:8 ends here
# Since we use the =tunnel= command, we need to update the weather system:
# [[file:../../../projects/mud.org::*Field][Field:7]]
# [[file:../../../projects/mud.org::*Field][Field:9]]
@update here = typeclasses.rooms_weather.TimeWeatherRoom
# Field:7 ends here
# Field:9 ends here
# And describe the way out:
# [[file:../../../projects/mud.org::*Field][Field:8]]
@name west = footpath;forest;west
# Field:8 ends here
# [[file:../../../projects/mud.org::*Field][Field:10]]
@name west = footpath;forest;west;w
# Field:10 ends here
# Along with a mesage:
# [[file:../../../projects/mud.org::*Field][Field:9]]
# [[file:../../../projects/mud.org::*Field][Field:11]]
@set footpath/traverse_msg = "You leave the open meadow for the footpath through the giant trees. The yellow flowers nod goodbye to you..."
# Field:9 ends here
# Field:11 ends here
# And a description:
# [[file:../../../projects/mud.org::*Field][Field:12]]
@desc footpath = This footpath goes in a forest of immense trees.
# Field:12 ends here
# The Dock
# The dock leads out into the large pond. The break in the trees lets you see the sky. Looks like a nice place to relax.
# Return to the Forest:
@ -413,36 +376,51 @@ east
# The Dock:3 ends here
# And move ourselves there:
# And a description:
# [[file:../../../projects/mud.org::*The Dock][The Dock:4]]
south
@desc south = You see a dock on a large pond... Is that a comfortable-looking chair you can |bsit|n on?
# The Dock:4 ends here
# And move ourselves there:
# [[file:../../../projects/mud.org::*The Dock][The Dock:5]]
south
# The Dock:5 ends here
# Since we are on a dock in the pond, well emphasize that:
# [[file:../../../projects/mud.org::*The Dock][The Dock:5]]
# [[file:../../../projects/mud.org::*The Dock][The Dock:6]]
@name here = Lazy Dock;mp06
# The Dock:5 ends here
# The Dock:6 ends here
# And describe this.
# [[file:../../../projects/mud.org::*The Dock][The Dock:6]]
# [[file:../../../projects/mud.org::*The Dock][The Dock:7]]
@desc here = The dock you stand on juts into an immense pond. Someone has set a nice chair for viewing.
# The Dock:6 ends here
# The Dock:7 ends here
# And describe the walk back into the forest:
# [[file:../../../projects/mud.org::*The Dock][The Dock:7]]
# [[file:../../../projects/mud.org::*The Dock][The Dock:8]]
@set north/traverse_msg = "You walk up a path and back into the forest of giant trees."
# The Dock:7 ends here
# The Dock:8 ends here
# And a description:
# [[file:../../../projects/mud.org::*The Dock][The Dock:9]]
@desc north = This lead into the forest of collosal trees.
# The Dock:9 ends here
# Chair
# A nice chair to sit on the dock by the bay, watching the clouds as they drift away.
# [[file:../../../projects/mud.org::*Chair][Chair:1]]
@ -480,52 +458,63 @@ Looks good for being out in the weather.
# What about some details:
# And tether it to the Dock, #10, which we need to reset if the ID numbers ever change.
# [[file:../../../projects/mud.org::*Fishing Pole][Fishing Pole:3]]
@detail hook;bait = One of those shiny lures, made from gold coins. Curiouser.
@lock pole = tethered:id(#10)
#
@set pole/tethered_msg = "Let's leave the fishing pole here at the dock for others to fish...besides, where else are you going to go fishing around here?"
# Fishing Pole:3 ends here
# What about some details:
# [[file:../../../projects/mud.org::*Fishing Pole][Fishing Pole:4]]
@detail hook;bait = One of those shiny lures, made from gold coins. Curiouser. Seems you don't need to bait this hook.
#
@detail water;waves = Despite the weather, the water looks nice...well, nice for fishing.
#
@detail dock = Sturdy and well made. Bobs a little with the waves.
# Fishing Pole:3 ends here
# Fishing Pole:4 ends here
# Need to make the fishing pole “stay” at the Dock. Maybe with a message about sticking around for the next person.
# [[file:../../../projects/mud.org::*Fishing Pole][Fishing Pole:4]]
# [[file:../../../projects/mud.org::*Fishing Pole][Fishing Pole:5]]
@create/drop sign:typeclasses.readables.Readable
# Fishing Pole:4 ends here
# Fishing Pole:5 ends here
# Should the description also be the message?
# [[file:../../../projects/mud.org::*Fishing Pole][Fishing Pole:5]]
# [[file:../../../projects/mud.org::*Fishing Pole][Fishing Pole:6]]
@desc sign = You see a wood sign tied with a rope around the back of the chair. It reads, |wFish at your own annoyance. Please return pole when finished.|n
# Fishing Pole:5 ends here
# Fishing Pole:6 ends here
# Might as well allow the user to read it:
# [[file:../../../projects/mud.org::*Fishing Pole][Fishing Pole:6]]
# [[file:../../../projects/mud.org::*Fishing Pole][Fishing Pole:7]]
@set sign/inside = "Fish at your own annoyance. Please return pole when finished."
# Fishing Pole:6 ends here
# Fishing Pole:7 ends here
# And lock down the sign:
# [[file:../../../projects/mud.org::*Fishing Pole][Fishing Pole:7]]
# [[file:../../../projects/mud.org::*Fishing Pole][Fishing Pole:8]]
@lock sign = get:false()
#
@set sign/get_err_msg = "This granny knot holding the sign to the chair is serious. You can't take it."
# Fishing Pole:7 ends here
# Fishing Pole:8 ends here
# Forest Path
# Return to the forest:
@ -555,30 +544,70 @@ Looks good for being out in the weather.
# Forest Path:4 ends here
# And a description:
# [[file:../../../projects/mud.org::*Forest Path][Forest Path:5]]
@desc west = A footpath winds between the roots of a tree. You try to jump to look over the roots, and something red catches your eye.
# Forest Path:5 ends here
# Jump into the new room:
# [[file:../../../projects/mud.org::*Forest Path][Forest Path:5]]
# [[file:../../../projects/mud.org::*Forest Path][Forest Path:6]]
west
# Forest Path:5 ends here
# Forest Path:6 ends here
# And name it:
# [[file:../../../projects/mud.org::*Forest Path][Forest Path:6]]
# [[file:../../../projects/mud.org::*Forest Path][Forest Path:7]]
@name here = Forest Path;mp04
# Forest Path:6 ends here
# Forest Path:7 ends here
# And describe the tree and the door:
# [[file:../../../projects/mud.org::*Forest Path][Forest Path:7]]
@desc here = Moss-covered tree roots border the meandering footpath. A red door with a round top lies at the base of a giant tree, a carved sign over it reads, Dabblers.
# Forest Path:7 ends here
# [[file:../../../projects/mud.org::*Forest Path][Forest Path:8]]
@desc here = Moss-covered tree roots border the meandering footpath. A red door with a round top lies at the base of a giant tree, a carved sign over it reads, |wDabblers|n.
# Forest Path:8 ends here
# And some aliases:
# [[file:../../../projects/mud.org::*Forest Path][Forest Path:9]]
@name east = east;e;footpath
# Forest Path:9 ends here
# A description if they look east:
# [[file:../../../projects/mud.org::*Forest Path][Forest Path:10]]
@desc east = A winding footpath that leads through the forest of giant trees.
# Forest Path:10 ends here
# And a nice journey message to go east in of the forest:
# [[file:../../../projects/mud.org::*Forest Path][Forest Path:11]]
@set east/traverse_msg = "The mossy tree roots makes for an interesting path..."
# Forest Path:11 ends here
# And extra things to see:
# [[file:../../../projects/mud.org::*Forest Path][Forest Path:12]]
@detail trees;tree = The trees in this forest are massive, but a smaller tree has a red door. Must lead to a closet!
# Forest Path:12 ends here
# Berry Bush
# The berry bush is a [[file:~/src/moss-n-puddles/typeclasses/consumables.py::class Producer(Object):][producer]] that makes /berries/, which is something to eat and use to feed the wildlife.
@ -664,20 +693,20 @@ west
@set bush/make_finish_msg = "Those were [delicious|great]."
# Berry Bush:12 ends here
# Knocker
# The knocker has the ability to make the door “open” using [[https://www.evennia.com/docs/latest/api/evennia.objects.objects.html][on_traverse]] hooks?
# Most of the work of the /knocker/ is in the Python code:
# [[file:../../../projects/mud.org::*Knocker][Knocker:1]]
@create/drop knocker:typeclasses.things.Knocker
@create/drop knocker: typeclasses.things.Knocker
# Knocker:1 ends here
# If it /looks/ like a goblin…
# [[file:../../../projects/mud.org::*Knocker][Knocker:2]]
@name knocker = door knocker;knocker;goblin
@name knocker = Door knocker;knocker;goblin
# Knocker:2 ends here
@ -695,7 +724,7 @@ west
# Since we can remove the ring, lets create it:
# [[file:../../../projects/mud.org::*Knocker][Knocker:4]]
@create ring = typeclasses.things.Returnable
@create ring: typeclasses.things.Ring
# Knocker:4 ends here
@ -724,59 +753,81 @@ west
# And put the ring in the knockers mouth:
# And lets make it so we can take that:
# [[file:../../../projects/mud.org::*Knocker][Knocker:8]]
@give ring to knocker
@set ring/can_take = True
# Knocker:8 ends here
# Lets tether the ring to the knocker …
# [[file:../../../projects/mud.org::*Knocker][Knocker:9]]
@lock ring = tethered:id(#21)
#
@set ring/tethered_msg = You put the ring back in the knocker's mouth. "Mmmuufffmm," says the door knocker.
# Knocker:9 ends here
# And put the ring in the knockers mouth:
# [[file:../../../projects/mud.org::*Knocker][Knocker:10]]
@give ring to knocker
# Knocker:10 ends here
# The description is dynamic from the Python code, so we dont need this statement:
# [[file:../../../projects/mud.org::*Knocker][Knocker:9]]
# [[file:../../../projects/mud.org::*Knocker][Knocker:11]]
@desc knocker = The brass face looks at you and winks.
# Knocker:9 ends here
# Knocker:11 ends here
# In order for us to /knock/ on the door, we have to give a =knock= command to the /room/.
# [[file:../../../projects/mud.org::*Knocker][Knocker:10]]
@update here = typeclasses.rooms_weather.KnockableOutsideRoom
# Knocker:10 ends here
# [[file:../../../projects/mud.org::*Knocker][Knocker:12]]
@set knocker/room_to_msg = mp03
# Knocker:12 ends here
# The python object has all the knowledge about knocking and whatnot, but we should have the descriptions here. First, what the knocker reads:
# [[file:../../../projects/mud.org::*Knocker][Knocker:11]]
# [[file:../../../projects/mud.org::*Knocker][Knocker:13]]
@set here/knock_msg = "You grab the ring and knock firmly on the door."
# Knocker:11 ends here
# Knocker:13 ends here
# Then, what the others in the room read:
# [[file:../../../projects/mud.org::*Knocker][Knocker:12]]
# [[file:../../../projects/mud.org::*Knocker][Knocker:14]]
@set here/knock_other_msg = "grabs the ring and knocks firmly on the door."
# Knocker:12 ends here
# Knocker:14 ends here
# And an error message if the ring is not with the goblin:
# [[file:../../../projects/mud.org::*Knocker][Knocker:13]]
# [[file:../../../projects/mud.org::*Knocker][Knocker:15]]
@set here/knock_err_msg = "This door knocker is defective, as it doesn't have a ring to...er, do the knockin'."
# Knocker:13 ends here
# Knocker:15 ends here
# Cozy Tea House
# [[file:../../../projects/mud.org::*Cozy Tea House][Cozy Tea House:1]]
@dig house = red door;door;house;inside,outside;leave
# Cozy Tea House:1 ends here
# Red Door
# The door description should match the artwork on the website:
# [[file:../../../projects/mud.org::*Red Door][Red Door:1]]
@ -806,7 +857,7 @@ west
@set door/traverse_msg = "You stoop as you step through the doorway..."
# Red Door:4 ends here
# Inside
# Fix the inside of the “House”:
@ -832,7 +883,7 @@ west
# Might as well stay a while:
# [[file:../../../projects/mud.org::*Inside][Inside:4]]
@sethome here
@sethome me = here
# Inside:4 ends here
@ -910,26 +961,28 @@ west
# Touch up the exit:
# [[file:../../../projects/mud.org::*Inside][Inside:11]]
@open leave;outside =
@desc leave = A wood door with a large brass knob leads to the outside.
#
@set leave/traverse_msg = "You open the door and step outside..."
# Inside:11 ends here
# Fireplace
# And a fire in the fireplace is a type of /pet/, since we can feed it:
# [[file:../../../projects/mud.org::*Fireplace][Fireplace:1]]
@create/drop fireplace : typeclasses.pets.Fire
@create/drop fireplace;fire;embers : typeclasses.pets.Fire
# Fireplace:1 ends here
# How about some aliases:
# [[file:../../../projects/mud.org::*Fireplace][Fireplace:2]]
@name fireplace = fireplace;fire;embers
@lock fire = get:false()
# Fireplace:2 ends here
# And a description that will be overridden:
# [[file:../../../projects/mud.org::*Fireplace][Fireplace:3]]
@ -937,6 +990,7 @@ west
# Fireplace:3 ends here
# Since we mentioned some details, we better mention them:
# [[file:../../../projects/mud.org::*Fireplace][Fireplace:4]]
@ -959,7 +1013,7 @@ west
@detail picture = Above the fireplace is a large, somewhat abstract painting stretching its arm-like branches with shadowing that looks like a yawn.
# Fireplace:6 ends here
# Knickknacks
# What sort of non-books do we want to look at?
# In the meantime, lets just have some descriptions:
@ -969,7 +1023,7 @@ west
@detail knickknacks;things;knick-knacks;doodads;stuff;crap = An odd assortment of knickknacks and doodads that decorate the minimal space between the askewed books on the skewampus shelves.
# Knickknacks:1 ends here
# Books
# We mentioned shelves of books:
@ -1000,7 +1054,7 @@ west
@desc books = Books of different sizes and colors. So many books. Perhaps you want to simply |blook|n at a |bbook|n at random?
# Books:4 ends here
# Trolley of Scones
# The trolley [[file:~/src/moss-n-puddles/typeclasses/consumables.py::class Producer(Object):][produces]] random scones, which, like the [[Berry Trolley][berries]] can be eaten or fed to the wildlife.
@ -1033,7 +1087,7 @@ py here.search("trolley").do_bake()
@lock trolley = view:tag(hidden_ring, mp)
# Trolley of Scones:4 ends here
# Tea Service
# The “room” gives the teacups, and the “teapot” fills the cups, so we just need descriptions:
@ -1064,7 +1118,7 @@ py here.search("trolley").do_bake()
@set teapot/get_err_msg = "You don't need to carry it around to make tea."
# Tea Service:3 ends here
# Chairs
# And [[file:~/src/moss-n-puddles/typeclasses/sittables.py::class Sittables(Sittable):][the chairs]] is a /plural/ location to sit, allowing anyone to sit down.