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