Let's have a little fun with our players! :) In a MUD, players expect the monster to just sit there, doing simple weapon attacks, while the player unleashes an arsenal of spells, feats, skills, and so on. Now let's turn the tables and give the players a little surprise!

ragbeard.c

inherit STD_LIVING_OB;

int roundnumber = 0;

void create()
{
	::create();
	set_name("ragbeard");
	set_id(({"ragbeard","pirate"}));
	set_short("Ragbeard the pirate");
	set_race("human");
	set_gender("male");
	set_long("Ragbeard the pirate takes a brief look at you and then "+
	"says \"%^GREEN%^Arrrrr!  Avast matey, or I be sendin' ye to "+
	"Davey Jones' locker!%^RESET%^\".  His flambouyant white ruffled "+
	"tunic is unbuttoned almost down to his navel.");
	set_level(6);
	classes = (["cleric":4,"fighter":2]);

	set_weight(120);	
	set_size("M");

	set_stat("int", 13);
	set_stat("wis", 16);
	recalc_maxsp();

	set_diemsg("$N says: \"Arrrrr!\" and dies.");  

	learn_spell(2,"divine","flame blade");
	learn_spell(2,"divine","silence");
	learn_spell(1,"divine","cure light wounds");
	add_sp(10);

	force_me("prepare flame blade");
	force_me("prepare flame blade");
	force_me("prepare silence");
	force_me("prepare silence");
	force_me("prepare cure light wounds");
	force_me("prepare cure light wounds");
	force_me("prepare cure light wounds");
	force_me("prepare cure light wounds");
	force_me("prepare cure light wounds");
	force_me("prepare cure light wounds");
}

int special_attack(object victim)
{
	if(!victim) return 0;
	if(!present(victim, environment(this_object()))) return 0;
	if(victim->query_invis()) return 0;

	roundnumber++;

	switch (roundnumber)
	{
	  case 1: { force_me("pray flame blade on me"); return 1; }
	  case 2: { force_me("pray silence"); return 1; }
	  default: 
	  {
	    if(!query_wielded()) 
	    {
	       force_me("equip staff"); 
	       return 1;
	    }
	    return 0; 
	  }
	}
}

void track_target()
{
	if(hp < max_hp)
	  force_me("pray cure light wounds on me");

	::track_target();
}

Hopefully you've already read the tutorial on simple monsters. If not, surrender now!! Go back and read the simple monster tutorial before reading further about smart monsters. :)

Let's first concentrate on changes we've made within the create() function.

set_level(6);
classes = (["cleric":4,"fighter":2]);

Here we're changing Ragbeard's mix of character classes. He's now a 4th level cleric and a 2nd level fighter. His base Attack Bonus and Saving Throws will be automatically recalculated according to these new settings. We're raising his Cleric class level so he can cast clerical spells.

set_stat("int", 13);
The MUD treats monsters differently when they have an Intelligence score of 13 or higher. This setting automatically activates the "smart monster" code. If a player flees a battle, and if the monster is smart and has isn't on the verge of death, it will begin tracking and pursuing the player!

set_stat("wis", 16);
recalc_maxsp();

The primary statistic for Clerics is Wisdom. This setting will give Ragbeard the ability to memorize more spells and give his spells a decent DC (difficulty rating). The "recalc_maxsp" function should be used whenever you reset a monster's level and/or stats pertaining to spellcasting. This function recalculates what spell levels the monster is capable of learning and preparing.

learn_spell(2,"divine","flame blade");
As of this writing, there are two categories of spells: "divine" and "arcane". This function adds the level 2 divine spell named "flame blade" to Ragbeard's list of known spells.

add_sp(10);
This "heals" 10 SP slots (spellcasting points), allowing Ragbeard to begin preparing spells.

force_me("prepare flame blade");
This tricks the MUD into thinking Ragbeard is a player and just typed the "prepare flame blade" command.


Okay! We're done with the create() function. Let's move ahead into new territory!

int special_attack(object victim) { ... }
Here's where the fun really begins. During the monster's turn in a combat round, the MUD checks if there's a special_attack function existing in the monster's code. If so, it executes the function before making a melee/weapon attack.

If you define this function, the MUD expects it to return an integer number of either 0 or 1. If a zero (0) is returned, the MUD will cause the monster to make a melee/weapon attack as normal. If a one (1) is returned, the monster will not make a normal melee/weapon attack. You always want to return a 1 if the monster is doing a special Combat Action -- usually this means quaffing a potion, using a magical item or casting a spell.

Notice that our special_attack function is expecting an object argument that we're assigning to the local variable "victim". The MUD will assign the monster's current target to this variable.

if(!victim) return 0;
if(!present(victim, environment(this_object()))) return 0;
if(victim->query_invis()) return 0;
Here's some housekeeping you should always do in your special_attack function. This makes sure your monster does indeed have a target, that target is in the same room as the monster, and the target is visible.

roundnumber++;
This is the same as saying roundnumber = roundnumber + 1;
Notice how at the very top of the file we defined a global integer variable named "roundnumber". (By the way, "global" means a variable is visible to all functions. "local" means a variable only exists within the context of the current function).

switch (roundnumber) {
case 1: { ... }
case 2: { ... }
default: { ... }

This is called a "switch-case" statement. The MUD checks the current value of roundnumber and sees if it matches any of the case values. If it does, it executes the code within the matching case:. Otherwise, it executes what's in the default: case.

case 2: { force_me("pray silence"); return 1; }
In this example, Ragbeard will cast the Silence spell in the 2nd round of combat. A 1 is then returned so the special_attack function is immediately exited and Ragbeard doesn't attack with his staff during this same combat round.

default: { if(!query_wielded()) { .... } return 0; }
Ragbeard casts the Flame Blade spell during his first round. This causes him to unwield his staff. When the Flame Blade spell eventually expires, he'll want to rewield his staff. The "query_wielded" function returns a zero (0) if Ragbeard isn't wielding anything (otherwise, it'll return an object pointing to the weapon he's wielding, which our "if" statement will consider a "true" value). Remember that the ! inside an "if" statement means "not". So we're really saying "if Ragbeard is not wielding anything, do what's contained within the {}s". In this example, we force him to wield his staff and then exit the function. Otherwise, we skip the {}s and return a zero (0), meaning he should execute a melee attack as a normal monster.

Whew! Now let's look at the last function.

void track_target()
{
    if(hp < max_hp)
      force_me("pray cure light wounds on me");

    ::track_target();
}

Remember than an Intelligence statistic of 13 or higher causes a monster to track a fleeing player. This is the function that does it. All monsters have a built-in "track_target" function. All we're doing in our custom-version here is checking if he's fully healed or not. If he isn't (if his Hit Points are less than his maximum Hit Points), we force Ragbeard to cast a healing spell upon himself. We then call "::track_target();", which tells the MUD to continue the pursuit by using the default track_target code that's defined for all monsters.

Defining your own "track_target" function is completely optional. The monster will pursue his enemies without it, as long as it has an INT of 13 and more than a third of his Hit Points remaining.


This page may be viewed from any browser, not just one from Redmond.
Last Modified: Jan 1, 2001
Page written by Dave Shay (how to contact me).
Page content copyright ©2000-2001 by Dave Shay. Do not copy without express permission.
Dungeons & Dragons is a registered trademark of TSR, Inc., a subsidiary of Wizards of the Coast.
D&D rules, races, classes, features, etc. used in accordance with the TSR, Inc. Internet Policy.