DIL Documentation
Version 5.0 (Draft)
This documentation is designed for people with some experience in programming. Experience in C is recommended, but PASCAL or BASIC in some form or other will do just fine too.
DIL is a simple programming language with fixed types reflecting the types used in Valhalla Mud. The language gives you almost endless possibilities in designing your adventures and quests. It gives you the control you need for making the game interesting and intelligent.
Core Concepts
Making a Program
You define your DIL programs within your zone file. Each program you make is a template. Such a template must be equipped with a unique name (for that zone).
Templates can either be defined in a new %dil section, just below the %zone section in the zone file, or directly attached to units defined in your zonefile.
If you define your DIL templates inside a unit definition, that unit is automatically assigned a program using that template. If you want to use an already designed template, either in your own, or another zone, you use a special function named dilcopy, that takes the name of a template, and any optional parameters in parenthesis. The parameters of a template called with dilcopy may only be of types integer, strings and stringlists.
Example:
dilcopy myfunc@myzone("say Hello, my friend!", 1, "say", {"name1", "name2"});
DIL templates can always be reused. In fact, with version 2.0 of DIL, programs are no longer saved with equipment, but only a symbolic reference is used. That way, if you make a change in your zone, ALL units using that program are changed.
This however requires you keep the name. Upon loading, if a template is not found, the program is halted and rendered useless until such a reference can be found during loading.
Technical note:
When you use several dilcopy in your zonefile, only one instance is present game-time, thus saving tremendous amounts of memory. It is similar to a shared library, in which code is shared but variables are not.
You may use your templates to define both procedures and functions for your other templates to call.
A template is defined by beginning with dilbegin and ending with dilend. Inside the template your program section is defined, marked by the keyword code, followed by the program itself:
dilbegin myprogram();
var
i : integer;
j : integer;
code
{
heartbeat := PULSE_SEC*5;
:start:
exec("say Hello world", self);
pause;
goto start;
}
dilend
This simple template does nothing but making its owner say ‘Hello world’ once every 5 seconds. The template is called myprogram and takes no arguments.
The pause command waits for the program to receive a timer message, whose interval is set by the inline variable heartbeat. The self variable is a unitptr referring to the unit owning the DIL program. Other inline variables will be explained later.
For a DIL program to work, it must be part of a unit: a room, player, non-player or object. The program uses messages to operate. Your program gets activated when it receives a message that some command is being executed, or a certain amount of time has passed. You decide yourself in the program what to wait for before the program continues executing.
Supposing you want your program to contain variables, you have to put them in a section before the code section marked var. The variables are declared by their type and their name, separated by a : and ended by a ;
For an example, see the program above. Variables of type string, stringlist and integer are saved, if the unit the program is attached to is saved with player inventory.
Variables of type unitptr and extraptr are ‘volatile’. This means that they are cleared whenever there is a chance that their contents may have been rendered non-usable. This ensures that you do not have any ‘loose’ pointers.
However it is possible to secure the contents of a unitptr type variable of a unit located in your local environment (the units directly ‘visible’ to the unit who owns the DIL program). See the secure / unsecure functions.
Data Types
DIL supports a fixed set of types you can use and manipulate. Through these, you can get and manipulate information about almost anything in the game. The types are listed below:
String
A string is some text. They are used for a lot of things such as command arguments, room descriptions etc. String variables are automatically resized and allocated when you assign them, so you do not have to worry about the string length nor allocation in your DIL programs. Strings may be compared with:
==(equal to, case-insensitive)!=(not equal, case-insensitive)$=(equal to, case-sensitive)
String comparisons with == are case-insensitive, so "Hello" == "hello" evaluates to TRUE. Use $= for case-sensitive comparison, or strcmp() if you need the comparison result (-1, 0, 1).
Static strings are defined just as in the rest of the zone, within double quotations. Strings may be searched easily with the ‘in’ operator. Variables of type string are saved with DIL programs, if attached to a saved unit.
Example:
"This is a static string"
Strings may also be added to each other, in an expression.
Example:
"say "+itoa(42)+" is the answer!"
Example:
if (self.name == self.names.[1]) ...
Elements of the string can also be accessed by referencing them by their position.
Example
if (str.[3]=="f")
{
exec ("say The 4th element is a F.",self);
}
Note
Currently you can only read individual characters from a string, you cannot
assign to them (e.g. str.[i] := "x" is not valid). To modify characters, build
a new string using concatenation:
Example
i := 0;
ln := length(str);
newstr := "";
while (i < ln)
{
if (str.[i] == "f")
newstr := newstr + "b";
else
newstr := newstr + str.[i];
i := i + 1;
}
str := newstr;
This replaces any ‘f’ in a string with a ‘b’. Since == is case-insensitive,
it will also match ‘F’.
Stringlist
A stringlist is a list of separate strings. This is used for things such as (multiple) names or keywords. You may request a specified word in a stringlist by its number.
Example:
mystring := self.names.[2];
Returning null if out of bounds of the stringlist (see ‘length()’). Static stringlists are defined just as in the rest of the zonefile, as a comma separated list of static strings within curly brackets.
Example:
mystringlist := {"Some string","another string","the last string"}
Stringlists are modified through the ‘addstring()’ and ‘substring()’ procedures. Stringlists are searched easily by the ‘in’ operator. See documentation below. They can also be set directly (see example above). Variables of type stringlist are saved with DIL programs, if attached to a saved unit.
Elements of each separate string in a stringlist can be accessed by appending a separate position at the end of the request for a string as follows:
Example
if (strlist.[5].[3]=="b")
{
do something
}
Note See the strings for more information on accessing a single element.
Integer
Non-fraction numbers can be used throughout your DIL programs. They are given normally, or in normal C style hexadecimal, preceded with ‘0x’. Integers are signed 32-bit integers. Variables of type integer are saved with DIL programs, if attached to a saved unit.
Example:
0x10
Example:
2
Integers are used within expressions as both number value and boolean (true/false) values. DIL has no separate boolean type - TRUE and FALSE are macros for 1 and 0 respectively. Lowercase true/false are not supported.
You may use comparison between integers through the comparison operators:
==(equality)<(less than)>(greater than)<=(less or equal)>=(greater or equal)!=(not equal)
Example:
if (42<12) ...
Returning the boolean value (true/false) depending on the comparison between integers. The result may be stored in an integer variable, and tested later, or used directly in an ‘if’ or ‘while’, etc. You may also operate on boolean expressions themselves, using logical operators:
andnotor
These allow you to combine expressions. They are logical operators that convert operands to boolean (non-zero = true) before evaluating. Use & and | for bitwise operations on flags.
Example:
if ( not ( (self.hp<42) or (self.level>10) ) ) ..
Integer expressions may also use a number of other operators:
+(addition)-(subtraction/negation)*(multiplication)/(division)|(bitwise or, for flags)&(bitwise and, for flags)%(modulo, remainder of division)
Precedence rules for using these operators are still somewhat messed up. You’d better use parenthesis where ever possible.
Integerlist
Intlists are a dynamic array of integers. This variable type allows you to keep an ordered list of integers without having to create a variable for each integer.
Initialising an intlist:
wpn := {5,3,6,9,3,9};
myintlist := {1,5,9,2,8,5};
myintlist := {}; // Empty list
Modifying elements:
You can assign to existing indices within the list bounds:
myintlist.[2] := 50; // Replaces element at index 2
Important: Assigning to an index at or beyond the current length silently fails. Lists do not auto-expand on assignment.
Adding elements:
Use addint() to append to the end, or insert() to add at a specific position.
addint(myintlist, 99); // Append 99 to end
insert(myintlist, 0, 42); // Insert 42 at beginning
insert(myintlist, 3, 77); // Insert 77 at index 3
Accessing intlists:
Elements are accessed using the .[index] syntax. Accessing an out-of-bounds index returns a failure value.
i := myintlist.[0]; // Get first element
ln := length(myintlist); // Get number of elements
// Iterate over all elements
i := 0;
while (i < ln)
{
val := myintlist.[i];
i := i + 1;
}
See Also: insert, remove, length
Extraptr
Extra descriptions, quest structures, etc. can be searched and manipulated using variables of this type. There is no way to declare static structures of this type in DIL programs.
Lists of extra descriptions are easily searched with the in operator (see below).
Extraptr variables are ‘volatile’, and thus cleared whenever there is a possibility that they are rendered unusable. See Extraptr Fields for available fields.
Cmdptr
The cmdptr can be used to search the command list or display all the commands. It is also now possible to sort the commands by type by defining your own command types and using the type field to sort on. See Cmdptr Fields for available fields.
In order to get the first command in the list you use: chead().
If you want to get a specific command then you use: getcommand(s: string).
Example:
dilbegin cmdtst(arg : string);
var
cmd : cmdptr;
st : string;
code
{
cmd := chead();
while (cmd)
{
st := cmd.name + " " + itoa(cmd.level) + " " + itoa(cmd.type) + " " +
itoa(cmd.loglevel) + " " + itoa(cmd.position);
act("CMD: $2t", A_ALWAYS, self, st, null, TO_CHAR);
cmd := cmd.next;
}
cmd := getcommand("kick");
sendtext ("You must be "+itoa(cmd.level)+" to use kick&n",self);
quit;
}
dilend
Unitptr
Unit pointers are used to keep track of units: rooms, players, non-player or objects. Through a variable of this type you can access most of the information in a unit. Unitptr variables are ‘volatile’, and thus cleared whenever there is a possibility that they are rendered unusable. See Unitptr Fields for available fields.
Zoneptr
The ‘zoneptr’ works like a unitptr. To get the first zoneptr in the global
list of zones you use zhead(). See Zoneptr Fields for available fields.
Example:
zoneptr := zhead();
Example: Zone list command
dilbegin zonelist (arg:string);
var
z:zoneptr;
buf:string;
code
{
z := zhead();
while (z)
{
buf := "Name: "+z.name+"&n";
buf := buf+"Filename: "+z.fname+"&n";
buf := buf+"Creator: "+z.creators.[0]+"&n";
buf := buf+"Notes: &n"+z.notes+"&n&n";
z := z.next;
}
pagestring (buf,self);
quit;
}
dilend
Messages
In DIL, a program attached to a unit gets activated when the program receives a message. In order to save CPU usage, there are a number of different message categories which can cause activation of a program.
The wait() command’s first parameter is an integer, telling what message categories the program should reactivate on. The second parameter is an expression, which also must evaluate to TRUE.
pause is just a special instance of the wait() command.
Caveat Builder:
Whenever you construct the arguments for the wait command, bear in mind that ALL your tests will be executed EVERYTIME a message of the relevant kind comes through. Thus you should keep the length of the activation expression to a reasonable minimum, and you should NEVER use the time-consuming findxxx-functions.
Valid example (That prohibits a player from removing an object):
:glue:
wait(SFB_CMD,command("remove"));
u := findunit(activator,argument,FIND_UNIT_IN_ME,null );
if (u != self)
{
goto glue;
}
act("You can't remove $2n, it's sticky.",
A_SOMEONE,activator,self,null,TO_CHAR););
block;
goto glue;
The message categories are as follows:
SFB_CMD
Command message
When this flag is set, the program gets activated by all commands (legal or illegal) issued by PC’s or NPC’s. Moving around is also considered a command.
Assume a janitor executes the command ’ Ge all from corpse’, And Ge interprets to ‘get’
then this will occur in the DIL program:
'activator'... is a unitptr to the janitor.
'cmdstr'...... is a string which contains 'get'
'excmdstr'...... is a string which contains 'ge'
'excmdstr_case'...... is a string which contains 'Ge'
'argument'.... will contain 'all from corpse'
command("get") will evaluate to TRUE.
SFB_DONE
‘Command has now been executed’ message
When this flag is set, the program gets activated by all successful commands issued by PC’s or NPC’s. The ‘activator’, ‘medium’ and ‘target’ special values are used as follows:
'activator'... The PC / NPC which executed the command
'medium'...... The unit which is operated upon.
'target'...... The target of the operation.
For example, assume the player John gives a garlic to Mary the Lumberjack wife. The following values are set:
activator == John (unitptr)
medium == Mushroom (unitptr)
target == Mary (unitptr)
command(“give”) will evaluate to true.
You thus know that Mary has in fact received the mushroom. It is NOT possible to block (using the ‘block’ command) these commands since they have already been executed at the time of notification. In a ‘get’ command, medium would be a unitptr to where the unit was taken from, and target would be the object which was taken.
See the file commands.txt for a description of how the arguments are set for each command. If you can not find a command in there, just ask to get it implemented. Especially you should pay attention to the non-obvious SFB_DONE command(CMD_AUTO_ENTER).
SFB_TICK
Timer message
When this flag is set, the routine gets activated by a “clock”. The clock ticks (in 1/4th of a second) are determined by the ‘heartbeat’ variable.
'activator'... is null.
'argument'.... will be empty.
command(CMD_AUTO_TICK) will evaluate to TRUE.
SFB_COM
Combat message
When this flag is set, the routine gets activated whenever a combat is in progress. The unit containing the DIL program needs not be involved in the combat itself:
'activator'... is a unitptr to the PC/NPC about to hit someone else.
'argument'.... is empty.
command(CMD_AUTO_COMBAT) will evaluate to TRUE.
SFB_DEAD
Death message
When this flag is set, the routine gets activated when a PC or NPC dies:
'activator'... will point to the PC/NPC that is about to die.
'argument'.... is empty.
command(CMD_AUTO_DEATH) will evaluate to TRUE
The SFB_DEAD message is sent, just as the character dies, while his items are still equipped, just before the corpse is created. The character’s ‘.fighting’ field points to his primary opponent, although this person does not necessarily have to be the one that killed him.
This can be exploited by making items wiz invisible (the .minv field) just as the message is received, causing them to stay inside the player rather than being transferred to the corpse. This does both give the ultimate crash protection, as well as a means of letting items, such as bribe items, forever be a part of the player (remember to make it un-wizinvis when the player returns from heaven - players should not have access to items while in heaven).
SFB_MSG
User message
When this flag is set, the routine gets activated when a message is passed to it. Messages can be passed with the DIL commands ‘sendto’ and ‘send’:
'activator'... is a unitptr to the unit sending the message.
'argument'.... is a string containing possible data from the sender.
command(CMD_AUTO_MSG) will evaluate to true.
Messages are normally not generated by actions performed by the owner of the program. For a program to be able to be aware of messages from the owner, the keyword aware should be listed just after dilbegin.
When a unit is saved, normally, the DIL programs in it would restart when the program is loaded. However it is possible to let DIL programs recall their execution from where they left off when they were saved. This is done by listing the keyword recall after dilbegin.
This is however a bit limited. Only the status of the original template is saved, not the state inside a template called as a function or procedure from inside the original template. secure and interrupts are not recalled upon loading the template.
Example:
dilbegin recall aware mute();
var
i : integer;
code
{
i := 10;
while (i > 0)
{
wait(SFB_CMD,command("say") or command("shout"));
exec("emote tries to make a sound, but only blood spurts through"+
"the lips",self);
block;
i := i - 1;
}
i := 10;
while (i > 0)
{
wait(SFB_CMD,command("say") or command("shout"));
exec("emote tries to make a sound, but can't",self);
block;
i := i - 1;
}
i := 10;
while (i > 0)
{
wait(SFB_CMD,command("say") or command("shout"));
exec("emote tries to make a loud sound, but can't",self);
block;
i := i - 1;
}
quit;
}
dilend
When attached to a PC:
- The first 10 sound commands, the PC will be unable to utter a sound, and blood will spurt out.
- The next 10 sound commands, the victim just can’t shout.
- The last 10 sound commands only say will work.
- In the end,
quitremoves the program all together.
The smart thing is that the aware keyword lets the program receive messages from the owner (player) commands. Secondly, the keyword recall makes sure that the program does not start over, if the victim quits in the middle, but restart at the position it had attained.
Do not put in the aware if it is not needed. It saves some interpretation time not having to pass the extra messages.
SFB_PRE
Command preprocessing
When this flag is set, the program is activated by a few special events which can then be blocked or changed. Currently, this event is triggered just prior to a spell being cast, and just prior to any sort of damage being given to a target.
PRE “command(“cast”)“
Assume a a spell is cast from player A on player B with a scroll C.
'activator'... is a unitptr to A.
'medium' ... is a unitptr to C.
'target' ... is a unitptr to B.
command("cast") will evaluate to TRUE.
'argument'.... will contain a number followed by any
argument to the spell itself. You should
parse this number, it equals the spell number SPL_XXX.
'power' .... is set to 'HM', i.e. how powerful the spell cast
is going to be. You can change this number at will.
If you decide to block the spell, you ought to set it
to -1.
Example:
wait(SFB_PRE, command("cast"));
s := getword(argument);
splno := atoi(s);
if (splno == SPL_FIREBALL_3)
power := 0; /* No damage */
...
PRE “command(CMD_AUTO_DAMAGE)”
Assume that damage is given from player A to player B with a sword C.
'activator'... is a unitptr to A.
'medium' ... is a unitptr to C.
'target' ... is a unitptr to B.
command(CMD_AUTO_DAMAGE) will evaluate to TRUE.
'power' .... is set to how much damage will be given. You can change
this number at will, but you should not set it to less
than zero.
'argument'.... will contain three numbers that you must parse to
determine what kind of damage we're talking about.
First number is the attack group, this can be one of
MSG_TYPE_XXX from values.h and/or vme.h.
Second number is dependant on the attack group and
identifies what damage compared to the group. For
example WPN_XXX for weapon group.
Third number is the body hit location, one of WEAR_XXX,
(but not all of them, just hands, feet, legs, body, head,
arms, i.e. the armour positions, not positions like finger
or ear).
Example:
wait(SFB_PRE, command(CMD_AUTO_DAMAGE));
s1 := getword(argument);
grpno := atoi(s1);
s2 := getword(argument);
atkno := atoi(s2);
s3 := getword(argument);
hitloc := atoi(s3);
if (grpno == MSG_TYPE_SPELL)
{
if ((s2 == SPL_FINGER_DEATH))
{
act("Your scarabaeus lights up as it protects your life force.");
power := -1;
block;
}
}
...
Note on DIL Program Activation:
If a DIL program is already active (e.g., sending a message or using exec), it cannot be activated again. Any active DIL program is unable to catch further messages.
Example: Imagine you have five programs P1..P5:
- P1 sends a message → intercepted by P2
- P2 sends a message → P1 is busy, so P3..P5 are candidates
- P3 sends a message → P4 acts upon it
- P4 sends a message → Only P5 can catch it (all others are “busy”)
- If P5 sends a message → no one can act upon it
- When P5 returns → P4 returns → P3 returns → P2 returns → P1 returns
DIL programs execute in a nested call stack. Once active, a program cannot be re-activated until it completes.
DIL Program Priority (fnpri)
DIL programs can specify an execution priority that determines the order in which
they are stored on a unit. When viewing a unit’s functions with wstat <unit> func,
DIL programs display in priority order from top to bottom, with the most important
(lowest number) at the top.
Lower numbers = higher priority.
If fnpri is not specified, the program defaults to FN_PRI_CHORES (40). Valid
values are 1-254.
Syntax:
dilbegin fnpri(FN_PRI_XXX) program_name();
Priority Constants (from vme.h):
| Constant | Value | Usage |
|---|---|---|
| FN_PRI_RES | 0 | Reserved - Don’t use |
| FN_PRI_SYS | 1 | System functions |
| FN_PRI_DEATH | 2 | Functions dealing with death |
| FN_PRI_BODY | 5 | Gaining hits, regeneration, etc. |
| FN_PRI_COMBAT | 10 | In-combat moves |
| FN_PRI_RESCUE | 20 | Rescue someone rather than passing by |
| FN_PRI_BLOCK | 25 | Blocking things like commands |
| FN_PRI_MISSION | 30 | Sent on an important mission |
| FN_PRI_CHORES | 40 | Picking up garbage or eating rabbits (default) |
Arithmetic is supported: fnpri(FN_PRI_RESCUE-1) sets priority to 19.
Example:
Death sequences should take highest priority:
dilbegin recall fnpri(FN_PRI_DEATH) death_seq();
Arrest check should run before general guard behavior:
dilbegin fnpri(FN_PRI_RESCUE-1) arrest_check(office:string);
Midgaard Guard Example:
When you wstat guard func, you might see:
dilcopy rescue@function("guard/captain/mayor");
dilcopy arrest_check@midgaard("accus_room@midgaard");
dilbegin SFUN_PROTECT_LAWFUL time PULSE_SEC*60 bits SFB_RANTIME
dilbegin guard2();
In this example:
rescuehas highest priority - guard will prioritize rescuing someone being attackedarrest_checkruns second - guard will arrest someone before keeping the peaceguard2has lowest priority - general patrol behavior
See also: priority and nopriority commands for dynamic blocking based on this ordering.
The ‘unique’ DIL Flag
The unique keyword ensures that a unit will never have more than one instance
of that specific DIL program running at the same time.
Syntax:
dilbegin unique programName();
This eliminates the need for if(dilfind()) checks when copying DILs onto units
that should only have one instance. Even if the DIL is copied multiple times,
only one instance will exist on the unit.
Example:
An entangle spell that should only have one instance per character:
dilbegin unique entangle_effect();
var
duration : integer;
code
{
/* Even if cast twice, only one instance runs */
duration := 10;
:loop:
if (duration <= 0)
quit;
/* Entangle logic here */
pause;
duration := duration - 1;
goto loop;
}
dilend
When this DIL is copied onto a character multiple times (e.g., spell cast twice),
the unique flag ensures only one instance exists, preventing duplicate effects.
Built-In Variables
cmdstr
This variable is a string which contains the “command” which was entered by a player. The result of adding:
cmdstr + " " + argument
is the entire string as entered by the player. The ‘cmdstr’ is EXPANDED by the interpreter, so assume a player types ‘s’ then the string is expanded to ‘south’.
excmdstr
This variable is a string which contains the “first string” which was entered by a player. The result of adding:
excmdstr + " " + argument
is the entire string as entered by the player. The ‘excmdstr’ is not EXPANDED by the interpreter, but it is converted to lower case. So assume a player types ‘S’ then the string is returned as ‘s’. The ‘excmdstr’ is however changed to all lower case if you don’t want this see ‘excmdstr_case’.
excmdstr_case
This variable is a string which contains the “first string” which was entered by a player. The result of adding:
excmdstr_case + " " + argument
is the entire string as entered by the player. The ‘excmdstr’ is not changed in anyway from how a player types it. If a player types ‘S’ then the ‘excmdstr_case’ will have ‘S’ in it.
self
This variable is a unitptr to the unit owning the DIL program. For C++ people, this is like the ‘this’ pointer. For example, if Mary has a program, then self.title equals “Mary the Lumberjack wife”
activator
This variable is a unit pointer to the unit which activated the DIL program. It is set if ‘activator’ issued a command or unknown command and the program was setup to catch it, with : wait (SFB_CMD…). See description of messages for more information.
target
This variable is a unit pointer to the unit which is the target of the current operation. For ‘done’ messages this could for example be the destination in a give command. See description of messages for more information.
medium
This variable is a unit pointer to the unit which was used during an operation. For ‘done’ messages this could for example be a bag which was the medium in a get operation. See description of messages for more information.
power
This variable is an integer which can be reassigned. It is used in permission messages and in a few done messages. For example a permission request to damage a person with 100 damage would have power equal 100. This could then be reduced to 50 by assigning a new number. See description of messages for more information.
argument
This variable is a string, showing the argument of a command resulting in the activation of the DIL program. See SFB_CMD for example.
heartbeat
This is the DIL programs heartbeat. It can be assigned runtime to change the rate with which SFB_TICK is activated. Do not set it too low, and remember that it is specified in 1/4th of a second. Use the constant PULSE_SEC to multiply your wanted delay. Maximum value is approximately 4 hours (do not exceed 16000 * PULSE_SEC). Example:
heartbeat := PULSE_SEC*25; /* Tick every 25 seconds */
null
This is a null pointer.
weather
This is the state of the mud-weather. It will equal one of the SKY_XXX values in values.h and/or vme.h.
realtime
This variable returns the number of seconds passed since 1970 something. For C buffs this is equivalent to time(NULL).
mudday
mudhour
mudmonth
mudyear
These variables lets your program keep track of the time in the mud. They all have integer types.
DIL Constructs
DIL offers a set of constructs for you to program with.
if
The if statement is much like C. It takes any type as argument. Non integers are considered ‘TRUE’ if they are not null.
Example:
dilbegin foo();
code
{
if (self.hp > 10)
{
exec("say Hehe!", self);
}
else
{
exec("say ouch!", self);
}
}
dilend
Example:
dilbegin foo();
code
{
if (self.loaded > 10)
{
exec("say its getting crowded!", self);
}
}
dilend
Example:
dilbegin foo();
code
{
if (self.loaded < 10)
exec("say plenty of room!", self);
}
dilend
goto
The goto statement lets you jump about in the code. Labels in your DIL programs, for ‘goto’ or interrupts are defined within ‘:’. For an example, see the program below.
Example:
dilbegin foo();
code
{
:mylabel:
exec("say Hello world",self);
pause;
goto mylabel;
}
dilend
while
The while statement lets you execute a series of statements while an expression evaluates to TRUE.
Example:
dilbegin foo();
code
{
while (not self.inside)
{
exec("say I own nothing", self);
pause;
}
exec("say ahh.. now i own something", self);
}
dilend
break
The break statement makes you break out of any loop you’re currently in.
Example:
dilbegin foo();
code
{
while (self.inside)
{
if (self.position < POSITION_SLEEPING)
break;
exec("say I own something", self);
pause;
}
}
dilend
continue
The continue statement makes you jump to the top of any loop you’re currently in.
Example:
dilbegin foo();
code
{
while (self.inside)
{
if (self.position < POSITION_SLEEPING)
break;
pause;
if (self.hp < 0)
continue;
exec("say I own something", self);
pause;
}
}
dilend
on .. goto
Syntax: on n goto la, lb, ..., ln
This construct is an easy way of performing a goto operation based on the result of an integer. The integer value ‘n’ must be zero or positive and less than the number of labels specified in the label-list. If n is outside this range, the on-goto operation is skipped and execution continues at the next instruction.
Based on the value of ‘n’ execution continues at the label corresponding to number ‘n’ in the list. I.e. if n is 0, then execution continues at the first specified label, if n is 1 then at the second, etc.
Example:
Assume you have an integer ‘i’ larger than zero, which takes on 0, 1, 2, 3, 4 or 5. Based on each value you need to take a different action, this is how you can do it:
on i goto grin, laugh, grin, nada, poke, laugh;
log("Value was not in the range 0..5");
quit;
:laugh:
exec("grin", self);
goto ...;
:grin:
exec("cackle", self);
goto ...;
:blank:
exec("cry", self);
goto ...;
:poke:
exec("smirk", self);
goto ...;
It is often used in this context
on rnd(0,4) goto l1, l2, l3, l4, l5;
:l1:
bla;
:l2:
bla;
....
foreach
Foreach is an easy way to process all the units in the local environment relative to the ‘self’ executing the foreach. Foreach takes care of creating a list of local units, and of securing them. You can use both break and continue in a foreach statement. The unit executing the foreach (‘self’) is always a part of the foreach.
Important: The units in the local environment are relative to the ‘self’ executing the foreach.
Example:
This foreach is copied onto the spell caster, and hence all units relative to the spell caster (i.e. self) are processed in the foreach. Assume that it was executed on the spell caster’s staff, then all units found would be relative to the staff, i.e. the spell caster’s inventory.
...
foreach (UNIT_ST_PC|UNIT_ST_NPC, u)
{
if (u.hp < u.max_hp)
{
act("Warm raindrops fall upon you, cleaning your wounds.",
A_ALWAYS, u, null, null, TO_CHAR);
u.hp := u.hp + 6;
if (u.hp > u.max_hp)
u.hp := u.max_hp;
}
else
act("Warm raindrops fall upon you.",
A_ALWAYS, u, null, null, TO_CHAR);
pause;
}
...
Language Features
Assignment
You can assign values to the variables you declare in your ‘var’ section, and some of the built-in variables. This is done by the := operator. Note that you can also assign a variable the result of an expression (the addition of strings below as an example).
Example:
dilbegin foo();
var
myvarsl : stringlist;
myvars : string;
code
{
:start:
myvarsl := {"a string","another","the first"};
myvars := self.name+" XX ";
myvarsl.[2] := "the last";
myvarsl.[3] := "illegal"; /* Will not work since [3] is not defined */
pause;
goto start:
}
dilend
Expressions
The expressions used in assignment can return any of the types you can declare variables. Also they might return fail or null. An expression returning fail will not result in any action being taken. Fail is returned when you request a field from a null pointer, or the like.
Example:
dilbegin foo();
var
myvarsl : stringlist;
myvars : string;
myvarint : integer;
myvaredp : extraptr;
myvarunit : unitptr;
code
{
:start:
myvarunit := self.inside.next;
myvarsl := {"a string","another","the last"};
myvars := self.name+" XX "+itoa(self.hp);
myvarint := activator.hp;
myvaredp := "Rabbit Stew Complete" in activator.quests;
pause;
goto start:
}
dilend
Operators
DIL features many operators. For integers, <, >, <=, >=, !=, == signify less than, greater than, less or equal, greater or equal, not equal, and equality.
Strings can be compared with == and != (case-insensitive) or $= (case-sensitive). See String section above.
Pointers (unitptr, etc.) can be compared with == and != to check if they reference the same unit.
Legacy operators:
#=- compares memory addresses rather than values, rarely useful in practice
in
The special operator ‘in’ is a multipurpose operator for searching. It allows you to search through quests and extra descriptions, stringlists, intlists, and search for words in strings.
string in string
Syntax: string in string
Searches for a string within another string (case-insensitive).
Arguments:
- Argument 1: A string to find
- Argument 2: A string to search
Returns: TRUE if first string is found in second string.
Example:
dilbegin foo();
code
{
if ("guard" in activator.title)
exec("say hello guard", self);
pause;
}
dilend
string in stringlist
Syntax: string in stringlist
Searches for a string within a stringlist (case-insensitive).
Arguments:
- Argument 1: A string to find
- Argument 2: A stringlist to search
Returns: 1+ if first string is found in stringlist (the number equals the index + 1), or 0 if non-existent.
Example 1:
s := "c";
sl := {"a","b","c","d"};
i := s in sl;
The result of ‘i’ is 3, and sl.[i-1] equals “c” (s).
Example 2:
dilbegin foo();
code
{
if ("james" in activator.names)
exec("say hello james.",self);
pause;
}
dilend
string in extraptr
Syntax: string in extraptr
Searches for a string within an extra description list.
Arguments:
- Argument 1: A string to find
- Argument 2: An extra description list to search
Returns: Extraptr to first entry with string matching .names or null if none. (Names must be exact letter by letter match, although case is ignored)
Example:
dilbegin foo();
code
{
if ("Rabbit Stew Complete" in activator.quests)
{
exec("say wow!, you helped Mary get her stew!", self);
exec("app ", self);
}
pause;
}
dilend
integer in intlist
Syntax: integer in intlist
Searches for an integer within an intlist.
Arguments:
- Argument 1: An integer to find
- Argument 2: An intlist to search
Returns: 1-based position if found (1 = first element), or 0 if not found. Use myintlist.[result - 1] to access the element.
Example 1:
n := 30;
il := {10, 20, 30, 40};
i := n in il;
The result of ‘i’ is 3, and il.[i-1] equals 30 (n).
Example 2:
dilbegin check_weapon();
var
allowed_weapons : intlist;
wpn : integer;
code
{
allowed_weapons := {WPN_SWORD, WPN_DAGGER, WPN_AXE};
wpn := self.weapon_type;
if (wpn in allowed_weapons)
exec("say That weapon is permitted here.", self);
else
exec("say That weapon is not allowed.", self);
quit;
}
dilend
Functions
DIL features an extended set of built-in functions for extended program control. Built-in functions can be part of any expression in DIL. The built-in functions are listed later.
Example:
dilbegin foo();
code
{
exec("say I exist in "+itoa(self.loaded)+"copies", self);
pause;
}
dilend
DIL also lets you use templates as functions, but in a limited way. Using templates as functions, you may only use the return value to assign it to one of the variables declared in the ‘var’ section. No fields.
Example:
dilbegin integer bar(s:string);
code
{
exec("say "+s,self);
return rnd(1,10);
}
dilend
dilbegin foo();
external
integer bar(s:string);
var
myint:integer;
code
{
myint := bar("rolling the dice.");
exec("say I rolled a "+itoa(myint));
pause;
}
dilend
quit
This simple command quits the entire DIL program, even if called while inside a procedure or function.
return
Return from a call to a procedure template (no return type declared). The execution continues from where the procedure was called.
return()
Return from a call to a function (return type declared). The expression inside the parenthesis evaluates to the value returned.
Symbolic Function Calls and Typecasting
DIL allows for runtime symbolic resolving of function/procedure names. This allows you to pass template names as string arguments and call them dynamically in your program. This kind of function call requires runtime lookups and type checking, so use normal procedure calls when possible for better performance.
You can use either a string variable or a string literal directly as the function name. If the string contains a valid reference to a procedure/function, and all the argument types match the found template’s argument types, the call is made. Otherwise, the call is skipped. Note that you will get no type checking by the compiler when using symbolic references.
Calling as a procedure (ignoring return value):
// Using a variable
funcname := "some_procedure@zone";
funcname(self, target);
// Using a string literal directly
"some_procedure@zone"(self, target);
Any return value from the called function is automatically discarded when called this way.
Calling as a function (capturing return value):
When capturing the return value, you must specify the expected return type using typecast syntax. Place the type in parentheses before the function call:
funcname := "calculate_damage@combat";
result := (integer) funcname(self, target, weapon);
// Or with a string literal
result := (integer) "calculate_damage@combat"(self, target, weapon);
All DIL types can be used for typecasting:
(string)(integer)(unitptr)(extraptr)(stringlist)(intlist)
Important: If you typecast a call to a procedure (which returns nothing), the DIL will stop with an error to prevent stack corruption. Only use typecasts when you know the target returns a value of that type.
Example:
dilbegin test_dynamic();
var
funcname : string;
result : integer;
strresult : string;
code
{
// Using variable
funcname := "calculate_damage@combat";
result := (integer) funcname(self, target, weapon);
funcname := "get_description@utils";
strresult := (string) funcname(self);
// Using string literal directly
"walk_room@function"(self, destination, 0, TRUE);
quit;
}
dilend
Fields
The extraptr and unitptr types contain information which is available to you by using fields.
For example (self is a unitptr), self.inside is a unitptr to what is inside the self unit. (Hence .inside is the field). And self.inside.outside is a unitptr to self (assuming that something is actually inside self, otherwise self.inside would evaluate to null).
Some fields may not be changed by DIL programs directly but must be modified indirectly using certain procedure calls in DIL. Others may not be changed at all. All are readable for DIL programs.
Note: In the following, (RW) means the value may be assigned new values by DIL, while (RO) means the value can only be read. A (RO) value might be possible to change through built-in functions/procedures.
Extraptr Fields
The extraptr structure is used for several things. The primary is extra descriptions for units, and quest data. Keywords in the ‘names’ field and the actual description/data in the ‘descr’ field.
Fields:
| Field | Type | Access | Description |
|---|---|---|---|
names | stringlist | RW | A list of strings of names that the extra description matches on |
descr | string | RW | The contents of the extra description |
next | extraptr | RO | The next extra description in a list |
vals | intlist | RW | A list of integer values attached to this extra |
Unitptr Fields
The unitptr is the key structure in the MUD, containing any kind of the following subtypes:
- object: A normal object (sword, vial, etc.)
- room: A room, location or the like
- pc: A playing character
- npc: A non-playing character (mobile, monster, etc.)
Common Fields (All Unit Types)
| Field | Type | Access | Description |
|---|---|---|---|
names | stringlist | RW | A list of names that matches the unit |
name | string | RO | The first name in the namelist in ‘names’. Modify via names field |
title | string | RW | The title of the unit |
outside_descr | string | RW | The description of the unit from the ‘outside’ (e.g. boat seen from outside) |
inside_descr | string | RW | The description of the unit from the ‘inside’ (e.g. boat seen from inside) |
extra | extraptr | RW | Extra descriptions of the unit (identify, look at, read etc.) |
next | unitptr | RO | The next unit in the local list of units (e.g. inventory linked list) |
inside | unitptr | RO | The inside of the unit hierarchy (carry, contains). First unit in inventory |
outside | unitptr | RO | The outside of the unit hierarchy (carried by, contained by). E.g. contents of a bag have outside pointing to the bag |
key | string | RW | The key that will open this unitptr (e.g. “big_key@blackzon”) |
gnext | unitptr | RO | Next unit in the global list of units |
gprevious | unitptr | RO | Previous unit in the global list of units |
hasfunc | integer | RO | Returns TRUE if unit has special functions attached to it |
hp | integer | RW | Current hitpoints. For objects, low HP = ‘broken’. -1 = unbreakable |
max_hp | integer | RO/RW | Maximum hitpoints. RW for NPCs, RO for PCs unless access level 0 |
manipulate | integer | RW | Bits specifying how unit can be handled (see MANIPULATE_* flags) |
flags | integer | RW | Bits specifying different properties of a unit (see UNIT_FL_* flags) |
baseweight | integer | RO | The empty weight of the unit. Use set_weight_base() to modify |
weight | integer | RO | Read-only current weight of unit and contents. Use set_weight() to modify |
capacity | integer | RW | The amount of weight the unit can contain |
height | integer | RW | Height of PC/NPC, length of rope, size of weapons/armor/shields |
alignment | integer | RW | Alignment value [1000..-1000]: 1000=good, 0=neutral, -1000=evil |
openflags | integer | RW | Bits specifying how unit may be opened, locked, etc (see EX_* flags) |
light | integer | RO | How much light is inside the unit |
bright | integer | RO | How much the unit lights up. Use setbright() to modify |
illum | integer | RO | How much light units inside a transparent unit create |
minv | integer | RW | The ‘wizard invisibility’ level |
spells[] | integer | RO/RW | Defence/skill of different spell spheres (index: SPL_* constants). RW for rooms/objects/NPCs, RO for PCs unless access level 0 |
zoneidx | string | RO | The unique database name in the zone for this unit |
nameidx | string | RO | The unique database zone name for this unit |
idx | integer | RO | Unique database index for this unit in the zone. Alternative to checking zoneidx+nameidx. Changes each reboot, don’t save |
zone | string | RO | The name of the zone the unit is in |
type | integer | RO | The type of the unit (see UNIT_ST_* constants) |
loadcount | integer | RO | Number of units loaded in the game of this definition (zoneidx/nameidx or idx) |
opendiff | integer | RW | Open/pick difficulty for this unit (e.g., locked containers). Higher values are harder |
symname | string | RO | Complete symbolic name in format “nameidx@zoneidx” |
Object Fields (UNIT_ST_OBJ)
| Field | Type | Access | Description |
|---|---|---|---|
objecttype | integer | RW | The type of an object (see ITEM_* constants) |
value[] | integer | RW | Values for an object [0..4]. Meaning differs by object type |
objectflags | integer | RW | Bits specifying special properties for object (see OBJ_* flags). Some combinations may not be logical |
cost | integer | RW | The basic cost of an object (old gold value) |
rent | integer | RW | The basic cost of renting an object, mostly used on very special objects (old gold value) |
equip | integer | RO | Position in equipment, indicates if item in inventory is equipped by PC/NPC. Use addequip() and unequip() to modify |
Room Fields (UNIT_ST_ROOM)
Note: Exit descriptions are available as standard extra keywords (‘north’, ‘east’, …, ‘down’).
Example:
ex := 'north' in self.extra;
| Field | Type | Access | Description |
|---|---|---|---|
exit_names[] | stringlist | RW | Names matching the exit (e.g. ‘open grate’). Index: NORTH/EAST/SOUTH/WEST/UP/DOWN |
exit_info[] | integer | RW | Bits specifying exit conditions (see EX_* flags). Index: NORTH/EAST/SOUTH/WEST/UP/DOWN |
exit_to[] | unitptr | RO | The unit the direction exits to. Index: NORTH/EAST/SOUTH/WEST/UP/DOWN. Cannot be changed through DIL |
roomflags | integer | RW | Bits specifying room properties (see ROOM_FL_* flags) |
movement | integer | RW | The type of movement in the room (see SECT_* constants) |
mapx | integer | RW | X coordinate on zone 2D map grid. -1 if unmapped |
mapy | integer | RW | Y coordinate on zone 2D map grid. -1 if unmapped |
exit_diff[] | integer | RW | Difficulty rating for exit. Index: direction (0-9) |
exit_key[] | string | RW | Key required to unlock exit, as symbolic reference “keyname@zone”. Index: direction (0-9) |
zoneweather | integer | RO | Current sky state for the room’s zone (see SKY_* constants). Shorthand for findzone(room.zone).zoneweather |
Example:
sl := self.exit_names[SOUTH];
act("The $2t slides open as you press the button.",
A_ALWAYS, activator, sl.[0], null, TO_CHAR);
Character Fields (UNIT_ST_PC or UNIT_ST_NPC)
| Field | Type | Access | Description |
|---|---|---|---|
speed | integer | RO | The current default combat-speed (as seen by wstat) |
fighting | unitptr | RO | The unit the PC/NPC is fighting |
opponentcount | integer | RO | Number of opponents currently in combat with this character. Use with getopponent() to iterate |
master | unitptr | RO | The unit the PC/NPC is following |
follower | unitptr | RO | First follower of a PC/NPC. Use getfollower() and followercount to iterate |
followercount | integer | RO | Number of characters currently following this character. Use with getfollower() to iterate |
exp | integer | RO/RW | Experience points for PC, or experience quality of monster. RW for access level 0 zones only. Use experience() to modify |
charflags | integer | RW | Bits specifying spell affects and other PC/NPC affects, and many other things (see CHAR_* flags) |
lastroom | unitptr | RO | The room the player just left |
mana | integer | RW | The amount of mana a PC/CHAR has left |
max_mana | integer | RO | The maximum mana of the character |
endurance | integer | RW | The amount of endurance a PC/NPC has left |
max_endurance | integer | RO | The maximum endurance of the character |
attack_type | integer | RW | The non-weapon attack type of a PC/NPC |
race | integer | RW | The race of a PC/NPC (see RACE_* constants) |
sex | integer | RW | The sex of a PC/NPC (see SEX_* constants) |
level | integer | RO/RW | The level of a PC/NPC. RW for NPCs, RO for PCs unless access level 0 |
lifespan | integer | RO/RW | The lifespan of a PC. RW for access level 0 zones only |
position | integer | RW | Position of PC/NPC (see POSITION_* constants). Affected by position_update() |
abilities[] | integer | RW | The abilities of a PC/NPC (index: ABIL_* constants). PC requires access level 0. NPC abilities are calculated from level at compile time; changing level at runtime does not update them |
weapons[] | integer | RO/RW | The weapon skills of a PC/NPC (index: WPN_* constants). RW for NPCs, RO for PCs unless access level 0 |
offensive | integer | RW | Combat offense bonus. Static base value used in hit calculations. Does not reset automatically |
defensive | integer | RW | Combat defense bonus. Static base value used in hit calculations. Does not reset automatically |
NPC-Only Fields (UNIT_ST_NPC)
| Field | Type | Access | Description |
|---|---|---|---|
defaultpos | integer | RW | The default position of an NPC (see POSITION_* constants) |
natural_armour | integer | RW | The natural armour of the NPC when naked |
switched | unitptr | RO | Character that is switched into this NPC |
PC-Only Fields (UNIT_ST_PC)
| Field | Type | Access | Description |
|---|---|---|---|
birth | integer | RO | The time a PC was created |
editing | integer | RO | Is the PC editing? TRUE for yes, FALSE for no |
hometown | string | RW | The hometown of a PC |
pcflags | integer | RW | The setup of player options (see PC_* flags) |
playtime | integer | RO | The time a PC has played |
skill_points | integer | RW | The amount of unused skill points the PC has |
ability_points | integer | RW | The amount of unused ability points the PC has |
exptol | integer | RO | The amount of experience needed to reach next level (calculated) |
vlevel | integer | RO/RW | Virtual player level. RW for access level 0 zones only |
skills[] | integer | RO/RW | The skills of a PC (index: SKI_* constants). RW for access level 0 zones only |
guild | string | RW | The guild the PC belongs to |
prompt | string | RW | The players prompt string |
protocols | integer | RO | Client protocol capabilities bitfield. Use with PROTOCOL_* constants (e.g., PROTOCOL_WEB for web/websocket connections) |
crimes | integer | RW | The number of crimes the PC has |
full | integer | RW | How hungry the PC is |
thirst | integer | RW | How thirsty the PC is |
drunk | integer | RW | How drunk the PC is |
quests | extraptr | RW | The quests a PC is finishing/has finished |
info | extraptr | RW | The info structure of a PC |
acc_balance | integer | RO | Player’s account balance if accounting mode enabled (shown in wstat <player> acc) |
acc_total | integer | RO | Player’s total credit if accounting mode enabled (shown in wstat <player> acc) |
ability_costs[] | integer | RO | Racial modifier for training this ability (-3 to +3). Based on PC’s race. Index: ABIL_* constant |
skill_costs[] | integer | RO | Racial modifier for training this skill (-3 to +3). Based on PC’s race. Index: SKI_* constant |
spell_costs[] | integer | RO | Racial modifier for training this spell (-3 to +3). Based on PC’s race. Index: SPL_* constant |
weapon_costs[] | integer | RO | Racial modifier for training this weapon (-3 to +3). Based on PC’s race. Index: WPN_* constant |
exp_multi | integer | RW | XP multiplier percentage (default 100). Always applied outside per mob XP cap. Does not reset itself |
exp_bonus | integer | RW | Flat XP bonus/penalty added after multiplier. Applied post-cap. Can be negative |
Cmdptr Fields
The cmdptr structure provides access to command information.
| Field | Type | Access | Description |
|---|---|---|---|
name | string | RO | Command name |
type | integer | RO | Command type (social, skill, or just command) |
level | integer | RO | Minimum level to use command |
loglevel | integer | RO | Level of character that can see the log (0 for no logs) |
position | integer | RO | Minimum position to use command |
next | cmdptr | RO | Pointer to the next cmdptr |
previous | cmdptr | RO | Pointer to the previous cmdptr |
Zoneptr Fields
The zoneptr structure provides access to zone information.
| Field | Type | Access | Description |
|---|---|---|---|
next | zoneptr | RO | Pointer to next zoneptr |
previous | zoneptr | RO | Pointer to previous zone |
creators | stringlist | RO | List of creators |
name | string | RO | Zone name (%zone) |
title | string | RO | Zone title (title “”) |
rooms | unitptr | RO | Pointer to the base room |
objs | unitptr | RO | Pointer to the base objects of the zone |
npcs | unitptr | RO | Pointer to base NPCs of the zone |
resetmode | integer | RO | Reset mode of zone (see values.h) |
resettime | integer | RO | The reset time of the zone |
access | integer | RO | The access level of the zone |
loadlevel | integer | RO | The loadlevel of the zone |
payonly | integer | RO | The paystatus of the zone |
roomcount | integer | RO | The number of rooms in a zone |
objcount | integer | RO | The number of objects in a zone |
npccount | integer | RO | The number of npcs/mobiles in a zone |
fname | string | RO | The filename of a zone |
notes | string | RO | The Zone Notes |
help | string | RO | The Zone Help |
zoneweather | integer | RO | Current sky state (see SKY_* constants in vme.h) |
pressure | integer | RO | Current barometric pressure (950-1050 range) |
weatherchange | integer | RO | Rate of weather change (-12 to +12). Positive = clearing, negative = worsening |
weatherbase | integer | RO | Base pressure for the zone (set in zone header) |
exp_multi | integer | RW | Zone XP multiplier percentage (100 = default). Applied to base NPC XP and subject to per kill cap |
exp_bonus | integer | RW | XP share bonus/penalty added after zone multiplier (can be negative). Added to base share before level, group, and cap shaping |
zone_exp | intlist | RO | Rolling 24h XP earned in 5-min buckets (288 entries). Index 0 = current bucket, 1 = 5 min ago |
zone_kills | intlist | RO | Rolling 24h NPC kill count in 5-min buckets. Same indexing as zone_exp |
zone_time | intlist | RO | Rolling 24h count of all player time in zone in 5-min buckets |
Built-In Functions
The following are definitions and documentation for the built-in functions in DIL. The definitions are not definitions ‘as such’, but serve to distinguish arguments in the documentation below.
asctime
Signature: string asctime(i: integer)
Converts a time value to a human-readable string.
Parameters:
| Parameter | Type | Description |
|---|---|---|
i | integer | The time to convert in seconds (realtime variable) |
Returns: The seconds converted into an ASCII format date of the following form: “Mon Nov 18 18:49:08 1996”
Example:
log(asctime(realtime));
strcmp
Signature: integer strcmp(s1: string, s2: string)
Compares two strings with case sensitivity. Returns an integer indicating the comparison result.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s1 | string | First string to compare |
s2 | string | Second string to compare |
Returns:
| Value | Meaning |
|---|---|
-1 | s1 < s2 |
0 | s1 == s2 |
1 | s1 > s2 |
Description:
This allows you to compare strings with case sensitivity in place. If you don’t care about the case of the string, use the normal ==, >, <, <=, >= operators.
Example:
if (strcmp("I Care about Capitals", s2) == 0)
{
sendtext("You care I can see.&n", self);
quit;
}
strncmp
Signature: integer strncmp(s1: string, s2: string, l: integer)
Compares the first l characters of two strings with case sensitivity.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s1 | string | First string to compare |
s2 | string | Second string to compare |
l | integer | Amount of significant characters to compare |
Returns:
| Value | Meaning |
|---|---|
-1 | s1 < s2 |
0 | s1 == s2 |
1 | s1 > s2 |
Description:
This allows you to compare strings with case sensitivity in place and it allows you to choose how much of the strings are compared.
Example:
if (strncmp("Mark Carper", s2, 4) == 0)
{
sendtext("Hi Mark how is it going?&n", self);
quit;
}
textformat
Signature: string textformat(s: string)
Formats a text string according to escape format codes, automatically adapting to each player’s settings.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | The text string to format using escape format codes |
Returns: The formatted string which will automatically adapt to each individual player’s settings.
Example:
ts := note.extra.descr;
rs := textformat(ts);
spellindex
Signature: integer spellindex(s: string)
Returns the spell index for a given spell name (abbreviated or full).
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | The abbreviated or full spell name |
Returns: -1 if no such spell exists, otherwise returns the spell index (SPL_XXX constant).
Example:
s := "cu li wo"; /* cure light wounds */
i := spellindex(s);
spellinfo
Signature: string spellinfo(idx: integer, i1: integer, i2: integer, i3: integer, i4: integer, i5: integer, i6: integer, i7: integer)
Returns detailed information about a spell and sets output parameters with spell properties.
Parameters:
| Parameter | Type | Description |
|---|---|---|
idx | integer | The spell index (SPL_XXX). Use spellindex() to find this value |
i1 | integer | Output: Spell realm (MAG / DIC) |
i2 | integer | Output: Spell sphere (SPL_XXX) |
i3 | integer | Output: Mana usage |
i4 | integer | Output: 0 if non-offensive spell, non-zero otherwise |
i5 | integer | Output: Resistance required (SPLCST_XXX) |
i6 | integer | Output: Mediums (MEDIA_XXX) |
i7 | integer | Output: Targets (FIND_UNIT_XXX & TAR_XXX) |
Returns: The full name of the spell, or the empty string if no such spell exists.
Example:
s := "cu li wo"; /* cure light wounds */
i := spellindex(s);
s := spellinfo(i, i1, i2, i3, i4, i5, i6, i7);
/* s & i1 - i7 now set */
can_carry
Signature: integer can_carry(ch: unitptr, u: unitptr, n: integer)
Checks if a character can carry a specified number of items.
Parameters:
| Parameter | Type | Description |
|---|---|---|
ch | unitptr | The character to check |
u | unitptr | The unit to check if they can carry |
n | integer | How many copies of that unit |
Returns:
| Value | Meaning |
|---|---|
0 | Character can carry n items |
1 | Too many items (hands are full) |
2 | Items are too heavy |
Example:
i := can_carry(activator, item, 1);
if (i == 1)
exec("say Your hands are full!", self);
else if (i == 2)
exec("say You cant carry that much weight.", self);
else
exec("give " + item.name + " to " + activator.name, self);
fits
Signature: string fits(char: unitptr, obj: unitptr, pos: integer)
Tests if an object can be worn by a character in a given position.
Parameters:
| Parameter | Type | Description |
|---|---|---|
char | unitptr | Character to test if obj can be fitted upon |
obj | unitptr | The object to test if it fits the character |
pos | integer | Position to test: -1 for automatic, or WEAR_XXX constant |
Returns: Empty string if the object fits, or a textual description of the size problem.
Description:
Fits tests if obj can be worn by char in position pos. If pos is -1, then fits automatically figures out the default worn position for obj.
Example:
s := fits(self, obj, -1);
if (s != "")
exec("say Don't buy it, its " + s, self);
replace
Signature: string replace(t: string, n: string, o: string)
Replaces all occurrences of a target string in the original string with a new string.
Parameters:
| Parameter | Type | Description |
|---|---|---|
t | string | The target string you want to replace |
n | string | What you want to replace the target with |
o | string | The original string |
Returns: The string with all occurrences of the old string replaced by the new string.
Example:
"Jafar %t% %l%" := replace(%n%, pc.name, "%n% %t% %l%");
"Jafar the human %l%" := replace(%t%, pc.title, "Jafar %t% %l%");
"Jafar the human 1" := replace(%l%, itoa(pc.vlevel), "Jafar the human %l%");
restore
Signature: unitptr restore(filename: string, u: unitptr)
Loads a copy of units that were previously saved with the store() command.
Parameters:
| Parameter | Type | Description |
|---|---|---|
filename | string | The name of the Unit file |
u | unitptr | The Unit to restore into, or null to create new units |
Returns:
- If
uis null: returns a pointer to the loaded Unit - If
uis not null: returns null and loads Units from file into unitu - The return value can be discarded if only needing the side effect of loading units into the target.
Description:
Restore loads a copy of units which were previously saved with the store() command. Just as with load(), the unit is put inside the unit which executes the restore command unless the u argument is not null. If the u argument is a unitptr like room, object, NPC, or PC, the items restored will be placed inside the u Unit.
Note: It is only possible to restore items as long as the main database contains a reference for the unit ‘name@zone’. Use store() and restore() sparingly, remember that items saved in player’s inventories are automatically saved in this instance.
The store() and restore() functions are perfect for operations such as:
- MUD mailing objects from player to player
- Storage devices for players that keep inventory through a reboot
- Saving a player’s inventory while they fight in an arena and restoring it undamaged when finished
- Saving a donation room through reboots (can save contents, NPCs, and objects in the room)
⚠️ Performance Warning: Disk access is always slow. If you use restore() on a continuous basis, keep file sizes to a minimum and spread operations over time. Excessive disk I/O can cause serious server delays.
Example 1:
dilbegin chest_load ();
var
waist:unitptr;/*to hold the null returned in this example*/
chest:unitptr;/*pointer to the storage chest*/
code
{
chest := load ("chest@myzone");/*get the container*/
if (chest==null)
{
log ("Error");/*log an error*/
quit;
}
waist := restore("chest."+self.zoneidx,chest);
/*
restore given filename into chest
waist can be ignored in this DIL since it is not used.
*/
link (chest, self);/*link chest into room*/
quit;/*DIL load routine done destroy self.*/
}
dilend
Example 2:
dilbegin chest_load ();
var
chest:unitptr;/*item to be loaded*/
code
{
chest := restore("chest."+self.zoneidx,null);/*restore into chest*/
if (chest== null)/*see if something was restored*/
chest := load("donate_chest@"+self.zoneidx);
/*load a new one if there is nothing restored*/
link (chest, self);/*link item into room*/
quit;/*destroy the load DIL.*/
}
dilend
Note: Example 1 is to be used if ‘storall’ was used not storing a container. Example 2 is for items stored with ‘store’ with the container saved as well. See Also: store, delunit
meleeattack
Signature: integer meleeattack(ch: unitptr, vict: unitptr, bonus: integer, wtype: integer [, primary: integer])
Performs a melee attack from one character against another.
Parameters:
| Parameter | Type | Description |
|---|---|---|
ch | unitptr | The character which should make an additional attack |
vict | unitptr | The victim of the attack |
bonus | integer | Any penalty or bonus added to the attack |
wtype | integer | The weapon type (WPN_XXX). If valid, bypasses wielded weapon |
primary | integer | (Optional) TRUE for primary weapon (wielded), FALSE for offhand. Defaults to TRUE |
Returns: The amount of damage given, or -1 if failed. Return value can be ignored / not assigned.
Description:
The character ch performs a melee attack (using whatever weapon is wielded or bare hands) against vict.
If wtype is within a valid weapon range (WPN_XXX), any wielded weapon will be bypassed, and the value will be used as the attacktype. Good for things like meleeattack(ch, vict, bonus, WPN_CIRCLE_KICK, TRUE) if you want a person to be able to perform an extra attack even though wielding a weapon. Note that this will require BOTH a weapon type WPN_CIRCLE_KICK and a skill “kick” in order for it to work.
Example:
dilbegin kick(arg : string);
external
provoked_attack (victim : unitptr, ch : unitptr);
var
bonus : integer;
targ : unitptr;
code
{
if ((self.type == UNIT_ST_PC) and (self.weapons[WPN_KICK] <= 0))
{
act("You must practice first.", A_ALWAYS, self, null, null, TO_CHAR);
quit;
}
if (arg == "")
{
if (self.fighting)
{
targ := self.fighting;
goto kick;
}
act("Kick who?", A_SOMEONE, self, null, null, TO_CHAR);
quit;
}
targ := findunit(self, arg, FIND_UNIT_SURRO, null);
if ((targ == null) or not visible(self, targ))
{
act("That person is not here!", A_SOMEONE, self, null, null, TO_CHAR);
quit;
}
if (not (targ.type & (UNIT_ST_PC | UNIT_ST_NPC)))
{
act("You can't kick that, silly!", A_SOMEONE, self, null, null, TO_CHAR);
quit;
}
if (targ == self)
{
act("You kick yourself.", A_HIDEINV, self, null, null, TO_CHAR);
act("$1n kicks $1mself.", A_HIDEINV, self, null, null, TO_ROOM);
quit;
}
if ((targ.type == UNIT_ST_PC) and (self.type == UNIT_ST_PC))
{
if (not(isset(self.pcflags, PC_PK_RELAXED)))
{
act("You are not allowed to do this unless you sign the book of blood.",
A_ALWAYS, self, null, null, TO_CHAR);
quit;
}
if (not(isset(targ.pcflags, PC_PK_RELAXED)))
{
act("You are not allowed to do this unless $2e signs the book of blood.",
A_ALWAYS, self, targ, null, TO_CHAR);
quit;
}
}
:kick:
/* Penalty for wielding a weapon while kicking! */
if (equipment(self, WEAR_WIELD))
bonus := -25;
else
bonus := +25;
if (self.endurance < 2)
act("You are too exhausted to attempt that.", A_ALWAYS, self, null, null, TO_CHAR);
else
self.endurance := self.endurance - 2;
provoked_attack(targ, self);
bonus := meleeattack(self, targ, (bonus+self.level), WPN_KICK, TRUE);
quit;
}
dilend
mid
Signature: string mid(o: string, s: integer, e: integer)
Extracts a substring from a string between start and end positions.
Parameters:
| Parameter | Type | Description |
|---|---|---|
o | string | The original string to be parsed |
s | integer | The starting point of the substring |
e | integer | The ending point of the substring |
Returns: The portion of the string defined by the start and end values.
Example:
"rock" := mid("sprocket", 3, 6);
moneystring
Signature: string moneystring(amt: integer, verbose: integer)
Converts a money amount to a string representation.
Parameters:
| Parameter | Type | Description |
|---|---|---|
amt | integer | The amount of money |
verbose | integer | TRUE for expanded string (copper pieces), FALSE for abbreviated (cp) |
Returns: The moneystring for the given currency.
equipment
Signature: unitptr equipment(u: unitptr, i: integer)
Returns the equipment worn by a character at a specific position.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The character to search |
i | integer | What equipment to find (WEAR_XXX constants) |
Returns: The unit on u which is equipped at position i. See WEAR_* in values.h and/or vme.h.
tolower
Signature: string tolower(s: string)
Converts a string to lowercase.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | String to convert to lowercase |
Returns: The string passed in, converted to lowercase (no capitals).
Example:
"hello!" := tolower("HELLO!");
toupper
Signature: string toupper(s: string)
Converts a string to uppercase.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | String to convert to uppercase |
Returns: The string passed in, with all characters capitalized.
Example:
"HELLO!" := toupper("hello!");
visible
Signature: integer visible(u1: unitptr, u2: unitptr)
Checks if one character can see another.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u1 | unitptr | The character who is looking at u2 |
u2 | unitptr | The unit which u1 is trying to see |
Returns: TRUE if u1 can see u2, FALSE otherwise.
Example:
if (visible(self, victim)) ...
opponent
Signature: integer opponent(u1: unitptr, u2: unitptr)
Checks if two characters are opponents in combat.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u1 | unitptr | First character |
u2 | unitptr | Second character |
Returns: TRUE if u1 is in combat with u2, FALSE otherwise.
Description:
When in combat, you are usually only melee-attacking one opponent, although you may have many other opponents. This function lets you check if you are directly or indirectly an opponent to another unit.
Example:
if (opponent(self, victim)) ...
getopponent
Signature: unitptr getopponent(u: unitptr, idx: integer)
Returns a specific opponent from a character’s combat list by index.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The character whose opponents to check |
idx | integer | Zero-based index into the opponent list |
Returns: The opponent at the specified index, or null if the unit is not a character, not in combat, or the index is out of range.
Description:
When in combat, a character may have multiple opponents. This function retrieves a specific opponent by index (0 = first opponent). Use with .opponentcount to iterate through all opponents.
Example:
i := 0;
while (i < self.opponentcount)
{
opp := getopponent(self, i);
// Process each opponent
i := i + 1;
}
See Also: opponent
purse
Signature: integer purse(u: unitptr, coinage: integer)
Checks how many coins of a specific type a character has.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The unit to check |
coinage | integer | The money type (IRON_PIECE, COPPER_PIECE, SILVER_PIECE, etc.) |
Returns: The number of coins of the specified type found.
Example:
if (purse(self, PLATINUM_PIECE) > 10)
exec("say I am loaded with platinum!", self);
atoi
Signature: integer atoi(s: string)
Converts a string to an integer.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | A string containing a number |
Returns: The integer value represented by the string.
Description:
Skips leading whitespace, then parses an optional sign (+/-) followed by digits until a non-digit character. Returns 0 for non-numeric strings (e.g., atoi("sword") returns 0, atoi(" 123abc") returns 123).
Example:
i := atoi("42");
attack_spell
Signature: integer attack_spell(n: integer, caster: unitptr, medium: unitptr, target: unitptr, bonus: integer, effect: string)
Low-level internal spell function for offensive spells.
Parameters:
| Parameter | Type | Description |
|---|---|---|
n | integer | The spell index of the offensive spell (SPL_XXX) |
caster | unitptr | The caster of the spell |
medium | unitptr | The medium with which the spell is cast, might be caster |
target | unitptr | The target of the spell |
bonus | integer | Possible (+) advantage or (-) penalty |
effect | string | Name of a DIL to handle the spell effect, or “” for default |
Returns: The amount of damage given. Return value can be ignored / not assigned.
Description:
This is low-level internal spell stuff used to develop new spells. Do not use unless you know what you are doing and have been allowed to do so by your Admin.
Unlike cast_spell, which performs full spell execution (validation, resistance checks, spell hooks, and running the spell’s implementation), attack_spell skips directly to the damage phase. It calculates damage using the specified spell’s damage chart and applies it immediately.
Key differences from cast_spell:
- No spell validation (target legality, spell type checks)
- No resistance rolls - you provide a direct bonus modifier instead
- No SFB_PRE/SFB_DONE events (DIL spell hooks won’t trigger)
- Does not run the spell’s normal implementation - it IS the damage calculation
When to use: For implementing custom spell-like damage effects (e.g., traps, environmental hazards, or custom spell variants) where you want to use a spell’s damage characteristics without the full casting mechanics. Most zone builders should use cast_spell instead.
If effect is provided, the named DIL program is called to handle the spell effect instead of the default. The effect DIL must take 3 arguments: (medium: unitptr, target: unitptr, hm: integer) where hm is the calculated hit/damage value. See the cast_spell example above for the effect DIL signature.
check_password
Signature: integer check_password(u: unitptr, s: string)
Checks if a string matches a unit’s password.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The unit whose password to check |
s | string | The password string to verify |
Returns: TRUE if the password matches the unit’s password, FALSE otherwise.
Description:
This function checks the string against the unit’s password and returns TRUE if they match.
Example:
if (not check_password(pc, arg))
{
sendtext(arg + " is not " + pc.name + "'s password.", self);
quit;
}
command
Signature: integer command(cmd: string or integer)
Checks if the activator’s command matches the specified command.
Parameters:
| Parameter | Type | Description |
|---|---|---|
cmd | string or integer | Full command name (e.g. “push”, “say”) or CMD_XXX constant |
Returns: TRUE if the activator’s command matches, FALSE otherwise.
Description:
Unlike simple string compares, command() handles partial matches like regular commands do. This allows shorthand notation while ensuring you’re doing what the user wants.
For example:
if ("spook" in cmdstr)- Activates even if cmdstr is “nospook”if (cmdstr == "spook")- Only activates if cmdstr exactly equals “spook”if (command("spook"))- Activates as long as cmdstr doesn’t conflict with “spook”
The command() function receives the same treatment as a regular command, providing ease of use to the user with shorthand notation.
Caveat: If you use a string argument, ensure there are no white-spaces in it. The argument should only consist of letters.
Example:
command("spook"); // Valid
is valid, while:
command("spook him"); // INVALID
is NOT valid. The reason is that cmdstr only contains the FIRST word of the input from the player, so the latter construct could never evaluate to true.
delstr
Signature: integer delstr(filename: string)
Deletes a string file created with savestr().
Parameters:
| Parameter | Type | Description |
|---|---|---|
filename | string | The name of the String file to be deleted |
Returns: TRUE if file was successfully deleted, FALSE otherwise. The return value can be discarded if not checking the result.
Description:
The delstr() function is used to delete files that are used with the loadstr() and savestr() functions. The function will only delete files that each individual Zone has access to, which is set up in the Zonelist file in the VME etc directory.
⚠️ Performance Warning: Disk access is always slow. Use sparingly as excessive disk I/O can cause server delays.
Example:
dilbegin news_del(arg: string /*filename to be deleted*/);
var
ret: integer; /* to hold the return value if deleted or not */
code
{
ret := delstr("news.txt");
if (!ret)
{
log("File not deleted.");
quit;
}
sendtext("News file deleted[&]n", self);
quit; /* DIL delete routine done */
}
dilend
delunit
Signature: integer delunit(filename: string)
Deletes a unit file created with store().
Parameters:
| Parameter | Type | Description |
|---|---|---|
filename | string | The name of the Unit file to be deleted |
Returns: TRUE if file was successfully deleted, FALSE otherwise. The return value can be discarded if not checking the result.
Description:
The delunit() function is used to delete files that are used with the restore() and store() functions.
⚠️ Performance Warning: Disk access is always slow. Use sparingly as excessive disk I/O can cause server delays.
Example:
dilbegin chest_del(arg: string /*filename to be deleted*/);
var
ret: integer; /* to hold the return value if deleted or not */
code
{
ret := delunit("chest.file");
if (!ret)
{
log("File not deleted.");
quit;
}
sendtext("Chest file deleted[&]n", self);
quit; /* DIL delete routine done */
}
dilend
dildestroy
Signature: integer dildestroy(s: string, u: unitptr)
Removes a DIL program from a unit.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | Name of DIL template to delete |
u | unitptr | Unit to remove program from |
Returns: TRUE if a program using the template name was successfully deleted, FALSE otherwise. The return value can be discarded if not checking the result.
dilfind
Signature: integer dilfind(s: string, u: unitptr [, search_frames: integer])
Checks if a DIL program exists on a unit.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | Name of DIL template to find |
u | unitptr | Unit to find program in |
search_frames | integer | (Optional) TRUE to also search nested function call frames, FALSE for top-level only. Defaults to FALSE |
Returns: TRUE if a program using the template name was found, FALSE otherwise.
Description:
By default, dilfind only checks top-level DIL programs that are directly attached to the unit (via dilcopy or zone definition). When a DIL program calls a remote symbolic function (e.g. walk_room@function()), that function executes as a nested frame within the calling program’s stack and is not visible to the default search.
When search_frames is TRUE, dilfind also searches the call stack of each DIL program on the unit. This is useful for detecting whether a symbolic function call is currently in progress on a unit, for example to prevent duplicate invocations:
// Check if walk_room is already running in any DIL's call stack on this NPC
if (dilfind("walk_room@function", self, TRUE))
// Already walking, don't start another
dilgetval
Signature: <type> dilgetval(u: unitptr, template: string, varname: string [, instance: integer])
Retrieves the runtime value of a variable from a DIL program running on a unit.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | Unit containing the DIL program |
template | string | Template name in “name@zone” format |
varname | string | Name of the variable to retrieve |
instance | integer | (Optional) Which instance to access if multiple exist (default: 1) |
Returns: The current value of the variable. You must typecast the result to the expected type.
Description:
This function provides cross-DIL variable access, allowing one DIL program to inspect variables in another running DIL program.
The optional instance parameter handles units with multiple copies of the same DIL template. Instance numbering starts at 1 and matches the order returned by dillist().
Typecasting the Return Value:
Because dilgetval() can return different types depending on the target variable, you must tell the compiler what type you expect by placing a typecast before the function call. A typecast is the type name in parentheses:
// The (integer) typecast tells the compiler to expect an integer
alignment := (integer) dilgetval(npc, "aggressive@monsters", "alignment");
// The (string) typecast tells the compiler to expect a string
msg := (string) dilgetval(npc, "talker@mobiles", "greeting_message");
Available typecasts: (integer), (string), (unitptr), (extraptr), (stringlist), (intlist), (zoneptr), (cmdptr)
For more on typecasting, see Symbolic Function Calls and Typecasting.
Dynamic Inspection:
If you need to discover variable information at runtime rather than accessing a known variable, use dilvars() to get the list of variable names in a template, then dilvartype() to check each variable’s type. The type constants returned by dilvartype() are:
| Constant | Type |
|---|---|
DILV_INT | integer |
DILV_SP | string |
DILV_UP | unitptr |
DILV_ZP | zoneptr |
DILV_SLP | stringlist |
DILV_ILP | intlist |
DILV_EDP | extraptr |
DILV_CP | cmdptr |
Failure Behavior:
If the lookup fails, the function returns a null/empty value and the assignment does nothing - your variable’s existing contents are not overwritten. Failures are logged and occur when:
- Your unit variable is null
- Template or variable name is empty
- Template doesn’t exist
- DIL instance not found on unit
- Variable not found in template
Examples:
// Basic usage - get an integer variable
npc := findunit(self, "guard", FIND_UNIT_WORLD, null);
alignment := (integer) dilgetval(npc, "aggressive@monsters", "alignment");
// Get a string variable
msg := (string) dilgetval(npc, "talker@mobiles", "greeting_message");
// Get a unitptr variable
target := (unitptr) dilgetval(npc, "hunter@mobiles", "current_target");
// Access second instance of same template on a unit
alignment2 := (integer) dilgetval(npc, "aggressive@monsters", "alignment", 2);
This function provides read-only access. Use dilsetval() to modify variables.
See Also: dillist, dilvars, dilvartype, dilsetval, dilfind
dillist
Signature: stringlist dillist(u: unitptr)
Returns a list of all DILs attached to the unit.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The unit to examine |
Returns: A stringlist containing the template names in “name@zone” format. If a template appears multiple times, it will be listed multiple times (once per instance).
Description:
This function enumerates all DIL programs attached to a unit. The order of entries matches the order DILs are stored on the unit, which is relevant when using the instance parameter in dilgetval() and dilsetval().
Examples:
// List all DILs on an NPC
funcs := dillist(npc);
// funcs might be: {"aggressive@monsters", "guard@mobiles", "aggressive@monsters"}
// Check how many DILs are running
log("NPC has " + itoa(length(funcs)) + " DIL programs");
// Count instances of a specific template
count := 0;
i := 0;
while (i < length(funcs))
{
if (funcs.[i] == "aggressive@monsters")
count := count + 1;
i := i + 1;
}
log("Found " + itoa(count) + " instances of aggressive@monsters");
See Also: dilfind, dilvars, dilgetval
dilvars
Signature: stringlist dilvars(template: string)
Returns a list of variable names defined in a DIL template.
Parameters:
| Parameter | Type | Description |
|---|---|---|
template | string | Template name in “name@zone” format |
Returns: A stringlist of variable names in declaration order. Returns an empty list if the template is not found.
Description:
This function inspects a DIL template’s variable definitions without requiring an active instance. This is useful for discovering what variables a template exposes before attempting to access them with dilgetval().
Examples:
// Get variable names for a template (doesn't need a unit)
vars := dilvars("aggressive@monsters");
// vars might be: {"alignment", "target", "last_attack", "attack_count"}
// Check if a variable exists before accessing
if ("alignment" in vars)
{
mobalign := (integer) dilgetval(npc, "aggressive@monsters", "alignment");
}
// List all variables with their types
i := 0;
while (i < length(vars))
{
vtype := dilvartype("aggressive@monsters", vars.[i]);
log(" " + vars.[i] + " : type " + itoa(vtype));
i := i + 1;
}
See Also: dilvartype, dilgetval, dillist
dilvartype
Signature: integer dilvartype(template: string, varname: string)
Returns the type of a variable defined in a DIL template.
Parameters:
| Parameter | Type | Description |
|---|---|---|
template | string | Template name in “name@zone” format |
varname | string | Name of the variable to check |
Returns: An integer constant indicating the variable type, or DILV_NULL if not found.
Type Constants:
| Constant | Value | Type |
|---|---|---|
DILV_NULL | 0 | Not found / null |
DILV_UP | 1 | unitptr |
DILV_SP | 2 | string |
DILV_SLP | 4 | stringlist |
DILV_EDP | 5 | extraptr |
DILV_INT | 6 | integer |
DILV_ILP | 9 | intlist |
DILV_ZP | 10 | zoneptr |
DILV_CP | 11 | cmdptr |
Description:
This function checks the declared type of a variable in a template without requiring an active instance. Use this to verify the expected type before calling dilgetval() or dilsetval().
Examples:
// Check variable type before accessing
vtype := dilvartype("aggressive@monsters", "alignment");
if (vtype == DILV_INT)
{
alignment := (integer) dilgetval(npc, "aggressive@monsters", "alignment");
}
else if (vtype == DILV_NULL)
{
log("Variable 'alignment' not found in template");
}
// Dynamic type handling
vtype := dilvartype("mytemplate@myzone", varname);
if (vtype == DILV_SP)
str_val := (string) dilgetval(unit, "mytemplate@myzone", varname);
else if (vtype == DILV_INT)
int_val := (integer) dilgetval(unit, "mytemplate@myzone", varname);
See Also: dilvars, dilgetval, dilsetval
itoa
Signature: string itoa(i: integer)
Converts an integer to a string.
Parameters:
| Parameter | Type | Description |
|---|---|---|
i | integer | A number to convert |
Returns: A string representation of the number.
Example:
s := itoa(42);
interrupt
Signature: integer interrupt(flags: integer, dilexp, label)
Sets up an interrupt matching specified message classes.
Parameters:
| Parameter | Type | Description |
|---|---|---|
flags | integer | Message classes to match (e.g., SFB_COM or SFB_COM | SFB_MSG) |
dilexp | expression | Expression to evaluate when activated |
label | label | Label to jump to if expression is true |
Returns: An integer index used with clear() to clear the interrupt.
Description:
When the program is activated on either of the specified conditions, the ‘dilexp’ is evaluated. If true, then execution continues at ‘label’, otherwise the next interrupt is checked (if any).
Interrupts are saved (restored) when ‘recall’ is set.
They are not evaluated while inside an ‘external’ DIL call even if it pauses, so be careful with long running DILs such as walking to another location.
Example:
The following program shows, that the owner (self) of the program keeps snoring while sleeping. The on_activation ensures that the program is only activated when the owner is sleeping. However, since the interrupt precede the on_activation, these may still be intercepted before the on_activation. The on_activation is just another type of interrupt that reacts on all flags (without actually setting them so that the program is activated).
When the program receives the message “relief” the snoring stops briefly. As used, “relief” may only be set once.
When the program receives the message “cured”, the snoring stops completely (i.e. the program quits itself).
dilbegin
var
i : integer;
code
{
/* Notice that the sequence in which the interrupts (and the on_activation)
are executed, is quite important: You can be cured at *any* time. The
program will skip if you are not sleeping. If you are sleeping you can
be relieved. */
interrupt(SFB_MSG, argument == "cured", the_end);
on_activation(self.position != POSITION_SLEEPING, skip);
i1 := interrupt(SFB_MSG, argument == "relief", relief);
:loop:
exec("snore", self);
pause;
goto loop;
:relief:
/* Suppose you can only be relieved once, then we must clear interrupt */
clear(i1);
pause;
pause;
goto loop;
:the_end:
/* Person is cured... */
quit;
}
dilend
isaff
Signature: integer isaff(u: unitptr, i: integer)
Checks if a unit is affected by a specific affect.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | A unit to be examined |
i | integer | An affect ID (see ID_* constants) |
Returns: TRUE if unit u is affected by affect id i, FALSE otherwise.
getaffects
Signature: stringlist getaffects(u: unitptr)
Returns all affects currently on a unit.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The unit to examine |
Returns: A stringlist where each entry represents one affect as a comma-separated string with format: "id,applyfi,data0,data1,data2,duration"
Fields in each entry:
| Position | Field | Description |
|---|---|---|
| 0 | id | Affect ID (ID_* constant) |
| 1 | applyfi | Apply function index (TIF_* constant for tick behavior) |
| 2 | data0 | Affect-specific data slot 0 |
| 3 | data1 | Affect-specific data slot 1 |
| 4 | data2 | Affect-specific data slot 2 |
| 5 | duration | Remaining duration in ticks (-1 = permanent) |
Example:
affs := getaffects(ch);
i := 0;
while (i < length(affs))
{
// Parse affect string: "id,applyfi,data0,data1,data2,duration"
parts := split(affs.[i], ",");
aff_id := atoi(parts.[0]);
duration := atoi(parts.[5]);
log("Affect " + itoa(aff_id) + " duration: " + itoa(duration));
i := i + 1;
}
See Also: isaff, addaff, subaff
islight
Signature: integer islight(u: unitptr)
Checks if a unit is illuminated (not dark).
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | Unit to check |
Returns: TRUE if it is light at/inside the unit, FALSE if dark.
Description:
Checks if the total light value at/inside the unit is >= 0. The calculation considers:
- Light inside the unit itself (its
.lightfield - sum of brightness from contents) - Outdoor lighting based on time of day (if the unit is not indoors)
- Light inside the unit’s container (e.g., if a player is in a room, includes the room’s light)
For a room: checks if the room itself is lit. For a character: checks if the character is in a lit environment (can see). For an object: checks if sufficient light reaches the object.
To check if a unit emits light (is a light source), check u.bright > 0 instead.
Note: Previous documentation incorrectly stated this checks if a unit “is a light source”. It actually checks if there is sufficient light at/inside the unit for visibility.
isplayer
Signature: integer isplayer(pcname: string)
Checks if a given name is a player character.
Parameters:
| Parameter | Type | Description |
|---|---|---|
pcname | string | The name of the player being checked |
Returns: TRUE if the name is a player, FALSE otherwise.
Description:
This function is used to find out if a string you pass to it is a player or not. This can be used to find out if a player is truly a player that an Administrator is deleting without having that player online.
Example:
if (not isplayer(arg))
{
sendtext(arg + " is not a character.&n", self);
quit;
}
isset
Signature: integer isset(i: integer, bit: integer)
Checks if specific bits are set in an integer.
Parameters:
| Parameter | Type | Description |
|---|---|---|
i | integer | An integer to examine |
bit | integer | A set of bits to match |
Returns: TRUE if bits in bit are set in i, FALSE otherwise.
Example:
if (isset(self.manipulate, MANIPULATE_TAKE)) ...
paycheck
Signature: integer paycheck(u1: unitptr, u2: unitptr)
Checks if a player has pay access to a zone.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u1 | unitptr | Unit to check against (determines the zone) |
u2 | unitptr | Player to check access for |
Returns: TRUE if player u2 has pay access to the zone where u1 is located. FALSE if the player does not have access. Using non-players as u2 will return TRUE.
findunit
Signature: unitptr findunit(u: unitptr, s: string, i: integer, l: unitptr)
Searches for a unit by name in a specified environment.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The unit the local environment is relative to (typically the searcher) |
s | string | Name of the unit to find |
i | integer | Search environment flags (FIND_UNIT_* constants) |
l | unitptr | Optional list of units to search (use null if using flags) |
Returns: The found unit, or null if nothing found.
Side Effect - String Modification:
When a unit is found, the string parameter s is modified to contain the remainder of the string after the matched name, with leading and trailing spaces stripped. This enables chained findunit calls for parsing multi-word arguments.
Example:
// arg contains "sword guard"
item := findunit(self, arg, FIND_UNIT_INVEN, null);
// After: item points to the sword, arg now contains "guard"
target := findunit(self, arg, FIND_UNIT_SURRO, null);
// After: target points to the guard, arg now contains ""
This pattern is commonly used for commands like give sword guard, pour bottle cup, slip dagger guard, etc.
VME 2.0 Note: In VME 2.0 and earlier,
findunitpreserved a leading space in the remainder (e.g.," guard"instead of"guard"). It now strips this space. Code that checksif (arg==" something")must be updated toif (arg=="something").
Description:
The first argument is typically the character that’s looking for something (e.g., if Mary needs a spoon, she’ll be the first argument).
The second argument is what you’re looking for, represented by a string (e.g., “spoon”).
For the third or fourth argument, you have a choice - use one and set the other to 0 or null. For instance, if you have a pointer to Mary’s kitchen utensil pot:
findunit(mary, "spoon", 0, pot);
Or you can let her look around using flags:
findunit(mary, "spoon", FIND_UNIT_INVEN or FIND_UNIT_SURRO, null);
Note: Using FIND_UNIT_PAY or FIND_UNIT_NOPAY in this function will be ignored.
Search Flags:
| Flag | Description |
|---|---|
FIND_UNIT_EQUIP | Objects visible with ‘equipment’. Assumes first argument is a character |
FIND_UNIT_INVEN | Objects visible with ‘inventory’ or ‘look in bag’ |
FIND_UNIT_SURRO | Objects/characters you can see and interact with in the room |
FIND_UNIT_ZONE | Search within the zone |
FIND_UNIT_WORLD | Search the entire world (starts randomly, not close to searcher) |
FIND_UNIT_IN_ME | Anything inside the first argument |
FIND_UNIT_HERE | Anything here (IN_ME + SURRO) |
FIND_UNIT_GLOBAL | ANYTHING, starting close and working outward |
FIND_UNIT_NOVIS | Ignore visibility restrictions - finds units regardless of invisibility or hidden status |
Note: FIND_UNIT_NOVIS can be OR’d onto the other flags (e.g. FIND_UNIT_INVEN|FIND_UNIT_NOVIS) when a script needs to locate and act on a unit it deliberately hid earlier, rather than modeling what a player could actually perceive.
findunit (with type filter)
Signature: unitptr findunit(u: unitptr, s: string, i: integer, l: unitptr, type: integer)
Searches for a unit by name, with an additional filter to restrict results by unit type.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The unit the local environment is relative to (typically the searcher) |
s | string | Name of the unit to find |
i | integer | Search environment flags (FIND_UNIT_* constants) |
l | unitptr | Optional list of units to search (use null if using flags) |
type | integer | Unit type filter (UNIT_ST_* constants, combined with |) |
Returns: The found unit, or null if nothing found.
Description:
Use UNIT_ST_NPC, UNIT_ST_PC, UNIT_ST_ROOM, or UNIT_ST_OBJ combined with | to specify which types to find. For example, UNIT_ST_PC | UNIT_ST_NPC finds only characters.
findrndunit
Signature: unitptr findrndunit(u: unitptr, sv: integer, uf: integer)
Returns a random unit matching specified criteria.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The unit pointer which the search is relative to |
sv | integer | Search location value (FIND_UNIT_XXX - NOT a bit vector) |
uf | integer | Bit vector of unit type flags to match (UNIT_ST_XXX) |
Returns: A pointer to a random unit, or null if none found.
Description:
This routine returns a random unit. Notice how uf lets you specify exactly what unit types to look for. The sv is not a bit vector, although FIND_UNIT_XXX is usually used as such. If you need to search multiple environments, call the routine once for each.
Using FIND_UNIT_PAY or FIND_UNIT_NOPAY will pick a random player which is also registered as a valid payer (or not). Asking for a room would yield a random room registered to be accessible for paying players only (or not). Asking for objects would return no unit (null).
Example:
u := findrndunit(self, FIND_UNIT_ZONE, UNIT_ST_PC|UNIT_ST_NPC);
findroom
Signature: unitptr findroom(s: string)
Finds a room by its symbolic name.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | Symbolic name of the room |
Returns: A pointer to the room, or null if not found.
Example:
findroom("inn@udgaard")
findzone
Signature: zoneptr findzone(s: string)
Finds a zone by its name.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | The zone name (as declared in %zone) |
Returns: A pointer to the zone, or null if not found.
Description:
Looks up a zone by its exact name. The name must match the zone’s %zone declaration exactly (case-sensitive). Once you have a zoneptr, you can access zone fields like .rooms, .npcs, .objs, .title, etc.
Example:
z := findzone("udgaard");
if (z)
{
log("Zone title: " + z.title);
log("Room count: " + itoa(z.roomcount));
}
See Also: findroom, Zoneptr Fields
findsymbolic (simple)
Signature: unitptr findsymbolic(s: string)
Finds an NPC or Object by its symbolic name (zone-specific reference).
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | Symbolic name of the NPC or Object to find |
Returns: A pointer to an instance of the unit, or null if not found.
Description:
This routine supplements findroom and findunit. It comes in handy if it is important to get a correct reference to a NPC in the world. If for example, Mary needs to send a message to John the Lumberjack, then she should NOT use the findunit() since it may locate a different John - even a player! If she instead locates him using findsymbolic("john@haon_dor") she will be certain that it is in fact her husband, and not the player John Dow from Norway.
Note: It will NOT locate rooms, for the only reason that findroom is a lot more efficient.
Example:
findsymbolic("bread@midgaard")
findsymbolic (by idx)
Signature: unitptr findsymbolic(s: string, idx: integer)
Finds a specific instance of a unit by its symbolic name and .idx value.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | Symbolic name in “zone@name” format |
idx | integer | The .idx value of the specific instance to find |
Returns: A pointer to the specific instance, or null if not found.
Description:
When multiple instances of the same unit exist in the game, this version lets you find a specific instance by its .idx value. You can store a unit’s .idx and later use it to find that exact same unit again.
findsymbolic (advanced)
Signature: unitptr findsymbolic(u: unitptr, s: string, i: integer)
Searches for a unit by symbolic name relative to another unit with search scope control.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | Search is relative to this unit |
s | string | Symbolic name of the NPC or Object to find |
i | integer | FIND_UNIT_XXX bit vector of places to search |
Returns: A pointer to an instance of the unit, or null if not found.
Description:
This routine supplements findroom, findunit and the simple findsymbolic(). It comes in handy if it is important to get a correct reference to a unit somewhere relative to ‘u’. If for example, Mary needs to check if she has her own cooking pot, then she should NOT use the findunit since it may locate a different pot, not belonging to Haon-Dor but to some other zone. If she instead locates it using findsymbolic(self, "pot@haon_dor", FIND_UNIT_IN_ME) she would be certain that it is in fact her own cooking pot that she is carrying around, and not some other pot from a Joe Blow’s zone.
Example:
findsymbolic(self, "bread@midgaard", FIND_UNIT_INVEN)
flog
Signature: integer flog(filename: string, s: string, wa: string)
Writes log messages to custom log files in the log directory.
Parameters:
| Parameter | Type | Description |
|---|---|---|
filename | string | The filename of the file to appear in the log directory |
s | string | The string to be logged |
wa | string | Write mode: "w" (overwrite) or "a" (append) |
Returns: TRUE on success, FALSE on failure. The return value can be discarded if not checking the result.
Description:
The ‘flog’ function allows you to split up your logs in the log directory so that you don’t end up with everything in the main vme.log.
Note: The append/write argument must be in lower case and can only be "w" or "a" surrounded by quotes. If the argument is "w" it will overwrite any log file by that name. If the argument is "a" it will append to the file by that name.
Example:
dilbegin zonelog (s:string);
code
{
flog (self.zonidx+".log",s,"a");
return;
}
dilend
The previous DIL function will work in any zone to log to a file with that zones name - each zone could use it to keep zone logs separate.
getword
Signature: string getword(var s: string)
Extracts and removes the first word from a string.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string (var) | A string with zero or more words separated by space |
Returns: The first word of the string. The return value can be discarded if only removing the first word from the string.
Note: The argument string has the returned word removed (modified in place).
getwords
Signature: stringlist getwords(var s: string)
Splits a string into a list of words.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string (var) | A string with zero or more words separated by space |
Returns: A stringlist where each string was a word in ‘s’.
ghead
Signature: unitptr ghead()
Returns the first unit in the global list.
Returns: The first unit in the global list, which will be the last character to have logged on.
phead
Signature: unitptr phead()
Returns the first PC (player character) in the global unit list.
Returns: The first PC, or null if no players are connected.
Description:
All units in the game exist in a single global linked list, with unit types grouped together in order: PCs first, then NPCs, then objects, then rooms. phead() returns the entry point to the PC group, providing a shortcut when you only need to work with players rather than iterating from ghead() through all units.
Since .gnext traverses the entire global list (not just PCs), iterating past the last PC will continue into the NPC group. To iterate only through PCs, check the type:
Example:
pc := phead();
while (pc and pc.type == UNIT_ST_PC)
{
// Process this PC
pc := pc.gnext;
}
See Also: nhead, ohead, rhead, ghead
nhead
Signature: unitptr nhead()
Returns the first NPC (non-player character) in the global unit list.
Returns: The first NPC, or null if no NPCs exist.
Description:
All units in the game exist in a single global linked list, with unit types grouped together in order: PCs first, then NPCs, then objects, then rooms. nhead() returns the entry point to the NPC group, providing a shortcut when you only need to work with NPCs rather than iterating from ghead() through all units.
Since .gnext traverses the entire global list (not just NPCs), iterating past the last NPC will continue into the object group. To iterate only through NPCs, check the type:
Example:
npc := nhead();
while (npc and npc.type == UNIT_ST_NPC)
{
// Process this NPC
npc := npc.gnext;
}
See Also: phead, ohead, rhead, ghead
ohead
Signature: unitptr ohead()
Returns the first object in the global unit list.
Returns: The first object, or null if no objects exist.
Description:
All units in the game exist in a single global linked list, with unit types grouped together in order: PCs first, then NPCs, then objects, then rooms. ohead() returns the entry point to the object group, providing a shortcut when you only need to work with objects rather than iterating from ghead() through all units.
Since .gnext traverses the entire global list (not just objects), iterating past the last object will continue into the room group. To iterate only through objects, check the type:
Example:
obj := ohead();
while (obj and obj.type == UNIT_ST_OBJ)
{
// Process this object
obj := obj.gnext;
}
See Also: phead, nhead, rhead, ghead
rhead
Signature: unitptr rhead()
Returns the first room in the global unit list.
Returns: The first room, or null if no rooms exist.
Description:
All units in the game exist in a single global linked list, with unit types grouped together in order: PCs first, then NPCs, then objects, then rooms. rhead() returns the entry point to the room group, providing a shortcut when you only need to work with rooms rather than iterating from ghead() through all units.
Since .gnext traverses the entire global list, iterating past the last room will reach null (end of list). To iterate only through rooms, check the type:
Example:
room := rhead();
while (room and room.type == UNIT_ST_ROOM)
{
// Process this room
room := room.gnext;
}
See Also: phead, nhead, ohead, ghead
split
Signature: stringlist split(var s: string, var t: string)
Splits a string into parts using a custom separator.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string (var) | A string with zero or more words separated by separator t |
t | string (var) | The separator string |
Returns: A stringlist where each string was a word in ‘s’ separated by string ‘t’.
Note: You can use &x to split a string by line. This is very useful when reading in files with loadstr.
left
Signature: string left(o: string, l: integer)
Returns the leftmost characters from a string.
Parameters:
| Parameter | Type | Description |
|---|---|---|
o | string | The original string to be parsed |
l | integer | The number of characters to extract |
Returns: The left portion of the string with length l.
Description:
This function parses the string passed to it and returns the number of characters defined in its second argument.
Example:
"short" := left("shorten me", 5);
Example:
dilbegin aware describe (arg:string);
var
side:string;
oneword:stringlist;
location:string;
ln:integer;
args:stringlist;
temp:string;
i:integer;
x:extraptr;
code
{
if (self.type != UNIT_ST_PC)
quit;
if (self.position < POSITION_SLEEPING)
{
act ("Recover first and then you can describe your body parts.",
A_ALWAYS,self,null,null,TO_CHAR);
quit;
}
args := getwords(arg);
ln := length(args);
if ((ln < 1) or (ln > 2))
{
sendtext ("No such location to describe.",self);
quit;
}
else if (ln > 1)
goto two_word;
:one_word:
if ((arg == left("help", length(arg))) or
(arg == ""))
goto hlp_dscr;
oneword := {"arms","butt","ears","eyes","face","feet",
"general","hair","hands","head","legs",
"mouth","neck","nose","nostrils","teeth",
"toes","tongue"};
i := 0;
ln := length(args.[0]);
temp := "ERROR";
while (i < 18)
{
if (args.[0] == left(oneword.[i], ln))
{
temp := oneword.[i];
break;
}
i := i + 1;
}
if (temp == "ERROR")
{
sendtext ("No such location to describe.",self);
quit;
}
goto describe;
:two_word:
oneword := {"arm","leg","foot","hand","eye","ear"};
temp := "ERROR";
ln := length(args.[0]);
if (args.[0] == left("left", ln))
side := "left";
else if (args.[0] == left("right", ln))
side := "right";
else
{
sendtext ("No such location to describe.",self);
quit;
}
i := 0;
while (i < 6)
{
if (args.[1]==left(oneword.[i],ln))
{
temp := oneword.[i];
break;
}
i := i + 1;
}
if (temp == "ERROR")
{
sendtext ("No such location to describe.",self);
quit;
}
temp := side+" "+temp;
:describe:
if (temp == "General")
location := "";
else
location := temp;
x := location in self.extra;
if (x != null)
if (location == "")
sendtext("your Current description for your body is: &n"+x.descr+"&n",self);
else
sendtext("your Current description for your "+location+"is: &n"+x.descr+"&n",self);
if (location == "")
sendtext ("Enter a text you would like others to see when they look at your body.&n",self);
else
sendtext ("Enter a text you would like others to see when they look at your "+location+".&n",self);
beginedit (self);
wait(SFB_EDIT,self==activator) ;
temp := textformat(argument);
oneword := {""};
subextra(self.extra,location);
addstring (oneword, location);
addextra (self.extra,oneword,temp);
sendtext ("Description added.&n",self);
quit;
:hlp_dscr:
sendtext ("&nCorrect usage of 'describe':&n&n",self);
sendtext ("describe <position>&n&n",self);
sendtext("<position> being one of the following:&n&n",self);
sendtext( "arms butt ears eyes&n"+
"face feet General hair&n"+
"hands head left arm left leg&n"+
"left foot left hand left eye left ear&n"+
"legs mouth neck nose&n"+
"nostrils right arm right leg right foot&n"+
"right hand right eye right ear teeth&n"+
"toes tongue&n&n",self);
sendtext ("Example: &n&n",self);
sendtext ("describe left leg&n",self);
quit;
}
dilend
length
Signature: integer length(a: string or stringlist)
Returns the length of a string or stringlist.
Parameters:
| Parameter | Type | Description |
|---|---|---|
a | string or stringlist | A string or stringlist to examine |
Returns: The length of the string in characters, or the number of strings in a list.
load
Signature: unitptr load(s: string)
Loads a unit by symbolic name and places it in the loading object.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | Symbolic name of unit |
Returns: A pointer to the unit, or null if loading fails. The return value can be discarded if only needing the side effect of loading the unit into self.
Description:
The loaded unit is automatically placed inside the object which loaded it. Use for example the link command to move it into other units.
Example:
load("garlic@midgaard")
clone
Signature: unitptr clone(u: unitptr)
Creates a copy of an existing unit and places it inside the unit running the DIL.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The unit to clone |
Returns: A pointer to the cloned unit, or null if cloning fails. The return value can be discarded if only needing the side effect of creating the clone.
Description:
The clone() function creates a complete copy of an existing unit, including all its properties, extras, and affects. The cloned unit is automatically placed inside the unit running the DIL. Use the link command to move it elsewhere. There may be minor variations between a cloned unit and one that has been stored and restored.
Example:
u := clone(item);
link(u, activator);
loadstr
Signature: integer loadstr(filename: string, buff: string)
Loads a string from disk into a buffer.
Parameters:
| Parameter | Type | Description |
|---|---|---|
filename | string | The name of the string file to be loaded |
buff | string | The string that you wish to read the file contents into |
Returns:
| Value | Meaning |
|---|---|
FILE_LOADED | File loaded successfully |
FILE_NOT_FOUND | File does not exist |
FILE_OUT_OF_MEMORY | Insufficient memory |
FILE_TO_LARGE | File is too large |
Description:
Loadstr is used to load strings from disk that were saved either by savestr or any text editor. The ‘loadstr’ is perfect for operations such as on-line edited newspapers, lotteries where tickets are sold to players, creating smarter NPCs that can remember through reboots who they are hunting, DIL-based teachers, message boards, mail system, news command, zone or room based help, competition boards, and much more.
⚠️ Performance Warning: Disk access is always slow. Keep file sizes to a minimum for quick loading. Excessive disk I/O can cause serious server delays.
Example:
dilbegin news_load();
var
ret:integer; /* to hold the return value if loaded or not */
buff:string; /* to hold the loaded string */
code
{
ret := loadstr("news.txt", buff);
if (!ret)
{
log("File not read.");
quit;
}
sendtext(buff+"[&]n", self);
quit; /* DIL load routine done destroy self. */
}
dilend
on_activation
Signature: integer on_activation(dilexp, label)
Sets up an interrupt that is executed before every activation of the DIL program.
Parameters:
| Parameter | Type | Description |
|---|---|---|
dilexp | expression | A boolean DIL expression |
label | label | Label to jump to - OR the reserved keyword SKIP |
Returns: The index to the interrupt handling the on_activation.
Description:
Sets up an interrupt that is executed before every activation of the DIL program. This is for example useful to catch situations where your NPC has fallen asleep or is injured.
If ‘dilexp’ evaluates to TRUE then the program jumps to ‘label’. If ‘label’ is ‘skip’ then the program is simply not activated.
When the on_activation evaluates to true, and jumps to a label other than skip, the condition is automatically cleared. If the dilexp evaluates to false, or if the label is skip, the activation remains active. Use the clear() to remove the on_activation.
Example:
on_activation(self.position <= POSITION_SLEEPING, skip);
or
on_activation(self.position > POSITION_SLEEPING, let_me_sleep);
openroll
Signature: integer openroll(size: integer, end: integer)
Performs an open-ended dice roll where extreme results trigger additional rolls.
Parameters:
| Parameter | Type | Description |
|---|---|---|
size | integer | The size of the die (e.g., 100 for a d100) |
end | integer | How many values at each extreme keep the roll open |
Returns: An integer result that can exceed the die size (positive or negative).
Description:
An open-ended roll simulates lucky and unlucky streaks. When you roll extremely high, bonus rolls are added to your total. When you roll extremely low, penalty rolls are subtracted.
For openroll(100, 5):
- Rolling 96-100 (top 5): add another d100, keep adding while rolling high
- Rolling 1-5 (bottom 5): subtract another d100, keep subtracting while rolling high
- Rolling 6-95: return that value directly
Most results fall in the normal 1-100 range. Occasionally you’ll see results above 100 (lucky streak) or negative values (unlucky streak).
Example:
hm := openroll(100, 5);
/* Possible results:
42 - rolled 42, returned directly
73 - rolled 73, returned directly
156 - rolled 98, then 58, total 156
210 - rolled 99, then 96, then 15, total 210
-47 - rolled 3, then 50, total -47
-183 - rolled 2, then 97, then 88, total -183
*/
See Also: rnd() for simple random numbers.
pathto
Signature: integer pathto(from: unitptr, to: unitptr)
Calculates the direction from one unit to another.
Parameters:
| Parameter | Type | Description |
|---|---|---|
from | unitptr | The unit which the path is taken from |
to | unitptr | The unit which the path is taken to |
Returns: DIR_XXX from values.h and/or vme.h.
Example:
i := pathto(self, findroom("inn@midgaard"));
right
Signature: string right(o: string, r: integer)
Returns the rightmost characters from a string.
Parameters:
| Parameter | Type | Description |
|---|---|---|
o | string | The original string to be parsed |
r | integer | The number of characters to extract |
Returns: The right portion of the string with length r.
Description:
This function parses the string passed to it and returns the number of characters from the right defined in its second argument.
Example:
"Easy" := right("This is Easy", 4);
rnd
Signature: integer rnd(i1: integer, i2: integer)
Returns a random integer in a specified range.
Parameters:
| Parameter | Type | Description |
|---|---|---|
i1 | integer | Start of range |
i2 | integer | End of range |
Returns: A random integer in an interval from ‘i1’ to ‘i2’.
Example:
i := rnd(1, 10);
Built-In Procedures
DIL features some built-in procedures that allows you increased control over in-game data structures and event handling. One such procedure (used above) is ‘exec()’. The inline procedures are used as any other procedure by typing its name, followed by a list of argument expression enclosed in parentheses. The return types of the expressions used for built-in procedure calls are checked by the compiler.
DIL also lets you call templates defined in the current or other zones. The naming of templates follows the normal naming convention for zone, using the ‘name@zone’ as a symbolic name for a procedure. Before being able to use external procedures, you must define their name and type in a separate ‘external’ section in the template. The declaration in the ‘external’ section should match the original declaration of the referenced program. You can define the number and type of arguments the template take, by listing them inside the declaration parenthesis (as well as in the original declaration of that template) (see example below)
Example:
dilbegin bar(s:string);
code
{
exec("say "+s,self);
return;
}
dilend
dilbegin foo();
external
someproc@hades1();
bar(s:string);
code
{
someproc@hades1();
bar("Hello "+activator.name);
pause;
}
dilend
When the procedure is called, the argument expressions are calculated, and passed to the template.
For information on calling procedures and functions dynamically via string variables, see Symbolic Function Calls and Typecasting.
The following are definitions and documentation for the built-in procedures in DIL. The definitions are not definitions ‘as such’, but serve to distinguish arguments in the documentation below.
set_weight
Signature: void set_weight(u: unitptr, weight: integer)
Sets the current (transient) weight of a unit.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The unit to set the weight for |
weight | integer | The new current (transient) weight value |
Description:
Sets the current weight of a unit. This is a transient value that may change as the unit’s contents change.
Note: In Diku3, this replaces the deprecated setweight() procedure.
set_weight_base
Signature: void set_weight_base(u: unitptr, weight: integer)
Sets the base (sustained) weight of a unit.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The unit to set the base weight for |
weight | integer | The new base (sustained) weight value |
Description:
Sets the base weight of a unit. This is a sustained value representing the empty weight of the unit, which persists regardless of contents.
Example:
/* Set base weight of container to 5 pounds */
set_weight_base(chest, 5);
/* Set current weight (including contents) */
set_weight(chest, 25);
follow
Signature: void follow(f: unitptr, m: unitptr)
Makes one character follow another unconditionally, or stops following.
Parameters:
| Parameter | Type | Description |
|---|---|---|
f | unitptr | The character to follow |
m | unitptr | The character to be followed, or null to stop following |
Description:
Unconditionally makes ‘f’ follow ‘m’, even if ‘f’ is mortally wounded, sleeping, fighting or whatever.
To make ‘f’ stop following their current master, pass null as the second parameter. This properly removes ‘f’ from their master’s follower list.
Example:
// Make an NPC follow a player
follow(self, pc);
// Stop following
follow(self, null);
getfollower
Signature: unitptr getfollower(u: unitptr, idx: integer)
Returns a specific follower from a character’s follower list by index.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The character whose followers to check |
idx | integer | Zero-based index into the follower list |
Returns: The follower at the specified index, or null if the unit is not a character, has no followers, or the index is out of range.
Description:
A character may have multiple followers. This function retrieves a specific follower by index (0 = first follower). Use with .followercount to iterate through all followers.
Example:
i := 0;
while (i < self.followercount)
{
fol := getfollower(self, i);
// Process each follower
i := i + 1;
}
See Also: follow
logcrime
Signature: void logcrime(c: unitptr, v: unitptr, t: integer)
Registers a crime in the game’s crime system.
Parameters:
| Parameter | Type | Description |
|---|---|---|
c | unitptr | The criminal (main suspect) |
v | unitptr | The victim |
t | integer | The crime type (CRIME_XXX) |
Description:
Registers a crime committed against ‘v’ by ‘c’. Use the CRIME_XXX values from values.h and/or vme.h as the ‘t’ argument. The logcrime routine automatically registers group members of the criminal, etc. In stuff like steal, remember to make the criminal visible if he fails his attempts.
acc_modify
Signature: void acc_modify(u: unitptr, i: integer)
Modifies a player’s account balance (admin only).
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | A player |
i | integer | An amount in 1/100s, for example 100 would equal $1.00, and -100 would equal -$1.00 |
Warning:
Access only allowed with ‘root’ access and all transactions are registered in the specially encrypted account log file. Use with great caution, and always test thoroughly before using. Use without prior permission may cause deletion of god / zone.
strdir
Signature: stringlist strdir(match: string)
Returns a list of filenames matching a wildcard pattern.
Parameters:
| Parameter | Type | Description |
|---|---|---|
match | string | The wildcard file pattern you want to match, or ‘*’ for all |
Returns: A stringlist with all the filenames that match the ‘match’ argument.
Description:
The ‘match’ argument uses the same wildcards as the Linux ‘ls’ command:
| Pattern | Description |
|---|---|
* | Match any character or group of characters |
? | Match one of any character |
[...] | Match one of a set of characters |
Wildcard Examples:
"corpse*" matches: corpse.10938 corpse.whistler corpseofwhistler ...
"corpse?" matches corpse1 corpses corpse5 ...
"[abc]*" matches ability about cost back ...
"[a-z]*" about zoo man father ...
"start[nm]end" matches startnend startmend
Example:
dilbegin wanted();
var
wantedlist:stringlist;
templist:stringlist;
i:integer;
ln:integer;
code
{
wantedlist := strdir("dead*");
i := 0;
ln := length(wantedlist);
while (i < ln)
{
templist := split(wantedlist.[i], ".");
sendtext(templist.[1]+" wanted dead!&n", self);
i := i + 1;
}
quit;
}
dilend
The previous DIL would be an example of a command to check the wanted dead players on the VME if you saved the files with the first word being ‘dead’ and separated it with a ‘.’ and the players name. For example if ‘whistler’ was wanted dead the file name would be ‘dead.whistler’
set_password
Signature: void set_password(u: unitptr, s: string)
Sets the password for a player character.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The unit that you want to set the password of |
s | string | The password you are using to set |
Description:
This function sets a unit password - it only works on player characters of course.
Example:
dilbegin aware do_password(arg:string);
var
prmt:string;
firstpwd:string;
i:integer;
tlist:stringlist;
code
{
if (self.type != UNIT_ST_PC)
quit;
arg := "";
prmt := self.prompt;
self.prompt := "Enter new password: ";
wait(SFB_CMD, self==activator);
block;
tlist := getwords(excmdstr);
if (length(tlist) > 1)
{
sendtext("Password must be only one word. Try again.&n", self);
self.prompt := prmt;
quit;
}
if (length(excmdstr) < 5)
{
sendtext("Password to short. Password must be 5 characters or longer. Try again.&n", self);
self.prompt := prmt;
quit;
}
if (length(excmdstr) > 16)
{
sendtext("Password to long. Try again.&n", self);
self.prompt := prmt;
quit;
}
firstpwd := excmdstr;
self.prompt := "Enter password again: ";
wait(SFB_CMD, self==activator);
block;
if (excmdstr != firstpwd)
{
sendtext("Passwords do not match try again.&n", self);
self.prompt := prmt;
quit;
}
set_password(self, excmdstr);
sendtext("Changed your Password to '"+excmdstr+"' Please write it down!&n", self);
self.prompt := prmt;
quit;
}
dilend
store
Signature: void store(u: unitptr, filename: string, container: integer)
Saves a unit and optionally its contents to disk.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The Unit that has the contents to be stored or is to be stored |
filename | string | The name of the file you want to store the Units to |
container | integer | Do you want to save the container? TRUE for yes, FALSE for no |
Description:
Store saves a copy of a unit or units. If the container value is TRUE, everything inside including the container itself will be saved. If the container argument is FALSE, only the contents of the object will be saved. You will want to use the TRUE value when saving something like a Clan chest that has items inside to store and has extras on the chest that you also wish to keep. The FALSE value for container would be good for temporary storage of PC inventory or for storing room contents.
The ‘store’ and ‘restore’ are perfect for operations such as mud mailing objects from player to player, storage devices for players that will keep inventory through a reboot. Even the ability to save a player’s inventory while they fight in an arena and restore it to them undamaged when finished. Finally it could be used to save a donation room through reboots since it can be used on a room to store the contents of a room - any NPC or objects in the room would be saved through reboot.
⚠️ Performance Warning: Disk access is always slow. If you use store() on a continuous basis, do so ONLY when needed and at amply spaced intervals. Excessive disk I/O can cause serious server delays. Note: Items in player inventories are automatically saved.
Example 1:
dilbegin save_contents();
code
{
:start:
wait(SFB_CMD, command("store")); /* wait for the store command */
block;
store("chest." + self.zoneidx, self, FALSE); /* store everything inside self. */
goto start;
}
dilend
Example 2:
dilbegin save_container_n_contents();
code
{
:start:
wait(SFB_CMD, command("store")); /* wait for the store command */
block;
store("chest." + self.zoneidx, self, TRUE); /* store everything inside self and self. */
goto start;
}
dilend
pagestring
Signature: void pagestring(buff: string, u: unitptr)
Displays a paged string to a player (press enter to continue).
Parameters:
| Parameter | Type | Description |
|---|---|---|
buff | string | The string that is to be paged to the player |
u | unitptr | The unitptr the buff is to be paged for |
Description:
This formats the buff string to the player so the buff text doesn’t scroll off the screen until after the player presses enter.
Example:
pagestring(buff, self);
position_update
Signature: void position_update(u: unitptr)
Updates a character’s position based on current hitpoints.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | A pointer to a player or a monster |
Description:
The character will be updated and perhaps killed, incapacitated, mortally wounded, revived, etc. depending on current hitpoints. Useful when tampering with the ‘hp’ field.
Example:
pc.hp := pc.hp - 100;
position_update(pc);
send_done
Signature: void send_done(c: string, a: unitptr, m: unitptr, t: unitptr, p: integer, arg: string, o: unitptr [, cmd_auto: integer])
Sends the SFB_DONE message to DIL programs waiting for command completion.
Parameters:
| Parameter | Type | Description |
|---|---|---|
c | string | The command string that is sending the message |
a | unitptr | The unitptr (activator) that activated the message |
m | unitptr | The unitptr (medium) that the DIL is acting through |
t | unitptr | The unitptr (target) the DIL is acting on |
p | integer | The power of the message |
arg | string | The argument sent with the message |
o | unitptr | The unitptr (other) you also want the message to go to |
cmd_auto | integer | Optional. The CMD_AUTO_XXX type. Defaults to CMD_AUTO_NONE (0) if omitted |
Description:
This sends the ‘SFB_DONE’ message to any DIL programs that are waiting for it in the surrounding area and to the other pointer if not null. The following is just one example - you can find many more in commands.zon.
Example:
dilbegin do_read (arg:string);
var
brdname:string;
i:integer;
u:unitptr;
x:extraptr;
ln:integer;
temp:string;
templist:stringlist;
buff:string;
f_name:string;
act_str:string;
code
{
i := atoi (arg);
if (i < 0)
{
exec ("look " + arg, self);
goto read_quit;
}
if (itoa (i) != arg)
{
exec ("look " + arg, self);
goto read_quit;
}
u := self.outside.inside;
while (u != null)
{
if ((u.type == UNIT_ST_OBJ) and (u.objecttype == ITEM_BOARD))
break;
u := u.next;
}
if (u == null)
{
act ("You do not see that here.", A_ALWAYS, self, null, null, TO_CHAR);
quit;
}
if (u.extra.["$BOARD_L_RES"].descr != "")
{
act_str := u.extra.["$BOARD_L_RES"].descr(self, u);
if (act_str != "")
{
act (act_str, A_ALWAYS, self, null, null, TO_CHAR);
quit;
}
}
brdname := u.names.[length (u.names) - 1];
i := loadstr (brdname + ".idx", temp);
if (i <= 0)
{
act ("But the board is empty!", A_ALWAYS, self, null, null, TO_CHAR);
goto read_quit;
}
templist := split(temp, "&x");
ln := length (templist);
x := "$BOARD_MAX" in self.extra;
if ((atoi(arg) > atoi(x.descr)) or (atoi(arg) > ln))
{
act("That message exists only within the boundaries of your mind.",
A_ALWAYS, self, null, null, TO_CHAR);
goto read_quit;
}
i := atoi(arg);
temp := templist.[i - 1];
f_name := getword(temp);
i := loadstr (brdname + "." + f_name, buff);
if (i == 0)
{
sendtext("You have to let the poster finish the post before reading it.", self);
quit;
}
if (i < 1)
{
log("05: Error when loading board info.");
act ("This board is not working report to an Administrator",
A_ALWAYS, self, null, null, TO_CHAR);
quit;
}
templist := split(f_name, ".");
if (length(templist) < 2)
act ("Message " + arg + ": " + temp,
A_ALWAYS, self, null, null, TO_CHAR);
else
act ("Message " + arg + ": Re: " + temp,
A_ALWAYS, self, null, null, TO_CHAR);
pagestring(buff, self);
:read_quit:
send_done("read", self, null, u, 0, arg, null, CMD_AUTO_NONE);
quit;
}
dilend
send_pre
Signature: integer send_pre(c: string, a: unitptr, m: unitptr, t: unitptr, p: integer, arg: string, o: unitptr)
Sends the SFB_PRE message and checks if the command is blocked.
Parameters:
| Parameter | Type | Description |
|---|---|---|
c | string | The command string that is sending the message |
a | unitptr | The unitptr (activator) that activated the message |
m | unitptr | The unitptr (medium) that the DIL is acting through |
t | unitptr | The unitptr (target) the DIL is acting on |
p | integer | The power of the message |
arg | string | The argument sent with the message |
o | unitptr | The unitptr (other) you also want the message to go to |
Returns: Either SFR_SHARE or SFR_BLOCK.
Description:
New Function send_pre(…) takes same arguments as send_done but returns either SFR_SHARE or SFR_BLOCK. If the command is blocked by another special or DIL, then SFR_BLOCK will be returned, and you should quit your DIL.
Example:
dilbegin cmdtst(arg: string);
var
i : integer;
code
{
i := send_pre("cmdtest", self, null, null, 0, argument, null);
if (i == SFR_BLOCK)
{
quit;
}
sendtext("No one blocked me!&n", self);
quit;
}
dilend
dilbegin pretest();
code
{
:loop:
wait(SFB_PRE, command("cmdtest"));
block;
act("hahahaha I blocked your cmdtest command",
A_SOMEONE, activator, medium, null, TO_ALL);
goto loop;
}
dilend
set
Signature: void set(var i: integer, bit: integer)
Sets specified bits in an integer variable.
Parameters:
| Parameter | Type | Description |
|---|---|---|
i | integer (var) | Integer variable to alter |
bit | integer | Bits to set in integer variable |
Description:
Sets bits in ‘i’.
unset
Signature: void unset(var i: integer, bit: integer)
Unsets specified bits in an integer variable.
Parameters:
| Parameter | Type | Description |
|---|---|---|
i | integer (var) | Integer variable to alter |
bit | integer | Bits to unset in integer variable |
Description:
Unsets bits in ‘i’.
addcolor
Signature: integer addcolor(ch: unitptr, ks: string, cstr: string)
Adds a custom color to a player’s personal color list.
Parameters:
| Parameter | Type | Description |
|---|---|---|
ch | unitptr | Character you are adding the color to (must be a PC) |
ks | string | Key string for the color (max 20 chars, alphanumeric + underscore) |
cstr | string | Color string |
Returns: TRUE (1) if the color was successfully added, FALSE (0) if the key already exists. The return value can be discarded if not checking the result.
Description:
This function allows your DIL programs to create and add their own personal colors to a player’s color list. That way you can actually make an act in a color that the player chooses or you yourself choose. If the color key already exists, the function returns FALSE and does not overwrite the existing color - use changecolor to modify existing colors.
Example:
addcolor(pc, "clan_who", "&c+w&bn");
delcolor
Signature: integer delcolor(ch: unitptr, ks: string)
Deletes a color from a player’s personal color list.
Parameters:
| Parameter | Type | Description |
|---|---|---|
ch | unitptr | Character you are deleting the color from (must be a PC) |
ks | string | Key string for the color (max 20 chars, alphanumeric + underscore) |
Returns: TRUE (1) if the color was successfully deleted, FALSE (0) if the key did not exist. The return value can be discarded if not checking the result.
Description:
This function is used to delete any colors from a player’s personal color list.
Example:
delcolor(pc, "clan_who");
getcolor
Signature: string getcolor(ch: unitptr, ks: string)
Retrieves a color from a player’s personal color list.
Parameters:
| Parameter | Type | Description |
|---|---|---|
ch | unitptr | Character you are getting the color from (must be a PC) |
ks | string | Key string for the color (max 20 chars, alphanumeric + underscore) |
Returns: Returns the player’s stored color as internal control codes. Returns an empty string if the color key is not found.
Description:
This function retrieves a color from a player’s personal color list. The returned string contains internal escape sequences used by the MUD to display colors, not the human-readable color tags (like &c+r) that were originally entered. These escape sequences are non-printable characters that can be used directly in text output to display the player’s custom color.
Note: The returned string can be reused in output but is not human-readable. If you need to check whether a color exists, test for an empty string.
Example:
// Get the player's clan color and use it in output
mycolor := getcolor(pc, "clan_who");
if (mycolor != "")
sendtext("Clan members: " + mycolor + "Alice, Bob, Carol&n", pc);
changecolor
Signature: integer changecolor(ch: unitptr, ks: string, cstr: string)
Changes an existing color in a player’s personal color list.
Parameters:
| Parameter | Type | Description |
|---|---|---|
ch | unitptr | Character you are changing the color on (must be a PC) |
ks | string | Key string for the color (max 20 chars, alphanumeric + underscore) |
cstr | string | Color string |
Returns: TRUE (1) if the color was successfully changed, FALSE (0) if the key did not exist. The return value can be discarded if not checking the result.
Description:
This will change a color in a player’s personal list. If the color key does not exist, the function returns FALSE - use addcolor to create new colors.
Example:
changecolor(pc, "clan_who", "&c+w&bn");
gamestate
Signature: void gamestate(u: unitptr, gs: integer)
Changes the game state of a character.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The unit to change the game state for |
gs | integer | The game state (use GS_ defines from vme.h) |
Description:
Change the gamestate of a unitptr, uses the GS_ defines from the vme.h. This is used on the log on menu of the VME 2.0 release, which is shown here as an example, but it can also be used in a command. When used it removes the char from playing so be aware that players could use this to hide if you run a player killing style mud.
Example:
dilbegin informer();
var
tgt : unitptr;
code
{
heartbeat := PULSE_SEC;
tgt := ghead();
while (tgt.type == UNIT_ST_PC)
{
if ((isset(tgt.pcflags, PC_INFORM)) and (tgt != self))
{
if (visible(tgt, self))
{
if (self.outside == tgt.outside)
sendtext(self.name + " has arrived.&n", tgt);
else
sendtext(self.name + " has entered the world.&n", tgt);
}
}
tgt := tgt.gnext;
}
return;
}
dilend
dilbegin aware on_connect();
external
informer();
login_modify(tgt : unitptr);
var
wizlvl : integer;
i : integer;
err : integer;
motd : string;
welcome : string;
goodbye : string;
code
{
heartbeat := PULSE_SEC;
if (dilfind("do_quit@commands", self))
i := dildestroy("do_quit@commands", self);
err := loadstr("motd", motd);
if (err > 0)
{
motd := textformat(motd);
sendtext(motd + "&n&n", self);
}
err := loadstr("welcome", welcome);
if (welcome)
welcome := textformat(welcome);
if (self.level < 200)
{
login_modify(self);
dilcopy ("clan_delete@clans", self);
dilcopy ("clan_clear@clans", self);
if (err > 0)
sendtext("&n" + welcome + "&n&n", self);
informer();
exec("look", self);
quit;
}
gamestate(self, GS_MENU);
:wiz_menu:
sendtext("Welcome to Valhalla&n&n", self);
sendtext("1) Enter Valhalla&n", self);
sendtext("W) Change Wizinv level&n [&c+g" + itoa(self.minv) + "&[default]]&n", self);
sendtext("0) Exit Valhalla&n&n", self);
sendtext("Make your choice: ", self);
wait(SFB_CMD, TRUE);
if (command("1"))
{
gamestate(self, GS_PLAY);
if (err > 0)
sendtext("&n" + welcome + "&n&n", self);
informer();
exec("look", self);
quit;
}
else if (command("0"))
{
err := loadstr("goodbye", goodbye);
if (err > 0)
{
goodbye := textformat(goodbye);
sendtext(goodbye, self);
}
destroy(self);
quit;
}
else if (command("w"))
{
sendtext("Enter new WizInv Level: ", self);
wait(SFB_CMD, TRUE);
wizlvl := atoi(cmdstr);
if (wizlvl > self.level)
wizlvl := self.level;
self.minv := wizlvl;
}
else
{
sendtext("Invalid Selection&n&n", self);
goto wiz_menu;
}
}
dilend
Look in vme.h for the possible values you can send to the menu function.
addextra
Signature: void addextra(var e: extraptr, l: stringlist, s: string [, v: intlist])
Adds an extra description to a list of extra descriptions.
Parameters:
| Parameter | Type | Description |
|---|---|---|
e | extraptr (var) | Extra description list to add element to |
l | stringlist | Stringlist for the .names field |
s | string | String for the .descr field |
v | intlist | (Optional) Intlist for the .vals field |
Caveat: You cannot use an extraptr variable as the e argument, but you may use the unit’s extra field directly:
addextra(self.quests, {"Bozo's Quest"}, "");
addstring
Signature: void addstring(var l: stringlist, s: string)
Appends a string to a stringlist.
Parameters:
| Parameter | Type | Description |
|---|---|---|
l | stringlist (var) | Stringlist to append to |
s | string | String to append |
Description:
Appends a string s to the end of stringlist l.
See Also: addint, insert, remove
addint
Signature: void addint(var l: intlist, n: integer)
Appends an integer to an intlist.
Parameters:
| Parameter | Type | Description |
|---|---|---|
l | intlist (var) | Intlist to append to |
n | integer | Integer to append |
Description:
Appends an integer n to the end of intlist l. This is the intlist equivalent of addstring().
Example:
var
mylist : intlist;
code
{
mylist := {};
addint(mylist, 10);
addint(mylist, 20);
addint(mylist, 30);
// mylist is now {10, 20, 30}
}
See Also: addstring, insert, remove
subextra
Signature: void subextra(var e: extraptr, s: string)
Removes an extra description from a list.
Parameters:
| Parameter | Type | Description |
|---|---|---|
e | extraptr (var) | Extra description list to remove element from |
s | string | String matching .names field in element |
Description:
Removes first extra description from list with matching name.
Caveat: You cannot use an extraptr variable as the e argument, but you may continue to use the following form:
subextra(self.quests, "Bozo's Quest");
substring
Signature: void substring(var l: stringlist, s: string)
Removes a string from a stringlist.
Parameters:
| Parameter | Type | Description |
|---|---|---|
l | stringlist (var) | Stringlist to remove string from |
s | string | String to remove |
Description:
Removes string ‘s’ from stringlist ‘l’.
stop_fighting
Signature: void stop_fighting(ch: unitptr, vict: unitptr)
Cancels combat for a character.
Parameters:
| Parameter | Type | Description |
|---|---|---|
ch | unitptr | Person you are stopping the fighting for |
vict | unitptr | Person you are removing from the fighting, or null for everyone |
Description:
This function can be used to cancel combat in a room or with two people. The following example copied to a player will stop any fight the player is in.
Example:
dilbegin stop_combat();
code
{
stop_fighting(self, null);
quit;
}
dilend
subaff
Signature: void subaff(u: unitptr, i: integer)
Removes an affect from a unit.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | Unit to remove affect from |
i | integer | Affect id to remove, see ID_* in values.h and/or vme.h |
Description:
Removes first affect at ‘u’ with matching id ‘i’.
addaff
Signature: void addaff(u: unitptr, id: integer, duration: integer, beat: integer, data0: integer, data1: integer, data2: integer, tif_first: integer, tif_tick: integer, tif_last: integer, apf: integer)
Adds an affect to a unit.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | Unit to add the affect to |
id | integer | Affect ID (e.g., ID_POISON, ID_INVISIBILITY) |
duration | integer | How long the affect lasts in ticks. Use -1 for permanent. |
beat | integer | How often tif_tick is called. Use -1 if no periodic tick needed. |
data0 | integer | Affect-specific data. Meaning depends on apf type. |
data1 | integer | Affect-specific data. Meaning depends on apf type. |
data2 | integer | Affect-specific data. Meaning depends on apf type. |
tif_first | integer | Tick function called when affect starts (TIF_* constant, or TIF_NONE) |
tif_tick | integer | Tick function called every beat ticks (TIF_* constant, or TIF_NONE) |
tif_last | integer | Tick function called when affect ends (TIF_* constant, or TIF_NONE) |
apf | integer | What the affect modifies (APF_* constant, or 0) |
Description:
Adds affect ‘id’ to unit ‘u’ with the specified duration, timing, data values, and tick functions.
destroy
Signature: void destroy(u: unitptr)
Removes a unit from the game.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | Unit to remove from game |
Description:
The destroy function works in two ways depending on the Unit being acted on:
- If the Unit being acted on is a PC, the player is saved and ejected from the game.
- If the Unit being acted on is a NPC or an Object, the purge function destroys the Unit.
Currently destroy will not destroy rooms. This is different from the old destroy function in that it removes the player out of the game instead of leaving the player in the menu.
Example:
dilbegin purge_all_pc();
var
u : unitptr; /* Unit used to purge each player */
n : unitptr; /* used to keep track of next player */
code
{
u := ghead(); /* get first pc in game list */
n := u;
while (n.type == UNIT_ST_PC) /* while unit is a pc */
{
n := u.gnext;
destroy(u);
}
quit; /* done wiping out the players */
}
dilend
walkto() - OBSOLETE
This procedure is obsolete, do not use it, it has temporarily been made an alias to the DIL equivalent but is not safe to use ongoing and will be deleted soon. Use walk_room@function instead.
The walkto() procedure made mobiles walk to a specified room. This is now handled by walk_room@function which is more robust, handling doors and navigation challenges.
Replacement:
external
integer walk_room(place:string, spd:integer);
code
{
/* Old way - do not use
Walks to room unitptr, speed used heartbeat.
*/
walkto(findroom("room_name@udgaard"));
/* Correct usage - call DIL as external function
Walks to room_name@udgaard with 5 seconds speed.
*/
walk_room("room_name@udgaard", 5);
/* Alternate - direct translation of old
Walks to to the room using symbolic name of room unit, speed as per heartbeat (discards part seconds).
*/
walk_room(room_unit.symname, heartbeat / PULSE_SEC);
/* The return value is optional - use it if you need to check success for the walk */
if (walk_room("inn@udgaard", 5) == FALSE)
exec("say I can't get there!", self);
}
Parameters:
place- Room symbolic name as a string (e.g."inn@udgaard"orroomunit.symname)spd- Speed in seconds between moves (minimum 3)
Returns: FALSE if destination unreachable, TRUE when arrived.
link
Signature: void link(u: unitptr, t: unitptr)
Links a unit into another unit’s hierarchy.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | Unit to link |
t | unitptr | Unit to link into |
Description:
Link a unit into another unit hierarchy. Automatically unequips if equipped.
weapon_name
Signature: string weapon_name(i: integer)
Gets the name of a weapon type.
Parameters:
| Parameter | Type | Description |
|---|---|---|
i | integer | Weapon to get the name of as defined in ‘values.h’ and ‘weapons.def’ |
Returns: The name of the weapon that corresponds with the integer value.
Example:
myweap := weapon_name(5);
weapon_info
Signature: intlist weapon_info(i: integer)
Gets detailed information about a weapon type.
Parameters:
| Parameter | Type | Description |
|---|---|---|
i | integer | Weapon to get the info of as defined in ‘values.h’ and ‘weapons.def’ |
Returns: Intlist containing 4 values:
| Index | Value |
|---|---|
0 | Number of hands |
1 | Weapon speed |
2 | Weapon type |
3 | Shield block value |
Description:
This function gives you access to the values in the weapons.def file. With this, things like ‘wear’, ‘equipment’ and ‘look’ are much easier to write in DIL.
Example:
dilcopy id_weap(arg:string);
var
i : integer;
il : intlist;
code
{
il := weapon_info(atoi(arg));
if (il != null)
{
sendtext("Number of hands: " + itoa(il.[0]) + "&n", self);
sendtext("Speed: " + itoa(il.[1]) + "&n", self);
sendtext("Type: " + itoa(il.[2]) + "&n", self);
sendtext("Shield value: " + itoa(il.[3]) + "&n", self);
}
else
{
sendtext("No such weapon.&n", self);
}
quit;
}
dilend
skill_name
Signature: string skill_name(i: integer)
Gets the name of a skill.
Parameters:
| Parameter | Type | Description |
|---|---|---|
i | integer | Skill to get the name of as defined in ‘values.h’ and ‘commands.def’ |
Returns: The name of the skill that corresponds with the integer value.
Example:
myski := skill_name(5);
spell_name
Signature: string spell_name(i: integer)
Gets the name of a spell.
Parameters:
| Parameter | Type | Description |
|---|---|---|
i | integer | Spell to get the name of as defined in ‘values.h’ and ‘spells.def’ |
Returns: The name of the spell that corresponds with the integer value.
Example:
myspl := spell_name(SPL_FIREBALL);
strlookup
Signature: string strlookup(idx: integer, u: unitptr, param: integer)
Multi-purpose function for retrieving various game strings.
Parameters:
| Parameter | Type | Description |
|---|---|---|
idx | integer | Which value to retrieve (see table below) |
u | unitptr | Unit context (or null if not needed) |
param | integer | Additional parameter (meaning varies by idx) |
Returns: The requested string value, or empty string on failure.
Index Values:
| idx | Constant | Description |
|---|---|---|
| 0 | DIL_GSTR_RACE_NAME | Race name from the race ID via param or the race of the unit if supplied |
| 1 | DIL_GSTR_RACE_ADVERB | As per race name, but the adverb, e.g., “elven”, “dwarven” etc. |
| 2 | DIL_GSTR_WEAPON_NAME | Weapon type name (param = WPN_* constant) |
| 3 | DIL_GSTR_SKILL_NAME | Skill name (param = SKI_* constant) |
| 4 | DIL_GSTR_SPELL_NAME | Spell name (param = SPL_* constant) |
| 5 | DIL_GSTR_DIR_NAME | The name of a direction from the direction ID in param |
| 6 | DIL_GSTR_MUD_NAME | The MUD name from server.cfg (unit and param are ignored) |
Example:
// Get race name by constant
rname := strlookup(DIL_GSTR_RACE_NAME, null, RACE_TROLL);
// Convert direction ID from pathto() to a name
dir := pathto(self, target);
if (dir >= 0)
sendtext("You should go " + strlookup(DIL_GSTR_DIR_NAME, null, dir) + ".&n", self);
Note: The weapon_name(), skill_name(), and spell_name() functions are convenience aliases that compile to strlookup() calls.
getinteger
Signature: integer getinteger(idx: integer, u: unitptr, param: integer)
Multi-purpose function for retrieving various game integers.
Parameters:
| Parameter | Type | Description |
|---|---|---|
idx | integer | Which value to retrieve (see table below) |
u | unitptr | Unit context (or null if not needed) |
param | integer | Additional parameter (meaning varies by idx) |
Returns: The requested integer value, or 0 on failure.
Index Values:
| idx | Constant | Description |
|---|---|---|
| 0 | DIL_GINT_EFFDEX | Effective dexterity of unit (requires character unit) |
| 1 | DIL_GINT_REQXP | Total XP required to reach level (param = level) |
| 2 | DIL_GINT_LEVELXP | XP needed to advance from level to level+1 (param = level) |
| 3 | DIL_GINT_DESCRIPTOR | 1 if PC has active connection, 0 if linkdead (requires PC unit) |
| 4 | DIL_GINT_CRIMENO | Returns next unique crime serial number |
| 5 | DIL_GINT_MANAREG | Mana regeneration rate for unit (requires character unit) |
| 6 | DIL_GINT_HITREG | HP regeneration rate for unit (requires character unit) |
| 7 | DIL_GINT_MOVEREG | Movement regeneration rate for unit (requires character unit) |
| 10 | DIL_GINT_REALYEAR | Current real-world year (e.g., 2025) |
| 11 | DIL_GINT_REALMONTH | Current real-world month (1-12) |
| 12 | DIL_GINT_REALDAY | Current real-world day of month (1-31) |
| 13 | DIL_GINT_RACEFLAGS | Race flag bit field, uses the race ID via param or the race of the unit if supplied |
Example:
// Check if player is linkdead
if (getinteger(DIL_GINT_DESCRIPTOR, pc, 0) == 0)
log(pc.name + " is linkdead");
// Get XP needed for level 10
xp := getinteger(DIL_GINT_REQXP, null, 10);
reboot
Signature: void reboot(exitcode: integer)
Reboots the MUD server immediately.
Parameters:
| Parameter | Type | Description |
|---|---|---|
exitcode | integer | Exit code passed to the system when the MUD restarts |
Description:
This function works like a quit command. Anything after the reboot function in a DIL will not be executed - the mud will reboot instantly. The zone must have root privileges in the zonelist in order to use this function.
Example:
dilbegin cmd_reboot(arg:string);
code
{
sendtext("Rebooting the mud.&n", self);
reboot(0);
}
dilend
zonereset
Signature: void zonereset(z: zoneptr)
Forces an immediate zone reset.
Parameters:
| Parameter | Type | Description |
|---|---|---|
z | zoneptr | The zone to reset |
Description:
Triggers an immediate zone reset regardless of the zone’s reset timer. Performs the same reset logic as a timed zone reset. The reset is logged to the zone log.
This does not affect the zone’s reset timer - the next scheduled reset will still occur at its original time.
Example:
z := findzone("haon_dor");
if (z)
{
log("Forcing reset of " + z.name);
zonereset(z);
}
See Also: findzone
killedit
Signature: void killedit(u: unitptr)
Terminates a text editing session started with beginedit().
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The PC whose text editing session to terminate |
Description:
This function is used to kill the editor on a PC if it needs to stop editing before the PC is done editing. Clears the edit buffer and returns the player to normal command mode. An example of when this is needed is when a player is killed while editing or is transferred away from a place where he was editing. You can let them finish but it may be weird for a person to finish posting in one room while in another.
Example:
dilbegin editextra(arg:string);
code
{
interrupt(SFB_DEAD, self == activator, int_quit);
beginedit(self);
wait(SFB_EDIT, self == activator);
temp := textformat(argument);
addextra(self.outside.extra, {"graphitee"}, temp);
quit;
:int_quit:
killedit(self);
quit;
}
dilend
experience
Signature: void experience(i: integer, u: unitptr)
Gives or removes experience from a player.
Parameters:
| Parameter | Type | Description |
|---|---|---|
i | integer | An integer number of experience to gain or lose |
u | unitptr | The player to receive the experience |
Description:
The integer amount of experience is added to the player’s total amount of experience (thus causing more or less experience).
Warning: USE WITH CARE! SUGGEST THAT YOU ONLY USE INTEGER CONSTANTS AS THE EXPRESSION TO AVOID ERRORS.
act
The purpose of act is to send a message to a number of players present in a room. The room is defined as the room containing the afore mentioned character (provided he himself is in the room, i.e. not in a closed container.)
Signature:
act(message, visibility, char, medium, victim, to_whom [, capitalize]);
Parameters:
| Parameter | Type | Description |
|---|---|---|
message | string | A string that will be shown to other mobiles when the act is executed. To refer to the following arguments use the formatters listed below. |
visibility | integer | An integer that defines what happens if the mobile that is about to receive the message can’t see the activator. See Visibility Flags below. |
char | unitptr | A unitptr, and the most important argument in the act. If this is not valid the act will never show, leaving mobiles without the message. |
medium | unitptr/string/null | A unit pointer, an integer or a string. Use this at your leisure. |
victim | unitptr/string/null | A unit pointer, an integer or a string. Certain flags in the next argument rely on the validity of victim. |
to_whom | integer | An integer that defines who gets the message. See To Whom Flags below. |
capitalize | integer (optional) | If TRUE (default), the first letter of the output message is capitalized. If FALSE, the message is sent as-is without first-letter capitalization. |
Act Formatters
Just as the description formatters, act has its own set of formatters that enable you to create generic messages that will be interpreted at run-time, lessening the load on you.
The format string is a normal message, containing some special characters:
$$- will be sent to the receiver as a single$$followed by a number and an extra character
The numbers:
| Number | Refers To |
|---|---|
1 | The ‘char’ argument |
2 | The ‘medium’ argument |
3 | The ‘victim’ argument |
The characters:
| Character | Description |
|---|---|
N | The name of the unit, or (depending on the visibility) ‘something’ or ‘someone’ |
a | ‘a’ or ‘an’ depending on the name of the unit referred by the number |
e | ‘it’, ‘he’ or ‘she’ depending on the gender of the unit referred by the number |
m | ‘it’, ‘him’ or ‘her’ depending on the gender of the unit referred by the number |
n | For PCs, the character’s name; for NPCs, the unit’s title. If the receiver cannot see the unit (depending on visibility), shows ‘something’ or ‘someone’ instead |
p | ‘fighting’, ‘standing’, ‘sleeping’, etc., depending on the position of the unit referred by the number |
s | ‘its’, ‘his’ or ‘her’ depending on the gender of the unit referred by the number |
t | The string in the argument referred by the number |
Visibility Flags
| Flag | Description |
|---|---|
A_SOMEONE | If the receiver cannot see char, replace any reference to char with “someone” |
A_HIDEINV | If the receiver cannot see char, don’t send the message at all. Use this when missing vision should eliminate the perception of the action the message represents. One instance where it is used is in smile. You would need a ridiculously sensitive ear to hear, let alone perceive that someone’s smiling if you can’t see them. Do not use it if ‘sound’ is inherent in the message |
A_ALWAYS | Works exactly like A_SOMEONE, except that the receiver will see the message even when sleeping |
To Whom Flags
| Flag | Description |
|---|---|
TO_ROOM | Sends the message to the entire room, excluding ‘char’ |
TO_VICT | Sends to ‘victim’ only. ‘victim’ must be valid, naturally |
TO_NOTVICT | Sends the message to the entire room, excluding ‘char’ and ‘victim’. Perfect for bystanders in a melee |
TO_CHAR | Sends the message to ‘char’ and no one else |
TO_ALL | Sends the message to everybody in the room |
TO_REST | This is a bit different from the rest. It sends the message to all other units in the local environment, excluding those inside ‘char’ |
Example:
act("You step over $2n.", A_SOMEONE, self, litter, null, TO_CHAR);
act("$1n steps over $2n.", A_SOMEONE, self, litter, null, TO_REST);
See Also: Refer to external documentation “DIL Act() for Dummies”
sact
Like act() but returns the formatted message string instead of sending it.
Signature:
string sact(message, visibility, char, medium, victim, to_whom [, capitalize]);
Parameters: Same as act(). See act for full parameter details.
Returns: The formatted message string, or empty string on failure.
Description:
This function formats a message using the same tokens as act() ($1n, $2t, $3m, etc.) but returns the result as a string instead of sending it to players. Useful when you need to manipulate, log, or conditionally send the formatted text.
The to_whom parameter determines the perspective used for pronoun substitution (e.g., TO_CHAR formats from char’s perspective).
Example:
// Format a message and log it
msg := sact("$1n picks up $2n.", A_SOMEONE, ch, obj, null, TO_ROOM);
log(msg);
// Build a custom multi-line message
line1 := sact("$1n attacks $3n!", A_ALWAYS, ch, null, victim, TO_ROOM);
line2 := sact("$3n staggers back.", A_ALWAYS, ch, null, victim, TO_ROOM);
sendtext(line1 + "&n" + line2 + "&n", bystander);
See Also: act
exec
Signature: void exec(s: string, u: unitptr)
Forces a unit to execute a command.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | Command and arguments to perform |
u | unitptr | Unit to perform command |
Description:
Unit (PC/NPC) executes command. The argument is treated just as if a normal PC entered a command at the command prompt in the game. It is not directly possible to detect whether the command was a success or fail. For example, you might force a PC to “get key”. If there is no ‘key’ available, the PC will get notified the normal way. Plenty of examples are given above.
wait
Signature: void wait(i: integer, dilexp)
Waits for a message matching specified criteria.
Parameters:
| Parameter | Type | Description |
|---|---|---|
i | integer | Message class to accept, see SFB_* in values.h and/or vme.h |
dilexp | expression | Expression that has to be TRUE or not null to accept message |
Description:
Waits for a command, matching the flagset, and satisfies the expression. When using the ‘wait()’ command, the first argument is an integer that tells what classes of messages to wait for. Each message class is represented by a bit named as described in the chapter on messages.
Example:
wait (SFB_TICK|SFB_MSG, TRUE)
/* Will accept a message, either from another DIL program or a
timer message. As the second argument is always TRUE, any
such message will reactivate the DIL program.
*/
Example:
wait (SFB_CMD, command("transmutate"))
/* waits for an command entered named 'transmutate'.
Activates the DIL program, that then can block the command, or
let it pass (and then let the game produce the normal error
message for unknown commands).
*/
waitnoop
Signature: void waitnoop()
Cooperative yield that allows other DILs to execute within the same tick.
Description:
Suspends the current DIL program and reschedules it to resume later in the same game tick. Unlike wait() or pause which wait for the next tick or a specific event, waitnoop() yields control briefly and then continues.
This is useful for:
- Breaking up intensive loops to prevent server lag
- Allowing multiple DIL programs to coordinate within the same tick
Example:
// Process a large list without blocking other DILs
i := 0;
while (i < length(biglist))
{
// Do some work
process_item(biglist.[i]);
i := i + 1;
// Yield every 10 iterations to let other DILs run
if (i % 10 == 0)
waitnoop();
}
savestr
Signature: integer savestr(filename: string, buff: string, wa: string)
Saves a string to disk.
Parameters:
| Parameter | Type | Description |
|---|---|---|
filename | string | The name of the String file to save the String to |
buff | string | The String you wish to save into the file |
wa | string | Write mode: "w" (overwrite) or "a" (append) |
Returns:
| Value | Meaning |
|---|---|
FILE_SAVED | File saved successfully |
FILE_NOT_SAVED | File was not saved |
FILE_NOT_CREATED | File could not be created |
FILE_ILEGAL_OPP | Illegal operation |
The return value can be discarded if not checking the result.
Description:
Savestr is used to save strings to disk to be loaded later by the ‘loadstr’ function. The ‘savestr’ and ‘loadstr’ are perfect for operations such as on-line edited newspapers, lotteries where tickets are sold to players, creating smarter NPCs that can remember through reboots who they are hunting, DIL-based teachers, message boards, mail system, news command, zone or room based help, competition boards, and much more.
Note: The append/write argument must be in lower case and can only be "w" or "a" surrounded by quotes. If the argument is "w" it will overwrite any string file by that name. If the argument is "a" it will append to the file by that name.
⚠️ Performance Warning: Disk access is always slow. If you use savestr() on a continuous basis, keep file sizes to a minimum and use it sparingly. Excessive disk I/O can cause serious server delays.
Example:
dilbegin news_save(arg:string); /* for saving */
var
ret : integer; /* to hold the return value if saved or not */
code
{
ret := savestr("news.txt", arg, "w");
if (!ret)
{
log("File not wrote");
quit;
}
sendtext("New news file wrote.[&]n", self);
quit; /* DIL save routine done destroy self. */
}
dilend
remove
Signature:
void remove(sl: stringlist, i: integer)void remove(il: intlist, i: integer)
Removes an element from a stringlist or intlist at a specified index.
Parameters:
| Parameter | Type | Description |
|---|---|---|
sl | stringlist | The stringlist to remove from |
il | intlist | The intlist to remove from |
i | integer | The index of the element to remove |
Description:
Removes the element at index i from the list, shifting subsequent elements down to fill the gap. Works with both stringlists and intlists.
Example:
mylist := {"a", "b", "c", "d"};
remove(mylist, 1);
// mylist is now {"a", "c", "d"}
numbers := {10, 20, 30, 40};
remove(numbers, 0);
// numbers is now {20, 30, 40}
See Also: insert, addstring, addint
reset_level
Signature: void reset_level(u: unitptr)
Resets a player’s level to starting values.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | Player you are resetting |
Description:
This function simply resets a player’s level. Can be used in functions like reroll where you set the players back to the way they first logged on.
Example:
reset_level(u);
See Also: reset_vlevel, reset_race
reset_vlevel
Signature: void reset_vlevel(u: unitptr)
Resets a player’s virtual level to starting values.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | Player you are resetting |
Description:
This function simply resets a player’s virtual level. Can be used in functions like reroll where you set the players back to the way they first logged on.
Example:
reset_vlevel(u);
See Also: reset_level, reset_race
reset_race
Signature: void reset_race(u: unitptr)
Resets a character’s race-related attributes.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | Player you are resetting |
Description:
Reset a character’s race, weight, height, age, lifespan, and costs for training - as if you first log on the character. Great for reroll along with ‘reset_level’ and ‘reset_vlevel’.
Example:
reset_race(u);
See Also: reset_level, reset_vlevel
secure
Signature: void secure(u: unitptr, label)
Secures a unitptr so the program jumps to a label if the unit leaves local environment.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | Unit to secure |
label | label | Label to jump to on removal |
Description:
Secures a unitptr so that the program will go to ‘label’ if unit leaves local environment. If this happens during a call to another function/procedure, it will continue at that label when the function/procedure returns.
If you perform some kind of call to a template, the removing of a unit from the local environment will not have effect until the return from that function, as the program execution will continue at the designated label after a call. Should several secured units leave local environment, the last such event will determine the point of execution upon return.
unitdir
Signature: stringlist unitdir(match: string)
Returns a list of unit filenames matching a wildcard pattern.
Parameters:
| Parameter | Type | Description |
|---|---|---|
match | string | The wildcard file pattern you want to match, or ‘*’ for all |
Returns: A stringlist with all the filenames that match the ‘match’ argument.
Description:
The ‘match’ argument uses the same wildcards as the Linux ‘ls’ command:
| Pattern | Description |
|---|---|
* | Match any character or group of characters |
? | Match one of any character |
[...] | Match one of a set of characters |
Wildcard Examples:
"corpse*" matches: corpse.10938 corpse.whistler corpseofwhistler ...
"corpse?" matches corpse1 corpses corpse5 ...
"[abc]*" matches ability about cost back ...
"[a-z]*" about zoo man father ...
"start[nm]end" matches startnend startmend
Example:
dilbegin aware reload_corpse();
var
corpselist : stringlist;
u : unitptr;
ln : integer;
i : integer;
x : extraptr;
code
{
corpselist := unitdir("corpse*");
ln := length(corpselist);
i := 0;
while (i < ln)
{
u := restore(corpselist.[i], null);
x := CORPSE_EXTRA in u.extra;
if (u != null)
{
if (x != null)
link(u, findroom(x.descr));
else
link(u, findroom("temple@udgaard"));
}
i := i + 1;
}
quit;
}
dilend
The previous DIL example is the DIL used in restoring corpses to the game in case of a crash. For more information you can see how the death DILs work by reading through the file death.zon in the vme2.0/zone directory.
unsecure
Signature: void unsecure(u: unitptr)
Drops secure on a given unit.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | Secured unit to unsecure |
Description:
Drop secure on a given unit.
beginedit
Signature: beginedit(u: unitptr)
Sets a PC into editing mode. When editing completes, sends an SFB_EDIT message.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The PC unit doing the editing |
Returns: When done editing it returns SFB_EDIT and activator is set to PC.
Description:
The beginedit() function sets a PC into editing mode. While in edit mode, the PC field editing is set to true. Once the PC is done editing, a SFB_EDIT message is sent to the unit to continue with the DIL. If the PC needs to break out of the editor before finishing, the DIL needs to call the killedit() function to interrupt editing.
Example:
dilbegin edextra();
var
temp: string;
code
{
beginedit(self);
wait(SFB_EDIT, self == activator);
temp := textformat(argument);
addextra(self.extra, "general", temp);
quit;
}
dilend
The previous DIL is an example of how you could make a command to set the general extra which is the character’s description when you do ‘look player’.
block
Signature: void block()
Blocks any command issued by a player or mobile.
Description:
Blocking a command prevents the command from being parsed by the interpreter. This is ideal if you need to make a routine which intercepts a command, reacts on it in a special way, and does not need the standard way of reacting.
priority
Syntax: priority;
Sets the priority blocking flag until nopriority is called.
Description:
When set, all DIL programs with less important priority (higher fnpri numbers)
on the same unit will be blocked from executing. This allows a DIL to temporarily
claim exclusive control while performing an important action.
Important: The priority flag only blocks DILs that are below the current
program in the priority list. DILs with more important priority (lower fnpri
numbers) will continue to execute normally regardless of whether a lower-priority
DIL has set this flag.
Example:
dilbegin fnpri(FN_PRI_BLOCK) shop_handler();
code
{
:loop:
wait(SFB_CMD, command("buy"));
priority; // Block FN_PRI_MISSION (30), FN_PRI_CHORES (40), etc.
// ... handle purchase - lower priority DILs won't interfere ...
nopriority; // Allow lower-priority DILs to resume
goto loop;
}
dilend
In this example, while priority is set:
- DILs with
fnpri(FN_PRI_COMBAT)(10) would still execute (more important) - DILs with
fnpri(FN_PRI_MISSION)(30) would be blocked (less important) - DILs with
fnpri(FN_PRI_CHORES)(40) would be blocked (less important)
See also: fnpri in the DIL Program Options section for setting static priority.
nopriority
Syntax: nopriority;
Clears the priority blocking flag.
Description:
Removes the SFB_PRIORITY flag set by priority, allowing lower-priority DIL
programs on the same unit to resume execution. Should be called when the critical
section of code is complete.
addequip
Signature: void addequip(u: unitptr, i: integer)
Equips an item on the PC/NPC currently holding it.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The item to equip (must be an object in a PC/NPC’s inventory) |
i | integer | Equipment position (see WEAR_* constants) |
Description:
Equips the item at the specified position. The item must already be in a PC/NPC’s inventory - the character who equips it is u.outside. Does nothing if the item is not in a character’s inventory, is not an object, or if the position is already occupied.
Example:
link(sword, player); // Put sword in player's inventory
addequip(sword, WEAR_WIELD); // Player wields the sword
unequip
Signature: void unequip(u: unitptr)
Unequips an item, moving it to the PC/NPC’s inventory.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The equipped item to unequip |
Description:
Removes the item from equipment and places it in the PC/NPC’s inventory. The character affected is u.outside. Does nothing if the item is not an object or is not currently equipped.
Example:
unequip(sword); // Sword moves from equipment to inventory
destroy(sword); // Now safe to destroy
delete_player
Signature: void delete_player(s: string)
Deletes a player character from the game.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | The player name you want to delete |
Description:
This function deletes a player but it doesn’t check to see if it was deleted or if it even existed - you will have to do that with ‘isplayer’.
Example:
dilbegin aware do_delete (arg:string);
var
temp : string;
err : integer;
code
{
if (self.type != UNIT_ST_PC)
quit;
if (self.level > 200)
goto admin_delete;
:char_delete:
if (arg != "self forever")
{
sendtext("To delete your char type: 'delete self forever'&n", self);
quit;
}
err := loadstr("delete.txt", temp);
if (err < 1)
goto no_insure;
sendtext(temp, self);
sendtext("If your sure you still want to delete your character, 'say delete me'&n", self);
sendtext("Doing anything else will abort the deletion.&n", self);
wait (SFB_CMD, self == activator);
if (command("say"))
{
if (argument == "delete me")
{
if (self.extra.[CLAN_RANK] != null)
exec("cdefect", self);
delete_player(self.name);
quit;
}
}
sendtext("Deletion aborted&n", self);
quit;
:no_insure:
if (self.extra.[CLAN_RANK] != null)
exec("cdefect", self);
delete_player(self.name);
quit;
:admin_delete:
if (arg == "self forever")
goto char_delete;
if (arg == "")
{
sendtext("You must supply a characters name to delete one.&n", self);
quit;
}
if (arg == self.name)
{
sendtext("To delete self you need to type 'delete self forever'&n", self);
quit;
}
if (not isplayer(arg))
{
sendtext(arg + " is not a character.&n", self);
quit;
}
dilcopy("god_delete@clans(" + arg + ")", self);
sendtext(arg + " has been deleted.&n", self);
quit;
}
dilend
dilcopy
Signature: void dilcopy(s: string, u: unitptr)
Attaches a DIL program to a unit.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | Template name with optional parameters in parentheses |
u | unitptr | Unit to attach a DIL program to |
Description:
Attaches a DIL program to a unit ‘u’, which uses a template named by ‘s’. Parameters can be passed to the target template by embedding them in the string inside parentheses after the template name. Only integer and string literal values can be passed this way. To pass variable values, you must convert them to strings and concatenate them into the template name string.
Examples:
dilcopy("master_servant@spells()", u);
dilcopy("spl_sleep@spells(7)", tgt);
dilcopy("sendmsg@spells(teleport)", targ);
// Passing a variable requires string concatenation.
// See dilcopy (native arglist) below for a cleaner approach..
power := 50;
dilcopy("cast_effect@spells(" + itoa(power) + ")", caster);
See Also: dildestroy, dilfind
dilcopy (native arglist)
Signature: void dilcopy(s: string, u: unitptr, arg1, arg2, ...)
Attaches a DIL program to a unit with native DIL arguments passed directly.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | Template name (without parentheses) |
u | unitptr | Unit to attach a DIL program to |
arg1, arg2, ... | various | Native arguments matching the target template’s parameter types |
Description:
Attaches a DIL program to a unit ‘u’ using the template named by ‘s’, passing arguments as native DIL values rather than embedding them in the template name string. This allows passing any DIL type including unitptrs, stringlists, intlists, and variables directly without string conversion.
Note: extraptr arguments cannot be passed and will cause a compile-time error. Extraptrs are volatile and cannot be secured, so they would become null when the new DIL program starts executing. Pass the unit and an identifier string instead, then look up the extra in the target DIL.
Examples:
// Pass an integer variable directly - no itoa() needed
power := 50;
dilcopy("cast_effect@spells", caster, power);
// Pass a unitptr variable
dilcopy("track_target@hunting", npc, target_unit);
// Pass multiple arguments of different types
dilcopy("complex_spell@magic", caster, victim, power, spell_names);
See Also: dildestroy, dilfind
dilsetval
Signature: void dilsetval(u: unitptr, template: string, varname: string, value: <type> [, instance: integer])
Sets the runtime value of a variable in a DIL program running on a unit.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | Unit containing the DIL program |
template | string | Template name in “name@zone” format |
varname | string | Name of the variable to set |
value | various | New value to assign (must match variable type) |
instance | integer | (Optional) Which instance to modify if multiple exist (default: 1) |
Description:
This procedure provides cross-DIL variable modification, allowing one DIL program to change variables in another running DIL program. The value must match the declared type of the target variable.
The optional instance parameter handles units with multiple copies of the same DIL template. Instance numbering starts at 1 and matches the order returned by dillist().
Type Matching:
The value type must match the target variable type exactly, with one exception: you may pass null to clear any pointer type (unitptr, zoneptr, extraptr, cmdptr, stringlist, intlist).
| Target Type | Allowed Value Types |
|---|---|
| integer | integer only |
| string | string only |
| unitptr | unitptr, null |
| zoneptr | zoneptr, null |
| extraptr | extraptr, null |
| cmdptr | cmdptr, null |
| stringlist | stringlist, null |
| intlist | intlist, null |
Error Handling:
Errors are logged to the log and the operation silently fails when:
- Your unit variable is null
- Template or variable name are empty
- DIL template not found
- DIL instance not found on unit
- Variable not found in template
- Type mismatch (except null for pointer types)
Examples:
// Set an integer variable
dilsetval(npc, "aggressive@monsters", "alignment", 1000);
// Set a string variable
dilsetval(npc, "talker@mobiles", "greeting_message", "Hello, adventurer!");
// Clear a unitptr variable (set to null)
dilsetval(npc, "hunter@mobiles", "current_target", null);
// Set a stringlist variable
names := {"alice", "bob", "charlie"};
dilsetval(npc, "guard@mobiles", "allowed_names", names);
// Modify second instance of same template
dilsetval(npc, "aggressive@monsters", "alignment", -1000, 2);
Important Notes:
-
Side effects on target DIL. The target DIL program may not expect external modifications to its variables. This can cause unexpected behavior if the target DIL relies on internal consistency in its variable values.
-
Secure unitptr handling. If the target variable is a unitptr that was
secure()’d, setting a new value preserves the secure label. The new unit will be checked for availability on the next activation and remain secured in the old units place (or jump to label if new unit is gone) -
Stringlist and intlist are copied. When setting a stringlist or intlist, the value is copied into the target variable. Modifying the original list after calling dilsetval will not affect the target.
See Also: dilgetval, dillist, dilvars, dilvartype, dilfind
dispatch
Signature: void dispatch(bridge: string, message: string)
Sends a message to a connected external bridge service.
Parameters:
| Parameter | Type | Description |
|---|---|---|
bridge | string | The name of the bridge to send to (e.g., “discord”, “irc”) |
message | string | The message payload to dispatch |
Description:
Dispatches a message from the MUD to an external bridge service. Bridges are external processes (such as Discord bots or IRC relays) that connect to the engine via TCP and relay messages between the MUD and external platforms.
The message format is defined by the receiving bridge, but typically follows the pattern channel|content where the channel routes the message to the appropriate destination on the external service.
If the specified bridge is not connected, the message is silently dropped. The pipe character | is used as a field delimiter in the bridge protocol; literal pipes in content should be escaped as <<pipech>>.
Example:
// Send a message to the Discord bridge
dispatch("discord", "chat|" + self.name + " has entered the realm!");
// Send to a specific channel on IRC
dispatch("irc", "announcements|Server reboot in 5 minutes");
// Escape pipe characters in user content
:start:
wait(SFB_CMD, command("shout"));
msg := replace("|", "<<pipech>>", argument);
dispatch("discord", "shout|" + self.name + ": " + msg);
goto start;
See Also: The bridge system is documented in bridge-system.md in the project root.
sendtext
Signature: void sendtext(s: string, u: unitptr)
Sends a string to a unit without appending a newline.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | Text to send |
u | unitptr | Unit to send the text to |
Description:
Sends the string ‘s’ to ‘u’. Useful only for nanny stuff, because no newline is appended.
change_speed
Signature: void change_speed(u: unitptr, i: integer)
Alters the current combat speed of a unit.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The unit on which you wish to alter the current combat speed |
i | integer | The amount to add to the speed |
Description:
Beware, this is not the ‘speed’ as in the speed field, rather this is the speed which is calculated during combat. It is used for spells like ‘stun’ which effectively puts the character out of combat for one round. Such a spell would be implemented like:
change_speed(u, 12)
This would only cause any effect if ‘u’ was fighting already (if not, use setfighting).
transfermoney
Signature: integer transfermoney(f: unitptr, t: unitptr, amount: integer)
Transfers money value between characters, handling change-making automatically.
Parameters:
| Parameter | Type | Description |
|---|---|---|
f | unitptr | The character money is taken from (or null to create money) |
t | unitptr | The character money is given to (or null to destroy money) |
amount | integer | How much money in “old gold” units (10 = 1 iron piece) |
Returns: 1 (TRUE) if money was transferred, 0 (FALSE) if could not afford or invalid parameters. Return value can be ignored / not assigned.
Description:
This is a value-based transfer that handles change-making. Use this when you want to transfer an abstract monetary value and let the system handle coin denominations.
- If
fis null andtis valid, money is created and given tot - If
fis valid andtis null, money is removed fromf - If both are valid, money is transferred from
ftot - Both
fandtmust be characters (PCs or NPCs) when non-null - passing rooms or containers will fail - Uses
char_can_afford()to check if source has sufficient funds
Note: For transferring specific physical coins (e.g., “get 50 gold from corpse”), use transfercoins instead.
transfercoins
Signature: integer transfercoins(f: unitptr, t: unitptr, amount: integer, mtype: integer)
Transfers specific coins of a given type from one unit to another.
Parameters:
| Parameter | Type | Description |
|---|---|---|
f | unitptr | Source: a container (char/room/object), a money object directly, or null to create |
t | unitptr | Destination: a container (char/room/object), or null to destroy |
amount | integer | Number of coins to transfer (0 or negative = all available) |
mtype | integer | Money type: IRON (0), COPPER (1), SILVER (2), GOLD (3), PLATINUM (4) |
Returns: The actual number of coins transferred (may be less than requested due to weight limits or availability). Returns 0 on failure. Return value can be ignored / not assigned.
Description:
Efficiently transfers physical coins using split/move/pile operations. Best for player commands like “get money from corpse” where you want to move actual coins rather than abstract value.
Basic behavior:
- If
fis null andtis valid, creates coins at destination - If
fis valid andtis null, destroys coins from source - If both are valid, transfers coins from source to destination
- Returns 0 if
fandtare the same unit (no-op) - Returns 0 if coins are already at destination
Source flexibility:
fcan be a container (character, room, or object) - searches for coins ofmtypeinsidefcan be a money object directly - uses it if type matches, otherwise fails- This allows efficient transfers when you already have a reference to the money object
Weight and capacity:
- Respects weight limits for characters and capacity limits for containers
- Rooms have no capacity limit
- Weight check is skipped when source container is inside destination (e.g., getting from your own bag doesn’t add weight since it’s already carried)
- If destination is overloaded, transfers as many coins as will fit
Other notes:
- Automatically piles with existing money of the same type at destination
- Overflow protection: caps at ~1 billion coins per transfer
Example:
// Transfer 50 gold coins from corpse to player
transferred := transfercoins(corpse, self, 50, GOLD);
if (transferred < 50)
act("You can only carry " + itoa(transferred) + " gold coins.", A_ALWAYS, self, null, null, TO_CHAR);
// Create 100 silver coins for a player
transfercoins(null, player, 100, SILVER);
// Destroy all iron coins from an NPC
transfercoins(npc, null, 0, IRON);
// Transfer using a money object reference directly (efficient in loops)
u := corpse.inside;
while (u)
{
nxt := u.next; // Save next before transfer moves u
if (u.objecttype == ITEM_MONEY)
transfercoins(u, self, 0, u.value[0]); // value[0] is money type
u := nxt;
}
set_fighting
Signature: void set_fighting(ch: unitptr, victim: unitptr [, melee: integer])
Sets two characters fighting.
Parameters:
| Parameter | Type | Description |
|---|---|---|
ch | unitptr | The character initiating combat |
victim | unitptr | The character being attacked |
melee | integer | (Optional) TRUE to force victim as melee target, FALSE to add as opponent only |
Description:
This is used to set two characters fighting. If the attacker is already fighting, the victim will be added to the opponent list.
When called with 2 arguments, the melee target is determined automatically:
- If
chis not yet fighting,victimbecomes the melee target - If
chis already fighting,victimis added as an opponent without changing the melee target
The optional 3rd argument allows explicit control over the melee target:
TRUE(1): Forcevictimto become the melee targetFALSE(0): Addvictimas opponent only, never set as melee target
Example:
// Basic usage - victim becomes melee target if not already fighting
set_fighting(self, target);
// Add an opponent without changing current melee target
set_fighting(self, second_target, FALSE);
// Force a new melee target mid-combat
set_fighting(self, priority_target, TRUE);
See Also: set_melee, stop_fighting
set_melee
Signature: void set_melee(ch: unitptr, victim: unitptr)
Changes the melee target during combat.
Parameters:
| Parameter | Type | Description |
|---|---|---|
ch | unitptr | The character whose melee target to change |
victim | unitptr | The new melee target, or null to pick randomly |
Description:
This is a low-level combat function that changes which opponent a character is actively engaged in melee with. This function is not required for most combat scenarios - the combat system automatically manages melee targets. Only use this if you fully understand the combat system internals.
Requirements:
chmust be a character currently in combatvictimmust be an existing opponent ofch(already in the opponent list)- If
victimis null, a random opponent is selected as the new melee target
The function silently does nothing if:
chis not in combatvictimis not null but is not an existing opponent
Use Cases:
- Forcing a tank to switch targets
- Spells or skills that redirect combat focus
Example:
// Switch melee target to the healer
healer := findunit(self, "healer", FIND_UNIT_SURRO, null);
if (healer != null)
set_melee(self, healer);
// Pick a random opponent as melee target
set_melee(self, null);
See Also: set_fighting, stop_fighting
setweight
Signature: void setweight(u: unitptr, i: integer)
DEPRECATED: Use set_weight instead.
Alters the weight of a unit.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The unit on which you wish to alter the weight |
i | integer | The new weight |
Description:
This is needed on for example drink-containers. I.e. if you wish to remove or add some liquid, you must also adjust the weight of the container, or you will mess up things.
Note: In Diku3, use set_weight instead of this deprecated procedure.
setbright
Signature: void setbright(u: unitptr, i: integer)
Changes the brightness of a unit.
Parameters:
| Parameter | Type | Description |
|---|---|---|
u | unitptr | The unit on which you want to change the brightness |
i | integer | The new brightness |
Description:
When you want to increase/decrease the amount of light shed by a unit, use this function. Units with “bright” light up rooms so that people can see.
shell
Signature: integer shell(command: string)
Executes a shell command on the server.
Parameters:
| Parameter | Type | Description |
|---|---|---|
command | string | The shell command to execute |
Returns: 0 on success, non-zero error code on failure. The return value can be discarded if not checking the result.
Description:
Executes a shell command in a separate thread on the server. This function is restricted to zones with access level 0 (highest privilege). If called from a zone without sufficient access, the DIL program will be terminated.
⚠️ Security Warning: This function can execute arbitrary system commands. Only use in trusted zones with proper access controls.
log
Signature: void log(s: string[, log: integer)
Puts text in the log for debugging purposes.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | Text to put in the log |
log | integer | (Optional) Type of logging, see below |
Description:
Puts text in the log for debugging purposes. You can specify an optional logging type to distinguish verbose noisy logs from critical logs for brief logging.
| Log Type | Description |
|---|---|
| LOGGING_OFF | Log to main log, not visible |
| LOGGING_BRIEF | Brief logs only |
| LOGGING_EXTENSIVE | Brief and extensive logs |
| LOGGING_ALL | Default verbose logging |
| LOGGING_DIL | DIL performance telemetry, noisy |
send
Signature: void send(s: string)
Sends a message to all DIL programs in current local environment.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | Message to send |
Description:
Send a message to all DIL programs in current local environment, matching the message class SFB_MSG. The message is not received by those DIL programs in the local environment that is not set up to wait for that message class.
sendto
Signature: void sendto(s: string, u: unitptr)
Sends a message to all DIL programs in a specific unit.
Parameters:
| Parameter | Type | Description |
|---|---|---|
s | string | Message to send |
u | unitptr | Unit to send it to |
Description:
The message is passed to all DIL programs in unit, matching the message class SFB_MSG. The message is not received by those DIL programs in the local environment that is not set up to wait for that message class.
sendtoall
Signature: void sendtoall(m: string, s: string)
Sends a message to all units matching a given database name.
Parameters:
| Parameter | Type | Description |
|---|---|---|
m | string | Message to send |
s | string | Name idx to send message to |
Description:
Send a message to all units matching a given database name.
Example:
sendtoall("some message", "rabbit@haon-dor");
The message “some message” is sent to all units in the world matching the database name “rabbit@haon-dor”. Like ‘send()’ and ‘sendto()’, the message received matches the SFB_MSG message class.
sendtoalldil
Signature: void sendtoalldil(m: string, s: string)
Sends a message to all DIL programs matching a given database name.
Parameters:
| Parameter | Type | Description |
|---|---|---|
m | string | Message to send |
s | string | Name idx to a DIL program to send message to |
Description:
Send a message to all DIL programs matching a given database name.
Example:
sendtoalldil ( "some message", "intercomm@cemetery");
The message “some message” is sent to all DIL program in the world matching the data base name “intercomm@cemetery”. Like ‘send()’ and ‘sendto()’, the message received matches the SFB_MSG message class.
cast_spell
Signature: integer cast_spell(i: integer, caster: unitptr, medium: unitptr, target: unitptr [, effect: string])
Casts a spell without mana costs.
Parameters:
| Parameter | Type | Description |
|---|---|---|
i | integer | Spell index to cast. See SPL_* in values.h and/or vme.h |
caster | unitptr | The caster of the spell |
medium | unitptr | The medium through which the spell is cast (see below) |
target | unitptr | The target of the spell |
effect | string | (Optional) A symbolic DIL program for custom effect handling. Defaults to “” which uses ordinary messages |
Returns: -1 on error (invalid spell, illegal target type, or spell not implemented), otherwise the spell result (damage dealt for offensive spells, amount healed for healing spells, or spell-specific value). Return value can be ignored / not assigned.
Description:
Use this to cast spells without performing all the usual mana stuff, etc. Very useful for rings/items possessing magical abilities.
The Medium Parameter:
The medium parameter serves two purposes:
-
Source identification - Identifies what object or character is the source of the spell for logging and power calculations. For NPC innate abilities, this is typically
self. For magical items, pass the item itself. -
Spell validation rules - When the medium is a magical item, the spell validation rules are automatically determined by the item type:
- Wands (
ITEM_WAND) - Uses wand spell rules - Staffs (
ITEM_STAFF) - Uses staff spell rules (area effect targeting). When medium is a staff, target may benullas spell_perform handles area targeting internally. - Scrolls (
ITEM_SCROLL) - Uses scroll spell rules - Potions (
ITEM_POTION) - Uses potion spell rules - Other objects or characters - Uses standard spell casting rules
- Wands (
This means when implementing use (wands/staffs), recite (scrolls), or quaff (potions) commands, simply pass the item as the medium and the correct validation rules will apply automatically. For general magical item effects (rings, weapons, armor with spell procs), pass the item as medium but standard spell rules will be used.
Please note that in the example programs below the variable ‘hm’ represents the number of hitpoints that will be deducted from the target.
Example:
%dil
dilbegin myeffect(medi : unitptr, targ : unitptr, hm : integer);
code
{
act("The caster is $1N medium is $2N and target is $3N",
A_ALWAYS, self, medi, targ, TO_ALL);
act("The spell result is $2d", A_ALWAYS, self, hm, null, TO_ALL);
quit;
}
dilend
.....
%dil
dilbegin test();
var
n : integer;
code
{
wait(SFB_DONE, command("beg"));
n := cast_spell(SPL_FIREBALL_1, self, self, activator, "myeffect@wiz");
exec("say Result of spell was " + itoa(n), self);
}
dilend
rawdamage
Signature: void rawdamage(ch: unitptr, vict: unitptr, med: unitptr, dam: integer, attack_group: integer, attack_number: integer, hit_location: integer, disp: integer)
Exposes the engine’s low level damage function used for combat and other damage types, and displays the relevant output from the the messages file not usually available in DIL. These can cover a complex range of player actions, damage types, body positions for damage and so on. This is not simple hitpoint removal, and should only when you need to use complex internal damage events such as varying environmental/affect messages, simulating combat using advanced combinations of weapon types, hit positions and the relevant acts() and so on.
Parameters:
| Parameter | Type | Description |
|---|---|---|
ch | unitptr | The attacker (can be same as vict for self-inflicted/environmental damage) |
vict | unitptr | The character receiving hitpoint damage |
med | unitptr | The item involved (weapon, shield, etc.) or null for environmental/spell damage |
dam | integer | Hitpoints of damage to inflict (0 or positive) |
attack_group | integer | Message group determining which category of messages to use |
attack_number | integer | Message ID within the group |
hit_location | integer | Body location or special message type |
disp | integer | 1 = show combat message, 0 = silent damage |
Description:
Reduces the victim’s hitpoints and displays the appropriate combat message from the VME messages file. Handles all damage side effects including:
- Death handling if HP drops too low
- Position updates based on remaining HP
- Immortal protection (damage reduced to 0)
- Waking sleeping characters when damaged
- Setting up combat state between attacker and victim (when different)
Constants:
Full lists of constants can be found in vme.h. Common values:
| Constant | Value | Description |
|---|---|---|
| MSG_TYPE_WEAPON | 0 | Weapon attacks - attack_number is weapon type |
| MSG_TYPE_SPELL | 1 | Spell damage - attack_number is spell number |
| MSG_TYPE_SKILL | 2 | Skill damage - attack_number is skill number |
| MSG_TYPE_OTHER | 3 | Environmental/other - attack_number is MSG_OTHER_* |
| MSG_OTHER_BLEEDING | 1 | Bleeding while incapacitated |
| MSG_OTHER_STARVATION | 3 | Starving/thirsting |
| COM_MSG_MISS | -2 | Attack missed |
| COM_MSG_NODAM | -3 | Hit but no damage |
| COM_MSG_EBODY | -4 | Generic body hit |
Medium parameter usage:
| Context | med value |
|---|---|
| Melee attack | equipment(ch, WEAR_WIELD) |
| Off-hand attack | equipment(ch, WEAR_HOLD) |
| Shield block | equipment(ch, WEAR_SHIELD) |
| Spell/environmental | null |
Example:
// Starvation damage with message
rawdamage(self, self, null, 5, MSG_TYPE_OTHER, MSG_OTHER_STARVATION, COM_MSG_EBODY, TRUE);
// Weapon damage using wielded weapon
wpn := equipment(self, WEAR_WIELD);
if (wpn != null)
rawdamage(self, targ, wpn, 10, MSG_TYPE_WEAPON, wpn.value[0], COM_MSG_EBODY, TRUE);
// Silent bleeding damage
rawdamage(self, self, null, 2, MSG_TYPE_OTHER, MSG_OTHER_BLEEDING, COM_MSG_EBODY, FALSE);
See Also: position_update, meleeattack
insert
Signature:
void insert(sl: stringlist, i: integer, s: string)void insert(il: intlist, i: integer, n: integer)
Inserts a value at a specific index in a stringlist or intlist.
Parameters:
| Parameter | Type | Description |
|---|---|---|
sl | stringlist | The stringlist you are inserting to |
il | intlist | The intlist you are inserting to |
i | integer | The index where you want to insert the value |
s | string | The string to insert (for stringlist) |
n | integer | The integer to insert (for intlist) |
Description:
This procedure inserts a value at the specified index, shifting existing elements at that index and beyond to the right.
Tip: To simply append to a list, use addstring() or addint() instead.
Example (stringlist):
dilbegin stringlist add_in_order(sl:stringlist, s:string);
var
i : integer;
ln : integer;
code
{
if (length(sl) == 0)
{
addstring(sl, s);
return(sl);
}
ln := length(s);
i := 0;
while (i < ln)
{
if (length(sl.[i]) <= ln)
{
insert(sl, i, s);
return(sl);
}
i := i + 1;
}
addstring(sl, s);
return(sl);
}
dilend
Example (intlist - appending):
var
dirlist : intlist;
i : integer;
code
{
dirlist := {}; // Start with empty list
i := 0;
while (i < 10)
{
insert(dirlist, length(dirlist), i); // Append i to end
i := i + 1;
}
// dirlist is now {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
}
changeweather
Signature: void changeweather(z: zoneptr | unitptr, change: integer)
Sets the weather change value for a zone.
Parameters:
| Parameter | Type | Description |
|---|---|---|
z | zoneptr or unitptr | The zone, or a room unitptr (uses the room’s current zone) |
change | integer | The change value to set (clamped to -12 to +12) |
Description:
Sets the weather change rate for a zone. Positive values push pressure up (toward clearer weather), negative values push pressure down (toward storms). The value is clamped to the range -12 to +12. The change value is applied to pressure on the next weather update (each MUD hour), then natural drift modifies the change value for subsequent updates.
If a room unitptr is passed instead of a zoneptr, the zone the room is currently in is used. Non-room unitptrs are ignored.
Example:
z := findzone(self.zone);
if (z)
{
cur := z.weatherchange;
if (cur < 0)
changeweather(z, cur + 6); // Push toward clearing
else
changeweather(z, cur - 6); // Push toward storms
}
See Also: findzone, Zoneptr Fields
clear
Signature: void clear(i: integer)
Clears an interrupt.
Parameters:
| Parameter | Type | Description |
|---|---|---|
i | integer | The interrupt number to clear |
Description:
Clears the interrupt number “i”. If i is invalid, this will either clear a wrong interrupt or do nothing.