Area Building

· Home
· Overview
· Helps
· Mobiles
· Objects
· Rooms
· Resets

· Shops
· Specials
· Time
· Weather
· Scripts
· FAQ


Tutorials

The best way to learn about scripts is by doing. So let's jump in with some scripts!

Gatekeeper

Lets say you have an area where you want to control access to a certain room or part of the area. This can be accomplished by a gatekeeper type mob script. The basic premise of this script is to look at people that come in, and decide if the they can stay or if they get the boot.

So first, we have to descide what script type to use. There are many types that could be made to work, but probably the most applicable here is the @greet script. This script fires on a percentage basis any time someone enters the room with the mob. If we want the script to fire each time, we simply make the percentage 100. Thus our first code will be:

@greet 100~
{

}
~
|

Here we have a basic shell for a greet script. Anything we put inside the main block (between the { and }) will be excecuted 100% of the time someone enters the room.

Now we have to start making some decisons about who we want to let pass, and who we want to exclude. Let's say this mob is a bouncer at a private club for high level fighters. So we want to exclude anyone who isn't a high level fighter.

Let's start with excluding mobs, since we don't want Jawas wandering into our private club. We will use the if statement to test attributes of people as they enter, and make some choices based on what they are. We will also make use of some script functions. Functions are bits of code that let us do more complicated things than just 'does this equal that', see Script Functions for a complete list. So our second pass at the script will look like this:

@greet 100~
{
 if (@ispc($n)) {
  say Welcome player!
 } else {
  say Begone creature!
 }
}
~
|

The first new line contains an if statement. If statements look at what follows in the parens ( ) and determines if everything in there is true or false. If it is true, the following block is executed. If it is false, we skip the next block, and look for any else statements. The @ispc( ) function looks at a character, and determines if it is a player or a mob. The character in this case is $n. $n is the actor variable that is defined by the @greet script and refers to the person who entered the room, the actor. See Script Variables and Attributes for a complete explanation of pre defined variables. So this simple script determines if the person who entered the room, $n, is a mob or a PC, and takes the appropriate steps.

Now we don't want to only exclude mobs, we also want to exclude players who are not 'high level warriors'. Now we will need to look at some attributes of the actor $n. We will want to look at their class, and their level. Let's start with their class. Let's not forget that knights are also warriors, so we need to include them too. Our next additions might look like this.

@greet 100~
{
 if (@ispc($n)) {
  if ($n.class == "warrior" or $n.class == "knight") {
   say Welcome fighter!
  } else {
   say Begone wimp!
  }
 } else {
  say Begone creature!
 }
}
~
|

We have added a nested block inside the first if check. So the mud first looks at $n as they enter and determines if they are a PC or an NPC. If this person is a player, then we evaluate the following block. So next the mud looks to see what class the player is. If the player is either or warrior or a knight, they are ok. Otherwise, they should get the boot.

New things in this version. First, the dot notation for attributes of the actor $n. All mobs, objects, scripts, room, everything has attributes. The class of $n is noted as $n.class. The level of $n would be $n.level etc. See Script Variables and Attributes for a complete explanation of attributes.

We are also doing our first comparison. We are comparing the class of $n ($n.class) to a string. In this case we want the two to be equal, so we use the operator ==. There is a difference here between one equal sign and two. A single equal sign is an assignment. That is 'make the left side equal to the right side'. Two equal signs is a comparison, 'is the left side the same as the right side'. Be sure to always use == when doing comparisons in if statements. You also have to make sure the right side of the comparison is the same 'type' as the left. For example, asking the mud if "$n.class == 4" would not work, because $n.class is a string, and 4 is a number. Strings must be quoted.

Our bouncer is also supposed to be able to reject people who are too low level. First we have to make the decision, what will be a 'high level' person for us? Over level 50? over level 80? For our case lets pick 80. So we will need to compare the level of the person entering, $n.level, to 80. Thus our next additions will look like this:

@greet 100~
{
 if (@ispc($n)) {
  if ($n.class == "warrior" or $n.class == "knight") {
   if ($n.level >= 80) {
    say Welcome fighter!
   } else {
    say Come back when you get some hair on your chest $n.persname
   }
  } else {
   say Begone wimp!
  }
 } else {
  say Begone creature!
 }
}
~
|

So once the mud determines they are a PC, and that they are a warrior or a knight, it then looks at the player's level. Again we are doing a comparison, using a different operator this time. The greater than or equal to operator. This looks at the level of $n, and if it is greater than or equal to 80, we let them in, otherwise we reject them. We are comparing two numbers this time, since $n.level is a number, and so is 80.

New in this part. Note the new rejection line. We are using an attribute of the player in a normal mud command. This statement is parsed by the script portion before being executed by the mud normally. $n.persname is an intelligent attribute. It returns the 'name' of a player, and the 'short description' of a mob. This provides us with a convenient way to talk about someone without knowing if they are a mob or a player. Now we already KNOW they are a player by the time we get here, so we could simply use $n.name. However I like to use $n.persname all the time so I don't use the wrong one in the wrong place. If Bob was the low level warrior who tried to enter, the mob would end up saying "Come back when you get some hair on your chest Bob".

Warning! It is very tempting to put a period after $n.persname. You would expect this to result in the mob saying "Come back when you get some hair on your chest Bob.", but this will not happen. The dot or period is used in scripts as the attribute delimiter. So the script parser will actually try to look for a sub-attribute of $n.persname, and complain when it cannot find any. So what can you do? Well you can use some other punctuation other than a dot, like !. Or you could arrange the statement such that the variable is not at the end of the sentance, ie. "$n.persname, come back when you get some hair on your chest.". This revision will work fine, since the comma following $n.persname is not a dot!

Great! We now have a script that can determine if someone who enters the room is a high level warrior player or not! Unfortunately, the script doesn't actually DO anything about it if they need to be kicked out. So we will have to add some new statements to actually move the player out of the room. There are a few ways we could do this. There is the mob command mptransfer that will magically move the person to anywhere we want. This is useful if you want to move someone to a completely different place in your area, or anywhere on the mud. If we only want to return them to the room they entered from, we could use the mob command mpforce, and force them to leave by an exit. See Script Commands for a compelte list of available mob commands. Let's use mpforce for our script, and asume that the exit we want them to use is west. Our revised script then looks like this.

@greet 100~
{
 if (@ispc($n)) {
  if ($n.class == "warrior" or $n.class == "knight") {
   if ($n.level >= 80) {
    say Welcome fighter!
   } else {
    say Come back when you get some hair on your chest $n.persname
    mpforce $n west
   }
  } else {
   say Begone wimp!
   mpforce $n west
  }
 } else {
  say Begone creature!
  mpforce $n west
 }
}
~
|

Our script is now finished! It correctly identifies players who are high level fighters, and lets them in while forcing all others back the way they came!

 

The Bartender's Brew

Our second tutorial will be much more complex. We will develop several scripts that will all interact to form a complete quest. We will be defining a quest, making several mob scripts to handle the quest's progress, and using room and object scripts as well.

First let's brainstorm about what we want this quest to do. We have a bartender with a problem. He is slowly being ruined by competition from out of town, and he needs help. Thugs are keeping his regular suppliers from coming by with the ingredients required to make his special brew. Our intrepid adventurer will need to help the poor guy out. The basic quest will follow something along these lines:

  • Bring the bartender the required ingredients for his brew
  • Bring the bartender 10 barrels to store his brew in
  • Help fight off the thugs to get his brew to market

First, let's define our quest. A complete description of the quest section format can be found in the Quest Script section.

#1
The bartender's brew.~
Fetch the bartender the ingredients to make his brew. Make sure he has
enough barrels to store his brew in, and help him get the brew to
market.
~
You have failed to help the bartender in time.
~
You think the bartender might need some more help by now.
~
4
Find the bartender hops, barley, yeast and spring water.~
Get the bartender 10 barrels to store his brew in.~
Help the bartender fight his way past street thugs to get his brew to market.~
Complete! You have successfully helped the bartender with his brew!~
1
604800
@init~
{
 declare global $gothops number
 declare global $gotbarley number
 declare global $gotyeast number
 declare global $gotwater number
 declare global $numbarrels number
}
~
|

Here we have the overall structure of our quest laid out. It has a title, some descriptions that show up in various places, 4 states, and some quest variables.

Quest variables are very powerful. They are values that are actually stored on individual players. So different players could have different values for the same variable. This allows for multiple people to be working on the quest at the same time, each at their own pace.

Give the Player a Quest

First off, we need to give the quest to the player. Now we probably don't want to give this quest to just anyone, so let's decide that this will be a fairly low level quest, so let's say that you can't get the quest below 15 (the thugs would be too hard) and you can't get the quest above 30 (it would be too easy) And we'll say anyone who meets these two conditions will be given the quest the first time they come across the Bartender. So we will attach a @greet script to our Bartender mob:

@greet 100~
{
 if (@ispc($n)) {
  if ($n.level < 15) {
   say Oh $n.persname I need some help!
   emote sizes you up.
   sigh
   say I just don't think you're strong enough to be of any use yet...
  } else if ($n.level > 30) {
   say Oh $n.persname, I wont trouble you with my lowly problems.
   sigh
  } else {
   say Oh $n.persname I need some help!
   emote sizes you up.
   say You look just perfect for the job!
   say Some thugs from out of town are trying to put me out of business!
   say They are stopping all my suppliers from getting to me, I don't have the ingredients to make my brew!
   say But you look strong and adventurous, what I need is Barley, Hops, Yeast, and Spring Water.
   say Please get me my ingredients before I'm ruined!
   mpgivequest $n 1
   mpechoat $n You have embarked on the quest for the Bartender's Brew!
  }
 }
}
~
|

So we have a moderately long script here, but it is mostly a bunch of says. The script logic is pretty simple. Are you below level 15? If you are we say something. Are you above 30? If you are we say something different. If you are not, you must be in the right level range, so we plead our case and give you the quest (mpgivequest $n 1). 1 is the vnum for our quest as defined above (#1). Once the player has been given the quest, they can see it listed in the 'quests' command on the mud. This command shows the quest title, and the description of the current state. Since they just started the quest, the state will be 1, so they will see "Find the bartender hops, barley, yeast and spring water." when they do a 'quests'.

Now our fist attempt at this script has one problem. What if they have already been given the quest? fortunately we have two fuctions at our disposal, @hasquest( ) and @runningquest( ). @hasquest returns true if the test player has been given the quest at all. @runningquest returns true if the 'state' of the quest is NOT the last state, since the final state of any quest denotes the quest being complete. We only ever want to use these fuctions if we KNOW we are dealing with a player, so be sure to place these tests after an @ispc( ) check. We want to NOT say anything to people who have either compelted the quest, or are still working on it. So let's add one more if check:

@greet 100~
{
 if (@ispc($n)) {
  if (@hasquest($n, 1)) {

  } else {

   if ($n.level < 15) {
    say Oh $n.persname I need some help!
    emote sizes you up.
    sigh
    say I just don't think you're strong enough to be of any use yet...
   } else if ($n.level > 30) {
    say Oh $n.persname, I wont trouble you with my lowly problems.
    sigh
   } else {
    say Oh $n.persname I need some help!
    emote sizes you up.
    say You look just perfect for the job!
    say Some thugs from out of town are trying to put me out of business!
    say They are stopping all my suppliers from getting to me, I don't have the ingredients to make my brew!
    say But you look strong and adventurous, what I need is Barley, Hops, Yeast, and Spring Water.
    say Please get me my ingredients before I'm ruined!
    mpgivequest $n 1
    mpechoat $n You have embarked on the quest for the Bartender's Brew!
   }
  }
 }
}

Unfortunately, we do not have a negation modifier for scripts, so we cannot say "if (!@hasquest( ))". Thus the odd empty block after the true condition. If the player has the quest, they go to the first block, which does nothing. Otherwise, they drop down to the else block and we make our check.

Collecting Items

So! Now our player has been given the quest, and they go forth to find the ingredients. Now we can do a lot of different things relating to how they get the ingredients. Are some on mobs that they have to kill? Are some available to be bought in shops outside of town? Are some just lying around hidden somewhere? For our quest let's say that there just happens to be another shop outside of town that sells all of the required ingredients! And we just happen to have the vnums of those items, hops are 10, barley is 11, yeast is 12, and the water's vnum is 13. Now we need to make a new script for our bartender mob that triggers when a player gives something to him. So we will use a @give script. Let's jump into our first attempt:

@give all~
{
if (@ispc($n)) {
 if (@hasquest($n, 1) and @runningquest($n, 1)) {
  if ($o.vnum == 10) {
   say Hops! I had almost given up hope of enjoying their sweet aroma again!
   $n.quest.1.gothops = 1;
   mpjunk $o
  } else if ($o.vnum == 11) {
   say Barley! Thank you so much!
   $n.quest.1.gotbarley = 1;
   mpjunk $o
  } else if ($o.vnum == 12) {
   say Yeast! I may yet make a batch of brew!
   $n.quest.1.gotyeast = 1;
   mpjunk $o
  } else if ($o.vnum == 13) {
   say Spring Water! Cool drink of water, such a sweet suprise!
   $n.quest.1.gotwater = 1;
   mpjunk $o
  } else {
   say What am I going to do with that!
   sigh
   give $o $n
   drop $o
  }
 } else {
  say I don't want that from you!
  give $o $n
  drop $o
 }
} else {
 say I don't want that from you!
 give $o $n
 drop $o
}
}
~

First we again test to see if the person giving the bartender something is a mob or a player. If it is a mob, we complain and give the object right back. Note how we also attempt to drop the object after each give. There are a variety of reasons why someone can't be given something, so if we fail to return the item, we'll just drop it. Next we test to see that the person both has this quest, and is still running it. If either of these fails, then the person either hasn't been given the quest, or else has already finished it. In either case, we complain and return the item. If they are a player, and they are still running the quest, then we actually look at what they've given us. $o is a variable that is defined for us by the @give script, and represents the object that triggered the script. So we proceed to test to see what the vnum of the object is. If it matches any of the items we are interested in, we say something useful, and set the appropriate quest variable on the player who gave it to us. We also destroy the object if it matches, otherwise over time the poor bartender's inventory will fill up.

We now have a script that will keep track of what items the player has turned in so far. Next we need some way of determining when they have turned them all in, and advance them to the next part of the quest. Each time he is given something, we should look to see if this person has given them all four ingredients, and if so, do something!

@give all~
{
 if (@ispc($n)) {
  if (@hasquest($n, 1) and @runningquest($n, 1)) {
   if ($o.vnum == 10) {
    say Hops! I had almost given up hope of enjoying their sweet aroma again!
    $n.quest.1.gothops = 1;
    mpjunk $o
   } else if ($o.vnum == 11) {
    say Barley! Thank you so much!
    $n.quest.1.gotbarley = 1;
    mpjunk $o
   } else if ($o.vnum == 12) {
    say Yeast! I may yet make a batch of brew!
    $n.quest.1.gotyeast = 1;
    mpjunk $o
   } else if ($o.vnum == 13) {
    say Spring Water! Cool drink of water, such a sweet suprise!
    $n.quest.1.gotwater = 1;
    mpjunk $o
   } else {
    say What am I going to do with that!
    sigh
    give $o $n
    drop $o
   }
   if ( $n.quest.1.gothops == 1
    and $n.quest.1.gotbarley == 1
    and $n.quest.1.gotyeast == 1
    and $n.quest.1.gotwater == 1 ) {
     say Thank you so much $n.persname! I now have everything I need to make my brew!
     emote shuffle's his feet.
     say There's just one more problem...
     say I don't have enough barrels to store my brew in once I've made it.
     say I need at least ten more barrels to have enough to store a full batch.
     say So... I don't suppose you'd want to help me with that? Of course you do!
     thank $n
     $n.quest.1.state = 2;
   }

  } else {
   say I don't want that from you!
   give $o $n
   drop $o
  }
 } else {
  say I don't want that from you!
  give $o $n
  drop $o
 }
}
~

Here we have added one block of an if statement. Each time someone gives the bartender something, he first looks to see if he's interested in it. Then he looks at the player to see if all four things have been given and if so, then he says some stuff, and advances the player to the next state of the quest. Note the use of $n.quest... The $n.quest attribute of a player contains all the info for all quests the player has. So $n.quest.1... will contain all variables defined in quest 1. We've used 4 of the 5 variables we defined in the quest statement at the top. The 'state' variable is pre-defined, and contains the current state of the quest. This is readable and writeable within scripts. There are also two more pre-defined variables for each quest, stardate and enddate. Right now these can only be read via $n.quest.1.starttime etc. See the Quest Attributes section for more detailed information.

We do have one problem with our addition. If the player were to give the bartender another item after advancing to the next state, he would still give the same message, since the four conditions are still met. So we need to add one more condition to our success check:

@give all~
{
 if (@ispc($n)) {
  if (@hasquest($n, 1) and @runningquest($n, 1)) {
   if ($o.vnum == 10) {
    say Hops! I had almost given up hope of enjoying their sweet aroma again!
    $n.quest.1.gothops = 1;
    mpjunk $o
   } else if ($o.vnum == 11) {
    say Barley! Thank you so much!
    $n.quest.1.gotbarley = 1;
    mpjunk $o
   } else if ($o.vnum == 12) {
    say Yeast! I may yet make a batch of brew!
    $n.quest.1.gotyeast = 1;
    mpjunk $o
   } else if ($o.vnum == 13) {
    say Spring Water! Cool drink of water, such a sweet suprise!
    $n.quest.1.gotwater = 1;
    mpjunk $o
   } else {
    say What am I going to do with that!
    sigh
    give $o $n
    drop $o
   }
   if ( $n.quest.1.gothops == 1
    and $n.quest.1.gotbarley == 1
    and $n.quest.1.gotyeast == 1
    and $n.quest.1.gotwater == 1
    and $n.quest.1.state == 1 ) {
     say Thank you so much $n.persname! I now have everything I need to make my brew!
     emote shuffle's his feet.
     say There's just one more problem...
     say I don't have enough barrels to store my brew in once I've made it.
     say I need at least ten more barrels to have enough to store a full batch.
     say So... I don't suppose you'd want to help me with that? Of course you do!
     thank $n
     $n.quest.1.state = 2;
   }
  } else {
   say I don't want that from you!
   give $o $n
   drop $o
  }
 } else {
  say I don't want that from you!
  give $o $n
  drop $o
 }
}
~

Our one addition simply makes sure that the state of the quest is 1 (the starting state) before advancing them to the next state. Thus once they are advance to state 2, they will no longer meet all the conditons and that block will not be executed.

 

More to come!

 

 

Things to think about

With any mob script that is important to the area or room like this one, it is important to think about things like, will the mob wander around? (need to add a sentinel flag) Can the mob be killed? (make the room safe, or make the mob act nokill) Can the mob SEE $n? (give the mob detect invis, detect hidden, make sure the room is lit, or give the mob holylight)