Castle Arcanum Mob Programming

=== Mob Programs ===                             December 23, 1996

Mob programs let your mobiles come alive.

Table of Contents:

        The Basic Idea
        Associating Mob Programs with a Mobile
        Trigger Types
        Control Flow Syntax
        The If-Check List
        Special Mob Program Commands
        Advanced Examples
        Advanced Commands and Features

The documentation provided below was prepared at the request of several
area-writers seeking to enliven their contributions to Castle Arcanum.
It is an extension of incomplete, anonymous documentation I found on the
Internet. It's short - perhaps a bit too concise in places. Many of the
examples are mobprogs found running at Castle Arcanum.  Please stop by
( 4000) to discuss mobprog issues, to ask questions, or just
to enjoy yourself.
                                               - Gomez (

The mob program code running at Castle Arcanum is built on the base
originally written by N'Atas-Ha for diku/merc/envy.

-----------------------------The Basic Idea---------------------------------

On most muds, the mobiles never do anything but wait to be slaughtered.
"Special" programs can be coded into the mud and attached to some mobs
(such as the spec_mage procedure that lets mobs cast spells randomly
during a fight), but sophisticated mob/player interaction is difficult
to achieve this way. Furthermore, many excellent area writers are not
coders, or lack access to a mud's source code, and therefore cannot
create "specials" on their own. Enter the mob programs.

A mob program is a list of actions attached to a mob (in an area file),
activated by any of a variety of "triggers". If something of interest
happens in the mobile's room and triggers a response, then the list is
sent to the mud's command interpreter in the mobile's name, thus letting
her/it/him take appropriate action.

An example might help to clarify this:

the castle butler~
... standard mobile definition ...
.......... in area file ..........
>greet_prog 75~
bow $n
say Welcome, $N! Glad to see you.

The '>' signals the start of a mob program. The type of trigger is listed
next: A 'greet_prog' happens to be triggered whenever a player enters the
room the mob is in. The '75' is an argument defining conditions which fire
the trigger: In this case, the trigger will have a 75% chance of being set
off by a player's entry. The '~' marks the end of the trigger specification.

The following lines, up to the next '~', are the commands executed when the
trigger is set off. '$n' refers to the new room entrant (the 'actor', or
person who set off the trigger), and '$N' is the entrant's name. So the mob
will bow to the entrant, and then speak a welcoming message. If "Fred" enters
the room and the trigger fires, then the command interpreter will immediately
receive two commands from the butler:

     bow fred
     say Welcome, Fred! Glad to see you.

Fred will see:

     The castle butler bows before you.
     The castle butler says, "Welcome, Fred! Glad to see you."

Others in the room will see:

     Fred enters the room from the north. <standard arrival message>
     The castle butler bows before Fred.
     The castle butler says, "Welcome, Fred! Glad to see you."

-------------------Associating Mob Programs With A Mobile--------------------

A mob program sits in an area file, immediately after the standard definition
of the mob.  The basic syntax is:

>triggertype argumentlist~
more commands
>anothertriggertype anotherargumentlist~
even more commands

Each trigger type comes from a set list of triggers, and contains no spaces.
The nature of the argument list depends on the type of trigger, and may
contain several words and/or numbers separated by spaces.

As you can see, a single mob can have several programs attached to it.
A 'pipe' character (vertical bar) indicates the end of the mob program section
for this mobile.

fido dog~
the beastly fido~
A beastly fido is mucking through the garbage looking for food here.
The fido is a small dog that has a foul smell and pieces of rotted meat hanging
around his teeth.
161 0 -200 S
0 20 10 1d6+4 1d4+0
0 25
8 8 1
>greet_prog 60~
if alignment( $n ) >= 300
 mpechoat $n $I wags his tail at you.
 mpechoaround $n $I wags his tail at $N.
  growl $n

some other mob~

This program has a 60% chance of being triggered whenever a player enters the
room containing the fido. If the entrant ("Mary") is good (has an alignment
of +300 or more), then the mp-commands (special commands not available to
players) are carried out: Mary is sent the message "The beastly fido wags
his tail at you.", and others in the room see "The beastly fido wags his
tail at Mary." On the other hand, if Mary is not so good, the fido will growl
at her.

The if...else...endif construct permits the mob's response to depend on any
of a variety of conditions.

-----------------------------Trigger Types----------------------------------

The basic list of trigger types permits substantial interaction between
mobs and others. Other triggers can, of course, be added to the list if
an area writer makes a compelling case for the additions. Simple examples
are given for each trigger: More complex examples will follow.

Motion triggers:

GREET_PROG %      When a player enters a room, all mobs which are awake,
                  not fighting, can see the player, and have greet triggers
                  will greet the player. $n is the entrant.
ENTRY_PROG %      Triggers whenever the associated mob enters a room.

>entry_prog 100~
mpecho $I looks around suspiciously.

Each trigger takes a single number between 1 and 100 as its argument.
The number determines the chance that the trigger will execute its commands.

Battle triggers:

HITPRCNT_PROG %   Triggers if the associated mob is awake and fighting, and
                  the mob has fallen below a specified percentage of its
                  maximum hitpoints. $n is the opponent. If fighting
                  is stopped, the mob delivers no blow in the battle round,
                  and if there's also a FIGHT_PROG, it is skipped.
                  HITPRCNT_PROG tests for the mob's current hitpoint
                  percentage being less than or equal to the argument, so you
                  should test for small values first.

>hitprcnt_prog 20~       The first will trigger each battle round that a mob
yell HELP! I'M DYING!!!  with 200 hitpoints when healthy has less than 40
~                        hitpoints remaining. The second will be reached (and
>hitprcnt_prog 50~       will trigger) if the mob has between 40 and 100
say Oww! I'm BLEEDING!   hitpoints remaining.

FIGHT_PROG %      Triggers if the mob is awake and fighting, $n = opponent; if
                  fighting is stopped by the commands in the mprog, the attack
                  on $n is skipped. The argument is the chance of the trigger
                  firing in any one battle round.

>fight_prog 30~
tell $n I'm going to kill you. I swear I will!

DEATH_PROG %      After receiving a killing blow, the associated mob will
                  quietly stand up (with negative hps), carry out the commands
                  in the mprog, then lay down and die. The argument is the
                  chance that the program will be triggered upon death.
                  If 'mppurge self' occurs at the very end of the mprog, all
                  death output and effects (including experience gain) will
                  be skipped, and the mob will be removed from the room.
                  If 'mppurge abort dying' occurs as the last command, all
                  will be aborted but the mob will remain standing in the room.

>death_prog 25~
mpecho $I tries valiantly to run away ... and fails.

>death_prog 100~
restore self
restore $n
pat $n
say Nice try, little one.
mppurge abort dying

Object-related triggers:

GIVE_PROG text    Triggers if the mob is given an item with a namelist fully
                  matching the argument. If the text argument is "all", the
                  trigger occurs on any 'give'. $n is the giver, $o is the item

>give_prog ring pink ice~
tell $n Thanks, $N! Those old dolls are tough, aren't they?
wear $o

BRIBE_PROG #      Triggers if the mob is given an amount of gold >= argument;
                  creates money object accessible as $o, with value0 = exact
                  amount given; handle large bribes before smaller ones, since
                  only one bribe_prog will have a chance to fire.

>bribe_prog 1000~
say Hey, big spender? Wanna dance?
>bribe_prog 100~
wink $n
>bribe_prog 1~
frown $n
say Cheapskate!

Timer trigger:

RAND_PROG %       Triggers on each aggressive_update tick (roughly every
                  5-6 seconds) with the specified percentage chance.
                  RAND_PROG 100 will let a mob run the associated commands
                  every tick.

>rand_prog 20~    (On average, every 30 seconds)
mpecho $I hiccups.

Communication triggers:

SPEECH_PROG text  Triggers on anything said in the room; triggers even on
                  mob-says, which lets mobs trigger other mobs (beware of
                  infinite loops); text = "p ...phrase..." or a list of
                  separate words; matches must be from word/phrase start to
                  space, punctuation, or end of what was spoken.
                  $n is the speaker.

>speech_prog hi hello hiya~
say Hi yourself!
speech_prog p open sesame~
get key bag
unlock cavedoor
open cavedoor

ACT_PROG text     Triggers same as speech_prog, but on actions observed
                  in the room;
                  all triggers carried out on first aggressive_update tick
                  after action(s) are observed. $n is the actor, $o the object
                  (if any) being acted upon, $p a secondary object involved in
                  the action (if any), and $t the victim (if any) of the

>act_prog p hugs you.~
smile $n
wink $n
french $n

No mob programs will be triggered if the mobile is charmed (since it lacks
self-volition, it should act that way) to protect mobiles which are given
special powers from being abused by players.


To make things come alive, variables are needed.  These are represented
in mob programs using dollar-sign notation (as in socials). When a command
is processed, the variables are expanded into the values shown below.

variable              mob actor  victim random   object object2
                        <use these in command references>
name (one word)       $i    $n     $t     $r       $o     $p

                             <use these for output>
name(player)          $I    $N     $T     $R       $O     $P  short desc
short desc (mob)                                              (or "something")
someone (if invis to mob)

he/she/it             $j    $e     $E     $J                  $$ = '$' symbol
him/her/it            $l    $m     $M     $L
his/hers/its          $k    $s     $S     $K
a/an                                               $a     $A

In oprogs (mob programs attached to objects, to be described later),
$o will always properly refer to the object that carried the trigger,
even if other nearby objects have the same name. Always use $O in output
meant to be seen by players.

$r refers to a randomly-selected player (visible to the mob) in the room
when the trigger fires.

---------------------------Control Flow Syntax------------------------------

In order for mobs to be able to act differently as circumstances vary, it's
necessary that a mob program have a way of checking local conditions. This
is done by way of the if ... else ... endif structure.

if conditioncheck
else                 | the "else" section
 commands            | may be omitted

If the condition checks to be true, the commands between the "if" and the
"else" are carried out; if false, the commands between the else and the endif
are carried out. In either case, the mprog then continues on, carrying out the
commands that follow the endif.

>act_prog p pets you.~       if petted, the trigger fires.
wake                         wake up,
if ispc( $n )                if a player did the petting,
 growl $n                     growl
else                         otherwise (if a mob did the petting)
 say Welcome, $N!             welcome the mob,
 smile $n                     and smile at it
sleep                        no matter who did the petting, go back to sleep

The condition check here is "ispc( $n )".  It checks to see if the actor
(the petter) is a player-character: if so, the next commands are carried
out; if not, the "else" commands are carried out.

Notice the even sleeping mobs can be act_triggered. If you wanted a sleeping
mob to ignore an action, you could do the following:

>act_prog p pets you.~
if issleeping( $i )      if the mob is asleep, do nothing (no commands
else                     before the "else"); if awake, growl or smile
 if ispc( $n )           depending on whether the actor is a player or a mob
  growl $n
 else                    Indentation is not required, but can help you keep
  smile $n               track of the logic flow. Here, each "if" eventually
 endif                   matches up with an evenly-aligned "endif".

>act_prog p sits down.~        when someone sits down in the room
if rand( 60 )                  60% of the time
 yell Stand up, lazybones!       yell at them
endif                          (the other 40% of the time, do nothing)

In the examples above, then condition checks were direct. Many condition
checks are comparisons:

>entry_prog 100~               when the mob enters a room (100% of the time)
if inroom( $i ) == 12001       check if that room is the mob's "home"
 say I'm home!                 (room vnum 12001), and if so, speak

>rand_prog 9~                  once a minute, check the mob's current
if inroom( $i ) != 12001       location; if it's not where it should be,
 mpgoto 12001                  move it there

Arithmetic operators: == != > < >= <=
Logical operators:    & |
String operators:     == != / !/

For strings, == and != check for an exact match between two strings
and the other two, / and !/ check to see if the second string is contained in
the first one or is not.  if name($n) / guard  will be true for
"cityguard", "guard", "guardian", and so forth. The string operators are
case-insensitive ('a' matches 'A').

----------------------------The If-Check List-------------------------------

    rand( 60 )      true 60% of the time

basic checks:

   exists ( $r )
   ispc( $n )
   isnpc( $n )
   vnuminroom == mob_vnum or obj_vnum
   nameinroom == string (mob or object by name)

statistic checks (all numerical comparisons ):

   alignment( $n ) >= 300
   hasprof == cleric    text argument

... easy to add others ...

status checks:

   isfollowing                  TRUE only if master is in same room
   isfollower                   TRUE if target has master anywhere online
   isaffected( $n ) == bit-value
   inroom ($i ) == room_vnum     typically used by mob on itself to
                                 check current location

possession checks:

   ownsobject( $n ) == full namelist of object

object checks:

   objtype ( $o ) == 1    ( a light )

special checks:

if mpcalc( $n 2 3 ) >= 3    A powerful way to monitor the progression of a
                            "relationship" between a mob and another, or for
                            the mob to keep track of special occurrences.
                            More on this later.

--------------------Special Mob Program Commands----------------------------

Mob programs can issue all normal player commands and socials. In addition
some mprog-only commands are available:

mpasound     argument goes to all rooms adjacent to mob->in_room
mpasound Come out, come out, wherever you are!

mpat         takes action remotely; like the immortal "at" command
mpat $n tickle $n

mpcalc       attaches permanent counter fields to players, 
             temporary ones to mobs

mpdamage     does a controlled amount of damage to the target
mpdmothers   message seen by other-than-victim due to mpdamage
mpdmvictim   mesage seen by victim die to mpdamage

mpdmothers The lava field %s $N
mpdmvictim The lava field %s you
mpdamage $n 30 5 8 (do 30 + 5d8 damage to the victim, send out the
                    appropriate damage messages)

mpecho       sends message to all in room
mpechoaround sends message to all in room except mob and target
mpechoat     sends message to target (if in room)

mpecho A bolt of lightning flashes down from the sky!
mpechoat $n Whew! Just missed.
mpechoaround $n Wow! $N barely evaded that one.

mpforce      forces the target to performm an action
mpgoto       moves self
mpjunk       cleans mob's inventory

mpjunk sword
mpjunk $o
mpjunk all.candy
mpjunk all

mpkill       mob attacks argument without using "murder"

mppurge      removes mobs or objects from the room
mppurge targetname
mppurge all  (clear room except for players and triggered mob)

mpquiet      kills all output to all players
mpunquiet    restores output to players

mptransfer   !roomall as target to send all except self + all takeable items;
             "look" at end to force a look after all transed characters move

mpresetpath  restarts the path in a mob's path_prog (more later)

In addition to these commands, all immortal commands are available to a mob
its mob program is active. Timers can be oset, players and mobs mset, players
advanced, demoted, or denied, and so on. Because of this, mob programs must
(and will) be carefully reviewed before being brought online.

--------------------------- Advanced Examples ------------------------------

>act_prog p pokes you in the~
if isnpc( $n )
 poke $n
 if level( $n ) <= 5            <- notice that multiple if-checks
    or alignment( $n ) >= 0     <- can be "or"ed together on separate lines
  tell $n I would rather you didn't poke me.
  if level( $n ) > 15
   say You know, $N, I hate being poked!!!
   mpkill $n
   slap $n
   shout MOMMY!!! $N is poking me.

Chuckles and pokes back at mobs; asks players of low level or positive
alignment to stop; tells negatively aligned players above level 15 that
it hates being poked (and attacks), and shouts for mother if the poker
is negatively aligned and between levels 6 and 15.

To see how the following three mprogs work, do the quest found in
Gnome Village on Castle Arcanum ( 4000).

>greet_prog 100~
tell $n Can you help us? The hobgoblins are threatening to summon the `
   `Helm of Evil. With the Helm, their king and his troops will overrun `
   `our peaceful village.
mpechoat $n The gnome leader shivers in fear.
tell $n Please try to summon the Helm yourself, and bring it to me.
mpechoat $n The gnome leader smiles hopefully at you.
>give_prog helm helmet evil questhelm~
mpjunk questhelm
mpoload 1528 10
say That's...that's...that's IT!
recho The gnome leader shudders. Then he utters a magical `
     `phrase of great power.
recho A bolt of pure white lightning strikes down from the sky, and
recho the Helm of Evil is destroyed.
mpechoaround $n The gnome leader breathes a sigh of relief, then smiles at $n.
mpechoat $n The gnome leader breathes a sigh of relief, then smiles at you.
say Please let me reward you with this admittedly inadequate token of our `
give release $n
drop release
tell $n Perhaps someday it will help you out of an otherwise `
       `hopeless situation. `
       `Don't wear it until you need its power.
if mpcalc($n chiefgnome 0 10) > 10
 say You know, $n, you're getting pretty familiar around here.
 recho The chief gnome grins.
 bow $n
mpcalc $n chiefgnome 0 10 1 +

Using a backquote (`) at the end of one line and the beginning of the next
"glues" the lines together, permitting longish statements by mobs. As a
matter of good style, greet_progs should not use "say"s: Otherwise, when
a group enters a room, the first entrants will be spammed with the same
greeting several times. If the mob wants to give an item to a player, it's
nice to do a give, followed immediately by a drop: This way, if the player's
inventory is full, they still can get the item.

>greet_prog 100~
say You can't come in here! Oops. You already did. Prepare to die!
recho The troll swings his club at your head.
mpkill $n
say The Priest and Priestess won't like this!
>fight_prog 70~
if rand(33)
 say Stay away from the Transmogrifier!
 if rand(50)
  say May Cthulhu eat your soul, and belch it to the four winds!
  emote swings his club wildly over his head.
>death_prog 100~
say I can't stop you. But the Priest and Priestess will.
yell Sathogua! Take me!
mpecho There is a roll of thunder, and a swirl of `
      `utter blackness appears overhead!
mpecho A gigantic gnarled hand reaches down and snatches the troll. The hand `
      `withdraws with the troll writhing in its grasp. The blackness `
      `dissipates, and all is silent.
mppurge self

The death_prog guarantees that the room entrant receives a clue about the
Priest and Priestess, even if the troll is killed with a single blow. The
"mppurge self" at the end means no corpse, and no experience from the kill.


a quietmob~
2|65536|262144 8|32|512 -800 S
12 0 0 0d0+0 0d0+0
0 0
8 Hobgoblin~ 2
>act_prog p in the transmogrifier.~
get all altar
if hasobject($i) == questleftbraceletevil
 if hasobject($i) == questrightbraceletevil
  mpjunk all
  mpoload 1524 12 (Helm)
  drop helm
  recho There is a roll of thunder, and the room grows dim for a moment, as
  recho though some sinister force is draining the very essence of existence
  recho from the air around you. The Helm of Evil appears in front of `
       `the Altar!
put all altar

This mob carries a TOTAL_INVIS flag, so it can't be seen or affected by any
player. If the altar/transmogrifier contains the two required items, it
appears to the player as if everything is happening by divine intervention.

----------------------- Advanced Commands and Features ---------------------

MPCALC: Command and condition-check

syntax: mpcalc target keyword startbit bitwidth value <+/->
        if mpcalc( target keyword startbit bitwidth ) operator value

Mobs can keep track of their current "status" online, and can permanently
attach status flags to players. The program below has the frog add 1 to
the value currently stored in bit-positions 0-9 of its "froggy" record each
tick, sing when the total reaches 300, then reset those positions to 0.
A total of 32 bit-positions are available for use (0-31).  The first time
(after a fresh boot of the mud) that "mpcalc target keyword" is referenced,
all 32 bit positions are set to zero for that target mob or player. (That is,
the frog starts counting at 0 after a reboot.)

>rand_prog 100~
mpcalc $i froggy 0 10 1 +
if mpcalc( $i froggy 0 10 ) >= 300
 music Ribbit ribbit ribbit!
 mpcalc $i froggy 0 10 0

This is an example of a mob that permanently flags a player. When a
newbie first enters the room, the mob delivers a long speech. Later
visits get only a smile and a reminder. Indeed, seven trainers in the
Training Tower at Castle Arcanum all use different bit positions in the
same "training" record to monitor whether the player has visited them yet.

>speech_prog again~
bow $n
tell $n Welcome! Please 'read scroll' before going down. And remember `
       `that my fellow trainers and I will always repeat our lessons `
       `if you simply type 'say again?'
>greet_prog 100~
if mpcalc( $n training 0 1 ) == 0
 tell $n Welcome! Please 'read scroll' before going down. And remember `
        `that my fellow trainers and I will always repeat our lessons `
        `if you simply type 'say again?'
 smile $n
 mpcalc $n training 0 1 1
 smile $n
 tell $n Remember, all we trainers will repeat our lessons if you just `
        `type 'say again?'.


PATH_PROG: Complex series of actions, done over time


A path_prog allows a mob to be given a fixed sequence of actions spaced out
over time. Successive actions are separated by either a semicolon (time break)
or colon (no time break). The length of the time break is governed by the
arguments of the path_prog: No argument sets the break to one mob_update tick,
"t 3" would set it to three ticks, and "r 5 2" would randomly set each time
break to between 5-2 = 3 and 5+2 = 7 ticks. To insert extra time between
specific actions, just pile up the semicolons.

The end of a path_prog MUST be a semicolon, followed by a period.  When this
is reached, the path_prog restarts from the beginning.

Subprograms can be called at any point in the path, using @subprogname.
Within a subprogram, the command "mpresetpath" will force the path_prog back
to its beginning. See subprogram "2" below for an example of how to check a
mob's location, and get it back on path if someone screwed with it.

>path_prog t 2~
n;n;recho The minstrel looks at the sign.;e;e;e;e;
n;recho The minstrel peers into his locker.:
recho The minstrel admires his collection of instruments.;
>subprog_prog 1~
if rand(25)
 smile Protector
 if rand(50)
  say I absolutely LOVE it here at Castle Arcanum!
  emote sings a holiday carol for you.
>subprog_prog 2~
if inroom($i) != 12023
 recho The minstrel wanders off.
 mpgoto 12023
 recho A wandering minstrel wanders into the room.


FOR...NEXT: Command structure

Syntax: for ... next

Occasionally, you might want to let a programmed mob interact with ALL the
players and mobs in the same room. The for ... next structure cycles through
all room occupants except the programmed mob, setting $x to each in turn.

The example below checks to see if any players are in the room. ($r only
returns players, so the entire program ends immediately if there are none.
The "if exists( $r )" is a way to avoid processing the entire program if
the room contains only mobs.) The for ... next loop then examines each room
occupant in turn, and either damages players or lets their boots save them.

>rand_prog 100~
if exists( $r )
  if ispc( $x )
   if iswearing( $x ) = 32048
    if rand(20)
     mpechoat $x Your lava boots protect you from the heat!
    if rand(33)
     mpdmvictim The heat from the lava %s you
     mpdmothers The heat from the lava %s $X
     mpdamage 50 3 8 $x

Syntax: mpdmvictim <text>, mpdmothers <text>,
        mpdamage base dice sides target

The "lava mob" that runs the preceding program carries a TOTAL_INVIS flag.
The program replaces the mud-standard damage messages with custom messages,
substituting the appropriate damage verb, and punctuation at the end, from
what would have been the mud-standard message. Then the mpdamage command
does base + (dice)d(sides) damage to the target.


Castle Arcanum also allows mprogs to be attached to rooms and objects.
The currently-available triggers are listed below. When a trigger fires,
an invisible mob is sent to the place where action is occuring, is given
the "name" of the room or object, and carries out the program.

The Monorail (northwest corner of the upper grandstands in "The Circus")
provides a demonstration of what can be done with room_progs.

Please ask if you'd like more information.


Still here? One more example, then. ;)
This is definitely somewhat beyond the pale.

santa claus~
Santa Claus~
OOooOOooOOoo! It's SANTA!!!!!!!
.   SATAN                             SANTA                   .
Called Old Nick                   Called St. Nick
Keeps list of the naughty.        Keeps list of the naughty (and nice).
Dresses in red                    Dresses in red
Laughs malevolently               Is jolly
Has beard                         Has beard
Has horns                         Wears hat (hides horns?)
Has hooves                        Wears boots (hides hooves?)
Has imps                          Has elves
Prince of the Air                 Flys in a sled
Likes fire                        Opts to go down fireplace chimneys
                                  instead of just using the door.

2|8|65536 8|32|512 1000 S
101 0 0 0d0+0 0d0+0
0 0
8 Human~ 1
>rand_prog 2~
if inroom( $i ) == 12191
 mpgoto 12002
 recho Santa Claus comes sliding down the Chapel chimney!
 recho Santa looks around and smiles.
 shout HO! HO! HO!
 mpcalc $i santa 0 4 0
>speech_prog good ~
 smile $n
>speech_prog bad ~
 frown $n
>speech_prog yup yes yeah ~
 ruffle $n
>rand_prog 101~
if inroom( $i ) == 12002
 if rand( 10 )
  if mpcalc( $i santa 0 4 ) >= 1
   recho Santa wishes everyone a merry holiday season.
   recho Santa Claus lays a finger aside his nose. Up the chimney he goes!
   mpgoto 12191
  if rand( 15 )
   if ispc( $r )
    if mpcalc( $r santa1996 0 10 ) < 3
     if hasobject( $r ) == gift
      if mpcalc( $i santa 0 1 ) == 0
       say Are you enjoying your gift, $r?
       mpcalc $i santa 0 1 1
       tickle $r
      if hasobject( $r ) == specialbox
       if mpcalc( $i santa 1 1 ) == 0
        tell $r Once you've gotten the gift out of the box, `
               `you can throw the box away.
        wink $r
        mpcalc $i santa 1 1 1
        hug $r
       mpcalc $r santa1996 0 10 1 +
       mpoload 12102 1
       mpoload 12103 1
       oset gift timer 600
       open present
       put all present
       close present
       wake $r
       recho Santa checks his list.
       smile $r
       say I see you've behaved yourself this year, $r.
       recho Santa rummages around in his bag.
       give present $r
       drop present
       mpjunk all
       mpcalc $i santa 2 1 1
     if mpcalc( $i santa 3 1 ) == 0
      say Merry Christmas, $r!
      pat $r
      mpcalc $i santa 3 1 1