/****************************************************************************
* [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame | \\._.// *
* -----------------------------------------------------------| (0...0) *
* SMAUG 1.0 (C) 1994, 1995, 1996 by Derek Snider | ).:.( *
* -----------------------------------------------------------| {o o} *
* SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus, | / ' ' \ *
* Scryn, Rennard, Swordbearer, Gorog, Grishnakh and Tricops |~'~.VxvxV.~'~*
* ------------------------------------------------------------------------ *
* Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. *
* ------------------------------------------------------------------------ *
* Battle & death module *
****************************************************************************/
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "mud.h"
extern char lastplayercmd[MAX_INPUT_LENGTH];
extern CHAR_DATA * gch_prev;
/*
* Local functions.
*/
void dam_message args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dam,
int dt ) );
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 ) );
int align_compute args( ( CHAR_DATA *gch, CHAR_DATA *victim ) );
ch_ret one_hit args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dt ) );
int obj_hitroll args( ( OBJ_DATA *obj ) );
bool dual_flip = FALSE;
/*
* Check to see if weapon is poisoned.
*/
bool is_wielding_poisoned( CHAR_DATA *ch )
{
OBJ_DATA *obj;
if ( ( obj = get_eq_char( ch, WEAR_WIELD ) )
&& (IS_SET( obj->extra_flags, 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 )
{
STRFREE( ch->hunting->name );
DISPOSE( ch->hunting );
ch->hunting = NULL;
}
return;
}
void stop_hating( CHAR_DATA *ch )
{
if ( ch->hating )
{
STRFREE( ch->hating->name );
DISPOSE( ch->hating );
ch->hating = NULL;
}
return;
}
void stop_fearing( CHAR_DATA *ch )
{
if ( ch->fearing )
{
STRFREE( ch->fearing->name );
DISPOSE( ch->fearing );
ch->fearing = NULL;
}
return;
}
void start_hunting( CHAR_DATA *ch, CHAR_DATA *victim )
{
if ( ch->hunting )
stop_hunting( ch );
CREATE( ch->hunting, HHF_DATA, 1 );
ch->hunting->name = QUICKLINK( victim->name );
ch->hunting->who = victim;
return;
}
void start_hating( CHAR_DATA *ch, CHAR_DATA *victim )
{
if ( ch->hating )
stop_hating( ch );
CREATE( ch->hating, HHF_DATA, 1 );
ch->hating->name = QUICKLINK( victim->name );
ch->hating->who = victim;
return;
}
void start_fearing( CHAR_DATA *ch, CHAR_DATA *victim )
{
if ( ch->fearing )
stop_fearing( ch );
CREATE( ch->fearing, HHF_DATA, 1 );
ch->fearing->name = QUICKLINK( victim->name );
ch->fearing->who = victim;
return;
}
/*
* Get the current armor class for a vampire based on time of day
*/
sh_int VAMP_AC( CHAR_DATA * ch )
{
if ( IS_VAMPIRE( ch ) || IS_OUTSIDE( ch ) )
{
switch(weather_info.sunlight)
{
case SUN_DARK:
return -8;
case SUN_RISE:
return 5;
case SUN_LIGHT:
return 10;
case SUN_SET:
return 2;
default:
return 0;
}
}
else
return 0;
}
int max_fight( CHAR_DATA *ch )
{
return 8;
}
/*
* Control the fights going on.
* Called periodically by update_handler.
* Many hours spent fixing bugs in here by Thoric, as noted by residual
* debugging checks. If you never get any of these error messages again
* in your logs... then you can comment out some of the checks without
* worry.
*/
void violence_update( void )
{
char buf[MAX_STRING_LENGTH];
CHAR_DATA *ch;
CHAR_DATA *lst_ch;
CHAR_DATA *victim;
CHAR_DATA *rch, *rch_next;
AFFECT_DATA *paf, *paf_next;
TIMER *timer, *timer_next;
ch_ret retcode;
int x, attacktype, cnt;
SKILLTYPE *skill;
lst_ch = NULL;
for ( ch = last_char; ch; lst_ch = ch, ch = gch_prev )
{
set_cur_char( ch );
if ( ch == first_char && ch->prev )
{
bug( "ERROR: first_char->prev != NULL, fixing...", 0 );
ch->prev = NULL;
}
gch_prev = ch->prev;
if ( gch_prev && gch_prev->next != ch )
{
sprintf( buf, "FATAL: violence_update: %s->prev->next doesn't point to ch.",
ch->name );
bug( buf, 0 );
bug( "Short-cutting here", 0 );
ch->prev = NULL;
gch_prev = NULL;
do_shout( ch, "Thoric says, 'Prepare for the worst!'" );
}
/*
* See if we got a pointer to someone who recently died...
* if so, either the pointer is bad... or it's a player who
* "died", and is back at the healer...
* Since he/she's in the char_list, it's likely to be the later...
* and should not already be in another fight already
*/
if ( char_died(ch) )
continue;
/*
* See if we got a pointer to some bad looking data...
*/
if ( !ch->in_room || !ch->name )
{
log_string( "violence_update: bad ch record! (Shortcutting.)" );
sprintf( buf, "ch: %d ch->in_room: %d ch->prev: %d ch->next: %d",
(int) ch, (int) ch->in_room, (int) ch->prev, (int) ch->next );
log_string( buf );
log_string( lastplayercmd );
if ( lst_ch )
sprintf( buf, "lst_ch: %d lst_ch->prev: %d lst_ch->next: %d",
(int) lst_ch, (int) lst_ch->prev, (int) lst_ch->next );
else
strcpy( buf, "lst_ch: NULL" );
log_string( buf );
gch_prev = NULL;
continue;
}
/*
* Experience gained during battle deceases as battle drags on
*/
if ( ch->fighting )
if ( (++ch->fighting->duration % 24) == 0 )
ch->fighting->xp = ((ch->fighting->xp * 9) / 10);
for ( timer = ch->first_timer; timer; timer = timer_next )
{
timer_next = timer->next;
if ( --timer->count <= 0 )
{
if ( timer->type == TIMER_DO_FUN )
{
int tempsub;
tempsub = ch->substate;
ch->substate = timer->value;
(timer->do_fun)( ch, "" );
if ( char_died(ch) )
break;
ch->substate = tempsub;
}
extract_timer( ch, timer );
}
}
if ( char_died(ch) )
continue;
/*
* We need spells that have shorter durations than an hour.
* So a melee round sounds good to me... -Thoric
*/
for ( paf = ch->first_affect; paf; paf = paf_next )
{
paf_next = paf->next;
if ( paf->duration > 0 )
paf->duration--;
else
if ( paf->duration < 0 )
;
else
{
if ( !paf_next
|| paf_next->type != paf->type
|| paf_next->duration > 0 )
{
skill = get_skilltype(paf->type);
if ( paf->type > 0 && skill && skill->msg_off )
{
set_char_color( AT_WEAROFF, ch );
send_to_char( skill->msg_off, ch );
send_to_char( "\n\r", ch );
}
}
if (paf->type == gsn_possess)
{
ch->desc->character = ch->desc->original;
ch->desc->original = NULL;
ch->desc->character->desc = ch->desc;
ch->desc->character->switched = NULL;
ch->desc = NULL;
}
affect_remove( ch, paf );
}
}
if ( ( victim = who_fighting( ch ) ) == NULL
|| IS_AFFECTED( ch, AFF_PARALYSIS ) )
continue;
retcode = rNONE;
if ( IS_SET(ch->in_room->room_flags, ROOM_SAFE ) )
{
sprintf( buf, "violence_update: %s fighting %s in a SAFE room.",
ch->name, victim->name );
log_string( buf );
stop_fighting( ch, TRUE );
}
else
if ( IS_AWAKE(ch) && ch->in_room == victim->in_room )
retcode = multi_hit( ch, victim, TYPE_UNDEFINED );
else
stop_fighting( ch, FALSE );
if ( char_died(ch) )
continue;
if ( retcode == rCHAR_DIED
|| ( victim = who_fighting( ch ) ) == NULL )
continue;
/*
* Mob triggers
*/
rprog_rfight_trigger( ch );
if ( char_died(ch) )
continue;
mprog_hitprcnt_trigger( ch, victim );
if ( char_died(ch) )
continue;
mprog_fight_trigger( ch, victim );
if ( char_died(ch) )
continue;
/*
* NPC special attack flags -Thoric
*/
if ( IS_NPC(ch) )
{
cnt = 0;
if ( ch->attacks )
for ( ;; )
{
if ( cnt++ > 10 )
{
attacktype = 0;
break;
}
x = number_range( 7, 31 );
attacktype = 1 << x;
if ( IS_SET( ch->attacks, attacktype ) )
break;
}
if ( 30 + (ch->level/4) < number_percent( ) )
attacktype = 0;
switch( attacktype )
{
case ATCK_BASH:
do_bash( ch, "" );
retcode = global_retcode;
break;
case ATCK_STUN:
do_stun( ch, "" );
retcode = global_retcode;
break;
case ATCK_GOUGE:
do_gouge( ch, "" );
retcode = global_retcode;
break;
case ATCK_FEED:
do_gouge( ch, "" );
retcode = global_retcode;
break;
case ATCK_DRAIN:
retcode = spell_energy_drain( skill_lookup( "energy drain" ), ch->level, ch, victim );
break;
case ATCK_FIREBREATH:
retcode = spell_fire_breath( skill_lookup( "fire breath" ), ch->level, ch, victim );
break;
case ATCK_FROSTBREATH:
retcode = spell_frost_breath( skill_lookup( "frost breath" ), ch->level, ch, victim );
break;
case ATCK_ACIDBREATH:
retcode = spell_acid_breath( skill_lookup( "acid breath" ), ch->level, ch, victim );
break;
case ATCK_LIGHTNBREATH:
retcode = spell_lightning_breath( skill_lookup( "lightning breath" ), ch->level, ch, victim );
break;
case ATCK_GASBREATH:
retcode = spell_gas_breath( skill_lookup( "gas breath" ), ch->level, ch, victim );
break;
case ATCK_SPIRALBLAST:
retcode = spell_spiral_blast( skill_lookup( "spiral blast" ),
ch->level, ch, victim );
break;
case ATCK_POISON:
retcode = spell_poison( gsn_poison, ch->level, ch, victim );
break;
case ATCK_NASTYPOISON:
/*
retcode = spell_nasty_poison( skill_lookup( "nasty poison" ), ch->level, ch, victim );
*/
break;
case ATCK_GAZE:
/*
retcode = spell_gaze( skill_lookup( "gaze" ), ch->level, ch, victim );
*/
break;
case ATCK_BLINDNESS:
retcode = spell_blindness( gsn_blindness, ch->level, ch, victim );
break;
case ATCK_CAUSESERIOUS:
retcode = spell_cause_serious( skill_lookup( "cause serious" ), ch->level, ch, victim );
break;
case ATCK_EARTHQUAKE:
retcode = spell_earthquake( skill_lookup( "earthquake" ), ch->level, ch, victim );
break;
case ATCK_CAUSECRITICAL:
retcode = spell_cause_critical( skill_lookup( "cause critical" ), ch->level, ch, victim );
break;
case ATCK_CURSE:
retcode = spell_curse( skill_lookup( "curse" ), ch->level, ch, victim );
break;
case ATCK_FLAMESTRIKE:
retcode = spell_flamestrike( skill_lookup( "flamestrike" ), ch->level, ch, victim );
break;
case ATCK_HARM:
retcode = spell_harm( skill_lookup( "harm" ), ch->level, ch, victim );
break;
case ATCK_FIREBALL:
retcode = spell_fireball( skill_lookup( "fireball" ), ch->level, ch, victim );
break;
case ATCK_COLORSPRAY:
retcode = spell_colour_spray( skill_lookup( "colour spray" ), ch->level, ch, victim );
break;
case ATCK_WEAKEN:
retcode = spell_weaken( skill_lookup( "weaken" ), ch->level, ch, victim );
break;
}
if ( retcode == rCHAR_DIED || (char_died(ch)) )
continue;
/*
* NPC special defense flags -Thoric
*/
cnt = 0;
if ( ch->defenses )
for ( ;; )
{
if ( cnt++ > 10 )
{
attacktype = 0;
break;
}
x = number_range( 2, 18 );
attacktype = 1 << x;
if ( IS_SET( ch->defenses, attacktype ) )
break;
}
if ( 50 + (ch->level/4) < number_percent( ) )
attacktype = 0;
switch( attacktype )
{
case DFND_CURELIGHT:
act( AT_MAGIC, "$n mutters a few incantations...and looks a little better.", ch, NULL, NULL, TO_ROOM );
retcode = spell_smaug( skill_lookup( "cure light" ), ch->level, ch, ch );
break;
case DFND_CURESERIOUS:
act( AT_MAGIC, "$n mutters a few incantations...and looks a bit better.", ch, NULL, NULL, TO_ROOM );
retcode = spell_smaug( skill_lookup( "cure serious" ), ch->level, ch, ch );
break;
case DFND_CURECRITICAL:
act( AT_MAGIC, "$n mutters a few incantations...and looks a bit healthier.", ch, NULL, NULL, TO_ROOM );
retcode = spell_smaug( skill_lookup( "cure critical" ), ch->level, ch, ch );
break;
case DFND_DISPELMAGIC:
act( AT_MAGIC, "$n mutters a few incantations...and waves $s arms about.", ch, NULL, NULL, TO_ROOM );
retcode = spell_dispel_magic( skill_lookup( "dispel magic" ), ch->level, ch, victim );
break;
case DFND_DISPELEVIL:
act( AT_MAGIC, "$n mutters a few incantations...and waves $s arms about.", ch, NULL, NULL, TO_ROOM );
retcode = spell_dispel_evil( skill_lookup( "dispel evil" ), ch->level, ch, victim );
break;
case DFND_SANCTUARY:
if ( !IS_AFFECTED(victim, AFF_SANCTUARY) )
{
act( AT_MAGIC, "$n mutters a few incantations...", ch, NULL, NULL, TO_ROOM );
retcode = spell_smaug( skill_lookup( "sanctuary" ), ch->level, ch, ch );
}
else
retcode = rNONE;
break;
}
if ( retcode == rCHAR_DIED || (char_died(ch)) )
continue;
}
/*
* Fun for the whole family!
*/
for ( rch = ch->in_room->first_person; rch; rch = rch_next )
{
rch_next = rch->next_in_room;
if ( IS_AWAKE(rch) && !rch->fighting )
{
/*
* PC's auto-assist others in their group.
*/
if ( !IS_NPC(ch) || IS_AFFECTED(ch, AFF_CHARM) )
{
if ( ( !IS_NPC(rch) || IS_AFFECTED(rch, AFF_CHARM) )
&& is_same_group(ch, rch) )
multi_hit( rch, victim, TYPE_UNDEFINED );
continue;
}
/*
* NPC's assist NPC's of same type or 12.5% chance regardless.
*/
if ( IS_NPC(rch) && !IS_AFFECTED(rch, AFF_CHARM)
&& !IS_SET(rch->act, ACT_NOASSIST) )
{
if ( char_died(ch) )
break;
if ( rch->pIndexData == ch->pIndexData
|| number_bits( 3 ) == 0 )
{
CHAR_DATA *vch;
CHAR_DATA *target;
int number;
target = NULL;
number = 0; for ( vch = ch->in_room->first_person; vch; vch = vch->next )
{
if ( can_see( rch, vch )
&& is_same_group( vch, victim )
&& number_range( 0, number ) == 0 )
{
target = vch;
number++;
}
}
if ( target )
multi_hit( rch, target, TYPE_UNDEFINED );
}
}
}
}
}
return;
}
/*
* Do one group of attacks.
*/
ch_ret multi_hit( CHAR_DATA *ch, CHAR_DATA *victim, int dt )
{
int chance;
int dual_bonus;
ch_ret retcode;
/* add timer if player is attacking another player */
if ( !IS_NPC(ch) && !IS_NPC(victim) )
add_timer( ch, TIMER_RECENTFIGHT, 20, NULL, 0 );
if ( !IS_NPC(ch) && IS_SET( ch->act, PLR_NICE ) && !IS_NPC( victim ) )
return rNONE;
if ( (retcode = one_hit( ch, victim, dt )) != rNONE )
return retcode;
if ( who_fighting( ch ) != victim || dt == gsn_backstab || dt == gsn_circle)
return rNONE;
/* Very high chance of hitting compared to chance of going berserk */
/* 40% or higher is always hit.. don't learn anything here though. */
/* -- Altrag */
chance = IS_NPC(ch) ? 100 : (ch->pcdata->learned[gsn_berserk]*5/2);
if ( IS_AFFECTED(ch, AFF_BERSERK) && number_percent() < chance )
if ( (retcode = one_hit( ch, victim, dt )) != rNONE ||
who_fighting( ch ) != victim )
return retcode;
if ( get_eq_char( ch, WEAR_DUAL_WIELD ) )
{
dual_bonus = IS_NPC(ch) ? (ch->level / 10) : (ch->pcdata->learned[gsn_dual_wield] / 10);
chance = IS_NPC(ch) ? ch->level : ch->pcdata->learned[gsn_dual_wield];
if ( number_percent( ) < chance )
{
learn_from_success( ch, gsn_dual_wield );
retcode = one_hit( ch, victim, dt );
if ( retcode != rNONE || who_fighting( ch ) != victim )
return retcode;
}
else
learn_from_failure( ch, gsn_dual_wield );
}
else
dual_bonus = 0;
if ( ch->move < 10 )
dual_bonus = -20;
/*
* NPC predetermined number of attacks -Thoric
*/
if ( IS_NPC(ch) && ch->numattacks > 0 )
{
for ( chance = 0; chance <= ch->numattacks; chance++ )
{
retcode = one_hit( ch, victim, dt );
if ( retcode != rNONE || who_fighting( ch ) != victim )
return retcode;
}
return retcode;
}
chance = IS_NPC(ch) ? ch->level
: (int) ((ch->pcdata->learned[gsn_second_attack]+dual_bonus)/1.5);
if ( number_percent( ) < chance )
{
learn_from_success( ch, gsn_second_attack );
retcode = one_hit( ch, victim, dt );
if ( retcode != rNONE || who_fighting( ch ) != victim )
return retcode;
}
else
learn_from_failure( ch, gsn_second_attack );
chance = IS_NPC(ch) ? ch->level
: (int) ((ch->pcdata->learned[gsn_third_attack]+(dual_bonus*1.5))/2);
if ( number_percent( ) < chance )
{
learn_from_success( ch, gsn_third_attack );
retcode = one_hit( ch, victim, dt );
if ( retcode != rNONE || who_fighting( ch ) != victim )
return retcode;
}
else
learn_from_failure( ch, gsn_third_attack );
chance = IS_NPC(ch) ? ch->level
: (int) ((ch->pcdata->learned[gsn_fourth_attack]+(dual_bonus*2))/3);
if ( number_percent( ) < chance )
{
learn_from_success( ch, gsn_fourth_attack );
retcode = one_hit( ch, victim, dt );
if ( retcode != rNONE || who_fighting( ch ) != victim )
return retcode;
}
else
learn_from_failure( ch, gsn_fourth_attack );
chance = IS_NPC(ch) ? ch->level
: (int) ((ch->pcdata->learned[gsn_fifth_attack]+(dual_bonus*3))/4);
if ( number_percent( ) < chance )
{
learn_from_success( ch, gsn_fifth_attack );
retcode = one_hit( ch, victim, dt );
if ( retcode != rNONE || who_fighting( ch ) != victim )
return retcode;
}
else
learn_from_failure( ch, gsn_fifth_attack );
retcode = rNONE;
chance = IS_NPC(ch) ? (int) (ch->level / 2) : 0;
if ( number_percent( ) < chance )
retcode = one_hit( ch, victim, dt );
if ( retcode == rNONE )
{
int move;
if ( !IS_AFFECTED(ch, AFF_FLYING)
&& !IS_AFFECTED(ch, AFF_FLOATING) )
move = encumbrance( ch, movement_loss[UMIN(SECT_MAX-1, ch->in_room->sector_type)] );
else
move = encumbrance( ch, 1 );
if ( ch->move )
ch->move = UMAX( 0, ch->move - move );
}
return retcode;
}
/*
* Weapon types, haus
*/
int weapon_prof_bonus_check( CHAR_DATA *ch, OBJ_DATA *wield, int *gsn_ptr )
{
int bonus;
bonus = 0; *gsn_ptr = -1;
if ( !IS_NPC(ch) && ch->level > 5 && wield )
{
switch(wield->value[3])
{
default: *gsn_ptr = -1; break;
case 0:
case 12:
case 10:
case 6: *gsn_ptr = gsn_pugilism; break;
case 3:
case 1: *gsn_ptr = gsn_long_blades; break;
case 11:
case 2: *gsn_ptr = gsn_short_blades; break;
case 4: *gsn_ptr = gsn_flexible_arms; break;
case 5: *gsn_ptr = gsn_talonous_arms; break;
case 7:
case 8: *gsn_ptr = gsn_bludgeons; break;
}
if ( *gsn_ptr != -1 )
bonus = (int) ((ch->pcdata->learned[*gsn_ptr] -50)/10);
/* Reduce weapon bonuses for misaligned clannies.
if ( IS_CLANNED(ch) )
{
bonus = bonus /
( 1 + abs( ch->alignment - ch->pcdata->clan->alignment ) / 1000 );
}*/
if ( IS_DEVOTED( ch ) )
{
bonus = bonus - abs( ch->pcdata->favor ) / -100 ;
}
}
return bonus;
}
/*
* Calculate the tohit bonus on the object and return RIS values.
* -- Altrag
*/
int obj_hitroll( OBJ_DATA *obj )
{
int tohit = 0;
AFFECT_DATA *paf;
for ( paf = obj->pIndexData->first_affect; paf; paf = paf->next )
if ( paf->location == APPLY_HITROLL )
tohit += paf->modifier;
for ( paf = obj->first_affect; paf; paf = paf->next )
if ( paf->location == APPLY_HITROLL )
tohit += paf->modifier;
return tohit;
}
/*
* Offensive shield level modifier
*/
sh_int off_shld_lvl( CHAR_DATA *ch, CHAR_DATA *victim )
{
sh_int lvl;
if ( !IS_NPC(ch) ) /* players get much less effect */
{
lvl = UMAX( 1, (ch->level - 10) / 2 );
if ( number_percent() + (victim->level - lvl) < 35 )
return lvl;
else
return 0;
}
else
{
lvl = ch->level / 2;
if ( number_percent() + (victim->level - lvl) < 70 )
return lvl;
else
return 0;
}
}
/*
* Hit one guy once.
*/
ch_ret one_hit( CHAR_DATA *ch, CHAR_DATA *victim, int dt )
{
OBJ_DATA *wield;
int victim_ac;
int thac0;
int thac0_00;
int thac0_32;
int plusris;
int dam, x;
int diceroll;
int attacktype, cnt;
int prof_bonus;
int prof_gsn;
ch_ret retcode;
/*
* Can't beat a dead char!
* Guard against weird room-leavings.
*/
if ( victim->position == POS_DEAD || ch->in_room != victim->in_room )
return rVICT_DIED;
/*
* Figure out the weapon doing the damage -Thoric
*/
if ( (wield = get_eq_char( ch, WEAR_DUAL_WIELD )) != NULL )
{
if ( dual_flip == FALSE )
{
dual_flip = TRUE;
wield = get_eq_char( ch, WEAR_WIELD );
}
else
dual_flip = FALSE;
}
else
wield = get_eq_char( ch, WEAR_WIELD );
prof_bonus = weapon_prof_bonus_check( ch, wield, &prof_gsn );
if ( ch->fighting /* make sure fight is already started */
&& dt == TYPE_UNDEFINED
&& IS_NPC(ch)
&& ch->attacks != 0 )
{
cnt = 0;
for ( ;; )
{
x = number_range( 0, 6 );
attacktype = 1 << x;
if ( IS_SET( ch->attacks, attacktype ) )
break;
if ( cnt++ > 16 )
{
attacktype = 0;
break;
}
}
if ( attacktype == ATCK_BACKSTAB )
attacktype = 0;
if ( wield && number_percent( ) > 25 )
attacktype = 0;
switch ( attacktype )
{
default:
break;
case ATCK_BITE:
do_bite( ch, "" );
retcode = global_retcode;
break;
case ATCK_CLAWS:
do_claw( ch, "" );
retcode = global_retcode;
break;
case ATCK_TAIL:
do_tail( ch, "" );
retcode = global_retcode;
break;
case ATCK_STING:
do_sting( ch, "" );
retcode = global_retcode;
break;
case ATCK_PUNCH:
do_punch( ch, "" );
retcode = global_retcode;
break;
case ATCK_KICK:
do_kick( ch, "" );
retcode = global_retcode;
break;
case ATCK_TRIP:
attacktype = 0;
break;
}
if ( attacktype )
return retcode;
}
if ( dt == TYPE_UNDEFINED )
{
dt = TYPE_HIT;
if ( wield && wield->item_type == ITEM_WEAPON )
dt += wield->value[3];
}
/*
* Calculate to-hit-armor-class-0 versus armor.
*/
if ( IS_NPC(ch) )
{
thac0_00 = ch->mobthac0;
thac0_32 = 0;
}
else
{
thac0_00 = class_table[ch->class]->thac0_00;
thac0_32 = class_table[ch->class]->thac0_32;
}
thac0 = interpolate( ch->level, thac0_00, thac0_32 ) - GET_HITROLL(ch);
victim_ac = UMAX( -19, (int) (GET_AC(victim) / 10) );
/* if you can't see what's coming... */
if ( wield && !can_see_obj( victim, wield) )
victim_ac += 1;
if ( !can_see( ch, victim ) )
victim_ac -= 4;
/*
* "learning" between combatients. Takes the intelligence difference,
* and multiplies by the times killed to make up a learning bonus
* given to whoever is more intelligent -Thoric
*/
if ( ch->fighting && ch->fighting->who == victim )
{
sh_int times = ch->fighting->timeskilled;
if ( times )
{
sh_int intdiff = get_curr_int(ch) - get_curr_int(victim);
if ( intdiff != 0 )
victim_ac += (intdiff*times)/10;
}
}
/* Weapon proficiency bonus */
victim_ac += prof_bonus;
/*
* The moment of excitement!
*/
while ( ( diceroll = number_bits( 5 ) ) >= 20 )
;
if ( diceroll == 0
|| ( diceroll != 19 && diceroll < thac0 - victim_ac ) )
{
/* Miss. */
if ( prof_gsn != -1 )
learn_from_failure( ch, prof_gsn );
damage( ch, victim, 0, dt );
tail_chain( );
return rNONE;
}
/*
* Hit.
* Calc damage.
*/
if ( !wield ) /* dice formula fixed by Thoric */
dam = number_range( ch->barenumdie, ch->baresizedie * ch->barenumdie );
else
dam = number_range( wield->value[1], wield->value[2] );
/*
* Bonuses.
*/
dam += GET_DAMROLL(ch);
if ( prof_bonus )
dam += prof_bonus / 4;
/* Auto-poisoning is bonus enough... and no check here for immunity
if ( wield && IS_SET( wield->extra_flags, ITEM_POISONED ) )
dam += dam / 4;
*/
if ( !IS_NPC(ch) && ch->pcdata->learned[gsn_enhanced_damage] > 0 )
{
dam += (int) (dam * ch->pcdata->learned[gsn_enhanced_damage] / 120);
learn_from_success( ch, gsn_enhanced_damage );
}
if ( !IS_AWAKE(victim) )
dam *= 2;
if ( dt == gsn_backstab )
dam *= (2 + URANGE( 2, ch->level - (victim->level/4), 30 ) / 8);
if ( dt == gsn_circle )
dam *= (2 + URANGE( 2, ch->level - (victim->level/4), 30 ) / 16);
if ( dam <= 0 )
dam = 1;
plusris = 0;
if ( wield )
{
if ( IS_SET( wield->extra_flags, ITEM_MAGIC ) )
dam = ris_damage( victim, dam, RIS_MAGIC );
else
dam = ris_damage( victim, dam, RIS_NONMAGIC );
/*
* Handle PLUS1 - PLUS6 ris bits vs. weapon hitroll -Thoric
*/
plusris = obj_hitroll( wield );
}
else
dam = ris_damage( victim, dam, RIS_NONMAGIC );
/* check for RIS_PLUSx -Thoric */
if ( dam )
{
int x, res, imm, sus, mod;
if ( plusris )
plusris = RIS_PLUS1 << UMIN(plusris, 7);
/* initialize values to handle a zero plusris */
imm = res = -1; sus = 1;
/* find high ris */
for ( x = RIS_PLUS1; x <= RIS_PLUS6; x <<= 1 )
{
if ( IS_SET( victim->immune, x ) )
imm = x;
if ( IS_SET( victim->resistant, x ) )
res = x;
if ( IS_SET( victim->susceptible, x ) )
sus = x;
}
mod = 10;
if ( imm >= plusris )
mod -= 10;
if ( res >= plusris )
mod -= 2;
if ( sus <= plusris )
mod += 2;
/* check if immune */
if ( mod <= 0 )
dam = -1;
if ( mod != 10 )
dam = (dam * mod) / 10;
}
if ( prof_gsn != -1 )
{
if ( dam > 0 )
learn_from_success( ch, prof_gsn );
else
learn_from_failure( ch, prof_gsn );
}
/* immune to damage */
if ( dam == -1 )
{
if ( dt >= 0 && dt < top_sn )
{
SKILLTYPE *skill = skill_table[dt];
bool found = FALSE;
if ( skill->imm_char && skill->imm_char[0] != '\0' )
{
act( AT_HIT, skill->imm_char, ch, NULL, victim, TO_CHAR );
found = TRUE;
}
if ( skill->imm_vict && skill->imm_vict[0] != '\0' )
{
act( AT_HITME, skill->imm_vict, ch, NULL, victim, TO_VICT );
found = TRUE;
}
if ( skill->imm_room && skill->imm_room[0] != '\0' )
{
act( AT_ACTION, skill->imm_room, ch, NULL, victim, TO_NOTVICT );
found = TRUE;
}
if ( found )
return rNONE;
}
dam = 0;
}
if ( (retcode = damage( ch, victim, dam, dt )) != rNONE )
return retcode;
if ( char_died(ch) )
return rCHAR_DIED;
if ( char_died(victim) )
return rVICT_DIED;
retcode = rNONE;
if ( dam == 0 )
return retcode;
/* weapon spells -Thoric */
if ( wield
&& !IS_SET(victim->immune, RIS_MAGIC)
&& !IS_SET(victim->in_room->room_flags, ROOM_NO_MAGIC) )
{
AFFECT_DATA *aff;
for ( aff = wield->pIndexData->first_affect; aff; aff = aff->next )
if ( aff->location == APPLY_WEAPONSPELL
&& IS_VALID_SN(aff->modifier)
&& skill_table[aff->modifier]->spell_fun )
retcode = (*skill_table[aff->modifier]->spell_fun) ( aff->modifier, (wield->level+3)/3, ch, victim );
if ( retcode != rNONE || char_died(ch) || char_died(victim) )
return retcode;
for ( aff = wield->first_affect; aff; aff = aff->next )
if ( aff->location == APPLY_WEAPONSPELL
&& IS_VALID_SN(aff->modifier)
&& skill_table[aff->modifier]->spell_fun )
retcode = (*skill_table[aff->modifier]->spell_fun) ( aff->modifier, (wield->level+3)/3, ch, victim );
if ( retcode != rNONE || char_died(ch) || char_died(victim) )
return retcode;
}
/*
* magic shields that retaliate -Thoric
*/
if ( IS_AFFECTED( victim, AFF_FIRESHIELD )
&& !IS_AFFECTED( ch, AFF_FIRESHIELD ) )
retcode = spell_fireball( gsn_fireball, off_shld_lvl(victim, ch), victim, ch );
if ( retcode != rNONE || char_died(ch) || char_died(victim) )
return retcode;
if ( IS_AFFECTED( victim, AFF_ICESHIELD )
&& !IS_AFFECTED( ch, AFF_ICESHIELD ) )
retcode = spell_chill_touch( gsn_chill_touch, off_shld_lvl(victim, ch), victim, ch );
if ( retcode != rNONE || char_died(ch) || char_died(victim) )
return retcode;
if ( IS_AFFECTED( victim, AFF_SHOCKSHIELD )
&& !IS_AFFECTED( ch, AFF_SHOCKSHIELD ) )
retcode = spell_lightning_bolt( gsn_lightning_bolt, off_shld_lvl(victim, ch), victim, ch );
if ( retcode != rNONE || char_died(ch) || char_died(victim) )
return retcode;
tail_chain( );
return retcode;
}
/*
* Calculate damage based on resistances, immunities and suceptibilities
* -Thoric
*/
sh_int ris_damage( CHAR_DATA *ch, sh_int dam, int ris )
{
sh_int modifier;
modifier = 10;
if ( IS_SET(ch->immune, ris ) )
modifier -= 10;
if ( IS_SET(ch->resistant, ris ) )
modifier -= 2;
if ( IS_SET(ch->susceptible, ris ) )
modifier += 2;
if ( modifier <= 0 )
return -1;
if ( modifier == 10 )
return dam;
return (dam * modifier) / 10;
}
/*
* Inflict damage from a hit.
*/
ch_ret damage( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt )
{
char buf[MAX_STRING_LENGTH];
char buf1[MAX_STRING_LENGTH];
sh_int dameq;
sh_int maxdam;
bool npcvict;
bool loot;
int xp_gain;
OBJ_DATA *damobj;
ch_ret retcode;
sh_int dampmod;
CHAR_DATA *gch;
int init_gold, new_gold, gold_diff;
retcode = rNONE;
if ( !ch )
{
bug( "Damage: null ch!", 0 );
return rERROR;
}
if ( !victim )
{
bug( "Damage: null victim!", 0 );
return rVICT_DIED;
}
if ( victim->position == POS_DEAD )
return rVICT_DIED;
npcvict = IS_NPC(victim);
/*
* Check damage types for RIS -Thoric
*/
if ( dam && dt != TYPE_UNDEFINED )
{
if ( IS_FIRE(dt) )
dam = ris_damage(victim, dam, RIS_FIRE);
else
if ( IS_COLD(dt) )
dam = ris_damage(victim, dam, RIS_COLD);
else
if ( IS_ACID(dt) )
dam = ris_damage(victim, dam, RIS_ACID);
else
if ( IS_ELECTRICITY(dt) )
dam = ris_damage(victim, dam, RIS_ELECTRICITY);
else
if ( IS_ENERGY(dt) )
dam = ris_damage(victim, dam, RIS_ENERGY);
else
if ( dt == gsn_poison )
dam = ris_damage(victim, dam, RIS_POISON);
else
if ( dt == (TYPE_HIT + 7) || dt == (TYPE_HIT + 8) )
dam = ris_damage(victim, dam, RIS_BLUNT);
else
if ( dt == (TYPE_HIT + 2) || dt == (TYPE_HIT + 11)
|| dt == (TYPE_HIT + 10) )
dam = ris_damage(victim, dam, RIS_PIERCE);
else
if ( dt == (TYPE_HIT + 1) || dt == (TYPE_HIT + 3)
|| dt == (TYPE_HIT + 4) || dt == (TYPE_HIT + 5) )
dam = ris_damage(victim, dam, RIS_SLASH);
if ( dam == -1 )
{
if ( dt >= 0 && dt < top_sn )
{
bool found = FALSE;
SKILLTYPE *skill = skill_table[dt];
if ( skill->imm_char && skill->imm_char[0] != '\0' )
{
act( AT_HIT, skill->imm_char, ch, NULL, victim, TO_CHAR );
found = TRUE;
}
if ( skill->imm_vict && skill->imm_vict[0] != '\0' )
{
act( AT_HITME, skill->imm_vict, ch, NULL, victim, TO_VICT );
found = TRUE;
}
if ( skill->imm_room && skill->imm_room[0] != '\0' )
{
act( AT_ACTION, skill->imm_room, ch, NULL, victim, TO_NOTVICT );
found = TRUE;
}
if ( found )
return rNONE;
}
dam = 0;
}
}
if ( dam && npcvict && ch != victim )
{
if ( !IS_SET( victim->act, ACT_SENTINEL ) )
{
if ( victim->hunting )
{
if ( victim->hunting->who != ch )
{
STRFREE( victim->hunting->name );
victim->hunting->name = QUICKLINK( ch->name );
victim->hunting->who = ch;
}
}
else
start_hunting( victim, ch );
}
if ( victim->hating )
{
if ( victim->hating->who != ch )
{
STRFREE( victim->hating->name );
victim->hating->name = QUICKLINK( ch->name );
victim->hating->who = ch;
}
}
else
start_hating( victim, ch );
}
/*
* Stop up any residual loopholes.
*/
maxdam = ch->level * 30;
if ( dt == gsn_backstab )
maxdam = ch->level * 80;
if ( dam > maxdam )
{
sprintf( buf, "Damage: %d more than %d points!", dam, maxdam );
bug( buf, dam );
sprintf(buf, "** %s (lvl %d) -> %s **",
ch->name,
ch->level,
victim->name);
bug( buf, 0 );
dam = maxdam;
}
if ( victim != ch )
{
/*
* Certain attacks are forbidden.
* Most other attacks are returned.
*/
if ( is_safe( ch, victim ) )
return rNONE;
check_attacker( ch, victim );
if ( victim->position > POS_STUNNED )
{
if ( !victim->fighting )
set_fighting( victim, ch );
if ( victim->fighting )
victim->position = POS_FIGHTING;
}
if ( victim->position > POS_STUNNED )
{
if ( !ch->fighting )
set_fighting( ch, victim );
/*
* If victim is charmed, ch might attack victim's master.
*/
if ( IS_NPC(ch)
&& npcvict
&& IS_AFFECTED(victim, AFF_CHARM)
&& victim->master
&& victim->master->in_room == ch->in_room
&& number_bits( 3 ) == 0 )
{
stop_fighting( ch, FALSE );
retcode = multi_hit( ch, victim->master, TYPE_UNDEFINED );
return retcode;
}
}
/*
* More charm stuff.
*/
if ( victim->master == ch )
stop_follower( victim );
/* Pkill stuff. If a deadly attacks another deadly or is attacked by one,
* then ungroup any nondealies. Disabled untill I can figure out the right
* way to do it.
*/
/* {
sh_int anopc = 0; * # of (non-pkill) pc in a (ch) *
sh_int bnopc = 0; * # of (non-pkill) pc in b (victim) *
CHAR_DATA *lch; * leader ch *
* count the # of non-pkill pc in a ( not including == ch ) *
for ( gch = ch->in_room->first_person; gch; gch = gch->next_in_room )
if ( is_same_group( ch, gch ) && !IS_NPC( gch )
&& !IS_PKILL( gch ) && ( ch != gch ) ) anopc++;
* count the # of non-pkill pc in b ( not including == victim ) *
for ( gch = victim->in_room->first_person; gch; gch = gch->next_in_room )
if ( is_same_group( victim, gch ) && !IS_NPC( gch )
&& !IS_PKILL( gch ) && ( victim != gch ) ) bnopc++;
* only consider disbanding if both groups have 1(+) non-pk pc *
if ( ( bnopc > 0 ) && ( anopc > 0 ) )
{
* look at group a through ch's leader first *
lch = ch->leader ? ch->leader : ch;
if ( lch != ch )
{
* stop following leader if it isn't pk *
if ( !IS_NPC(lch) && !IS_PKILL( lch ) )
stop_follower( ch );
else
{
* disband non-pk members from lch's group if it is pk *
for ( gch = ch->in_room->first_person; gch;
gch = gch->next_in_room )
{
if ( is_same_group( lch, gch )
&& ( lch != gch )
&& !IS_NPC(gch) && !IS_PKILL( gch ) )
stop_follower( gch );
}
}
}
else
for ( gch = ch->in_room->first_person; gch; gch = gch->next_in_room )
{
* ch is leader - disband non-pks from group *
if ( is_same_group( ch, gch )
&& ( ch != gch )
&& ( !IS_PKILL( gch ) && !IS_NPC( gch ) ) )
stop_follower( gch );
}
* time to look at the victims group through its leader *
lch = victim->leader ? victim->leader : victim;
if ( lch != victim )
{
* if leader isn't deadly, stop following lch *
if ( !IS_PKILL( lch ) && !IS_NPC( lch ) )
stop_follower( victim );
else
{
* lch is pk, disband non-pk's from group *
for ( gch = victim->in_room->first_person; gch;
gch = gch->next_in_room )
{
if ( is_same_group( lch, gch )
&& ( lch != gch )
&& ( !IS_PKILL( gch ) && !IS_NPC( gch ) ) )
stop_follower( gch );
}
}
}
else
{
* victim is leader of group - disband non-pks *
for ( gch = victim->in_room->first_person; gch;
gch = gch->next_in_room )
{
if ( is_same_group( victim, gch )
&& ( victim != gch )
&& !IS_PKILL( gch ) && !IS_NPC( gch ) )
stop_follower( gch );
}
}
}
}*/
{
sh_int anopc = 0; /* # of (non-pkill) pc in a (ch) */
sh_int bnopc = 0; /* # of (non-pkill) pc in b (victim) */
/* count the # of non-pkill pc in a ( not including == ch ) */
for ( gch = ch->in_room->first_person; gch; gch = gch->next_in_room )
if ( is_same_group( ch, gch ) && !IS_NPC( gch )
&& !IS_PKILL( gch ) && ( ch != gch ) ) anopc++;
/* count the # of non-pkill pc in b ( not including == victim ) */
for ( gch = victim->in_room->first_person; gch; gch = gch->next_in_room )
if ( is_same_group( victim, gch ) && !IS_NPC( gch )
&& !IS_PKILL( gch ) && ( victim != gch ) ) bnopc++;
/* only consider disbanding if both groups have 1(+) non-pk pc,
or when one participant is pc, and the other group has 1(+)
pk pc's (in the case that participant is only pk pc in group) */
if ( ( bnopc > 0 && anopc > 0 )
|| ( bnopc > 0 && !IS_NPC(ch) )
|| ( anopc > 0 && !IS_NPC(victim) ) )
{
/* Disband from same group first */
if ( is_same_group(ch, victim) )
{
/* Messages to char and master handled in stop_follower */
act( AT_ACTION, "$n disbands from $N's group.",
(ch->leader == victim) ? victim : ch, NULL,
(ch->leader == victim) ? victim->master : ch->master,
TO_NOTVICT );
if ( ch->leader == victim )
stop_follower(victim);
else
stop_follower(ch);
}
/* if leader isnt pkill, leave the group and disband ch */
if ( ch->leader != NULL && !IS_NPC(ch->leader) &&
!IS_PKILL(ch->leader) )
{
act( AT_ACTION, "$n disbands from $N's group.", ch, NULL,
ch->master, TO_NOTVICT );
stop_follower( ch );
}
else
{
for ( gch = ch->in_room->first_person; gch; gch = gch->next_in_room )
if ( is_same_group(gch, ch) && !IS_NPC( gch ) &&
!IS_PKILL( gch ) && gch != ch )
{
act( AT_ACTION, "$n disbands from $N's group.", ch, NULL,
gch->master, TO_NOTVICT );
stop_follower( gch );
}
}
/* if leader isnt pkill, leave the group and disband victim */
if ( victim->leader != NULL && !IS_NPC(victim->leader) &&
!IS_PKILL(victim->leader) )
{
act( AT_ACTION, "$n disbands from $N's group.", victim, NULL,
victim->master, TO_NOTVICT );
stop_follower( victim );
}
else
{
for ( gch = victim->in_room->first_person; gch; gch = gch->next_in_room )
if ( is_same_group(gch, victim) && !IS_NPC( gch ) &&
!IS_PKILL( gch ) && gch != victim )
{
act( AT_ACTION, "$n disbands from $N's group.", gch, NULL,
gch->master, TO_NOTVICT );
stop_follower( gch );
}
}
}
}
/* for ( gch = ch->in_room->first_person; gch; gch = gch->next_in_room )
{
if ( is_same_group( ch, gch )
&& ( IS_PKILL( ch ) != IS_PKILL( gch ) ) )
{
stop_follower( ch );
stop_follower( gch );
}
}
for ( gch = victim->in_room->first_person; gch; gch = gch->next_in_room )
{
if ( is_same_group( victim, gch )
&& ( IS_PKILL( victim ) != IS_PKILL( gch ) ) )
{
stop_follower( victim );
stop_follower( gch );
}
}
*/
/*
* Inviso attacks ... not.
*/
if ( IS_AFFECTED(ch, AFF_INVISIBLE) )
{
affect_strip( ch, gsn_invis );
affect_strip( ch, gsn_mass_invis );
REMOVE_BIT( ch->affected_by, AFF_INVISIBLE );
act( AT_MAGIC, "$n fades into existence.", ch, NULL, NULL, TO_ROOM );
}
/* Take away Hide */
if ( IS_AFFECTED(ch, AFF_HIDE) )
REMOVE_BIT(ch->affected_by, AFF_HIDE);
/*
* Damage modifiers.
*/
if ( IS_AFFECTED(victim, AFF_SANCTUARY) )
dam /= 2;
if ( IS_AFFECTED(victim, AFF_PROTECT) && IS_EVIL(ch) )
dam -= (int) (dam / 4);
if ( dam < 0 )
dam = 0;
/*
* Check for disarm, trip, parry, and dodge.
*/
if ( dt >= TYPE_HIT )
{
if ( IS_NPC(ch)
&& IS_SET( ch->attacks, DFND_DISARM )
&& ch->level > 9
&& number_percent( ) < ch->level / 2 )
disarm( ch, victim );
if ( IS_NPC(ch)
&& IS_SET( ch->attacks, ATCK_TRIP )
&& ch->level > 5
&& number_percent( ) < ch->level / 2 )
trip( ch, victim );
if ( check_parry( ch, victim ) )
return rNONE;
if ( check_dodge( ch, victim ) )
return rNONE;
}
/*
* Check control panel settings and modify damage
*/
if ( IS_NPC(ch) )
{
if ( npcvict )
dampmod = sysdata.dam_mob_vs_mob;
else
dampmod = sysdata.dam_mob_vs_plr;
}
else
{
if ( npcvict )
dampmod = sysdata.dam_plr_vs_mob;
else
dampmod = sysdata.dam_plr_vs_plr;
}
if ( dampmod > 0 )
dam = ( dam * dampmod ) / 100;
dam_message( ch, victim, dam, dt );
}
/*
* Code to handle equipment getting damaged, and also support -Thoric
* bonuses/penalties for having or not having equipment where hit
*/
if (dam > 10 && dt != TYPE_UNDEFINED)
{
/* get a random body eq part */
dameq = number_range(WEAR_LIGHT, WEAR_EYES);
damobj = get_eq_char(victim, dameq);
if ( damobj )
{
if ( dam > get_obj_resistance(damobj) )
{
set_cur_obj(damobj);
damage_obj(damobj);
}
dam -= 5; /* add a bonus for having something to block the blow */
}
else
dam += 5; /* add penalty for bare skin! */
}
/*
* Hurt the victim.
* Inform the victim of his new state.
*/
victim->hit -= dam;
/*
* Get experience based on % of damage done -Thoric
*/
if ( dam && ch != victim
&& !IS_NPC(ch) && ch->fighting && ch->fighting->xp )
{
if ( ch->fighting->who == victim )
xp_gain = (int) (ch->fighting->xp * dam) / victim->max_hit;
else
xp_gain = (int) (xp_compute( ch, victim ) * 0.85 * dam) / victim->max_hit;
gain_exp( ch, xp_gain );
}
if ( !IS_NPC(victim)
&& victim->level >= LEVEL_IMMORTAL
&& victim->hit < 1 )
victim->hit = 1;
/* Make sure newbies dont die */
if (!IS_NPC(victim) && NOT_AUTHED(victim) && victim->hit < 1)
victim->hit = 1;
if ( dam > 0 && dt > TYPE_HIT
&& !IS_AFFECTED( victim, AFF_POISON )
&& is_wielding_poisoned( ch )
&& !IS_SET( victim->immune, RIS_POISON )
&& !saves_poison_death( ch->level, victim ) )
{
AFFECT_DATA af;
af.type = gsn_poison;
af.duration = 20;
af.location = APPLY_STR;
af.modifier = -2;
af.bitvector = AFF_POISON;
affect_join( victim, &af );
ch->mental_state = URANGE( 20, ch->mental_state + 2, 100 );
}
/*
* Vampire self preservation -Thoric
*/
if ( IS_VAMPIRE(victim) )
{
if ( dam >= (victim->max_hit / 10) ) /* get hit hard, lose blood */
gain_condition(victim, COND_BLOODTHIRST, -1 - (victim->level / 20));
if ( victim->hit <= (victim->max_hit / 8)
&& victim->pcdata->condition[COND_BLOODTHIRST]>5 )
{
gain_condition(victim, COND_BLOODTHIRST,
-URANGE(3, victim->level / 10, 8) );
victim->hit += URANGE( 4, (victim->max_hit / 30), 15);
set_char_color(AT_BLOOD, victim);
send_to_char("You howl with rage as the beast within stirs!\n\r", victim);
}
}
if ( !npcvict
&& get_trust(victim) >= LEVEL_IMMORTAL
&& get_trust(ch) >= LEVEL_IMMORTAL
&& victim->hit < 1 )
victim->hit = 1;
update_pos( victim );
switch( victim->position )
{
case POS_MORTAL:
act( AT_DYING, "$n is mortally wounded, and will die soon, if not aided.",
victim, NULL, NULL, TO_ROOM );
act( AT_DANGER, "You are mortally wounded, and will die soon, if not aided.",
victim, NULL, NULL, TO_CHAR );
break;
case POS_INCAP:
act( AT_DYING, "$n is incapacitated and will slowly die, if not aided.",
victim, NULL, NULL, TO_ROOM );
act( AT_DANGER, "You are incapacitated and will slowly die, if not aided.",
victim, NULL, NULL, TO_CHAR );
break;
case POS_STUNNED:
if ( !IS_AFFECTED( victim, AFF_PARALYSIS ) )
{
act( AT_ACTION, "$n is stunned, but will probably recover.",
victim, NULL, NULL, TO_ROOM );
act( AT_HURT, "You are stunned, but will probably recover.",
victim, NULL, NULL, TO_CHAR );
}
break;
case POS_DEAD:
if ( dt >= 0 && dt < top_sn )
{
SKILLTYPE *skill = skill_table[dt];
if ( skill->die_char && skill->die_char[0] != '\0' )
act( AT_DEAD, skill->die_char, ch, NULL, victim, TO_CHAR );
if ( skill->die_vict && skill->die_vict[0] != '\0' )
act( AT_DEAD, skill->die_vict, ch, NULL, victim, TO_VICT );
if ( skill->die_room && skill->die_room[0] != '\0' )
act( AT_DEAD, skill->die_room, ch, NULL, victim, TO_NOTVICT );
}
act( AT_DEAD, "$n is DEAD!", victim, 0, 0, TO_ROOM );
act( AT_DEAD, "You have been KILLED!\n\r", victim, 0, 0, TO_CHAR );
break;
default:
if ( dam > victim->max_hit / 4 )
{
act( AT_HURT, "That really did HURT!", victim, 0, 0, TO_CHAR );
if ( number_bits(3) == 0 )
worsen_mental_state( ch, 1 );
}
if ( victim->hit < victim->max_hit / 4 )
{
act( AT_DANGER, "You wish that your wounds would stop BLEEDING so much!",
victim, 0, 0, TO_CHAR );
if ( number_bits(2) == 0 )
worsen_mental_state( ch, 1 );
}
break;
}
/*
* Sleep spells and extremely wounded folks.
*/
if ( !IS_AWAKE(victim) /* lets make NPC's not slaughter PC's */
&& !IS_AFFECTED( victim, AFF_PARALYSIS ) )
{
if ( victim->fighting
&& victim->fighting->who->hunting
&& victim->fighting->who->hunting->who == victim )
stop_hunting( victim->fighting->who );
if ( victim->fighting
&& victim->fighting->who->hating
&& victim->fighting->who->hating->who == victim )
stop_hating( victim->fighting->who );
if (!npcvict && 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 ( !npcvict )
{
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 );
to_channel( log_buf, CHANNEL_MONITOR, "Monitor", LEVEL_IMMORTAL );
/*
* Dying penalty:
* 1/2 way back to previous level.
*/
if ( victim->exp > exp_level(victim, victim->level) )
gain_exp( victim, (exp_level(victim, victim->level) - victim->exp)/2 );
/*
* New penalty... go back to the beginning of current level.
victim->exp = exp_level( victim, victim->level );
*/
}
else
if ( !IS_NPC(ch) ) /* keep track of mob vnum killed */
add_kill( ch, victim );
check_killer( ch, victim );
loot = legal_loot( ch, victim );
set_cur_char(victim);
raw_kill( ch, victim );
victim = NULL;
if ( !IS_NPC(ch) && loot )
{
/* Autogold by Scryn 8/12 */
if ( IS_SET(ch->act, PLR_AUTOGOLD) )
{
init_gold = ch->gold;
do_get( ch, "coins corpse" );
new_gold = ch->gold;
gold_diff = (new_gold - init_gold);
if (gold_diff > 0)
{
sprintf(buf1,"%d",gold_diff);
do_split( ch, buf1 );
}
}
if ( IS_SET(ch->act, PLR_AUTOLOOT) )
do_get( ch, "all corpse" );
else
do_look( ch, "in corpse" );
if ( IS_SET(ch->act, PLR_AUTOSAC) )
do_sacrifice( ch, "corpse" );
}
if ( IS_SET( sysdata.save_flags, SV_KILL ) )
save_char_obj( ch );
return rVICT_DIED;
}
if ( victim == ch )
return rNONE;
/*
* Take care of link dead people.
*/
if ( !npcvict && !victim->desc
&& !IS_SET( victim->pcdata->flags, PCFLAG_NORECALL ) )
{
if ( number_range( 0, victim->wait ) == 0)
{
do_recall( victim, "" );
return rNONE;
}
}
/*
* Wimp out?
*/
if ( npcvict && 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 ( !npcvict
&& victim->hit > 0
&& victim->hit <= victim->wimpy
&& victim->wait == 0 )
do_flee( victim, "" );
else
if ( !npcvict && IS_SET( victim->act, PLR_FLEE ) )
do_flee( victim, "" );
tail_chain( );
return rNONE;
}
bool is_safe( CHAR_DATA *ch, CHAR_DATA *victim )
{
/* Thx Josh! */
if ( who_fighting( ch ) == ch )
return FALSE;
if ( IS_SET( victim->in_room->room_flags, ROOM_SAFE ) )
{
set_char_color( AT_MAGIC, ch );
send_to_char( "A magical force prevents you from attacking.\n\r", ch );
return TRUE;
}
if( !IS_NPC( ch ) && !IS_NPC( victim )
&& ch != victim
&& IS_SET( victim->in_room->area->flags, AFLAG_NOPKILL ) )
{
set_char_color( AT_IMMORT, ch );
send_to_char( "The gods have forbidden player killing in this area.\n\r", ch );
return TRUE;
}
if ( IS_NPC(ch) || IS_NPC(victim) )
return FALSE;
if ( get_age( ch ) < 18 || ch->level < 5 )
{
set_char_color( AT_WHITE, ch );
send_to_char( "You are not yet ready, needing age or experience, if not both. \n\r", ch );
return TRUE;
}
if ( get_age( victim ) < 18 || victim->level < 5 )
{
set_char_color( AT_WHITE, ch );
send_to_char( "They are yet too young to die.\n\r", ch );
return TRUE;
}
if ( ch->level - victim->level > 5
|| victim->level - ch->level > 5 )
{
set_char_color( AT_IMMORT, ch );
send_to_char( "The gods do not allow murder when there is such a difference in level.\n\r", ch );
return TRUE;
}
if ( get_timer(victim, TIMER_PKILLED) > 0 )
{
set_char_color( AT_GREEN, ch );
send_to_char( "That character has died within the last 5 minutes.\n\r", ch);
return TRUE;
}
if ( get_timer(ch, TIMER_PKILLED) > 0 )
{
set_char_color( AT_GREEN, ch );
send_to_char( "You have been killed within the last 5 minutes.\n\r", ch );
return TRUE;
}
return FALSE;
}
/*
* just verify that a corpse looting is legal
*/
bool legal_loot( CHAR_DATA *ch, CHAR_DATA *victim )
{
/* anyone can loot mobs */
if ( IS_NPC(victim) )
return TRUE;
/* non-charmed mobs can loot anything */
if ( IS_NPC(ch) && !ch->master )
return TRUE;
/* members of different clans can loot too! -Thoric */
if ( !IS_NPC(ch) && !IS_NPC(victim)
&& IS_SET( ch->pcdata->flags, PCFLAG_DEADLY )
&& IS_SET( victim->pcdata->flags, PCFLAG_DEADLY ) )
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) )
{
if ( !IS_NPC( ch ) )
{
if ( ch->pcdata->clan )
ch->pcdata->clan->mkills++;
ch->pcdata->mkills++;
ch->in_room->area->mkills++;
if ( ( ch->pcdata->deity )
&& ( victim->race == ch->pcdata->deity->npcrace ) )
adjust_favor( ch, 3, 1 );
else
adjust_favor( ch, 2, 1 );
}
return;
}
/*
* Any character in the arena is ok to kill.
*/
if ( in_arena( ch ) )
return;
/*
* So are killers and thieves.
*/
if ( IS_SET(victim->act, PLR_KILLER)
|| IS_SET(victim->act, PLR_THIEF) )
{
if ( !IS_NPC( ch ) )
{
if ( ch->pcdata->clan )
ch->pcdata->clan->pkills++;
ch->pcdata->pkills++;
ch->in_room->area->pkills++;
}
return;
}
/* clan checks -Thoric */
if ( !IS_NPC(ch) && !IS_NPC(victim)
&& IS_SET( ch->pcdata->flags, PCFLAG_DEADLY )
&& IS_SET( victim->pcdata->flags, PCFLAG_DEADLY ) )
{
/* not of same clan? Go ahead and kill!!! */
if ( !ch->pcdata->clan
|| !victim->pcdata->clan
|| ( ch->pcdata->clan->clan_type != CLAN_NOKILL
&& victim->pcdata->clan->clan_type != CLAN_NOKILL
&& ch->pcdata->clan != victim->pcdata->clan ) )
{
if ( ch->pcdata->clan ) ch->pcdata->clan->pkills++;
ch->pcdata->pkills++;
ch->hit = ch->max_hit;
ch->mana = ch->max_mana;
ch->move = ch->max_move;
if ( ch->pcdata )
ch->pcdata->condition[COND_BLOODTHIRST] = (10 + ch->level);
update_pos(victim);
if ( victim != ch )
{
act( AT_MAGIC, "Bolts of blue energy rise from the corpse, seeping into $n.", ch, victim->name, NULL, TO_ROOM );
act( AT_MAGIC, "Bolts of blue energy rise from the corpse, seeping into you.", ch, victim->name, NULL, TO_CHAR );
}
if ( victim->pcdata->clan )
victim->pcdata->clan->pdeaths++;
victim->pcdata->pdeaths++;
adjust_favor( victim, 11, 1 );
adjust_favor( ch, 2, 1 );
add_timer( victim, TIMER_PKILLED, 115, NULL, 0 );
WAIT_STATE( victim, 3 * PULSE_VIOLENCE );
/* SET_BIT(victim->act, PLR_PK); */
return;
}
}
/*
* Charm-o-rama.
*/
if ( IS_SET(ch->affected_by, AFF_CHARM) )
{
if ( !ch->master )
{
char buf[MAX_STRING_LENGTH];
sprintf( buf, "Check_killer: %s bad AFF_CHARM",
IS_NPC(ch) ? ch->short_descr : ch->name );
bug( buf, 0 );
affect_strip( ch, gsn_charm_person );
REMOVE_BIT( ch->affected_by, AFF_CHARM );
return;
}
/* stop_follower( ch ); */
if ( ch->master )
check_killer( ch->master, victim );
return;
}
/*
* NPC's are cool of course (as long as not charmed).
* Hitting yourself is cool too (bleeding).
* So is being immortal (Alander's idea).
* And current killers stay as they are.
*/
if ( IS_NPC(ch) )
{
if ( !IS_NPC(victim) )
{
if ( victim->pcdata->clan )
victim->pcdata->clan->mdeaths++;
victim->pcdata->mdeaths++;
victim->in_room->area->mdeaths++;
if ( ( victim->pcdata->deity )
&& ( ch->race == victim->pcdata->deity->npcrace ) )
adjust_favor( victim, 12, 1 );
else
adjust_favor( victim, 11, 1 );
}
return;
}
if ( ch == victim || ch->level >= LEVEL_IMMORTAL )
return;
if ( !IS_NPC(ch) )
{
if ( ch->pcdata->clan )
ch->pcdata->clan->illegal_pk++;
ch->pcdata->illegal_pk++;
ch->in_room->area->illegal_pk++;
}
if ( !IS_NPC(victim) )
{
if ( victim->pcdata->clan )
victim->pcdata->clan->pdeaths++;
victim->pcdata->pdeaths++;
victim->in_room->area->pdeaths++;
}
if ( IS_SET(ch->act, PLR_KILLER) )
return;
set_char_color( AT_WHITE, ch );
send_to_char( "A strange feeling grows deep inside you, and a tingle goes up your spine...\n\r", ch );
set_char_color( AT_IMMORT, ch );
send_to_char( "A deep voice booms inside your head, 'Thou shall now be known as a deadly murderer!!!'\n\r", ch );
set_char_color( AT_WHITE, ch );
send_to_char( "You feel as if your soul has been revealed for all to see.\n\r", ch );
SET_BIT(ch->act, PLR_KILLER);
if ( IS_SET( ch->act, PLR_ATTACKER) )
REMOVE_BIT(ch->act, PLR_ATTACKER);
save_char_obj( ch );
return;
}
/*
* See if an attack justifies a ATTACKER flag.
*/
void check_attacker( CHAR_DATA *ch, CHAR_DATA *victim )
{
/*
* Made some changes to this function Apr 6/96 to reduce the prolifiration
* of attacker flags in the realms. -Narn
*/
/*
* NPC's are fair game.
* So are killers and thieves.
*/
if ( IS_NPC(victim)
|| IS_SET(victim->act, PLR_KILLER)
|| IS_SET(victim->act, PLR_THIEF) )
return;
/* deadly char check */
if ( !IS_NPC(ch) && !IS_NPC(victim)
&& CAN_PKILL( ch ) && CAN_PKILL( victim ) )
{
if ( !(ch->pcdata->clan && victim->pcdata->clan
&& ch->pcdata->clan == victim->pcdata->clan ) )
return;
}
/*
* Charm-o-rama.
*/
if ( IS_SET(ch->affected_by, AFF_CHARM) )
{
if ( !ch->master )
{
char buf[MAX_STRING_LENGTH];
sprintf( buf, "Check_attacker: %s bad AFF_CHARM",
IS_NPC(ch) ? ch->short_descr : ch->name );
bug( buf, 0 );
affect_strip( ch, gsn_charm_person );
REMOVE_BIT( ch->affected_by, AFF_CHARM );
return;
}
/* Won't have charmed mobs fighting give the master an attacker
flag. The killer flag stays in, and I'll put something in
do_murder. -Narn */
/*SET_BIT(ch->master->act, PLR_ATTACKER);*/
/* stop_follower( ch ); */
return;
}
/*
* NPC's are cool of course (as long as not charmed).
* Hitting yourself is cool too (bleeding).
* So is being immortal (Alander's idea).
* And current killers stay as they are.
*/
if ( IS_NPC(ch)
|| ch == victim
|| ch->level >= LEVEL_IMMORTAL
|| IS_SET(ch->act, PLR_ATTACKER)
|| IS_SET(ch->act, PLR_KILLER) )
return;
SET_BIT(ch->act, PLR_ATTACKER);
save_char_obj( ch );
return;
}
/*
* Set position of a victim.
*/
void update_pos( CHAR_DATA *victim )
{
if ( !victim )
{
bug( "update_pos: null victim", 0 );
return;
}
if ( victim->hit > 0 )
{
if ( victim->position <= POS_STUNNED )
victim->position = POS_STANDING;
if ( IS_AFFECTED( victim, AFF_PARALYSIS ) )
victim->position = POS_STUNNED;
return;
}
if ( IS_NPC(victim) || victim->hit <= -11 )
{
if ( victim->mount )
{
act( AT_ACTION, "$n falls from $N.",
victim, NULL, victim->mount, TO_ROOM );
REMOVE_BIT( victim->mount->act, ACT_MOUNTED );
victim->mount = 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->position > POS_STUNNED
&& IS_AFFECTED( victim, AFF_PARALYSIS ) )
victim->position = POS_STUNNED;
if ( victim->mount )
{
act( AT_ACTION, "$n falls unconscious from $N.",
victim, NULL, victim->mount, TO_ROOM );
REMOVE_BIT( victim->mount->act, ACT_MOUNTED );
victim->mount = NULL;
}
return;
}
/*
* Start fights.
*/
void set_fighting( CHAR_DATA *ch, CHAR_DATA *victim )
{
FIGHT_DATA *fight;
if ( ch->fighting )
{
char buf[MAX_STRING_LENGTH];
sprintf( buf, "Set_fighting: %s -> %s (already fighting %s)",
ch->name, victim->name, ch->fighting->who->name );
bug( buf, 0 );
return;
}
if ( IS_AFFECTED(ch, AFF_SLEEP) )
affect_strip( ch, gsn_sleep );
/* Limit attackers -Thoric */
if ( victim->num_fighting > max_fight(victim) )
{
send_to_char( "There are too many people fighting for you to join in.\n\r", ch );
return;
}
CREATE( fight, FIGHT_DATA, 1 );
fight->who = victim;
fight->xp = (int) xp_compute( ch, victim ) * 0.85;
fight->align = align_compute( ch, victim );
if ( !IS_NPC(ch) && IS_NPC(victim) )
fight->timeskilled = times_killed(ch, victim);
ch->num_fighting = 1;
ch->fighting = fight;
ch->position = POS_FIGHTING;
victim->num_fighting++;
if ( victim->switched && IS_AFFECTED(victim->switched, AFF_POSSESS) )
{
send_to_char( "You are disturbed!\n\r", victim->switched );
do_return( victim->switched, "" );
}
return;
}
CHAR_DATA *who_fighting( CHAR_DATA *ch )
{
if ( !ch )
{
bug( "who_fighting: null ch", 0 );
return NULL;
}
if ( !ch->fighting )
return NULL;
return ch->fighting->who;
}
void free_fight( CHAR_DATA *ch )
{
if ( !ch )
{
bug( "Free_fight: null ch!", 0 );
return;
}
if ( ch->fighting )
{
if ( !char_died(ch->fighting->who) )
--ch->fighting->who->num_fighting;
DISPOSE( ch->fighting );
}
ch->fighting = NULL;
if ( ch->mount )
ch->position = POS_MOUNTED;
else
ch->position = POS_STANDING;
/* Berserk wears off after combat. -- Altrag */
if ( IS_AFFECTED(ch, AFF_BERSERK) )
{
affect_strip(ch, gsn_berserk);
set_char_color(AT_WEAROFF, ch);
send_to_char(skill_table[gsn_berserk]->msg_off, ch);
send_to_char("\n\r", ch);
}
return;
}
/*
* Stop fights.
*/
void stop_fighting( CHAR_DATA *ch, bool fBoth )
{
CHAR_DATA *fch;
free_fight( ch );
update_pos( ch );
if ( !fBoth ) /* major short cut here by Thoric */
return;
for ( fch = first_char; fch; fch = fch->next )
{
if ( who_fighting( fch ) == ch )
{
free_fight( fch );
update_pos( fch );
}
}
return;
}
/*
* Improved Death_cry contributed by Diavolo.
* Additional improvement by Thoric (and removal of turds... sheesh!)
*/
void death_cry( CHAR_DATA *ch )
{
ROOM_INDEX_DATA *was_in_room;
char *msg;
EXIT_DATA *pexit;
int vnum;
if ( !ch )
{
bug( "DEATH_CRY: null ch!", 0 );
return;
}
vnum = 0;
switch ( number_bits( 4 ) )
{
default: msg = "You hear $n's death cry."; break;
case 0: msg = "$n hits the ground ... DEAD."; break;
case 1: msg = "$n splatters blood on your armor."; break;
case 2: if ( HAS_BODYPART(ch, PART_GUTS) )
{
msg = "$n's guts spill all over the ground.";
vnum = OBJ_VNUM_SPILLED_GUTS;
}
else
msg = "$n collapses lifeless to the ground.";
break;
case 3: if ( HAS_BODYPART(ch, PART_HEAD) )
{
msg = "$n's severed head plops on the ground.";
vnum = OBJ_VNUM_SEVERED_HEAD;
}
else
msg = "You hear $n's death cry.";
break;
case 4: if ( HAS_BODYPART(ch, PART_HEART) )
{
msg = "$n's heart is torn from $s chest.";
vnum = OBJ_VNUM_TORN_HEART;
}
else
msg = "$n collapses lifeless to the ground.";
break;
case 5: if ( HAS_BODYPART(ch, PART_ARMS) )
{
msg = "$n's arm is sliced from $s dead body.";
vnum = OBJ_VNUM_SLICED_ARM;
}
else
msg = "You hear $n's death cry.";
break;
case 6: if ( HAS_BODYPART( ch, PART_LEGS) )
{
msg = "$n's leg is sliced from $s dead body.";
vnum = OBJ_VNUM_SLICED_LEG;
}
else
msg = "$n collapses lifeless to the ground.";
break;
}
act( AT_CARNAGE, msg, ch, NULL, NULL, TO_ROOM );
if ( vnum )
{
char buf[MAX_STRING_LENGTH];
OBJ_DATA *obj;
char *name;
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 );
STRFREE( obj->short_descr );
obj->short_descr = STRALLOC( buf );
sprintf( buf, obj->description, name );
STRFREE( obj->description );
obj->description = STRALLOC( buf );
obj = 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 ( pexit = was_in_room->first_exit; pexit; pexit = pexit->next )
{
if ( pexit->to_room
&& pexit->to_room != was_in_room )
{
ch->in_room = pexit->to_room;
act( AT_CARNAGE, msg, ch, NULL, NULL, TO_ROOM );
}
}
ch->in_room = was_in_room;
return;
}
void raw_kill( CHAR_DATA *ch, CHAR_DATA *victim )
{
CHAR_DATA *victmp;
/* char buf[MAX_STRING_LENGTH]; */
if ( !victim )
{
bug( "raw_kill: null victim!", 0 );
return;
}
/* backup in case hp goes below 1 */
if (NOT_AUTHED(victim))
{
bug( "raw_kill: killing unauthed", 0 );
return;
}
stop_fighting( victim, TRUE );
/* Take care of polymorphed chars */
if(IS_NPC(victim) && IS_SET(victim->act, ACT_POLYMORPHED))
{
char_from_room(victim->desc->original);
char_to_room(victim->desc->original, victim->in_room);
victmp = victim->desc->original;
do_revert(victim, "");
raw_kill(ch, victmp);
return;
}
mprog_death_trigger( ch, victim );
if ( char_died(victim) )
return;
/* death_cry( victim ); */
rprog_death_trigger( ch, victim );
if ( char_died(victim) )
return;
make_corpse( victim, ch );
make_blood( victim );
if ( IS_NPC(victim) )
{
victim->pIndexData->killed++;
extract_char( victim, TRUE );
victim = NULL;
return;
}
set_char_color( AT_DIEMSG, victim );
do_help(victim, "_DIEMSG_" );
extract_char( victim, FALSE );
if ( !victim )
{
bug( "oops! raw_kill: extract_char destroyed pc char", 0 );
return;
}
while ( victim->first_affect )
affect_remove( victim, victim->first_affect );
victim->affected_by = race_table[victim->race].affected
| (victim->pcdata->deity ? victim->pcdata->deity->affected : 0);
victim->carry_weight= 0;
victim->armor = 100;
victim->mod_str = 0;
victim->mod_dex = 0;
victim->mod_wis = 0;
victim->mod_int = 0;
victim->mod_con = 0;
victim->mod_cha = 0;
victim->mod_lck = 0;
victim->damroll = 0;
victim->hitroll = 0;
victim->mental_state = -10;
victim->alignment = URANGE( -1000, victim->alignment, 1000 );
victim->saving_spell_staff = 0;
victim->position = POS_RESTING;
victim->hit = UMAX( 1, victim->hit );
victim->mana = UMAX( 1, victim->mana );
victim->move = UMAX( 1, victim->move );
/*
* 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);
}
victim->pcdata->condition[COND_FULL] = 12;
victim->pcdata->condition[COND_THIRST] = 12;
if ( IS_VAMPIRE( victim ) )
victim->pcdata->condition[COND_BLOODTHIRST] = (victim->level / 2);
if ( IS_SET( sysdata.save_flags, SV_DEATH ) )
save_char_obj( victim );
return;
}
void group_gain( CHAR_DATA *ch, CHAR_DATA *victim )
{
char buf[MAX_STRING_LENGTH];
CHAR_DATA *gch;
CHAR_DATA *lch;
int xp;
int members;
/*
* 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->first_person; 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->first_person; 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 > 8 )
{
send_to_char( "You are too high for this group.\n\r", gch );
continue;
}
if ( gch->level - lch->level < -8 )
{
send_to_char( "You are too low for this group.\n\r", gch );
continue;
}
xp = (int) (xp_compute( gch, victim ) * 0.1765) / members;
if ( !gch->fighting )
xp /= 2;
gch->alignment = align_compute( gch, victim );
sprintf( buf, "You receive %d experience points.\n\r", xp );
send_to_char( buf, gch );
gain_exp( gch, xp );
for ( obj = ch->first_carrying; obj; obj = obj_next )
{
obj_next = obj->next_content;
if ( obj->wear_loc == WEAR_NONE )
continue;
if ( ( IS_OBJ_STAT(obj, ITEM_ANTI_EVIL) && IS_EVIL(ch) )
|| ( IS_OBJ_STAT(obj, ITEM_ANTI_GOOD) && IS_GOOD(ch) )
|| ( IS_OBJ_STAT(obj, ITEM_ANTI_NEUTRAL) && IS_NEUTRAL(ch) ) )
{
act( AT_MAGIC, "You are zapped by $p.", ch, obj, NULL, TO_CHAR );
act( AT_MAGIC, "$n is zapped by $p.", ch, obj, NULL, TO_ROOM );
obj_from_char( obj );
obj = obj_to_room( obj, ch->in_room );
oprog_zap_trigger(ch, obj); /* mudprogs */
if ( char_died(ch) )
return;
}
}
}
return;
}
int align_compute( CHAR_DATA *gch, CHAR_DATA *victim )
{
int align, newalign;
align = gch->alignment - victim->alignment;
if ( align > 500 )
newalign = UMIN( gch->alignment + (align-500)/4, 1000 );
else
if ( align < -500 )
newalign = UMAX( gch->alignment + (align+500)/4, -1000 );
else
newalign = gch->alignment - (int) (gch->alignment / 4);
return newalign;
}
/*
* Calculate how much XP gch should gain for killing victim
* Lots of redesigning for new exp system by Thoric
*/
int xp_compute( CHAR_DATA *gch, CHAR_DATA *victim )
{
int align;
int xp;
int xp_ratio;
int gchlev = gch->level;
xp = (get_exp_worth( victim )
* URANGE( 0, (victim->level - gchlev) + 10, 13 )) / 10;
align = gch->alignment - victim->alignment;
/* bonus for attacking opposite alignment */
if ( align > 990 || align < -990 )
xp = (xp*5) >> 2;
else
/* penalty for good attacking same alignment */
if ( gch->alignment > 300 && align < 250 )
xp = (xp*3) >> 2;
xp = number_range( (xp*3) >> 2, (xp*5) >> 2 );
/* get 1/4 exp for players -Thoric */
if ( !IS_NPC( victim ) )
xp /= 4;
else
/* reduce exp for killing the same mob repeatedly -Thoric */
if ( !IS_NPC( gch ) )
{
int times = times_killed( gch, victim );
if ( times >= 20 )
xp = 0;
else
if ( times )
{
xp = (xp * (20-times)) / 20;
if ( times > 15 )
xp /= 3;
else
if ( times > 10 )
xp >>= 1;
}
}
/*
* semi-intelligent experienced player vs. novice player xp gain
* "bell curve"ish xp mod by Thoric
* based on time played vs. level
*/
if ( !IS_NPC( gch ) && gchlev > 5 )
{
xp_ratio = (int) gch->played / gchlev;
if ( xp_ratio > 20000 )
xp = (xp*5) >> 2;
else
if ( xp_ratio < 16000 )
xp = (xp*3) >> 2;
else
if ( xp_ratio < 10000 )
xp >>= 1;
else
if ( xp_ratio < 5000 )
xp >>= 2;
else
if ( xp_ratio < 3500 )
xp >>= 3;
else
if ( xp_ratio < 2000 )
xp >>= 4;
}
/*
* Level based experience gain cap. Cannot get more experience for
* a kill than the amount for your current experience level -Thoric
*/
return URANGE(0, xp, exp_level( gch, gchlev ));
}
/*
* Revamped by Thoric to be more realistic
*/
void dam_message( CHAR_DATA *ch, CHAR_DATA *victim, int dam, int dt )
{
char buf1[256], buf2[256], buf3[256];
const char *vs;
const char *vp;
const char *attack;
char punct;
sh_int dampc;
struct skill_type *skill = NULL;
bool gcflag = FALSE;
bool gvflag = FALSE;
if ( ! dam )
dampc = 0;
else
dampc = ( (dam * 1000) / victim->max_hit) +
( 50 - ((victim->hit * 50) / victim->max_hit) );
/* 10 * percent */
if ( dam == 0 ) { vs = "miss"; vp = "misses"; }
else if ( dampc <= 5 ) { vs = "barely scratch";vp = "barely scratches";}
else if ( dampc <= 10 ) { vs = "scratch"; vp = "scratches"; }
else if ( dampc <= 20 ) { vs = "nick"; vp = "nicks"; }
else if ( dampc <= 30 ) { vs = "graze"; vp = "grazes"; }
else if ( dampc <= 40 ) { vs = "bruise"; vp = "bruises"; }
else if ( dampc <= 50 ) { vs = "hit"; vp = "hits"; }
else if ( dampc <= 60 ) { vs = "injure"; vp = "injures"; }
else if ( dampc <= 75 ) { vs = "thrash"; vp = "thrashes"; }
else if ( dampc <= 80 ) { vs = "wound"; vp = "wounds"; }
else if ( dampc <= 90 ) { vs = "maul"; vp = "mauls"; }
else if ( dampc <= 125 ) { vs = "decimate";vp = "decimates"; }
else if ( dampc <= 150 ) { vs = "devastate";vp = "devastates"; }
else if ( dampc <= 200 ) { vs = "maim"; vp = "maims"; }
else if ( dampc <= 300 ) { vs = "MUTILATE";vp = "MUTILATES"; }
else if ( dampc <= 400 ) { vs = "DISEMBOWEL";vp = "DISEMBOWELS"; }
else if ( dampc <= 500 ) { vs = "MASSACRE"; vp = "MASSACRES"; }
else if ( dampc <= 600 ) { vs = "PULVERIZE"; vp = "PULVERIZES"; }
else if ( dampc <= 750 ) { vs = "EVISCERATE";vp = "EVISCERATES"; }
else if ( dampc <= 990 ) { vs = "* OBLITERATE *";
vp = "* OBLITERATES *"; }
else { vs = "*** ANNIHILATE ***";
vp = "*** ANNIHILATES ***"; }
punct = (dampc <= 30) ? '.' : '!';
if ( dam == 0 && (!IS_NPC(ch) &&
(IS_SET(ch->pcdata->flags, PCFLAG_GAG)))) gcflag = TRUE;
if ( dam == 0 && (!IS_NPC(victim) &&
(IS_SET(victim->pcdata->flags, PCFLAG_GAG)))) gvflag = TRUE;
if ( dt >=0 && dt < top_sn )
skill = skill_table[dt];
if ( dt == TYPE_HIT )
{
sprintf( buf1, "$n %s $N%c", vp, punct );
sprintf( buf2, "You %s $N%c", vs, punct );
sprintf( buf3, "$n %s you%c", vp, punct );
}
else
if ( dt > TYPE_HIT && is_wielding_poisoned( ch ) )
{
if ( dt < TYPE_HIT + sizeof(attack_table)/sizeof(attack_table[0]) )
attack = attack_table[dt - TYPE_HIT];
else
{
bug( "Dam_message: bad dt %d.", dt );
dt = TYPE_HIT;
attack = attack_table[0];
}
sprintf( buf1, "$n's poisoned %s %s $N%c", attack, vp, punct );
sprintf( buf2, "Your poisoned %s %s $N%c", attack, vp, punct );
sprintf( buf3, "$n's poisoned %s %s you%c", attack, vp, punct );
}
else
{
if ( skill )
{
attack = skill->noun_damage;
if ( dam == 0 )
{
bool found = FALSE;
if ( skill->miss_char && skill->miss_char[0] != '\0' )
{
act( AT_HIT, skill->miss_char, ch, NULL, victim, TO_CHAR );
found = TRUE;
}
if ( skill->miss_vict && skill->miss_vict[0] != '\0' )
{
act( AT_HITME, skill->miss_vict, ch, NULL, victim, TO_VICT );
found = TRUE;
}
if ( skill->miss_room && skill->miss_room[0] != '\0' )
{
act( AT_ACTION, skill->miss_room, ch, NULL, victim, TO_NOTVICT );
found = TRUE;
}
if ( found ) /* miss message already sent */
return;
}
else
{
if ( skill->hit_char && skill->hit_char[0] != '\0' )
act( AT_HIT, skill->hit_char, ch, NULL, victim, TO_CHAR );
if ( skill->hit_vict && skill->hit_vict[0] != '\0' )
act( AT_HITME, skill->hit_vict, ch, NULL, victim, TO_VICT );
if ( skill->hit_room && skill->hit_room[0] != '\0' )
act( AT_ACTION, skill->hit_room, ch, NULL, victim, TO_NOTVICT );
}
}
else if ( dt >= TYPE_HIT
&& dt < TYPE_HIT + sizeof(attack_table)/sizeof(attack_table[0]) )
attack = attack_table[dt - TYPE_HIT];
else
{
bug( "Dam_message: bad dt %d.", dt );
dt = TYPE_HIT;
attack = attack_table[0];
}
sprintf( buf1, "$n's %s %s $N%c", attack, vp, punct );
sprintf( buf2, "Your %s %s $N%c", attack, vp, punct );
sprintf( buf3, "$n's %s %s you%c", attack, vp, punct );
}
act( AT_ACTION, buf1, ch, NULL, victim, TO_NOTVICT );
if (!gcflag) act( AT_HIT, buf2, ch, NULL, victim, TO_CHAR );
if (!gvflag) act( AT_HITME, buf3, ch, NULL, victim, TO_VICT );
return;
}
void do_kill( CHAR_DATA *ch, char *argument )
{
char arg[MAX_INPUT_LENGTH];
CHAR_DATA *victim;
one_argument( argument, arg );
if ( arg[0] == '\0' )
{
send_to_char( "Kill whom?\n\r", ch );
return;
}
if ( ( victim = get_char_room( ch, arg ) ) == NULL )
{
send_to_char( "They aren't here.\n\r", ch );
return;
}
if ( !IS_NPC(victim) )
{
if ( !IS_SET(victim->act, PLR_KILLER)
&& !IS_SET(victim->act, PLR_THIEF) )
{
send_to_char( "You must MURDER a player.\n\r", ch );
return;
}
}
/*
*
else
{
if ( IS_AFFECTED(victim, AFF_CHARM) && victim->master != NULL )
{
send_to_char( "You must MURDER a charmed creature.\n\r", ch );
return;
}
}
*
*/
if ( victim == ch )
{
send_to_char( "You hit yourself. Ouch!\n\r", ch );
multi_hit( ch, ch, TYPE_UNDEFINED );
return;
}
if ( is_safe( ch, victim ) )
return;
if ( IS_AFFECTED(ch, AFF_CHARM) && ch->master == victim )
{
act( AT_PLAIN, "$N is your beloved master.", ch, NULL, victim, TO_CHAR );
return;
}
if ( ch->position == POS_FIGHTING )
{
send_to_char( "You do the best you can!\n\r", ch );
return;
}
WAIT_STATE( ch, 1 * PULSE_VIOLENCE );
check_attacker( ch, victim );
multi_hit( ch, victim, TYPE_UNDEFINED );
return;
}
void do_murde( CHAR_DATA *ch, char *argument )
{
send_to_char( "If you want to MURDER, spell it out.\n\r", ch );
return;
}
void do_murder( CHAR_DATA *ch, char *argument )
{
char buf[MAX_STRING_LENGTH];
char arg[MAX_INPUT_LENGTH];
CHAR_DATA *victim;
one_argument( argument, arg );
if ( arg[0] == '\0' )
{
send_to_char( "Murder whom?\n\r", ch );
return;
}
if ( ( victim = get_char_room( ch, arg ) ) == NULL )
{
send_to_char( "They aren't here.\n\r", ch );
return;
}
if ( victim == ch )
{
send_to_char( "Suicide is a mortal sin.\n\r", ch );
return;
}
if ( is_safe( ch, victim ) )
return;
if ( IS_AFFECTED(ch, AFF_CHARM) )
{
if ( ch->master == victim )
{
act( AT_PLAIN, "$N is your beloved master.", ch, NULL, victim, TO_CHAR );
return;
}
else
{
if ( ch->master )
SET_BIT(ch->master->act, PLR_ATTACKER);
}
}
if ( ch->position == POS_FIGHTING )
{
send_to_char( "You do the best you can!\n\r", ch );
return;
}
if ( !IS_NPC( victim ) && IS_SET( ch->act, PLR_NICE ) )
{
send_to_char( "You feel too nice to do that!\n\r", ch );
return;
}
/*
if ( !IS_NPC( victim ) && IS_SET( victim->act, PLR_PK ) )
*/
if (!IS_NPC (victim))
{
sprintf( log_buf, "%s: murder %s.", ch->name, victim->name );
log_string_plus( log_buf, LOG_NORMAL, ch->level );
}
WAIT_STATE( ch, 1 * PULSE_VIOLENCE );
sprintf( buf, "Help! I am being attacked by %s!",
IS_NPC( ch ) ? ch->short_descr : ch->name );
if ( IS_PKILL(victim) )
do_wartalk( victim, buf );
else
do_yell( victim, buf );
check_illegal_pk( ch, victim );
check_attacker( ch, victim );
multi_hit( ch, victim, TYPE_UNDEFINED );
return;
}
bool in_arena( CHAR_DATA *ch )
{
if ( !str_cmp( ch->in_room->area->filename, "arena.are" ) )
return TRUE;
if ( ch->in_room->vnum < 29 || ch->in_room->vnum > 43 )
return FALSE;
return TRUE;
}
bool check_illegal_pk( CHAR_DATA *ch, CHAR_DATA *victim )
{
if (!IS_NPC (victim) && !IS_NPC(ch))
{
if ( ( !IS_SET( victim->pcdata->flags, PCFLAG_DEADLY )
|| ch->level - victim->level > 10
|| !IS_SET( ch->pcdata->flags, PCFLAG_DEADLY ) )
&& !in_arena( ch )
&& ch != victim
&& !( IS_IMMORTAL( ch ) && IS_IMMORTAL( victim ) ) )
{
sprintf( log_buf, "%s performing illegal pkill on %s at %d",
(IS_NPC(ch) ? ch->short_descr : ch->name),
victim->name,
victim->in_room->vnum );
log_string( log_buf );
to_channel( log_buf, CHANNEL_MONITOR, "Monitor", LEVEL_IMMORTAL );
return TRUE;
}
}
return FALSE;
}
void do_flee( CHAR_DATA *ch, char *argument )
{
ROOM_INDEX_DATA *was_in;
ROOM_INDEX_DATA *now_in;
char buf[MAX_STRING_LENGTH];
int attempt, los;
sh_int door;
EXIT_DATA *pexit;
if ( !who_fighting( ch ) )
{
if ( ch->position == POS_FIGHTING )
{
if ( ch->mount )
ch->position = POS_MOUNTED;
else
ch->position = POS_STANDING;
}
send_to_char( "You aren't fighting anyone.\n\r", ch );
return;
}
if ( ch->move <= 0 )
{
send_to_char( "You're too exhausted to flee from combat!\n\r", ch );
return;
}
/* No fleeing while stunned. - Narn */
if ( ch->position < POS_FIGHTING )
return;
was_in = ch->in_room;
for ( attempt = 0; attempt < 8; attempt++ )
{
door = number_door( );
if ( ( pexit = get_exit(was_in, door) ) == NULL
|| !pexit->to_room
|| ( IS_SET(pexit->exit_info, EX_CLOSED)
&& !IS_AFFECTED( ch, AFF_PASS_DOOR ) )
|| ( IS_NPC(ch)
&& IS_SET(pexit->to_room->room_flags, ROOM_NO_MOB) ) )
continue;
affect_strip ( ch, gsn_sneak );
REMOVE_BIT ( ch->affected_by, AFF_SNEAK );
if ( ch->mount && ch->mount->fighting )
stop_fighting( ch->mount, TRUE );
move_char( ch, pexit, 0 );
if ( ( now_in = ch->in_room ) == was_in )
continue;
ch->in_room = was_in;
act( AT_FLEE, "$n flees head over heels!", ch, NULL, NULL, TO_ROOM );
ch->in_room = now_in;
act( AT_FLEE, "$n glances around for signs of pursuit.", ch, NULL, NULL, TO_ROOM );
if ( !IS_NPC(ch) )
{
CHAR_DATA *wf = who_fighting(ch);
los = (exp_level(ch, ch->level+1) - exp_level(ch, ch->level)) * 0.03;
sprintf(buf, "You flee from combat! You lose %d experience.", los );
act( AT_FLEE, buf, ch, NULL, NULL, TO_CHAR );
gain_exp( ch, 0 - los );
if ( ( ch->pcdata->deity )
&& ( wf && wf->race == ch->pcdata->deity->npcrace ) )
adjust_favor( ch, 1, 1 );
else
adjust_favor( ch, 0, 1 );
}
if ( ch->fighting )
WAIT_STATE( who_fighting( ch ), 0 );
stop_fighting( ch, TRUE );
return;
}
los = (exp_level(ch, ch->level+1) - exp_level(ch, ch->level)) * 0.01;
sprintf(buf, "You attempt to flee from combat! You lose %d experience.\n\r", los );
send_to_char( buf, ch );
gain_exp( ch, 0 - los );
return;
}
void do_sla( CHAR_DATA *ch, char *argument )
{
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 arg[MAX_INPUT_LENGTH];
char arg2[MAX_INPUT_LENGTH];
argument = one_argument( argument, arg );
one_argument( argument, arg2 );
if ( arg[0] == '\0' )
{
send_to_char( "Slay whom?\n\r", ch );
return;
}
if ( ( victim = get_char_room( ch, arg ) ) == NULL )
{
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) && get_trust( victim ) >= get_trust( ch ) )
{
send_to_char( "You failed.\n\r", ch );
return;
}
if ( !str_cmp( arg2, "immolate" ) )
{
act( AT_IMMORT, "Your fireball turns $N into a blazing inferno.", ch, NULL, victim, TO_CHAR );
act( AT_IMMORT, "$n releases a single fireball in your direction.", ch, NULL, victim, TO_VICT );
act( AT_IMMORT, "$n points at $N, who bursts into flames.", ch, NULL, victim, TO_NOTVICT );
}
else if ( !str_cmp( arg2, "shatter" ) )
{
act( AT_IMMORT, "You freeze $N with a glance and shatter the frozen corpse into tiny shards.", ch, NULL, victim, TO_CHAR );
act( AT_IMMORT, "$n freezes you with a glance and shatters your frozen body into tiny shards.", ch, NULL, victim, TO_VICT );
act( AT_IMMORT, "$n freezes $N with a glance and shatters the frozen body into tiny shards.", ch, NULL, victim, TO_NOTVICT );
}
else if ( !str_cmp( arg2, "demon" ) )
{
act( AT_IMMORT, "You gesture, and a slavering demon appears. With a horrible grin, the", ch, NULL, victim, TO_CHAR );
act( AT_IMMORT, "foul creature turns on $N, who screams in panic before being eaten alive.", ch, NULL, victim, TO_CHAR );
act( AT_IMMORT, "$n gestures, and a slavering demon appears. The foul creature turns on", ch, NULL, victim, TO_VICT );
act( AT_IMMORT, "you with a horrible grin. You scream in panic before being eaten alive.", ch, NULL, victim, TO_VICT );
act( AT_IMMORT, "$n gestures, and a slavering demon appears. With a horrible grin, the", ch, NULL, victim, TO_NOTVICT );
act( AT_IMMORT, "foul creature turns on $N, who screams in panic before being eaten alive.", ch, NULL, victim, TO_NOTVICT );
}
else
{
act( AT_IMMORT, "You slay $N in cold blood!", ch, NULL, victim, TO_CHAR );
act( AT_IMMORT, "$n slays you in cold blood!", ch, NULL, victim, TO_VICT );
act( AT_IMMORT, "$n slays $N in cold blood!", ch, NULL, victim, TO_NOTVICT );
}
set_cur_char(victim);
raw_kill( ch, victim );
return;
}