>
Original Author: Andrew Cowan (Icculus)
Please use this tutorial only as a guide to writing your first Dil quest, it hopefully explains enough so that your next will not need it. If you have any comments please email wrh2@po.cwru.edu.
If you have been playing Valhalla for a while you may recognize the following definition as the eagle who was located in Asgaard near the cloud stairs. Here is the full code for this quest (Asgaard Quest), which you'll notice is part of the eagle mobile definition.
Comments will be done in C style ( /* to indicate the beginning of a multiline comment and */ to indicate the end ).
eagle
names {"eagle", "giant", "giant eagle"}
title "a giant eagle"
descr "A giant eagle is here."
extra {}
"One of the few remaining in Asgaard, Odin enjoys watching them enjoy
their freedom. "
alignment -250
level 95
money 2 GOLD_PIECE
sex SEX_NEUTRALS
weight 150
height 210
race RACE_EAGLE
NATURAL_DEF(WPN_CLAW, ARM_HLEATHER)
MSET_ABILITY(35,30,20,10,5,0,0,0)
MSET_WEAPON(2,2,2,2,35,2)
MSET_SPELL(4,4,4,5,5,5,5,5,6,6,6)
position POSITION_STANDING
default POSITION_STANDING
/* Dil to accept new members to the quest, and check if all
of the items were given to the eagle. If they were, the eagle
gives the char the proper quest reward. -Icc */
/* These two C pre-processor directives define two quest
flags for use with the Dil program. */
#define ASGAARD_ONGOING "Asgaard Quest Ongoing"
#define ASGAARD_COMPLETE "Asgaard Quest Complete"
/* During runtime, the ASGAARD_ values are substituted in for
the appropriate defined strings. This is used mainly to aid
the dil coder in a more stable way of referring to these values.
C and C++ coders will recognize the benefit of such definitions! */
/* We start every Dil pogram with the 'dilbegin' label, and end every
Dil program with a 'dilend' label. Though variables are not always
necessary, there are for every quest you can think up, so be sure
to have this section. To make the variable declaration section, just
have the 'var' label and follow it by a list of variables and their
type. This is explained in dil.doc so we shall move on. */
dilbegin
var
pcname : string;
item : unitptr;
pc : unitptr;
i : integer;
exdp : extraptr;
/* The actual Dil program always follows the 'code' and '{' lines.
The heartbeat sets up the delay for such statements that use
SFB_TICK, i.e. wait(SFB_TICK, whatever might be here) and walkto
statements. If you want a mobile to 'walkto' a specific target
you can use this command and each step will be timed based on the
heartbeat value. */
code
{
:init:
heartbeat := PULSE_SEC*5;
on_activation((self.position < POSITION_SITTING) or
(self.position == POSITION_FIGHTING), skip);
/* The :init: and :start: labels are just used for references
so they can actually be named anything. What is contained
in them is quite important however. The on_activation()
call is a way to make sure our eagle is actually in a
position to help before we begin the quest. If he is not
then the dil is skipped via the 'skip' command. There is
a bit more on this towards the end of the tutorial. */
/* The contents of the start label is very important. We use this
label as a reference so when we want to come back to the beginning
of the program we can just use goto start. We generally provide
a means for the program to return here on completion of the dil
or if something went wrong during execution. The wait() statement
is probably the single most important aspect of dil there is!
The dil does nothing until the conditions specified in the wait
statement are met. The SFB_CMD is just one option, it tells the
dil to wait for any command given, then you can specify more
like what command was given, and by who! */
/* The activator is usually either an npc or pc which activates the
dil. It is treated as a structure (unitptr) and you can reference
many things about it, like activator.hp (hitpoints) or
activator.quests (what quests it has done) or activator.type
which is either UNIT_ST_PC or UNIT_ST_NPC for these types.
(NOTE) For advanced dil coding using SFB_MSG, the activator can
also be a room or object. */
/* Not sure I have to clear this up but for anyone wondering what
'mobile' means, it is just another name for the monsters (non-
playing characters) on the MUD. The term mobile comes specifically
from those that wander around in a zone. */
/* Ok, this statement following waits for any command, it makes
sure the command was given by a pc (playing character) and
it will accept and proceed with the rest of the program if
the command is not the 'give' command*/
:start:
wait(SFB_CMD,(activator.type == UNIT_ST_PC) and (not command(CMD_GIVE)));
/* Ok, so we have a pc activator which has given some command and
it is not the 'give' command. Now we want to make sure the
activator has not completed this quest already. */
if (ASGAARD_COMPLETE in activator.quests)
goto waitawhile;
/* Great! So the activator has not, lets continue. We want to
store activator in a memory locaion so if we modify it in
any way, it gets remembered later! We assign the unitptr
variable 'pc' to the activator structure and secure it. This
means it has protected memory and we can now modify it however
we want. We no longer refer to the activator, we now use pc
instead, it is the same person! */
/* Note added by Papi -
What secure does is: Whenever the DIL program is activated (tick,
command, combat, etc) or the DIL program executes a command that could
mean the possible destruction of another unit, it scans through all of
it's secured variables.If any of the secured unitptr's are removed from
the 'local environment' during this search, the variable is set to NULL
and execution continues at 'label' specified in the secure. The local
environment is the environment which the unit is currently in
( transparency counts too ). */
pc := activator;
secure(pc, lostpc);
/* Ok, here is what the eagle will do. It will explain to
pc what it wants now! The structure 'self' is how we
refer to the thing the Dil is defined on! For us, it is
the eagle. If we were defining this dil on an object or
room it would mean that instead. We can make the eagle
do what we want now, and try to get the pc to join our
quest. */
if (not (ASGAARD_ONGOING in pc.quests))
{
pause;
exec("say You seek the Sky Father Odin do you?", self);
pause;
exec("say The only way in requires my aid, I'm afraid!", self);
pause;
exec("say You see, you seek the key, and only I can give it to " +
"thee.", self);
pause;
exec("laugh", self);
pause;
exec("say and don't think about my death, a crazy idea like" +
" that is as foul as your breath!", self);
pause;
exec("say Now, this is what you must bring me. Upon success " +
"I will get you the key.", self);
pause;
exec("say To get the key, bring these things three.", self);
pause;
exec("say In the run down village of Ratswarf is an object " +
"crafted in the image of a skull.", self);
pause;
exec("say This you shall bring me. But that's not all!", self);
pause;
exec("say In a small room near the gates of hell, a blood red" +
"trident can be found. This you shall also bring..", self);
pause;
exec("say Nearby where the ninjas practice, a sword made for " +
"guards can be found. Bring this to me as well...", self);
pause;
exec("say When I have all three, you'll have the key.",self);
pause;
exec("say Is such a key worth all of the bloodshed to " +
"surely follow? Please nod if you think so.", self);
/* The loop will wait for the pc to enter a command
either a shake of the head to say 'no quest for me'
or nod to say 'ok', sign me up and give me the quest
flag that says I am ready to try! The loop will prompt
for a decision 5 times, then quit if not met */
i := 0;
while (i < 5){
wait(SFB_CMD, activator #= pc);
if (command(CMD_NOD))
{
goto quest_accepted; /* Goto the label if accepted */
}
else if (command(CMD_SAY))
{
/* This specifies what happens if the pc says something */
pause;
exec("say Nod if you accept my challenge, shake your head" +
"otherwise.", self);
}
else if (command(CMD_SHAKE))
/* Ok,the pc didn't want the quest, we tried and failed */
goto no_quest;
i := i+1;
}
/* Here is what happens when the pc shakes its head, we
basically say ok, sorry, and want to undo our variable
secure. Always remember to unsecure the secured variable
before returning to :start:. Otherwise, if the player
leaves the room, quits or gets killed while the dil is
still executing, the execution will continue where told
in the secure statement. */
:no_quest:
pause;
exec("say So be it! It is most unlikely you will ever meet the " +
"Sky Father.", self);
unsecure(pc);
goto waitawhile;
/* Ok, quest accepted, what do we do? Well, we add the quest
flag to the pc and unsecure it, then return to star. This
part of the dil is done. */
:quest_accepted:
pause;
exec("say Begone then, do not return until you have all you " +
"need!", self);
addextra(pc.quests, {ASGAARD_ONGOING}, "");
unsecure(pc);
goto start;
}
/* Here is what happens if the pc has already joined the quest. We
will check the extra flags on the quest to see if the other Dil
(the one coming up after this) has received the items. These
names you see (skull key,...) were defined in the next dil,
as a way of alerting this one that the items were given. If
all 3 items were given, we replace the ongoing flag with the
complete flag, this pc has completed it and shall now receive
the reward. */
else /* ASGAARD_ONGOING in pc.quests! */
{
exdp := ASGAARD_ONGOING in pc.quests;
if (("skull key" in exdp.names) and ("trident" in exdp.names) and
("imperial sword" in exdp.names))
{
pause;
subextra(pc.quests, ASGAARD_ONGOING);
addextra(pc.quests, {ASGAARD_COMPLETE}, "");
exec("emote sets the skull key down before him.", self);
pause;
exec("emote magically transforms the skull key into a " +
"cloud-gray key.", self);
pause;
exec("emote gives you the cloud-gray key.", self);
item := load("sky_key@asgaard"); /* Loads the item */
link(item, pc); /* Gives the item to the pc. NOTE- The link
will not work unless pc is secured! */
experience(90000, pc); /* what can I say, I am extra nice! */
/* ^^^^^^^^^^^^^^^^^^^^^
Note added by Papi -
Be very careful when using this command. It can easily raise a 3rd
level to 200th level if in a loop. Always remember to test these
constructs carefully BEFORE they become available to players. */
exec("say This will gain you entrance into the planes " +
"of sky.", self); pause;
exec("pat "+pc.name, self); pause;
exec("say I also have for you a gift!", self); pause;
exec("emote gives you a Stick of Bridging.", self);
item := load("bridge_stick@asgaard");
link(item, pc);
pause;
exec("say That will help in your efforts to get around " +
"in this world.", self); pause;
exec("wave", self);
}
}
/* Ok, we are done, lets unsecure the pc and return to start so
that we may get a new quest member. */
unsecure(pc);
goto start;
/* Just a label to go to if we want to skip the main body of
code, like if the char has completed this quest already! If
we didn't do this, the eagle would immediately check for a new
activator to tell the whole story to. */
:waitawhile:
heartbeat := PULSE_SEC*60;
pause;
goto init;
/* Ok, you noticed that we used secure(pc, lost_pc) at the top
of the progam. What this means is this: We secure our pc
variable, and at any time if the eagle and the pc get
seperated, this is what the Dil jumps to. So if the pc
leaves in the middle, we come here, and then return to start
to wait for another pc. */
:lostpc:
exec("say Too much to handle I suppose.", self);
pause;
exec("shrug", self);
goto start;
/* This does not get used at all! I used to use it if the pc
attacked the eagle during the dil, but removed the call to it.
Pay no heed, although I am anal retentive and leave it in
anyway. If I were going to use it, this is how. All the way
at the top of the dil, right after the :init: label I would
put this call.
on_combat(self.poisiton == POSITION_FIGHTING, ohno_combat);
Then when the quest host (self) is attacked, the :ohno_combat:
routine is executed. I could also use 'skip' here as I did at
the top of the program for our on_activation() call. If I also
want to prevent the dil from operating when the host is asleep,
I can do this in :init: also: (You'll also notice the one we
actually used in :init: checked for fighting as well.)
(NOTE) This on_activation() call not only checks the eagle's
condition at the point it is called, but also at any given time
while the program is executed! This means if the eagle were to
be hit by a sleep spell during his talk, the on_activation()
would sense his condition and abort the program execution then
and there.
Here is the call:
on_activation(self.position < POSITION_SITTING, skip);
where 'skip' is a special statement used to stop the Dil
program from activating. So if the eagle is in a position
of sleeping, or worse (dead or incap) then the dil will not
execute! Otherwise execution continues.
(Thanks to Papi for this explanation)
*/
:ohno_combat:
heartbeat := PULSE_SEC*5;
exec("say Ok then, so it shall be this way with you...", self);
pause;
exec("say Die nerf herder.", self);
pause;
goto start;
}
dilend
/* Ok, the last Dil script necessary. This one will activate on
any command given by a pc. The purpose of this one is to accept
the quest items and for everyone accepted, put an entry on the
quest flag (exdp entry) that says 'I got that itme!' */
dilbegin
var
pc : unitptr;
item : unitptr;
exdp : extraptr;
code
{
heartbeat := PULSE_SEC*5;
on_activation((self.position < POSITION_SITTING) or
(self.position == POSITION_FIGHTING), skip);
/* Ok, so again we set up a heartbeat to use and check that the
eagle is in a position to help. I.E. That he is not sleeping,
incap or dead, and not fighting as well. */
:start:
wait(SFB_CMD, command(CMD_GIVE) and (activator.type == UNIT_ST_PC));
/* This label (give) is referred to below. Note - The label will
be ignored now, we will just go to the next line, but later
if we do a 'goto give', this is where we come! */
:give:
if (not (ASGAARD_ONGOING in activator.quests))
goto start;
/* The pc stuff we discussed. The item works as follows. We assign
item to whatever is 'inside' the self (eagle in our case). So
if I just gave the eagle the skull key, that is inside the eagle,
which means in its inventory. And we want to secure the item
just as we did before, we use the label labsecure where we used
lost_pc earlier! */
pc := activator;
item := self.inside;
secure(pc, labsecure);
secure(item, labsecure);
/* Basically we wait for a command now! */
wait(SFB_CMD or SFB_TICK, TRUE);
/* Ok, now we compare what is inside the eagle's inventory. */
if (not (item #= self.inside))
{
unsecure(item);
exdp := ASGAARD_ONGOING in pc.quests;
/* Very Important! The "Skull key" is exactly the FIRST name on the
namelist for the item skull key. If you use any other name on
the list, this will not work, so always use the first! Ok, so
if the skull key is an item in the eagle's inventory, we add
the extra description (exdp.names) "skull key" to the quest
flag. This name "skull key" can be specified by you, use what
ever you like here, but remember to use the same one in the
first dil wich checks for it. Go back now and look to see if
we checked for "skull key" in the first dil! What purpose does
this serve you ask? Well, if the pc only gives one item at a time
and has to leave, when he returns, the item given is still
considered given since we put it on his quest flag! It is a way
of recording data on the pc itself! Finally we have the eagle
say something, and then destroy the key in its inventory! Lastly
we repeat this process for each of the other two items! */
/* (NOTE) An alternative to our check on the item below is to
use a symbolic comparison. We could check the same skull key
like this:
if (("skull_key" $= self.inside.nameidx) and
("ratswarf" $= self.inside.zoneidx))
This call above verifies both the symbolic name of the item
and the zone it is defined in. For those of you C coders you'll
be happy to note that these string comparisons are NOT case-
sensitive. */
if ("Skull key" in self.inside.name)
{
addstring(exdp.names, "skull key");
exec("say Heh, the old slaver suffered I reckon *grin*...",
self);
destroy(self.inside);
}
else if ("Trident" in self.inside.name)
{
addstring(exdp.names, "trident");
exec("say I gather the Guardian Devil didn't take to your " +
"insolence?", self);
destroy(self.inside);
}
else if ("Imperial sword" in self.inside.name)
{
addstring(exdp.names, "imperial sword");
exec("say Ahh yes, a fine imperial sword, " +
"a most devastating weapon indeed!", self);
destroy(self.inside);
}
else
{
exec("say Did I ask for this, "+pc.name +"?", self);
pause;
exec("say I think not.", self);
/* Return the item to the player */
link(self.inside, pc);
}
}
unsecure(pc);
/* If the 'give' command is given, at any time, go back to the
:give: label and start from there to check what was given!
Also, a drawback to this whole procedure is the fact that if
two items were given quickly enough, one of them would not
be recorded as being given, the give label would be referenced
before the first item was done triggering its code sequence.
You'll also notice that like in C, if our if() statement only
has one line to execute, the braces {, } are not necessary, I
use them generally out of habit and anal-coding style. */
if ((command(CMD_GIVE)) and (activator.type == UNIT_ST_PC))
{
goto give;
}
else
goto start;
/* This is equiv. to our :lost_pc: of the first dil script. The dil
comes here when there is a seperation of self and either pc, or
item. */
:labsecure:
exec("say They won't get far!", self);
goto start;
}
dilend
/* Well, that is all there is to it. I must give credit where it is
due. I used the Mary's quest format, I think written by Papi but I
am not sure. I just modified it to suit my needs for my quest. This
is what you can do also for your quest, but feel free to play and
explore...You can think of many things to add here if you find the
need. An important thing to realize about Dil is this: It works in
parallel with other dil scripts. That is, the two we defined here
on the eagle will both be running at the same time, and that is why
we use conditions like command("give"), to specify which dil we want
to use at the time. I hope this tutorial helps you, please feel free
to send any questions/comments to me (Icculus, Andrew Cowan) at
cowana@magicnet.net I would highly recommend also checking out
haon-dor.zon which is available through the same ftp site. This file
contains much more advanced dil code, and illustrates the use of sending
messages to/from dil programs.
Enjoy the wonderful world of Dil!
-Icculus */
end /* end of eagle deinition */
/* *************** End of Tutorial *************** */