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