Necromancer's Raise Dead

Posted by Celestine on Wed 25 Jun 2003 08:49 PM — 6 posts, 20,544 views.

#0
I decided to add a necromancer class, and I realized that the entire point of being a necromancer would be to raise a dead corpse. Glancing over the spell creation data, I discovered that spells only affect certain properties of some character/object or another. To raise the corpse, I would need to make it a command. Knowing that, I decided to go ahead and make a raise dead function which looks like the code below. I have successfully added all the necessary amendments to make it a command. However, executing this command from within the game crashes the server.

Basically I have two questions. First, is it possile to make this a spell? Second, what exactly is wrong with the spell right now (example, I typed in raise crawler to raise a carrion crawler)? The code is as follows:

void do_raise_dead( CHAR_DATA *ch, char *argument )
{
OBJ_DATA *corpse;
OBJ_DATA *obj;
MOB_INDEX_DATA *pMobIndex;
CHAR_DATA *victim;
sh_int vnum;
char arg2[MAX_INPUT_LENGTH];
char temp[MAX_INPUT_LENGTH];
char target[MAX_INPUT_LENGTH];
char cname[MAX_INPUT_LENGTH];
char buf[MAX_STRING_LENGTH];
char *ptr;
int hash, cnt, count;
ptr = cname;

if ( ch->class != 13 )
{
send_to_char( "You are not a necromancer!" , ch );
return;
}
if ( argument[0] == '\0' )
{
send_to_char( "Which corpse do you want to raise?" , ch );
return;
}
if ( ( obj = get_obj_here( ch, argument ) ) == NULL )
{
send_to_char( "You do not see that here.\n\r", ch );
return;
}

corpse = get_obj_here( ch , argument );
strcpy(cname,corpse->name);
ptr = one_argument( ptr , temp);
ptr = one_argument( ptr , target);

//from the minvoke code in act_wiz.c
count = number_argument( corpse->name, arg2 );
for ( hash = cnt = 0; hash < MAX_KEY_HASH; hash++ )
for ( pMobIndex = mob_index_hash[hash];
pMobIndex;
pMobIndex = pMobIndex->next )
if ( nifty_is_name( arg2, pMobIndex->player_name )
&& ++cnt == count )
{
vnum = pMobIndex->vnum;
break;
}
victim = create_mobile( pMobIndex );
//players can't raise corpses of other players after pkill
if (!IS_NPC(victim))
{
send_to_char( "You cannot raise that corpse." , ch );
do_purge(ch,victim->name);
return;
}

//renames newly raised creature and basically makes it a pet.
sprintf( buf, "%sA neck tag says 'I belong to %s'.\n\r", victim->description, ch->name );
STRFREE( victim->description );
victim->description = STRALLOC( buf );

char_to_room( victim, ch->in_room );
add_follower( victim, ch );
ch_printf( ch , "The corpse of %s, which you have just risen, now follows you.\n\r", victim->name );
return;
}

USA #1
Actually, the usual method is to make this a spell rather than a command for various reasons. I have a raise dead spell you'd probably be able to port with minor alterations :) I'll have to find it so it may be a few hours.
USA #2
Ok, I found it but its really too long to post here so email me and I'll send it to ya :)

#3
I put in the code, following the directions, but I was unable to find where to put the below code in. const.c didn't have a skill_table, and I grepped but found nothing. When compiling without it, I get a conflicting types error (magic.c, 6375) and a previously defined error (mud.h, 4270). The conflicting types is just a function declaration, and the mud.h error is a declare_spell_fun line. The skill_table requirement is displayed below. Does anyone know where this should go? I'm using basically the stock smaug.

In const.c, in the skill_table:

{
"resurrect", { 53, 53, 53, 53 }, { 1, 1, 2, 2},
spell_resurrect, TAR_IGNORE, POS_STANDING,
NULL, SLOT(xxx), 100, 15,
"", "!Resurrect!", "",
},

Of course, you will want to lower the levels and change SLOT(xxx) to a
free spell slot.
USA #4
Sounds like theres already an animate dead spell in the standard SMAUG dist. Wouldn't really surprise me if there was actually, there are usually at least a few unused spells in any codebase for various reasons. Nick or another coder with SMAUG source would need to confirm this since I don't work with SMAUG.
USA #5
I don't know if your code still crashes, but I did find a problem with it that would cause a crash. In the lines:

victim = create_mobile( pMobIndex );
//players can't raise corpses of other players after pkill
if (!IS_NPC(victim))
{
send_to_char( "You cannot raise that corpse." , ch );
do_purge(ch,victim->name);
return;
}

You assume that create_mobile actually returns a valid mobile, because pMobIndex actually contains something valid. However, if the search routine above finds nothing, the last state of pMobIndex will be null (and the inner forloop will break, causing the outer forloop to start again until it breaks...) So, if the game can't find the mobile prototype pMobIndex, it will return null and set victim to null. And then, if you try to check IS_NPC, THAT will crash the MUD, since you'll be deferencing a null pointer.

Generally, it's always a good idea to check if a pointer is null before using it, unless you can be 100% certain that it is not null. For instance, you checked if argument[0] == '\0', but you might also want to check if argument == NULL (unless the interpret function always gives an argument pointer - even if it's empty. I'm not sure about that.)

As for the skill_table, I don't really know about that. My code base is derived from SMAUG 1.0 - and that was a long time ago, and SMAUG has evolved a lot since then. It wouldn't surprise me if this part has changed... but in any case back then when you added a skill, you added entries to tables.c (or table.c...), which should be fairly self-explanatory once you open the file, and then you add a DECLARE_SPELL_FUN declaration to mud.h (why everything is in one giant header file is a little beyond me, but anyways), and then all the other parameters are set in-game. Like I said this information might be totally out of date now, since it comes from 1.0 and you're probably on 1.4, but it's worth a try.

Whether or not you make it a do_ command or a spell_ command won't really change a whole lot in the net effect, but it will change how the player accesses it and some of the surrounding behavior. For example, a do command is entered directly, but a spell command needs to be cast. Also, do_cast is a wrapper function for all spells, and handles nifty things like spell failure messages, and whatnot.


I hope that something in all that is useful to you :)