.-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-. 
 |                                                                       | 
 |                                                                       | 
 |                                                                       | 
 |                          IMPROVED MPDAMAGE                            | 
 |                                                                       | 
 !                                                                       ! 
 :                         A Snippet written by                          : 
 :                              Valcados                                 : 
 .                           for SMAUG MUDS                              . 
 .                                                                       . 
 :                                                                       : 
 :                                                                       : 
 !                                                                       ! 
 |                                                                       | 
 |                                                                       | 
 |                                                                       | 
 |                                                                       | 
 |                                                                       | 
 `-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-'
 

DESCRIPTION:
	A routine upgrade for the "mpdamage" command for MUDProgs.
	This adds three new kinds of arguments to mpdamage, to
	optionally follow after the damage itself.
	1.  You can now specify a type of damage to inflict
	2.  You can now specify a wearloc to be targeted for item
	    damage
	3.  You can specify a damage noun and have the mpdamage
	    generate an explicit damage message (see examples).

	By specifying multiple damtypes and/or wearlocs, you can
	tell mpdamage to randomly choose one in a weighted way.
	See the examples.

	Additionally, "mpdamage all" is handled in a more efficient
	and streamlined way (although this is invisible to players).
	
	Additionally, just for fun, if you are level 65 then you can
	use mpdamage as a PC.
	

EXAMPLES:
	mpdamage $n 100 fire
	  -- Does 100 damage to $n, of type "fire"
	
	mpdamage $n 100 head
	  -- Does 100 damage to $n, and aims for the head
	
	mpdamage $n 100 fire head
	  -- Does 100 fire damage to $n's head
	
	mpdamage $n 100 !spinning blade!
	  -- Does 100 damage and generates a damage message, like
	  	"The ninja's spinning blade wounds you!"
	
	mpdamage $n 500 fire !dark flame of doom!
	  -- Does 500 fire damage and generates a damage message, like
	  	"The archmage's dark flame of doom _maims_ you!"
	
	mpdamage $n 100 fire cold
	  -- Does 100 damage, with 50% probability of fire, 50% of cold
	
	mpdamage $n 100 fire fire cold
	  -- Does 100 damage, with 66% probability of fire, 33% of cold
	
	mpdamage $n 100 body body head
	  -- Does 100 damage, with 66% probability of hitting body, 33%
	        of hitting head
	

TESTING:
 	This code has been in use on the Lands of Aethar MUD
 	(www.aethar.com) for several years now and is thoroughly
 	assimilated into that MUD.  However, in writing this
 	snippet, I've made some tiny changes to make things more
 	SMAUGlike, and those are untested.
 	Also, bear in mind that Lands of Aethar code in general
 	is thoroughly modified, and is built out of an earlier
 	code release than 1.4a.
 
 
KNOWN PROBLEMS/BUGS:
	The following very obscure "bug" is from stock SMAUG, and
	while it is tangentially related to mpdamage, it is not
	CAUSED by this snippet, and it is just as present with the
	stock mpdamage code (as well as with all area attacks).
	
	If mob X is not the last mob in the linked list of room
	occupants, and has a death prog where it summons mob Y,
	and if mob X is killed by an "mpdamage all", then mob Y
	will be summoned while the "mpdamage all" code is still
	being processed.  Thus mob Y will be hit with the
	mpdamage even though mob Y was not in the room when it
	started.  In THEORY, infinite loops could arise if a
	room had several mobs who all load copies of themselves
	on death and who all instantly die from the mpdamage.
	But this is a rather contrived scenario.  Anyway it has
	nothing to do with this particular snippet, and is only
	incidentally related.

 	Obviously, if you save an area using improved mpdamage and
 	port it to a MUD where this code isn't installed, that MUD
 	will either ignore the extra arguments, or maybe complain
 	about them with bug messages.
 	
 	Overall problem/bug assessment:  utterly negligible.
 
 
IN-GAME MECHANICS:
 	Mpdamage is a MUDProg command.  Old progs using mpdamage
 	will continue to run in the usual way and will be undisturbed
 	by this code snippet.
 
 
HOW TO INSTALL:
 
 
1.  Open mud_comm.c.  No other .c or .h files will need modifying :)
 
 
2.  Almost at the top of the file, find the local declaration for
 	the simple_damage function, it should look like this:
 
 	ch_ret  simple_damage( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt );
 
    Change it to this:
 
 	ch_ret  simple_damage( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt, int damtype, int wearloc, char *attack );
 
 
3.  If it isn't already declared here or in mud.h, declare the
 	global function
 	
 	int get_risflag args( ( char *flag ) );
 
    You could, for example, declare it right above where the do_mp_damage
    function starts.


4.  Right below the line declaring get_risflag from step 3, declare
 	the new local get_item_wflag function:
 
 	int get_item_wflag args( ( char *flag ) );
 
 
5.  Comment out (or delete) the old do_mp_damage function and replace
 	it with this:
 
void do_mp_damage( CHAR_DATA *ch, char *argument )
{
        char arg1[MAX_INPUT_LENGTH];
        char arg2[MAX_INPUT_LENGTH];
        char arg3[MAX_INPUT_LENGTH];
        sh_int wearloc = -2;
        sh_int wearloc_tmp;
        sh_int wearloc_chance = 0;
        sh_int damtype = -1;
        sh_int damtype_tmp;
        sh_int damtype_chance = 0;
        char buf[MAX_STRING_LENGTH];
        CHAR_DATA *victim;
        CHAR_DATA *v_next;
        int dam;
        sh_int ch_trust = get_trust( ch );
        char *attack = NULL;
        char *c, *d;
 
        if ( ( !IS_NPC( ch ) && ch_trust < LEVEL_SUPREME ) || IS_AFFECTED( ch, AFF_CHARM ) )
        {
                send_to_char( "Huh?\n\r", ch );
                return;
        }
        if ( *argument == '\0' )
        {
                if ( ch_trust >= LEVEL_SUPREME )
                  send_to_char( "Mpdamage who?\n\r", ch );
                else
                  progbug( "Mpdamage without argument", ch );
                return;
        }
        argument = one_argument( argument, arg1 );
        if ( *argument == '\0' )
        {
                if ( ch_trust >= LEVEL_SUPREME )
                  send_to_char( "Mpdamage them how much?\n\r", ch );
                else
                progbug( "Mpdamage without damage specified", ch );
                return;
        }
        argument = one_argument( argument, arg2 );
        if ( !is_number( arg2 ) || ( dam = atoi( arg2 ) ) < 1 )
        {
                if ( ch_trust >= LEVEL_SUPREME )
                  send_to_char( "Damage must be a number greater than 0.\n\r", ch );
                else
                  progbug( "Mpdamage - invalid damage", ch );
                return;
        }
        while ( *argument != '\0' )
        {
                argument = one_argument( argument, arg3 );
                if ( *arg3 == '!' )
                {
                        c = arg3 + 1;
                        buf[0] = '\0';
                        d = buf;
                        for ( ; ; )
                        {
                                if ( *c == '\0' )
                                {
                                        if ( *argument == '\0' )
                                        {
                                                if ( ch_trust >= LEVEL_SUPREME )
                                                  send_to_char( "Unterminated quote... aborting.\n\r", ch );
                                                else
                                                  progbug( "Unterminated quote in MPDamage", ch );
                                                if ( attack )
                                                  free( attack );
                                                return;
                                        }
                                        else
                                        {
                                                argument = one_argument( argument, arg3 );
                                                c = arg3;
                                                *d++ = ' ';
                                                continue;
                                        }
                                }
                                else if ( *c == '!' )
                                {
                                        if ( attack )
                                          free( attack );
                                        *d = '\0';
                                        attack = str_dup( buf );
                                        break;
                                }
                                else
                                  *d++ = *c++;
                        }
                        continue;
                }
                wearloc_tmp = get_item_wflag( arg3 );
                if ( wearloc_tmp == -1 )
                {
                        damtype_tmp = get_risflag( arg3 );
                        if ( damtype_tmp == -1 )
                        {
                                if ( ch_trust >= LEVEL_SUPREME )
                                {
                                        sprintf( buf, "Argument '%s' is not a known wearloc nor a known damage type\n\r", arg3 );
                                        send_to_char( buf, ch );
                                }
                                else
                                {
                                        sprintf( buf, "MPDamage - Argument '%s' is neither a known wearloc, nor a known damtype", arg3 );
                                        progbug( buf, ch );
                                }
                                if ( attack )
                                  free( attack );
                                return;
                        }
                        if ( !damtype_chance || !number_range( 0, damtype_chance ) )
                        {
                        	damtype_chance++;
                        	damtype = damtype_tmp;
                        }
                }
                else
                if ( !wearloc_chance || !number_range( 0, wearloc_chance ) )
                {
                        wearloc_chance++;
                        wearloc = wearloc_tmp;
                }
        }
        if ( str_cmp( arg1, "all" ) )
        {
                if ( ch_trust >= LEVEL_SUPREME )
                  victim = get_char_world( ch, arg1 );
                else
                  victim = get_char_room( ch, arg1 );
                if ( !victim )
                {
                        if ( ch_trust >= LEVEL_SUPREME )
                        send_to_char( "They're not here.\n\r", ch );
                        else
                        progbug( "Mpdamage: victim not in room", ch );
                        if ( attack )
                          free( attack );
                        return;
                }
                if ( victim == ch && ch_trust < LEVEL_SUPREME )
                {
                        progbug( "Attempting to mpdamage self", ch );
                        if ( attack )
                          free( attack );
                        return;
                }
                if ( simple_damage(ch, victim, dam, TYPE_UNDEFINED, damtype, wearloc, attack ) == rVICT_DIED )
                {
                        stop_fighting( ch, FALSE );
                        stop_hating( ch );
                        stop_fearing( ch );
                        stop_hunting( ch );
                }
        }
        else
        {
                bool fKill = FALSE;

                for ( victim = ch->in_room->first_person; victim; victim = v_next )
                {
                        v_next = victim->next_in_room;
                        if ( victim == ch )
                        continue;
                        if ( simple_damage( ch, victim, dam, TYPE_UNDEFINED, damtype, wearloc, attack ) == rVICT_DIED )
                        fKill = TRUE;
                }
                if ( fKill )
                {
                        stop_fighting( ch, FALSE );
                        stop_hating( ch );
                        stop_fearing( ch );
                        stop_hunting( ch );
                }
        }
        if ( attack )
          free( attack );
        return;
}


6.  In simple_damage, find where it modifies damage for victim's RIS_PIERCE, RIS_SLASH and RIS_BLUNT.
	This should look something like this:

	if ( dt == (TYPE_HIT + 7) || dt == (TYPE_HIT + 8) )
	  dam = ris_damage(victim, dam, RIS_BLUNT);
	else
	if ( dt == (TYPE_HIT + 2) || dt == (TYPE_HIT + 11) )
	  dam = ris_damage(victim, dam, RIS_PIERCE);
	else
	if ( dt == (TYPE_HIT + 1) || dt == (TYPE_HIT + 3) )
	  dam = ris_damage(victim, dam, RIS_SLASH);

    Right below this, add the following:
    
	if ( damtype > 0 )
	  dam = ris_damage( victim, dam, 1 << damtype );


7.  In simple_damage, find a line like this:

	dameq  = number_range(WEAR_LIGHT, WEAR_EYES);

    Replace it with this:
    
	dameq  = wearloc == -2 ? number_range(WEAR_LIGHT, WEAR_BACK) : wearloc;


8.  In simple_damage, find the following code, or similar:

	if ( damobj )
	{
	  if ( dam > get_obj_resistance(damobj) )
	  {
	     set_cur_obj(damobj);
	     damage_obj(damobj);
	  }
	}
        }

    Below it, add this:

        /*
         *  Customizable damage messages for mpdamage
         */
        if ( attack )
        {
                sh_int dampc;
                ROOM_INDEX_DATA *was_in_room;
                int d_index;
                char punct;
                char buf[MAX_STRING_LENGTH];
                extern char * p_generic_messages[24];

                if ( !dam )
                dampc = 0;
                else
                dampc = ( (dam * 1000) / victim->max_hit) + ( 50 - ((victim->hit * 50) / victim->max_hit) );
                if ( ch->in_room != victim->in_room )
                {
                        was_in_room = ch->in_room;
                        char_from_room( ch );
                        char_to_room( ch, victim->in_room );
                }
                else
                  was_in_room = NULL;
                if ( !dam )
                  d_index = 0;
                else if ( dampc < 0 )
                  d_index = 1;
                else if ( dampc <= 100 )
                  d_index = 1 + dampc / 10;
                else if ( dampc <= 200 )
                  d_index = 11 + ( dampc - 100 ) / 20;
                else if ( dampc <= 900 )
                  d_index = 16 + ( dampc - 200 ) / 100;
                else
                  d_index = 23;
                punct = ( dampc <= 30 ) ? '.' : '!';
                if ( dam != 0 || IS_NPC( ch ) || !IS_SET( ch->pcdata->flags, PCFLAG_GAG ) )
                {
                        sprintf( buf, "Your %s %s $N%c", attack, p_generic_messages[d_index], punct );
                        act( AT_HIT, buf, ch, NULL, victim, TO_CHAR );
                }
                if ( dam != 0 || IS_NPC( victim ) || !IS_SET( victim->pcdata->flags, PCFLAG_GAG ) )
                {
                        sprintf( buf, "$n's %s %s you%c", attack, p_generic_messages[d_index], punct );
                        act( AT_HITME, buf, ch, NULL, victim, TO_VICT );
                }
                sprintf( buf, "$n's %s %s $N%c", attack, p_generic_messages[d_index], punct );
                act( AT_ACTION, buf, ch, NULL, victim, TO_NOTVICT );
                if ( was_in_room )
                {
                        char_from_room( ch );
                        char_to_room( ch, was_in_room );
                }
        }
        

9.  At the bottom of the file (or wherever you feel like putting it), add the function

/*
 *  Get a wearloc for do_mp_damage. 
 *  This is a little redundant, but it is needed so that,
 *    for example, if an mpdamage targets "finger", it will
 *    hit right finger and left finger with even odds,
 *    rather than hitting right finger 100% of the time.
 */
int get_item_wflag args( ( char *flag ) )
{
        if ( !str_cmp( flag, "light" ) ) return WEAR_LIGHT;
        else if ( !str_cmp( flag, "finger" ) )
        return number_bits( 1 ) ? WEAR_FINGER_L : WEAR_FINGER_R;
        else if ( !str_cmp( flag, "neck" ) )
        return number_bits( 1 ) ? WEAR_NECK_1 : WEAR_NECK_2;
        else if ( !str_cmp( flag, "body" ) ) return WEAR_BODY;
        else if ( !str_cmp( flag, "head" ) ) return WEAR_HEAD;
        else if ( !str_cmp( flag, "legs" ) ) return WEAR_LEGS;
        else if ( !str_cmp( flag, "feet" ) ) return WEAR_FEET;
        else if ( !str_cmp( flag, "hands" ) ) return WEAR_HANDS;
        else if ( !str_cmp( flag, "arms" ) ) return WEAR_ARMS;
        else if ( !str_cmp( flag, "shield" ) ) return WEAR_SHIELD;
        else if ( !str_cmp( flag, "about" ) ) return WEAR_ABOUT;
        else if ( !str_cmp( flag, "waist" ) ) return WEAR_WAIST;
        else if ( !str_cmp( flag, "wrist" ) )
        return number_bits( 1 ) ? WEAR_WRIST_L : WEAR_WRIST_R;
        else if ( !str_cmp( flag, "wield" ) || !str_cmp( flag, "dual-wield" ) || !str_cmp( flag, "missile" ) )
        switch ( number_range( 0, 3 ) )
        {
                default: return WEAR_WIELD;
                case 2: return WEAR_DUAL_WIELD;
                case 3: return WEAR_MISSILE_WIELD;
        }
        else if ( !str_cmp( flag, "hold" ) ) return WEAR_HOLD;
        else if ( !str_cmp( flag, "ears" ) ) return WEAR_EARS;
        else if ( !str_cmp( flag, "eyes" ) ) return WEAR_EYES;
        else if ( !str_cmp( flag, "ankle" ) )
        return number_bits( 1 ) ? WEAR_ANKLE_L : WEAR_ANKLE_R;
        else if ( !str_cmp( flag, "shoulder" ) ) return WEAR_SHOULDER;
        else if ( !str_cmp( flag, "back" ) ) return WEAR_BACK;
        else return -1;
}


10. Recompile SMAUG, reboot the MUD, and have fun!!!