.-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-.
| |
| |
| |
| 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!!!