[DGD] RE: [MUD-Dev] Dynamic Descriptions

Christopher Allen ChristopherA at skotos.net
Fri Mar 25 18:38:01 CET 2005


On Wednesday, March 23, 2005 1:12 AM Bernard Graham <> wrote:
> Has anyone ever created a workable dynamic description-solution for
> their muds.  I am delving into this and would like to get some
> pointers and ideas.  
> 
> Things I have started to think about include having weather, time of
> day and seasons affect the description.  Also, how would you properly
> implement smells and even things like the size of a room, for
> example, if you are in a small room it might feel claustrophobic,
> whereas if you are on a road inside a town it might feel "open".    
> 
> If anybody has any interesting ideas I would like to hear about it.

We do this at Skotos through something called SAM, or skotos active markup.
Here are two of our developer documents on SAM, we have a number more
internally, including how to create SAM tags with Merry (our sandboxed
version of LPC).

-- Christopher Allen

------------------------------------------------------------------------
.. Christopher Allen                                 Skotos Tech Inc. ..
.. <ChristopherA at skotos dot net>         2342 Shattuck Ave Ste 512 ..
.. www.skotos.net  www.rpg.net                Berkeley, CA 94704-1517 ..
.. office 510/649-4030         fax 510/849-1717     main 510-647-2760 ..
 

Introduction to SAM: Skotos Active Markup

The Problem of Dynamic Text

The Problem

SkotOS is largely concerned with presenting text to users. In a dynamic,
interactive system such as ours, environments and occurances are forever
changing, yet they must be described in consistent ways, even artistic ways,
to the inhabitants of the virtual world:

   You are kneeling next to Fred in a dingy hut.  Jane stands near the door.
A
   long, black sword lies at her feet. The rain pours down outside.

This is a description which is clearly associated with the hut in which we
are kneeling, yet refers to current occupants, items on the floor, and the
weather outside: the description mingles static text with dynamic data. The
system's ability to do this well is absolutely vital to making the user's
experience in the world a good one.

A Solution

Since the program that serves the user with the description is the same one
in which the dynamic data resides, there is a simple solution to the quandry
above. Let the descriptive text contain special codes that instruct the
world server to calculate and embed dynamic descriptions. This could be part
of the description of a statue you find in a room:

   "The statue appears to be $(this.expression) at you."

This statue is alive, it turns out, and can either frown at you or smile,
depending on whether or not you've kept up your sacrifices to the local dark
lord. Thus when a user looks at the statue, the server knows to query the
expression property of the statue. The definition of this property in the
statue includes some logic that investigates the user staring at it, and
sends back either 'frowning' or 'smiling'.

This is the core of SAM: the developer writes static descriptions that
contain active markup. The markup is evaluated by the server at each time
the user actually observes the description. The perceived description is
populated with current data.

SAM Everywhere

Once we came up with this idea of marked-up text, a whole world of
possibilities opened up before us. We look further than description; since
web pages are essentially text, why not use this same system to generate web
pages on the fly, populated with up-to-date world data:

   Welcome to Sir John's village status page!

   Johnsville currently has 612 inhabitants. The weather is cold
   and sunny. Thirty-one cows graze the fields. Alewife Christina
   made her latest batch of ale approximately nine hours ago.

The SAM in this case would underlie HTML rather than straight text, but to
the server there is no difference.

More Power

The more we play with SAM, the more its potential power became apparant to
us, and the more we added to it. Early on we decided to add conditional
functionality:

   This is a long black sword with intricate carvings.
   <if val="$(greaterthan lhs='$looker.skill:weapon-lore' rhs='5')">
      <true>
         . You recognize the make as Alvatian.
      </true>
      <false>
         It is nighttime.
      </false>
   </if>

where the user will only be told of the sword's make if he has studied his
weapon lore.

We also add iteration, e.g.

   Welcome to Sir John's cow status page! Here's who's grazing
   the fields today:
   <for var="cow" val="$(Cows)">
      $(Cow.CapitalizedName) is grazing in $(Cow.Pasture) and is
      feeling $(Cow.Contendedness).
   </for>

where $(Cows) is an array of, yes, cows, and the SAM within the for loop is
executed once for each element of this array. The result is a list of cows,
their whereabouts, and how happy they are at the moment.

We've introduced new syntax here; readers may recognize it as similar to
HTML. It is in fact XML, a close relative, and it is the syntactic model we
have chosen to express SAM.

Shortcuts for SAM

As we used more and more SAM, the extra "text" in the shortcuts required for
XML often made reading descriptions more difficult. To address this, we
created SAM Shortcuts. These are alternative ways to use SAM but with
shorter tags, making it descriptions easier to read.

For example, in the Black Sword example above, the following SAM shortcut
works exactly the same way:

   This is a long black sword with intricate carvings.
   {? greaterthen | $looker.skill:weapon-lore | 5 |
      . You recognize the make as Alvatian.|
      .}

Technically this is not XML markup, but internally SkotOS converts this to
XML.

Existing Approaches

(We need someone to write up how LPMuds and other MUDs have tried to do this
in the past)

Problems Solved by Skotos SAM System

(TBW)

State of Development

The SAM System is relatively mature and complete. Much of the fundamental
power of it was proven in Castle Marrach, and now is being used more fully
in various Skotos-Seven games, and is now also used for dynamic web pages
and for our web client.

Open Issues in the SAM System

There are very few major issues with SAM itself, due to its maturity and
several years of development. However, there are some related systems that
have weaknesses that affect SAM:

    * Right now brief/glance/look/examine descriptions (and their
all/dark/dim/bright/superbright variants) are not actually socials, so they
do not get as many arguments as do the social tags. Useful information that
is lacking includes which verb was used, the distance the actor is from the
detail being viewed, etc. As soon as these commands are converted to Merry,
all of these useful arguments will be available to these descriptions as
well.
    * There are quite a few commands that are not converted to Merry yet, so
can't be used with signal descriptions written in SAM.
    * The combinables system may have impact on SAM tags, as not all
'objects' will be single objects anymore.

=============

Quick Summary of the Skotos SAM System

SAM (Skotos Active Markup) allows text in descriptions (see Quick Summary:
Description System) to be dynamic.

OneOf

The simplest SAM is the OneOf -- basically using a OneOf in your text allows
you to randomize the output.

   Look: [
      You are inside {a large|an oversize|a} tent.
   ]
   Output(s):
      > look at tent
      You are inside a large tent.
      > look at tent
      You are inside an oversize tent
      > look at tent
      You are inside a tent.
      >

Output a Property

You can also output the value of a property very easily.

   Look: [
      The gem is $(this.gemcolor).
   ]
   Output(s):
      > look at gem
      The gem is blue.
      > +setp gem "gemcolor green
      Setting 'gemcolor' in <[Examples:complete:sam:gem]#4544> to "green".
      > look at gem
      The gem is green.
      >

In the above example, $(this) refers to the object itself, the gem itself.
$(this.gemcolor) refers to the value of the property "gemcolor" inside the
gem.

Arguments

$(this) is only one of many 'arguments' that SAM knows about that is
different depending on the circumstance that the SAM is executed.

In almost every situation, the following arguments are available:

    * $(actor) -- The object executing the action, e.g. whomever is taking
the sword
    * $(action) -- The name of the action currently executing, e.g. take
    * $(this) -- The object containing the SAM itself, e.g. the sword
    * $(target) -- The detail of the object that is the target of the
action.

In addition, in many situations, the following additional arguments are
available:

    * $(verb) -- The verb used in the action, e.g. 'polish' in "polish the
sword with my cloth"
    * $(dob) -- The object that is the direct object of the action, e.g. the
sword
    * $(using) -- the object that is the indirect object of the action, e.g.
the cloth

References

In addition to arguments, you can also reference specific objects.

    * $(Full:Woe:Name) -- the object with the woe name "Full:Woe:Name"
    * $[${Full:Woe:Name}] -- the object that is currently named
"Full:Woe:Name", and if the object is renamed, the reference will change
with it.

Property References

Once you have object defined, either by argument $this or by reference
$(Full:Woe:Name) or $[${Full:Woe:Name}], you can directly output the value
of properties that are in these objects into your descriptions, just by
adding a period followed by the property name:

    * $(this.trait:variant)
    * $(actor.trait:variant)
    * $(Full:Woe:Name.trait:variant)
    * $[${Full:Woe:Name.trait:variant}]

Some commonly used properties that output text:

    * $(this.base:possessive) -- possessive pronoun e.g. 'his'
    * $(this.base:objective) -- objective pronoun e.g. 'him'
    * $(this.base:pronoun) -- basic pronoun e.g. 'he'
    * $(this.base:light-category) -- object's luminosity, e.g. "dark",
"dim", or "bright"
    * $(this.details:default:description:brief) -- object's default brief
description

Examples of use:

   Look: [
      $(this.base:pronoun) looks very fierce. A scar runs from
$(this.base:possessive) cheek to $(this.base:possessive) chin, which doesn't
seem to bother $(this.base:objective).
   ]
   Output:
      > look at warrior
      She looks very fierce. A scar runs from her cheek to her chin, which
doesn't seem to bother her.
      >

   Look: [
      You are in a $(this.base:light-category) room.
   ]
   Output:
      > look
      You are in a dim room.
      >

Some property references don't output text, but instead refer to other
objects:

    * $(this.base:environment) -- object's environment
    * $(this.base:worn-by) -- object's wearer (same as wielded-by)
    * $(this.base:wielded-by -- object's wielder (same as wielded-by)

These are not that useful to use directly, but in turn you can then use
properties with these:

    * $(this.base:environment.base:possessive) -- object's environment
possessive pronoun
    * $(this.base:worn-by.base:pronoun) -- object's wearer's basic pronoun

Details of objects can be output:

* $("this.details:default:descriptions:look") -- the prime detail's look
description * $("this.details:lamp:descriptions:brief") -- the lamp detail's
brief description

Some detail IDs have spaces in them. The way to refer to a SAM reference
with a space in it is to put it in quotes -- e.g.

    * $("this.details:exit west:descriptions:look") -- the look description
of exit west

rather than

    * $(room.details:exit west:descriptions:look) -- won't work

XHTML Tags

There are some XHTML formatting tags that can be useful are are available in
SAM

    * <br/> -- a line break, a single newline
    * <sbr> -- single break, inserts <br/> when used in a webpage, or a
newline when used elsewhere.
    * <p/> -- a paragraph break, two newlines
    * <pre>text</pre> -- will output the text in a monospace font, without
formatting

Common Sam Tags

There are various times when you need specific output that is not easily
available in a property, or the property is in the wrong form. These SAM
tags can be useful in these cases:

These are Sam Tags for describing things:

    * <describe what="$(this)"/> -- This will output the brief of $(this)
object, e.g. "a sword"
    * <describemany what="$(this.base:inventory)"/> -- Describe many will
output multiple briefs, as if in an inventory, of the objects in
$(this.base:inventory).
    * <describe-holder what="$(this)"/> -- Equivalent to <describe
what="$(foo.base:holder)"/>'s except when the person's name ends in 's' or
'x' in which case it does the right thing and just adds the apostrophe
without a trailing s.
    * <describe-poss what="$(this)"> -- Describes $(this) in the possessive
form, e.g. "his sword"
    * <describe-prop this="$(this)" what="property:name"/> -- Adds "a" "the"
"an" to the text of the property appropriately.
    * *<describe-view view="$(this)" cap/> -- Will give the full description
of the object, including any visible inventory. e.g. "A tall orc, weilding
his sword."
    * <capitalize what="lower"> -- will initial capitalize the text "lower"
into "Lower"
    * <eaten what="$(this)"/> -- will output how much of the item has been
consumed.

Most of these description Sam Tags have a variety of options, see SamSystem
for details, but one of the most useful ones is the 'cap' option, which will
capitalize the output. For instance:

    * <describe what="$(this)"/> -- This will output the brief of $(this)
object, e.g. "A sword"

Client Sam

The following SAM tags are useful for special features in the Alice and
Zealous clients:

    * <atag tag="command"> text that you want themed with the color in the
'command' theme </atag>
    * <acmd tag="command" cmd="open west door">click here to open the west
door.</acmd>

Simple If Comparison

A simple if statement allows you to output one text if something is true, a
different text if false.

   Look: [
      A statue of a man. The statue's face {? this.trait:expression |appears
to be $(this.trait:expression) at you. |is expressionless. }
   ]
   Result(s):
      > look at statue
      A statue of a man. The statue's face is expressionless.
      > +setp statue "trait:expression similing
      Setting 'trait:expression' in <[Examples:complete:sam:statue]#4586> to
"smiling".
      > look at statue
      A statue of a man. The statue's face appears to be smiling at you.
      >

In this example, if the property trait:expression doesn't exist (i.e.
false), then "is expressionless" is output. If it does exist (true) then it
is "appears to be smiling at you" will be output.

Other Comparisons

There are quite a few inds of comparisons you can do in SAM:

BOOLEAN

    {? | $this.trait:variant |
        true text output |
        false text output }

    {? not | $this.trait:variant |
        true text output |
        false text output }
    {? equal | $this.trait:variant | ordinary |
        true text output |
        false text output }

    {? notequal | $this.trait:variant | ordinary |
        true text output |
        false text output }

    {? lessthen | $this.trait:variant | ordinary |
        true text output |
        false text output }

    {? lessthenorequal | $this.trait:variant | ordinary |
        true text output |
        false text output }

    {? greaterthen | $this.trait:variant | ordinary |
        true text output |
        false text output }

    {? greaterthenorequal | $this.trait:variant | ordinary |
        true text output |
        false text output }

    {? range | $this.trait:variant | lowervalue | uppervalue |
        true text output |
        false text output }
    
    Comment: range is exclusive, i.e. 
         {? range | 1 | 3 | 6 | true | false }"
            1   false
            2   false
            3   false
            3.1 true
            4   true
            5   true
            6   true
            6.1 false
            7   false
            8   false
            9   false


    {? when | $this.trait.variant |
        value1 | value1 equal $this.trait:variant |
        value2 | value2 equal $this.trait:variant |
        value3 | value2 equal $this.trait:variant |
        *      | any other value }

Inline Merry

You can put Merry scripts inside of of SAM as follows:

   Look: [
      You are on a road. There are $[this.stars_in_sky + this.moons_in_sky]
celestial bodies above you.
   ]

If the piece of Merry does not end in a semi-colon or a right-brace, it is
assumed to be a 'statement' rather than an 'expression'. This means the code
needs to explicitly return a value. For example,

   You are on a road. There are
     $[
         if (this.stars_in_sky > 5) {
             return "lots of";
         }
         if (this.stars_in_sky > 0) {
             return "a few";
         }
         return "no";
      ]
   stars in the sky.

Inline Merry and Text

It can often be useful in SAM to manipulate text, so there are some very
simple inline Merry functions that can perform this for you.

Text related:

    * $[capitalize("this is a test")] -- result: "This is a test"
    * $[proper("This is a test")] -- result: "This Is A Test"
    * $[decapitalize("This Is A Test")] -- result: "this Is A Test"
    * $[upper_case("This Is A Test")] -- result: "THIS IS A TEST"
    * $[lower_case("this Is A Test")] -- result: "this is a test"

Number related:

    * $[desc_cardinal(128)] -- result: "one hundred twenty-eight"
    * $[desc_ordinal(128)] -- result: "one hundred twenty-eighth"
    * $[comma(1024)] -- result: "1,024"

Inline Merry and Comparisons

You can combine arbitrary Merry expressions for tests to use in comparisons,
to make very sophisticated output. For example:

    {? | $[this.foo == "bar"] |
        true text output |
        false text output }

    {? | $[random(100)] |
        true one in 100 times. |
        false text output }

    {? | $[random(6)+random(6)+random(6)+3 == 18] |
        You rolled 3 sixes! |
        You didn't roll 3 sixes. }

    {? range | $[random(100)] | 25 | 75 |
        You rolled in the middle of the range. |
        You rolled outside the range. }
        

These can be quite sophisticated, for instance:

    {? | $[sizeof(Match($actor, "cigarette"))] |
    Actor has a cigarette. | Actor doesn't have cigarette. }
       Actor has a cigarette.
       Actor doesn't have a cigarette
       

Common Inline Merry Uses

There are many things you can do in SAM by using inline Merry, but these are
some of the most common:

    * $[Set($this, "my:property:name", 1);] -- Sets the property
my:property:name in object $this to 1.
    * $[Set($this, "my:property:name", Get($this, "my:property:name) + 1); ]
-- Adds 1 to property my:property:name on object $this


You look at <describe what=$(this)> and your surroundings change!
   $[      EmitIn(Get($actor, "base:environment"), Describe($actor) + "
looks at " + Describe($dbob) + " and disappears!\n", $actor);
         $actor."base:environment" =
${Examples:complete:desc:room-go-nowhere};
         $actor."base:proximity" =
NewNRef(${Examples:complete:desc:room-go-nowhere}, "floor");
         $actor."base:stancestring" = "lying";
         $actor."base:prepositionstring" = "on";
         EmitIn(Get($actor, "base:environment"), Describe($actor) + "
appears out of nowhere lying on the floor!\n", $actor);
   ]





More information about the DGD mailing list