/*************************************************************************** * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, * * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. * * * * Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael * * Chastain, Michael Quan, and Mitchell Tse. * * * * Envy Diku Mud improvements copyright (C) 1994 by Michael Quan, David * * Love, Guilherme 'Willie' Arnold, and Mitchell Tse. * * * * EnvyMud 2.0 improvements copyright (C) 1995 by Michael Quan and * * Mitchell Tse. * * * * EnvyMud 2.2 improvements copyright (C) 1996, 1997 by Michael Quan. * * * * In order to use any part of this Envy Diku Mud, you must comply with * * the original Diku license in 'license.doc', the Merc license in * * 'license.txt', as well as the Envy license in 'license.nvy'. * * In particular, you may not remove either of these copyright notices. * * * * 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. * ***************************************************************************/ #if defined( macintosh ) #include <types.h> #else #include <sys/types.h> #endif #include <stdlib.h> #include <stdio.h> #include <string.h> #include <time.h> #include "merc.h" /* * Local functions. */ bool check_dodge args( ( CHAR_DATA *ch, CHAR_DATA *victim ) ); bool check_parry args( ( CHAR_DATA *ch, CHAR_DATA *victim ) ); bool check_shield_block args( ( CHAR_DATA *ch, CHAR_DATA *victim ) ); void dam_message args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt, int wpn, bool immune ) ); void spl_dam_message args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt, bool immune ) ); void death_cry args( ( CHAR_DATA *ch ) ); void group_gain args( ( CHAR_DATA *ch, CHAR_DATA *victim ) ); int xp_compute args( ( CHAR_DATA *gch, 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 ) ); void disarm args( ( CHAR_DATA *ch, CHAR_DATA *victim ) ); void trip args( ( CHAR_DATA *ch, CHAR_DATA *victim ) ); bool check_race_special args( ( CHAR_DATA *ch ) ); void use_magical_item args( ( CHAR_DATA *ch ) ); /* * Control the fights going on. * Called periodically by update_handler. * Slightly less efficient than Merc 2.2. Takes 10% of * total CPU time. */ void violence_update( void ) { CHAR_DATA *ch; CHAR_DATA *victim; CHAR_DATA *rch; bool mobfighting; for ( ch = char_list; ch; ch = ch->next ) { if ( !ch->in_room || ch->deleted ) continue; if ( ( victim = ch->fighting ) ) { mprog_hitprcnt_trigger( ch, victim ); mprog_fight_trigger( ch, victim ); if ( IS_AWAKE( ch ) && ch->in_room == victim->in_room && !victim->deleted ) { /* Ok here we test for switch if victim is charmed */ if ( IS_AFFECTED( victim, AFF_CHARM ) && victim->master && victim->in_room == victim->master->in_room && ch != victim && number_percent( ) > 40 ) { stop_fighting( ch, FALSE ); multi_hit( ch, victim->master, TYPE_UNDEFINED ); } else { multi_hit( ch, victim, TYPE_UNDEFINED ); } } else { stop_fighting( ch, FALSE ); } continue; } if ( IS_AFFECTED( ch, AFF_BLIND ) || ( IS_NPC( ch ) && ch->pIndexData->pShop ) || ( IS_NPC( ch ) && ch->pIndexData->pGame ) ) continue; /* * Ok. So ch is not fighting anyone. * Is there a fight going on? */ mobfighting = FALSE; for ( rch = ch->in_room->people; rch; rch = rch->next_in_room ) { if ( rch->deleted || !IS_AWAKE( rch ) || !( victim = rch->fighting ) ) continue; if ( !IS_NPC( ch ) && ( !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 ) continue; /* * 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 ) { victim = vch; number++; } } if ( ( rch->pIndexData != ch->pIndexData && number_bits( 3 ) != 0 ) || ( IS_GOOD( ch ) && IS_GOOD( victim ) ) || abs( victim->level - ch->level ) > 3 ) continue; } multi_hit( ch, victim, TYPE_UNDEFINED ); } return; } /* * Do one group of attacks. */ void multi_hit( CHAR_DATA *ch, CHAR_DATA *victim, int dt ) { int chance; /* * Set the fighting fields now. */ if ( victim->position > POS_STUNNED ) { if ( !victim->fighting ) set_fighting( victim, ch ); // Can't have bashed/prone people just automatically be standing. if( victim->position == POS_STANDING ) victim->position = POS_FIGHTING; if ( !ch->fighting ) set_fighting( ch, victim ); } // Okay, so what is check_race_special? if ( !IS_NPC( ch ) && ch->fighting ) { if ( check_race_special( ch ) ) return; } // Everyone gets at least one swing in battle. one_hit( ch, victim, dt, WEAR_HAND ); // And if they're initiating combat, backstabbing, or circling it's their only swing this round. // Except for the case of thieves/assassins doing a double backstab which hasn't been coded yet. if ( ch->fighting != victim || dt == gsn_backstab || dt == gsn_circle ) return; // For NPCs we assume they have max skill value for their level. // When checking combat skills we only practice them on a successful // check in order to make them go up slower. If they go up too slow // we can always practice them before they check - Veygoth // Check for second attack if( IS_NPC( ch )) { if( ch->level < skills_table[gsn_second_attack].skill_level[ch->class] ) chance = 0; else chance = ((ch->level - skills_table[gsn_second_attack].skill_level[ch->class] ) * 2 + 25) * 3 / 4; } else { chance = ch->pcdata->skl_lrn[gsn_second_attack] * 3 / 4; } if( chance > 95 ) chance = 95; if ( number_percent( ) < chance ) { skill_practice( ch, gsn_second_attack ); one_hit( ch, victim, dt, WEAR_HAND ); if ( ch->fighting != victim ) return; } // Check for third attack if( IS_NPC( ch )) { if( ch->level < skills_table[gsn_third_attack].skill_level[ch->class] ) chance = 0; else chance = ((ch->level - skills_table[gsn_third_attack].skill_level[ch->class] ) * 2 + 25 ) * 3 / 8; } else { chance = ch->pcdata->skl_lrn[gsn_third_attack] * 3 / 8; } if( chance > 95 ) chance = 95; if ( number_percent( ) < chance ) { skill_practice( ch, gsn_third_attack ); one_hit( ch, victim, dt, WEAR_HAND ); if ( ch->fighting != victim ) return; } // Check for fourth attack if( IS_NPC( ch )) { if( ch->level < skills_table[gsn_fourth_attack].skill_level[ch->class] ) chance = 0; else chance = ((ch->level - skills_table[gsn_fourth_attack].skill_level[ch->class] ) * 2 + 25 ) / 4; } else { chance = ch->pcdata->skl_lrn[gsn_fourth_attack] / 4; } if( chance > 95 ) chance = 95; if ( number_percent( ) < chance ) { skill_practice( ch, gsn_fourth_attack ); one_hit( ch, victim, dt, WEAR_HAND ); if ( ch->fighting != victim ) return; } // Check for dual wield. May want to allow a second swing when dual wielding. // We'll wait and see what combat looks like before we decide - Veygoth if ( get_eq_char( ch, WEAR_HAND_2 ) ) { skill_practice( ch, gsn_dual ); chance = IS_NPC( ch ) ? ch->level : ch->pcdata->skl_lrn[gsn_dual] * 2 / 3; if ( number_percent( ) < chance ) one_hit( ch, victim, dt, WEAR_HAND_2 ); } return; } /* * Hit one guy once. * * Hitroll is now done on a 200-sided die rather than a 20-sided die * This allows for more dynamic modifiers to hitroll. * i.e. a couple extra points of strength and whatnot _may_ make the * difference between a hit and a miss rather than incrementing something * every 10-20 points of an ability we can modify it every 1-2 points. * - Veygoth * */ void one_hit( CHAR_DATA *ch, CHAR_DATA *victim, int dt, int wpn ) { OBJ_DATA *wield; char buf [ MAX_STRING_LENGTH ]; int victim_ac; int thac0; int thac0_00; int thac0_47; int dam; int chance; int diceroll; int wpn_gsn; int dam_type; /* * Can't beat a dead char! * Guard against weird room-leavings. */ if ( victim->position == POS_DEAD || ch->in_room != victim->in_room ) { sprintf( buf, "one_hit: ch %s not with victim %s, or victim POS_DEAD", ch->name, victim->name ); bug( buf, 0 ); return; } /* * Figure out the type of damage message. */ wield = get_eq_char( ch, wpn ); if ( dt == TYPE_UNDEFINED ) { dt = TYPE_HIT; if ( wield && wield->item_type == TYPE_WEAPON ) dt += wield->value[3]; } /* * Weapon proficiencies. */ wpn_gsn = gsn_hit; dam_type = DAM_BASH; if ( wield && wield->item_type == TYPE_WEAPON ) { if ( wield->value[3] >= 0 && wield->value[3] < MAX_ATTACK ) { wpn_gsn = (*attack_table[wield->value[3]].wpn_gsn); dam_type = attack_table[wield->value[3]].dam_type; } else { sprintf( buf, "one_hit: bad weapon dt %d caused by %s.", dt, ch->name ); bug( buf, 0 ); wield->value[3] = 0; } } /* * Calculate to-hit-armor-class-0 versus armor. */ thac0_00 = class_table[ch->class]->thac0_00; thac0_47 = class_table[ch->class]->thac0_47; /* Weapon-specific hitroll and damroll */ thac0 = interpolate( ch->level, thac0_00, thac0_47 ) - get_hitroll( ch, wpn ); victim_ac = UMAX( -150, get_ac( victim ) ); // Added blindfighting skill - Veygoth if ( !can_see( ch, victim ) ) { if( ch->level >= skills_table[gsn_blindfighting].skill_level[ch->class] ) { if( IS_NPC( ch )) { chance = ((ch->level * 3) / 2 + 15); } else { chance = ch->pcdata->skl_lrn[gsn_blindfighting]; skill_practice( ch, gsn_blindfighting ); } if( number_percent() > chance ) victim_ac -= 40; else victim_ac -= 5; } else { victim_ac -= 40; } } /* Weapon proficiencies */ if ( wield && wield->item_type == TYPE_WEAPON && ( IS_NPC( ch ) ? UMIN( 30, 2 * ch->level ) : ch->pcdata->skl_lrn[wpn_gsn] ) > ( IS_NPC( victim ) ? UMIN( 30, 2 * victim->level ) : victim->pcdata->skl_lrn[wpn_gsn] ) ) { skill_practice( ch, wpn_gsn ); victim_ac += 20; } /* * The moment of excitement! */ diceroll = number_range( 0, 199 ); // Give them a small bonus if they can make a successful luck check. if( number_percent() <= get_curr_luk( ch ) ) diceroll += 5; if ( diceroll == 0 || ( diceroll <= 196 && diceroll < thac0 - victim_ac ) ) { /* Miss. */ damage( ch, victim, 0, dt, wpn, dam_type ); tail_chain( ); return; } /* * Hit. * Calc damage. * * NPCs are more badass barehanded than players. If they weren't * the game would be too damned easy since mobs almost never have * weapons. */ if ( IS_NPC( ch ) ) { dam = number_range( ch->level / 2, ch->level * 3 / 2 ); if ( wield ) dam += dice( wield->value[1], wield->value[2] ); else if( ch->level >= skills_table[gsn_unarmed].skill_level[ch->class] ) { chance = ((ch->level * 3) / 2 + 15); if( number_percent() < chance ) dam += number_range( 1, 3 ); } } else { if ( wield ) dam = dice( wield->value[1], wield->value[2] ); else { dam = number_range( 1, 2 ) + race_table[ ch->race ].size / 2; if( ch->level >= skills_table[gsn_unarmed].skill_level[ch->class] ) { skill_practice( ch, gsn_unarmed ); chance = ch->pcdata->skl_lrn[gsn_unarmed]; if( number_percent() < chance ) dam += number_range( 1, 3 ); } } if ( wield && dam > 1000 ) { sprintf( buf, "One_hit dam range > 1000 from %d to %d", wield->value[1], wield->value[2] ); bug( buf, 0 ); } } /* * Bonuses. */ dam += get_damroll( ch, wpn ); // Ya know we might want to check whether they are resistant to poison // before we do this math. if ( wield && IS_OBJ_STAT( wield, ITEM_POISONED ) ) dam += dam / 4; /* Weapon proficiencies */ /* Up to 50% increase based on weapon skill */ if ( wield && !IS_NPC( ch ) && ch->pcdata->skl_lrn[wpn_gsn] > 0 ) dam += dam * ch->pcdata->skl_lrn[wpn_gsn] / 180; /* Up to 33% for offense skill */ /* This means someone that has mastered a weapon and offense automatically does double damage in combat */ if ( !IS_NPC( ch ) && ch->pcdata->skl_lrn[gsn_enhanced_damage] > 0 ) { dam += dam * ch->pcdata->skl_lrn[gsn_enhanced_damage] / 270; skill_practice( ch, gsn_enhanced_damage ); } /* Bad idea to get caught napping in a fight */ if ( !IS_AWAKE( victim ) ) dam *= 2; /* Ummm.... what the hell does this math out to!? */ if ( dt == gsn_backstab ) dam *= 2 + UMIN( ( ch->level / 8 ), 4 ); else if ( dt == gsn_circle ) /* 150% to 200% at lev. 50 */ dam += dam / 2 + ( dam * ch->level ) / 100; if ( dam <= 0 ) dam = 1; if ( wield && wield->item_type == TYPE_WEAPON && attack_table[wield->value[3]].hit_fun && (*attack_table[wield->value[3]].hit_fun)(ch, victim, diceroll, dam) ) return; damage( ch, victim, dam, dt, wpn, dam_type ); tail_chain( ); return; } /* * Inflict damage from a hit. */ void damage( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt, int wpn, int dam_type ) { bool immune; char buf[MAX_STRING_LENGTH]; if ( victim->position == POS_DEAD ) return; /* * Stop up any residual loopholes. */ if ( dam > 1000 ) { char buf [ MAX_STRING_LENGTH ]; if ( IS_NPC( ch ) && ch->desc ) sprintf( buf, "Damage: %d from %s by %s: > 1000 points with %d dt!", dam, ch->name, ch->desc->original->name, dt ); else sprintf( buf, "Damage: %d from %s: > 1000 points with %d dt!", dam, IS_NPC( ch ) ? ch->short_descr : ch->name, dt ); bug( buf, 0 ); dam = 1000; } // Remove memorization and meditation bits - Veygoth if( !IS_NPC( victim ) && IS_SET( victim->act, PLR_MEMORIZING )) { act( "$N&n abandons $S studies.", ch, NULL, victim, TO_ROOM ); act( "$N&n abandons $S studies.", ch, NULL, victim, TO_CHAR ); act( "You abandon your studies.", ch, NULL, victim, TO_VICT ); REMOVE_BIT( victim->act, PLR_MEMORIZING ); } if( !IS_NPC( victim ) && IS_SET( victim->act, PLR_MEDITATING )) { act( "$N&n is disrupted from meditation.", ch, NULL, victim, TO_ROOM ); act( "$N&n is disrupted from meditation.", ch, NULL, victim, TO_CHAR ); act( "Your meditation is disrupted.", ch, NULL, victim, TO_VICT ); REMOVE_BIT( victim->act, PLR_MEDITATING ); } immune = FALSE; if ( victim != ch ) { /* * Certain attacks are forbidden. * Most other attacks are returned. */ victim = check_guarding( ch, victim ); if ( is_safe( ch, victim ) ) return; check_killer( ch, victim ); if ( victim->position > POS_STUNNED ) { if ( !victim->fighting ) set_fighting( victim, ch ); // Can't have prone people automatically stand if( victim->position == POS_STANDING ) 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_AFFECTED( ch, AFF_INVISIBLE ) ) { affect_strip( ch, 0, spl_invis ); affect_strip( ch, 0, spl_mass_invis ); REMOVE_AFF_BIT( ch, AFF_INVISIBLE ); act( "$n&n snaps into visibility.", ch, NULL, NULL, TO_ROOM ); } /* * Hunting stuff... */ if ( dam && IS_NPC( victim ) ) { if ( !IS_SET( victim->act, ACT_SENTINEL ) ) { if ( victim->hunting ) { if ( victim->hunting->who != ch ) { free_string( victim->hunting->name ); victim->hunting->name = str_dup( ch->name ); victim->hunting->who = ch; } } else start_hunting( victim, ch ); } if ( victim->hating ) { if ( victim->hating->who != ch ) { free_string( victim->hating->name ); victim->hating->name = str_dup( ch->name ); victim->hating->who = ch; } } else start_hating( victim, ch ); } /* * Damage modifiers. */ if ( IS_AFFECTED( victim, AFF_SANCTUARY ) || IS_SET( race_table[ victim->race ].race_abilities, RACE_SANCT ) ) dam /= 2; if ( ( IS_AFFECTED( victim, AFF_PROTECT_EVIL ) || IS_SET( race_table[ victim->race ].race_abilities, RACE_PROTECTION ) ) && IS_EVIL( ch ) ) dam -= dam / 4; else if ( ( IS_AFFECTED( victim, AFF_PROTECT_GOOD ) || IS_SET( race_table[ victim->race ].race_abilities, RACE_PROTECTION ) ) && IS_GOOD( ch ) ) dam -= dam / 4; if ( dam < 0 ) dam = 0; /* * Check for disarm, trip, parry, dodge and shield block. */ if ( dt >= TYPE_HIT || dt == gsn_kick ) { // Trip and disarm removed because those should be handled // by each individual mob's special function. if ( IS_NPC( ch ) && IS_SET( race_table[ ch->race ].race_abilities, RACE_WEAPON_WIELD ) && number_percent( ) < UMIN( 25, UMAX( 10, ch->level ) ) && !IS_NPC( victim ) ) use_magical_item( ch ); if ( check_parry( ch, victim ) && dam > 0 ) return; if ( check_shield_block( ch, victim ) && dam > 0 ) return; if ( check_dodge( ch, victim ) && dam > 0 ) return; } } switch( check_ris( victim, dam_type ) ) { case IS_RESISTANT: dam -= dam / 3; break; case IS_IMMUNE: immune = TRUE; dam = 0; break; case IS_SUSCEPTIBLE: dam += dam / 2; break; } /* * 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 ) dam_message( ch, victim, dam, dt, wpn, immune ); /* * 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; /* * Magic shields that retaliate */ if ( ( dam > 1 ) && victim != ch ) { /* For compatibility with old Envy2.2 areas & pfiles */ if ( IS_AFFECTED( victim, AFF_FLAMING ) && !IS_AFFECTED( ch, AFF_FLAMING ) ) spell_damage( victim, ch, dam/2, spl_flame_shield, DAM_FIRE ); if ( is_affected( victim, 0, spl_frost_shield ) && !is_affected( ch, 0, spl_frost_shield ) ) spell_damage( victim, ch, dam/2, spl_frost_shield, DAM_COLD ); if ( is_affected( victim, 0, spl_shock_shield ) && !is_affected( ch, 0, spl_shock_shield ) ) spell_damage( victim, ch, dam/2, spl_shock_shield, DAM_ELECTRICITY ); } if ( is_affected( victim, gsn_berserk, 0 ) && victim->position <= POS_STUNNED ) affect_strip( victim, gsn_berserk, 0 ); if ( dam > 0 && dt > TYPE_HIT && is_wielding_poisoned( ch, wpn ) && !saves_spell( ch->level, victim, DAM_POISON ) ) { AFFECT_DATA af; af.skill = 0; af.spell = spl_poison; af.duration = 1; af.location = APPLY_STR; af.modifier = -2; set_bitvector( &af, AFF_POISON); affect_join( victim, &af ); } update_pos( victim ); 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&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&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&n is stunned, but will probably recover.", victim, NULL, NULL, TO_ROOM ); break; case POS_DEAD: if( victim == ch ) { send_to_char( "You have been slain!\n\r\n\r", victim ); } else { sprintf( buf, "You have been slain by %s!\n\r\n\r", PERS( ch, victim ) ); send_to_char( buf, victim ); } act( "$n&n is DEAD!!", victim, NULL, NULL, TO_ROOM ); break; default: if ( dam > victim->max_hit / 5 ) send_to_char( "That really did HURT!\n\r", victim ); if ( victim->hit < victim->max_hit / 10 ) send_to_char( "You sure are BLEEDING!\n\r", victim ); break; } /* * Sleep spells and extremely wounded folks. */ if ( !IS_AWAKE( victim ) ) /* lets make NPC's not slaughter PC's */ { if ( victim->fighting && victim->fighting->hunting && victim->fighting->hunting->who == victim ) stop_hunting( victim->fighting ); if ( victim->fighting && victim->fighting->hating && victim->fighting->hating->who == victim ) stop_hating( victim->fighting ); if ( victim->fighting && !IS_NPC( victim ) && IS_NPC( ch ) ) stop_fighting( victim, TRUE ); else stop_fighting( victim, FALSE ); } /* * Payoff for killing things. */ if ( victim->position == POS_DEAD ) { group_gain( ch, victim ); if ( !IS_NPC( victim ) ) { int exp = 0; char buf[ MAX_STRING_LENGTH ]; if ( IS_NPC( ch ) ) { victim->pcdata->mdeaths++; if ( is_clan( victim ) ) victim->pcdata->clan->mdeaths++; } else { ch->pcdata->pkills++; victim->pcdata->pdeaths++; if ( is_clan( ch ) && is_clan( victim ) && ch->pcdata->clan != victim->pcdata->clan ) { ch->pcdata->clan->pkills++; victim->pcdata->clan->pdeaths++; } } sprintf( log_buf, "%s killed by %s at %d", victim->name, ( IS_NPC( ch ) ? ch->short_descr : ch->name ), victim->in_room->vnum ); log_string( log_buf ); /* * Dying penalty: * 1/2 way back to previous 2 levels. */ if ( IS_NPC( ch ) ) { if ( victim->exp > EXP_PER_LEVEL * ( victim->level - 1 ) ) gain_exp( victim, ( EXP_PER_LEVEL * ( victim->level - 1 ) - victim->exp ) / 4 ); } else { if ( ch != victim ) { exp = number_range( 250, 750 ); gain_exp( victim, 0 - exp ); send_to_char( buf, victim ); if( victim->exp < 1000) victim->exp = 1000; } } } else { if ( !IS_NPC( ch )) { ch->pcdata->mkills++; if ( is_clan( ch ) ) ch->pcdata->clan->mkills++; } } raw_kill( ch, victim ); // Keep in mind after this point the character is not in the // char_list, not in any room, and is at the menu. Don't do // anything that would cause a segmentation fault - Veygoth if ( is_clan( ch ) && is_clan( victim ) && ch->pcdata->clan != victim->pcdata->clan && ch->pcdata->clan->clan_type != CLAN_NOKILL && victim->pcdata->clan->clan_type != CLAN_NOKILL ) { ch->pcdata->clan->score += 20; } /* * Ok, now we want to remove the deleted flag from the * PC victim. */ if ( !IS_NPC( victim ) ) victim->deleted = FALSE; return; } if ( victim == ch ) return; /* * Wimp out? */ if ( IS_NPC( victim ) && dam > 0 ) { if ( ( IS_SET( victim->act, ACT_WIMPY ) && number_bits( 1 ) == 0 && victim->hit < victim->max_hit / 2 ) || ( IS_AFFECTED( victim, AFF_CHARM ) && victim->master && victim->master->in_room != victim->in_room ) ) { start_fearing( victim, ch ); stop_hunting( victim ); do_flee( victim, "" ); } } if ( !IS_NPC( victim ) && victim->hit > 0 && victim->hit <= victim->wimpy && victim->wait == 0 ) do_flee( victim, "" ); tail_chain( ); return; } /* * Inflict damage from a spell. */ void spell_damage( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt, int dam_type ) { bool immune; char buf[MAX_STRING_LENGTH]; if ( victim->position == POS_DEAD ) return; /* * Stop up any residual loopholes. */ if ( dam > 1000 ) { char buf [ MAX_STRING_LENGTH ]; if ( IS_NPC( ch ) && ch->desc ) sprintf( buf, "Damage: %d from %s by %s: > 1000 points with %d dt!", dam, ch->name, ch->desc->original->name, dt ); else sprintf( buf, "Damage: %d from %s: > 1000 points with %d dt!", dam, IS_NPC( ch ) ? ch->short_descr : ch->name, dt ); bug( buf, 0 ); dam = 1000; } // Remove memorization and meditation bits - Veygoth if( !IS_NPC( victim ) && IS_SET( victim->act, PLR_MEMORIZING )) { act( "$N&n abandons $S studies.", ch, NULL, victim, TO_ROOM ); act( "$N&n abandons $S studies.", ch, NULL, victim, TO_CHAR ); act( "You abandon your studies.", ch, NULL, victim, TO_VICT ); REMOVE_BIT( victim->act, PLR_MEMORIZING ); } if( !IS_NPC( victim ) && IS_SET( victim->act, PLR_MEDITATING )) { act( "$N&n is disrupted from meditation.", ch, NULL, victim, TO_ROOM ); act( "$N&n is disrupted from meditation.", ch, NULL, victim, TO_CHAR ); act( "Your meditation is disrupted.", ch, NULL, victim, TO_VICT ); REMOVE_BIT( victim->act, PLR_MEDITATING ); } immune = FALSE; if ( victim != ch ) { /* * Certain attacks are forbidden. * Most other attacks are returned. */ victim = check_guarding( ch, victim ); if ( is_safe( ch, victim ) ) return; check_killer( ch, victim ); if ( victim->position > POS_STUNNED ) { if ( !victim->fighting ) set_fighting( victim, ch ); // Can't have prone people automaticaly stand. if( victim->position == POS_STANDING ) 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_AFFECTED( ch, AFF_INVISIBLE ) ) { affect_strip( ch, 0, spl_invis ); affect_strip( ch, 0, spl_mass_invis ); REMOVE_AFF_BIT( ch, AFF_INVISIBLE ); act( "$n&n snaps into visibility.", ch, NULL, NULL, TO_ROOM ); } /* * Hunting stuff... */ if ( dam && IS_NPC( victim ) ) { if ( !IS_SET( victim->act, ACT_SENTINEL ) ) { if ( victim->hunting ) { if ( victim->hunting->who != ch ) { free_string( victim->hunting->name ); victim->hunting->name = str_dup( ch->name ); victim->hunting->who = ch; } } else start_hunting( victim, ch ); } if ( victim->hating ) { if ( victim->hating->who != ch ) { free_string( victim->hating->name ); victim->hating->name = str_dup( ch->name ); victim->hating->who = ch; } } else start_hating( victim, ch ); } /* * Damage modifiers. */ if ( IS_AFFECTED( victim, AFF_SANCTUARY ) || IS_SET( race_table[ victim->race ].race_abilities, RACE_SANCT ) ) dam /= 2; if ( ( IS_AFFECTED( victim, AFF_PROTECT_EVIL ) || IS_SET( race_table[ victim->race ].race_abilities, RACE_PROTECTION ) ) && IS_EVIL( ch ) ) dam -= dam / 4; else if ( ( IS_AFFECTED( victim, AFF_PROTECT_GOOD ) || IS_SET( race_table[ victim->race ].race_abilities, RACE_PROTECTION ) ) && IS_GOOD( ch ) ) dam -= dam / 4; if ( dam < 0 ) dam = 0; } switch( check_ris( victim, dam_type ) ) { case IS_RESISTANT: dam -= dam / 3; break; case IS_IMMUNE: immune = TRUE; dam = 0; break; case IS_SUSCEPTIBLE: dam += dam / 2; break; } /* * 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 ) spl_dam_message( ch, victim, dam, dt, immune ); /* * 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 ( is_affected( victim, gsn_berserk, 0 ) && victim->position <= POS_STUNNED ) affect_strip( victim, gsn_berserk, 0 ); update_pos( victim ); 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&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&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&n is stunned, but will probably recover.", victim, NULL, NULL, TO_ROOM ); break; case POS_DEAD: if( victim == ch ) { send_to_char( "You have been slain!\n\r\n\r", victim ); } else { sprintf( buf, "You have been slain by %s!\n\r\n\r", PERS( ch, victim ) ); send_to_char( buf, victim ); } act( "$n&n is DEAD!!", victim, NULL, NULL, TO_ROOM ); break; default: if ( dam > victim->max_hit / 5 ) send_to_char( "That really did HURT!\n\r", victim ); if ( victim->hit < victim->max_hit / 10 ) send_to_char( "You sure are BLEEDING!\n\r", victim ); break; } /* * Sleep spells and extremely wounded folks. */ if ( !IS_AWAKE( victim ) ) /* lets make NPC's not slaughter PC's */ { if ( victim->fighting && victim->fighting->hunting && victim->fighting->hunting->who == victim ) stop_hunting( victim->fighting ); if ( victim->fighting && victim->fighting->hating && victim->fighting->hating->who == victim ) stop_hating( victim->fighting ); if ( victim->fighting && !IS_NPC( victim ) && IS_NPC( ch ) ) stop_fighting( victim, TRUE ); else stop_fighting( victim, FALSE ); } /* * Payoff for killing things. */ if ( victim->position == POS_DEAD ) { group_gain( ch, victim ); if ( !IS_NPC( victim ) ) { int exp = 0; char buf[ MAX_STRING_LENGTH ]; if ( IS_NPC( ch ) ) { victim->pcdata->mdeaths++; if ( is_clan( victim ) ) victim->pcdata->clan->mdeaths++; } else { ch->pcdata->pkills++; victim->pcdata->pdeaths++; if ( is_clan( ch ) && is_clan( victim ) && ch->pcdata->clan != victim->pcdata->clan ) { ch->pcdata->clan->pkills++; victim->pcdata->clan->pdeaths++; } } sprintf( log_buf, "%s killed by %s at %d", victim->name, ( IS_NPC( ch ) ? ch->short_descr : ch->name ), victim->in_room->vnum ); log_string( log_buf ); /* * Dying penalty: * 1/2 way back to previous 2 levels. */ if ( IS_NPC( ch ) ) { if ( victim->exp > EXP_PER_LEVEL * ( victim->level - 1 ) ) gain_exp( victim, ( EXP_PER_LEVEL * ( victim->level - 1 ) - victim->exp ) / 4 ); } else { if ( ch != victim ) { exp = number_range( 250, 750 ); gain_exp( victim, 0 - exp ); send_to_char( buf, victim ); if( victim->exp < 1000) victim->exp = 1000; } } } else { if ( !IS_NPC( ch )) { ch->pcdata->mkills++; if ( is_clan( ch ) ) ch->pcdata->clan->mkills++; } } raw_kill( ch, victim ); // Keep in mind after this point the character is not in the // char_list, not in any room, and is at the menu. Don't do // anything that would cause a segmentation fault - Veygoth if ( is_clan( ch ) && is_clan( victim ) && ch->pcdata->clan != victim->pcdata->clan && ch->pcdata->clan->clan_type != CLAN_NOKILL && victim->pcdata->clan->clan_type != CLAN_NOKILL ) { ch->pcdata->clan->score += 20; } /* * Ok, now we want to remove the deleted flag from the * PC victim. */ if ( !IS_NPC( victim ) ) victim->deleted = FALSE; /* * Remove victims of pk who no longer have exps left */ if ( !IS_NPC( victim ) && IS_SET( victim->act, PLR_DENY ) ) { do_quit( victim, "" ); } return; } if ( victim == ch ) return; /* * Wimp out? */ if ( IS_NPC( victim ) && dam > 0 ) { if ( ( IS_SET( victim->act, ACT_WIMPY ) && number_bits( 1 ) == 0 && victim->hit < victim->max_hit / 2 ) || ( IS_AFFECTED( victim, AFF_CHARM ) && victim->master && victim->master->in_room != victim->in_room ) ) { start_fearing( victim, ch ); stop_hunting( victim ); do_flee( victim, "" ); } } if ( !IS_NPC( victim ) && victim->hit > 0 && victim->hit <= victim->wimpy && victim->wait == 0 ) do_flee( victim, "" ); tail_chain( ); return; } CHAR_DATA *check_guarding( CHAR_DATA *ch, CHAR_DATA *victim ) { CHAR_DATA *guard; for( guard = victim->in_room->people; guard; guard = guard->next_in_room ) { if( !IS_NPC( guard ) && (guard->pcdata->guarding == victim) && (guard != victim) && (guard != ch)) { skill_practice( guard, gsn_guard ); if(number_percent() < guard->pcdata->skl_lrn[gsn_guard] ) { act( "$n&n bravely jumps in front of you!.", guard, NULL, victim, TO_VICT ); act( "$n&n bravely jumps in front of $N&n.", guard, NULL, victim, TO_NOTVICT ); act( "You heriocally jump in front of $N&n's attacker.", guard, NULL, victim, TO_CHAR ); ch->fighting = guard; return guard; } else { act( "$n&n watches helplessly as you are attacked.", guard, NULL, victim, TO_VICT ); act( "$n&n futilely tries to protect $N&n.", guard, NULL, victim, TO_NOTVICT ); act( "You heriocally step aside for $N&n's attacker.", guard, NULL, victim, TO_CHAR ); } } } return victim; } bool is_safe( CHAR_DATA *ch, CHAR_DATA *victim ) { 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( "Your attack passes through $N.", ch, NULL, victim, TO_CHAR ); act( "$n's attack passes through $N.", ch, NULL, victim, TO_NOTVICT ); act( "$n's attack passes through you.", ch, NULL, victim, TO_VICT ); return TRUE; } if ( IS_NPC( ch ) || IS_NPC( victim ) ) return FALSE; if ( IS_SET( victim->act, PLR_KILLER ) || IS_SET( victim->act, PLR_THIEF ) ) return FALSE; // removed restriction allowing only registered players to pkill if ( victim->level >= LEVEL_HERO ) { act( "$N&n is a HERO and is automatically safe.", ch, NULL, victim, TO_CHAR ); return TRUE; } if ( victim->fighting ) return FALSE; if ( IS_SET( victim->in_room->room_flags, ROOM_SAFE ) ) { act( "$N&n is in a safe room.", ch, NULL, victim, TO_CHAR ); return TRUE; } return FALSE; } /* * See if an attack justifies a KILLER flag. */ void check_killer( CHAR_DATA *ch, CHAR_DATA *victim ) { /* * 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 ( IS_NPC( ch ) || ch == victim || victim->level > LEVEL_HERO ) return; /* * KILLERs are fair game. * THIEVES are fair game too. * KILLER aggressors should not be penalized 1000 exps per attack but * per combat started. * ARENA combat is fair game. */ if ( IS_SET( victim->act, PLR_KILLER ) || IS_SET( victim->act, PLR_THIEF ) || ( IS_SET( ch->act, PLR_KILLER ) && ch->fighting) ) return; /* * Vampires are fair game. */ if ( !str_cmp( race_table[victim->race].name, "Vampire" ) ) return; // Veygoth - took away all of the pkilling penalties, since this // is meant for a pkill MUD. If you are not licensed or registered // to kill, then you get a killer flag. if ( !licensed( ch )) { SET_BIT( ch->act, PLR_KILLER ); return; } 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_OBJ_STAT( obj, ITEM_POISONED ) ) return TRUE; return FALSE; } /* * Hunting, Hating and Fearing code. -Thoric */ bool is_hunting( CHAR_DATA *ch, CHAR_DATA *victim ) { if ( !ch->hunting || ch->hunting->who != victim ) return FALSE; return TRUE; } bool is_hating( CHAR_DATA *ch, CHAR_DATA *victim ) { if ( !ch->hating || ch->hating->who != victim ) return FALSE; return TRUE; } bool is_fearing( CHAR_DATA *ch, CHAR_DATA *victim ) { if ( !ch->fearing || ch->fearing->who != victim ) return FALSE; return TRUE; } void stop_hunting( CHAR_DATA *ch ) { if ( ch->hunting ) { free_string( ch->hunting->name ); free_mem( ch->hunting, sizeof( HHF_DATA ) ); ch->hunting = NULL; } return; } void stop_hating( CHAR_DATA *ch ) { if ( ch->hating ) { free_string( ch->hating->name ); free_mem( ch->hating, sizeof( HHF_DATA ) ); ch->hating = NULL; } return; } void stop_fearing( CHAR_DATA *ch ) { if ( ch->fearing ) { free_string( ch->fearing->name ); free_mem( ch->fearing, sizeof( HHF_DATA ) ); ch->fearing = NULL; } return; } void start_hunting( CHAR_DATA *ch, CHAR_DATA *victim ) { if ( ch->hunting ) stop_hunting( ch ); ch->hunting = alloc_mem( sizeof( HHF_DATA ) ); ch->hunting->name = str_dup( victim->name ); ch->hunting->who = victim; return; } void start_hating( CHAR_DATA *ch, CHAR_DATA *victim ) { if ( ch->hating ) stop_hating( ch ); ch->hating = alloc_mem( sizeof( HHF_DATA ) ); ch->hating->name = str_dup( victim->name ); ch->hating->who = victim; return; } void start_fearing( CHAR_DATA *ch, CHAR_DATA *victim ) { if ( ch->fearing ) stop_fearing( ch ); ch->fearing = alloc_mem( sizeof( HHF_DATA ) ); ch->fearing->name = str_dup( victim->name ); ch->fearing->who = victim; return; } /* * Check for parry. */ bool check_parry( CHAR_DATA *ch, CHAR_DATA *victim ) { int chance; if ( !IS_AWAKE( victim ) ) return FALSE; if ( IS_NPC( victim ) ) { /* Tuan was here. :) */ chance = UMIN( 60, 2 * victim->level ); if ( !get_eq_char( victim, WEAR_HAND ) ) { if ( !get_eq_char( victim, WEAR_HAND_2 ) ) chance /= 2; else chance = 3 * chance / 4; } } else { if ( !get_eq_char( victim, WEAR_HAND ) ) { if ( !get_eq_char( victim, WEAR_HAND_2 ) ) return FALSE; chance = victim->pcdata->skl_lrn[gsn_parry] / 4; } else chance = victim->pcdata->skl_lrn[gsn_parry] / 2; skill_practice( ch, gsn_parry ); } if ( number_percent( ) >= chance + victim->level - ch->level ) return FALSE; act( "$N&n parries your attack.", ch, NULL, victim, TO_CHAR ); act( "You parry $n&n's attack.", ch, NULL, victim, TO_VICT ); act( "$N&n parries $n&n's attack.", ch, NULL, victim, TO_NOTVICT ); return TRUE; } /* * Check for block. */ bool check_shield_block( CHAR_DATA *ch, CHAR_DATA *victim ) { OBJ_DATA *obj; int chance; if ( !IS_AWAKE( victim ) ) return FALSE; if ( !(obj = get_eq_char( victim, WEAR_HAND )) ) { if ( !(obj = get_eq_char( victim, WEAR_HAND_2 )) ) return FALSE; else if( obj->item_type != TYPE_SHIELD ) return FALSE; } if ( obj->item_type != TYPE_SHIELD ) { if ( !(obj = get_eq_char( victim, WEAR_HAND_2 )) ) return FALSE; else if( obj->item_type != TYPE_SHIELD ) return FALSE; } if ( IS_NPC( victim ) ) /* Zen was here. :) */ chance = UMIN( 60, 2 * victim->level ); else chance = victim->pcdata->skl_lrn[gsn_shield_block] / 2; skill_practice( ch, gsn_shield_block ); if ( number_percent( ) >= chance + victim->level - ch->level ) return FALSE; act( "You block $n&n's attack with your shield.", ch, NULL, victim, TO_VICT ); act( "$N&n blocks your attack with a shield.", ch, NULL, victim, TO_CHAR ); act( "$N&n blocks $n&n's attack with a shield.", ch, NULL, victim, TO_NOTVICT ); return TRUE; } /* * Check for dodge. */ bool check_dodge( CHAR_DATA *ch, CHAR_DATA *victim ) { int chance; if ( !IS_AWAKE( victim ) ) return FALSE; if ( IS_NPC( victim ) ) /* Tuan was here. :) */ chance = UMIN( 60, 2 * victim->level ); else chance = victim->pcdata->skl_lrn[gsn_dodge] / 2; skill_practice( ch, gsn_dodge ); if ( number_percent( ) >= chance + victim->level - ch->level ) return FALSE; act( "$N&n dodges your attack.", ch, NULL, victim, TO_CHAR ); act( "You dodge $n&n's attack.", ch, NULL, victim, TO_VICT ); act( "$N&n dodges $n&n's attack.", ch, NULL, victim, TO_NOTVICT ); return TRUE; } /* * Set position of a victim. */ void update_pos( CHAR_DATA *victim ) { if ( victim->hit > 0 ) { if ( victim->position <= POS_STUNNED ) victim->position = POS_STANDING; return; } if ( IS_NPC( victim ) || victim->hit <= -11 ) { if ( victim->riding ) { act( "$n&n falls from $N&n.", victim, NULL, victim->riding, TO_ROOM ); victim->riding->rider = NULL; victim->riding = NULL; } 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; if ( victim->riding ) { act( "$n falls unconscious from $N.", victim, NULL, victim->riding, TO_ROOM ); victim->riding->rider = NULL; victim->riding = NULL; } return; } /* * Start fights. */ void set_fighting( CHAR_DATA *ch, CHAR_DATA *victim ) { char buf [ MAX_STRING_LENGTH ]; if ( ch->fighting ) { bug( "Set_fighting: already fighting", 0 ); sprintf( buf, "...%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 ); bug( buf, 0 ); return; } if ( IS_AFFECTED( ch, AFF_SLEEP ) ) affect_strip( ch, 0, spl_sleep ); ch->fighting = victim; if( ch->position == POS_STANDING ) ch->position = POS_FIGHTING; return; } /* * Stop fights. */ void stop_fighting( CHAR_DATA *ch, bool fBoth ) { CHAR_DATA *fch; for ( fch = char_list; fch; fch = fch->next ) { if ( fch == ch || ( fBoth && fch->fighting == ch ) ) { fch->fighting = NULL; if( fch->position == POS_FIGHTING ) fch->position = POS_STANDING; if ( is_affected( fch, gsn_berserk, 0 ) ) affect_strip( fch, gsn_berserk, 0 ); 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 ]; int corpse_vnum; int timer; // Different corpse settings for players and mobs - Veygoth if( IS_NPC( ch ) ) { corpse_vnum = OBJ_VNUM_CORPSE_NPC; name = ch->short_descr; timer = number_range( 15, 45 ); } else { corpse_vnum = OBJ_VNUM_CORPSE_PC; name = ch->name; timer = number_range( 180, 360 ) + (ch->level * 2); } /* * 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) */ if ( get_cash( ch ) > 0 ) { OBJ_DATA * coins; coins = create_money( ch->money.copper, ch->money.silver, ch->money.gold, ch->money.platinum ); corpse = create_object( get_obj_index( corpse_vnum ), 0 ); obj_to_obj( coins, corpse ); ch->money.copper = 0; ch->money.silver = 0; ch->money.gold = 0; ch->money.platinum = 0; } else { corpse = create_object( get_obj_index( corpse_vnum ), 0 ); } corpse->timer = timer; corpse->value[0] = race_table[ch->race].parts; 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 ); sprintf( buf, "%s corpse", name ); free_string( corpse->name ); corpse->name = str_dup( buf ); 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_OBJ_STAT( obj, ITEM_INVENTORY ) ) { extract_obj( obj ); } else { obj_to_obj( obj, corpse ); } } obj_to_room( corpse, ch->in_room ); return; } /* * Improved Death_cry contributed by Diavolo. */ void death_cry( CHAR_DATA *ch ) { ROOM_INDEX_DATA *was_in_room; char *msg; char mesg [ MAX_STRING_LENGTH ]; int vnum; int door; int parts; vnum = 0; msg = "You hear $n&n's death cry."; parts = race_table[ch->race].parts; switch ( number_bits( 6 ) ) { default: msg = "You hear $n&n's death cry."; break; case 0: msg = "$n&n hits the ground ... DEAD."; break; case 1: msg = "$n&n splatters blood on your armor."; break; case 4: if ( IS_SET( parts, PART_HEAD ) ) { msg = "$n&n's severed head plops on the ground."; vnum = OBJ_VNUM_SEVERED_HEAD; } break; case 6: if ( IS_SET( parts, PART_ARMS ) ) { msg = "$n&n's arm is sliced from $s dead body."; vnum = OBJ_VNUM_SLICED_ARM; } break; case 7: if ( IS_SET( parts, PART_LEGS ) ) { msg = "$n&n's leg is sliced from $s dead body."; vnum = OBJ_VNUM_SLICED_LEG; } break; } sprintf( mesg, "%s", msg ); act( mesg, ch, NULL, NULL, TO_ROOM ); if ( vnum != 0 ) { OBJ_DATA *obj; char *name; char buf [ MAX_STRING_LENGTH ]; name = IS_NPC( ch ) ? ch->short_descr : ch->name; obj = create_object( get_obj_index( vnum ), 0 ); obj->timer = number_range( 4, 7 ); 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_to_room( obj, ch->in_room ); } if ( IS_NPC( ch ) ) msg = "You hear something's death cry."; else msg = "You hear someone's death cry."; was_in_room = ch->in_room; for ( door = 0; door < MAX_DIR; door++ ) { EXIT_DATA *pexit; if ( ( pexit = was_in_room->exit[door] ) && pexit->to_room && pexit->to_room != was_in_room ) { ch->in_room = pexit->to_room; act( msg, ch, NULL, NULL, TO_ROOM ); } } ch->in_room = was_in_room; return; } void raw_kill( CHAR_DATA *ch, CHAR_DATA *victim ) { AFFECT_DATA *paf; CHAR_DATA *wch; ROOM_INDEX_DATA *room; stop_fighting( victim, TRUE ); if ( ch != victim ) mprog_death_trigger( victim ); make_corpse( victim ); if ( victim->rider ) { act( "$n&n dies suddenly, and you fall to the ground.", victim, NULL, victim->rider, TO_VICT ); victim->rider->riding = NULL; victim->rider->position = POS_RESTING; victim->rider = NULL; } if ( victim->riding ) { act( "$n&n falls off you, dead.", victim, NULL, victim->riding, TO_VICT ); victim->riding->rider = NULL; victim->riding = NULL; } if ( !IS_NPC( victim ) && IS_AFFECTED( victim, AFF_VAMP_BITE ) ) victim->race = race_lookup( "Vampire" ); for ( paf = victim->affected; paf; paf = paf->next ) { if ( paf->deleted ) continue; /* Keep the ghoul affect */ if ( !IS_NPC( victim ) && IS_AFFECTED( victim, AFF_GHOUL ) ) continue; affect_remove( victim, paf ); } if ( IS_NPC( victim ) ) { victim->pIndexData->killed++; kill_table[URANGE( 0, victim->level, MAX_LEVEL-1 )].killed++; extract_char( victim, TRUE ); return; } // Character has died in combat, extract them to repop point and put // them at the menu. extract_char( victim, FALSE ); victim->armor = 100; victim->position = POS_STANDING; victim->hit = UMAX( 1, victim->hit ); victim->mana = UMAX( 1, victim->mana ); victim->move = UMAX( 1, victim->move ); victim->hitroll = 0; victim->damroll = 0; victim->saving_throw[0] = 0; victim->saving_throw[1] = 0; victim->saving_throw[2] = 0; victim->saving_throw[3] = 0; victim->saving_throw[4] = 0; victim->mod_str = 0; victim->mod_int = 0; victim->mod_wis = 0; victim->mod_dex = 0; victim->mod_con = 0; victim->mod_agi = 0; victim->mod_cha = 0; victim->mod_pow = 0; victim->mod_luk = 0; /* * Pardon crimes... -Thoric */ if ( IS_SET( victim->act, PLR_KILLER ) ) { REMOVE_BIT( victim->act, PLR_KILLER ); send_to_char( "The gods have pardoned you for your murderous acts.\n\r", victim ); } if ( IS_SET( victim->act, PLR_THIEF ) ) { REMOVE_BIT( victim->act, PLR_THIEF ); send_to_char( "The gods have pardoned you for your thievery.\n\r", victim ); } if ( !str_cmp( race_table[ch->race].name, "Vampire" ) ) { victim->pcdata->condition[COND_FULL ] = 0; victim->pcdata->condition[COND_THIRST] = 0; } // This is where we send them to the menu. die_follower( victim, victim->name ); if( victim->in_room ) { log_string( "Victim had a room when they died." ); room = victim->in_room; } else { log_string( "Victim did not have a room when they died." ); room = get_room_index( repop_point[ch->race][ch->class] ); if( !room ) room = get_room_index( ROOM_VNUM_LIMBO ); } char_from_room( victim ); if( room ) victim->in_room = room; // Put them in the correct body if( victim->desc && victim->desc->original ) { log_string( "Returning victim to original body." ); do_return( victim, "" ); } // Reset reply pointers for( wch = char_list; wch; wch = wch->next ) if( wch->reply == victim ) wch->reply = NULL; log_string( "Saving victim..." ); save_char_obj( victim ); for( wch = char_list; wch; wch = wch->next ) { if( wch == victim ) { log_string( "Removing victim from the top of char_list..." ); char_list = victim->next; } else if( wch->next && wch->next == victim ) { if( victim->next ) { log_string( "Removing victim from middle of char_list..." ); wch->next = victim->next; } else { log_string( "Removing victim from end of char_list..." ); wch->next = NULL; } } } log_string( "Sending victim to the menu." ); do_help( victim, "login_menu" ); if( victim->desc ) victim->desc->connected = CON_MENU; else victim->deleted = TRUE; return; } void group_gain( CHAR_DATA *ch, CHAR_DATA *victim ) { CHAR_DATA *gch; CHAR_DATA *lch; char buf[ MAX_STRING_LENGTH ]; int members; 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; for ( gch = ch->in_room->people; gch; gch = gch->next_in_room ) { if ( is_same_group( gch, ch ) ) members++; } if ( members == 0 ) { bug( "Group_gain: members.", members ); members = 1; } lch = ( ch->leader ) ? ch->leader : ch; 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; if ( gch->level - lch->level >= 6 ) { send_to_char( "You are too high level for this group.\n\r", gch ); continue; } if ( gch->level - lch->level <= -6 ) { send_to_char( "You are too low level for this group.\n\r", gch ); continue; } xp = xp_compute( gch, victim ) / members; if( IS_NPC( victim ) && !IS_NPC( gch ) && (gch->level > 4)) xp = ( xp * check_trophy( gch, victim, members )) / 100; check_frag( ch, victim ); if ( !str_infix( race_table[lch->race].name, race_table[gch->race].hate) && members > 1 ) { send_to_char( "You lost a third of your exps due to grouping with scum.\n\r", ch ); xp -= xp/3; } sprintf( buf, "You receive your share of experience.\n\r" ); 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 ) continue; if ( obj->wear_loc == WEAR_NONE ) 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( "You are zapped by $p&n.", gch, obj, NULL, TO_CHAR ); act( "$n&n is zapped by $p&n.", gch, obj, NULL, TO_ROOM ); obj_from_char( obj ); obj_to_room( obj, gch->in_room ); } } } return; } /* * Compute xp for a kill. * Also adjust alignment of killer. * Edit this function to change xp computations. */ int xp_compute( CHAR_DATA *gch, CHAR_DATA *victim ) { OBJ_DATA *obj; double bonus; int xp; int align; int extra; int level; int number; bonus = 1.0; xp = 105 - URANGE( -4, gch->level - victim->level, 6 ) * 25; align = gch->alignment - victim->alignment; if ( align > 500 ) { gch->alignment = UMIN( gch->alignment + ( align - 500 ) / 4, 1000 ); xp = 5 * xp / 4; } else if ( align < -500 ) { gch->alignment = UMAX( gch->alignment + ( align + 500 ) / 4, -1000 ); xp = 5 * xp / 4; } else { gch->alignment -= gch->alignment / 4; xp = 3 * xp / 4; } if ( IS_AFFECTED( victim, AFF_SANCTUARY ) || IS_SET( race_table[ victim->race ].race_abilities, RACE_SANCT ) ) { bonus += 1.0/2.0; } if ( IS_AFFECTED( victim, AFF_FLAMING ) ) { bonus += 4.0/10.0; } if ( ( obj = get_eq_char( victim, WEAR_HAND ) ) ) { bonus += 1.0/4.0; } if ( ( obj = get_eq_char( victim, WEAR_HAND_2 ) ) ) { bonus += 1.0/5.0; } if ( !str_infix( race_table[victim->race].name, race_table[gch->race].hate ) ) { bonus += 1.0/10.0; } if ( victim->race == gch->race ) { bonus -= 1.0/8.0; } if ( IS_NPC( victim ) ) { if ( IS_SET( victim->act, ACT_AGGRESSIVE ) ) { bonus += 1.0/20.0; } if ( victim->pIndexData->pShop != 0 ) bonus -= 1.0/4.0; if ( victim->spec_fun != 0 ) { bonus += 1.0/10.0; } if( victim->spec_fun2 != 0 ) { bonus += 1.0/20.0; } } else { // Player-vs-player experience // no exp for under level 5, 1/2 exp for level 6-10, double exp // for non-newbies (10+) if( victim->level < 6) { send_to_char( "You killed a newbie! You are a twink!\n\r", gch ); bonus = 0.0; } else if( victim->level < 11) bonus *= 0.5; else bonus *= 2.0; } 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, 250 ); return xp; } void spl_dam_message( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt, bool immune ) { const char *vp; const char *attack; char buf [ MAX_STRING_LENGTH ]; char buf1 [ 256 ]; char buf2 [ 256 ]; char buf3 [ 256 ]; char buf4 [ 256 ]; char buf5 [ 256 ]; if ( dam == 0 ) { vp = "misses"; } else { vp = "hits"; } if ( dt == TYPE_HIT ) { if ( ch->race > MAX_RACE ) { bug( "Spl_Dam_message: %d invalid race", ch->race ); ch->race = 0; } attack = race_table[ch->race].dmg_message; sprintf( buf1, "Your %s %s $N&n.", attack, vp ); sprintf( buf2, "$n&n's %s %s you.", attack, vp ); sprintf( buf3, "$n&n's %s %s $N&n.", attack, vp ); sprintf( buf4, "You %s %s yourself.", attack, vp ); sprintf( buf5, "$n&n's %s %s $m.", attack, vp ); } else { if ( dt >= 0 && dt < MAX_SPELL ) attack = spells_table[dt].msg_damage; else if ( dt >= TYPE_HIT && dt < TYPE_HIT + MAX_ATTACK ) attack = attack_table[dt - TYPE_HIT].name; else { sprintf( buf, "Spl_dam_message: bad dt %d for %d damage caused by %s to %s.", dt, dam, ch->name, victim->name ); bug( buf, 0 ); dt = TYPE_HIT; attack = attack_table[0].name; } if ( immune ) { sprintf( buf1, "$N&n seems unaffected by your %s!", attack ); sprintf( buf2, "$n&n's %s seems powerless against you.", attack ); sprintf( buf3, "$N&n seems unaffected by $n&n's %s!", attack ); sprintf( buf4, "$n&n seems unaffected by $s own %s.", attack ); sprintf( buf5, "Luckily, you are immune to %s.", attack ); } else { { sprintf( buf1, "Your %s %s $N&n.", attack, vp ); sprintf( buf2, "$n&n's %s %s you.", attack, vp ); sprintf( buf3, "$n&n's %s %s $N&n.", attack, vp ); sprintf( buf4, "Your %s %s you.", attack, vp ); sprintf( buf5, "$n&n's %s %s $m.", attack, vp ); } } } if ( victim != ch ) { act( buf1, ch, NULL, victim, TO_CHAR ); act( buf2, ch, NULL, victim, TO_VICT ); act( buf3, ch, NULL, victim, TO_NOTVICT ); } else { act( buf4, ch, NULL, victim, TO_CHAR ); act( buf5, ch, NULL, victim, TO_ROOM ); } return; } void dam_message( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt, int wpn, bool immune ) { const char *vp; const char *attack; char buf [ MAX_STRING_LENGTH ]; char buf1 [ 256 ]; char buf2 [ 256 ]; char buf3 [ 256 ]; char buf4 [ 256 ]; char buf5 [ 256 ]; char punct; if ( dam == 0 ) { vp = "misses"; } else { dam *= 100; if ( victim->hit > 0 ) dam /= victim->hit; if ( dam <= 2 ) { vp = "scratches"; } else if ( dam <= 4 ) { vp = "grazes"; } else if ( dam <= 8 ) { vp = "hits"; } else if ( dam <= 12 ) { vp = "injures"; } else if ( dam <= 20 ) { vp = "wounds"; } else if ( dam <= 30 ) { vp = "mauls"; } else if ( dam <= 40 ) { vp = "decimates"; } else if ( dam <= 50 ) { vp = "devastates"; } else if ( dam <= 60 ) { vp = "maims"; } else if ( dam <= 70 ) { vp = "mutilates"; } else if ( dam <= 80 ) { vp = "disembowels"; } else if ( dam <= 90 ) { vp = "eviscerates"; } else if ( dam <= 100 ) { vp = "massacres"; } else { vp = "annihilates"; } } punct = ( dam <= 40 ) ? '.' : '!'; 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; sprintf( buf1, "Your %s %s $N&n%c", attack, vp, punct ); sprintf( buf2, "$n&n's %s %s you%c", attack, vp, punct ); sprintf( buf3, "$n&n's %s %s $N&n%c", attack, vp, punct ); sprintf( buf4, "You %s %s yourself%c", attack, vp, punct ); sprintf( buf5, "$n&n's %s %s $m%c", attack, vp, punct ); } else { if ( dt >= 0 && dt < MAX_SKILL ) attack = skills_table[dt].noun_damage; else if ( dt >= TYPE_HIT && dt < TYPE_HIT + MAX_ATTACK ) attack = attack_table[dt - TYPE_HIT].name; else { sprintf( buf, "Dam_message: bad dt %d for %d damage caused by %s to %s with weapon %d.", dt, dam, ch->name, victim->name, wpn ); bug( buf, 0 ); dt = TYPE_HIT; attack = attack_table[0].name; } if ( immune ) { sprintf( buf1, "$N&n seems unaffected by your %s!", attack ); sprintf( buf2, "$n&n's %s seems powerless against you.", attack ); sprintf( buf3, "$N&n seems unaffected by $n&n's %s!", attack ); sprintf( buf4, "$n&n seems unaffected by $s own %s.", attack ); sprintf( buf5, "Luckily, you seem immune to %s.", attack ); } else { if ( dt > TYPE_HIT && is_wielding_poisoned( ch, wpn ) ) { sprintf( buf1, "Your poisoned %s %s $N&n%c", attack, vp, punct ); sprintf( buf2, "$n&n's poisoned %s %s you%c", attack, vp, punct ); sprintf( buf3, "$n&n's poisoned %s %s $N&n%c", attack, vp, punct ); sprintf( buf4, "Your poisoned %s %s you%c", attack, vp, punct ); sprintf( buf5, "$n&n's poisoned %s %s $m%c", attack, vp, punct ); } else { sprintf( buf1, "Your %s %s $N&n%c", attack, vp, punct ); sprintf( buf2, "$n&n's %s %s you%c", attack, vp, punct ); sprintf( buf3, "$n&n's %s %s $N&n%c", attack, vp, punct ); sprintf( buf4, "Your %s %s you%c", attack, vp, punct ); sprintf( buf5, "$n&n's %s %s $m%c", attack, vp, punct ); } } } if ( victim != ch ) { act( buf1, ch, NULL, victim, TO_CHAR ); act( buf2, ch, NULL, victim, TO_VICT ); act( buf3, ch, NULL, victim, TO_NOTVICT ); } else { act( buf4, ch, NULL, victim, TO_CHAR ); act( buf5, ch, NULL, victim, TO_ROOM ); } return; } /* * Disarm a creature. * Caller must check for successful attack. */ void disarm( CHAR_DATA *ch, CHAR_DATA *victim ) { OBJ_DATA *obj; if ( ( race_table[ ch->race ].size - race_table[ victim->race ].size ) < -2 ) return; if ( !( obj = get_eq_char( victim, WEAR_HAND ) ) ) if ( !( obj = get_eq_char( victim, WEAR_HAND_2 ) ) ) return; if ( IS_OBJ_STAT( obj, ITEM_NODROP ) ) return; if ( !get_eq_char( ch, WEAR_HAND ) && !get_eq_char( ch, WEAR_HAND_2 ) && number_bits( 1 ) == 0 ) return; 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 ) ) obj_to_char( obj, victim ); else obj_to_room( obj, victim->in_room ); return; } /* * Trip a creature. * Caller must check for successful attack. */ void trip( CHAR_DATA *ch, CHAR_DATA *victim ) { if ( IS_AFFECTED( victim, AFF_FLYING ) || IS_SET( race_table[ victim->race ].race_abilities, RACE_FLY ) ) return; if ( victim->riding ) { if ( IS_AFFECTED( victim->riding, AFF_FLYING ) || IS_SET( race_table[ victim->riding->race ].race_abilities, RACE_FLY ) ) return; act( "$n trips your mount and you fall off!", ch, NULL, victim, TO_VICT ); act( "You trip $N's mount and $N falls off!", ch, NULL, victim, TO_CHAR ); act( "$n trips $N's mount and $N falls off!", ch, NULL, victim, TO_NOTVICT ); victim->riding->rider = NULL; victim->riding = NULL; WAIT_STATE( ch, 2 * PULSE_VIOLENCE ); WAIT_STATE( victim, 2 * PULSE_VIOLENCE ); victim->position = POS_RESTING; return; } if ( victim->wait == 0 ) { act( "You trip $N and $N goes down!", ch, NULL, victim, TO_CHAR ); act( "$n trips you and you go down!", ch, NULL, victim, TO_VICT ); act( "$n trips $N and $N goes down!", ch, NULL, victim, TO_NOTVICT ); WAIT_STATE( ch, 2 * PULSE_VIOLENCE ); WAIT_STATE( victim, 2 * PULSE_VIOLENCE ); victim->position = POS_RESTING; } return; } void do_kill( CHAR_DATA *ch, char *argument ) { CHAR_DATA *victim; char arg [ MAX_INPUT_LENGTH ]; int chance; one_argument( argument, arg ); if ( arg[0] == '\0' ) { send_to_char( "Kill whom?\n\r", ch ); return; } if ( !( victim = get_char_room( ch, arg ) ) ) { send_to_char( "They aren't here.\n\r", ch ); return; } if ( victim == ch ) { send_to_char( "Don't be a moron.\n\r", ch ); return; } victim = check_guarding( ch, victim ); if ( is_safe( ch, victim ) ) return; if ( ch->position == POS_FIGHTING || ch->fighting ) { if( victim == ch->fighting ) { send_to_char( "You do the best you can!\n\r", ch ); return; } else { if( IS_NPC( ch )) { chance = (ch->level * 3 / 2 + 15); } else if( ch->level >= skills_table[gsn_switch].skill_level[ch->class] ) { chance = ch->pcdata->skl_lrn[gsn_switch]; skill_practice( ch, gsn_switch ); } else { chance = ch->level / 2; } if( number_percent() < chance ) { send_to_char( "You switch opponents!\n\r", ch ); act( "$n&n switches targets...", ch, NULL, victim, TO_NOTVICT ); act( "$n&n switches targets...", ch, NULL, victim, TO_NOTVICT ); ch->fighting = victim; WAIT_STATE( ch, skills_table[gsn_switch].beats ); return; } else { send_to_char( "You can't seem to break away from your current opponent.\n\r", ch ); stop_fighting( ch, FALSE ); WAIT_STATE( ch, skills_table[gsn_switch].beats ); return; } } } WAIT_STATE( ch, 1 * PULSE_VIOLENCE ); multi_hit( ch, victim, TYPE_UNDEFINED ); return; } /* * I'm only allowing backstabbing with the primary weapon...immortals * who wield two weapons, with the first not being a dagger, will be * unable to backstab or circle. Tough cookie. --- Thelonius */ void do_backstab( CHAR_DATA *ch, char *argument ) { OBJ_DATA *obj; CHAR_DATA *victim; char arg [ MAX_INPUT_LENGTH ]; if ( !IS_NPC( ch ) && ch->level < skills_table[gsn_backstab].skill_level[ch->class] ) { send_to_char( "You better leave the assassin trade to thieves.\n\r", ch ); return; } if ( ch->riding ) { send_to_char( "You can't get close enough while mounted.\n\r", ch ); return; } one_argument( argument, arg ); if ( arg[0] == '\0' ) { send_to_char( "Backstab whom?\n\r", ch ); return; } if ( !( victim = get_char_room( ch, arg ) ) ) { send_to_char( "They aren't here.\n\r", ch ); return; } if ( victim == ch ) { send_to_char( "How can you sneak up on yourself?\n\r", ch ); return; } victim = check_guarding( ch, victim ); if ( is_safe( ch, victim ) ) return; if ( !( obj = get_eq_char( ch, WEAR_HAND ) ) || (*attack_table[obj->value[3]].wpn_gsn) != gsn_pierce ) { send_to_char( "You need to wield a piercing weapon.\n\r", ch ); return; } if ( victim->fighting ) { send_to_char( "You can't backstab a fighting person.\n\r", ch ); return; } check_killer( ch, victim ); WAIT_STATE( ch, skills_table[gsn_backstab].beats ); if ( !IS_AWAKE( victim ) || IS_NPC( ch ) || number_percent( ) < ch->pcdata->skl_lrn[gsn_backstab] ) multi_hit( ch, victim, gsn_backstab ); else damage( ch, victim, 0, gsn_backstab, WEAR_HAND, DAM_PIERCE ); skill_practice( ch, gsn_backstab ); return; } void do_circle( CHAR_DATA *ch, char *argument ) { OBJ_DATA *obj; CHAR_DATA *rch; CHAR_DATA *victim; char arg [ MAX_INPUT_LENGTH ]; if ( !IS_NPC( ch ) && ch->level < skills_table[gsn_circle].skill_level[ch->class] ) { send_to_char( "You'd better leave the assassin trade to thieves.\n\r", ch ); return; } if ( IS_NPC( ch ) && IS_AFFECTED( ch, AFF_CHARM ) ) return; if ( !ch->fighting ) { send_to_char( "You must be fighting in order to do that.\n\r", ch ); return; } if ( ch->riding ) { send_to_char( "You can't circle while mounted.\n\r", ch ); return; } one_argument( argument, arg ); if ( arg[0] == '\0' ) victim = ch->fighting; else if ( !( victim = get_char_room( ch, arg ) ) ) { send_to_char( "They aren't here.\n\r", ch ); return; } if ( victim == ch ) { send_to_char( "You spin around in a circle. Whee!\n\r", ch ); return; } victim = check_guarding( ch, victim ); if ( is_safe( ch, victim ) ) return; if ( victim != ch->fighting ) { send_to_char( "One fight at a time.\n\r", ch ); return; } if ( !victim->fighting ) { act( "Why? $E isn't bothering anyone.", ch, NULL, victim, TO_CHAR ); return; } if ( !is_same_group( ch, victim->fighting ) ) { send_to_char( "Why call attention to yourself?\n\r", ch ); return; } for ( rch = ch->in_room->people; rch; rch = rch->next_in_room ) if ( rch->fighting == ch ) break; if ( rch ) { send_to_char( "You're too busy being hit right now.\n\r", ch ); return; } if ( !( obj = get_eq_char( ch, WEAR_HAND ) ) || obj->value[3] != 11 ) { send_to_char( "You need to wield a piercing weapon.\n\r", ch ); return; } act( "You circle around behind $N...", ch, NULL, victim, TO_CHAR ); act( "$n circles around behind $N...", ch, NULL, victim, TO_NOTVICT ); check_killer( ch, victim ); WAIT_STATE( ch, skills_table[gsn_circle].beats ); if ( IS_NPC( ch ) || number_percent( ) < ch->pcdata->skl_lrn[gsn_circle] / 2 ) { stop_fighting( victim, FALSE ); multi_hit( ch, victim, gsn_circle ); } else act( "You failed to get around $M", ch, NULL, victim, TO_CHAR ); skill_practice( ch, gsn_circle ); return; } void do_flee( CHAR_DATA *ch, char *argument ) { CHAR_DATA *victim; ROOM_INDEX_DATA *was_in; ROOM_INDEX_DATA *now_in; int attempt; char buf[MAX_STRING_LENGTH]; // Panicked people can flee when not fighting - Veygoth if ( !( victim = ch->fighting ) ) { if ( ch->position == POS_FIGHTING ) ch->position = POS_STANDING; } if ( is_affected( ch, gsn_berserk, 0 ) ) { send_to_char( "You can't flee, you're &+RBerserk&n!\n\r", ch ); return; } if ( IS_AFFECTED( ch, AFF_HOLD ) ) { send_to_char( "You are stuck in a snare! You can't move!\n\r", ch ); act( "$n&n wants to flee, but is caught in a snare!", ch, NULL, NULL, TO_ROOM ); return; } was_in = ch->in_room; for ( attempt = 0; attempt < 6; attempt++ ) { EXIT_DATA *pexit; int door; door = number_door( ); if ( ( pexit = was_in->exit[door] ) == 0 || !pexit->to_room || IS_SET( pexit->exit_info, EX_CLOSED ) || ( IS_NPC( ch ) && ( IS_SET( pexit->to_room->room_flags, ROOM_NO_MOB ) || ( IS_SET( ch->act, ACT_STAY_AREA ) && pexit->to_room->area != ch->in_room->area ) ) ) ) continue; if ( ch->riding && ch->riding->fighting ) stop_fighting( ch->riding, TRUE ); // Just to keep the damned messages from being wacky... SET_AFF_BIT( ch, AFF_IS_FLEEING ); move_char( ch, door ); REMOVE_AFF_BIT( ch, AFF_IS_FLEEING ); if ( ( now_in = ch->in_room ) == was_in ) continue; ch->in_room = was_in; act( "$n&n panics and attempts to flee...", ch, NULL, NULL, TO_ROOM ); if( IS_AFFECTED( ch, AFF_SNEAK )) { act( "$n&n has fled!", ch, NULL, NULL, TO_ROOM ); } else { sprintf( buf, "$n&n flees %sward.", dir_name[door] ); act( buf, ch, NULL, NULL, TO_ROOM ); } ch->in_room = now_in; if ( !IS_NPC( ch ) ) { sprintf( buf, "You flee %sward!\n\r", dir_name[door] ); send_to_char( buf, ch ); } stop_fighting( ch, TRUE ); return; } send_to_char( "PANIC! You cannot escape!\n\r", ch ); return; } void do_berserk( CHAR_DATA *ch, char *argument ) { AFFECT_DATA af; /* Don't allow charmed mobs to do this, check player's level */ if ( ( IS_NPC( ch ) && IS_AFFECTED( ch, AFF_CHARM ) ) || ( !IS_NPC( ch ) && ch->level < skills_table[gsn_berserk].skill_level[ch->class] ) ) { send_to_char( "You're not enough of a warrior to go Berserk.\n\r", ch ); return; } if ( !ch->fighting ) { send_to_char( "You aren't fighting anyone.\n\r", ch ); return; } if ( is_affected( ch, gsn_berserk, 0 ) ) return; send_to_char( "Your weapon hits your foe and blood splatters all over!\n\r", ch ); send_to_char( "The taste of blood begins to drive you crazy!\n\r", ch ); if ( IS_NPC( ch ) || number_percent( ) < ch->pcdata->skl_lrn[gsn_berserk] ) { af.skill = gsn_berserk; af.spell = 0; af.duration = -1; af.location = APPLY_HITROLL; af.modifier = UMIN( ch->level / 4, 8 ); set_bitvector( &af, AFF_NONE); affect_to_char( ch, &af ); af.location = APPLY_DAMROLL; affect_to_char( ch, &af ); af.location = APPLY_AC; af.modifier = ch->level; affect_to_char( ch, &af ); send_to_char( "You have gone BERSERK!\n\r", ch ); act( "$n has gone BERSERK!", ch, NULL, NULL, TO_ROOM ); return; } send_to_char( "You shake off the madness.\n\r", ch ); skill_practice( ch, gsn_berserk ); return; } void do_rescue( CHAR_DATA *ch, char *argument ) { CHAR_DATA *victim; CHAR_DATA *fch; char arg [ MAX_INPUT_LENGTH ]; int count; /* Don't allow charmed mobs to do this, check player's level */ if ( ( IS_NPC( ch ) && IS_AFFECTED( ch, AFF_CHARM ) ) || ( !IS_NPC( ch ) && ch->level < skills_table[gsn_rescue].skill_level[ch->class] ) ) { send_to_char( "You'd better leave the heroic acts to warriors.\n\r", ch ); return; } if ( is_affected( ch, gsn_berserk, 0 ) ) { send_to_char( "You can't rescue anyone, you're BERSERK!\n\r", ch ); return; } if ( ch->riding ) { send_to_char( "You can't do that while mounted.\n\r", ch ); return; } one_argument( argument, arg ); if ( arg[0] == '\0' ) { send_to_char( "Rescue whom?\n\r", ch ); return; } if ( !( victim = get_char_room( ch, arg ) ) ) { send_to_char( "They aren't here.\n\r", ch ); return; } if ( victim == ch ) { send_to_char( "What about fleeing instead?\n\r", ch ); return; } if ( !IS_NPC( ch ) && IS_NPC( victim ) ) { send_to_char( "Doesn't need your help!\n\r", ch ); return; } if ( ch->fighting == victim ) { send_to_char( "Too late.\n\r", ch ); return; } if ( !victim->fighting ) { send_to_char( "That person is not fighting right now.\n\r", ch ); return; } if ( !is_same_group( ch, victim ) ) { send_to_char( "Why would you want to?\n\r", ch ); return; } if ( !check_blind ( ch ) ) return; WAIT_STATE( ch, skills_table[gsn_rescue].beats ); count = 0; for ( fch = victim->in_room->people; fch; fch = fch->next_in_room ) { if ( !IS_NPC( fch ) || fch->deleted ) continue; if ( fch->fighting == victim ) { if ( number_range( 0, count ) == 0 ) break; count++; } } skill_practice( ch, gsn_rescue ); if ( !fch || ( !IS_NPC( ch ) && number_percent( ) > ch->pcdata->skl_lrn[gsn_rescue] ) ) { send_to_char( "You fail the rescue.\n\r", ch ); return; } act( "You rescue $N&n!", ch, NULL, victim, TO_CHAR ); act( "$n&n rescues you!", ch, NULL, victim, TO_VICT ); act( "$n&n rescues $N&n!", ch, NULL, victim, TO_NOTVICT ); stop_fighting( fch, FALSE ); set_fighting( fch, ch ); return; } void do_kick( CHAR_DATA *ch, char *argument ) { CHAR_DATA *victim; char arg [ MAX_INPUT_LENGTH ]; /* Check player's level and class, allow mobs to do this too */ if ( ( ch->level < skills_table[gsn_kick].skill_level[ch->class] ) ) { send_to_char( "You'd better leave the martial arts to fighters.\n\r", ch ); return; } if ( !check_blind( ch ) ) return; one_argument( argument, arg ); victim = ch->fighting; if ( arg[0] != '\0' ) { if ( !( victim = get_char_room( ch, arg ) ) ) { send_to_char( "They aren't here.\n\r", ch ); return; } } else { if( !victim ) { send_to_char( "You aren't fighting anyone.\n\r", ch ); return; } } WAIT_STATE( ch, number_fuzzy(skills_table[gsn_kick].beats) ); skill_practice( ch, gsn_kick ); if ( IS_NPC( ch ) || number_percent( ) < ch->pcdata->skl_lrn[gsn_kick] ) damage( ch, victim, number_range( 1, ch->level ), gsn_kick, WEAR_NONE, DAM_BASH ); else damage( ch, victim, 0, gsn_kick, WEAR_NONE, DAM_BASH ); return; } /* * Bash by Veygoth * Usable to initiate combat and during combat */ void do_bash( CHAR_DATA *ch, char *argument ) { OBJ_DATA *obj; int chance; CHAR_DATA *victim; char arg [ MAX_INPUT_LENGTH ]; /* Check player's level and class, mobs can use this skill */ if ( (ch->level < skills_table[gsn_bash].skill_level[ch->class] ) ) { send_to_char( "You'd better leave the martial arts to fighters.\n\r", ch ); return; } if ( !check_blind( ch ) ) return; one_argument( argument, arg ); /* Bash self? JPG */ if ( victim == ch ) { send_to_char( "You knock yourself to the ground!\n\r", ch ); damage( ch, ch, number_range( 1,ch->level ), gsn_bash, WEAR_NONE, DAM_BASH ); WAIT_STATE( ch, ( skills_table[gsn_bash].beats * 5 / 6 )); ch->position = POS_SITTING; return; } victim = ch->fighting; if ( arg[0] != '\0' ) { if ( !( victim = get_char_room( ch, arg ) ) ) { send_to_char( "They aren't here.\n\r", ch ); return; } } else { if( !victim ) { send_to_char( "You aren't fighting anyone.\n\r", ch ); return; } } WAIT_STATE( ch, number_fuzzy( skills_table[gsn_bash].beats )); if( IS_NPC( ch )) chance = (ch->level * 3) / 2 + 15; else chance = ch->pcdata->skl_lrn[gsn_bash] - 5; if( victim->position <= POS_RESTING ) chance = 0; if( chance > 95 ) chance = 95; if ( !(obj = get_eq_char( victim, WEAR_HAND )) ) { if ( !(obj = get_eq_char( victim, WEAR_HAND_2 )) ) { if( ch->class != CLASS_PALADIN && ch->class != CLASS_ANTIPALADIN ) { chance -= 25; send_to_char( "Bashing without a shield is tough, but you try anyway...\n\r", ch ); } else { chance -=5; // Hidden penalty for not having a shield } } else if( obj->item_type != TYPE_SHIELD ) { if( ch->class != CLASS_PALADIN && ch->class != CLASS_ANTIPALADIN ) { chance -= 25; send_to_char( "Bashing without a shield is tough, but you try anyway...\n\r", ch ); } else { chance -= 3; // Small hidden penalty for not having a shield } } else if( ch->class == CLASS_PALADIN || ch->class == CLASS_ANTIPALADIN ) { chance += 3; // Small hidden bonus for having a shield } } else if ( obj->item_type != TYPE_SHIELD ) { if ( !(obj = get_eq_char( victim, WEAR_HAND_2 )) ) { if( ch->class != CLASS_PALADIN && ch->class != CLASS_ANTIPALADIN ) { chance -= 25; send_to_char( "Bashing without a shield is tough, but you try anyway...\n\r", ch ); } else { chance -=5; // Hidden penalty for not having a shield } } else if( obj->item_type != TYPE_SHIELD ) { if( ch->class != CLASS_PALADIN && ch->class != CLASS_ANTIPALADIN ) { chance -= 25; send_to_char( "Bashing without a shield is tough, but you try anyway...\n\r", ch ); } else { chance -=5; // Hidden penalty for not having a shield } } else if( ch->class == CLASS_PALADIN || ch->class == CLASS_ANTIPALADIN ) { chance += 3; // Small hidden bonus for having a shield } } skill_practice( ch, gsn_bash ); if (!ch->fighting) set_fighting( ch, victim ); if(!victim->fighting) set_fighting( victim, ch ); if ( IS_NPC( ch ) || number_percent( ) < chance ) { damage( ch, victim, number_range( 1, ch->level ), gsn_bash, WEAR_NONE, DAM_BASH ); WAIT_STATE( victim, ( skills_table[gsn_bash].beats * 5 / 6 )); victim->position = POS_SITTING; } else { act( "As $N&n avoids your bash you topple to the ground.", ch, NULL, victim, TO_CHAR ); act( "$n&n falls over as you avoid $s bash.", ch, NULL, victim, TO_VICT ); act( "$n&n misses $s bash at $N&n and topples to the ground.", ch, NULL, victim, TO_NOTVICT ); ch->position = POS_KNEELING; } return; } /* * Trip by Veygoth * Usable to initiate combat and during combat */ void do_trip( CHAR_DATA *ch, char *argument ) { int chance; CHAR_DATA *victim; char arg [ MAX_INPUT_LENGTH ]; /* Check player's level and class, mobs can use this skill */ if ( (ch->level < skills_table[gsn_trip].skill_level[ch->class] ) ) { send_to_char( "You would just fall over if you tried to trip someone.\n\r", ch ); return; } if ( !check_blind( ch ) ) return; one_argument( argument, arg ); victim = ch->fighting; if ( arg[0] != '\0' ) { if ( !( victim = get_char_room( ch, arg ) ) ) { send_to_char( "They aren't here.\n\r", ch ); return; } } else { if( !victim ) { send_to_char( "You aren't fighting anyone.\n\r", ch ); return; } } WAIT_STATE( ch, number_fuzzy(skills_table[gsn_trip].beats) ); skill_practice( ch, gsn_trip ); if( IS_NPC( ch )) chance = (ch->level * 3) / 2 + 10; else chance = ch->pcdata->skl_lrn[gsn_trip] - 10; if( chance > 90 ) chance = 90; if (!ch->fighting) set_fighting( ch, victim ); if(!victim->fighting) set_fighting( victim, ch ); if ( IS_NPC( ch ) || number_percent( ) < chance ) { WAIT_STATE( victim, ( skills_table[gsn_trip].beats * 5 / 6 )); act( "You trip $N&n and $E goes down!", ch, NULL, victim, TO_CHAR ); act( "$n&n trips you and you go down!", ch, NULL, victim, TO_VICT ); act( "$n&n trips $N&n and $E goes down!", ch, NULL, victim, TO_NOTVICT ); if( victim->position > POS_SITTING ) victim->position = POS_SITTING; } else { act( "You try to trip $N&n and fall down!", ch, NULL, victim, TO_CHAR ); act( "$n&n tries to trip you and falls down!", ch, NULL, victim, TO_VICT ); act( "$n&n tries to trip $N&n and falls down!", ch, NULL, victim, TO_NOTVICT ); ch->position = POS_RECLINING; } return; } /* * Springleap by Veygoth * Usable to initiate combat and during combat */ void do_springleap( CHAR_DATA *ch, char *argument ) { int chance; CHAR_DATA *victim; char arg [ MAX_INPUT_LENGTH ]; /* Check player's level and class, mobs can use this skill */ if ( (ch->level < skills_table[gsn_springleap].skill_level[ch->class] ) ) { send_to_char( "You'd better leave the martial arts to fighters.\n\r", ch ); return; } if ( !check_blind( ch ) ) return; one_argument( argument, arg ); victim = ch->fighting; if ( arg[0] != '\0' ) { if ( !( victim = get_char_room( ch, arg ) ) ) { send_to_char( "They aren't here.\n\r", ch ); return; } } else { if( !victim ) { send_to_char( "You aren't fighting anyone.\n\r", ch ); return; } } WAIT_STATE( ch, number_fuzzy(skills_table[gsn_springleap].beats) ); skill_practice( ch, gsn_springleap ); if( IS_NPC( ch )) chance = (ch->level * 3) / 2 + 15; else chance = ch->pcdata->skl_lrn[gsn_springleap] - 5; if( chance > 95 ) chance = 95; if (!ch->fighting) set_fighting( ch, victim ); if(!victim->fighting) set_fighting( victim, ch ); if ( IS_NPC( ch ) || number_percent( ) < chance ) { damage( ch, victim, number_range( 1, ch->level ), gsn_springleap, WEAR_NONE, DAM_BASH ); WAIT_STATE( victim, ( skills_table[gsn_springleap].beats * 5 / 6 )); if( victim->position > POS_SITTING ) victim->position = POS_SITTING; } else { ch->hit -= number_range( 1, 4 ); act( "As $N&n avoids your leap you crash to the ground.", ch, NULL, victim, TO_CHAR ); act( "$n&n crashes to the ground as you avoid $s springleap.", ch, NULL, victim, TO_VICT ); act( "$n&n misses a springleap at $N&n and falls to the ground.", ch, NULL, victim, TO_NOTVICT ); ch->position = POS_RECLINING; } return; } /* * Bodyslam by Veygoth * Usable to initiate combat */ void do_bodyslam( CHAR_DATA *ch, char *argument ) { int chance; CHAR_DATA *victim; char arg [ MAX_INPUT_LENGTH ]; /* Check player's level and class, mobs can use this skill */ if ( !IS_SET( race_table[ch->race].race_abilities, RACE_BODYSLAM ) ) { send_to_char( "You don't feel massive enough.\n\r", ch ); return; } if ( !check_blind( ch ) ) return; one_argument( argument, arg ); if ( arg[0] != '\0' ) { if ( !( victim = get_char_room( ch, arg ) ) ) { send_to_char( "They aren't here.\n\r", ch ); return; } } else { send_to_char( "Bodyslam who?\n\r", ch ); return; } WAIT_STATE( ch, skills_table[gsn_bodyslam].beats ); skill_practice( ch, gsn_bodyslam ); if( IS_NPC( ch )) chance = (ch->level * 3) / 2 + 15; else chance = ch->pcdata->skl_lrn[gsn_bodyslam] - 5; if( chance > 95 ) chance = 95; if( IS_AFFECTED( victim, AFF_AWARE )) { chance -= 15; } else if( IS_AFFECTED( victim, AFF_SKL_AWARE )) { if( ch->level >= skills_table[gsn_aware].skill_level[ch->class] ) { if( IS_NPC( ch )) { if( number_percent() < ((ch->level * 3) / 2 + 15)) { chance -= 15; } } else if( number_percent() < ch->pcdata->skl_lrn[gsn_aware] ) { skill_practice( ch, gsn_aware ); chance -= 15; } else { skill_practice( ch, gsn_aware ); } } } if (!ch->fighting) set_fighting( ch, victim ); if(!victim->fighting) set_fighting( victim, ch ); if ( IS_NPC( ch ) || number_percent( ) < chance ) { damage( ch, victim, number_range( 1, ch->level ), gsn_bodyslam, WEAR_NONE, DAM_BASH ); WAIT_STATE( victim, skills_table[gsn_bodyslam].beats ); victim->position = POS_RECLINING; } else { ch->hit -= number_range( 1, 5 ); act( "As $N&n avoids your slam, you smack headfirst into the ground.", ch, NULL, victim, TO_CHAR ); act( "$n&n throws $mself to the ground in a fit of clumsiness.", ch, NULL, victim, TO_VICT ); act( "$n&n misses a bodyslam on $N&n and slams $s head into the ground.", ch, NULL, victim, TO_NOTVICT ); ch->position = POS_RECLINING; } return; } /* * Headbutt by Veygoth * Usable to initiate combat and during combat */ void do_headbutt( CHAR_DATA *ch, char *argument ) { CHAR_DATA *victim; char arg [ MAX_INPUT_LENGTH ]; /* Check player's level and class, mobs can use this skill */ if ( (ch->level < skills_table[gsn_headbutt].skill_level[ch->class] ) ) { send_to_char( "You'd better leave the martial arts to fighters.\n\r", ch ); return; } if ( !check_blind( ch ) ) return; one_argument( argument, arg ); victim = ch->fighting; if ( arg[0] != '\0' ) { if ( !( victim = get_char_room( ch, arg ) ) ) { send_to_char( "They aren't here.\n\r", ch ); return; } } else { if( !victim ) { send_to_char( "You aren't fighting anyone.\n\r", ch ); return; } } WAIT_STATE( ch, number_fuzzy(skills_table[gsn_headbutt].beats) ); skill_practice( ch, gsn_headbutt ); if (!ch->fighting) set_fighting( ch, victim ); if(!victim->fighting) set_fighting( victim, ch ); if ( IS_NPC( ch ) || number_percent( ) < ( ch->pcdata->skl_lrn[gsn_headbutt] - 5) ) { damage( ch, victim, number_range( 1, ch->level ), gsn_headbutt, WEAR_NONE, DAM_BASH ); WAIT_STATE( victim, (skills_table[gsn_headbutt].beats / 2) ); } else damage( ch, victim, 0, gsn_headbutt, WEAR_NONE, DAM_BASH ); return; } /* * Charge by Veygoth * Usable to initiate combat */ void do_charge( CHAR_DATA *ch, char *argument ) { CHAR_DATA *victim; char arg [ MAX_INPUT_LENGTH ]; /* Check player's level and class, mobs can use this skill */ if ( !IS_SET( race_table[ch->race].race_abilities, RACE_CHARGE ) ) { send_to_char( "You can't do that.\n\r", ch ); return; } if ( !check_blind( ch ) ) return; one_argument( argument, arg ); if ( arg[0] != '\0' ) { if ( !( victim = get_char_room( ch, arg ) ) ) { send_to_char( "They aren't here.\n\r", ch ); return; } } else { send_to_char( "Charge who?\n\r", ch ); return; } WAIT_STATE( ch, number_fuzzy( skills_table[gsn_charge].beats )); skill_practice( ch, gsn_charge ); if (!ch->fighting) set_fighting( ch, victim ); if(!victim->fighting) set_fighting( victim, ch ); if ( IS_NPC( ch ) || number_percent( ) < ( ch->pcdata->skl_lrn[gsn_charge] - 5)) damage( ch, victim, number_range( 1, ch->level ), gsn_charge, WEAR_NONE, DAM_BASH ); else damage( ch, victim, 0, gsn_charge, WEAR_NONE, DAM_BASH ); return; } void do_dirt( CHAR_DATA *ch, char *argument ) { CHAR_DATA *victim; char arg [ MAX_INPUT_LENGTH ]; int percent; /* Don't allow charmed mobs to do this, check player's level */ if ( ( IS_NPC( ch ) && IS_AFFECTED( ch, AFF_CHARM ) ) || ( !IS_NPC( ch ) && ch->level < skills_table[gsn_dirt].skill_level[ch->class] ) ) { send_to_char( "You get your feet dirty.\n\r", ch ); return; } if ( !ch->fighting ) { send_to_char( "You aren't fighting anyone.\n\r", ch ); return; } if ( !check_blind( ch ) ) return; one_argument( argument, arg ); victim = ch->fighting; if ( arg[0] != '\0' ) { if ( !( victim = get_char_room( ch, arg ) ) ) { send_to_char( "They aren't here.\n\r", ch ); return; } } if ( victim == ch ) { send_to_char( "Very funny.\n\r", ch ); return; } if ( IS_AFFECTED( victim, AFF_BLIND ) ) { act( "$E's already been blinded.", ch, NULL, victim, TO_CHAR ); return; } percent = ( ch->level - victim->level ) * 2; percent += get_curr_dex( ch ) - 2 * get_curr_dex( victim ); // Why waste time listing sectors with no modifier? - Veygoth switch( ch->in_room->sector_type ) { case SECT_INSIDE: case SECT_ARCTIC: case SECT_SWAMP: percent -= 20; break; case SECT_CITY: case SECT_MOUNTAIN: percent -= 10; break; case SECT_PLANE_FIRE: case SECT_PLANE_AIR: case SECT_PLANE_WATER: case SECT_PLANE_ASTRAL: case SECT_PLANE_ETHEREAL: case SECT_UNDERWATER: case SECT_UNDERWATER_GROUND: case SECT_WATER_SWIM: case SECT_WATER_NOSWIM: case SECT_AIR: case SECT_OCEAN: case SECT_UNDERG_WATER: case SECT_UNDERG_WATER_NOSWIM: percent = 0; break; case SECT_FIELD: percent += 5; break; case SECT_DESERT: percent += 10; break; case SECT_PLANE_EARTH: percent += 15; default: break; } if ( percent <= 0 ) { send_to_char( "There isn't any dirt to kick.\n\r", ch ); return; } skill_practice( ch, gsn_dirt ); if ( percent > number_percent( ) ) { AFFECT_DATA af; act( "$n is blinded by the dirt in $s eyes!", victim, NULL, NULL, TO_ROOM ); act( "$n kicks dirt into your eyes!", ch, NULL, victim, TO_VICT ); damage( ch, victim, 5, gsn_dirt, WEAR_NONE, DAM_NONE ); send_to_char( "You can't see a thing!\n\r", victim ); af.skill = gsn_dirt; af.spell = 0; af.duration = 0; af.location = APPLY_HITROLL; af.modifier = -4; set_bitvector( &af, AFF_BLIND); affect_to_char( victim, &af ); } WAIT_STATE( ch, skills_table[gsn_dirt].beats ); return; } void do_whirlwind( CHAR_DATA *ch, char *argument ) { CHAR_DATA *pChar; CHAR_DATA *pChar_next; OBJ_DATA *wield; bool found = FALSE; if ( !IS_NPC( ch ) && ch->level < skills_table[gsn_whirlwind].skill_level[ch->class] ) { send_to_char( "You don't know how to do that...\n\r", ch ); return; } if ( !( wield = get_eq_char( ch, WEAR_HAND ) ) || wield->item_type != TYPE_WEAPON ) { send_to_char( "You need to wield a weapon first...\n\r", ch ); return; } act( "$n&n holds $p&n firmly, and starts spinning round...", ch, wield, NULL, TO_ROOM ); act( "You hold $p&n firmly, and start spinning round...", ch, wield, NULL, TO_CHAR ); pChar_next = NULL; for ( pChar = ch->in_room->people; pChar; pChar = pChar_next ) { pChar_next = pChar->next_in_room; if ( IS_NPC( pChar ) ) { found = TRUE; act( "$n&n turns towards YOU!", ch, NULL, pChar, TO_VICT ); one_hit( ch, pChar, gsn_whirlwind, WEAR_HAND ); // Added small amount of lag per target hit WAIT_STATE( ch, 4 ); } } if ( !found ) { act( "$n&n looks dizzy, and a tiny bit embarassed.", ch, NULL, NULL, TO_ROOM ); act( "You feel dizzy, and a tiny bit embarassed.", ch, NULL, NULL, TO_CHAR ); } WAIT_STATE( ch, skills_table[gsn_whirlwind].beats ); skill_practice( ch, gsn_whirlwind ); if ( !found && number_percent( ) < 25 ) { act( "$n&n loses $s balance and falls into a heap.", ch, NULL, NULL, TO_ROOM ); act( "You lose your balance and fall into a heap.", ch, NULL, NULL, TO_CHAR ); ch->position = POS_STUNNED; } return; } void do_disarm( CHAR_DATA *ch, char *argument ) { CHAR_DATA *victim; char arg [ MAX_INPUT_LENGTH ]; int percent; /* Don't allow charmed mobiles to do this, check player's level */ if ( ( IS_NPC( ch ) && IS_AFFECTED( ch, AFF_CHARM ) ) || ( !IS_NPC( ch ) && ch->level < skills_table[gsn_disarm].skill_level[ch->class] ) ) { send_to_char( "You don't know how to disarm opponents.\n\r", ch ); return; } if ( !get_eq_char( ch, WEAR_HAND ) && !get_eq_char( ch, WEAR_HAND_2 ) ) { send_to_char( "You must wield a weapon to disarm.\n\r", ch ); return; } if ( !ch->fighting ) { send_to_char( "You aren't fighting anyone.\n\r", ch ); return; } one_argument( argument, arg ); victim = ch->fighting; if ( arg[0] != '\0' ) if ( !( victim = get_char_room( ch, arg ) ) ) { send_to_char( "They aren't here.\n\r", ch ); return; } if ( victim->fighting != ch && ch->fighting != victim ) { act( "$E is not fighting you!", ch, NULL, victim, TO_CHAR ); return; } if ( !get_eq_char( victim, WEAR_HAND ) && !get_eq_char( victim, WEAR_HAND_2 ) ) { send_to_char( "Your opponent is not wielding a weapon.\n\r", ch ); return; } if( victim->level > ch->level + 3 ) { send_to_char( "They are much too clever for such a maneuver.\n\r", ch ); return; } WAIT_STATE( ch, skills_table[gsn_disarm].beats ); skill_practice( ch, gsn_disarm ); percent = number_percent( ) + victim->level - ch->level; if ( !get_eq_char( ch, WEAR_HAND ) ) percent *= 2; /* 1/2 as likely with only 2nd weapon */ if ( IS_NPC( ch ) || percent < ch->pcdata->skl_lrn[gsn_disarm] * 2 / 3 ) disarm( ch, victim ); else if( number_percent() < 50 ) { send_to_char( "They expertly counter your maneuver and dislodge your weapon.\n\r", ch ); disarm( victim, ch ); } else send_to_char( "You failed.\n\r", ch ); return; } void do_sla( CHAR_DATA *ch, char *argument ) { CHAR_DATA *rch; rch = get_char( ch ); if ( !authorized( rch, "slay" ) ) return; send_to_char( "If you want to SLAY, spell it out.\n\r", ch ); return; } void do_slay( CHAR_DATA *ch, char *argument ) { CHAR_DATA *victim; CHAR_DATA *rch; char arg1 [ MAX_INPUT_LENGTH ]; char arg2 [ MAX_INPUT_LENGTH ]; rch = get_char( ch ); if ( !authorized( rch, "slay" ) ) return; one_argument( argument, arg1 ); one_argument( argument, arg2 ); if ( arg1[0] == '\0' ) { send_to_char( "Slay whom?\n\r", ch ); return; } if ( !( victim = get_char_room( ch, arg1 ) ) ) { send_to_char( "They aren't here.\n\r", ch ); return; } if ( ch == victim ) { send_to_char( "Suicide is a mortal sin.\n\r", ch ); return; } if ( !IS_NPC( victim ) && victim->level >= ch->level ) { send_to_char( "You failed.\n\r", ch ); return; } if ( !str_cmp( arg2, "immolate" ) ) { act( "Your fireball turns $N into a blazing inferno.", ch, NULL, victim, TO_CHAR ); act( "$n releases a searing fireball in your direction.", ch, NULL, victim, TO_VICT ); act( "$n points at $N, who bursts into a flaming inferno.", ch, NULL, victim, TO_NOTVICT ); } else if ( !str_cmp( arg2, "pounce" ) && get_trust( ch ) >= L_DIR ) { act( "Leaping upon $N with bared fangs, you tear open $S throat and toss the corpse to the ground...", ch, NULL, victim, TO_CHAR ); act( "In a heartbeat, $n rips $s fangs through your throat! Your blood sprays and pours to the ground as your life ends...", ch, NULL, victim, TO_VICT ); act( "Leaping suddenly, $n sinks $s fangs into $N's throat. As blood sprays and gushes to the ground, $n tosses $N's dying body away.", ch, NULL, victim, TO_NOTVICT ); } else if ( !str_cmp( arg2, "shatter" ) ) { act( "You freeze $N with a glance and shatter the frozen corpse into tiny shards.", ch, NULL, victim, TO_CHAR ); act( "$n freezes you with a glance and shatters your frozen body into tiny shards.", ch, NULL, victim, TO_VICT ); act( "$n freezes $N with a glance and shatters the frozen body into tiny shards.", ch, NULL, victim, TO_NOTVICT ); } else if ( !str_cmp( arg2, "slit" ) && get_trust( ch ) >= L_DIR ) { act( "You calmly slit $N's throat.", ch, NULL, victim, TO_CHAR ); act( "$n reaches out with a clawed finger and calmly slits your throat.", ch, NULL, victim, TO_VICT ); act( "$n calmly slits $N's throat.", ch, NULL, victim, TO_NOTVICT ); } else if ( !str_cmp( arg2, "squeeze" ) ) { act( "You grasp $S head and squeeze it until it explodes!", ch, NULL, victim, TO_CHAR ); act( "$n grasps your head and squeezes until it explodes!", ch, NULL, victim, TO_VICT ); act( "$n grasps $N's head and squeezes until it explodes!", ch, NULL, victim, TO_NOTVICT ); } else { act( "You slay $M in cold blood!", ch, NULL, victim, TO_CHAR ); act( "$n slays you in cold blood!", ch, NULL, victim, TO_VICT ); act( "$n slays $N in cold blood!", ch, NULL, victim, TO_NOTVICT ); } raw_kill( ch, victim ); return; } /* This code is for PC's who polymorph into dragons. * Yeah I know this is specialized code, but this is fun. :) * Breathe on friend and enemy alike. * -Kahn */ void pc_breathe( CHAR_DATA *ch ) { CHAR_DATA *victim; CHAR_DATA *victim_next; int sn; send_to_char( "You feel the urge to burp!\n\r", ch ); act( "$n belches!", ch, NULL, NULL, TO_ROOM ); for ( victim = ch->in_room->people; victim; victim = victim_next ) { victim_next = victim->next_in_room; if ( victim->deleted ) continue; if ( victim == ch ) continue; if ( !IS_NPC( victim ) || ( !licensed( ch ) && str_cmp( race_table[victim->race].name, "Vampire" ) ) ) continue; sn = spell_lookup( "fire breath" ); (*spells_table[sn].spell_fun) ( sn, ch->level, ch, victim ); } return; } /* This code is for PC's who polymorph into harpies. * Yeah I know this is specialized code, but this is fun. :) * Scream into the ears of enemy and friend alike. * -Kahn */ void pc_screech( CHAR_DATA *ch ) { CHAR_DATA *victim; CHAR_DATA *victim_next; int sn; send_to_char( "You feel the urge to scream!\n\r", ch ); interpret( ch, "scream" ); for ( victim = ch->in_room->people; victim; victim = victim_next ) { victim_next = victim->next_in_room; if ( victim->deleted ) continue; if ( victim == ch ) continue; if ( !IS_NPC( victim ) || ( !licensed( ch ) && str_cmp( race_table[victim->race].name, "Vampire" ) ) ) continue; act( "Your ears pop from $n's scream. Ouch!", ch, NULL, victim, TO_VICT ); sn = spell_lookup( "agitation" ); (*spells_table[sn].spell_fun) ( sn, ch->level, ch, victim ); } return; } void pc_spit( CHAR_DATA *ch ) { CHAR_DATA *victim; CHAR_DATA *victim_next; send_to_char( "You feel the urge to spit!\n\r", ch ); act( "$n spews vitriol!", ch, NULL, NULL, TO_ROOM ); for ( victim = ch->in_room->people; victim; victim = victim_next ) { victim_next = victim->next_in_room; if ( victim->deleted ) continue; if ( victim == ch ) continue; if ( !IS_NPC( victim ) || ( !licensed( ch ) && str_cmp( race_table[victim->race].name, "Vampire" ) ) ) continue; act( "You are splattered with $n's vitriol. Ouch!", ch, NULL, victim, TO_VICT ); (*spells_table[spl_poison].spell_fun) ( spl_poison, ch->level, ch, victim ); damage( ch, victim, number_range( 1, ch->level ), gsn_poison_weapon, WEAR_NONE, DAM_POISON ); } return; } bool check_race_special( CHAR_DATA *ch ) { if ( !str_cmp( race_table[ch->race].name, "Dragon" ) ) { if ( number_percent( ) < ch->level ) { pc_breathe( ch ); return TRUE; } } if ( !str_cmp( race_table[ch->race].name, "Harpy" ) ) { if ( number_percent( ) < ch->level ) { pc_screech( ch ); return TRUE; } } if ( !str_cmp( race_table[ch->race].name, "Arachnid" ) || !str_cmp( race_table[ch->race].name, "Snake" ) ) { if ( number_percent( ) < ch->level ) { pc_spit( ch ); return TRUE; } } return FALSE; } bool licensed ( CHAR_DATA *ch ) { OBJ_DATA *obj; if ( !str_cmp( race_table[ch->race].name, "Vampire" ) ) return TRUE; /* * If already fighting then we just jump out. */ if ( ch->fighting ) return TRUE; for ( obj = ch->carrying; obj; obj = obj->next_content ) { if ( obj->pIndexData->vnum == OBJ_VNUM_LICENSE ) return TRUE; } return FALSE; } /* * 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; int i = 0; int sn = 0; char buf[ MAX_INPUT_LENGTH ]; for ( obj = ch->carrying; obj; obj = obj->next_content ) { if ( ( obj->item_type == TYPE_SCROLL || obj->item_type == TYPE_WAND || obj->item_type == TYPE_STAFF || obj->item_type == TYPE_PILL ) && number_range( 0, number ) == 0 ) { cobj = obj; number++; } } if ( !cobj ) return; /* * Modified so mobs don't cause damage to themselves and * don't aid players to help stop HEAVY player cheating -Zen */ switch( cobj->item_type ) { case TYPE_SCROLL: for ( i = 1; i < 5; i++ ) { sn = cobj->value[i]; if ( sn <= 0 || sn >= MAX_SPELL ) { extract_obj( cobj ); return; } if ( spells_table[sn].target == TAR_CHAR_DEFENSIVE ) { act( "$n discards a $p.", ch, cobj, NULL, TO_ROOM ); extract_obj( cobj ); return; } } break; case TYPE_POTION: case TYPE_PILL: for ( i = 1; i < 5; i++ ) { sn = cobj->value[i]; if ( sn <= 0 || sn >= MAX_SPELL ) { extract_obj( cobj ); return; } if ( spells_table[sn].target == TAR_CHAR_OFFENSIVE ) { act( "$n discards a $p.", ch, cobj, NULL, TO_ROOM ); extract_obj( cobj ); return; } } break; } switch( cobj->item_type ) { case TYPE_SCROLL: do_recite( ch, "scroll" ); break; case TYPE_WAND: if ( cobj->wear_loc == WEAR_HAND ) do_zap( ch, "" ); break; case TYPE_STAFF: if ( cobj->wear_loc == WEAR_HAND ) do_brandish( ch, "" ); break; case TYPE_POTION: do_quaff( ch, "potion" ); break; case TYPE_PILL: sprintf( buf, "%s", cobj->name ); do_eat( ch, buf ); break; } return; } /* * From TheIsles16 code. TheIsles by Locke <locke@lm.com> */ bool hit_suck_disarm( CHAR_DATA *ch, CHAR_DATA *victim, int hit, int dam ) { OBJ_DATA *obj; if ( number_percent( ) <= ch->level ) { if ( ( race_table[ch->race].size - race_table[victim->race].size ) < -2 ) return FALSE; if ( !( obj = get_eq_char( victim, WEAR_HAND ) ) ) if ( !( obj = get_eq_char( victim, WEAR_HAND_2 ) ) ) return FALSE; if ( IS_OBJ_STAT( obj, ITEM_NODROP ) ) return FALSE; if ( !get_eq_char( ch, WEAR_HAND ) && !get_eq_char( ch, WEAR_HAND_2 ) && number_bits( 1 ) == 0 ) return FALSE; act( "You suck $N's weapon right out of $S hand!", ch, NULL, victim, TO_CHAR ); act( "$n sucks your weapon right out of your hand!", ch, NULL, victim, TO_VICT ); act( "$n sucks $N's weapon right out of $S hand!", ch, NULL, victim, TO_NOTVICT ); obj_from_char( obj ); obj_to_char( obj, ch ); return TRUE; } return FALSE; } /* * From TheIsles16 code. TheIsles by Locke <locke@lm.com> */ bool hit_vorpal( CHAR_DATA *ch, CHAR_DATA *victim, int hit, int dam ) { OBJ_DATA *obj; char *name; char buf [MAX_STRING_LENGTH]; dam *= 100; if ( victim->hit > 0 ) dam /= victim->hit; if ( hit >= 17 && dam >= 40 ) /* dam >= 40 is MUTILATES */ /* * Cut victim's head off, generate a corpse, do not * give experience for kill. Needless to say, * it would be bad to be a target of this weapon! */ { act( "You slice $N's head clean off!", ch, 0, victim, TO_CHAR ); act( "$n slices your head clean off!", ch, 0, victim, TO_VICT ); act( "$n slices $N's head clean off!", ch, 0, victim, TO_NOTVICT ); act( "$n's severed head plops on the ground.", victim, 0, 0, TO_ROOM ); stop_fighting( victim, TRUE ); name = IS_NPC( victim ) ? victim->short_descr : victim->name; obj = create_object( get_obj_index( OBJ_VNUM_SEVERED_HEAD ), 0 ); obj->timer = number_range( 4, 7 ); 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( victim, AFF_POISON ) ) obj->value[3] = 1; obj_to_room( obj, ch->in_room ); raw_kill( ch, victim ); return TRUE; } return FALSE; } /* * From TheIsles16 code. TheIsles by Locke <locke@lm.com> * Heavily modified by Zen. */ void do_shoot( CHAR_DATA *ch, char *argument ) { extern char * const dir_name [ ]; extern char * const dir_rev [ ]; OBJ_DATA *obj; OBJ_DATA *newobj; OBJ_INDEX_DATA *pObjIndex; EXIT_DATA *pexit; ROOM_INDEX_DATA *to_room; CHAR_DATA *mob; char *msg; char arg2 [ MAX_INPUT_LENGTH ]; char arg3 [ MAX_INPUT_LENGTH ]; char buf [ MAX_STRING_LENGTH ]; int range = 3; int dir; int n; if ( IS_NPC( ch ) && IS_SET( ch->act, ACT_PET ) ) return; argument = one_argument( argument, arg2 ); argument = one_argument( argument, arg3 ); dir = -1; for ( dir = 0; dir < MAX_DIR; dir++ ) { if ( !str_prefix( arg2, dir_name[dir] ) ) break; } if ( arg2[0] == '\0' || dir < 0 ) { send_to_char( "Syntax: shoot <direction> [range]\n\r", ch ); return; } if ( !( obj = get_eq_char( ch, WEAR_HAND ) ) ) { send_to_char( "You aren't using a missile weapon.\n\r", ch ); return; } if ( obj->item_type != TYPE_WEAPON || obj->value[1] < 0 || obj->value[2] < 0 ) { send_to_char( "You can't do that.\n\r", ch); return; } if ( obj->value[0] == 0 ) { send_to_char( "Its payload is empty.\n\r", ch); return; } if ( obj->value[3] == RNG_CATAPULT ) { if ( arg3[0] != '\0' ) range = atoi( arg3 ); if ( range <= 0 ) { send_to_char( "Try shooting it away from you.\n\r", ch ); return; } if ( range > obj->value[4] ) { if ( obj->value[4] > 0 ) range = obj->value[4]; else range = 1; } } switch ( obj->value[3] ) { default: msg = "You pull back the string on %s and fire %s!\n\r"; break; case RNG_BOW: msg = "You pull back the string on %s and fire %s!\n\r"; break; case RNG_CROSSBOW: msg = "You fire %s bolt %s!\n\r"; break; case RNG_CATAPULT: msg = "You crank back %s, and release it %sward!\n\r"; break; } sprintf( buf, msg, obj->short_descr, dir_name[dir] ); send_to_char( buf, ch ); switch ( obj->value[3] ) { default: msg = "You pull back the string on %s and fire %s!"; break; case RNG_BOW: msg = "%s pulls back the string on %s and fires %s!"; break; case RNG_CROSSBOW: msg = "%s fires %s bolt %s!"; break; case RNG_CATAPULT: msg = "%s cranks back %s, and releases it %sward!"; break; } sprintf( buf, msg, ( IS_NPC( ch ) ? ch->short_descr : ch->name ), obj->short_descr, dir_name[dir] ); act( buf, ch, NULL, NULL, TO_ROOM ); if ( !(pObjIndex = get_obj_index( obj->value[0] ) ) ) { bug( "Ranged weapon (vnum %d) has invalid ammunition.", 0 ); return; } if ( !ch->in_room->exit[dir] || IS_SET( ch->in_room->exit[dir]->exit_info, EX_CLOSED ) ) { newobj = create_object( get_obj_index( obj->value[0] ), 0 ); obj->value[0] = 0; switch ( obj->value[3] ) { default: msg = "%s impales itself into the ground."; break; case RNG_BOW: msg = "%s impales itself into the ground."; break; case RNG_CROSSBOW: msg = "%s falls to the ground."; break; case RNG_CATAPULT: msg = "%s crashes to the ground."; break; } sprintf( buf, msg, newobj->short_descr ); act( capitalize( buf ), ch, NULL, NULL, TO_ROOM ); act( capitalize( buf ), ch, NULL, NULL, TO_CHAR ); obj_to_room( newobj, ch->in_room ); return; } newobj = create_object( pObjIndex, 0 ); obj->value[0] = 0; for ( n = 1, pexit = ch->in_room->exit[dir]; pexit && n <= (range+1); n++ ) { if ( IS_SET( pexit->exit_info, EX_CLOSED ) || IS_SET( pexit->exit_info, EX_JAMMED ) ) { switch ( obj->value[3] ) { default: msg = "%s impales itself into the ground."; break; case RNG_BOW: msg = "%s impales itself into the ground."; break; case RNG_CROSSBOW: msg = "%s falls to the ground."; break; case RNG_CATAPULT: msg = "%s crashes to the ground."; break; } sprintf( buf, msg, newobj->short_descr ); if ( pexit->to_room && pexit->to_room->people ) { act( buf, pexit->to_room->people, NULL, NULL, TO_ROOM ); act( buf, pexit->to_room->people, NULL, NULL, TO_CHAR ); } obj_to_room( newobj, pexit->to_room ); act( buf, ch, NULL, NULL, TO_ROOM ); return; } to_room = pexit->to_room; if ( to_room->people ) { for ( mob = to_room->people; mob; mob = mob->next_in_room ) { if ( mob->deleted ) continue; if ( ( mob->position == POS_STANDING || mob->position == POS_FIGHTING ) && ( check_dodge( ch, mob ) || ch->level > number_percent( ) ) ) { int dam; sprintf( buf, "%s streaks into %s from %s!", newobj->short_descr, ( IS_NPC( mob ) ? mob->short_descr : mob->name ), dir_rev[dir] ); act( buf, mob, NULL, NULL, TO_ROOM ); n = range; sprintf( buf, "%s streaks into you from %s!", newobj->short_descr, dir_rev[dir] ); act( buf, mob, NULL, NULL, TO_CHAR ); dam = number_range( obj->value[1], obj->value[2] ); dam += newobj->value[1]; /* * Weapon Proficiencies. */ if ( !IS_NPC( ch ) && ch->pcdata->skl_lrn[gsn_shot] > 0 ) dam += dam * ch->pcdata->skl_lrn[gsn_shot] / 150; extract_obj( newobj ); damage( ch, mob, dam, TYPE_UNDEFINED, WEAR_NONE, DAM_NONE ); stop_fighting( mob, FALSE ); if ( IS_NPC( mob ) ) { start_hating( mob, ch ); start_hunting( mob, ch ); } if ( mob->position > POS_STUNNED && obj->value[3] == RNG_CATAPULT ) { act ( "The impact puts $n to sleep!", mob, NULL, NULL, TO_ROOM ); act ( "The impact puts you to sleep... ZzZzZ", mob, NULL, NULL, TO_CHAR ); mob->position = POS_SLEEPING; } return; } } } if ( to_room->people ) { if ( n <= range ) { sprintf( buf, "%s streaks through the air and continues %s.", newobj->short_descr, dir_name[dir] ); act( buf, to_room->people, NULL, NULL, TO_ROOM ); act( buf, to_room->people, NULL, NULL, TO_CHAR ); } else { sprintf( buf, "%s zooms in and lands on the ground.", newobj->short_descr ); act( buf, to_room->people, NULL, NULL, TO_ROOM ); act( buf, to_room->people, NULL, NULL, TO_CHAR ); obj_to_room( newobj, to_room ); } } pexit = to_room->exit[dir]; } return; } /* * From TheIsles16 code. TheIsles by Locke <locke@lm.com> * Heavily modified by Zen. */ void do_reload( CHAR_DATA *ch, char *argument ) { OBJ_DATA *weapon; OBJ_DATA *ammo; OBJ_DATA *ammo_next; char buf [MAX_STRING_LENGTH]; if ( IS_NPC( ch ) && IS_SET( ch->act, ACT_PET ) ) return; if ( !( weapon = get_eq_char( ch, WEAR_HAND ) ) ) { send_to_char( "I don't see that weapon here.\n\r", ch ); return; } if ( weapon->item_type != TYPE_WEAPON ) { send_to_char( "That is not a missile weapon.\n\r", ch ); return; } for ( ammo = ch->carrying; ammo; ammo = ammo_next ) { ammo_next = ammo->next_content; if ( ammo->deleted ) continue; if ( ammo->item_type == TYPE_WEAPON && ammo->value[0] == weapon->value[3] ) break; } if ( !ammo ) { send_to_char( "You do not have ammo for this weapon.\n\r", ch ); return; } if ( weapon->value[0] != 0 ) { sprintf( buf, "%s is already loaded.\n\r", weapon->short_descr ); send_to_char( buf, ch ); return; } weapon->value[0] = ammo->pIndexData->vnum; act( "You get $p&n.", ch, ammo, weapon, TO_CHAR ); act( "$n&n get $p&n.", ch, ammo, weapon, TO_ROOM ); act( "You load $P&n with $p&n.", ch, ammo, weapon, TO_CHAR ); act( "$n&n loads $P&n with $p&n.", ch, ammo, weapon, TO_ROOM ); extract_obj( ammo ); return; } // Returns the percent of experience player should get for kill // based on their trophy and increases the amount of kills on // the player's trophy - Veygoth int check_trophy( CHAR_DATA *ch, CHAR_DATA *victim, int members ) { int percent; int count; int vnum; int maxlev; bool found = FALSE; bool found2 = FALSE; maxlev = ch->level; if( !(vnum = victim->pIndexData->vnum) ) { bug( "Mobile without vnum in check_trophy!", 0 ); return 100; } for( count = 0; count < maxlev; count++ ) { if( ch->pcdata->trophy[count].vnum == vnum ) { found = TRUE; break; } } if( !found ) { for( count = 0; count < maxlev; count++ ) { if( ch->pcdata->trophy[count].vnum == 0 || ch->pcdata->trophy[count].number == 0 ) { ch->pcdata->trophy[count].vnum = vnum; ch->pcdata->trophy[count].number = ( 100 / members); found2 = TRUE; break; } } // Roll the oldest item off of trophy if( !found2 ) { for( count = 0; count < (maxlev - 1); count++ ) { ch->pcdata->trophy[count].vnum = ch->pcdata->trophy[count + 1].vnum; ch->pcdata->trophy[count].vnum = ch->pcdata->trophy[count + 1].number; } ch->pcdata->trophy[maxlev].vnum = vnum; ch->pcdata->trophy[maxlev].number = ( 100 / members ); } return 100; } percent = 100 - ( ch->pcdata->trophy[count].number / 40 ); if( percent < 1 ) percent = 1; if( percent <= 50 ) send_to_char( "What's the point anymore? It just doesen't seem worth it.\n\r", ch ); else if( percent <= 80 ) send_to_char( "This is starting to get dull.\n\r", ch ); else if( percent <= 90 ) send_to_char( "You are beginning to learn your victim's weak spots.\n\r", ch ); ch->pcdata->trophy[count].number += ( 100 / members ); return percent; } void do_disengage( CHAR_DATA *ch, char *argument ) { if( ch->position != POS_FIGHTING && !ch->fighting ) { send_to_char( "You're not fighting anyone!\n\r", ch ); return; } if( ch->fighting->fighting && ch->fighting->fighting == ch ) { send_to_char( "You're a little busy getting beat on at the moment.\n\r", ch ); return; } send_to_char( "You disengage from the fight!\n\r", ch ); stop_fighting( ch, FALSE ); return; } void do_assist( CHAR_DATA *ch, char *argument ) { CHAR_DATA *victim; char arg [ MAX_INPUT_LENGTH ]; if ( !check_blind( ch ) ) return; one_argument( argument, arg ); if( ch->fighting ) { send_to_char( "You're a bit busy at the moment.\n\r", ch ); return; } if ( arg[0] != '\0' ) { if ( !( victim = get_char_room( ch, arg ) ) ) { send_to_char( "They aren't here.\n\r", ch ); return; } } else { send_to_char( "Assist who?\n\r", ch ); return; } if( victim == ch ) { send_to_char( "You're too busy assisting yourself to assist yourself.\n\r", ch ); return; } if( !victim->fighting ) { send_to_char( "They're not fighting anyone.\n\r", ch ); return; } if( victim->fighting == ch ) { send_to_char( "Assist!? They're fighting YOU, fool!\n\r", ch ); return; } act( "You assist $N&n heroically.", ch, NULL, victim, TO_CHAR ); act( "$n&n assists you heroically.", ch, NULL, victim, TO_VICT ); act( "$n&n assists $N&n heroically.", ch, NULL, victim, TO_NOTVICT ); one_hit( ch, victim->fighting, TYPE_UNDEFINED, WEAR_HAND ); return; } void do_aware( CHAR_DATA *ch, char *argument ) { AFFECT_DATA af; if( IS_NPC( ch )) return; if( ch->level < skills_table[gsn_aware].skill_level[ch->class] ) { send_to_char( "Your general obliviousness prevents your use of this skill.\n\r", ch ); return; } if( IS_AFFECTED( ch, AFF_SKL_AWARE )) { send_to_char( "You are already about as tense as you can get.\n\r", ch ); return; } send_to_char( "You try to become more aware of your surroundings.\n\r", ch ); skill_practice( ch, gsn_aware ); af.skill = gsn_aware; af.spell = 0; af.duration = ( ch->level / 3 ) + 3; af.location = 0; af.modifier = 0; set_bitvector( &af, AFF_SKL_AWARE ); affect_to_char( ch, &af ); return; }