Complex Mobprogs

 

Contents:

Nested Mobprogs vs Non-Nested Mobprogs

Subroutined Mobprogs

Using the strcat Function

Regular Expressions

 

 [Return to Index]

Nested Mobprogs vs Non-Nested Mobprogs

 

The main purpose of this section of the tutorial is to show you how you should put together more complicated mobprogs, namely, by showing you the difference between nested progs and non-nested complicated progs.

 

A nested prog basically means that a mobprog has nested if statements. Nested means that they are inside of one another. Here is an example:

 

if ispc($n)

  if <(stat($i,”con”),16)

    if ==(inroom($i),10034)

      mpadd mpecho I am working!

    else

      endif

    endif

  else

    endif

  endif

else

  endif

endif

 

This nesting basically checks to see if $n is a pc, and if so, is the con stat of $i less than 16, and if so, is $i in room 10034. Quite frankly, nesting can get ugly quickly. Simple nested if statements usually aren’t a problem, but more complicated ones are hard to debug and can even fail just because they are nested so much. Now consider the following:

 

if and(ispc($n),<(stat($i,”con”),16),==(inroom($i),10034))

  mpadd mpecho I am working!

else

  endif

endif

 

Much cleaner isn’t it? And it does the same thing. Using the and, or, and not functions can really clean up your if checks. You can even combine them like so:

 

if or(=(var-checkone,2),and(=(var-checktwo,3),=(var-checkthree,4)),ispc($n))

This means, if checkone is 2, OR checktwo is 3 AND checkthree is 4, OR ispc($n).

 

Now let’s consider another example. Here is a nested mobprog:

 

if ispc($n)

  if ==(inroom($i),2450)

    mpadd mpecho $I says, ‘Go away!’

  else

    if ==(inroom($i),2451)

      mpadd mpecho $I says, ‘Welcome to my home, $n.’

    else

        if rand(20)

          mpadd kill $n

        else

          mpadd hug $n

        endif

    endif

  endif

else

  if ischarmed($n)

    if rand(50)

      mpadd mppurge $n

    else

      mpadd growl $n

    endif

  else

    mpadd bow $n

  endif

endif

 

This is a heavily nested mobprog! Is it so bad to have nesting? Yes and no. Most of the time, it doesn’t make much difference. However, when you have complicated mobprogs like this, with lots of else and endif all over the place, things can be screwed up fairly easily, and debugging progs tangled up like this can be a mess. But now look at this:

 

if and(isnpc($n),!(ischarmed($n)))

  mpadd bow $n

  break

else

  endif

endif

if and(isnpc($n),ischarmed($n))

  if rand(40)

    mpadd mppurge $n

    break

  else

    mpadd growl $n

    break

  endif

else

  endif

endif

if ==(inroom($i),2450)

  mpadd mpecho $I says, ‘Go away!’

  break

else

  endif

endif

if ==(inroom($i),2451)

  mpadd mpecho $I says, ‘Welcome to my home, $n.’

  break

else

  endif

endif

if rand(20)

  mpadd kill $n

else

  mpadd hug $n

endif

 

At first glance you may realize that this mobprog is actually longer than the first, and that it, too, contains a little bit of nesting. The fact of the matter is however, that this second mobprog has a much greater chance of actually working, and you can see step by step how everything is behaving. Using the break command can really help in these situations. Having a little nesting is okay, having a lot can be tedious and may not work. If you look at everything the right way, and know how to use all your operators and functions, you can take even the most difficult prog and make it not be nested. In truth, I could have easily not nested the if rand(40) in the above example, but I wanted to show you that a little bit of nesting doesn’t usually hurt anything. Ultimately it is up to you whether or not you want to nest your if statements, but I would encourage you to avoid it. Once you begin writing larger and larger mobprogs, not having to untangle a nested mess can really be of great value.

 

 [Return to Index]

Subroutined Mobprogs

 

If you do find your mobprogs are getting really big and tedious, consider creating ‘subroutines’ of sorts. You can have a mob trigger itself in a number of ways. A good example is a casino mob. It has subroutines for collecting a bet, starting a game, allowing a player to take a turn, having the dealer take a turn, and so on, and then ending the game. Each of these is a separate mobprog triggered by the mob. The mob may perform some social that triggers itself, use a secret password kind of speech_prog or act_prog somewhere else to trigger itself, etc. This is another good way to untangle hairy mobprogs or mobprogs that (gasp!) are getting a little too close to the 16000 character limit per mobprog. It also is a good way to create recursion. Here is an example of this in action:

 

>speech_prog p Go.~

  $[counter] := 3

  mpat 99999 say Time to get going here.

~

>speech_prog p Time to get going here.~

  if !=($n,$i)

    break

  else

    endif

  endif

  if <($[counter],1)

    mpat 99999 say Time for the next stage.

    break

  else

    endif

  endif

  if ==($[counter],1)

    mpsetvar me firstroll number(1,6)

    $[counter] := -($[counter],1)

    mpat 99999 say Time to get going here.

    break

  else

    endif

  endif

  if ==($[counter],2)

    mpsetvar me secondroll number(1,6)

    $[counter] := -($[counter],1)

    mpat 99999 say Time to get going here.

    break

  else

    endif

  endif

  if ==($[counter],3)

    mpsetvar me thirdroll number(1,6)

    $[counter] := -($[counter],1)

    mpat 99999 say Time to get going here.

    break

  else

    endif

  endif

~

>speech_prog p Time for the next stage.~

  if !=($n,$i)

    break

  else

    endif

  endif

  mpadd mpecho $I says, ‘Well, the lucky numbers for today are $[firstroll] and $[secondroll] and $[thirdroll].’

~

 

The above is an example of creating very primitive recursion. The mob triggers itself multiple times until it has fulfilled what is required of it and then moves on. Notice the use of the if !=($n,$i) check. This checks to make sure that the var-actor triggering the mobprog is the same as var-me, the mob itself. That way only the mob can trigger itself. You’ll also notice the mob does its says in a room different from where it is sitting. This is so no one in the room with the mob can see what it’s doing. A final note – a spell like silence would cause this mobprog to totally break. As a mobprog writer, you must be able to anticipate possible exploits and problems that can occur. Your perfect prog may very well be rendered useless  by a player, intentionally or not.

 

 [Return to Index]

Using the strcat Function

 

The strcat is a powerful function capable of doing a number of useful things for an ambitious mobprog designer. Here I will try to explain the different possible uses for this function and how to use it correctly. The strcat and strrem function are detailed in the Mobprog Functions section of the tutorial. Basically what strcat does is store multiple strings inside of a single variable. Strrem can be used to remove the strings in question, but currently is disabled, so I won’t go into much detail about it.

 

The first thing strcat can be used to do is create a list of strings. The reason you might want to do this is to store a list of players that are involved in the mobprog of the mob, or to store a list of object keywords that have been brought to the mob, for instance. Keep in mind that the max string length of a variable is 256 characters, so you have to have some way of not exceeding that length by limiting the number of strings stored in it, etc. There are a few ways to handle that. The poker mobprogs for example, limit the players in a game to six, and the six player names are stored inside a variable which serves as the list of active players in the current game. Since only six strings maximum will be stored in the variable, there is no danger of exceeding 256 characters. It’s a good idea to start by using a load_prog to create an “empty” variable to be later strcat with strings:

 

>load_prog 100~
  $[playerlist] := “”

~

 

The above creates the variable playerlist and sets its value to “”, type string. This makes us a blank slate to work with, so to speak. Okay, so let’s say we want to add a player name to our player list. Remember that only strings can be added to a variable using the strcat function. So we’ll need to create a string variable from the character variable of the player:

 

mpsetvar me tempplayer “$n”

 

This makes a string variable that contains the name of $n – the person triggering the mobprog. Now we can use strcat:

 

$[playerlist] := strcat($[playerlist],$[tempplayer])

 

The above will take the variable playerlist, and set its string value to its old value plus the string contained in tempplayer. From our “empty” variable, the new value of playerlist would be “” + “Bobzilla” = “Bobzilla”. Now of course, we have a problem if we use the above over and over again if something is already held in the variable. After all, let’s say we want to add the string “Killa” to playerlist. We’d end up with “Bobzilla” + “Killa” = “Bobzillakilla”, which while awesome, is not really useful. So, we can nest two strcat functions like so:

 

$[playerlist] := strcat(strcat($[playerlist],$[tempplayer])," : ")

 

Now we will have a separator - “ : “ - in place to keep the names all separate. Now “Bobzilla” + “ : “ + “Killa” = “Bobzilla : Killa” and we can differentiate between all the names stored in our playerlist variable. So, how do we check for a name in our playerlist? Once again, we will need to create a string variable to compare it to. So let’s say Bobzilla walks up and triggers a mobprog in which we want to make sure he is on the list before we continue:

 

mpsetvar me tempplayer “$n”

if !/(var-tempplayer,var-playerlist)

  mpadd mpecho $I says, ‘You aren’t on the list!”

  break

else

  endif

endif

mpadd mpecho $I says, ‘Okay, $n. I guess you’re on the list.’

 

That’s pretty much all there is to it when creating lists using strcat. Keep in mind you can only nest two strcat functions in one line. Removing names from your list is not really possible at the moment, but once strrem is operational will be simple enough to handle. Until then, you can take steps to recreate your whole list without the string in question using other progs. For example, in poker, when someone folds, they need to be taken off the list. So any time a player folds, a mobprog is triggered that checks the current state of each player in the game, and completely remakes the playerlist excluding anyone who has a score of 0 – anyone who has folded.

 

Another use for strcat is when you want to have multiple variables with different values, but also want to have them put together into a single variable. Alternatively, you might want to piece together a string from an assortment of other strings. Let’s say you want to have a setup where a player needs to figure out the combination to open a vault. This number is a five digit number comprised of five separate values which must be collected from various spots throughout the zone. First, you set up the initial random numbers and “empty” combo variable at load up:

 

>load_prog 100~

  $[@1_num] := number(1,9)

  $[@2_num] := number(1,9)

  $[@3_num] := number(1,9)

  $[@4_num] := number(1,9)

  $[@5_num] := number(1,9)

  $[combo] := “”

~

 

I’ve made the above variables shared across the zone. That way, if the player needs to collect the five individual numbers from five different mobs, they all know what the numbers are. Now let’s add to the load_prog the strcat to create the combo – and remember, we have to turn those integer variables into strings first:

 

>load_prog 100~

  $[@1_num] := number(1,9)

  $[@2_num] := number(1,9)

  $[@3_num] := number(1,9)

  $[@4_num] := number(1,9)

  $[@5_num] := number(1,9)

  $[combo] := “”

  mpsetvar me combo_1_num “$[@1_num]”

  mpsetvar me combo_2_num “$[@2_num]”

  mpsetvar me combo_3_num “$[@3_num]”

  mpsetvar me combo_4_num “$[@4_num]”

  mpsetvar me combo_5_num “$[@5_num]”

  mpsetvar me combo strcat(“$[combo]”,”$[combo_1_num]”)

  mpsetvar me combo strcat(“$[combo]”,”$[combo_2_num]”)

  mpsetvar me combo strcat(“$[combo]”,”$[combo_3_num]”)

  mpsetvar me combo strcat(“$[combo]”,”$[combo_4_num]”)

  mpsetvar me combo strcat(“$[combo]”,”$[combo_5_num]”)

~

 

So now our variable combo should have a string that is made up of the five randomly generated numbers. If we wanted this string to be usable as a number (for example, to compare against another number for some reason), we would use the atoi function at the end of the load_prog:

 

mpsetvar me combination atoi($[combo])

 

The above load_prog in the end could produce the following variable list for the mob:

 

$[@1_num] = "2", type integer

$[@2_num] = "6", type integer

$[@3_num] = "7", type integer

$[@4_num] = "5", type integer

$[@5_num] = "2", type integer

$[combo_1_num] = "2", type string

$[combo_2_num] = "6", type string

$[combo_3_num] = "7", type string

$[combo_4_num] = "5", type string

$[combo_5_num] = "2", type string

$[combo] = "26752", type string

$[combination] = "26752", type integer

 

You’ll notice that different variable names were used for combo and combination, and the individual integer variables and the individual string variables. Once a variable contains a certain type of variable in it, it is generally best not to try and change that type. So if variable combo contains a string type variable, having it get set to an integer type variable is not usually a good idea. Most of the time it will mess up and your mobprog will not do what it’s supposed to do.

 

The final use of strcat I will discuss is the naming of variables using other variables. This is a powerful tool and can be used to create variables named after players, variable names put together from a variety of existing variable names, and more. The uses for this may seem limited, but with it a zone designer can create a database of player information, or shorten mobprogs considerably. Let’s create a variable named after a var-actor, in this case, Bobzilla.

 

mpsetvar me temp "$n"

$[var_name] := strcat($[temp], "_account")

mpsetvar me $[var_name] 10000

 

This would create a variable called Bobzilla_account and set its value to 10000. Now Bobzilla has his own little tracked variable of some kind. It could be a credit for use in some sort of game in your zone, or tracking a number of killed mobs in a quest, or a high score for completing some kind of challenge in a certain amount of time. Let’s take a look at how you might do the last of these. First you’d need to set the initial time the person started the challenge at and use their name:

 

mpsetvar me temp "$n"

$[var_name] := strcat($[temp], "_starttimer")

mpsetvar me $[var_name] time

 

Now Bobzilla_starttimer contains the value time, the time at which the variable was set. So Bobzilla finishes his task, and now we are faced with checking his variable to see how long it’s taken him to do it. So we’re going to have to reconstruct his specific variable and check it. Doing this requires a bit of work, and this is how it will look:

 

mpsetvar me temp "$n"

$[tempvar] := strcat(strcat("$[",strcat($[temp],”_starttimer”)),"]")

mpsetvar me tempstarttimer $[tempvar]

$[time_now] := time

mpsetvar me time_completed –($[timenow],$[tempstarttimer])

 

The variable time_completed is now equal to the number of seconds it has taken Bobzilla to finish his task (the current time minus the start time). Let’s now add a check to set a permanent record of Bobzilla’s accomplishments:

 

mpsetvar me temp "$n"

$[tempvar] := strcat(strcat("$[",strcat($[temp],”_record”)),"]")

mpsetvar me temprecord $[tempvar]

if ==($[temprecord],0)

  mpsetvar me $[tempvar] time_completed

  break

else

  endif

endif

if <($[time_completed],$[temprecord])

  mpsetvar me $[tempvar] time_completed

  break

else

  endif

endif

 

So the above compares time_completed to Bobzilla_record, and if it’s less than the record, makes time_completed the new Bobzilla_record. If no existing Bobzilla_record is there, it will set it to time_completed. Using some operators we can also format the time so it looks nice for displaying:

 

mpsetvar me temp "$n"

$[tempvar] := strcat(strcat("$[",strcat($[temp],”_record”)),"]")

mpsetvar me temprecord $[tempvar]

mpsetvar me minutes /($[temprecord],60)

mpsetvar me seconds %($[temprecord],60)

mpadd mpecho $I says, ‘Your personal best is $[minutes] minutes and $[seconds] seconds.’

 

That’s about it for strcat. There are many things you can do with it, but the syntax can be a bit daunting. My advice to you is to copy after the examples provided here as best you can, and of course never hesitate to ask for help from a seasoned mobprog veteran, or simply experiment and see if what you’ve made works or not.

 

 [Return to Index]

Regular Expressions

 

Regular expressions make use of the act_prog r and speech_prog r trigger types. Using regular expressions, you can accept and/or capture variable arguments in a speech_prog or act_prog. This is an invaluable tool for mobprog writers, as they allow for many great possibilities. Here are a few examples of it in action, just to show you what they look like:

 

>speech_prog r ^enter +([^ ]+)$~

  $[enterit] := $1

  enter $[enterit]

~

>act_prog r ([^ ]+) +leaves +(east|west|south|north|up|down). *~

  mpsetvar me myvictim %1

  mpsetvar me gothisway %2

  $[gothisway]

  kill $[myvictim]

~

>speech_prog r ^Player +(1|2|3|4|5|6) +card +one +has +([^ ]+) +: +([^ ]+) +: +([^ ]+) +: +([^ ]+) +values. *~

  if !=($n,$i)

    break

  else

    endif

  endif

  $[playval] := $1

  mpsetvar me tempvre "$[playval]"

  $[tempval] := strcat(strcat("player_",$[tempvre]),"_card_1")

  $[tempvle] := strcat($[tempval],"_value")

  mpsetvar me $[tempvle] $2

  $[suitval] := $3

  $[tempsuit] := strcat($[tempval],"_suit")

  mpsetvar me $[tempsuit] "$[suitval]"

  $[nameval] := $4

  $[tempname] := strcat($[tempval],"_name")

  mpsetvar me $[tempname] "$[nameval]"

  $[shortval] := MakeFirstLetterUpper($5)

  $[tempshort] := strcat($[tempval],"_short")

  mpsetvar me $[tempshort] "$[shortval]"

  mpsetvar me tempvae "$[cardpool]"

  $[tempvalcard] := strcat(strcat("$[",strcat("varcard_",$[tempvae])),"]")

  mpsetvar me var_name $[tempvalcard]

  $[tempvaecard] := strcat("varcard_",$[temp])

  mpsetvar me $[tempvaecard] var-var_name

  mpsetvar me cardpool -(var-cardpool,1)

  mpat 18796 say Get player second card.

~

 

These may seem a little confusing, but the syntax is fairly straightforward. The following table lists what each symbol means when used in a regular expression:

 

^          Place at the beginning of a regular expression, first word MUST be first.

$          Place at the end of a regular expression, last word MUST be last.

?          The preceding item is optional and matched at most once.

*           The preceding item will be matched zero or more times.

+          The preceding item will be matched one or more times.

()          Captures the item in the form of %1, %2, etc.

|           Creates a list of options for the argument; in other words – OR.

.           Match any single character.

            [ ]         Match a space or one or more characters.

            [^ ]       Match one or more non-space characters (a word).

 

Let’s take apart a simple regular expression piece by piece:

 

>speech_prog r ^Deal +([^ ]+) +(1|2|3|4) +cards$~

Match the word “Deal” at the beginning of the sentence.

 

>speech_prog r ^Deal +([^ ]+) +(1|2|3|4) +cards$~

Match one or more spaces.

 

>speech_prog r ^Deal +([^ ]+) +(1|2|3|4) +cards$~

Match one or more non-space characters (a word).

 

>speech_prog r ^Deal +([^ ]+) +(1|2|3|4) +cards$~

Capture the word into %1.

 

>speech_prog r ^Deal +([^ ]+) +(1|2|3|4) +cards$~

Match one or more spaces.

 

>speech_prog r ^Deal +([^ ]+) +(1|2|3|4) +cards$~

Match either 1 2 3 or 4.

 

>speech_prog r ^Deal +([^ ]+) +(1|2|3|4) +cards$~

Capture the word into %2.

 

>speech_prog r ^Deal +([^ ]+) +(1|2|3|4) +cards$~

Match one or more spaces.

 

>speech_prog r ^Deal +([^ ]+) +(1|2|3|4) +cards$~

Match the word “cards”.

 

>speech_prog r ^Deal +([^ ]+) +(1|2|3|4) +cards$~

Force the word “cards” to have to end the sentence.

 

>speech_prog r ^Deal +([^ ]+) +(1|2|3|4) +cards$~

Ends the mobprog trigger – just like in every other mobprog trigger type.

 

The arguments that are captured are stored in temporary values marked %1 %2 and so on in order of their creation. Up to nineteen of these temporary variables can be captured at once. From there, it is a simple task of applying those variables to permanent variables (ie. $[permvar] := %1) or comparing the temporary variables immediately (ie. if ==(%1,0)).

 

There are actually even more syntax symbols you can use in regular expressions, but for most people these are adequate. Regular expressions are a little tricky to use at first, but it is easy to make a really simple one. Here’s another example:

 

>speech_prog r ^drop +([^ ]+) *~

  $[dropit] := $1

  drop $[dropit]

~

 

The above first matches the word ‘drop’ at the beginning of the sentence (by using the ^ symbol before the word ‘drop’), then matches a space (by using ‘ +’ where + matches the preceding character one or more times), then matches and captures one or more non-space characters to %1 – a word that is (by using ([^ ]+) to match and capture), and then matches a space zero or more times (using ‘ *’ where * matches the preceding character zero or more times), and thus ending what the trigger cares about. The mobprog then sets a variable named ‘dropit’ to contain the argument stored in %1. Finally, the mob takes an action to ‘drop $[dropit]’ and therefore will drop whatever it has been told to drop.

 

My advice to those who want to play around with regular expressions is to copy the examples given as much as possible. There are a lot of ways to handle the syntaxes of regular expressions, and the above examples are certainly not the only way to set them up, but they do work. So copying the way they are written will likely create a trigger that works as well.



A MUD based on Robert Jordan's Wheel of Time series. With roleplaying encouraged through guilds, clans, clanwars, holywars and throne wars. Experience the Wheel of Time world in a whole new way: in an Age ravaged by the Last Battle. The time lace has been broken, the barrier between dream and reality shattered. Weaves. Clans. Crafting. Huge World. Free Online Role Playing Game or commonly called RPG. The most unique Free Online RPG set in the Wheel of Time world.




Wheel of Time Age of Chaos Home Page - Wheel of Time Game - Wheel of Time Forum - Wheel of Time News - Wheel of Time About