Fatal Dimensions
 

Benedict's Mob Program Patterns

By Benedict
(c) January 30 2000

This document gives an overview of simple and more complex mob programs in the TCL-secured programming language that is used in Fatal Dimensions. In this way, non-programmers can get a head-start with adding behaviour and effects to their own designed and built areas.

CREDENTIALS

How to use this document

I. WHAT ARE MOB PROGRAMS?
I.1. The .are file
I.2.1. Offline editing
I.2.2. Online editing

II. PATTERNS

III. THINGS THAT OFTEN GO WRONG

IV. CONCLUSION


CREDENTIALS

This document has been written by Benedict (aka Henri Achten irl). Last known url that worked: http://www.ds.arch.tue.nl/General/staff/henri/. The document draws from experience on JoranMUD (http://www.fataldimensions.org/), also known as Fatal Dimensions. It therefore leans on the feedback and kind debugging of Fatal, MavEtJu, Verlag and players who have pointed out bugs many times. The source code examples are all made by Benedict. They are offered here as study material and basic material to start your own programming.
Keep this document intact and make due references to the sources mentioned in the credentials.

How to use this document

This document gives you a number of examples of tcl-code as it is used in Fatal Dimensions. It helps you to set up the basic structure for a varied number of mob programs you would like to make with the same functionality and offers suggestions how to make variations. In this way, non-programmer builders can get started with mob programming in their own areas. Use this document in conjunction with the documentation on the Mob Programs Section of the Fatal Dimensions website, in particular with the TCL-MobProgs Reference Manual. Benedict's Mob Program Patterns is not a course nor a complete manual for learning TCL.


I. WHAT ARE MOB PROGRAMS?

In a MUD, you can often encounter monsters, creatures, and other entities (mobs) that you can have interaction with, or who behave otherwise than just merely die after your onslaught. They can greet you, start a conversation, perform some tasks, or just amble about in their own everyday life with their own everyday problems. The stuff that makes these mobs tick are the mob programs. An example of a mob program is the healer in Midgaard. Here is a little piece:

      proc goodluck {} {
        ! say "Good luck in your adventures, [char short]!!"
      }

      addtrigger 1 leave {goodluck} {trig true}

Let's look at it line by line:

      proc goodluck {} {

This part of the mob program has a proc(edure) called "goodluck". You can give a procedure any name, but there may be no spaces in the name! The part "{} {" you can ignore, but it is important not to forget to include it in your source.

        ! say "Good luck in your adventures, [char short]!!"

Here is the first piece of action code. The "!" forces the mob who runs this program (the healer) to do something, in this case, to say something. The line "say "Good luck in your adventures, [char short]!!" is rather clear, save for the "[char short]" piece. That is a variable which has the name of the player who triggered this program.

      }

This "}" closes the program. Now you have a piece of code that will state some action of the healer. The only thing that needs to be defined is when this code has to run, or in other words, when it is triggered. That is done in the next line:

      addtrigger 1 leave {goodluck} {trig true}

Each trigger added with "addtrigger" gets an identifier (in this case the numeral 1). Then you'll have the kind of trigger on which the program will run. In this case, the program has to run when a player leaves the healer, so the "leave" trigger is used. The piece "{goodluck}" says that if a player leaves, the proc named "goodluck" must be used. Then, there can be conditions when the procedure has to run. In this case, the healer will always wish a player good luck, since the {trig true} states that the trigger is always true. It could also be a random act, for example with a chance of 50%: {trig chance 50}.

This triggering is a key element to remember! A program always is associated with a mob, so that means the mob has to be in the room if you want this program to run. Also, programs are triggered by events, and these most often are initiated by players (a player entering a room, saying something, doing something), since a MUD is a player-oriented system. Look in the TCL-MobProgs Reference Manual for a list of trigger-types that you can use.

I. 1. The .are file

Mob programs are stored together with the room, mob, and equipment description in one file: the .are file. The mob programs are located at the end between two signifiers that indicate start and end of the mobprogram section:

      #MOBPROGS

      #0

New mobprograms must be added in the following way between these signifiers:

      #MOBPROGS
      #11600
      Title in use by horseman~
      proc encounter {} {
        ! say "Make way! Make way!"
      }

      addtrigger 1 greet {encounter} {trig true}
      ~
      #0

The #11600 is the number for the mob program. It is used for the set of procedures that make up the behaviour of the mob. Give the mob program number the same number as the mob (it's vnum when you have built it with "medit"). In this way you will not confuse mob program numbers with the mobs they belong to. The line "Title in use by horseman~" identifies the name of the mob. The phrase "Title in use by" should not be changed, the name of the mob obviously yes. Don't forget the "~" at the end. The rest is basically the same as with the healer example program, only in this case a mob called the horseman shouts "Make way! Make way!" when you enter the same room as it is in. Before the section of mob programs closes, a line with "~" is also used.
Any next programs are added in the same way:

      #MOBPROGS
      #11600
      Title in use by horseman~
      proc encounter {} {
        ! say "Make way! Make way!"
      }

      addtrigger 1 greet {encounter} {trig true}
      ~
      #11601
      Title in use by new mob~
      proc first_routine {} {
      ...
      }

      addtrigger 1 triggertype {first_routine} {trig X}
      ~
      #0

Note that after you have run this edited .are file in JoranMUD, the mobprogs will be ordered numerically on their numbers, so don't panic if at first you cannot find the program at the place you put it in.

I.2. Editing.

I.2.1. Offline editing

Editing mobprograms is best done offline using a good ascii-editor. Take note for JoranMUD to save files in DOS format and not UNIX format. The latter will surely send your JoranMUD offline editor into a crash. ALWAYS make a copy of the old -functional- .are file before you add new programs or change old code. Then, when you are hopelessly muddled in the code you can always fall back on the old code.

I.2.2. Online editing

Once your .are file is checked, checked, and checked again, it will be uploaded and locked in the MUD. Invariably, you will encounter tiny little bugs or have ideas of changing your code. You can edit mobprograms with the line editor that the MUD offers.

  1. Type 'mpedit X' (with X = number of the mob program that you want to edit).
  2. Type 'append' (now line numbers appear).
  3. Now you have the same functionality as in writing notes, etc. Use .h for more information about editing.
  4. Type '@' to close your editing in the line editor.
  5. Type 'done' to close your editing of the mob program.
  6. Type 'asave changed' to make the MUD save your changes.


II. PATTERNS

In this section, a number of mobprogs that are used in Fatal Dimensions are discussed. You can use them as basic patterns for your own mob programs.

II.1. WHEN DYING
II.2. RANDOM ACTIONS IN TIME
II.3. LEVEL-BASED BEHAVIOUR WHEN GREETING
II.3.1. The dauphin
II.3.2. The prime minister
II.4. THE MOB REMEMBERS YOU
II.5. TUTORS AND LECTURERS
II.6. MORE COMPLEX MOB SPEECH INTERACTION
II.7. SEX-BASED BEHAVIOUR
II.8. MOBS AMONG EACH OTHER
II.8.1. The astronomers
II.8.2. The horseman and passerby
II.9. GIVING BRIBES TO MOBS
II.10. MOB DELAY OR ACTIONS AFTER A TIME SPAN
II.11. AGGRESSIVE OR NOT WHEN PLAYER IS HOLDING SOMETHING
II.12. PLAYER HOLDS SOMETHING, AND DELAY
II.13. SPEECH OR ACTS WHEN FIGHTING
II.14. WHEN THE MOB IS HURT TO SOME EXTENT
II.15. A SEQUENCE OF ACTIONS IN TIME
II.16. WHEN PLAYERS LEAVE ROOMS
II.17. MOBS AND YOUR MONEY
II.18. SPECIAL CASES:
II.18.1. The king of Balnibardi
II.18.2. The nasty kid
II.18.3. The fighting sailors
II.18.4. The coward seagulls

II.1. WHEN DYING

Mobs have a standard way of dying in JoranMUD (or in any other MUD for that matter). If you want them to behave different, then you can use the so-called "death" trigger (indicated in bold in the example code below).

#mobprognumber
Title in use by X~
proc die {} {
  ! say "Too soon, my studies have come to an end!"
}

addtrigger 1 death {die} {trig true}
~
Comments: Variations can be made in many ways here. For example, you could have a chance to have the mob not always perform the same when dying. Simply replace {trig true} with {trig chance 33} to have this action happen about once every three times when the mob is killed. You can also let the mob do many other things than just say something. For this, I refer to the TCL-MobProgs Reference Manual.

II.2. RANDOM ACTIONS IN TIME

Much "autonomous" behaviour can be simulated by having the mob do some action once in a while that is consistent with its "character", such as a cook stirring soup, putting salt on food, etc. These actions can be set at random with the random trigger.

#mobprognumber
Title in use by X~
proc act1 {} {
  ! drool
}

proc act2 {} {
  mob act toroom {$n is contemplating everything!} ""
}

proc act3 {} {
  ! say "42!"
  ! beam self
}

addtrigger 1 random {act1} {trig chance 3}
addtrigger 2 random {act2} {trig chance 10}
addtrigger 3 random {act3} {trig chance 5}
~
Comments: the mob of this program has a 3% percent chance of drooling, a 10% chance of contemplating everything, and a 5% chance of saying "42!" and then beaming to itself. The mob act toroom {$n is contemplating everything!} "" is a code that will make the mob do something. toroom means that everyone in the room will see this action. $n means that there the name of the mob is inserted in the message: if the mob is called John, then you would see "John is contemplating everything!" If John is invisible, and a player in the room cannot see invisible things, then it would say "Someone is contemplating everything!" Here is why the $n is very useful: it takes care of things such as visibility, hiding, and blindness, etc. Don't forget the two quotes at the end of the line, or you will get an error message!

II.3. LEVEL-BASED BEHAVIOUR WHEN GREETING

The greet trigger can be used together with level-determination to give mobs behaviour that varies with the level of the player who enters the room. Note in the following example in particular how the if {"condition"} {"what when true"} {"what when false"} structure is used:

II.3.1. The Dauphin

#mobprognumber
proc do_what {} {
  if {[char level] < 50} {
    ! say "My mother has sent me a child!"
    mob act tovict {$n looks at you with death in his eyes.} [char id]
    mob act tonotvict {$n looks at $N menacingly.} [char id]
    ! say "There is no place here for you, [char]..."
    ! kill [char id]
  } {
    if {[char level] < 80} {
      ! say "An assassin, surely sent by my loving father!"
      ! kill [char id]
    } {
      mob act tovict {$n looks at you, full of hatred.} [char id]
      mob act tonotvict {$n looks at $N, eyes filled with hatred.} [char id]
      ! glare [char]
    }
  }
}

addtrigger 1 grall {do_what} {trig true}
~
Comments: There are three options here, when a player walks in the room with this mob in it. If the player is lower than level 50, then it stands no chance fighting the mob (which is level 80). The mob therefore will humiliate the player ("My mother has sent me a child!") and then attack it. If the player is lower than 80 (but higher than 50) then it could pose a threat to the mob. It therefore assumes that the player is an assassin, and then attacks the player. If the player is higher than 80, then the mob is "outnumbered" and resigns to a hateful glare.
The "mob act tovict {$n looks at you with death in his eyes.} [char id]" is a code that is similar to "mob act toroom" yet with the difference that here the action "$n looks at you with death in his eyes." is what the player who triggered the event will see (tovict = to victim), and the line "mob act tonotvict {$n looks at $N, eyes filled with hatred.} [char id]" contains the message that the other players in the room will see (tonotvict = to not victims).
The "[char id]" is necessary to refer to the player who triggers the event. This should not be forgotten in your own version of the code, as well as the proper use and place of all the "{" and "}".
Also note that the trigger here is not greet but grall. This also works when a player enters the room, but it will always work, even if a player is invisible or sneaking.

II.3.2. The prime minister

To make things a little more complicated in this example, here is the prime minister of Laputa. He has more degrees of differentation between levels (which is more of the same as in the example above). However, take note of the char goto 11236 / ! -char look part, and the way in which the program determines if a player is evil or good.

#mobprognumber
Title in use by Prime Minister~
proc do_what {} {
  if {[char level] < 40} {
    ! say "I have no time to play, child!"
    mob act tovict {$n waves disadmittedly with his hand to you.} [char id]
    mob act tonotvict {$n waves disadmittedly at $N.} [char id]
    char goto 11236
    ! -char look
  } {
    if {[char level] < 80} {
      ! say "You have chosen a nice day to die, [char short]."
      mob act tovict {$n gets up from his desk and charges towards you!} [char id]
      mob act tonotvict {$n gets up and attacks $N!} [char id]
      ! kill [char id]
    } {
      if {[char level] < 92} {
        ! say "Let me see if my powers are strong enough to defeat you, [char short]."
        ! kill [char id]
      } {
        if {[char isgood]} {
          ! say "You're a goodie goodie [char short], yet I will not attack."
          mob act tovict {$n knows when he is bested.}
        } {
          if {[char isevil]} {
            ! say "[char short]! My Master of Darkness!"
            ! worship [char id]
          } {
            mob act tovict {$n looks at you indifferently.} [char id]
            mob act tonotvict {$n looks at $N indifferently.} [char id]
          }
        }
      }
    }
  }
}

addtrigger 1 grall {do_what} {trig true}
Comments: Unlike the previous mob, the prime minister will not kill a low-level player (level less than 40). Instead, he forces the player to return to the previous room: "char goto 11236". The line "! -char look" is required to force the player to look just as if he had gone away voluntarily (just try it without this line, it works very strange).
Any other players that are not immortals are attacked by the prime minister, however, his respons is different for level 41-80 and level 81-92 players. This is done to reflect his own level, which is 86.
Players with higher level than 91 are immortals. It is foolish to attack immortals, so here the program checks with "if {[char isgood]} {} {}" the alignment of the immortal and then has three different responses, again dependent on its own alignment, which is evil.

II.4. THE MOB REMEMBERS YOU

With mob remember [char id] and mob forget it is possible to make the mob remember a player, and then to act on it. The following example shows what the Laputa queen does to impolite players... Note in particular that a new trigger type is introduced: the act trigger.

#mobprognumber
proc react1 {} {
  mob forget
  ! say "A civilised person, at last!"
  ! beam [char]
  mob remember [char id]
}

proc react2 {} {
  if {[mob hastarget]} {
    ! blush
    ! say "Oh, you make me blush, [char short]!"
    mob forget
  } {
    ! say "You infidel, [char short]!"
    ! knee [char]
    mob remember [char id]
  }
}

addtrigger 1 act {react1} {trig words bows curtseys greets}
addtrigger 2 act {react2} {trig words looks}
~
Comments: The basic idea is like this. The queen appreciates good manners and a little bit of flirting -if done with some dignitity. If not, then she will show her contempt for the brute in the room. In order to achieve this, the queen-mob must respond to actions by the player: for which we use the act trigger. The part "{trig words bows curtseys greets}" defines for the first routine "{react1}" that the program will run when the player either bows, curtseys, or greets. The mob remembers the player, meaning that it has been polite. Then when the player looks at the queen "{trig words looks}", this is alright when he/she has been polite ("if {[mob hastarget]} {} {}"), but not when the looking has not been preceeded with a bow or something (she gives the player a knee in the groin).

II.5. TUTORS AND LECTURERS

Mobs can not only react to actions by players but also on words spoken by players (however, they cannot react on words spoken by other mobs!). For this, the speech trigger is used:

#mobprognumber
Title in use by tutor~
proc lesson1 {} {
  ! say "It is the adamantite that makes Laputa fly!"
  ! beam
}

proc lesson2 {} {
  ! say "Adamantite is the magic material of which this island is made."
  ! point
}

addtrigger 1 speech {lesson1} {trig words fly floating air}
addtrigger 2 speech {lesson2} {trig words adamantite}
~
Comments: The mechanism is really straightforward. With the "{trig words fly floating air}" part, you define on what words the mob will react. Although it very simple, this kind of behaviour needs to be thought through very carefully to see if the reaction is adequate in most cases.

II.6. MORE COMPLEX MOB SPEECH INTERACTION

Plain reaction to words is simple, yet always predictable and not very interactive with the environment. Here a number of new concepts are introduced, where a mob inquires about the whereabouts of another mob, and depending whether the mob is there will react differently.

#mobprognumber
Title in use by female citizen~
proc missing {} {
  if {[room charhere v:11216]} {
    mob act toroom {$n acts worried about her daughters plans.} ""
    } {
    ! say "Have you seen my little girl?"
    }
}

proc react1 {} {
  if {[room charhere v:11216]} {
    ! say "Yes, I noticed. Thank you."
    } {
    ! say "What has she been up to?"
    ! say "She just wait until I see her again!"
    ! shake
    }
}

proc react2 {} {
  if {[room charhere v:11216]} {
    ! say "Just ignore these strangers, sweetie."
    } {
    ! blush
    }
}

proc react3 {} {
  if {[room charhere v:11216]} {
    ! say "Eh! No funny ideas now!"
    } {
    mob act toroom {$n puts her hands on her sides.} ""
    }
}

addtrigger 1 greet {missing} {trig chance 15}
addtrigger 2 speech {react1} {trig wordsand yes girl}
addtrigger 3 act {react2} {trig words pats admires}
addtrigger 4 act {react3} {trig wordsand stares dreamily}
~
Comments: This program is connected to mob female citizen, who is worried about her daughter, who walks about on Laputa. The first routine, called "missing", starts on a 15% chance when a player enters a room with the female citizen in it. The routine checks whether mob number 11216 (slim girl, see also next example) is also in the room ("{[room charhere v:11216]}"), and if not, then she asks whether the player has seen her little girl. If the slim girl is also in the room, then the female citizen acts worried about the bad ideas her daughter may be having.
When the player responds with the words 'yes' and 'girl' in his answer, then routine "{react1}" checks if the girl is there or not, and this again influences the reaction of the female citizen.
The other two routines, "{react2}" and "{react3}" are made to respond to actions of the slim girl (see next example).

II.7. SEX-BASED BEHAVIOUR

Since players have a sex, and mobs too, it should be only too natural if mobs behave different towards the various genders. Here the slim girl, that is also interacting with the female citizen (see previous example), reacts in different ways on male and female players. Take careful notice of the room randomchar command as well!

#mobprognumber
Title in use by slim girl~
proc act1 {} {
  if {[char sex] == "male"} {
    mob act tovict {$n pats you on your bottom.} [char id]
    mob act tonotvict {$n pats $N on his behind!} [char id]
  } {
    mob act tovict {$n admires your outfit.} [char id]
    mob act tonotvict {$n admires $N's outfit.} [char id]
  }
}

proc react1 {} {
  room randomchar {
    ! stare [char]
    }
  if {[room charhere v:11215]} {
    room echo {The female citizen frowns at her daughter.}
    ! say {Mum! I was only looking!}
    ! pout
    }
}

addtrigger 1 greet {act1} {trig chance 10}
addtrigger 2 random {react1} {trig chance 3}
~
Comments: The "{[char sex] == "male"}" tests for the player's sex and then gives different behaviour for male and female players (in routine "{act1}").
The "room randomchar" is used for the following reason, and a very important reason! When a mob program has to respond or act towards a player, then there must be a player to respond to. Now, in the case of the greet trigger, the player is known (the one who enters the room and is seen by the mob), but in the case of the random trigger, there is no player who triggers the routine (it happens at random, so there is not necessarily a player to refer to). For this purpose the line "room randomchar" selects a player in the room.

II.8. MOBS AMONG EACH OTHER

Mobs have a hard time responding to each other. If mobs are interacting, then this must be programmed by yourself, including the test whether the mobs are there in fact. Take note of {[room count -vnum 11217] > 0} and ! -find 2.astronomer to achieve this. room echo is used for simulation of an action.

II.8.1. The astronomers
#mobprognumber
Title in use by Astronomer~
proc act1 {} {
  if {[room count -vnum 11217] > 0} {
    ! say "I sincerely apologise, my esteemed Collegue, but could you hand me..."
    ! -find 2.astronomer say "By all means, my esteemed Collegue! I am a brute. Here..."
    room echo {The astronomer gives an instrument to his fellow.}
    ! say "Thank you so much!"
  } {
    ! say "I am deserted. An awful duty rests on my shoulders."
  }
}

addtrigger 1 random {act6} {trig chance 4}
~
Comments: The idea of this code is that two Astronomers (vnum 11217) interact. First, there needs to be a check whether there is more than one Astronomer: "if {[room count -vnum 11217] > 0} {} {}". If so, then the first part of the if-then-else structure can be run. The mob that runs the program says "I sincerely apologise, my esteemed Collegue, but could you hand me...". The program then finds the other Astronomer in the room (with the "-find 2.astronomer" switch) and makes it say "By all means, my esteemed Collegue! I am a brute. Here..." The "room echo {The astronomer gives an instrument to his fellow.}" is a message that every player in the room gets to see.

II.8.2. The horseman and passerby

Here a moving mob has different responses for several other mobs it can meet in its journey. We use a different if-then-else structure in this case...

#mobprognumber
Title in use by horseman~
proc enter {} {
  if {[room charhere v:11603]} {
    ! say "That wheel sure looks bad..."
    room echo {The driver looks not happy with that remark.}
    } elseif {[room charhere v:11618]} {
    ! say "Everything all right in Maldonada, I hope?"
    ! tip guard
    ! -find v:11618 say "I have seen better days..."
    } elseif {[room charhere v:11609]} {
    ! say "No messages to deliver to Lord Munodi?"
    ! -find v:11609 say "Nothing today."
    } elseif {[room charhere v:11604]} {
    room echo {The horseman shakes his head at the peasant's sight.}
    ! say "Get a life!"
    } elseif {[room charhere v:11628]} {
    ! say "Out of the way child!"
    } elseif {[room charhere v:11642]} {
    ! say "Them damned seaguls! Cowards bastards is what they are!"
    }
}

addtrigger 2 greet {encounter} {trig true}
~
Comments: The horseman can meet the following other mobs: a driver, the Maldonada guard, Munodi estate guard, the meagre peasant, a child, and a seagul. With "{[room charhere v:11618]}" the test for the mob with that vnum is done. With "! -find v:11609 say "Nothing today."" there is also a response from that particular mob. Just follow the horseman on his trip in Balnibardi...

II.9. GIVING BRIBES TO MOBS

Mobs also like money, and sometimes may reward you when you give them some. New here is mob oload 11237, bribe and {trig bribe 16000}.

#mobprognumber
Title in use by Y~
proc sell_pass {} {
  mob act toroom {$n looks around to see if anyone is paying attention.} ""
  mob oload 11237
  ! give passage [char]
  mob act toroom {$n straightens $s back and raises $s voice.} ""
  ! say "It is a pleasure doing business with you, [char short]!"
  ! chuckle
}

addtrigger 3 bribe {sell_pass} {trig bribe 16000}
~
Comments: When you give this mob more than 16000 silver (160 gold), it will load object with vnum 11237 in its inventory ("mob oload 11237") and then give it to the player who gave the amount of money. The trigger that does this, "bribe", has a lower value under which it will not react (160 gold).

II.10. MOB DELAY OR ACTIONS AFTER A TIME SPAN

Mobs can wait a while and then react further, using the mob delay command. Together with the previous mentioned mob remember this can add additional time-behaviour of the mobs in your area.

#mobprognumber
Title in use stone master~
proc act1 {} {
  ! say "No trepps... press... Tresspassers allowed!"
  mob remember [char id]
  mob delay 5
}

proc act2 {} {
  if {[mob targethere]} {
    ! say "Methinks..."
    ! blush
    mob forget
  }
}

addtrigger 1 greet {act1} {trig chance 35}
addtrigger 2 delay {act2} {trig true}
~
Comments: This one is very simple: when you enter the room, the mob will have a 35% chance of saying "No trepps... press... Tresspassers allowed!", after which he will remember the player and start a delay of 5 seconds. After the 5 seconds (during which the mob may still react on triggers!) the delay-alarm rings, and the delay trigger starts its routine called "{act2}". It tests whether the player is still there, and then makes the remark "Methinks..." (the test is done because the saying does not make sense for a player who has not heard the previous remark).

II.11. AGGRESSIVE OR NOT WHEN PLAYER IS HOLDING SOMETHING

You want to leave a player in peace when he has a permit? Simply test whether he is wearing the object with the vnum of your desire! if {[char iswearing v:11237]} {} {} does the trick here.

#mobprognumber
Title in use by M~
proc do_what {} {
  if {[char iswearing v:11237]} {
    ! nod [char]
  } {
    ! say "You have no right to be here!"
    ! kill [char]
  }
}

addtrigger 1 grall {do_what} {trig true}
~
Comments: Do not use this in combination with a mob that you have defined as aggressive - because in that case it will always attack when the player is in its level range. So a test for attacking or not can only be done with a mob that is not aggressive.

II.12. PLAYER HOLDS SOMETHING, AND DELAY

A bit more complicated is this behaviour, where after a delay the mob tests again for the object it wants to see the player hold, and only then the final decision is made for attacking or not. In this way, you can give a fair warning to a player. Notice the variation if {[char -target iswearing v:11306]} {} {} and [char -target]:

#mobprognumber
Title in use by mob~
proc greeting {} {
  if {[char iswearing v:11306]} {
    ! say "An invitee, [char short]. Welcome!"
    ! bow [char]
  } {
    ! say "You are not welcome on these premises, [char short]."
    ! say "You better leave fast, or I'll take measures."
    mob remember [char id]
    mob delay 5
  }
}

proc do_what {} {
  if {[char -target iswearing v:11306]} {
    mob act tovict {$n looks rather surprised at you.} [char -target]
    mob act tonotvict {$n looks rather surprised at $N.} [char -target]
  } {
    ! say "I told you to leave!"
    ! kill [char -target]
  }
}

addtrigger 1 greet {greeting} {trig true}
addtrigger 2 delay {do_what} {trig true}
~
Comments: The switch "- target" is used to refer to the player that the mob has remembered using the "mob remember [char id]" command. This switch must be used in all references to the player since that is the player who the action is concerned with.

II.13. SPEECH OR ACTS WHEN FIGHTING

Mobs can say stuff during a fight rather than being just silent and stubbornly attacking you. The trigger for this is ...fight:

#mobprognumber
Title in use by Cook~
proc fight1 {} {
  ! say "Baked liver... shushushushushu!"
  ! smack
}

proc fight2 {} {
  ! say "I will make sausages of your intestines!"
  mob act toroom {$n raises his voice and sings.} ""
}

addtrigger 2 fight {fight1} {trig chance 5}
addtrigger 3 fight {fight2} {trig chance 5}
~
Comments: For both reactions during a fight, there is a 5% chance every tick of the system that it will do any of these sentences. Obviously the more variations you can come up with, the more fun the mob can be when you are fighting it.

II.14. WHEN THE MOB IS HURT TO SOME EXTENT

When a mob is hurt to some extent, it can also be triggered to have some action or whatever. The trigger for this neat trick is hpcnt:

#mobprognumber
Title in use by F~
proc ouch {} {
  ! say "I should have never let the major talk me into this!"
}

addtrigger 2 hpcnt {ouch} {trig hpcnt 20}
~
Comments: When the mob has less than 20% of its hit points, it will run the routine called "{ouch}" and complain about its mistake to take this job.

II.15. A SEQUENCE OF ACTIONS IN TIME

Sometimes it pays to stay a while longer with a mob, and so sometimes it pays to award a patient player with a mob that has a lot to do in a time period. For this you can use the timer construction. Look at it carefully and make your own variations. A more complex and mob-interacting example is in the fighting sailors routine.

#mobprognumber
Title in use by Z~
proc behaviour {} {
  if {![mob hastimer]} {
  mob remember [char id]
  mob timer 0
  return
  }

  switch [mob timer] {
    10 { ! wait
       }
    20 { if {[mob targethere]} {
       mob act tovict {$n looks at you discretely.} [char -target]
       mob act tonotvict {$n looks at $N discretely.} [char -target]
         } {
         mob forget
         mob timer cancel
         }
       }
    30 { if {[mob targethere]} {
       mob act tovict {$n friendly wipes some dust from your clothes.} [char -target]
       mob act tonotvict {$n wipes some dust from $N.} [char -target]
         } {
         mob forget
         mob timer cancel
         }
       }
    40 { if {[mob targethere]} {
         mob act tovict {$n tries to ignore your dandruff.} [char -target]
         mob act tonotvict {$n tries to ignore $N's dandruff.} [char -target]
         } {
         mob forget
         mob timer cancel
         }
       }
    50 { mob act toroom {$n wiggles on $s feet.} ""
       }
    60 { mob act toroom {$n puts $s hands on $s back.} ""
       }
    70 { mob act toroom {$n scratches $s nose.} ""
       }
    80 { if {[mob targethere]} {
       mob act tovict {$n checks your pulse to see if you're not dead.} [char -target]
       mob act tonotvict {$n checks $N's pulse.} [char -target]
         } {
         mob forget
         mob timer cancel
         }
       }
    90 { mob act toroom {$n finally gives up!} ""
         mob forget
         mob timer cancel
       }
  }
}

proc stop {} {
  mob forget
  mob timer cancel
}

addtrigger 1 greet {behaviour} {trig true}
addtrigger 2 timer {behaviour} {trig compare <=100}
addtrigger 3 leall {stop} {trig true}
~
Comments: When you preserve the structure as shown here, but make variations on the actions per timing (you can also change the timing of course), this will give your mob behaviour over a period of time as long as a player is there.

II.16. WHEN PLAYERS LEAVE ROOMS

Here is a simple routine that -relative to the sex of the player- makes the mob say something when the player leaves the room the mob is in. The leave trigger takes care of this functionality.

proc goodbye {} {
  if {[char sex] == "male"} {
    mob act toroom {$n shakes his head.} ""
    ! say "This man needs a new haircut..."
    } {
    mob act toroom {$n seems displeased.} ""
    ! say "Never in my life have I seen a woman with such aaawful hair..."
    }
}

addtrigger 2 leave {goodbye} {trig chance 35}
~
Comments: The "mob act toroom {$n shakes his head.} """ is used because the reaction is valid for both player who triggered the event (is leaving) and any other player who is still in the room. This trigger has a chance of 35% of running the routine called "{goodbye}", so that it is not an automatism each time you pass the mob.

II.17. MOBS AND YOUR MONEY

If you don't have enough money, mobs will not be as helpful as in other cases. How to make this all too human behaviour? [char money] gives insight in the size of your wallet (here you see a combination with hpcnt for a healer):

#mobprognumber
Title in use by Q~
proc greeting {} {
  if {[char hpcnt] < 10} {
    if {[char money] < 2500} {
      ! say "Hm, you sure look hurt, but I can't help you."
    } {
      ! say "Hm, you sure look hurt. I can heal you."
    }
  } elseif {[char hpcnt] < 40} {
    if {[char money] < 2500} {
      ! say "Hi [char short], come again when you have some money."
    } {
      ! say "Want me to fix those scratches?"
    }
  } elseif {[char hpcnt] < 90} {
    if {[char money] < 2500} {
      ! say "It is not that I dislike you, but I can't help you."
    } {
      ! say "Ran into some trouble?"
    }
  } elseif {[char hpcnt] < 98} {
    if {[char money] < 2500} {
      ! say "A poor visitor, how nice."
    } {
      ! say "Hi, did you stumble over a pebble?"
    }
  } {
    ! say "A healthy patient, now that is new!"
  }
}

proc react {} {
  if {[char money] < 2500} {
    ! say "Why? Because you are a poor bum!"
  } {
    ! say "Well, why indeed?"
    ! ponder
  }
}

addtrigger 1 greet {greeting} {trig chance 70}
addtrigger 2 speech {react} {trig words why}
~
Comments: The healer first tests on your hpcnt what your status is, and then the amount of money you have. If you are hurt, but cannot afford at least one heal spell (cost 2500 silver or 25 gold), then the healer will decline an offer for help. If your hpcnt is over 98% the healer will remark on your health ("A healthy patient, now that is new!").
The routine called "{react}" responds to a player's speech act which includes 'why', to simulate an answer on his own remarks. So if you have no money, he will respond with "Why? Because you are a poor bum!", and else with a philosophical "Well, why indeed?" (this is for the case the player is not really reacting to a previous remark of the healer).

II.18. SPECIAL CASES:

To conclude, here are four special cases that combine a number of the stuff discussed above to get rather complex examples of mob behaviour and interaction. Study them carefully!

II.18.1. The king of Balnibardi

The King of Balnibardi is always in deep thought about matters of some magnitude. Once in a while he exclaims the solution to a Grave Problem, and the court around him responds (pages, courtier, officer, etc.) each in their own way. However, his outcry is so loud, that it can be heard in surrounding rooms, and the mobs that are there respond in their own characteristic way to the King's outbursts. The King's son, deep in his studies, is disturbed in his concentration; the Dauphin is just anxious for his precious father to die; the queen simply tolerates her husband; and the Prime Minister better understands the solutions than the King himself does.
Note how the dense notation mob around { if {[room charhere v:11204]} { ! -find v:11204 say "Dad, please!"} } makes the King's son (vnum 11204) react to the King of Balnibardi:

#mobprognumber
Title in use by King~
proc cry1 {} {
  ! say "Surely the World is round, like a donut!"
  ! -find v:11212 gasp
  ! -find v:11213 smile
  ! -find v:11211 curtsey
  ! -find v:11203 bow
  mob around { ! say "Surely the World is round, like a donut!" }
  mob around { if {[room charhere v:11204]} { ! -find v:11204 say "Dad, please!"} }
  mob around { if {[room charhere v:11205]} { ! -find v:11205 say "And you'll fall through its hole..." } }
  mob around { if {[room charhere v:11206]} { ! -find v:11206 say "My husband..." } }
  mob around { if {[room charhere v:11214]} { ! -find v:11214 shake } }
}

addtrigger 1 random {cry1} {trig chance 5}
~
Comments: The "! -find v:11212 gasp" makes the mob with vnum 11212 in the same room as the King gasp as a reaction to his outcry.
The text after "mob around" is executed in all rooms surrounding the room in which the King is, and players in those rooms see: "The King of Balnibardi exclaims "Surely the World is round, like a donut!" ".
In the following lines the "mob around" is used to test for the presence of a particular mob ("if {[room charhere v:11204]}"), after which the action for this mob is executed: "{ ! -find v:11204 say "Dad, please!"}". Don't forget the closing "}"!

II.18.2. SPECIAL CASE: The nasty kid

Obviously mobs can a unpleasurable disposition to player. A case here is the kid on the pier in Maldonada who mistakes players in their class:

#mobprognumber
Title in use by boy~
proc ask3 {} {
  room randomchar {
    if {[char class]=="mage"} {
      ! say "A real cleric, wow!"
    } elseif {[char class]=="cleric"} {
      ! say "You must be a master thief!"
    } elseif {[char class]=="warrior"} {
      ! say "Wait till my mom hears I talked with a thief!"
    } elseif {[char class]=="thief"} {
      ! say "Woopie, a mage!"
    }
  }
}

proc no_thief {} {
  mob act tovict {$n throws a second glance at you.} [char id]
  mob act tonotvict {$n throws a second glance at $N.} [char id]
  ! say "Right, and I am a banana-na-na-na!"
}

proc no_thief1 {} {
  mob act toroom {$n checks $s belongings...} ""
  ! say "And still I think you are a thief!"
}

addtrigger 3 random {ask3} {trig chance 6}
addtrigger 16 speech {no_thief} {trig wordsand no thief}
addtrigger 20 speech {no_thief1} {trig wordsand not thief}
~
Comments: The "{[char class]= ="mage"}" tests for the class of the player and compares it with mage (note the double equation mark = =!). When the player is a cleric, the kid will maintain that he is thief, which is rather annoying for a cleric. The player can deny that he is no thief (which will trigger "{no_thief}") or is not a thief (which will trigger "{no_thief1}"), which will prompt the kid into another reaction. This particular mob has many more routines for interaction, just find him and take care of the seagulls...
Two new variables are introduced here as well. In "{$n throws a second glance at $N.}" $N is the name of the player who triggered the program as other players in the room see it. In "{$n checks $s belongings...}", $s fills is "his", "her" or "its" dependent on the gender of the mob. Again, the complete list of these variables can be found in the TCL-MobProgs Reference Manual.

II.18.3. SPECIAL CASE: The fighting sailors

Sure mobs like to fight once in a while, and especially sailors on leave get this anxious feeling in their knuckles after a good drink... Here are the fighting sailors from the Maldonada Inn. They work on the same timer routine as the example above, but now it involves two mobs and the surrender of one mob to the other to stop the fight. Also, since there are two mobs involved, one must test for the presence of the other mob (vnum 11631) to see whether it makes sense to start the sequence at all.

#mobprognumber
Title in use by thin sailor~
proc behaviour {} {
  if {![mob hastimer]} {
  mob timer 0
  return
  }

  switch [mob timer] {
    1  { mob act toroom {$n wakes from $s drinking stupor.} ""
       if {[room charhere v:11631]} {
         mob act toroom {$n has found a victim among the sailors.} ""
       } {
         ! say "I shouldn't drink so much."
         mob timer cancel
         }
       }
    2  { ! glare 'fat sailor'
       ! -find v:11631 blink
       }
    3  { mob act toroom {$n makes a rude gesture to the fat sailor.} ""
       ! say "Hey fatso!"
       ! -find v:11631 say "You wanna me bonk your pretty face, tindlestick?!"
       room echo {The fat sailor moves to the thin sailor.}
       }
    4  { ! say "You're gonna bring your sister for that, eh?"
       ! kill 'fat sailor'
       }
    8  { mob act toroom {$n looks impressed at the fat sailor.} ""
       ! say "Can I get you a drink?"
       ! -find v:11631 say "I sure got thirsty from ouwa clubba hands!"
       }
    9  { room echo {The sailors get a drink and return to their places.}
       }
    10 { mob forget
       mob timer cancel
       }
  }
}

proc stop {} {
  mob forget
  mob timer cancel
}

proc surrender {} {
  ! say "Wow! You sure deal a good blow!"
  ! surrender
}

addtrigger 1 greet {behaviour} {trig true}
addtrigger 2 timer {behaviour} {trig compare <=13}
addtrigger 3 leall {stop} {trig true}
addtrigger 4 hpcnt {surrender} {trig hpcnt 97}
~
#mobprognumber second mob
Title in use by fat sailor~
proc surrender_accept {} {
  ! say "Got some sense now, eh?"
}

addtrigger 1 surr {surrender_accept} {trig true}
~
Comments: The mob who starts the fight wants to stop fighting when his hpcnt reaches below 97%. At that point he surrenders to the mob he is fighting (note that in the same way, it will also surrender to a player when he is fighting a player and gets under 97%!). The other mob therefore, must have a trigger that responds to a surrender, which is surr, and which he will always accept. After that (timer 8 and beyond) the two sailors will make peace, buy each other a drink and and return to their old places.

II.18.4. SPECIAL CASE: The coward seagulls

The last special case holds a nasty surprise for players. This neat little program tests whether there are four or more mobs in the same room with the player and only then they attack!!! Cowards, all of them!

#mobprognumber
Title in use by W~
proc do_what {} {
  if {[room count -vnum 11642] > 2} {
    ! say "Nowww!"
    room randomchar {
      ! kill [char]
    }
  }
}

addtrigger 1 entry {do_what} {trig true}
addtrigger 2 greet {do_what} {trig true}
~
Comments: "{[room count -vnum 11642] > 2}" checks whether there are more than two other mobs with vnum 11642 in the room. Together with the mob itself, this means that there are more than three mobs in the room, and then they pick a player from the room and attack him. The same routine in triggered on entry and greet since a player may enter a room with four or more mobs, or a mob may enter a room with three or more mobs (in both cases, it has to attack). Since the mobs have ASSIST_VNUM is their mob definition, all of them will attack the player.

III. THINGS THAT OFTEN GO WRONG

Very often you'll forget to put a '~' on the right place, or that a mob act needs a '[char id]' at the end of the instruction. When you are sure a code works, but the mob does not respond at all, then you have forgotten in MEDIT to link the mob program with 'Proginstance' to the mob. Another mistake is to forget to type 'asave changed' after your work, in which case the MUD does not save.

If you have used a pattern from this text, and the program does not function, check if you have copied every "{", "}", and whether you have put them in the same format with line-breaks as the examples here. If then still all else fails, contact one of the immortal staff, preferable Benedict first when you are on JoranMUD, and then one of the others.

IV. CONCLUSION

This document presents a few examples of simple and more complex mob programs as they actually run on JoranMUD. I hope they form a good inspiration and starting point for your own programming. Experiment, make variations, invent new and nifty ways of making the mobs do what you want them to do! Use the TCL-MobProgs Reference Manual for your reference on the many other features of TCL. The most important thing is to know beforehand what you want your mobs to do! That is part of the design of an area (see also "How to design an area for JoranMUD" document). After that, the programming becomes a creative search for the realisation of your ideas.

Benedict