DG_scripts, mob_progs and other scripting languages

 
Post new topic   Reply to topic    mudlab.org Forum Index -> Building
View previous topic :: View next topic  
Author Message
Molly O'Hara



Joined: 11 May 2005
Posts: 99
Location: Sweden

PostPosted: Sun May 29, 2005 8:19 am    Post subject: DG_scripts, mob_progs and other scripting languages Reply with quote

I thought it might be helpful to compare some of the advantages and disadvantages of different scripting languages, like we did with the codebases. I have some first hand experience of both Mob_progs and DG_scripts myself, so I will start out with a very basic comparison between those.

It has however been almost 6 years since I last worked with Mob_progs, so hopefully someone else will take over the task of describing more in detail what you can achieve with the latest generation of those. I remember them as rather limited in what they could do, and buggy in the sense that they often crashed the mud. But that could of course have been due to my own inexperience at the time. Also they have probably developed and improved over the years, like the DG_scripts have. But based on what I know then and what I know now, I’d take the DG_scripts over the mob_progs any time.

There are many similarities between DG_scripts and mob_progs. For instance they share many of the script types (like speech, command, greet etc.). The main set-up of the script is also similar; both systems use a number of if_checks to determine if and why an action in the script will be performed or not.

The basic difference lies in the syntax. The variables in mob_progs resemble the ones used for socials in the code, and look a bit cryptic at first glance. The variables in DG_scripts are usually a bit easier to grasp at first look. Below are some examples to illustrate this.

In Mob_progs some of the variables for different actors in a script are:
$i the name of the scripted mobile itself.
$n the name of whomever caused the trigger to happen.

In DG_scripts the same variables would be:
%self.name% the name of the scripted mobile itself.
%actor.name% *) the name of whomever caused the trigger to happen.
*) (or %actor%, based on the context, more about this below).

To go on with the variables, the Mob_progs use the following for
$e he,she,it based on sex of $n.
$j he,she,it based on sex of $i.
$m him,her,it based on sex of $n.
$k him,her,it based on sex of $i.
$s his,hers,its based on sex of $n.
$l his,hers,its based on sex of $i.

In DG_scripts you use the more straightforward variables "hisher", ”heshe” and ”himher”.
"heshe" returns "he", "she" or "it" depending on actor’s sex
"himher" returns "him", "her" or "it" depending on actor’s sex
”Hisher” returns "his" or "her" or "its" depending on actor’s sex

* Example:
%echo% %actor.name% grins as %heshe% reaches for %hisher% dagger
will be read
‘Bobba grins as he reaches for his dagger’ if Bobba is male, otherwise
‘Bobba grins as she reaches for her dagger’

There are of course other syntax differences, but with these examples I will leave the description of Mob_progs to someone with a more recent experience of them, and concentrate on some of the things you can do with the DG_scripts.

The symbols % % are used to define variables and/or special ‘script commands’. They replace the older system of using the prefixes m, o and r for mob, object and room commands respectively, (like for instance oecho, mecho or wecho). By using %echo% instead, the same command works for all three types of scripts. Basically the % %are used to separate ‘ordinary’ mud commands that all players have, (like look, say, give, take), from ‘imm’ commands (like echo, send teleport)*)

*) (For those that don’t know, ‘%send% %actor% <message>’ determines what the actor sees, while ‘%echoaround% %actor% <message>’ determines what everybody else in the room sees).

The same logic applies to decide whether to use %actor% or %actor.name%.
%Actor% is a reference to the player exactly, the unique id number, while %actor.name% is simply the name of the player/mob. The basic rule is to use %actor% with 'imm' commands (like: %echo%, %send%, %echoaround%, %damage% %teleport% etc), and %actor.name% with mortal commands, (like tell, whisper, kill, look, give etc).

If_checks are used by both DG-scripts and Mob_progs. Every if_check loop needs to be closed by an endif, or the script will screw up or, in the worst case, the Mud will crash and not reboot.

You can check for a lot of simple things with if_checks, like race, sex, class, level, align, visibility etc. But you can also check for more complex things, like if the actor is fighting, using a particular weapon or armor, riding, following someone or having a follower etc.

For instance:
if %actor.riding% or more specifically
if %actor.riding.vnum(1234)%
will determine if someone is riding a certain mob.

Or, you can check if the actor is followed by a certain mob and that mob is present in the room.
Example:
Script Type: Greet
Num arg: 100
Arg:
Wait 1 *)
if %actor.follower(Joe)% && %actor.follower_in_room(Joe)%
%load% obj 3301 %actor%
say Thank you for bringing Joe to me, %actor.name%!
%load% obj 3301 %actor%
emote hands over a token.
else
bow %actor.name%
endif

*) Note the delay at the beginning of the script. Without that delay the script won’t work, because the code is instantaneous, and will trigger the script before the follower arrives in the room.

It is usually a wise precaution to always use a delay at the beginning of a script. One of the few times when you should NOT use them is with the command line ‘return 0’. (This is used to stop a command from being executed, for instance to prevent someone from receiving or using an object unless they have the right flag, or to stop a player from using two of the same item):

Example:
Script Type: Wear
Num arg: 100
Arg:
if !%actor.varexists(doneQuest1)%
return 0
%send% %actor% You are not worthy of using this item, %actor.name%
%echoaround% %actor% %actor.name% is a CHEATER!
else
wait 1
eval w1 %actor.eq(16)%
*/Primary wield
eval w2 %actor.eq(2Cool%
*/Secondary wield
if ((%w1.vnum%==5641)||(%w2.vnum%==5641))
return 0
%send% %actor% You are not strong enough to handle two of these items.
endif
endif

The most common if_check of all is to ascertain if the actor is a NPC or not. You do this by using the line ‘if %actor.is_pc%’
or ‘if %actor.vnum%<0’.

This if_check is used for any number of things, for instance to avoid some spam in a Greet script by making it react only to PCs. Another use is to stop a player from using a charmed mob to receive the reward more than once in a restricted quest.

Example:
Script Type: Receive
Num arg: 1
Arg:
if %actor.is_pc%
if !%actor.varexists(quest1)%
set quest1 1
remote quest1 %actor.id%
*<load and hand out the reward, etc.>
else
say You have already done this Quest, %actor.name%.
endif
else
say May the Demons eat your minion, cheater!
mkill %actor.master% *)
mslay %actor%
endif

*) Notice the order in the last else option. You have set the script to attack the master before slaying the follower, or of course no master will exist to attack. DG_scripts are very logical that way, and it is often the reason why some part of a script ‘inexplicably’ won’t work. It’s so easy to miss little things like that in a long script.

Another common reason for why a script doesn’t work is that you make the mob junk an item that it received before checking the vnum of it. The logical order is very important in scripts.

Scripts can also be used to make charmed mobs or other followers enter portals, climb or descend ladders or jump obstacles. This can be set up by using a Command trigger, with the argument *.
Below is a universal script, covering all presently available options in my mud. (The || stands for ‘or’ in the scripting language).

Example
Script Type: Command
Num arg: 100
Arg: *
Commands:
if %cmd.mudcommand% == enter
if ((%self.master% == %actor.name%)||(%self.ridden_by% == %actor%))
enter %arg%
endif
elseif %cmd.mudcommand% == climb
if ((%self.master% == %actor.name%)||(%self.ridden_by% == %actor%))
climb %arg%
endif
elseif %cmd.mudcommand% == descend
if ((%self.master% == %actor.name%)||(%self.ridden_by% == %actor%))
descend %arg%
endif
elseif %cmd.mudcommand% == jump
if ((%self.master% == %actor.name%)||(%self.ridden_by% == %actor%))
jump %arg%
endif
else
return 0
endif

(A skilled scriptor could no doubt make this shorter and more elegant, but I tend to stick to the simple and self-explanatory myself).

Scripts are often used to make mobs harder to kill, or just react in slightly more intelligent ways than just standing there waiting to be slaughtered. They can be set to cast different spells quaff healing potions when they drop below a certain hitpercent, load helpers, teleport all over the place or track and hunt the player that attacked them if he flees from battle.

But the main use for scripts, at least in our Mud, is to run different built-in Quests. When I first started designing Quests, I was extremely naive. I thought the player would just collect the reward for solving the Quest and the happily move on the next challenge. The thought of someone setting up a bot to mindlessly do the same quest over and over again, never even entered my mind, perhaps because it’s something I’d never do myself. Over the years we have learnt better, and we are using the DG_scripts in many different ways, to thwart the different attempts of cheating the purpose of a Quest. We even use them to some extent to stop players ‘blabbing’ about the solutions, by randomising the outcome, or sending players on different paths depending on race, class, sex, align or level.

The extremely useful player flags (%actor.var(whatever)%) have already been mentioned above. They are of two kinds, global or remote. A global flag can only be checked by the mob/object/room that set it, and it goes away when the mud crashes or reboots. A remote flag stays on the player’s id until removed by another script or an imm.

The global flags can for instance be used for restricting a quest to once per player and reboot, while at the same time leaving the option open for other players to do it. Or for stopping a mob from loading more than one helper when it drops blow a certain hitpercent. (Without the flag it will load a new helper with each pulse). It can also set the script to only react to the player the first time they enter the room, which prevents a lot of spam. The drawback when used for Quests is that you need to complete the entire task in the same session. Should the Mud crash, you need to start from the beginning again.

That’s why the remote flags are so useful for Quests. A player can work with the same Quest for months, if necessary. You can set up a complex Quest in ten or more steps and have the script change the player flag each time a new part of the Quest is completed. In this way the same mob can be set to react differently to the same player in different stages of the Quest. It can also remove the flag or set it back to a lower number if the player chooses the wrong path

Example:
if %actor.varexists(whatever)%
if %actor.whatever% == 1
set whatever 2
remote whatever %actor.id%
* <do stuff>
halt
elseif %actor.whatever% == 2
set whatever 3
remote whatever %actor.id%
* <do stuff>
endif
endif

Scripts can also be used for things like ‘Games in the Game’, subskills, crafting, or features like fishing, farming, gardening, lumberjacking, mining, etc. (Most of these can of course also be run by code; it’s usually a question whether the Coders or the Builders are the most active at the Mud design).

The best thing about the DG_scripts is that the code is being developed continuously, since Welcor took over the management of it. There is a pretty active mailing list, where you can get answers to any scripting questions, and several coders, (including our own) are contributing with new code, examples and suggestions for improvement.

Below are some examples of particularly useful relatively recent additions:

You can now load an object directly into the player's inventory with the command line
%load% obj <vnum> %actor%

You can also load items directly onto the wear_locs or into containers. For instance

%load% obj 215 %actor% lwrist
will put object 215 on to actors left wrist

%load% obj <vnum> %container%
will load an object directly into the container.

You can check if a certain mob is in a certain room with the command
if %findmob.21311(21332)%
(the first vnum is the mob, the second is the room)

The latest version of dg scripts also allows for several variable fields:
For instance
%actor.fighting.eq(wield).shortdesc%
gives the name of the weapon being used against %actor%, if any.

This post is way too long already, so I’ll stop here. We could open another thread for people to post some examples of ’cool’ or useful scripts/mob_progs. But let’s try and keep this one to the basics of the different scripting languages, if possible.
Back to top
View user's profile Send private message Send e-mail Visit poster's website
Author Message
Scandum



Joined: 13 May 2005
Posts: 28
Location: I'm in the TV

PostPosted: Sun May 29, 2005 11:11 am    Post subject: Reply with quote

As you mentioned yourself, the differences do not completely lie at the language, but how well the language has been implemented. As far as I know my mud has the most developed mob_prog derived implementation so I'll refer to it as mp2. I won't get syntax specific though.

Mp2 uses hashing and tokenization giving it speeds comparable to native c code. One problem of a mud surpassing over 50.000 lines of script is cpu and memory taxation. I must say I'm not sure if resources are a problem with purely string based scripting languages, and to what degree muds limit their builders to using tick based programs.

The mob_prog code on my mud cannot crash the program, and I'm not sure if that would make it better than DG scripts, though the DG scripts on BubbaMUD might not crash either. So I hope you see the problem in discussing different languages as static entities.

Using %pointer.value/function% is a nice improvement. What I found missing in your explenation of DG if checks were tags. For example: eval w1 %actor.eq(wear_lhand)% would be easier to read and write than filling in 16.

The variable system seems dodgy as well. Setting quest1 to 4 on a player might seem okay, but if 2 area creators use quest1 there is a problem. The more areas the harder it is to maintain, as well as you would want to hash the variables for speed when a player ends up with over 200 variables. An area with 10 quests will need 10 variables, possibly more.

mp2 gives 128 bits for each area to each player. The builders can use them however they see fit, allowing 32 quests existing of 16 steps, or 16 quests existing of 256 steps, or 128 quest existing of 2 steps, like the ones in your examples. It allows some useful bit operations as well. A bitrange can be set on every object, room, mob, and area as well. Teaching builders how to use binary is a bit of a drag, but mostly they catch up eventually.

Indentation seems lacking as well. Though you might have forgotten to use pre tags, it's possible dg doesn't allow for it. Mp2 automatically indents scripts, as well as adding syntax highlighting when displaying them. Useful to see if you forgot an endif or made a typo.

From what I gathered on mud connector DG has debugging problems as well. Mp2 allows builders to enter a debugging mode so you can watch precisely what a mob is doing, especially if checks, actions that do not generate spam, and parts that are executed in quiet aka silent mode.

mp2 also has a fully tokenized scripting language for object and room programs. Which generally looks something like this:

Code:

[ 3721] glathkar the Drinker
<  1> [ 20%] TRIG_DAMAGE
IF CHECK     OIF_WEAR_LOC ! WEAR_NONE [2 0]
<  2> [----] TRIG_VOID
IF CHECK     OIF_USER_PERCENT_HITPT > 15 [3 4]
<  3> [----] TRIG_VOID
GOD COMMAND  mpechoat self You feel a rush of energy run up your arm!
<  3> [----] TRIG_VOID
APPLY TEMP   OAPPLY_HIT 10
<  3> [----] TRIG_VOID
SET QUEST 0 5 0
<  4> [----] TRIG_VOID
IF CHECK     OIF_USER_SEX = SEX_FEMALE [5 6]
<  5> [----] TRIG_VOID
GOD COMMAND  mpechoat self Glathkar the Drinker murmurs 'I recommend withdrawing from the fight, Mistress.'
<  6> [----] TRIG_VOID
GOD COMMAND  mpechoat self Glathkar the Drinker murmurs 'I recommend withdrawing from the fight, Master.'
<  7> [100%] TRIG_WEAR
IF CHECK     OIF_USER_SEX = SEX_FEMALE [8 9]
<  8> [----] TRIG_VOID
GOD COMMAND  mpechoat self Glathkar the Drinker murmurs 'Greetings Mistress.'
<  9> [----] TRIG_VOID
GOD COMMAND  mpechoat self Glathkar the Drinker murmurs 'Greetings Master.'
<10> [100%] TRIG_TICK
ADD QUEST 0 5 1
<10> [----] TRIG_VOID
IF O QUEST 0 5 = 30 [11 0]
<11> [----] TRIG_VOID
SET QUEST 0 5 0
<11> [----] TRIG_VOID
IF CHECK     OIF_USER_SEX = SEX_FEMALE [12 13]
<12> [----] TRIG_VOID
GOD COMMAND  mpechoat self Glathkar the Drinker murmurs 'I'm thirsty for blood Mistress.'
<13> [----] TRIG_VOID
GOD COMMAND  mpechoat self Glathkar the Drinker murmurs 'I'm thirsty for blood Master.'

If checks are translated into a pointer to the true, and false argument. As most people can probably guess it's indeed a pain in the ass to write big programs with it.
Back to top
View user's profile Send private message Visit poster's website
Author Message
Molly O'Hara



Joined: 11 May 2005
Posts: 99
Location: Sweden

PostPosted: Sun May 29, 2005 3:02 pm    Post subject: Reply with quote

Scandum wrote:

The mob_prog code on my mud cannot crash the program, and I'm not sure if that would make it better than DG scripts, though the DG scripts on BubbaMUD might not crash either. So I hope you see the problem in discussing different languages as static entities.


Yeah, I realise that, and this is basically why I started this thread, to see what has developed over the years. Like I said, it's been almost 6 years since I last worked with mob_progs, and back then room_progs and object_progs weren't even implemented in our Mud. The DG_scripts too used to crash the Mud frequently in the past if the script was badly written, just a missing endif would do it. Random triggers used to be quite dodgy too, probably because they were badly written and created spam and unintended interaction between different scripts. A lot of work has been put into making the code more stable, I guess the same thing has happened to the mob_progs.

Quote:

The variable system seems dodgy as well. Setting quest1 to 4 on a player might seem okay, but if 2 area creators use quest1 there is a problem. The more areas the harder it is to maintain, as well as you would want to hash the variables for speed when a player ends up with over 200 variables. An area with 10 quests will need 10 variables, possibly more.


I agree about this, and if there is a better way of doing it, I'd appreciate some input on it. The best players on our Mud have 4 pagelengts of variable lists, and we even implemented a special stat command to be able to view the variables separated from other player stats. The 'quest1' flag was just an example though, normally we try to use flags that are related to both the zone and the questmob they refer to. For instance in a large Greek archipelago zone that I am working on now, the flags refer to both the island and the mob on it.
(Example: if %actor.varexists(TenedosGuard)%)
That makes it easier to keep the flags unique, but of course the problem will be building up with each new quest added.

Quote:
Indentation seems lacking as well. Though you might have forgotten to use pre tags, it's possible dg doesn't allow for it. Mp2 automatically indents scripts, as well as adding syntax highlighting when displaying them. Useful to see if you forgot an endif or made a typo.


Heh - I think the indentation problem is with this webpage, not with the DG_scripts. The examples I gave actually were indented when I wrote the post down in wordpad, but when pasted into the page here, the indentation disappeared for some reason. (Possibly my fault, I am a computer idiot, as I frequently point out. Probably that is why I still like the DG_scripts better than the mob_progs, because they look less like code to me...) Razz
Back to top
View user's profile Send private message Send e-mail Visit poster's website
Author Message
Scandum



Joined: 13 May 2005
Posts: 28
Location: I'm in the TV

PostPosted: Sun May 29, 2005 3:43 pm    Post subject: Reply with quote

To translate one of your programs, it would look like this:
Code:

>give_prog i6000~
mpjunk i6000
if quest (0,4,$n) == 0
  mpmset $n quest 0 4 1
  *<load and hand out the reward, etc.>
else
  say You have already done this Quest, $N.
endif
~

There's no need to check for a npc because mobs can't trigger other mobs. (except with a special command/trigger)

The first digit in the quest check is the offset (0) and the second digit is the number of bits (4).

The data itself is visible as hex like this:
Code:

       Quest: Ellis City             #0000-0000-0000-0000-0000-0000-0029-0008
       Quest: Elven Empire           #0000-0000-0000-0000-0000-0000-0000-2021
       Quest: Island of Paragon      #0000-0000-0000-0000-0000-0000-0022-8000
       Quest: Manitou Marsh          #0000-0000-0000-0000-0000-0000-0000-01AA
       Quest: Ruins of Sharthea     
#0000-0000-0000-0000-0000-0000-0000-03C5
       Quest: Tethyr                 #0000-0000-0000-0000-0000-0000-2000-0150
       Quest: Tower of Training      #0000-0000-0000-0000-0000-002F-00F2-4345

While I'm not bad with reading hex it's close to impossible when odd bit ranges are used like bit 3 to 7. As you can see 128 bits are more than enough which is why we ask the builders to try to use groups of 4, 8, or 12 bits for their quests to improve readability.

While it might look odd at first it works quite well.
Back to top
View user's profile Send private message Visit poster's website
Author Message
Molly O'Hara



Joined: 11 May 2005
Posts: 99
Location: Sweden

PostPosted: Sun May 29, 2005 8:49 pm    Post subject: Reply with quote

Scandum wrote:
There's no need to check for a npc because mobs can't trigger other mobs. (except with a special command/trigger)


What our NPC-check is used for is players using a charmed mob to give the quest-item to the quest-mob and receive the reward in return, to avoid the questflag being set on themselves. Then they just order the charmie to drop or give them the reward. (In our system any mob can trigger other mobs).

As for your system of storing the data, how do you display the list to the builders? Is there a command to view all existing quests that all builders have access to?
Back to top
View user's profile Send private message Send e-mail Visit poster's website
Author Message
Scandum



Joined: 13 May 2005
Posts: 28
Location: I'm in the TV

PostPosted: Mon May 30, 2005 8:06 am    Post subject: Reply with quote

Molly O'Hara wrote:
What our NPC-check is used for is players using a charmed mob to give the quest-item to the quest-mob and receive the reward in return, to avoid the questflag being set on themselves. Then they just order the charmie to drop or give them the reward. (In our system any mob can trigger other mobs).

We used to have the same problem, though it only worked well for simple quests. Players can still make level 1 characters to do certain quests, but few think/bother with it.

Quote:
As for your system of storing the data, how do you display the list to the builders? Is there a command to view all existing quests that all builders have access to?

It's added to the stat command for objects, mobs, etc. The data is displayed as 32 hexadecimal digits for builders as well. Though it's not very readable builders somehow never had a problem with it.

A global quest list isn't available, mainly because it would involve quite some work. Another problem is players sharing walkthroughs for quests, so there's little motivation to make quests any easier. Builders never showed to have the need for it either. They would probably help with updating a quest journal for their area, which could be quite fun using dynamic descriptions.
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    mudlab.org Forum Index -> Building All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

Powered by phpBB © 2001, 2002 phpBB Group
BBTech Template by © 2003-04 MDesign