30 Sep, 2006, Guest wrote in the 1st comment:
Votes: 0
There are lots of languages for MUDs, I'd like to start this topic off by describing how a new script I've written in NiM5's NimScripts works.

Here's some interesting automations from NiM5:

Displacer Beast, uses 3 scripts, basic behavior is that it will randomly teleport itself around the mud, but when attacked, will randomly teleport its assailants around the mud.

Here, we'll create and stat the existing displacer beast mob,
which has 3 scripts attached to it.
Quote
You create a displacer beast!

Name: [displacer beast]
Vnum: [16720] zone: [ 40] Wilderness
Hp: [130/130] Mana: [ 0/160] Move: [280/280] Karma: [0] Exp: [100]
Drunk: [ 0/100] Thirst: [ 0/100] Full: [0/100]
Size: [ 24] Race: [ 0] Sex: [neutral] Scene: Outside Rask's
[1010]
Str: [ 18] Int: [ 18] Wis: [ 18] Dex: [ 18] Con: [ 18]
Lv: [ -1] AC: [ 70] Coins: [ 0] Fight: [0, 0]
Wait: [ 0] Invis: [ 0] Bounty: [ 0] Owed: [ 0]
Age: [ 17] Timer: [ 0] Trace: [ -1] Played: [0, 0]
Act: [npc aggressive wimpy] 0
Bonuses: []
Wielding: [0/25] Hitroll/Damroll: [2/4] [standing]
Carrying 0 items of weight 0.
Short: [a displacer beast]
Long:
A displacer beast is grazing nearby.
[16701] displacer-assist (0,0)
[16721] displacer-teleport (642,0)
[16720] displacer (9,0)


Note the three scripts on the displacer beast:
displacer-assist,
displacer-teleport,
displacer

The numbers next to the names of the scripts are coordinates
for the script, the first coordinate is the current delay in 20ths
of a second. The second number designates the value of the
autowait counter, between each line of the script.

The number to the left of the script name in the stat display is the vnum of the associated script. In (Link removed in accordance with Rule 5), the command 'stat <vnum>' displays the vnum for a script.

Let's take a look at the code for each script:

Vnum:  [16701]  Name:  [displacer-assist]
Type: [ 1] [EACH_PULSE (Each pulse.)]
Script:
wait(5);
do({assist displacer});


Script 16701 repeats each pulse. A pulse is about a 20th of a second and is part of a tick. The pulse script is a looping script. It waits 5 pulses (about a second) before it attempts to assist another displacer beast who is being attacked. This is a standard assist script and is used throughout the mud for various mobiles who assist each other.

There is 1 copy of the script on each displacer beast.

Here's the next script:
Vnum:  [16721]  Name:  [displacer-teleport]
Type: [ 1] [EACH_PULSE (Each pulse.)]
Script:
wait(random(1000));
jump(random(20000));


Displacer beasts do wander from room to room, but they also jump randomly between rooms every 1-1000 pulses, or about once every 4-5 minutes.

This is also a looping "pulse" script, which is constantly running and is attached to each displacer. If you attached this script twice to the same displacer, you could double the speed it teleports, but it is probably not worth it since you could also just decrease the random number generated in the wait() function call on line 1 of this script.

The final script for the displacer is for combat. While NiM5 has a combat script, it is not consistent and this is a much better way of writing a round-for-round special attack or special action script to be executed when the creature enters combat either aggressively or by being attacked.

Here is the "displacer" script, which is the combat-round special attack for the displacer:
Vnum:  [16720]  Name:  [displacer]
Type: [ 1] [EACH_PULSE (Each pulse.)]
Script:
wait(15);
if ( not(is(foe,{none})),
{
do({:glows with an eerie blue light!});
disarm(foe);
while ( and(sameroom(foe,me), move(foe,random(25000))));
}
);


In this script line 1 is the delay between teleports. For each round of combat, the creature could teleport an assailant at 15 pulse intervals, or about once every 3 seconds. The second line is a conditional which is standard for all combat scripts. This basically determines if the creature is fighting or not.

When the creature is fighting, the creature will emote "glows with an eerie blue light!", automatically disarm its foe, and then attempt to teleport its foe as many times as necessary until the foe is successfully teleported to a room with a vnum less than or equal to 25000, and greater than 0 (or, basically, any room in my 25000 vnum world).

So, that's basically how a displacer beast can be written with NiMScripts! Happy mudding!
30 Sep, 2006, Splork wrote in the 2nd comment:
Votes: 0
MUDL Overview by Jason Modisette(Kjartan)

This overview has a mixture of material aimed at people familiar with Sloth MUD and material aimed at people interested in the author's musings on languages. Sloth MUD is a heavily modified DIKU mud, based on the gamma 0.0 code release (so it split from the main DIKU base in late 1991).

MUDL stands for "MUD language", and is pronounced "muddle". It
is a scripting language for making monsters, objects, and rooms in Sloth MUD that do stuff that is too complicated to handle with existing techniques.

Historically, we have done this sort of work in Sloth MUD by hard-coding it in C as part of the MUD source code. The result is that Sloth now has about 30,000 lines of very non-generic code, 15% of the total body of code, to make monsters behave in certain ways. Most of the content in Sloth MUD was generated by experienced players, but if that content was going to have complex behavior then the players had to be given access to the source code. This was not good. They could steal the source code and start their own competing MUDs, which has happened several times over the history of Sloth, but even worse it was all in C with pointers and segmentation faults and everything. A bunch of novice programmers with minimal supervision were writing an embedded system in C. Stability suffered as a result.

There was also the problem that many of the people creating content for the MUD didn't have access to the source code, and so they had very limited capabilities to put "special" behaviors in their content. They had to use one of the very specific and limited custom routines already present in the C code. For example, if they wanted their wizard to cast the "fireball" spell there was a way to do it, but if they only wanted him to cast "fireball" if he encountered an ice elemental there was no way to do that. With MUDL, everybody can make content with this sort of basic logic, and the C coding is reserved for the core functionality of the game.

The primary design goal of MUDL, more important even than functionality, was that it be difficult to break the MUD with it. Now I will explain how I made it easy to use and hard to break.

Language Stuff
No Infinite Loops

MUDL is a large language (at least as MUD scripting languages go) which includes a library of more than a hundred system functions. It is, however, not Turing-complete. This was done deliberately; the missing elements are that (1) there is nothing in MUDL analogous to a goto and (2) there is no universal ability to set the values of variables.

The only construct in MUDL that allows looping is a foreach loop that executes a block of code once for each of the elements in an array. The first argument of the foreach statement is an expression that must have an array type (that is, it must evaluate to an array). When the interpreter encounters a foreach, the first thing it does is evaluate that expression and stick the resulting array someplace where nobody can mess with it while the loop is looping. Due to the lack of a general ability to set variable values, the loop index variable cannot be altered from inside the loop.

While MUDL code can't set loop index variables or most other variables, the user has the ability to create and set as many of a special type of writeable variable as he likes. MUDL only allows the user to create string variables. This is a deliberate design feature. The problem with storing the more complex data types like characters (in the sense of "Cast of Characters", not in the sense of "letters") and objects (in the sense "shovels, rakes, and implements of destruction") is that they can come and go and their identity isn't well defined. What if I kill a mold-encrusted zombie, and then another one is generated by the MUD? Is it the same zombie? Maybe not, but what if I kill Heinrich the Mad, Terror of Twickham Tower and eventually he is regenerated by the MUD? Theoretically it's the same guy; we sweep under the table the exact mechanism by which he is now Terrorizing again even though you killed him last Tuesday.

So, if MUDL stored Heinrich as a character-typed variable it might find that even though Heinrich is still around, the variable for him was no longer attached to that Heinrich because the Heinrich it was attached to was slain. Instead, it stores his name. The next time Heinrich is needed, the programmer can use the various system functions that MUDL provides for this purpose to find the best match to Heinrich. It may happen that there is no good match, a contingency that the programmer must deal with.

I have found that I rarely miss either of the lack of a general loop, or the lack of a general ability to set variables. I have run into a few cases where I had to make some pretty awkward MUDL code to do what I wanted, but I think it was worth it to avoid the possibility of infinite loops.
What if My Variable Gets Eaten By a Dragon?

MUDL has a hard life because it does not act in isolation, but runs inside of a dynamic MUD with other entities (players, that 30,000 lines of C code mentioned earlier, etc.) wandering around in it doing things. You might issue an innocuous MUDL statement like

cmd(%1,'north')

causing the being indicated by the variable %1 to move one room north. But there was a pit trap there! Full of spikes! And %1 fell into it, and DIED! And not only that, but since he wasn't a player the memory his data was occupying has now been FREED and REUSED! All before your poor MUDL script even gets to execute the next statement…

MUDL makes extensive use of null values to handle this. All variable types can be null, and any system function called with one or more null arguments is short-circuited (it isn't even called at all) and evaluates to null. This means if someone or something is destroyed by unforeseen circumstances in the middle of execution of some MUDL code, the system will very often handle it gracefully without requiring any special code to check for it: all of the code depending on the dead person being there just won't execute, but the rest of the script will carry on as normal.
There Is No ==, There Is Only =

Novice programmers (and most of the MUDL programmers in the world, as of this writing, are novice programmers) can be confused by the syntactical similarity between definition (=, in C) and tests for equality (==, in C). MUDL only uses this symbol for tests for equality, which are written =, and performs definition with the set statement. This is similar to the way it was long ago in BASIC, but even BASIC has moved away from this. I think it was a good distinction, and so MUDL has it.

MUDL's set command declaws some complex functionality by presenting it in a simple way. A novice programmer might wonder, "if the function height(%c) tells me how tall %c is, and I want to make %c 200 cm tall, then why can't I just say height(%c) = 200?" In MUDL he can. The command

set(height(%c),200)

does just that. Internally, this works because the compiler recognizes that the first argument of the set command is special and should not be treated like it would be if encountered roaming free in the wild. It's only necessary to extend this special treatment one level down the tree; the arguments of the first argument of set can be compiled just as usual.

Is MUDL Compiled, Or Interpreted, Or What?

Each MUDL script is compiled into an internal form which is higher-level than machine code. The internal form is a tree. This tree is then executed by an interpreter at the appropriate time.

Sloth MUD and MUDL are written in C. MUDL uses lex and yacc to generate its compiler. I am not a compiler guy; for example, my lex code has dangerous trailing context and I don't even know what that means.


A pair of arm guards that provide the PC with immune fire when the player fights a dragon…
addpr guards mudl
setpr guards 0 every_1
if(target(bearer(%o)) AND isnpc(target(bearer(%o))) AND
type(target(bearer(%o)))='dragon' AND (equipment(bearer(%o),3)=%o
OR equipment(bearer(%o),4)=%o),
(
if(affected_by_spell(bearer(%o),'fire breath'),
(
remove_affect(bearer(%o),'fire breath','')
),(
msg_character(bearer(%o),'\\d03Your $$p begins to glow brightly!',%o),
msg_everyone_else(bearer(%o),'\\d03$$n~`s $$p begins to glow brightly!',%o)
)
),
affect(bearer(%o),'fire breath','location=immune_blast level=45 seconds=30')
)
)
@
setpr guards 0 proc_enabled 1
30 Sep, 2006, Guest wrote in the 3rd comment:
Votes: 0
NiMScripts are not compiled, run fairly fast, and are interpreted in real time in memory. We did not use LEX or YACC. The code, bound by the NiMUD license, for the script interpreter © 1997-2006 by H. 'Locke' Gilliland III.

It uses a function evaluation function, which looks something like:

/*              
* Receives: func(param, …. )
* Breaks up parameters, and recurses into each.
*
* To add a new function, simply add it to the long if-statements.
*/
VARIABLE_DATA *eval_function( void * owner, int type, char *exp )
{
VARIABLE_DATA *value=NULL;
char name[MAX_STRING_LENGTH];
char buf[MAX_STRING_LENGTH];
VARIABLE_DATA *ppoint[MAX_PARAMS];
char params [MAX_PARAMS][MAX_STRING_LENGTH];
char *point;
char *original;
int x;

if ( *exp == '\0' ) return NULL;
if ( *exp == '{' || *exp == ';
char *point;
char *original;
int x;

if ( *exp == '\0' ) return NULL;
if ( *exp == '{' || *exp == '[' )
return new_variable( TYPE_STRING, str_dup( exp ) );

original = exp;

/*
* Prepare the pointers for the parameters.
*/
for ( x = 0; x < MAX_PARAMS; x++ )
{
params[x][0] = '\0';
ppoint[x] = NULL;
}

/*
* Grab the function name.
* func(param, …. )
* ^
*/
exp = skip_spaces( exp );
point = name;
while ( *exp != '('
&& *exp != ' '
&& *exp != '\0' ) *point++ = *exp++;
*point = '\0';
exp = skip_spaces( exp );

/*
* Grab and evaluate, recursively, each parameter.
* func(param, …. )
* ^
*/
[/code]

A translate variables statement, and a 'mini parser' which is used for some sub-routines such as the if conditional.

I don't find it to be the greatest language in the world and would have written it differently, but as far as I'm concerned 'case closed' on this part of the project. All I do now is add functions when necessary. It is a powerful language, and has been through several iterations. Parts of it are reminiscent of MS-DOS batch file format (in variable declarations only) and the rest is sort of like C.

The most innovative parts of the language are around the spell and skill applications of the language, where you can write spells in the three coexisting magical systems on the fly.

These are the functions related to this part of the language:
[quote]
Spell-related functions for use with both elemental and alchemical
systems.
mana(t,g) Modifies the mana of a target t by gain g
mix(t,types,quantities)
Attempts to mix reagents of mage t, returns TRUE if succeeds, types
and quantities quantify 1:1, ex:
mix(Locke, 10 2 33 201 222, * mixes components 10,2,33,201,222
1 1 1 2 1 ); * one of each, 2 of component 201
reagents(t,types,quantities)
Checks to see if a mage t has the required reagents, returns TRUE if
quantities are in the possession of the mage t
reagents(Locke, 10 2 33 201 222, * components index list
1 1 1 2 1 ); * one of each, 2 of component 201
[/quote]

The three systems (elemental, natural and alchemical) are also controlled by the spell type edited in the OLC's SPEDITOR.

Another interesting part of the language is the AGE or Ascii Graphical Engine, which works much like the meta-language Processing (www.processing.org)

You can create panels, animations and even buttons for Zmud in this language if you combine it with MXP.

Here is the function list for the AGE:
[quote]

> help graphics-functions^M
GRAPHICS-FUNCTIONS (Script)

Graphics functions work with the internal graphics buffer and are grouped
under the sub-system 'Ascii Graphics Engine' - a specialized MXP-enabled
ascii GUI/typesetting renderer. Utilizes a frame buffer with dynamic
resolution to store a screen buffer, which can be displayed to the user.

show() displays the framebuffer to the user
box(x,y,dx,dy) Draw a box using the current line style
rect(x,y,dx,dy) Draw a rectangle using the current brush
line(x,y,dx,dy) Draw a line using the current brush
circle(x,y,r) Draw a circle of radius r at x,y using current brush
color(a) Returns a color code, 255 if MXP, 16 colors if ansi
stroke(a) Set the color for drawing (see above)
point(x,y) Draws with current brush at x,y
draw or draw() Displays the current contents of the frame buffer
blank or blank() Blanks the frame buffer, redraws ruler (see rulers)
gmode(x,y) Sets the resolution of the framebuffer, x-size, y-size
By default, the frame buffer is usually 77x25
mode(0|1) same as below (?)
style(0|1) boxstyle setting, 0=thin 1=thick
ruler(0|1) toggles debug ruler; 0=off 1=on, mud-boot default=1
rulers or rulers() Returns the current value of the rulers toggle
clear or clear() Clears the screen, determined by 'set clrscr'
brush© Sets the 'brush' character
hline(y) Draws a horizontal line with the current brush
vline(x) Draws a verticle line with the current brush
column(x,y,w,{t}) display text in a formatted column (unimplemented)
wrap({text}) Wordwrap standard formatting 77 character
fill(x,y,dx,dy) Fill an area with the current brush character (?)
fills(x,y,dx,dy,s) Fill an area with a string, used w/ column (?)
text(x,y,{content}) Draws text content at x,y
textline(x,y,dx,dy,{content}) Draws text content along a line x,y->dx,dy
popup(x,y,w,h,{title}) Display a box with a title
button(x,y,{label}) Display an MXP button
[/quote]
30 Sep, 2006, Guest wrote in the 4th comment:
Votes: 0
One interesting innovation of NiMUD (NiM5) is the ability to not only create the game, but to create games within games .. you could, though I've never tried, write Space Invaders inside the mud itself.
01 Oct, 2006, Guest wrote in the 5th comment:
Votes: 0
Here's another example of scripting in NiM:

Quote
Vnum: [ 1] Name: [panel]
Type: [ 1] [EACH_PULSE (Each pulse.)]
Script:
* zmud graphics window default
gmode(90,31);
blank();
rulers(0);
stroke(6); popup(2,2,30,30,{Mud Weather Statistics}); stroke(9);
text(4,4,{Moon: }); text(11,4,moon);


This generates a simple panel that is updated when an immortal types ".show" …
02 Oct, 2006, Justice wrote in the 6th comment:
Votes: 0
I've been working on a general scripting snippet for SmaugFUSS based muds. As Samson will attest, there are a few issues with distribution, linking, and installation I need to deal with before it can be released. Currently I'm using the Mozilla Spidermonkey engine for javascript, at a later date I intend to embed Python.

Rather than invent my own language, I opted to use existing languages that people may be familiar with. The other affect of this is that there are already many tutorials and resources available. Additionally, users will learn a "real world" skill that can be applied outside of the mudding community.

Many commercial games use Python for scripting and it is common in the world of GIS. Javascript is available on almost any OS and is useful for various things like shell scripting, web scripting, etc. It's easy to embed into java and C/C++ programs using the 2 available Mozilla distributions, Rhino for java, and Spidermonkey for C.

Also, there's no reason why you couldn't write games using the scripting system. Currently I fully support scripting in any prog, command, or skill. Additionally, I allow you to write scripts onto include vnums to allow code to be reused.
0.0/6