I've received a number of "suggestions" about which spells I should impement on Coastal Legends, and then received additional "suggestions" on where I should stick my complicated spell code when I told the person "No, it's too difficult to do within the context of objective MUD code". :)

This webpage is less of a tutorial and more of a request: before telling me the MUD is worthless and "can't truly be called 3rd Edition D&D" (because I didn't implement Evard's Black Tentacles from page 201 in the Players' Handbook), please see how Melf's Acid Arrow has been implemented.

Melf's Acid Arrow is, conceptually speaking, a very simple spell. Here's the actual description of the spell from page 227 in the Players' Handbook:

Level:Sor/Wiz 2
Duration:1 round + 1 round/three levels
A magical arrow of acid springs from your hand and speeds to its target. You must succeed at a ranged touch attack to hit your target. The arrow deals 2d4 points of acid damage... For every three caster levels (maximum 18), the acid, unless somehow neutralized, lasts for another round, dealing another 2d4 points of damage for that round.

That being said, let's see the actual implementation, which consists of two parts: the initial casting, and the arrow that lasts at least another round if the caster is a 3rd level Mage or higher.

/bin/spells/arcane/melfs_acid_arrow.c

// Melf's Magic Arrow 
//
//  Written on 07/31/2000 by Grey

inherit STD_BIN_OB;

int main(object whom)
{
	int myroll, mylevel, x, rounds;
	object arrow;

	if(file_name(previous_object()) != "/bin/player/_cast")
	  return notify_fail("Stop foolin' around.");

	if(whom == this_player())
	  return notify_fail("You wish to damage yourself?");

	if(!whom->can_kill(this_player())) return 0;
	if(!this_player()->can_kill(whom)) return 0;

	// Determine timing of damage.  This is level-based.
	mylevel = this_player()->query_level("mage");

	// Initiate combat if this is the first attack
	this_player()->set_target(whom);
	if(!whom->query_target()) whom->set_target(this_player());

	if(present("invis_minor_globe", whom))
	{
	  this_player()->action("magic",({this_player(),whom}),
	    "$N $vfire Melf's Acid Arrow at $t, to no effect!");
	  return 1;
	}

	rounds = to_int(mylevel/3);
	myroll = rolldie("2d4");

	this_player()->action("magic",({this_player(),whom}),
	"$N $vfire Melf's Acid Arrow, damaging $t "+ myroll +" HP.");

	whom->do_damage(myroll, this_player());

	if(!whom) return 1;

	if(rounds)
	{
	  arrow = new("/std/obj/spells/acid_arrow");
	  arrow->set_wearoff(rounds*10);  
	  arrow->move(whom);
	  arrow->set_rounds(rounds);
	  whom->add_spell_effect(rounds*10, arrow);
	}
	return 1;
}

string get_desc1() { return "Damage enemy 2-8 HP"; }
string get_desc2() { return "Extra acid dmg every 3 levels"; }
string get_savethrow() { return "None"; }
int is_combat()    { return 1; } 

/std/obj/spells/acid_arrow.c

inherit STD_MATERIAL_OB;

int wearoff, rounds;
object attacker;

void create()
{
	::create();
	set_value(0);
	set_weight(0);
	set_id(({"melf's acid arrow"}));
	set_short("Arcane 2: Melf's Acid Arrow");
	set_invis(1);

	call_out("acid_burn", 4);   // Same call_out rate as combat
}

void set_wearoff(int x) 
{ 
	wearoff = x;
	attacker = this_player();
}

int no_drop() { return 1; }

void terminate_spell()
{
	wearoff = 0;
	remove();
}

mixed *query_auto_load()
{
        return ({ base_name(this_object()), 
        ({ wearoff, rounds }) });
}

void init_arg(mixed *args)
{
	wearoff = args[0];
	rounds = args[1];

	if(previous_object())
	if(interactive(previous_object()))
	  previous_object()->add_spell_effect(wearoff, this_object());
}

void acid_burn()
{
	int damage;
	object env;

	damage = rolldie("2d4");
	env = environment(this_object());
	if(!env) return;

	env->action("combat",({ env }),
	"$N $vburn with "+ damage +" HP of acid damage from a lodged arrow!");

	if(env && attacker)
	  env->do_damage(damage, attacker);
	rounds --;

	if(rounds > 0)
	  call_out("acid_burn", 4);
}

As you can see, it takes a lot of effort to implement something that only takes a few short sentences in the Players' Handbook. I normally don't publish my code like this, but perhaps this will help put things into perspective.

As of this writing, I've coded 37 spells for Coastal Legends. You may rest assured that many more will be added! The going is slow, though, and many spells cannot not be coded because of their subjective nature (they'd require a Dungeon Master's active participation, interpretaton, and roleplaying... they can't be reduced to pure formulas like a Melf's Acid Arrow can).

An example of a spell that cannot be coded is the "Wish" spell. The only way the "Wish" spell could be implemented would be to either make a menu of pre-coded things that the caster would be allowed to wish for, or to not code anything at all and have it just send a message to a coder... if one is logged-in and not idle. The first option would be rather lame, and the second... well, it would like be asking a bunch of counties in Florida to do manual vote recounts and expect the method of interpreting the votes to be done consistently! :)


This page may be viewed from any browser, not just one from Redmond.
Last Modified: Jan 14, 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.