/*___________________________________________________________________________* )()( DalekenMUD 1.12 (C) 2000 )()( `][' by Martin Thomson, Lee Brooks, `][' || Ken Herbert and David Jacques || || ----------------------------------------------------------------- || || Envy Diku Mud improvements copyright (C) 1994 by Michael Quan, || || David Love, Guilherme 'Willie' Arnold, and Mitchell Tse. || || Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael || || Chastain, Michael Quan, and Mitchell Tse. || || Original Diku Mud copyright (C) 1990, 1991 || || by Sebastian Hammer, Michael Seifert, Hans Henrik St{rfeldt, || || Tom Madsen, and Katja Nyboe. || || ----------------------------------------------------------------- || || Any use of this software must follow the licenses of the || || creators. Much time and thought has gone into this software and || || you are benefitting. We hope that you share your changes too. || || What goes around, comes around. || || ----------------------------------------------------------------- || || fight.c || || Combat, combat skills and death code. || *_/<>\_________________________________________________________________/<>\_*/ #include <math.h> #include "mud.h" #include "event.h" /* * Local functions. */ bool check_shield_block args( ( CHAR_DATA *ch, CHAR_DATA *victim ) ); bool check_blink args( ( CHAR_DATA *ch, CHAR_DATA *victim ) ); bool check_dodge args( ( CHAR_DATA *ch, CHAR_DATA *victim ) ); bool check_parry args( ( CHAR_DATA *ch, CHAR_DATA *victim ) ); bool check_stumble args( ( CHAR_DATA *ch, CHAR_DATA *victim ) ); bool check_familiar args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dam ) ); void dam_message args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt, int wpn ) ); void group_gain args( ( CHAR_DATA *ch, CHAR_DATA *victim ) ); bool is_wielding_poisoned args( ( CHAR_DATA *ch, int wpn ) ); void make_corpse args( ( CHAR_DATA *ch ) ); void one_hit args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dt, int wpn ) ); void raw_kill args( ( CHAR_DATA *ch, CHAR_DATA *victim ) ); void set_fighting args( ( CHAR_DATA *ch, CHAR_DATA *victim ) ); bool disarm args( ( CHAR_DATA *ch, CHAR_DATA *victim ) ); void trip args( ( CHAR_DATA *ch, CHAR_DATA *victim ) ); void use_magical_item args( ( CHAR_DATA *ch ) ); void strangle_hit args( ( CHAR_DATA *ch ) ); void strangle_struggle args( ( CHAR_DATA *ch ) ); void check_critical args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int wpn, int dt ) ); void remove_part args( ( CHAR_DATA *ch, CHAR_DATA *victim, int part ) ); void damage_eq args( ( CHAR_DATA *ch, CHAR_DATA *victim ) ); void start_fights args( ( CHAR_DATA *ch ) ); void attack args( ( CHAR_DATA *ch, int dt, int number ) ); void barehand_hit args( ( CHAR_DATA *ch, CHAR_DATA *victim ) ); void flashing_blades args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dt, int wpn ) ); bool get_juggle_attack args( ( CHAR_DATA *ch, int num ) ); void dirty_fighting args( ( CHAR_DATA *ch ) ); void end_battle_check args( ( CHAR_DATA *victim ) ); #define ATTACK_COUNT 10 const int attack_lag[ATTACK_COUNT] = { 0, 2, 12, 18, 25, 32, 43, 55, 75, 100 }; const struct limb_loss part_loss_table[] = { { 1, BODY_PART_HEAD|BODY_PART_EYES|BODY_PART_EAR_L|BODY_PART_EAR_R| BODY_PART_NOSE|BODY_PART_HORNS, 50, "&yYour head is chopped off!", "&y$n's head goes flying with an arc of blood!" }, { 3, BODY_PART_EYES, 51, "&yYour eyes are gouged out.", "&y$n's eyes drop out on the ground!" }, { 13, BODY_PART_ARM_L | BODY_PART_HAND_L | BODY_PART_FINGERS_L, 52, "&yYour left arm is chopped off!", "&y$n's left arm goes flying!" }, { 23, BODY_PART_ARM_R | BODY_PART_HAND_R | BODY_PART_FINGERS_R, 52, "&yYour right arm is chopped off!", "&y$n's right arm goes flying!" }, { 29, BODY_PART_HAND_L | BODY_PART_FINGERS_L, 53, "&yYour left hand is chopped off!", "&y$n's left hand goes flying!" }, { 35, BODY_PART_HAND_R | BODY_PART_FINGERS_R, 53, "&yYour right hand is chopped off!", "&y$n's right hand goes flying!" }, { 38, BODY_PART_FINGERS_L, 54, "&yYour left fingers are chopped off!", "&y$n's left fingers drop to the ground!" }, { 41, BODY_PART_FINGERS_R, 54, "&yYour right fingers are chopped off!", "&y$n's right fingers drop to the ground!" }, { 51, BODY_PART_LEG_L | BODY_PART_FOOT_L, 55, "&yYour left leg is chopped off!", "&y$n's left leg goes flying!" }, { 61, BODY_PART_LEG_R | BODY_PART_FOOT_R, 55, "&yYour right leg is chopped off!", "&y$n's right leg goes flying!" }, { 67, BODY_PART_FOOT_L, 56, "&yYour left foot is chopped off!", "&y$n's left foot goes flying!" }, { 73, BODY_PART_FOOT_R, 56, "&yYour right foot is chopped off!", "&y$n's right foot goes flying!" }, { 75, BODY_PART_TAIL, 57, "&yYour tail is severed!", "&y$n's tail is severed!" }, { 77, BODY_PART_HORNS, 58, "&yYour horns are shorn from your head!", "&y$n's horns are shorn from $s head!" }, { 79, BODY_PART_WINGS, 59, "&yYour wings are chopped off!", "&y$n's wings flutter to the ground!" }, { 81, BODY_PART_LUNGS, 60, "&yYour lungs are torn out!", "&y$n's lungs are torn out!" }, { 83, BODY_PART_GILLS, 61, "&yYour gills are ripped off!", "&y$n's gills are ripped off!" }, { 86, BODY_PART_EAR_L, 62, "&yYour left ear drops to the ground.", "&y$n's left ear drops to the ground." }, { 89, BODY_PART_EAR_R, 62, "&yYour right ear drops to the ground.", "&y$n's right ear drops to the ground." }, { 91, BODY_PART_NOSE, 63, "&yYour nose drops to the ground.", "&y$n's nose drops to the ground." }, { 0, 0, 1, NULL, NULL } }; /* * Called from violence_update * starts everyone fighting each other * this is the most expensive part of violence update. */ void start_fights( CHAR_DATA *ch ) { CHAR_DATA *victim; CHAR_DATA *rch; bool mobfighting = FALSE; if( IS_AFFECTED( ch, AFF_BLIND ) || ( IS_NPC( ch ) && ch->pIndexData->pShop ) || !IS_AWAKE( ch ) ) return; if( ( victim = ch->fighting ) ) { if( victim->deleted || ch->in_room != victim->in_room ) stop_fighting( ch, FALSE ); /* switch to a charmie's master if there is one */ else if( IS_AFFECTED( victim, AFF_CHARM ) && IS_NPC( victim ) && !xIS_SET( victim->act, ACT_MERCENARY ) && victim->master && !victim->master->deleted && ch->in_room == victim->master->in_room && number_bits( 3 ) > 3 ) { stop_fighting( ch, FALSE ); set_fighting( ch, victim->master ); } return; } /* * Ok. So ch is not fighting anyone. * Is there a fight going on? * will we join in? */ for( rch = ch->in_room->people; rch; rch = rch->next_in_room ) { if( rch->deleted || !IS_AWAKE( rch ) || !( victim = rch->fighting ) ) continue; /* a player or mercenary (ch) will assist * players or charmies (rch) in the same group * that are fighting NPCs */ if( ( !IS_NPC( ch ) || ( IS_AFFECTED( ch, AFF_CHARM ) && xIS_SET( ch->act, ACT_MERCENARY ) ) ) && ( !IS_NPC( rch ) || IS_AFFECTED( rch, AFF_CHARM ) ) && is_same_group( ch, rch ) && IS_NPC( victim ) ) break; if( IS_NPC( ch ) && IS_NPC( rch ) && !IS_NPC( victim ) ) { mobfighting = TRUE; break; } } if( !victim || !rch ) return; /* * Now that someone is fighting, consider fighting another pc * or not at all. */ if( mobfighting ) { CHAR_DATA *vch; int number; number = 0; for( vch = ch->in_room->people; vch; vch = vch->next_in_room ) { if( can_see( ch, vch ) && is_same_group( vch, victim ) && ( number_range( 0, number ) == 0 || IS_AFFECTED( vch, AFF_TAUNT ) ) ) { victim = vch; number++; } } if( !xIS_SET( ch->act, ACT_ASSIST ) && ( ( rch->pIndexData != ch->pIndexData ) || ( IS_GOOD( ch ) && IS_GOOD( victim ) ) || ( !IS_EVIL( ch ) && !IS_EVIL( victim ) && number_bits( 2 ) != 0 ) || abs( victim->level - ch->level ) > 4 ) ) return; } set_fighting( ch, victim ); return; } /* * Attack. * Called from update_violence and multi_hit * Does one attack depending on number. */ void attack( CHAR_DATA *ch, int dt, int number ) { CHAR_DATA *victim; int wield = WEAR_WIELD_R; if( !( victim = ch->fighting ) || victim->deleted ) return; if( get_eq_char( ch, WEAR_WIELD_DOUBLE ) ) wield = WEAR_WIELD_DOUBLE; else if( !get_eq_char( ch, WEAR_WIELD_R ) && get_eq_char( ch, WEAR_WIELD_L ) ) wield = WEAR_WIELD_L; if( number > 0 && ( dt == gsn_backstab || dt == gsn_circle || dt == gsn_slit_throat || dt == gsn_impale || dt == gsn_atemi || dt == gsn_charge ) ) return; if( wield == WEAR_WIELD_DOUBLE && number > 0 && number != 2 && number != 9 ) return; if( number != -1 && ( !IS_NPC( ch ) && ch->pcdata->pc_bits >= PC_BIT_STRANGLE ) ) return; /* * The action starts here, special considerations * are taken into account like dual and so forth */ switch( number ) { case -1: if( !IS_NPC( ch ) ) { if( ch->pcdata->pc_bits >= PC_BIT_STRANGLE ) strangle_hit( ch ); else if( get_success( ch, gsn_dirty_fighting, 50 ) ) dirty_fighting( ch ); } return; case 0: one_hit( ch, victim, dt, wield ); return; case 1: if( IS_AFFECTED( ch, AFF_HASTE ) && number_bits( 5 ) < 25 ) one_hit( ch, victim, dt, wield ); return; case 2: if( IS_NPC_CLASS( ch ) ? number_percent( ) < ch->level : get_success( ch, gsn_second_attack, 70 ) ) one_hit( ch, victim, dt, wield ); return; case 3: if( dt == TYPE_UNDEFINED && IS_SET( ch->body_parts, BODY_PART_TAIL ) && number_bits( 2 ) == 0 ) one_hit( ch, victim, gsn_race_tail, WEAR_WIELD_R ); return; case 4: if( wield == WEAR_WIELD_DOUBLE ) { if( IS_NPC( ch ) ) { if( number_percent( ) < UMIN( 50, ch->level / 2 ) ) one_hit( ch, victim, dt, wield ); return; } if( ch->pcdata->learned[gsn_second_attack] * 3 / 4 > number_bits( 7 ) && ch->pcdata->learned[gsn_third_attack] / 2 > number_bits( 7 ) ) one_hit( ch, victim, dt, wield ); } else if( IS_NPC_CLASS( ch ) ? number_bits( 5 ) < ch->level : get_success( ch, gsn_third_attack, 50 ) ) one_hit( ch, victim, dt, wield ); return; case 5: if( get_juggle_attack( ch, 1 ) || IS_NPC_CLASS( ch ) ? number_bits( 6 ) < ch->level : get_success( ch, gsn_fourth_attack, 30 ) ) one_hit( ch, victim, dt, wield ); return; case 6: if( get_juggle_attack( ch, 2 ) || IS_NPC_CLASS( ch ) ? number_bits( 7 ) < ( ch->level - 25 ) : get_success( ch, gsn_fifth_attack, 25 ) ) one_hit( ch, victim, dt, wield ); return; case 7: if( get_juggle_attack( ch, 3 ) || ( IS_NPC_CLASS( ch ) && number_bits( 8 ) < ( ch->level - 100 ) / 2 ) ) one_hit( ch, victim, dt, wield ); return; case 8: if( IS_NPC( ch ) && number_bits( 10 ) < ch->level ) one_hit( ch, victim, dt, wield ); return; case 9: if( IS_NPC( ch ) ) one_hit( ch, victim, dt, wield ); return; default: bug( "attack: Unknown attack action %d from %s.", number, ch->name ); return; } return; } /* * Update all the fighting in a single room. * This has the advantage of a very short list when working out initiatives * so inserts are extremely simple. */ void room_violence_update( ROOM_INDEX_DATA *room ) { CHAR_DATA *ch; CHAR_DATA *list = NULL; CHAR_DATA *threads[ATTACK_COUNT]; CHAR_DATA *extra_threads[ATTACK_COUNT]; int i, count; int min = 100000, max = -100000; for( ch = room->people; ch; ch = ch->next_in_room ) { if( ch->deleted ) continue; start_fights( ch ); if( !ch->fighting ) continue; ch->temp = get_speed( ch ) + number_range( 1, 10 ); /* get minimum/maximum values so we can fix it */ if( ch->temp > max ) max = ch->temp; if( ch->temp < min ) min = ch->temp; if( !list || ch->temp > list->temp ) { ch->next_initiative = list; list = ch; } else { CHAR_DATA *tmp; for( tmp = list; tmp && tmp->next_initiative; tmp = tmp->next_initiative ) if( ch->temp > tmp->next_initiative->temp ) break; if( !tmp ) { bug( "room_violence_update: can't insert %s", ch->name ); continue; } ch->next_initiative = tmp->next_initiative; tmp->next_initiative = ch; } } if( !list ) return; /* special attacks as well as a little twiddling * it would be nice to have all the values in * a restricted range... */ max = UMAX( max, min + 50 ); for( ch = list; ch; ch = ch->next_initiative ) { ch->temp = ( ch->temp - min ) * 100 / ( max - min ); if( !ch->deleted && ch->fighting ) attack( ch, TYPE_UNDEFINED, -1 ); } /* there is a thread for each value in attack() */ for( i = 0; i < ATTACK_COUNT; ++i ) { threads[i] = list; extra_threads[i] = list; } for( count = 0; count < 125; ++count ) { for( i = 0; i < ATTACK_COUNT; ++i ) { if( threads[i] ) { if( threads[i]->deleted || !threads[i]->fighting ) threads[i] = threads[i]->next_initiative; else if( threads[i]->temp - attack_lag[i] == list->temp - count ) { attack( threads[i], TYPE_UNDEFINED, i ); threads[i] = threads[i]->next_initiative; } } /* A bonus to any with high initiative */ if( extra_threads[i] ) { if( extra_threads[i]->deleted || !extra_threads[i]->fighting ) extra_threads[i] = extra_threads[i]->next_initiative; else if( extra_threads[i]->temp - 50 - 2 * attack_lag[i] == list->temp - count ) { if( number_bits( 1 ) ) attack( extra_threads[i], TYPE_UNDEFINED, i ); extra_threads[i] = extra_threads[i]->next_initiative; } } } } /* clean up */ for( ch = list; ch; ch = ch->next_initiative ) if( !ch->deleted ) ch->extra_bits &= ~CH_EX_RESET; create_room_event( room, evn_room_violence, PULSE_VIOLENCE ); } /* * Do one group of attacks. * Now passed on to attack( ), only used to set fighting */ void multi_hit( CHAR_DATA *ch, CHAR_DATA *victim, int dt ) { int i; /* * Set the fighting fields now. */ if( victim->position > POS_STUNNED ) { if( !victim->fighting ) set_fighting( victim, ch ); if( victim->position != POS_SMASHED && victim->position != POS_SLEEPING && victim->position != POS_GETTING_UP ) victim->position = POS_FIGHTING; if( !ch->fighting ) set_fighting( ch, victim ); } for( i = 0; i < ATTACK_COUNT; i++ ) attack( ch, dt, i ); if( !IS_NPC( victim ) && dt != gsn_retaliate && get_success( victim, gsn_retaliate, 40 ) ) multi_hit( victim, ch, gsn_retaliate ); return; } /* * Hit one guy once. * one_hit() modified for new damage/combat system. * --Symposium (6/1998) */ void one_hit( CHAR_DATA *ch, CHAR_DATA *victim, int dt, int wpn ) { OBJ_DATA *wield = NULL; int combat_no; int dam, basic_dam; int save_hit; bool offwield = FALSE; /* * Can't beat a dead char! * Guard against weird room-leavings. */ if( victim->position == POS_DEAD || ch->in_room != victim->in_room ) { bug( "one_hit: ch %s not with victim %s, or victim POS_DEAD", ch->name, victim->name ); return; } if( !IS_NPC( victim ) && victim->pcdata->pc_bits >= PC_BIT_STRANGLE && victim->fighting == ch ) { strangle_struggle( ch ); return; } /* Juggle those weapons! * Make sure this is only for regular hits, not specials. * Otherwise people could backstab with the wrong weapon :) */ if( dt == TYPE_UNDEFINED ) { juggle_shuffle( ch ); if( ch->deleted || !ch->fighting || ch->position <= POS_STUNNED ) return; } /* * Figure out the type of damage message. */ if( wpn != WEAR_NONE ) wield = get_eq_char( ch, wpn ); if( dt == TYPE_UNDEFINED ) { if( wpn == WEAR_WIELD_R ) offwield = TRUE; dt = TYPE_HIT; if( wield && wield->item_type == ITEM_WEAPON ) dt += wield->value[3]; } dam = GET_AC( victim ) - 100; dam += ( get_size( ch ) - get_size( victim ) ) / 2; if( !can_see( victim, ch ) ) dam += 50; if( ch->class == CLASS_NONE ) combat_no = 400 + ch->level / 5; else combat_no = 375 + class_table[ch->class].combat_no; dam = dam * 100 + ch->level * combat_no; dam = UMIN( -1, dam / 100 ); dam += number_range( 0, get_hitroll( ch ) * 6 + 5 ); dam += number_range( 0, ( ch->level - 3 ) * 4 + 10 ); if( dam > ch->level ) dam = UMIN( dam, ( ch->level + get_hitroll( ch ) ) * 12 ); /* MISSED 'IM * (1/32 chance of always hit and always miss) */ if( ( dam <= 0 || number_bits( 5 ) == 0 ) && !IS_AFFECTED( ch, AFF_NEVERMISS ) && number_bits( 5 ) ) { damage( ch, victim, 0, dt, wpn ); return; } else if( dam < 0 ) dam = 1; /* if you hit you get the weapon's base damage added */ if( IS_NPC( ch ) ) { basic_dam = number_range( ch->level / 3, ch->level ) + 1; if( wield ) { if( wield->wear_loc == WEAR_WIELD_DOUBLE ) basic_dam *= 3; else basic_dam += basic_dam * 2 / 3; } } else if( wield && dt != gsn_race_tail ) /* weaponed PC */ { basic_dam = number_range( wield->value[1], wield->value[2] ); if( basic_dam > 100000 ) { bug( "One_hit basic_dam range > 100000 from %d to %d", wield->value[1], wield->value[2] ); } if( IS_SET( wield->extra_flags, ITEM_HORNED ) && number_bits( 5 ) == 0 ) basic_dam *= 2; if( wield->wear_loc == WEAR_WIELD_DOUBLE ) { if( dt == gsn_backstab || dt == gsn_atemi ) basic_dam += basic_dam * get_success( ch, gsn_two_handed, 200 ) / 50; else basic_dam += basic_dam * get_success( ch, gsn_two_handed, 200 ) / 30; } if( wield->required_skill > 0 ) { if( get_success( ch, gsn_weapon_skill, 100 ) ) basic_dam = basic_dam / 2 + basic_dam * get_success( ch, wield->required_skill, 200 ) / 180; else basic_dam = basic_dam * get_success( ch, wield->required_skill, 200 ) / 90; } if( ch->pcdata->learned[gsn_weapon_skill] > 0 ) basic_dam += basic_dam * get_success( ch, gsn_weapon_skill, 250 ) / 150; } else /* no weapon basic damage */ { basic_dam = get_size( ch ) / 10 + 1; if( dt != gsn_race_tail ) /* barehand */ { int i; basic_dam /= 2; if( ( i = get_success( ch, gsn_barehand, 100 ) ) ) { basic_dam += i * ch->level / ( 400 - get_curr_dex( ch ) * 3 ); } basic_dam = number_range( basic_dam, basic_dam * 2 ); if( dt == gsn_smash ) { basic_dam += get_curr_str( ch ) * ch->level / 30; } else if( dt == TYPE_HIT && number_bits( 5 ) == 0 && get_success( ch, gsn_golden_touch, 60 ) ) { dt = gsn_golden_touch; dam = dam * 5 / 2; } } else basic_dam = number_range( basic_dam / 2, basic_dam ); } /* * An idea I had, maybe this will work if I put some thought into it. dam = dam * ( 75 + get_damroll( ch ) ); * --Symposium */ dam = dam / 10 + basic_dam + get_damroll( ch ); /* * Now the other bonuses. */ if( wield && IS_SET( wield->extra_flags, ITEM_CHARGED ) ) { if( ch->level < L_SEN ) REMOVE_BIT( wield->extra_flags, ITEM_CHARGED ); dam += dam * get_curr_int( ch ) / 12; } if( wield && wpn == WEAR_WIELD_L && get_eq_char( ch, WEAR_WIELD_R ) ) dam /= 2; if( !IS_NPC( ch ) && ch->pcdata->learned[gsn_enhanced_damage] > 0 ) dam += dam * get_success( ch, gsn_enhanced_damage, 250 ) / 150; if( !wield && !str_cmp( race_table[ch->race].name, "Vampire" ) ) dam += dam / 2; if( IS_AFFECTED( ch, AFF_BLEEDING ) ) dam -= dam / 3; if( dt == gsn_whirlwind ) dam += dam / 2; else if( dt == gsn_headbutt ) { OBJ_DATA *helmet; helmet = get_eq_char( ch, WEAR_HEAD ); if( ch != victim ) dam *= 2; if( ( helmet && IS_SET( helmet->extra_flags, ITEM_HORNED ) ) || ( !helmet && IS_SET( ch->body_parts, BODY_PART_HORNS ) ) ) dam += dam / 2; } else if( dt == gsn_stomp ) { dam += get_damroll( ch ); dam += get_size( ch ) / 2; dam -= get_size( victim ) / 3; dam = dam * ( get_curr_str( ch ) * 5 + get_curr_dex( ch ) * 2 ) / 120; if( !str_cmp( class_table[ch->class].who_name, "cru" ) ) { dam *= 3; dam /= 2; } } else if( dt == gsn_impale ) { dam *= get_curr_str( ch ); dam /= 6; if( !number_range( 0, UMAX( 70, 200 - ch->pcdata->learned[gsn_impale] ) ) ) { send_to_char( "You have struck a vital organ!\n\r", ch ); act( "$n has struck a vital organ!", ch, NULL, victim, TO_ROOM ); dam *= 4; } } else if( dt == gsn_backstab || dt == gsn_atemi ) { dam = power( dam * ( 2 + UMIN( ( ch->level / 10 ), 4 ) ), 3, get_curr_dex( ch ) - 18 ); if( number_range( 0, power( 1000, 1, 20 - get_curr_dex( ch ) ) ) == 1 ) dam *= 4; } else if( dt == gsn_slit_throat ) { dam = power( dam * ( 2 + UMIN( ( ch->level / 8 ), 8 ) ), 6, get_curr_dex( ch ) - 20 ); if( number_range( 0, power( 250, 2, 20 - get_curr_dex( ch ) ) ) == 0 ) { OBJ_DATA *head; char buf[MAX_INPUT_LENGTH]; act( "$n tear$% $N's head clean off!", ch, NULL, victim, TO_ALL ); head = create_object( get_obj_index( 50 ), victim->level ); head->value[0] = BODY_PART_HEAD; sprintf( buf, head->short_descr, IS_NPC( victim ) ? victim->short_descr : victim->name ); free_string( head->short_descr ); head->short_descr = str_dup( buf ); if( IS_AFFECTED( victim, AFF_POISON ) ) head->value[3] = 1; obj_to_room( head, ch->in_room ); victim->hit /= 2; damage( ch, victim, victim->max_hit * 3, dt, wpn ); return; } } else if( dt == gsn_circle ) /* 150% to 200% at lev. 50 */ dam += dam / 2 + ( dam * ch->level ) / 100; else if( dt == gsn_retaliate ) dam *= 2; else if( dt == gsn_roundhouse ) dam = dam + dam * get_success( ch, dt, 100 ) / 50; else if( dt == gsn_uppercut ) dam = dam + dam * get_success( ch, dt, 100 ) / 100; else if( dt == gsn_elbow ) dam = dam + 3 * dam * get_success( ch, dt, 100 ) / 400; else if( dt == gsn_knee ) dam = dam + dam * get_success( ch, dt, 100 ) / 200; else if( dt == gsn_backhand ) dam = dam + dam * get_success( ch, dt, 100 ) / 400; else if( dt == gsn_smash ) dam *= 2; else if( dt == gsn_golden_touch ) dam = dam * 5 / 2; /* they have hit so they must do SOME damage */ if( dam <= 0 ) dam = 1; save_hit = victim->hit; damage( ch, victim, dam, dt, wpn ); if( !victim->deleted && ch->fighting && victim->hit < save_hit ) damage_eq( ch, victim ); if( wield && number_bits( 7 ) < pow( 8, ( get_curr_str( ch ) - wield->weight - 20 ) / 12. ) && !get_success( ch, gsn_weapon_skill, 30 ) ) { act( "&rYou struck out too powerfully that stroke ...", ch, NULL, NULL, TO_CHAR ); mod_item_condition( ch, wield, UMAX( 1, ( get_curr_str( ch ) + number_bits( 3 ) - wield->weight ) / 27 ) ); } if( ch->fighting && offwield && dt >= TYPE_HIT && get_eq_char( ch, WEAR_WIELD_L ) && ( ( IS_SET( race_table[ch->race].race_abilities, RACE_DUAL_WIELD ) && number_percent( ) < 40 ) || IS_NPC_CLASS( ch ) ? number_percent( ) < UMIN( ch->level, 40 ) : get_success( ch, gsn_dual, 50 ) ) ) one_hit( ch, victim, TYPE_UNDEFINED, WEAR_WIELD_L ); flashing_blades( ch, victim, dt, wpn ); return; } /* * casts a spell from a weapon */ void cast_weapon_spell( CHAR_DATA *ch, CHAR_DATA *victim, OBJ_DATA *wield ) { int cost[MAGIC_MAX], i; for( i = 0; i < MAGIC_MAX; ++i ) { cost[i] = mana_cost( ch, wield->value[0], i ) / 3; if( ch->mana[i] < cost[i] ) return; } for( i = 0; i < MAGIC_MAX; ++i ) ch->mana[i] -= cost[i]; ( *skill_table[wield->value[0]].spell_fun ) ( gsn_weapon_spell, ( wield->level / 2 ), ch, victim ); } /* * Apply any damage modifiers for spell sphere and other magical considerations */ int sphere_damage_mod( int dam, int sn, CHAR_DATA *ch, CHAR_DATA *victim ) { int i, total, sphere[MAGIC_MAX]; float diff; for( i = 0; i < MAGIC_MAX; ++i ) { sphere[i] = 0; if( skill_table[sn].min_mana[i] != 0 ) break; } if( i >= MAGIC_MAX ) /* this isn't a spell */ return dam; dam *= 1000; total = 0; for( ; i < MAGIC_MAX; ++i ) { if( skill_table[sn].min_mana[i] > 0 ) total += sphere[i] = skill_table[sn].min_mana[i]; else sphere[i] = 0; } for( i = 0; i < MAGIC_MAX; ++i ) { if( ( sphere[i] = sphere[i] * 1000 / total ) <= 0 ) continue; diff = ch->level - victim->level; diff = pow( 1.4, diff ) / 3; diff += get_magic( ch, i ) - get_magic( victim, i ); diff -= get_magic_resist( victim ) / 40; /* * 1.5 times the damage if diff is 10, * 0.5 times damage if diff is -10 * formula is inverse tan so that maximum damage is 2 times * and minimum is zero. */ dam *= 1 + sphere[i] * atan( diff / 10 ) / ( M_PI * 500 ); } /* intelligence mod, base damage at 20, 1.5 times at 30, 0.5 times at 10 */ diff = get_curr_int( ch ) * 2 - get_curr_wis( victim ); diff = diff / 20 - 1; dam *= 1 + 2 * atan( diff ) / M_PI; /* body temperature mods */ if( IS_SET( skill_table[sn].skill_type, SKILL_TYPE_FIRE ) ) dam -= ( dam * get_curr_temp( ch ) ) / 250; if( IS_SET( skill_table[sn].skill_type, SKILL_TYPE_ICE ) ) dam += ( dam * get_curr_temp( ch ) ) / 250; return UMAX( 1, dam / 1000 ); } /* * Inflict damage from a hit. */ void damage( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt, int wpn ) { int sntemp; if( is_safe( ch, victim ) ) { if( ch->fighting ) stop_fighting( ch, TRUE ); return; } if( victim->position == POS_DEAD ) return; if( IS_AFFECTED( ch, AFF_FAST_TALK ) ) affect_strip( ch, skill_lookup( "fast talk" ) ); /* * Stop up any residual loopholes. */ if( dam > 100000 && ch->level <= LEVEL_HERO ) { if( IS_NPC( ch ) && ch->desc ) bug( "Damage: %d from %s by %s: > 100000 points with %d dt!", dam, ch->name, ch->desc->original->name, dt ); else bug( "Damage: %d from %s: > 100000 points with %d dt!", dam, ch->name, dt ); } if( victim != ch ) { /* * Certain attacks are forbidden. * Most other attacks are returned. */ check_killer( ch, victim ); if( victim->position > POS_STUNNED && dt != TYPE_MPDAMAGE ) { if( !victim->fighting ) set_fighting( victim, ch ); if( victim->position != POS_SMASHED && victim->position != POS_SLEEPING && victim->position != POS_GETTING_UP ) victim->position = POS_FIGHTING; if( !ch->fighting ) set_fighting( ch, victim ); /* * If NPC victim is following, ch might attack victim's master. * No charm check here because charm would be dispelled from * tanking mobile when combat ensues thus ensuring PC charmer is * not harmed. * Check for is_same_group wont work as following mobile is not * always grouped with PC charmer - Kahn */ if( IS_NPC( ch ) && IS_NPC( victim ) && victim->master && victim->master->in_room == ch->in_room && number_bits( 2 ) == 0 ) { stop_fighting( ch, FALSE ); set_fighting( ch, victim->master ); return; } } /* * More charm stuff. */ if( victim->master == ch ) stop_follower( victim ); /* * Inviso attacks ... not. */ if( IS_NPC( ch ) && xIS_SET( ch->act, ACT_BURIED ) && dt != TYPE_MPDAMAGE ) { act( "$n tears $mself up from the earth.", ch, NULL, NULL, TO_ROOM ); xREMOVE_BIT( ch->act, ACT_BURIED ); } if( IS_AFFECTED( ch, AFF_INVISIBLE ) && dt != TYPE_MPDAMAGE ) { affect_strip( ch, gsn_invis ); affect_strip( ch, gsn_mass_invis ); if( !is_affected( ch, gsn_vanish ) ) { xREMOVE_BIT( ch->affected_by, AFF_INVISIBLE ); act( "$n fades into existence.", ch, NULL, NULL, TO_ROOM ); } else xSET_BIT( ch->affected_by, AFF_INVISIBLE ); } /* * Damage modifiers. */ /* Surge bonus, before changing from the weapon spell */ if( dt >= 0 && dt < MAX_SKILL && IS_SET( skill_table[dt].skill_type, SKILL_TYPE_MAGIC ) && !IS_NPC( ch ) && IS_SET( ch->pcdata->pc_bits, PC_BIT_SURGE ) ) dam += dam; if( dt == gsn_weapon_spell ) { OBJ_DATA *wield; wield = get_eq_char( ch, WEAR_WIELD_R ); if( !wield || wield->value[0] <= 0 ) wield = get_eq_char( ch, WEAR_WIELD_DOUBLE ); if( !wield || wield->value[0] <= 0 ) wield = get_eq_char( ch, WEAR_WIELD_L ); if( !wield ) bug( "Weird weapon spell bug [%s].", ch ); else dt = wield->value[0]; } if( dt < 0 || dt > MAX_SKILL || !IS_SET( skill_table[dt].skill_type, SKILL_TYPE_NO_RESIL ) ) { dam *= get_curr_resil( victim ); dam /= 1000; } /* magic spells */ if( dt >= 0 && dt < MAX_SKILL && IS_SET( skill_table[dt].skill_type, SKILL_TYPE_MAGIC ) ) dam = sphere_damage_mod( dam, dt, ch, victim ); /* weapon strikes + warp flesh */ if( IS_AFFECTED( victim, AFF_WARP_FLESH ) && ( ( dt > 0 && dt < MAX_SKILL && IS_SET( skill_table[dt].skill_type, SKILL_TYPE_WEAPONSTRIKE ) ) || dt >= TYPE_HIT ) ) dam *= 2; switch( victim->position ) { case POS_SMASHED: dam += dam / 2; break; case POS_SITTING: dam += dam / 10; break; case POS_RESTING: dam += dam / 5; break; default: break; } if( IS_AFFECTED( victim, AFF_SANCTUARY ) || ( !IS_NPC( victim ) && IS_SET( race_table[victim->race].race_abilities, RACE_SANCT ) ) ) dam /= 2; if( ( IS_AFFECTED( victim, AFF_PROTECT ) || IS_SET( race_table[victim->race].race_abilities, RACE_PROTECTION ) ) && ( ( IS_EVIL( ch ) && IS_GOOD( victim ) ) || ( IS_GOOD( ch ) && IS_EVIL( victim ) ) ) ) dam -= dam / 4; if( dam < 0 ) dam = 0; /* * Make sure you start counting hits. */ if( ch->num_hits < 0 && victim == ch->fighting ) ch->num_hits = 0; /* * Check for disarm, trip, parry, and dodge. */ if( dt >= TYPE_HIT || dt == gsn_kick || dt == gsn_race_tail ) { int leveldiff = ch->level - victim->level - 10; if( IS_NPC( ch ) && dam == 0 && number_percent( ) * 2 < ( leveldiff < -5 ? ch->level / 2 : UMAX( 10, leveldiff ) ) ) disarm( ch, victim ); if( IS_NPC( ch ) && dam == 0 && number_percent( ) * 2 < UMIN( 30, leveldiff ) ) trip( ch, victim ); if( IS_NPC( ch ) && !IS_SET( race_table[ch->race].race_abilities, RACE_NO_WEAPON_WIELD ) && number_percent( ) < UMIN( 25, UMAX( 10, ch->level ) ) && !IS_NPC( victim ) ) use_magical_item( ch ); if( dam > 0 && !IS_AFFECTED( ch, AFF_NEVERMISS ) ) { if( check_stumble( ch, victim ) ) return; if( check_parry( ch, victim ) ) return; if( check_dodge( ch, victim ) ) return; if( check_shield_block( ch, victim ) ) return; if( check_blink( ch, victim ) ) return; if( check_familiar( ch, victim, dam ) ) return; } } } /* sleep modifier, has to be after dodge and parry */ if( victim->position == POS_SLEEPING && dam > 0 ) { dam += ch->level * 2 + dam; victim->position = POS_GETTING_UP; } /* * We moved dam_message out of the victim != ch if above * so self damage would show. Other valid type_undefined * damage is ok to avoid like mortally wounded damage - Kahn */ if( dt != TYPE_UNDEFINED && dt != TYPE_MPDAMAGE ) dam_message( ch, victim, dam, dt, wpn ); /* * Hurt the victim. * Inform the victim of his new state. */ victim->hit -= dam; if( !IS_NPC( victim ) && victim->level >= LEVEL_IMMORTAL && victim->hit < 1 ) victim->hit = 1; if( ( ( dt > 0 && dt < MAX_SKILL && IS_SET( skill_table[dt].skill_type, SKILL_TYPE_WEAPONSTRIKE ) ) || ( dt >= TYPE_HIT && dt != TYPE_MPDAMAGE ) ) && dam > 1 && victim != ch && IS_AFFECTED( victim, AFF_ATTACKSHIELD ) ) { sntemp = skill_lookup( "flamestrike" ); ( *skill_table[sntemp].spell_fun ) ( sntemp, ( victim->level / 4 ), victim, ch ); } if( victim->position <= POS_STUNNED && IS_AFFECTED( victim, AFF_BERSERK ) ) affect_strip( victim, gsn_berserk ); update_pos( victim ); /* * Sleep spells and extremely wounded folks. */ if( !IS_AWAKE( victim ) && victim->fighting ) stop_fighting( victim, FALSE ); switch( victim->position ) { case POS_MORTAL: send_to_char( "You are mortally wounded, and will die soon, if not aided.\n\r", victim ); act( "$n is mortally wounded, and will die soon, if not aided.", victim, NULL, NULL, TO_ROOM ); break; case POS_INCAP: send_to_char( "You are incapacitated and will slowly die, if not aided.\n\r", victim ); act( "$n is incapacitated and will slowly die, if not aided.", victim, NULL, NULL, TO_ROOM ); break; case POS_STUNNED: send_to_char( "You are stunned, but will probably recover.\n\r", victim ); act( "$n is stunned, but will probably recover.", victim, NULL, NULL, TO_ROOM ); break; case POS_DEAD: if( IS_AFFECTED( victim, AFF_DEAD ) ) return; send_to_char( "&RYou have been &fKILLED!!&n\n\r\n\r", victim ); act( "&r$n is DEAD!!", victim, NULL, NULL, TO_ROOM ); if( dt == gsn_hand_of_kaz ) { OBJ_DATA *obj; char buf[MAX_INPUT_LENGTH]; act( "&yYou lift $N's still beating heart from $S chest in triumph.", ch, NULL, victim, TO_CHAR ); obj = create_object( get_obj_index( OBJ_VNUM_TORN_HEART ), victim->level ); sprintf( buf, obj->short_descr, IS_NPC( victim ) ? victim->short_descr : victim->name ); free_string( obj->short_descr ); obj->short_descr = str_dup( buf ); sprintf( buf, obj->description, IS_NPC( victim ) ? victim->short_descr : victim->name ); free_string( obj->description ); obj->description = str_dup( buf ); obj->cost /= 20; obj_to_char( obj, ch ); if( !get_eq_char( ch, WEAR_HOLD_L ) && !get_eq_char( ch, WEAR_SHIELD ) && !get_eq_char( ch, WEAR_WIELD_L ) && !get_eq_char( ch, WEAR_WIELD_DOUBLE ) ) equip_char( ch, obj, WEAR_HOLD_L ); else if( !get_eq_char( ch, WEAR_HOLD_R ) && !get_eq_char( ch, WEAR_WIELD_R ) && !get_eq_char( ch, WEAR_WIELD_DOUBLE ) ) equip_char( ch, obj, WEAR_HOLD_R ); } break; default: if( dam > victim->max_hit / 5 ) send_to_char( "&rThat really did &RHURT!&n\n\r", victim ); check_critical( ch, victim, dam, wpn, dt ); if( victim->position != POS_DEAD && victim->hit < victim->max_hit / 5 ) { xSET_BIT( victim->affected_by, AFF_BLEEDING ); send_to_char( "You sure are &rBLEEDING!&n\n\r", victim ); } break; } /* * Payoff for killing things. */ if( victim->position == POS_DEAD ) { group_gain( ch, victim ); if( !IS_NPC( ch ) ) { ch->pcdata->killed++; if( ch->pcdata->clan && ( ( ( IS_NPC( victim ) && ch->pcdata->clan->clan_type != CLAN_NORMAL ) ) || ( !IS_NPC( victim ) && ch->pcdata->clan->clan_type == CLAN_NORMAL ) ) ) ch->pcdata->clan->kills++; } if( !IS_NPC( victim ) ) { SysInfo->deaths++; if( victim->pcdata->clan && ( ( IS_NPC( ch ) && victim->pcdata->clan->clan_type != CLAN_NORMAL ) || ( !IS_NPC( ch ) && victim->pcdata->clan->clan_type == CLAN_NORMAL ) ) ) victim->pcdata->clan->deaths++; victim->pcdata->died++; sprintf( log_buf, "%s killed by %s at %s", victim->name, ch == victim ? "their own stupidity" : ( IS_NPC( ch ) ? ch->short_descr : ch->name ), victim->in_room->name ); talk_channel( NULL, log_buf, CHANNEL_INFO, "INFO" ); log_string( "%s (%d)", log_buf, victim->in_room->vnum ); wiznetf( victim, WIZ_DEATHS, get_trust( victim ), "%s (%d)", log_buf, victim->in_room->vnum ); /* * Battle stuff. * Need to keep the battle flag set so they can keep * all their gear and get a restore. */ end_battle_check( victim ); /* * Dying penalty: * 1/2 way back to previous 2 levels. */ if( IS_NPC( ch ) && !xIS_SET( victim->act, PLR_BATTLE ) ) { if( victim->exp < get_tnl( victim ) - 4 ) gain_exp( victim, ( victim->exp - get_tnl( victim ) ) / 3 ); else gain_exp( victim, -4 ); } } if( !IS_NPC( victim ) && victim->pcdata->bounty > 0 && !xIS_SET( victim->act, PLR_BATTLE ) && !IS_SET( victim->in_room->room_flags, ROOM_ARENA ) ) { act( "&g$n collect$% the bounty on $O head.", ch, NULL, victim, TO_ALL ); ch->gold += victim->pcdata->bounty; victim->pcdata->bounty = 0; } if( !IS_NPC( victim ) && !IS_AFFECTED( victim, AFF_DEAD ) && get_success( victim, gsn_swan_song, 60 ) ) { xSET_BIT( victim->affected_by, AFF_DEAD ); update_pos( victim ); create_char_event( victim, evn_swan_song, dice( 4, 24 ) * PULSE_PER_SECOND ); return; } raw_kill( ch, victim ); /* Ok, now we want to remove the deleted flag from the * PC victim. */ if( !IS_NPC( victim ) ) victim->deleted = FALSE; if( !IS_NPC( ch ) && IS_NPC( victim ) ) { if( ch->pcdata->pc_bits >= PC_BIT_STRANGLE ) ch->pcdata->pc_bits %= PC_BIT_STRANGLE; /* Autogold by Morpheus */ if( xIS_SET( ch->act, PLR_AUTOGOLD ) ) do_get( ch, "coins corpse" ); /* autogold mod by Canth */ if( xIS_SET( ch->act, PLR_AUTOLOOT ) ) do_get( ch, "all corpse" ); else do_look( ch, "in corpse" ); if( xIS_SET( ch->act, PLR_AUTOSAC ) ) do_sacrifice( ch, "corpse" ); } else if( IS_NPC( ch ) && xIS_SET( ch->act, ACT_MERCENARY ) && IS_NPC( victim ) ) { do_get( ch, "all corpse" ); sort_inventory( ch ); } /* * Remove victims of pk who no longer have exps left */ if( !IS_NPC( victim ) && xIS_SET( victim->act, PLR_DENY ) ) { do_quit( victim, "" ); } return; } else { if( dam > 0 && dt == TYPE_HIT && !get_eq_char( ch, WEAR_HOLD_R ) && !get_eq_char( ch, WEAR_HOLD_L ) && !get_eq_char( ch, WEAR_SHIELD ) ) { barehand_hit( ch, victim ); if( victim->deleted || victim->position == POS_DEAD ) return; } else if( ( dt > 0 && dt < MAX_SKILL && IS_SET( skill_table[dt].skill_type, SKILL_TYPE_WEAPONSTRIKE ) ) || ( dt >= TYPE_HIT && dt != TYPE_MPDAMAGE ) ) { OBJ_DATA *wield; if( dam > 0 && ( wield = get_eq_char( ch, wpn ) ) ) { if( IS_SET( wield->extra_flags, ITEM_POISONED ) ) { ( *skill_table[gsn_poison].spell_fun ) ( gsn_poison, ( wield->level / 2 ), ch, victim ); } if( victim->deleted || victim->position == POS_DEAD ) return; if( wield->item_type == ITEM_WEAPON && wield->value[0] > 0 ) cast_weapon_spell( ch, victim, wield ); if( victim->deleted || victim->position == POS_DEAD ) return; } } } if( victim == ch ) return; /* * Take care of link dead people. if( !IS_NPC( victim ) && !victim->desc ) { if( number_range( 0, victim->wait ) == 0 ) { do_recall( victim, "" ); return; } } */ /* * Wimp out? */ if( IS_NPC( victim ) && dam > 0 ) { if( ( xIS_SET( victim->act, ACT_WIMPY ) && number_bits( 1 ) == 0 && victim->hit < victim->max_hit / 3 ) || ( IS_AFFECTED( victim, AFF_CHARM ) && victim->master && victim->master->in_room != victim->in_room ) ) do_flee( victim, "" ); } if( !IS_NPC( victim ) && victim->hit > 0 && victim->hit <= victim->wimpy && victim->wait == 0 ) do_flee( victim, "" ); return; } bool is_safe( CHAR_DATA *ch, CHAR_DATA *victim ) { if( IS_SET( SysInfo->flags, SYSINFO_NOFIGHT ) ) { send_to_char( "You can't fight!\n\r", ch ); return TRUE; } /* the organised PK options... checked before some of the standard * 'safe' options such as a safe room. */ if( !IS_NPC( ch ) && !IS_NPC( victim ) ) { if( IS_SET( victim->in_room->room_flags, ROOM_ARENA ) ) return FALSE; if( xIS_SET( ch->act, PLR_BATTLE ) && xIS_SET( victim->act, PLR_BATTLE ) && ( !IS_SET( SysInfo->flags, SYSINFO_HOLYWAR ) || ch->pcdata->religion != victim->pcdata->religion ) ) return FALSE; } if( !IS_NPC( victim ) && xIS_SET( victim->act, PLR_OUTLAW ) ) return FALSE; if( !IS_NPC( ch ) && IS_AFFECTED( ch, AFF_GHOUL ) ) { send_to_char( "You may not participate in combat while in ghoul form.\n\r", ch ); return TRUE; } if( !IS_NPC( victim ) && IS_AFFECTED( victim, AFF_GHOUL ) ) { act( "&g$o attack passes through $N.", ch, NULL, victim, TO_ALL ); return TRUE; } if( IS_SET( victim->in_room->room_flags, ROOM_SAFE ) ) { act( "&g$N is in a safe room.", ch, NULL, victim, TO_CHAR ); return TRUE; } if( is_clan_enemy( victim, ch ) ) return FALSE; if( !IS_NPC( ch ) && IS_NPC( victim ) && victim->spec_fun && IS_SET( spec_table[victim->spec_fun].usage, SPEC_ATTACK ) ) { return (*spec_table[victim->spec_fun].spec_fun) ( victim, ch, SPEC_ATTACK, NULL ); } if( IS_NPC( ch ) ) return FALSE; if( IS_NPC( victim ) ) { /* * Mobprog for getting attacked. * Note that the hard coded prog has precedence, if it exists * these will be ignored. */ if( !victim->fighting && xIS_SET( victim->pIndexData->progtypes, ATTACK_PROG ) ) mprog_percent_check( victim, ch, NULL, NULL, ATTACK_PROG ); if( ch->in_room != victim->in_room || ch->in_room->area != victim->in_room->area ) return TRUE; return FALSE; } if( ch != victim && str_cmp( race_table[victim->race].name, "Vampire" ) ) { if( victim->level > LEVEL_HERO ) { act( "$N is a &uHERO&n and is automatically safe.", ch, NULL, victim, TO_CHAR ); return TRUE; } return TRUE; } return FALSE; } void show_opponent( CHAR_DATA *ch ) { int state; const char *message; if( !ch->fighting ) return; state = ch->fighting->hit * 100 / ch->fighting->max_hit; if( state < 12 ) message = "&b$N is really buggered, $E looks ready to drop.&n"; else if( state < 25 ) message = "&b$N is wounded badly, $E looks gone for all money.&n"; else if( state < 38 ) message = "&b$N is bleeding freely from many cuts.&n"; else if( state < 51 ) message = "&b$N sways in the breeze, looking dazed.&n"; else if( state < 64 ) message = "&b$N is bruised and sore looking.&n"; else if( state < 77 ) message = "&b$N has a few minor scratches and abrasions.&n"; else if( state < 90 ) message = "&b$N is slightly hurt, but would still pass physical.&n"; else message = "&b$N has no obvious health problems.&n"; act( message, ch, NULL, ch->fighting, TO_CHAR ); if( ch->in_room != ch->fighting->in_room ) { stop_fighting( ch->fighting, FALSE ); stop_fighting( ch, FALSE ); } } void barehand_hit( CHAR_DATA *ch, CHAR_DATA *victim ) { struct bh_type { int * num; int cha; }; const struct bh_type bh_gsns[] = { { &gsn_backhand, 10 }, { &gsn_jab, 20 }, { &gsn_knee, 40 }, { &gsn_elbow, 80 }, { &gsn_uppercut, 160 }, { &gsn_roundhouse, 320 }, { &gsn_headbutt, 500 }, { NULL, 0 } }; int i, chance; int cost = 20; if( !IS_NPC( ch ) && IS_SET( ch->pcdata->pc_bits, PC_BIT_SURGE ) ) cost *= 3; /* if you are barehanded you get the chance at the barehanded effects */ if( IS_AFFECTED( ch, AFF_MAGICHANDS ) ) { if( ch->mana[MAGIC_FIRE] >= cost && is_affected( ch, gsn_burning_hands ) ) { ch->mana[MAGIC_FIRE] -= cost; spell_power_3( gsn_burn, ch->level, ch, victim ); } else if( ch->mana[MAGIC_WATER] >= cost && is_affected( ch, gsn_chill_touch ) ) { ch->mana[MAGIC_WATER] -= cost; spell_freeze( skill_lookup( "freeze" ), ch->level, ch, victim ); } } if( ch->fighting != victim || victim->deleted || victim->position == POS_DEAD ) return; if( IS_AFFECTED( ch, AFF_MAGICHANDS ) && ch->mana[MAGIC_AIR] >= cost && is_affected( ch, gsn_shocking_grasp ) ) { ch->mana[MAGIC_AIR] -= cost; spell_power_5( skill_lookup( "shock" ), ch->level, ch, victim ); if( ch->fighting != victim || victim->deleted || victim->position == POS_DEAD ) return; } if( IS_AFFECTED( ch, AFF_MAGICHANDS ) && is_affected( ch, gsn_dark_claws ) ) { spell_poison( skill_lookup( "poison" ), ch->level * 2 / 3, ch, victim ); if( ch->fighting != victim || victim->deleted || victim->position == POS_DEAD ) return; } if( IS_NPC( ch ) ) return; for( i = 0; bh_gsns[i].num; ++i ) { if( !can_use( ch, *bh_gsns[i].num ) ) continue; chance = 300 * bh_gsns[i].cha; chance /= 2 * ch->pcdata->learned[*bh_gsns[i].num] + ch->pcdata->learned[gsn_barehand] + 2 * get_curr_dex( ch ); if( number_range( 0, chance ) == 0 ) { one_hit( ch, victim, *bh_gsns[i].num, WEAR_WIELD_R ); return; } } return; } void flashing_blades( CHAR_DATA *ch, CHAR_DATA *victim, int dt, int wpn ) { int chance; if( IS_NPC_CLASS( ch ) || ch->fighting != victim || dt < TYPE_HIT || ( wpn != WEAR_WIELD_L && wpn != WEAR_WIELD_R ) || !can_use( ch, gsn_flashing_blades ) ) return; if( ( wpn == WEAR_WIELD_L && get_eq_char( ch, WEAR_WIELD_R ) ) || !get_eq_char( ch, wpn ) ) return; chance = power( get_success( ch, gsn_flashing_blades, 100 ), -2, ch->carry_weight - 10 ); if( number_bits( 9 ) < chance ) one_hit( ch, victim, dt, wpn ); return; } bool get_juggle_attack( CHAR_DATA *ch, int num ) { OBJ_DATA * obj; int amount = 0; int chance; if( ch->deleted || !ch->fighting || ch->position < POS_FIGHTING ) return FALSE; for( obj = get_eq_char( ch, WEAR_JUGGLED ); obj; obj = obj->next_content ) { if( obj->deleted || obj->wear_loc != WEAR_JUGGLED ) continue; amount++; } /* only a small reduction of chance for higher number attacks as these are less likely to occur due to initiative lossage. */ chance = num * 5 + power( 20, 5, 22 - get_curr_dex( ch ) ); if( amount < num || number_bits( 7 ) < chance ) return FALSE; return TRUE; } void juggle_shuffle( CHAR_DATA *ch ) { OBJ_DATA *obj; OBJ_DATA *juggled[MAX_JUGGLED]; int i = 0; int slot; if( !get_eq_char( ch, WEAR_JUGGLED ) ) return; for( obj = ch->carrying; i < MAX_JUGGLED && obj; obj = obj->next_content ) { if( !obj->deleted && ( obj->wear_loc == WEAR_JUGGLED || obj->wear_loc == WEAR_WIELD_L || obj->wear_loc == WEAR_WIELD_R ) ) { juggled[i++] = obj; obj->wear_loc = WEAR_JUGGLED; } } if( i < 3 ) { bug( "%s is juggling but not enough.\n\r", ch->name ); return; } do /* pick new right wield */ { slot = number_range( 0, i - 1 ); } while( juggled[slot] == NULL ); juggled[slot]->wear_loc = WEAR_WIELD_R; juggled[slot] = NULL; do /* pick new left wield */ { slot = number_range( 0, i - 1 ); } while( juggled[slot] == NULL ); juggled[slot]->wear_loc = WEAR_WIELD_L; juggled[slot] = NULL; if( !get_success( ch, gsn_juggle, 130 - i * 10 ) ) { if( get_success( ch, gsn_juggle, 30 + get_curr_dex( ch ) ) ) { act( "$n stuff$% up but make$% a hasty recovery.", ch, NULL, NULL, TO_ALL ); WAIT_STATE( ch, skill_table[gsn_juggle].beats ); return; } act( "$n stuffs up $s juggling and drops weapons all over...", ch, NULL, NULL, TO_ROOM ); act( "You stuff up your juggling routine.", ch, NULL, NULL, TO_CHAR ); while( ( obj = get_eq_char( ch, WEAR_JUGGLED ) ) ) { if( number_bits( i ) > 10 ) { slot = obj->level * ( i - 3 ); slot = number_range( slot, slot * 3 / 2 ); damage( ch, ch, slot, gsn_juggle, WEAR_NONE ); } act( "$p falls to the ground.", ch, obj, NULL, TO_ALL ); obj_from_char( obj ); obj_to_room( obj, ch->in_room ); } } return; } void check_critical( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int wpn, int dt ) { OBJ_DATA *obj = NULL; int chance = 50 - get_curr_con( victim ); int part, i; if( dam <= victim->level /* rare occurance */ || victim->level < 10 || number_bits( 5 ) ) return; if( wpn != WEAR_NONE ) obj = get_eq_char( ch, wpn ); if( !obj ) chance -= 10; else { if( obj->item_type == ITEM_WEAPON ) return; /* can't do any damage without a weapon */ if( IS_SET( obj->extra_flags, ITEM_SHARP ) ) chance += 5; if( dam > victim->max_hit / 5 ) chance += 12; else if( dam > victim->max_hit / 10 ) chance += 5; } chance += ( get_curr_str( ch ) ) - 20; part = number_percent( ); for( i = 0; part_loss_table[i].chance > 0; ++i ) { if( part > part_loss_table[i].chance || ( part_loss_table[i].body_parts & victim->body_parts ) == 0 ) continue; if( IS_SET( victim->damaged_parts, part_loss_table[i].body_parts ) ) chance += 20; break; } chance = UMIN( chance, 25 ) - number_bits( 7 ); if( chance > 15 && part_loss_table[i].chance > 0 && dt > TYPE_HIT && dt != TYPE_HIT + 2 && ( dt <= TYPE_HIT + 5 || dt >= TYPE_HIT + 10 ) && dt != TYPE_HIT + 11 && dt != TYPE_HIT + 12 ) remove_part( ch, victim, i ); else if( chance > 0 ) { act( "You get a fluke shot on $N and blood spurts from a fresh wound.", ch, NULL, victim, TO_CHAR ); act( "$n gets a fluke shot on you, opening a new wound.", ch, NULL, victim, TO_VICT ); act( "$n strikes $N, making an ugly wound appear.", ch, NULL, victim, TO_NOTVICT ); if( IS_SET( victim->damaged_parts, part_loss_table[i].body_parts ) ) dam += dam / 2; SET_BIT( victim->damaged_parts, part_loss_table[i].body_parts ); xSET_BIT( victim->affected_by, AFF_BLEEDING ); } if( chance > 0 && victim->position != POS_DEAD ) { xSET_BIT( victim->affected_by, AFF_BLEEDING ); chance = UMAX( 10, chance ); dam = number_range( dam * chance / 75, dam * chance / 30 ); damage( ch, victim, dam, gsn_lucky_blow, wpn ); } return; } void remove_part( CHAR_DATA *ch, CHAR_DATA *victim, int part ) { int smeg; OBJ_DATA *obj, *obj_next; char buf[MAX_INPUT_LENGTH]; obj = create_object( get_obj_index( part_loss_table[part].limb_vnum ), victim->level ); sprintf( buf, obj->short_descr, IS_NPC( victim ) ? victim->short_descr : victim->name ); free_string( obj->short_descr ); obj->short_descr = str_dup( buf ); obj->value[0] = victim->body_parts & part_loss_table[part].body_parts; if( IS_AFFECTED( ch, AFF_POISON ) ) SET_BIT( obj->extra_flags, ITEM_POISONED ); if( IS_AFFECTED( victim, AFF_POISON ) ) obj->value[3] = 1; obj->cost /= 20; obj_to_room( obj, victim->in_room ); act( part_loss_table[part].act_char, victim, NULL, ch, TO_CHAR ); act( part_loss_table[part].act_other, victim, NULL, ch, TO_ROOM ); for( obj = victim->carrying; obj; obj = obj_next ) { obj_next = obj->next_content; if( obj->wear_loc == WEAR_NONE ) continue; smeg = part_loss_table[part].body_parts & victim->body_parts; smeg &= wear_table[obj->wear_loc].body_parts; if( smeg ) { act( "&m$p&m drops to the ground.&n", victim, obj, NULL, TO_ALL ); unequip_char( ch, obj ); obj_from_char( obj ); obj_to_room( obj, victim->in_room ); } } victim->body_parts &= ~part_loss_table[part].body_parts; if( IS_SET( part_loss_table[part].body_parts, BODY_PART_HEAD ) && victim->position != POS_DEAD ) { act( "$n die$%.", victim, NULL, ch, TO_ALL ); victim->hit = -10; victim->position = POS_DEAD; } return; } void damage_eq( CHAR_DATA *ch, CHAR_DATA *victim ) { OBJ_DATA *obj; if( number_bits( 10 ) > 65 - get_curr_dex( victim ) ) return; for( obj = victim->carrying; obj; obj = obj->next_content ) { if( obj->deleted || obj->wear_loc == WEAR_NONE || number_bits( 10 ) > 40 - get_curr_dex( victim ) ) continue; mod_item_condition( ch, obj, 1 ); return; } return; } /* * Check to see if the battle that is currently raging is over. */ void end_battle_check( CHAR_DATA *victim ) { CHAR_DATA *rch; int contin = 0; RELIGION_DATA *pReligion = NULL; CLAN_DATA *pClan; if( !xIS_SET( victim->act, PLR_BATTLE ) ) return; if( IS_SET( SysInfo->flags, SYSINFO_HOLYWAR ) ) { for( rch = char_list; rch; rch = rch->next ) { if( rch->deleted || IS_NPC( rch ) || rch == victim || !xIS_SET( rch->act, PLR_BATTLE ) ) continue; if( !pReligion ) pReligion = rch->pcdata->religion; else if( pReligion != rch->pcdata->religion ) return; /* More than one religion left, no action */ } for( rch = char_list; rch; rch = rch->next ) if( !IS_NPC( rch ) ) xREMOVE_BIT( rch->act, PLR_BATTLE ); REMOVE_BIT( SysInfo->flags, SYSINFO_HOLYWAR ); if( !pReligion ) { bug( "In the grasp of a Holy War and there is no winner." ); return; } sprintf( log_buf, "%s has won the Holy War for %s!", pReligion->display_name, pReligion->god_name ); talk_channel( NULL, log_buf, CHANNEL_INFO, "INFO" ); for( pClan = clan_first; pClan; pClan = pClan->next ) if( pClan->clan_type == CLAN_ORDER && pClan->religion == pReligion ) add_karma( pClan, 250 ); } else if( battle_min && battle_max ) { for( rch = char_list; rch; rch = rch->next ) { if( !IS_NPC( rch ) && xIS_SET( rch->act, PLR_BATTLE ) && rch != victim ) contin++; } if( contin < 2 ) { for( rch = char_list; rch; rch = rch->next ) if( !IS_NPC( rch ) && rch != victim && xIS_SET( rch->act, PLR_BATTLE ) ) break; if( rch ) { xREMOVE_BIT( rch->act, PLR_BATTLE ); sprintf( log_buf, "%s has won the battle!", rch->name ); talk_channel( NULL, log_buf, CHANNEL_INFO, "INFO" ); do_recall( rch, "auto" ); } battle_min = 0; battle_max = 0; } } } /* * See if an attack justifies a OUTLAW flag. */ void check_killer( CHAR_DATA *ch, CHAR_DATA *victim ) { OBJ_DATA *obj; OBJ_DATA *obj_next; char buf[MAX_STRING_LENGTH]; /* * NPC's are fair game. */ if( IS_NPC( victim ) ) return; /* * NPC's are cool of course * Hitting yourself is cool too (bleeding). * Hitting immortals are fine, if you can. */ if( IS_NPC( ch ) || ch == victim || victim->level > LEVEL_HERO ) return; /* * OUTLAWs are fair game. * OUTLAW aggressors should not be penalized 1000 exps per attack but * per combat started. */ if( xIS_SET( victim->act, PLR_OUTLAW ) || ( xIS_SET( ch->act, PLR_OUTLAW ) && ch->fighting ) ) return; if( IS_SET( ch->in_room->room_flags, ROOM_ARENA ) || ( xIS_SET( ch->act, PLR_BATTLE ) && xIS_SET( victim->act, PLR_BATTLE ) ) ) return; /* Allowing attacks on temporary vampires. */ if( !str_cmp( race_table[victim->race].name, "Vampire" ) && IS_AFFECTED( victim, AFF_VAMP_BITE ) ) return; send_to_char( "You are a &ROUTLAW!&n You lose 1000 exps.\n\r", ch ); sprintf( buf, "Help! I'm being attacked by %s!", ch->name ); do_shout( victim, buf ); xSET_BIT( ch->act, PLR_OUTLAW ); gain_exp( ch, -100000 ); demote_level( ch ); for( obj = ch->carrying; obj; obj = obj_next ) { obj_next = obj->next_content; if( obj->deleted ) continue; obj_from_char( obj ); /* * Remove item inventories * Includes licenses to kill */ if( IS_SET( obj->extra_flags, ITEM_INVENTORY ) ) { extract_obj( obj ); continue; } obj_to_char( obj, ch ); } save_char_obj( ch ); return; } /* * Check to see if weapon is poisoned. */ bool is_wielding_poisoned( CHAR_DATA *ch, int wpn ) { OBJ_DATA *obj; if( ( obj = get_eq_char( ch, wpn ) ) && IS_SET( obj->extra_flags, ITEM_POISONED ) ) return TRUE; return FALSE; } /* * Check for block. */ bool check_shield_block( CHAR_DATA *ch, CHAR_DATA *victim ) { int chance; if( IS_NPC_CLASS( victim ) || IS_SET( victim->extra_bits, CH_EX_SHIELD_BLOCK ) || !IS_AWAKE( victim ) ) return FALSE; if( get_eq_char( victim, WEAR_SHIELD ) == NULL ) return FALSE; chance = get_success( victim, gsn_shield_block, 200 ) * get_curr_str( victim ) / 40; if( number_percent( ) >= chance + victim->level - ch->level ) return FALSE; if( victim->desc && !xIS_SET( CH( victim->desc )->act, PLR_BATTLESELF ) ) act( "&gYou block $n's attack with your shield.", ch, NULL, victim, TO_VICT ); if( ch->desc && !xIS_SET( CH( ch->desc )->act, PLR_BATTLESELF ) ) act( "&g$N blocks your attack with a shield.", ch, NULL, victim, TO_CHAR ); if( number_bits( 2 ) ) SET_BIT( victim->extra_bits, CH_EX_SHIELD_BLOCK ); return TRUE; } /* * Check for parry. */ bool check_parry( CHAR_DATA *ch, CHAR_DATA *victim ) { int chance; if( IS_SET( victim->extra_bits, CH_EX_PARRY ) || !IS_AWAKE( victim ) ) return FALSE; if( IS_NPC_CLASS( victim ) ) { /* Tuan was here. :) */ chance = UMIN( 40, victim->level - ch->level / 3 ); if( !get_eq_char( victim, WEAR_WIELD_R ) ) { if( !get_eq_char( victim, WEAR_WIELD_L ) ) chance /= 2; else chance = 3 * chance / 4; } } else { if( !get_eq_char( victim, WEAR_WIELD_R ) ) { if( !get_eq_char( victim, WEAR_WIELD_L ) ) return FALSE; chance = get_success( victim, gsn_parry, 200 ) / 4; } else chance = get_success( victim, gsn_parry, 200 ) / 2; if( !IS_NPC( victim ) ) chance -= victim->pcdata->condition[COND_DRUNK] / 10; } if( ch->position < POS_FIGHTING ) chance = chance * 3 / 4; if( number_percent( ) >= chance + victim->level - ch->level ) return FALSE; if( number_bits( 2 ) ) SET_BIT( victim->extra_bits, CH_EX_PARRY ); if( ch->desc && !xIS_SET( CH( ch->desc )->act, PLR_BATTLESELF ) ) act( "&g$N parries your attack.", ch, NULL, victim, TO_CHAR ); if( victim->desc && !xIS_SET( CH( victim->desc )->act, PLR_BATTLESELF ) ) act( "&gYou parry $n's attack.", ch, NULL, victim, TO_VICT ); return TRUE; } /* * Check for dodge. */ bool check_dodge( CHAR_DATA *ch, CHAR_DATA *victim ) { int chance, which_bit; if( !IS_AWAKE( victim ) ) return FALSE; if( IS_SET( victim->extra_bits, CH_EX_DODGE ) ) { if( IS_SET( victim->extra_bits, CH_EX_DODGE_2 ) ) return FALSE; else which_bit = CH_EX_DODGE_2; } else which_bit = CH_EX_DODGE; if( IS_NPC_CLASS( victim ) ) /* Tuan was here. :) */ chance = UMIN( 25, victim->level - ch->level / 3 ); else { if( which_bit == CH_EX_DODGE ) chance = get_success( victim, gsn_dodge, 200 ) / 3; else chance = get_success( victim, gsn_avoidance, 200 ) / 3; chance = power( chance, 2, get_curr_dex( ch ) - 20 ); if( !IS_NPC( victim ) ) chance -= victim->pcdata->condition[COND_DRUNK] / 10; } if( chance > 0 ) chance += UMIN( get_speed( victim ), 30 ); if( ch->position < POS_FIGHTING ) chance = chance * 2 / 3; if( number_percent( ) >= chance + victim->level - ch->level ) return FALSE; if( number_bits( 2 ) ) SET_BIT( victim->extra_bits, which_bit ); if( ch->desc && !xIS_SET( CH( ch->desc )->act, PLR_BATTLESELF ) ) act( "&g$N dodges your attack.", ch, NULL, victim, TO_CHAR ); if( victim->desc && !xIS_SET( CH( victim->desc )->act, PLR_BATTLESELF ) ) act( "&gYou dodge $n's attack.", ch, NULL, victim, TO_VICT ); return TRUE; } /* * Check for dodge. */ bool check_blink( CHAR_DATA *ch, CHAR_DATA *victim ) { int chance; if( !IS_AWAKE( victim ) || IS_NPC( victim ) || victim->pcdata->blink <= 0 ) return FALSE; chance = get_success( victim, gsn_blink, 100 ) * victim->pcdata->blink / 100; chance = power( chance, 2, get_curr_int( ch ) - 20 ); chance -= victim->pcdata->condition[COND_DRUNK] / 10; if( chance > 0 ) chance += UMIN( get_speed( victim ), 30 ); if( ch->position < POS_FIGHTING ) chance = chance * 2 / 3; if( number_percent( ) >= chance + victim->level - ch->level ) return FALSE; if( number_bits( 2 ) ) SET_BIT( victim->extra_bits, CH_EX_BLINK ); if( ch->desc && !xIS_SET( CH( ch->desc )->act, PLR_BATTLESELF ) ) act( "&g$N blinks out of existance avoiding your strike.", ch, NULL, victim, TO_CHAR ); if( victim->desc && !xIS_SET( CH( victim->desc )->act, PLR_BATTLESELF ) ) act( "&gYou blink out of existance, narrowly avoiding $n's attack.", ch, NULL, victim, TO_VICT ); take_generic_mana( victim, victim->pcdata->blink * MAGIC_MAX ); return TRUE; } /* * One of the advantages to being drunk. */ bool check_stumble( CHAR_DATA *ch, CHAR_DATA *victim ) { if( IS_NPC( victim ) || IS_SET( victim->extra_bits, CH_EX_STUMBLE ) || !IS_AWAKE( victim ) ) return FALSE; if( number_percent( ) < ( victim->pcdata->condition[COND_DRUNK] / 10 - 20 ) / 2 ) { if( ch->desc && !xIS_SET( CH( ch->desc )->act, PLR_BATTLESELF ) ) act( "&g$N stumbles drunkenly and your blow goes wide.", ch, NULL, victim, TO_CHAR ); if( victim->desc && !xIS_SET( CH( victim->desc )->act, PLR_BATTLESELF ) ) act( "&gYou trip over your weapon and land in a heap on the ground.", ch, NULL, victim, TO_VICT ); victim->position = POS_GETTING_UP; WAIT_STATE( victim, PULSE_VIOLENCE / 2 ); SET_BIT( victim->extra_bits, CH_EX_STUMBLE ); return TRUE; } return FALSE; } #define chance 42 bool check_familiar( CHAR_DATA *ch, CHAR_DATA *victim, int dam ) { if( IS_NPC( victim ) || IS_SET( victim->extra_bits, CH_EX_FAMILIAR ) || !IS_AWAKE( victim ) || victim->pcdata->familiar <= 0 ) return FALSE; if( number_percent( ) > UMIN( 60, chance - victim->level + ch->level ) ) { if( number_percent( ) > UMIN( 5, ( chance - victim->level + ch->level ) / 12 ) ) return FALSE; else { if( victim->desc && !xIS_SET( CH( victim->desc )->act, PLR_BATTLESELF ) ) act( "&gYour familiar distracts $n from $s attack.", ch, NULL, victim, TO_VICT ); if( ch->desc && !xIS_SET( CH( ch->desc )->act, PLR_BATTLESELF ) ) act( "&g$N's familiar distracts you and you miss $M, #&&*@!", ch, NULL, victim, TO_CHAR ); SET_BIT( victim->extra_bits, CH_EX_FAMILIAR ); return TRUE; } } if( victim->desc && !xIS_SET( CH( victim->desc )->act, PLR_BATTLESELF ) ) act( "&gYour familiar bravely takes $n's attack for you.", ch, NULL, victim, TO_VICT ); if( ch->desc && !xIS_SET( CH( ch->desc )->act, PLR_BATTLESELF ) ) act( "&g$N's familiar gets in the way of your attack.", ch, NULL, victim, TO_CHAR ); victim->pcdata->familiar -= dam; if( victim->pcdata->familiar <= 0 ) { victim->pcdata->familiar = 0; act( "&y$o familiar screams and disappears.", victim, NULL, ch, TO_ALL ); } if( number_bits( 2 ) ) SET_BIT( victim->extra_bits, CH_EX_FAMILIAR ); return TRUE; } #undef chance void dirty_fighting( CHAR_DATA *ch ) { CHAR_DATA *victim = ch->fighting; AFFECT_DATA af; OBJ_DATA *obj; int i; char buf[MAX_INPUT_LENGTH]; if( !victim ) return; switch( number_bits( 3 ) ) { case 0: case 1: act( "$n fling$% a handful of dirt in $O face, blinding $M!", ch, NULL, victim, TO_ALL ); af.type = gsn_dirty_fighting; af.location = APPLY_HITROLL; af.modifier = -4 - ch->level / 25; af.duration = 0; vset( af.bitvector, AFF_BLIND ); affect_to_char( victim, &af, NULL ); break; case 2: act( "$n knee$% $N in the groin.", ch, NULL, victim, TO_ALL ); if( victim->sex != SEX_MALE ) { act( "$n shrugs off the effect of the blow.", victim, NULL, NULL, TO_ROOM ); send_to_char( "That didn't hurt too much.\n\r", victim ); damage( ch, victim, number_range( ch->level / 2, ch->level ), gsn_dirty_fighting, WEAR_NONE ); } else { act( "$n groans in agony and doubles over, clutching at $s groin.", victim, NULL, NULL, TO_ROOM ); send_to_char( "OOOMMPH... you double over in agony!\n\r", victim ); damage( ch, victim, number_range( ch->level, ch->level * 3 ), gsn_dirty_fighting, WEAR_NONE ); victim->position = POS_GETTING_UP; WAIT_STATE( victim, PULSE_VIOLENCE ); } break; case 3: if( get_curr_dex( ch ) - get_curr_dex( victim ) + number_range( -10, 10 ) < 0 ) break; act( "$n grabs $O ears and crushes $S face to a pulp with $s forehead.", ch, NULL, victim, TO_ROOM ); act( "You grab $N and headbutt him, square between the eyes.", ch, NULL, victim, TO_CHAR ); if( get_eq_char( victim, WEAR_FACE ) || !IS_SET( victim->body_parts, BODY_PART_NOSE ) ) damage( ch, victim, number_range( ch->level / 2, ch->level ), gsn_dirty_fighting, WEAR_NONE ); else { act( "$O nose is splattered across $S face.", ch, NULL, victim, TO_ALL ); damage( ch, victim, number_range( ch->level, ch->level * 3 ), gsn_dirty_fighting, WEAR_NONE ); REMOVE_BIT( victim->body_parts, BODY_PART_NOSE ); xSET_BIT( victim->affected_by, AFF_BLEEDING ); } break; case 4: act( "$n leap$% for $O eyes, fingers clawed.", ch, NULL, victim, TO_ALL ); if( number_bits( 3 ) || get_eq_char( victim, WEAR_FACE ) || !IS_SET( victim->body_parts, BODY_PART_EYES ) || get_curr_dex( ch ) - get_curr_dex( victim ) + number_range( -10, 10 ) < 0 ) break; for( i = 0; part_loss_table[i].body_parts != BODY_PART_EYES; ) ++i; obj = create_object( get_obj_index( part_loss_table[i].limb_vnum ), victim->level ); sprintf( buf, obj->short_descr, IS_NPC( victim ) ? victim->short_descr : victim->name ); free_string( obj->short_descr ); obj->short_descr = str_dup( buf ); obj->value[0] = victim->body_parts & part_loss_table[i].body_parts; if( IS_AFFECTED( victim, AFF_POISON ) ) obj->value[3] = 1; obj->cost /= 20; obj_to_char( obj, ch ); act( part_loss_table[i].act_char, victim, NULL, ch, TO_CHAR ); act( part_loss_table[i].act_other, victim, NULL, ch, TO_ROOM ); break; case 5: act( "$n deliver$% a swift kick to $O shins.", ch, NULL, victim, TO_ALL ); damage( ch, victim, number_range( ch->level / 2, ch->level ), gsn_dirty_fighting, WEAR_NONE ); break; default: break; } } /* Mobs using magical items by Spitfire from merc mailing list */ /* Modified to give every magical item an equal chance of being used plus * eating pills -Kahn */ void use_magical_item( CHAR_DATA *ch ) { OBJ_DATA *obj; OBJ_DATA *cobj = NULL; int number = 0; char buf[MAX_INPUT_LENGTH]; for( obj = ch->carrying; obj; obj = obj->next_content ) { if( ( obj->item_type == ITEM_SCROLL || obj->item_type == ITEM_WAND || obj->item_type == ITEM_STAFF || obj->item_type == ITEM_PILL ) && number_range( 0, number ) == 0 ) { cobj = obj; number++; } } if( !cobj ) return; switch( cobj->item_type ) { case ITEM_SCROLL: do_recite( ch, "scroll" ); break; case ITEM_WAND: if( cobj->wear_loc == WEAR_HOLD_L || cobj->wear_loc == WEAR_HOLD_R ) do_zap( ch, "" ); break; case ITEM_STAFF: if( cobj->wear_loc == WEAR_HOLD_L || cobj->wear_loc == WEAR_HOLD_R ) do_brandish( ch, "" ); break; case ITEM_POTION: do_quaff( ch, "potion" ); break; case ITEM_PILL: sprintf( buf, "%s", cobj->name ); do_eat( ch, buf ); break; } return; } /* * Set position of a victim. */ void update_pos( CHAR_DATA *victim ) { if( victim->hit > 0 || IS_AFFECTED( victim, AFF_DEAD ) ) { if( victim->position <= POS_STUNNED ) victim->position = POS_STANDING; return; } if( IS_NPC( victim ) || victim->hit <= -11 ) { victim->position = POS_DEAD; return; } if( victim->hit <= -6 ) victim->position = POS_MORTAL; else if( victim->hit <= -3 ) victim->position = POS_INCAP; else victim->position = POS_STUNNED; return; } /* * Start fights. */ void set_fighting( CHAR_DATA *ch, CHAR_DATA *victim ) { if( ch->fighting ) { bug( "Set_fighting: already fighting" ); bug( "...%s attacking %s at %d", ( IS_NPC( ch ) ? ch->short_descr : ch->name ), ( IS_NPC( victim ) ? victim->short_descr : victim->name ), victim->in_room->vnum ); return; } if( IS_AFFECTED( ch, AFF_SLEEP ) ) affect_strip( ch, gsn_sleep ); ch->fighting = victim; if( ch->position != POS_SMASHED && ch->position != POS_SLEEPING && ch->position != POS_GETTING_UP ) ch->position = POS_FIGHTING; if( IS_NPC( ch ) ) add_mob_fight_triggers( ch ); if( get_time_left( ch->in_room->events, evn_room_violence ) < 0 ) create_room_event( ch->in_room, evn_room_violence, PULSE_VIOLENCE ); return; } /* * Stop fights. */ void stop_fighting( CHAR_DATA *ch, bool fBoth ) { CHAR_DATA *fch; purge_spam( ch->in_room ); for( fch = char_list; fch; fch = fch->next ) { if( fch == ch || ( fBoth && fch->fighting == ch ) ) { fch->fighting = NULL; if( fch->position != POS_SMASHED ) fch->position = POS_STANDING; if( IS_AFFECTED( fch, AFF_BERSERK ) ) affect_strip( fch, gsn_berserk ); if( !IS_NPC( fch ) && fch->pcdata->pc_bits >= PC_BIT_STRANGLE ) fch->pcdata->pc_bits %= PC_BIT_STRANGLE; update_pos( fch ); } } return; } /* * Stop fights only in the character's room. * easier on CPU from mobile_update */ void stop_fighting_room( CHAR_DATA *ch, bool fBoth ) { CHAR_DATA *fch; if( ch->in_room->area->nplayer ) purge_spam( ch->in_room ); for( fch = ch->in_room->people; fch; fch = fch->next_in_room ) { if( fch == ch || ( fBoth && fch->fighting == ch ) ) { fch->fighting = NULL; if( fch->position != POS_SMASHED ) fch->position = POS_STANDING; if( IS_AFFECTED( fch, AFF_BERSERK ) ) affect_strip( fch, gsn_berserk ); if( !IS_NPC( fch ) && fch->pcdata->pc_bits >= PC_BIT_STRANGLE ) fch->pcdata->pc_bits %= PC_BIT_STRANGLE; update_pos( fch ); } } return; } /* * Make a corpse out of a character. */ void make_corpse( CHAR_DATA *ch ) { OBJ_DATA *corpse; OBJ_DATA *obj; OBJ_DATA *obj_next; char *name; char buf[MAX_STRING_LENGTH]; if( IS_NPC( ch ) ) { /* * This longwinded corpse creation routine comes about because * we dont want anything created AFTER a corpse to be placed * INSIDE a corpse. This had caused crashes from obj_update( ) * in extract_obj( ) when the updating list got shifted from * object_list to obj_free. --- Thelonius (Monk) */ name = ch->short_descr; if( ch->gold > 0 ) { OBJ_DATA *coins; coins = create_money( ch->gold ); corpse = create_object( get_obj_index( OBJ_VNUM_CORPSE_NPC ), 0 ); obj_to_obj( coins, corpse ); ch->gold = 0; } else { corpse = create_object( get_obj_index( OBJ_VNUM_CORPSE_NPC ), 0 ); } set_timer_tick( corpse, number_fuzzy( 2 ) ); } else { name = ch->name; corpse = create_object( get_obj_index( OBJ_VNUM_CORPSE_PC ), 0 ); set_timer_tick( corpse, number_range( 20, 30 ) ); } corpse->value[0] = ch->race; sprintf( buf, "%s %s", corpse->name, name ); free_string( corpse->name ); corpse->name = str_dup( buf ); sprintf( buf, corpse->short_descr, name ); free_string( corpse->short_descr ); corpse->short_descr = str_dup( buf ); sprintf( buf, corpse->description, name ); free_string( corpse->description ); corpse->description = str_dup( buf ); corpse->level = ch->level; for( obj = ch->carrying; obj; obj = obj_next ) { obj_next = obj->next_content; if( obj->deleted ) continue; obj_from_char( obj ); /* * Remove item inventories from all corpses. * Includes licenses to kill */ if( IS_SET( obj->extra_flags, ITEM_INVENTORY ) ) { extract_obj( obj ); } else { obj_to_obj( obj, corpse ); } } obj_to_room( corpse, ch->in_room ); strip_events( &corpse->events, evn_imp_grab ); return; } /* * Improved Death_cry contributed by Diavolo. */ void death_cry( CHAR_DATA *ch ) { OBJ_DATA *obj; char buf[MAX_STRING_LENGTH]; const char *msg; int vnum; int door; int i, chance; vnum = 0; switch( number_bits( 4 ) ) { default: msg = "&yYou hear $n's death cry."; break; case 0: case 1: case 2: msg = "&yYou hear $n's death cry."; vnum = -1; /* take an actual limb */ break; case 3: msg = "&y$n hits the ground ... DEAD."; break; case 4: msg = "&y$n splatters blood on your armour."; break; case 5: case 6: msg = "&yYou smell $n's sphincter releasing in death."; vnum = OBJ_VNUM_FINAL_TURD; break; case 7: msg = "&y$n crumples to the ground, lifeless."; break; case 8: case 9: msg = "&yYou lift $o still beating heart from $s chest in triumph."; vnum = OBJ_VNUM_TORN_HEART; break; case 10: msg = "&y$n screams in agony as $s life leaves $m."; break; case 11: msg = "&yYou slice $n open and spill $s guts on the ground."; vnum = OBJ_VNUM_SPILLED_GUTS; break; case 12: msg = "&yYour blow smacks $n fair in the mouth and broken teeth fly."; vnum = OBJ_VNUM_LOOSE_TEETH; break; } if( !IS_NPC( ch ) && number_bits( 1 ) ) vnum = 0; if( vnum > 0 ) { char *name; act( msg, ch, NULL, NULL, TO_ALL ); name = IS_NPC( ch ) ? ch->short_descr : ch->name; obj = create_object( get_obj_index( vnum ), ch->level ); set_timer_tick( obj, number_range( 4, 7 ) ); if( IS_AFFECTED( ch, AFF_POISON ) ) SET_BIT( obj->extra_flags, ITEM_POISONED ); sprintf( buf, obj->short_descr, name ); free_string( obj->short_descr ); obj->short_descr = str_dup( buf ); sprintf( buf, obj->description, name ); free_string( obj->description ); obj->description = str_dup( buf ); if( IS_AFFECTED( ch, AFF_POISON ) ) obj->value[3] = 1; obj->cost /= 20; obj_to_room( obj, ch->in_room ); } else { chance = number_percent( ); for( i = 0; vnum < 0 && part_loss_table[i].chance > 0; ++i ) { if( chance > part_loss_table[i].chance || ( part_loss_table[i].body_parts & ch->body_parts ) == 0 ) continue; remove_part( ch, ch, i ); break; } if( part_loss_table[i].chance <= 0 ) act( msg, ch, NULL, NULL, TO_ALL ); } if( IS_NPC( ch ) ) msg = "&yYou hear something's death cry.&n"; else msg = "&yYou hear someone's death cry.&n"; for( door = 0; door <= 5; door++ ) { EXIT_DATA *pexit; if( ( pexit = ch->in_room->exit[door] ) && pexit->to_room && pexit->to_room != ch->in_room ) send_to_room( msg, pexit->to_room ); } return; } void raw_kill( CHAR_DATA *ch, CHAR_DATA *victim ) { AFFECT_DATA *paf; bool safe_kill = FALSE; EVENT *e, *e_next; int can_cry = 1; if( !IS_NPC( victim ) && ( xIS_SET( victim->act, PLR_BATTLE ) || IS_SET( victim->in_room->room_flags, ROOM_ARENA ) ) ) safe_kill = TRUE; stop_fighting( victim, TRUE ); if( ch != victim && IS_NPC( victim ) && xIS_SET( victim->pIndexData->progtypes, DEATH_PROG ) ) { victim->position = POS_STANDING; can_cry = mprog_percent_check( victim, ch, NULL, NULL, DEATH_PROG ); } if( ch != victim && can_cry && can_cry != NO_FLAG ) death_cry( victim ); if( victim->spec_fun && IS_SET( spec_table[victim->spec_fun].usage, SPEC_DEATH ) ) { if( !( *spec_table[victim->spec_fun].spec_fun ) ( victim, ch, SPEC_DEATH, NULL ) ) make_corpse( victim ); } else if( safe_kill ) { OBJ_DATA *obj; for( obj = victim->carrying; obj; obj = obj->next_content ) { if( !obj->deleted && obj->wear_loc != WEAR_NONE ) unequip_char( victim, obj ); } } else make_corpse( victim ); if( !IS_NPC( victim ) && IS_AFFECTED( victim, AFF_VAMP_BITE ) ) { paf = new_affect( ); paf->type = gsn_vampiric_bite; paf->location = APPLY_RACE; paf->modifier = race_lookup( "Vampire" ) - victim->race; paf->duration = 1000; vset( paf->bitvector, AFF_POLYMORPH ); xSET_BIT( paf->bitvector, AFF_VAMP_BITE ); paf->next = victim->affected; victim->affected = paf; } for( paf = victim->affected; paf; paf = paf->next ) { if( paf->deleted || xIS_SET( paf->bitvector, AFF_POLYMORPH ) ) continue; /* Keep the ghoul affect, not if( !IS_NPC( victim ) && IS_AFFECTED( victim, AFF_GHOUL ) ) continue; */ affect_remove( victim, paf ); } for( e = ch->events; e; e = e_next ) { e_next = e->next_local; if( !IS_SET( e->flags ^ event_table[e->type].flags, EVENT_DEATH_REMOVE ) ) continue; event_remove_global( e ); event_remove_local( &ch->events, e ); free_event( e ); } if( IS_NPC( victim ) ) { if( !IS_NPC( ch ) && ch->pcdata->quest->target == victim->pIndexData ) { send_to_char( "&BYou have almost completed your QUEST!&n\n\r", ch ); send_to_char( "Better hurry and finish it now.\n\r", ch ); ch->pcdata->quest->type = QUEST_KILL_COMPLETE; } SysInfo->mob_deaths++; victim->pIndexData->killed++; kill_table[URANGE( 0, victim->level, ( LEVEL_HERO * 2 ) - 1 )].killed++; extract_char( victim, TRUE ); return; } extract_char( victim, FALSE ); victim->position = POS_RESTING; clean_char( victim ); victim->body_parts &= race_table[victim->race].body_parts; if( safe_kill ) { int i; victim->hit = victim->max_hit; for( i = 0; i < MAGIC_MAX; ++i ) victim->mana[i] = victim->max_mana[i]; victim->move = victim->max_move; xREMOVE_BIT( victim->affected_by, AFF_BLEEDING ); xREMOVE_BIT( victim->act, PLR_BATTLE ); victim->pcdata->died--; victim->body_parts = race_table[victim->race].body_parts; } if( !str_cmp( race_table[victim->race].name, "Vampire" ) ) { victim->pcdata->condition[COND_FULL] = 10; victim->pcdata->condition[COND_THIRST] = 10; } save_char_obj( victim ); return; } void group_gain( CHAR_DATA *ch, CHAR_DATA *victim ) { CHAR_DATA *gch; char buf[MAX_STRING_LENGTH]; int members, member_levels, highest_member = 0; int xp; /* * Monsters don't get kill xp's or alignment changes. * Dying of mortal wounds or poison doesn't give xp to anyone! */ if( IS_NPC( ch ) || victim == ch ) return; members = 0; member_levels = 0; for( gch = ch->in_room->people; gch; gch = gch->next_in_room ) { if( is_same_group( gch, ch ) ) { if( gch->level > highest_member ) highest_member = gch->level; members++; member_levels += gch->level; } } if( members == 0 || member_levels == 0 ) { bug( "Group_gain: members-%d levels-%d", members, member_levels ); members = 1; member_levels = ch->level; } members--; for( gch = ch->in_room->people; gch; gch = gch->next_in_room ) { OBJ_DATA *obj; OBJ_DATA *obj_next; if( !is_same_group( gch, ch ) ) continue; xp = xp_compute( gch, victim, -1 ) * gch->level / member_levels; xp += xp * members / 10; /* So high levels can't drag littlies around, xp drops off. * Once the highest character in the group is 18 levels more * than gch, gch gets no experience. --Symp */ if( gch->level + 5 < highest_member ) xp = 2 * xp - power( xp, 4, highest_member - gch->level ); if( xp < 0 ) xp = 0; if( xp >= 2500 ) sprintf( buf, "&YYou receive %d experience points.&n\n\r", xp / 100 ); else sprintf( buf, "&YYou receive %d.%2.2d experience points.&n\n\r", xp / 100, xp % 100 ); send_to_char( buf, gch ); gain_exp( gch, xp ); for( obj = gch->carrying; obj; obj = obj_next ) { obj_next = obj->next_content; if( obj->deleted || obj->wear_loc == WEAR_NONE || number_bits( 1 ) ) continue; if( ( IS_OBJ_STAT( obj, ITEM_ANTI_EVIL ) && IS_EVIL( gch ) ) || ( IS_OBJ_STAT( obj, ITEM_ANTI_GOOD ) && IS_GOOD( gch ) ) || ( IS_OBJ_STAT( obj, ITEM_ANTI_NEUTRAL ) && IS_NEUTRAL( gch ) ) ) { act( "&m$n is zapped by $p.&n", gch, obj, NULL, TO_ALL ); obj_from_char( obj ); obj_to_room( obj, gch->in_room ); } } } return; } /* * Compute xp for a kill. * Also adjust alignment of OUTLAW. * Edit this function to change xp computations. */ int xp_compute( CHAR_DATA *gch, CHAR_DATA *victim, int dam ) { float bonus; int xp; int extra; int level; int number; /* * alignment change if this is a k"ng blow * this has grown a little to include religious follow-on * in the event of significant alignment change. */ if( dam < 0 ) { extra = gch->alignment - victim->alignment * 3 / 2; extra += race_table[gch->race].natural_align / 10; bonus = log( (double)( abs( extra ) + 1 ) ); extra = bonus * bonus * ( ( extra < 0 ) ? -1 : 1 ); gch->alignment = URANGE( -1000, gch->alignment + extra, 1000 ); if( gch->pcdata->clan && gch->pcdata->clan->clan_type == CLAN_ORDER && gch->pcdata->clan_rank != RANK_EXILED && victim->alignment >= gch->pcdata->religion->align_min && victim->alignment <= gch->pcdata->religion->align_max ) add_karma( gch->pcdata->clan, -3 ); if( !IS_NPC( gch ) && gch->pcdata->religion && ( !xIS_SET( gch->act, PLR_OUTLAW ) || number_bits( 1 ) ) && ( gch->alignment < gch->pcdata->religion->align_min || gch->alignment > gch->pcdata->religion->align_max ) ) { send_to_char( "&ROh NO!&r You have angered your god!\n\r", gch ); act( "$n has angered his god!.", gch, NULL, NULL, TO_ROOM ); act( "A large bolt of lightning strikes $n from the heavens.", gch, NULL, NULL, TO_ROOM ); act( "A large bolt of lightning strikes you from the heavens.", gch, NULL, NULL, TO_CHAR ); spell_power_6( skill_lookup( "lightning bolt" ), gch->level + 5, gch, gch ); xSET_BIT( gch->act, PLR_OUTLAW ); if( gch->pcdata->clan && gch->pcdata->clan->clan_type == CLAN_ORDER && gch->pcdata->clan_rank != RANK_EXILED ) { CLAN_DATA *order = gch->pcdata->clan; log_string( "Order %s: forcibly exiled from %s", gch->name, order->name ); wiznetf( gch, WIZ_CLAN, 0, "%s has been forced from the order of %s.", gch->name, order->name ); talk_channel( gch, "&MINFO: $n has angered $g and has been exiled!", CHANNEL_INFO, "INFO" ); remove_from_clan( gch ); /* easier this way */ gch->pcdata->clan = order; gch->pcdata->clan_rank = RANK_EXILED; order->members++; add_karma( order, -100 ); } else if( !xIS_SET( gch->act, PLR_OUTLAW ) ) talk_channel( gch, "&MINFO: $n has angered $g and is now an outlaw", CHANNEL_INFO, "INFO" ); } } bonus = 1.0; xp = 4000 - URANGE( -10, gch->level - victim->level, 6 ) * 950; xp = xp - ( xp / 2 ) + ( xp * 500 / get_curr_resil( victim ) ); if( gch->level < 10 ) bonus += (float)1 / 2; if( xIS_SET( victim->affected_by, AFF_SANCTUARY ) || IS_SET( race_table[victim->race].race_abilities, RACE_SANCT ) ) bonus += (float)1 / 2; else if( IS_NPC( victim ) && xIS_SET( victim->pIndexData->affected_by, AFF_SANCTUARY ) ) bonus += (float)1 / 3; if( IS_AFFECTED( victim, AFF_ATTACKSHIELD ) ) bonus += (float)2 / 5; if( get_eq_char( victim, WEAR_WIELD_R ) ) bonus += (float)1 / 4; if( get_eq_char( victim, WEAR_WIELD_DOUBLE ) ) bonus += (float)1 / 3; if( get_eq_char( victim, WEAR_WIELD_L ) ) bonus += (float)1 / 5; if( victim->race == gch->race ) bonus -= (float)1 / 8; if( IS_NPC( victim ) ) { if( victim->class >= 0 ) { if( victim->class < AVAIL_CLASS ) bonus += (float)1/3; else bonus += (float)2/5; } if( xIS_SET( victim->pIndexData->act, ACT_BURIED ) ) bonus += (float)1 / 7; if( xIS_SET( victim->pIndexData->affected_by, AFF_WARP_FLESH ) ) bonus -= (float)1 / 3; if( xIS_SET( victim->pIndexData->affected_by, AFF_MIND_MIST ) ) bonus -= (float)1 / 4; if( xIS_SET( victim->act, ACT_AGGRESSIVE ) ) bonus += (float)1 / 20; if( victim->pIndexData->pShop != 0 ) bonus -= (float)1 / 4; if( victim->spec_fun != 0 && spec_table[victim->spec_fun].prcnt_xp ) bonus += (float)spec_table[victim->spec_fun].prcnt_xp / 100; } else { if( !xIS_SET( victim->act, PLR_BATTLE ) ) bonus = 0.0; else bonus *= (float)2; } if( dam >= 0 ) { dam = UMIN( dam, victim->hit ); bonus *= (float)dam / victim->max_hit; } if( gch->class == CLASS_BUILDER ) bonus /= 5; xp = (int)( xp * bonus ); /* * Adjust for popularity of target: * -1/8 for each target over 'par' ( down to - 50% ) * +1/8 for each target under 'par' ( up to + 25% ) */ if( IS_NPC( victim ) ) { level = URANGE( 0, victim->level, MAX_LEVEL - 1 ); number = UMAX( 1, kill_table[level].number ); extra = victim->pIndexData->killed - kill_table[level].killed / number; xp -= xp * URANGE( -2, extra, 4 ) / 8; } xp = number_range( xp * 3 / 4, xp * 5 / 4 ); xp = UMAX( 0, xp ); if( !IS_NPC( victim ) ) xp = UMIN( xp, 50000 ); return xp; } char *xp_str( char *outstr, const int xp ) { if( xp < 0 ) return strcpy( outstr, "" ); if( xp >= 2500 ) sprintf( outstr, " &g(&c%d&gxp)&n", xp / 100 ); else sprintf( outstr, " &g(&c%d.%2.2d&gxp)&n", xp / 100, xp % 100 ); return outstr; } char *dam_amount( char *outstr, CHAR_DATA *ch, const int dam ) { if( !IS_IMMORTAL( ch ) ) return strcpy( outstr, "" ); sprintf( outstr, " &r<%d>&n", dam ); return outstr; } void dam_message( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt, int wpn ) { static const char *const him_her[] = { "it", "him", "her" }; const char *vp; const char *attack; char *point; char buf[MAX_STRING_LENGTH]; char buf1[256]; char buf2[256]; char buf3[256]; char buf4[256]; char buf5[256]; char punct; CHAR_DATA *vch; int xp; int members, member_levels; /* All the bulk is in the dam table now. */ for( xp = 0; dam_table[xp].max_dam >= 0; xp++ ) { if( dam_table[xp].max_dam >= dam ) break; } if( !dam_table[xp].mesg ) { bug( "Unreal bug, no damage message for %d dam.", dam ); return; } if( dt >= 0 && dt <= MAX_SKILL && IS_SET( skill_table[dt].skill_type, SKILL_TYPE_FIRE ) ) vp = dam_table[xp].fire_mesg; else if( dt >= 0 && dt <= MAX_SKILL && IS_SET( skill_table[dt].skill_type, SKILL_TYPE_ICE ) ) vp = dam_table[xp].ice_mesg; else vp = dam_table[xp].mesg; punct = ( dam <= UMAX( ch->level * 3 / 2, 25 ) ) ? '.' : '!'; if( dt == TYPE_HIT ) { if( ch->race > MAX_RACE ) { bug( "Dam_message: %d invalid race", ch->race ); ch->race = 0; } attack = race_table[ch->race].dmg_message; } else { if( dt >= 0 && dt < MAX_SKILL ) attack = skill_table[dt].noun_damage; else if( dt >= TYPE_HIT ) { xp = dt - TYPE_HIT; attack = flag_string( weapon_flags, &xp ); } else { bug( "Dam_message: bad dt %d caused by %s.", dt, ch->name ); dt = TYPE_HIT; attack = "hit"; } } sprintf( buf1, "Your %s %s %%s&n%c%%s%%s\n\r", attack, vp, punct ); sprintf( buf2, "%%s&n's %s %s you%c%%s\n\r", attack, vp, punct ); sprintf( buf3, "%%s&n's %s %s %%s&n%c%%s%%s\n\r", attack, vp, punct ); sprintf( buf4, "Your %s %s you%c%%s\n\r", attack, vp, punct ); sprintf( buf5, "%%s&n's %s %s %%s%c%%s%%s\n\r", attack, vp, punct ); members = 0; member_levels = 0; if( ch != victim ) { for( vch = ch->in_room->people; vch; vch = vch->next_in_room ) { if( !IS_AWAKE( vch ) ) continue; if( is_same_group( vch, ch ) ) { members++; member_levels += vch->level; } } if( members == 0 || member_levels == 0 ) { bug( "dam_message: members-%d levels-%d", members, member_levels ); members = 1; member_levels = ch->level; } } for( vch = ch->in_room->people; vch; vch = vch->next_in_room ) { char tmpxp[25], tmpdam[25]; if( !IS_AWAKE( vch ) ) continue; xp = -1; if( ch != victim ) { if( !IS_NPC( vch ) && is_same_group( vch, ch ) ) { xp = xp_compute( vch, victim, dam ) * vch->level / member_levels; xp += xp * members / 10; if( vch == ch ) xp += xp / 12; } if( xp > 0 ) { gain_exp( vch, xp ); vch->tot_xp += xp; } } if( ch->fighting == victim && dt != gsn_throw_weapon ) { if( !vch->desc || ( ( vch == ch || vch == victim ) && xIS_SET( CH( vch->desc )->act, PLR_BATTLESELF ) ) || ( ( vch != ch && vch != victim ) && xIS_SET( CH( vch->desc )->act, PLR_BATTLEOTHER ) ) ) continue; } if( vch == ch ) { if( ch == victim ) sprintf( buf, buf4, dam_amount( tmpdam, vch, dam ) ); else sprintf( buf, buf1, PERS( victim, vch ), xp_str( tmpxp, xp ), dam_amount( tmpdam, vch, dam ) ); } else if( vch == victim ) sprintf( buf, buf2, PERS( ch, vch ), dam_amount( tmpdam, vch, dam ) ); else if( ch == victim ) sprintf( buf, buf5, PERS( ch, vch ), him_her[URANGE( 0, ch->sex, 2 )], xp_str( tmpxp, xp ), dam_amount( tmpdam, vch, dam ) ); else sprintf( buf, buf3, PERS( ch, vch ), PERS( victim, vch ), xp_str( tmpxp, xp ), dam_amount( tmpdam, vch, dam ) ); point = &buf[0]; while( *point == '&' ) point += 2; *point = UPPER( *point ); send_to_char( buf, vch ); } if( ch->fighting == victim && dt != gsn_throw_weapon ) { if( dam > 0 ) { ch->num_hits++; ch->tot_dam += dam; } else ch->num_hits = UMAX( ch->num_hits, 0 ); } return; } /* * Disarm a creature. * Caller must check for successful attack. */ bool disarm( CHAR_DATA *ch, CHAR_DATA *victim ) { OBJ_DATA *obj; if( ( get_size( ch ) - get_size( victim ) ) < -20 + number_range( -40, 40 ) ) return FALSE; if( !( obj = get_eq_char( victim, WEAR_WIELD_R ) ) ) if( !( obj = get_eq_char( victim, WEAR_WIELD_L ) ) ) if( !( obj = get_eq_char( victim, WEAR_WIELD_DOUBLE ) ) ) return FALSE; if( IS_SET( obj->extra_flags, ITEM_NOREMOVE ) || IS_SET( obj->extra_flags, ITEM_NODROP ) ) return FALSE; if( !get_eq_char( ch, WEAR_WIELD_R ) && !get_eq_char( ch, WEAR_WIELD_L ) && !get_eq_char( ch, WEAR_WIELD_DOUBLE ) && ch->class != CLASS_MARTIAL_ARTIST && get_first_class( ch ) != CLASS_MARTIAL_ARTIST && get_second_class( ch ) != CLASS_MARTIAL_ARTIST && number_bits( 3 ) != 0 ) return FALSE; act( "You disarm $N!", ch, NULL, victim, TO_CHAR ); act( "$n DISARMS you!", ch, NULL, victim, TO_VICT ); act( "$n DISARMS $N!", ch, NULL, victim, TO_NOTVICT ); obj_from_char( obj ); if( IS_NPC( victim ) ) { switch( number_bits( 2 ) ) { case 0: obj_to_room( obj, ch->in_room ); break; case 1: case 2: act( "$n retrieves the weapon.", victim, NULL, NULL, TO_ROOM ); obj_to_char( obj, victim ); break; default: act( "$n retrieves the weapon.", victim, NULL, NULL, TO_ROOM ); obj_to_char( obj, victim ); do_wear( victim, obj->name ); break; } } else obj_to_room( obj, victim->in_room ); return TRUE; } /* * Trip a creature. * Caller must check for successful attack. */ void trip( CHAR_DATA *ch, CHAR_DATA *victim ) { if( IS_SET( victim->body_parts, BODY_PART_WINGS ) || xIS_SET( ch->affected_by, AFF_FLYING ) ) return; if( victim->wait == 0 ) { act( "$n trips $N and $N goes down!", ch, NULL, victim, TO_ALL ); WAIT_STATE( ch, 2 * PULSE_VIOLENCE ); WAIT_STATE( victim, 2 * PULSE_VIOLENCE ); victim->position = POS_RESTING; } return; } /* * At the moment this is for all those trapped in a snare or web (AFF_HOLD) * they will struggle to free themselves and in the process hurt themselves, * an NPC who breaks free will flee if it is fighting. * -- Symposium */ void web_update( CHAR_DATA *ch ) { int dam; if( !IS_AFFECTED( ch, AFF_HOLD ) ) return; if( IS_AFFECTED( ch, AFF_PASS_DOOR ) || IS_AFFECTED( ch, AFF_GHOUL ) || power( number_percent( ), 5, 20 - get_curr_str( ch ) ) < 9 ) { affect_strip( ch, gsn_snare ); affect_strip( ch, gsn_web ); affect_strip( ch, gsn_strangle ); xREMOVE_BIT( ch->affected_by, AFF_HOLD ); if( IS_AFFECTED( ch, AFF_PASS_DOOR ) || IS_AFFECTED( ch, AFF_GHOUL ) ) act( "$o bonds pass straight through $m as $e move$%.", ch, NULL, NULL, TO_ALL ); else act( "$n break$% free of the entanglement.", ch, NULL, NULL, TO_ALL ); if( IS_NPC( ch ) && xIS_SET( ch->act, ACT_WIMPY ) && ch->fighting && ch->hit < ch->max_hit / 4 ) do_flee( ch, "" ); } else { dam = 5 + ( ch->level / 2 ) + ( ch->hit / 35 ); dam = number_range( dam / 3, dam * 2 / 3 ); act( "$n struggle$% in the web.", ch, NULL, NULL, TO_ALL ); damage( ch, ch, dam, gsn_web, WEAR_NONE ); create_char_event( ch, evn_web_struggle, number_range( PULSE_PER_SECOND * 18, PULSE_PER_SECOND * 27 ) ); } return; }