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);
]