Adding a batchcommands file with descriptions

And fixed a number of bugs with starting from scratch.
This commit is contained in:
Howard Abrams 2025-02-12 22:37:19 -08:00
parent 0850301c65
commit 3d1ebfa3fc
9 changed files with 1255 additions and 343 deletions

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python
from commands.command import Command
from evennia import CmdSet
from evennia import Command, CmdSet
# from .command import Command
class CmdLight(Command):
"""
@ -11,19 +11,17 @@ class CmdLight(Command):
key = "light"
aliases = ["burn"]
# only allow this command if command.obj is carried by caller.
locks = "cmd:holds()"
# locks = "cmd:holds()"
def func(self):
"""
Implements the light command. Since this command is designed
to sit on a "lightable" object, we operate only on self.obj.
"""
if self.obj.light():
self.caller.msg("You light %s." % self.obj.key)
self.caller.location.msg_contents(
"%s lights %s!" % (self.caller, self.obj.key), exclude=[self.caller]
)
"%s lights %s!" % (self.caller, self.obj.key), exclude=[self.caller])
else:
self.caller.msg("%s is already burning." % self.obj.key)
@ -31,10 +29,6 @@ class CmdLight(Command):
class CmdSetLight(CmdSet):
"""CmdSet for the lightsource commands"""
key = "lightsource_cmdset"
# this is higher than the dark cmdset - important!
priority = 3
def at_cmdset_creation(self):
"""called at cmdset creation"""
self.add(CmdLight())
self.add(CmdLight)

97
typeclasses/lightables.py Executable file
View file

@ -0,0 +1,97 @@
#!/usr/bin/env python
from commands.lighting import CmdSetLight
# -------------------------------------------------------------
#
# LightSource
#
# This object emits light. Once it has been turned on it
# cannot be turned off. When it burns out it will delete
# itself.
#
# This could be implemented using a single-repeat Script or by
# registering with the TickerHandler. We do it simpler by
# using the delay() utility function. This is very simple
# to use but does not survive a server @reload. Because of
# where the light matters (in the Dark Room where you can
# find new light sources easily), this is okay here.
#
# -------------------------------------------------------------
class LightSource(Object):
"""
This implements a light source object.
When burned out, the object will be deleted.
"""
def at_init(self):
"""
If this is called with the Attribute is_giving_light already
set, we know that the timer got killed by a server
reload/reboot before it had time to finish. So we kill it here
instead. This is the price we pay for the simplicity of the
non-persistent delay() method.
"""
if self.db.is_giving_light:
self.delete()
def at_object_creation(self):
"""Called when object is first created."""
super().at_object_creation()
self.db.is_giving_light = False
self.db.burntime = 60 * 3 # 3 minutes
# this is the default desc, it can of course be customized
# when created.
self.db.desc = "A tapered candle."
# add the Light command
self.cmdset.add_default(CmdSetLight, persistent=True)
def _burnout(self):
"""
This is called when this light source burns out. We make no
use of the return value.
"""
# delete ourselves from the database
self.db.is_giving_light = False
try:
self.location.location.msg_contents(
"%s's %s flickers and dies." % (self.location, self.key), exclude=self.location
)
self.location.msg("Your %s flickers and dies." % self.key)
self.location.location.check_light_state()
except AttributeError:
try:
self.location.msg_contents("A %s on the floor flickers and dies." % self.key)
self.location.location.check_light_state()
except AttributeError:
# Mainly happens if we happen to be in a None location
pass
self.delete()
def light(self):
"""
Light this object - this is called by Light command.
"""
if self.db.is_giving_light:
return False
# burn for 3 minutes before calling _burnout
self.db.is_giving_light = True
# if we are in a dark room, trigger its light check
try:
self.location.location.check_light_state()
except AttributeError:
try:
# maybe we are directly in the room
self.location.check_light_state()
except AttributeError:
# we are in a None location
pass
finally:
# start the burn timer. When it runs out, self._burnout
# will be called. We store the deferred so it can be
# killed in unittesting.
self.deferred = delay(60 * 3, self._burnout)
return True

110
typeclasses/objects.py Normal file → Executable file
View file

@ -1,3 +1,5 @@
#!/usr/bin/env python
"""
Object
@ -9,9 +11,8 @@ with a location in the game world (like Characters, Rooms, Exits).
"""
from evennia.objects.objects import DefaultObject
from evennia.utils import delay, search
from evennia.utils import delay, logger, search
from commands.lighting import CmdSetLight
class ObjectParent:
"""
@ -231,96 +232,27 @@ class Object(ObjectParent, DefaultObject):
elif i == item:
return i
# -------------------------------------------------------------
#
# LightSource
#
# This object emits light. Once it has been turned on it
# cannot be turned off. When it burns out it will delete
# itself.
#
# This could be implemented using a single-repeat Script or by
# registering with the TickerHandler. We do it simpler by
# using the delay() utility function. This is very simple
# to use but does not survive a server @reload. Because of
# where the light matters (in the Dark Room where you can
# find new light sources easily), this is okay here.
#
# -------------------------------------------------------------
class LightSource(Object):
"""
This implements a light source object.
When burned out, the object will be deleted.
"""
def at_init(self):
def at_look(self, target, **kwargs):
"""
If this is called with the Attribute is_giving_light already
set, we know that the timer got killed by a server
reload/reboot before it had time to finish. So we kill it here
instead. This is the price we pay for the simplicity of the
non-persistent delay() method.
"""
if self.db.is_giving_light:
self.delete()
When we look at something that _might_ be hidden, we check
if we are looking at something that _is_ hidden, i.e. has a
view lock of a particular tag, and if so, we interpret this as
'you are looking at something by name', therefore you see it.
So we give ourselves the right to see it from now on.
def at_object_creation(self):
"""Called when object is first created."""
super().at_object_creation()
To use this, simply add the following lock:
self.db.is_giving_light = False
self.db.burntime = 60 * 3 # 3 minutes
# this is the default desc, it can of course be customized
# when created.
self.db.desc = "A tapered candle."
# add the Light command
self.cmdset.add_default(CmdSetLight, persistent=True)
@lock thing = view:tag(hidden_thing, mp)
def _burnout(self):
Where thing is the single name of the hidden object.
"""
This is called when this light source burns out. We make no
use of the return value.
"""
# delete ourselves from the database
self.db.is_giving_light = False
try:
self.location.location.msg_contents(
"%s's %s flickers and dies." % (self.location, self.key), exclude=self.location
)
self.location.msg("Your %s flickers and dies." % self.key)
self.location.location.check_light_state()
except AttributeError:
try:
self.location.msg_contents("A %s on the floor flickers and dies." % self.key)
self.location.location.check_light_state()
except AttributeError:
# Mainly happens if we happen to be in a None location
pass
self.delete()
hidden_tag = f"hidden_{target}"
lock_string = f"view:tag({hidden_tag}, mp)"
view_lock = target.locks.get("view")
def light(self):
"""
Light this object - this is called by Light command.
"""
if self.db.is_giving_light:
return False
# burn for 3 minutes before calling _burnout
self.db.is_giving_light = True
# if we are in a dark room, trigger its light check
try:
self.location.location.check_light_state()
except AttributeError:
try:
# maybe we are directly in the room
self.location.check_light_state()
except AttributeError:
# we are in a None location
pass
finally:
# start the burn timer. When it runs out, self._burnout
# will be called. We store the deferred so it can be
# killed in unittesting.
self.deferred = delay(60 * 3, self._burnout)
return True
if view_lock == lock_string:
self.tags.add(hidden_tag, category="mp")
# Regardless of what happened before, we return the normal
# function call.
return super().at_look(target)

View file

@ -7,7 +7,8 @@ Each level of pet requires more aspects for interaction.
"""
from typeclasses.objects import Object, LightSource
from typeclasses.objects import Object
# from typeclasses.lightables import LightSource
from commands.feedables import CmdFeed, CmdFeedSet
from utils.word_list import squish
@ -180,12 +181,12 @@ class Fire(Pet):
gets_up = "gets up and"
if self.db.hunger_level < 5:
feeder.msg(squish(f"You {get_up} put some {adj} wood in the "
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)
else:
feeder.msg(squish(f"You {get_up} put some {adj} wood on the "
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)

View file

@ -9,7 +9,7 @@ from evennia.objects.objects import DefaultRoom
from evennia.contrib.grid.extended_room import ExtendedRoom
from evennia.prototypes.spawner import spawn
from .objects import LightSource
# from .objects import LightSource
from .drinkables import TEACUP_DESCS
from .pets import Hunger
from commands.drinkables import CmdSetTrolley
@ -35,7 +35,6 @@ from evennia import (
utils,
)
# from .objects import LightSource
_SEARCH_AT_RESULT = utils.object_from_module(settings.SEARCH_AT_RESULT)
from .objects import ObjectParent
@ -54,242 +53,6 @@ class Room(ObjectParent, ExtendedRoom):
is_dark = False
has_weather = False
pass
# -------------------------------------------------------------------------------
#
# Dark Room - a room with states
#
# This room limits the movemenets of its denizens unless they carry an active
# LightSource object (LightSource is defined in objects.LightSource)
#
# -------------------------------------------------------------------------------
DARK_MESSAGES = (
"It is pitch black. You are likely to be eaten by a grue.",
"It's pitch black. You fumble around but cannot find anything.",
"You don't see a thing. You feel around, managing to bump your fingers hard against something. Ouch!",
"You don't see a thing! Blindly grasping the air around you, you find nothing.",
"It's totally dark here. You almost stumble over something on the floor.",
"You are completely blind. For a moment you think you hear someone breathing nearby ... "
"\n ... surely you must be mistaken.",
)
ALREADY_LIGHTSOURCE = (
"You don't want to stumble around in blindness anymore. You already "
"found what you need. Let's get the fireplace lit already!"
)
FOUND_LIGHTSOURCE = (
"Your fingers bump against a candle on a candleabra."
"You pick it up, holding it firmly. Now you just need to"
" |wlight|n it using the flint and steel you carry with you."
)
class CmdLookDark(Command):
"""
Look around in darkness
Usage:
look
Look around in the darkness, trying
to find something.
"""
key = "look"
aliases = ["l", "feel", "search", "feel around", "fiddle"]
locks = "cmd:all()"
def func(self):
"""
Implement the command.
This works both as a look and a search command; there is a
random chance of eventually finding a light source.
"""
caller = self.caller
# count how many searches we've done
nr_searches = caller.ndb.dark_searches
if nr_searches is None:
nr_searches = 0
caller.ndb.dark_searches = nr_searches
if nr_searches < 4 and random.random() < 0.90:
# we don't find anything
caller.msg(random.choice(DARK_MESSAGES))
caller.ndb.dark_searches += 1
else:
# we could have found something!
if any(obj for obj in caller.contents if utils.inherits_from(obj, LightSource)):
# we already carry a LightSource object.
caller.msg(ALREADY_LIGHTSOURCE)
else:
# don't have a light source, create a new one.
create_object(LightSource, key="candle", location=caller)
caller.msg(FOUND_LIGHTSOURCE)
class CmdDarkHelp(Command):
"""
Help command for the dark state.
"""
key = "help"
locks = "cmd:all()"
def func(self):
"""
Replace the the help command with a not-so-useful help
"""
string = (
"Can't help you until you find some light! Try looking/feeling around for something to burn. "
"You shouldn't give up even if you don't find anything right away."
)
self.caller.msg(string)
class CmdDarkNoMatch(Command):
"""
This is a system command. Commands with special keys are used to
override special sitations in the game. The CMD_NOMATCH is used
when the given command is not found in the current command set (it
replaces Evennia's default behavior or offering command
suggestions)
"""
key = syscmdkeys.CMD_NOMATCH
locks = "cmd:all()"
def func(self):
"""Implements the command."""
self.caller.msg(
"Until you find some light, there's not much you can do. "
"Try feeling around, maybe you'll find something helpful!"
)
class DarkCmdSet(CmdSet):
"""
Groups the commands of the dark room together. We also import the
default say command here so that players can still talk in the
darkness.
We give the cmdset the mergetype "Replace" to make sure it
completely replaces whichever command set it is merged onto
(usually the default cmdset)
"""
key = "darkroom_cmdset"
mergetype = "Replace"
priority = 2
def at_cmdset_creation(self):
"""populate the cmdset."""
self.add(CmdLookDark())
self.add(CmdDarkHelp())
self.add(CmdDarkNoMatch())
self.add(default_cmds.CmdSay())
self.add(default_cmds.CmdQuit())
self.add(default_cmds.CmdHome())
class DarkRoom(Room):
"""A dark room. This tries to start the DarkState script on all
objects entering. The script is responsible for making sure it is
valid (that is, that there is no light source shining in the room).
The is_lit Attribute is used to define if the room is currently lit
or not, so as to properly echo state changes.
Since this room is meant as a sort of catch-all, we also make sure
to heal characters ending up here.
"""
def at_object_creation(self):
"""
Called when object is first created.
"""
super().at_object_creation()
# the room starts dark.
self.db.is_lit = False
self.cmdset.add(DarkCmdSet, persistent=True)
def at_init(self):
"""
Called when room is first recached (such as after a reload)
"""
self.check_light_state()
def _carries_light(self, obj):
"""
Checks if the given object carries anything that gives light.
Note that we do NOT look for a specific LightSource typeclass,
but for the Attribute is_giving_light - this makes it easy to
later add other types of light-giving items. We also accept
if there is a light-giving object in the room overall (like if
a candle was dropped in the room or the fireplace is lit).
"""
return (
obj.is_superuser
or obj.db.is_giving_light
or any(o for o in obj.contents if o.db.is_giving_light)
)
def check_light_state(self, exclude=None):
"""
This method checks if there are any light sources in the room.
If there isn't it makes sure to add the dark cmdset to all
characters in the room. It is called whenever characters enter
the room and also by the Light sources when they turn on.
Args:
exclude (Object): An object to not include in the light check.
"""
if any(self._carries_light(obj) for obj in self.contents if obj != exclude):
self.locks.add("view:all()")
self.cmdset.remove(DarkCmdSet)
self.db.is_lit = True
for char in (obj for obj in self.contents if obj.has_account):
# this won't do anything if it is already removed
char.msg("The room is lit up.")
else:
# noone is carrying light - darken the room
self.db.is_lit = False
self.locks.add("view:false()")
self.cmdset.add(DarkCmdSet, persistent=True)
for char in (obj for obj in self.contents if obj.has_account):
if char.is_superuser:
char.msg("You are Superuser, so you are not affected by the dark state.")
else:
# put players in darkness
char.msg("The room is completely dark.")
def at_object_receive(self, obj, source_location, move_type="move", **kwargs):
"""
Called when an object enters the room.
"""
if obj.has_account:
# a puppeted object, that is, a Character
self._heal(obj)
# in case the new guy carries light with them
self.check_light_state()
def at_object_leave(self, obj, target_location, move_type="move", **kwargs):
"""
In case people leave with the light, we make sure to clear the
DarkCmdSet if necessary. This also works if they are
teleported away.
"""
# since this hook is called while the object is still in the room,
# we exclude it from the light check, to ignore any light sources
# it may be carrying.
self.check_light_state(exclude=obj)
class DabblersRoom(Room):
teacup_prototype = {

235
typeclasses/rooms_dark.py Executable file
View file

@ -0,0 +1,235 @@
#!/usr/bin/env python
# -------------------------------------------------------------------------------
#
# Dark Room - a room with states
#
# This room limits the movemenets of its denizens unless they carry an active
# LightSource object (LightSource is defined in objects.LightSource)
#
# -------------------------------------------------------------------------------
DARK_MESSAGES = (
"It is pitch black. You are likely to be eaten by a grue.",
"It's pitch black. You fumble around but cannot find anything.",
"You don't see a thing. You feel around, managing to bump your fingers hard against something. Ouch!",
"You don't see a thing! Blindly grasping the air around you, you find nothing.",
"It's totally dark here. You almost stumble over something on the floor.",
"You are completely blind. For a moment you think you hear someone breathing nearby ... "
"\n ... surely you must be mistaken.",
)
ALREADY_LIGHTSOURCE = (
"You don't want to stumble around in blindness anymore. You already "
"found what you need. Let's get the fireplace lit already!"
)
FOUND_LIGHTSOURCE = (
"Your fingers bump against a candle on a candleabra."
"You pick it up, holding it firmly. Now you just need to"
" |wlight|n it using the flint and steel you carry with you."
)
class CmdLookDark(Command):
"""
Look around in darkness
Usage:
look
Look around in the darkness, trying
to find something.
"""
key = "look"
aliases = ["l", "feel", "search", "feel around", "fiddle"]
locks = "cmd:all()"
def func(self):
"""
Implement the command.
This works both as a look and a search command; there is a
random chance of eventually finding a light source.
"""
caller = self.caller
# count how many searches we've done
nr_searches = caller.ndb.dark_searches
if nr_searches is None:
nr_searches = 0
caller.ndb.dark_searches = nr_searches
if nr_searches < 4 and random.random() < 0.90:
# we don't find anything
caller.msg(random.choice(DARK_MESSAGES))
caller.ndb.dark_searches += 1
else:
# we could have found something!
if any(obj for obj in caller.contents if utils.inherits_from(obj, LightSource)):
# we already carry a LightSource object.
caller.msg(ALREADY_LIGHTSOURCE)
else:
# don't have a light source, create a new one.
create_object(LightSource, key="candle", location=caller)
caller.msg(FOUND_LIGHTSOURCE)
class CmdDarkHelp(Command):
"""
Help command for the dark state.
"""
key = "help"
locks = "cmd:all()"
def func(self):
"""
Replace the the help command with a not-so-useful help
"""
string = (
"Can't help you until you find some light! Try looking/feeling around for something to burn. "
"You shouldn't give up even if you don't find anything right away."
)
self.caller.msg(string)
class CmdDarkNoMatch(Command):
"""
This is a system command. Commands with special keys are used to
override special sitations in the game. The CMD_NOMATCH is used
when the given command is not found in the current command set (it
replaces Evennia's default behavior or offering command
suggestions)
"""
key = syscmdkeys.CMD_NOMATCH
locks = "cmd:all()"
def func(self):
"""Implements the command."""
self.caller.msg(
"Until you find some light, there's not much you can do. "
"Try feeling around, maybe you'll find something helpful!"
)
class DarkCmdSet(CmdSet):
"""
Groups the commands of the dark room together. We also import the
default say command here so that players can still talk in the
darkness.
We give the cmdset the mergetype "Replace" to make sure it
completely replaces whichever command set it is merged onto
(usually the default cmdset)
"""
key = "darkroom_cmdset"
mergetype = "Replace"
priority = 2
def at_cmdset_creation(self):
"""populate the cmdset."""
self.add(CmdLookDark())
self.add(CmdDarkHelp())
self.add(CmdDarkNoMatch())
self.add(default_cmds.CmdSay())
self.add(default_cmds.CmdQuit())
self.add(default_cmds.CmdHome())
class DarkRoom(Room):
"""A dark room. This tries to start the DarkState script on all
objects entering. The script is responsible for making sure it is
valid (that is, that there is no light source shining in the room).
The is_lit Attribute is used to define if the room is currently lit
or not, so as to properly echo state changes.
Since this room is meant as a sort of catch-all, we also make sure
to heal characters ending up here.
"""
def at_object_creation(self):
"""
Called when object is first created.
"""
super().at_object_creation()
# the room starts dark.
self.db.is_lit = False
self.cmdset.add(DarkCmdSet, persistent=True)
def at_init(self):
"""
Called when room is first recached (such as after a reload)
"""
self.check_light_state()
def _carries_light(self, obj):
"""
Checks if the given object carries anything that gives light.
Note that we do NOT look for a specific LightSource typeclass,
but for the Attribute is_giving_light - this makes it easy to
later add other types of light-giving items. We also accept
if there is a light-giving object in the room overall (like if
a candle was dropped in the room or the fireplace is lit).
"""
return (
obj.is_superuser
or obj.db.is_giving_light
or any(o for o in obj.contents if o.db.is_giving_light)
)
def check_light_state(self, exclude=None):
"""
This method checks if there are any light sources in the room.
If there isn't it makes sure to add the dark cmdset to all
characters in the room. It is called whenever characters enter
the room and also by the Light sources when they turn on.
Args:
exclude (Object): An object to not include in the light check.
"""
if any(self._carries_light(obj) for obj in self.contents if obj != exclude):
self.locks.add("view:all()")
self.cmdset.remove(DarkCmdSet)
self.db.is_lit = True
for char in (obj for obj in self.contents if obj.has_account):
# this won't do anything if it is already removed
char.msg("The room is lit up.")
else:
# noone is carrying light - darken the room
self.db.is_lit = False
self.locks.add("view:false()")
self.cmdset.add(DarkCmdSet, persistent=True)
for char in (obj for obj in self.contents if obj.has_account):
if char.is_superuser:
char.msg("You are Superuser, so you are not affected by the dark state.")
else:
# put players in darkness
char.msg("The room is completely dark.")
def at_object_receive(self, obj, source_location, move_type="move", **kwargs):
"""
Called when an object enters the room.
"""
if obj.has_account:
# a puppeted object, that is, a Character
self._heal(obj)
# in case the new guy carries light with them
self.check_light_state()
def at_object_leave(self, obj, target_location, move_type="move", **kwargs):
"""
In case people leave with the light, we make sure to clear the
DarkCmdSet if necessary. This also works if they are
teleported away.
"""
# since this hook is called while the object is still in the room,
# we exclude it from the light check, to ignore any light sources
# it may be carrying.
self.check_light_state(exclude=obj)

View file

@ -69,6 +69,8 @@ class Sittables(Sittable):
multiple = True
def sit_msg(self):
aliases = self.aliases.all()
adjective = self.db.adjective or "on"
article = self.db.article or "the"
name = aliases[-1] # Last alias is singular
@ -76,8 +78,6 @@ class Sittables(Sittable):
singular = self.db.singular or default
extra = self.db.extra or ""
aliases = self.aliases.all()
return routput(f"You sit {adjective} {singular}. {extra}")
def stand_msg(self):

View file

@ -20,6 +20,12 @@ from random import choice, random
import re
class Unhider(Object):
"""
This thing has the ability to "unhide" other things.
The idea is .... ugh.
@set thing/
"""
class Returnable(Object):
"""
This object can't go far from one or two locations.

884
world/version1.ev Normal file
View file

@ -0,0 +1,884 @@
# [[file:../../../projects/mud.org::*Shell][Shell:4]]
# # -*- mode:ruby; -*-
# Shell:4 ends here
# Can I rename myself?
# [[file:../../../projects/mud.org::*Character: Dabble][Character: Dabble:1]]
@name self = Dabbler;gnome;old gnome
# Character: Dabble:1 ends here
# And a good description that I can rework:
# [[file:../../../projects/mud.org::*Character: Dabble][Character: Dabble:2]]
@desc self = A small, hunched old man with a gray vandyke and an eye
twinkle. 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
# Rename the Limbo (or starting place) with the name *Forest*. Note the term =mp01= as a global label that matches my map.
# [[file:../../../projects/mud.org::*The Forest][The Forest:1]]
@name here = The Forest;mp01
# The Forest:1 ends here
# The description will take advantage of the /seasons/ and /times/ of the day:
# [[file:../../../projects/mud.org::*The Forest][The Forest:2]]
@desc here = A giant, moss-covered boulder stands among immense trees
that etch the sky and slice the clouds in the <evening>darkening
twilight</evening><morning>awakening dawn</morning><afternoon>lazy
afternoon</afternoon><night>night sky</night>. A footpath winds around
the giant, moss-covered tree roots to the East and West. To the south,
a dock lounges on a large pond.
# The Forest:2 ends here
# Need to add weather and time data to this:
# [[file:../../../projects/mud.org::*The Forest][The Forest:3]]
# from evennia.contrib.tutorials.tutorial_world.rooms import WeatherRoom
@update here = typeclasses.rooms_weather.TimeWeatherRoom
# The Forest:3 ends here
# 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]]
@detail symbol = You move an ivy runner to see three curves join
together as if it were three legs.
# Boulder:1 ends here
# The moss is purdy:
# [[file:../../../projects/mud.org::*Boulder][Boulder:2]]
@detail moss = "Even in this light, the moss radiates a surreal color of green."
# Boulder:2 ends here
# And the ivy is … uh.
# [[file:../../../projects/mud.org::*Boulder][Boulder:3]]
@detail ivy = "You see...well, ivy. Uhm, it's green, and..."
# Boulder:3 ends here
# The runes use multibyte unicode which screws up the =telnet= client.
# [[file:../../../projects/mud.org::*Boulder][Boulder:4]]
@detail runes = "The runes read ᛞ ᚪ ᛒ ᛚ ᚱ"
# Boulder:4 ends here
# The top of the boulder should be another room, accessible by the =climb=, but maybe it isnt “seen” until the boulder has been looked? First, we put some weather at the top of the boulder:
# [[file:../../../projects/mud.org::*Boulder][Boulder:5]]
@dig Top of Boulder;mp02 : typeclasses.rooms_weather.TimeWeatherRoom = boulder,climb
# Boulder:5 ends here
# The ability to /climb/ the boulder isnt immediately obvious, so lets make it a bit of a secret:
# [[file:../../../projects/mud.org::*Boulder][Boulder:6]]
@desc boulder = A boulder with patches of moss and delicate clover.
A carved symbol and even some runes try to hide behind tendrils of ivy
as if keeping a secret. Wait! You notice a foot hold, and then
another. You can |bclimb|n this boulder!
# Boulder:6 ends here
# To take advantage of our [[Hidden Things]], we put the right tag on it:
# [[file:../../../projects/mud.org::*Boulder][Boulder:7]]
@lock boulder = view:tag(hidden_boulder, mp)
# Boulder:7 ends here
# And give it some aliases:
# [[file:../../../projects/mud.org::*Boulder][Boulder:8]]
@name boulder = boulder;climb;climb up
# Boulder:8 ends here
# [[file:../../../projects/mud.org::*Boulder][Boulder:9]]
@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
# Lets jump to the top of the boulder to proceed:
# [[file:../../../projects/mud.org::*Top of Boulder][Top of Boulder:1]]
@teleport mp02
# @name here = Top of Boulder;mp02
# Top of Boulder:1 ends here
# And the description:
# [[file:../../../projects/mud.org::*Top of Boulder][Top of Boulder:2]]
@desc here = While high, the trees still tower over you. Still, a nice view. Lots of patches of moss to sit down and relax.
# Top of Boulder:2 ends here
# Describe the climb down:
# [[file:../../../projects/mud.org::*Top of Boulder][Top of Boulder:3]]
@name climb = climb back down;climb down;climb;down
# Top of Boulder:3 ends here
# And a description of the climb:
# [[file:../../../projects/mud.org::*Top of Boulder][Top of Boulder:4]]
@desc climb = The climb seemed easy, but you're not sure you can
handle the footholds going the other way around.
# Top of Boulder:4 ends here
# And describe the journey:
# [[file:../../../projects/mud.org::*Top of Boulder][Top of Boulder:5]]
@set climb/traverse_msg = "You crawl backwards, feeling with your foot for a hold. Got it! Then another one...hoping the moss' rhizoids hold..."
# Top of Boulder:5 ends here
# Lets make a nice spot to sit down on:
# [[file:../../../projects/mud.org::*Top of Boulder][Top of Boulder:6]]
@create/drop moss;patch:typeclasses.sittables.Sittable
# Top of Boulder:6 ends here
# With a nice description:
# [[file:../../../projects/mud.org::*Top of Boulder][Top of Boulder:7]]
@desc moss = A cushioned patch of moss of the most vibrant green.
# Top of Boulder:7 ends here
# Cant take the moss with you:
# [[file:../../../projects/mud.org::*Top of Boulder][Top of Boulder:8]]
@lock moss = get:false()
# Top of Boulder:8 ends here
# Can we hide it too?
# [[file:../../../projects/mud.org::*Top of Boulder][Top of Boulder:9]]
@lock moss = view:tag(hidden_moss, mp)
# Top of Boulder:9 ends here
# And a lovely message about why you cant steal moss:
# [[file:../../../projects/mud.org::*Top of Boulder][Top of Boulder:10]]
@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
# To the east, lets make a nice meadow. Start at the Forest:
# [[file:../../../projects/mud.org::*Field][Field:1]]
@teleport mp01
# Field:1 ends here
# And we use the =tunnel= command this time:
# [[file:../../../projects/mud.org::*Field][Field:2]]
@tunnel e = path
# Field:2 ends here
# And a nice journey message to go east out of the forest:
# [[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..."
# Field:3 ends here
# Before we label it, we follow =east=:
# [[file:../../../projects/mud.org::*Field][Field:4]]
east
# Field:4 ends here
# And we give this area a name:
# [[file:../../../projects/mud.org::*Field][Field:5]]
@name here = Meadow;mp05
# Field:5 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]]
@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
# Since we use the =tunnel= command, we need to update the weather system:
# [[file:../../../projects/mud.org::*Field][Field:7]]
@update here = typeclasses.rooms_weather.TimeWeatherRoom
# Field:7 ends here
# And describe the way out:
# [[file:../../../projects/mud.org::*Field][Field:8]]
@name west = footpath;forest;west
# Field:8 ends here
# Along with a mesage:
# [[file:../../../projects/mud.org::*Field][Field:9]]
@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
# 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:
# [[file:../../../projects/mud.org::*The Dock][The Dock:1]]
@teleport mp01
# The Dock:1 ends here
# And tunnel to the pond:
# [[file:../../../projects/mud.org::*The Dock][The Dock:2]]
@tunnel s = dock
# The Dock:2 ends here
# With a mesage about leaving the trees so that I dont have to repeat that in the room description:
# [[file:../../../projects/mud.org::*The Dock][The Dock:3]]
@set south/traverse_msg = "You follow a path down and step out from under giant trees to see the sky."
# The Dock:3 ends here
# And move ourselves there:
# [[file:../../../projects/mud.org::*The Dock][The Dock:4]]
south
# The Dock:4 ends here
# Since we are on a dock in the pond, well emphasize that:
# [[file:../../../projects/mud.org::*The Dock][The Dock:5]]
@name here = Lazy Dock;mp06
# The Dock:5 ends here
# And describe this.
# [[file:../../../projects/mud.org::*The Dock][The Dock:6]]
@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
# And describe the walk back into the forest:
# [[file:../../../projects/mud.org::*The Dock][The Dock:7]]
@set north/traverse_msg = "You walk up a path and back into the forest of giant trees."
# The Dock:7 ends here
# 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]]
@create/drop chair;lounge chair:typeclasses.sittables.Sittable
# Chair:1 ends here
# How descriptive:
# [[file:../../../projects/mud.org::*Chair][Chair:2]]
@desc chair = A cushioned wood chair of craftsmanship.
Looks good for being out in the weather.
# Chair:2 ends here
# Cant steal this chair either:
# [[file:../../../projects/mud.org::*Chair][Chair:3]]
@lock chair = get:false()
# Chair:3 ends here
# [[file:../../../projects/mud.org::*Chair][Chair:4]]
@set chair/get_err_msg = "It's way too heavy for you to lift."
# Chair:4 ends here
# And a fishing pole?
# First we need to make a =PermanentObject= (from the regular =Object=), but one that offers a
# [[file:../../../projects/mud.org::*Fishing Pole][Fishing Pole:1]]
@create/drop fishing pole;pole:typeclasses.things.FishingPole
@desc pole = A nice pole for catching fish. It even has a hook.
@detail hook = One of those shiny lures, made from gold coins. Curiouser.
@detail water = Despite the weather, the water looks fine. Perhaps there is a hole
# Fishing Pole:1 ends here
# Return to the forest:
# [[file:../../../projects/mud.org::*Forest Path][Forest Path:1]]
@teleport mp01
# Forest Path:1 ends here
# Lets travel west along the path in the forest:
# [[file:../../../projects/mud.org::*Forest Path][Forest Path:2]]
@tunnel w = path
# Forest Path:2 ends here
# With a nice message about wandering:
# [[file:../../../projects/mud.org::*Forest Path][Forest Path:3]]
@set west/traverse_msg = "You meander between mossy tree roots along a
footpath for a while..."
# Forest Path:3 ends here
# And some aliases:
# [[file:../../../projects/mud.org::*Forest Path][Forest Path:4]]
@name west = west;w;footpath
# Forest Path:4 ends here
# Jump into the new room:
# [[file:../../../projects/mud.org::*Forest Path][Forest Path:5]]
west
# Forest Path:5 ends here
# And name it:
# [[file:../../../projects/mud.org::*Forest Path][Forest Path:6]]
@name here = Forest Path;mp04
# Forest Path:6 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
# 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
# Knocker:1 ends here
# If it /looks/ like a goblin…
# [[file:../../../projects/mud.org::*Knocker][Knocker:2]]
@name knocker = door knocker;knocker;goblin
# Knocker:2 ends here
# The knocker shouldnt be stealable:
# [[file:../../../projects/mud.org::*Knocker][Knocker:3]]
@lock knocker = get:false()
#
@set knocker/get_err_msg = "It appears firmly attached to the door."
# Knocker:3 ends here
# Since we can remove the ring, lets create it:
# [[file:../../../projects/mud.org::*Knocker][Knocker:4]]
@create ring = typeclasses.things.Returnable
# Knocker:4 ends here
# And give it an alias:
# [[file:../../../projects/mud.org::*Knocker][Knocker:5]]
@name ring = brass ring;ring
# Knocker:5 ends here
# And a description:
# [[file:../../../projects/mud.org::*Knocker][Knocker:6]]
@desc ring = "A brass ring that should be in the door knocker's mouth.
How else are you going to knock on a door?"
# Knocker:6 ends here
# Although we can interact with it, lets not make it obvious that it is an object:
# [[file:../../../projects/mud.org::*Knocker][Knocker:7]]
@lock ring = view:tag(hidden_ring, mp)
# Knocker:7 ends here
# And put the ring in the knockers mouth:
# [[file:../../../projects/mud.org::*Knocker][Knocker:8]]
@give ring to knocker
# Knocker:8 ends here
# The description is dynamic from the Python code, so we dont need this statement:
# [[file:../../../projects/mud.org::*Knocker][Knocker:9]]
@desc knocker = The brass face looks at you and winks.
# Knocker:9 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
# 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]]
@set here/knock_msg = "You grab the ring and knock firmly on the door."
# Knocker:11 ends here
# Then, what the others in the room read:
# [[file:../../../projects/mud.org::*Knocker][Knocker:12]]
@set here/knock_other_msg = "grabs the ring and knocks firmly on the door."
# Knocker:12 ends here
# And an error message if the ring is not with the goblin:
# [[file:../../../projects/mud.org::*Knocker][Knocker:13]]
@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
# [[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
# The door description should match the artwork on the website:
# [[file:../../../projects/mud.org::*Red Door][Red Door:1]]
@desc red door = A painted red door where the round top of the door
fits snugly in the bark of the tree. Along with a large brass
doorknob, the door sports the most curious door knocker in the shape
of a goblin's face.
# Red Door:1 ends here
# This exit should be special. First, it is locked unless a character has the =open_red_door= tag.
# [[file:../../../projects/mud.org::*Red Door][Red Door:2]]
@lock door = traverse:tag(open_red_door, mp)
# Red Door:2 ends here
# Needs a message why one cant walk through:
# [[file:../../../projects/mud.org::*Red Door][Red Door:3]]
@set door/err_traverse = "The door seems locked."
# Red Door:3 ends here
# If you do figure out how to get through the door:
# [[file:../../../projects/mud.org::*Red Door][Red Door:4]]
@set door/traverse_msg = "You stoop as you step through the doorway..."
# Red Door:4 ends here
# Fix the inside of the “House”:
# [[file:../../../projects/mud.org::*Inside][Inside:1]]
@teleport red door
# Inside:1 ends here
# Add the Python /special-ness/ for [[file:~/src/moss-n-puddles/typeclasses/rooms.py::class DabblersRoom(Room):][this room]]:
# [[file:../../../projects/mud.org::*Inside][Inside:2]]
@update here = typeclasses.rooms.DabblersRoom
# Inside:2 ends here
# Give it a title:
# [[file:../../../projects/mud.org::*Inside][Inside:3]]
@name here = Dabbler's House;mp03
# Inside:3 ends here
# Might as well stay a while:
# [[file:../../../projects/mud.org::*Inside][Inside:4]]
@sethome here
# Inside:4 ends here
# And the best description ever:
# [[file:../../../projects/mud.org::*Inside][Inside:5]]
@desc here = An enormous stone hearth overshadows this round room with
dark paneling. A subtle smell of tea and incense. Large, overstuffed
chairs sit invitingly by the fireplace. Oddly angled shelves with
books and knickknackery adorn the walls while a trolley supports a
large kettle, cups and scones.
# Inside:5 ends here
# Since we want the description to include the state of the fire, we need some /parts/ to assemble. Still not sure how this should be done.
# [[file:../../../projects/mud.org::*Inside][Inside:6]]
@set here/initial_desc = "You found a cozy, cornerless room."
# Ravenous State
@set here/fire_out = "The room is dim, but you see large, overstuffed chairs placed
by a dark fireplace in a large stone hearth. Perhaps you could light a fire?"
# Hungry State
@set here/fire_dim = "Large, overstuffed chairs sit invitingly close to the
dimly glowing embers in the fireplace of a stone hearth."
# Fed State
@set here/fire_on = "Large, overstuffed chairs sit invitingly by a fire casting
shadows that dance on the dark paneling."
# Full State
@set here/fire_full = "Large, overstuffed chairs slightly shield you
from the bright light of the roaring fire in the fireplace of a stone hearth."
# And a final description:
@set here/final_desc = "Oddly angled shelves with books and
knickknackery adorn the walls around a tapestry. The subtle smell of
wood smoke, incense and tea leads you to a trolley supporting a large
teapot, cups and freshly baked scones."
# Inside:6 ends here
# Granted, none of the dynamic description will work until we create the [[Fire]].
# Lets come up with a lot of descriptions:
# [[file:../../../projects/mud.org::*Inside][Inside:7]]
@detail tapestry = The muted colors of the tapestry either show its
age or its location over the sometimes smokey hearth. It shows a
gallant stag surrounded by woodland creatures. A racoon holds aloft a
gold box while a wolf has a gnarled staff. A raven perched on the
stag's antlers grips a blue ball in its beak.
# Inside:7 ends here
# We should describe all the objects in the tapestry, eh?
# [[file:../../../projects/mud.org::*Inside][Inside:8]]
@detail raven = The raven winks at you, and says, “Nevermore.” Really?
It probably didn't. Your eyes must be playing trickster.
# Inside:8 ends here
# And the other animals:
# [[file:../../../projects/mud.org::*Inside][Inside:9]]
@detail wolf = The gnarled staff the wolf holds looks a lot like the staff the gnome, Dabbler, has.
#
@detail stag = A majestic looking deer with gold fur and antlers that look more like tree branches.
#
@detail raccoon = The box he's holding seems to be a distraction, for he's pocketing a gold coin with his other paw.
# Inside:9 ends here
# Will this staff get confused at some point?
# [[file:../../../projects/mud.org::*Inside][Inside:10]]
@detail staff = A gnarled staff made of oak has a dark petina from age. Three small leaves has sprouted from the side.
# Inside:10 ends here
# Touch up the exit:
# [[file:../../../projects/mud.org::*Inside][Inside:11]]
@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
# 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
# Fireplace:1 ends here
# How about some aliases:
# [[file:../../../projects/mud.org::*Fireplace][Fireplace:2]]
@name fireplace = fireplace;fire;embers
# Fireplace:2 ends here
# And a description that will be overridden:
# [[file:../../../projects/mud.org::*Fireplace][Fireplace:3]]
@desc fireplace = A stone fireplace with a carved wooden mantel supporting
small pictures, some books and a black statue.
# Fireplace:3 ends here
# Since we mentioned some details, we better mention them:
# [[file:../../../projects/mud.org::*Fireplace][Fireplace:4]]
@detail pictures = Two small framed pictures perch above the fireplace
mantle. One is of a satyr playing a saxophone, and the other is of a
fish with a big smile.
# Fireplace:4 ends here
# This reference in this detail is obviously, only for me:
# [[file:../../../projects/mud.org::*Fireplace][Fireplace:5]]
@detail statue = A small, black statue of a salamander curled around a
book, engraved with a title, "Seeing Stones".
# Fireplace:5 ends here
# I imagine a giant picture over the fireplace … need to do something interesting with it.
# [[file:../../../projects/mud.org::*Fireplace][Fireplace:6]]
@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
# What sort of non-books do we want to look at?
# In the meantime, lets just have some descriptions:
# [[file:../../../projects/mud.org::*Knickknacks][Knickknacks:1]]
@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
# We mentioned shelves of books:
# [[file:../../../projects/mud.org::*Books][Books:1]]
@detail shelves;bookshelf;bookshelves = Shelves at various angles
embellish the walls of this small, cozy room. Leatherbound books
weigh each shelf, while some stacks of books support other shelves.
Dabbler has decorated some shelves with odd trinkets.
# Books:1 ends here
# This should be an object of “books”, but looking should pull up a random list of books.
# [[file:../../../projects/mud.org::*Books][Books:2]]
@create/drop books:typeclasses.readables.Books
# Books:2 ends here
# I start with the name, =books=, but when we look in the room, I want to see /collection/:
# [[file:../../../projects/mud.org::*Books][Books:3]]
@name books = collection of books;books
# Books:3 ends here
# With a good description:
# [[file:../../../projects/mud.org::*Books][Books:4]]
@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
# We mentioned a /trolley/ with tea, cups and scones:
# [[file:../../../projects/mud.org::*Tea Service][Tea Service:1]]
@detail trolley = A tea trolley, complete with a small collection of
teacups, a magical teapot, as well as an assortment of scones and pastries.
# Tea Service:1 ends here
# The “room” gives the teacups, and the “teapot” fills the cups, so we just need descriptions:
# [[file:../../../projects/mud.org::*Tea Service][Tea Service:2]]
@detail teacups = An odd, yet interesting assortment of teacups. Take one.
#
@detail teacup = Each cup is unique. Take one to look further.
# Tea Service:2 ends here
# A [[file:~/src/moss-n-puddles/typeclasses/drinkables.py::class Teapot(Object):][teapot]] will contain a type of tea … either given or randomly chosen.
# A teapot will “fill” a teacup forever, until it is “remade”.
# [[file:../../../projects/mud.org::*Tea Service][Tea Service:3]]
@create/drop teapot;pot;tea pot:typeclasses.drinkables.Teapot
#
@desc teapot = An adorable brown teapot, waiting for you to |bmake|n some tea.
# Tea Service:3 ends here
# And of course, you cant steal the tea pot:
# [[file:../../../projects/mud.org::*Tea Service][Tea Service:4]]
@lock teapot = get:false()
#
@set teapot/get_err_msg = "You don't need to carry it around to make tea."
# Tea Service:4 ends here
# 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.
# [[file:../../../projects/mud.org::*Chairs][Chairs:1]]
@create/drop chairs:typeclasses.sittables.Sittables
# Chairs:1 ends here
# Add aliases afterwards:
# [[file:../../../projects/mud.org::*Chairs][Chairs:2]]
@name chairs = few overstuffed chairs;overstuffed chairs;chairs;chair
# Chairs:2 ends here
# And the description:
# [[file:../../../projects/mud.org::*Chairs][Chairs:3]]
@desc chairs = A few, dark leather, overstuffed chairs, each with a soft, colorful blanket and pillow. An invitation for a cozy nap.
# Chairs:3 ends here
# Cant steal em:
# [[file:../../../projects/mud.org::*Chairs][Chairs:4]]
@lock chairs = get:false()
#
@set chairs/get_err_msg = "It's way too heavy for you to lift."
# Chairs:4 ends here
# And textual descriptions the object can use:
# [[file:../../../projects/mud.org::*Chairs][Chairs:5]]
@set chairs/adjective = "in"
#
@set chairs/article = "the"
#
@set chairs/singular = "an overstuffed chair"
#
@set chairs/extra = "This feels [|very|quite] [nice|cozy|comfortable].|n"
# Chairs:5 ends here