/****************************************************************************
* [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame | *
* -----------------------------------------------------------| \\._.// *
* SmaugWiz (C) 1998 by Russ Pillsbury (Windows NT version) | (0...0) *
* -----------------------------------------------------------| ).:.( *
* SMAUG (C) 1994, 1995, 1996 by Derek Snider | {o o} *
* -----------------------------------------------------------| / ' ' \ *
* SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus, |~'~.VxvxV.~'~*
* Scryn, Swordbearer, Rennard, Tricops, and Gorog. | *
* ------------------------------------------------------------------------ *
* 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 Staerfeldt, Tom Madsen, and Katja Nyboe. *
* ------------------------------------------------------------------------ *
* Battle & death module *
****************************************************************************/
#include "stdafx.h"
#include "smaug.h"
#include "SysData.h"
#include "skill.h"
#include "mobiles.h"
#include "objects.h"
#include "rooms.h"
#include "deity.h"
#include "area.h"
#include "races.h"
#include "class.h"
#include "Exits.h"
#include "SmaugWizDoc.h"
#include "descriptor.h"
#include "character.h"
#include "smaugx.h"
extern CCharacter * gch_prev;
// Local functions.
void dam_message (CCharacter *ch, CCharacter *victim, int dam, int dt,
CObjData* pObj = NULL);
void death_cry (CCharacter *ch);
void group_gain (CCharacter *ch, CCharacter *victim);
int xp_compute (CCharacter *gch, CCharacter *victim);
int align_compute (CCharacter *gch, CCharacter *victim);
ch_ret one_hit (CCharacter *ch, CCharacter *victim, int dt);
int obj_hitroll (CObjData *obj);
BOOL dual_flip = FALSE;
BOOL HandleAttackFlags (CCharacter* ch, CCharacter* victim);
BOOL HandleDefenseFlags (CCharacter* ch, CCharacter* victim);
// Check to see if weapon is poisoned.
BOOL is_wielding_poisoned (CCharacter *ch)
{
CObjData *obj;
if ((obj = get_eq_char (ch, WEAR_WIELD)) && obj->IsPoisoned ())
return TRUE;
return FALSE;
}
// hunting, hating and fearing code -Thoric
BOOL is_hunting (CCharacter *ch, CCharacter *victim)
{
if (!ch->hunting || ch->hunting->who != victim)
return FALSE;
return TRUE;
}
BOOL is_hating (CCharacter *ch, CCharacter *victim)
{
if (!ch->hating || ch->hating->who != victim)
return FALSE;
return TRUE;
}
BOOL is_fearing (CCharacter *ch, CCharacter *victim)
{
if (!ch->fearing || ch->fearing->who != victim)
return FALSE;
return TRUE;
}
void stop_hunting (CCharacter *ch)
{
if (ch->hunting) {
STRFREE (ch->hunting->name);
delete ch->hunting;
ch->hunting = NULL;
}
}
void stop_hating (CCharacter *ch)
{
if (ch->hating) {
STRFREE (ch->hating->name);
delete ch->hating;
ch->hating = NULL;
}
}
void stop_fearing (CCharacter *ch)
{
if (ch->fearing) {
STRFREE (ch->fearing->name);
delete ch->fearing;
ch->fearing = NULL;
}
}
void start_hunting (CCharacter *ch, CCharacter *victim)
{
if (ch->hunting)
stop_hunting (ch);
ch->hunting = new HHF_DATA;
ch->hunting->name = QUICKLINK (victim->GetName ());
ch->hunting->who = victim;
}
void start_hating (CCharacter *ch, CCharacter *victim)
{
if (ch->hating)
stop_hating (ch);
ch->hating = new HHF_DATA;
ch->hating->name = QUICKLINK (victim->GetName ());
ch->hating->who = victim;
}
void start_fearing (CCharacter *ch, CCharacter *victim)
{
if (ch->fearing)
stop_fearing (ch);
ch->fearing = new HHF_DATA;
ch->fearing->name = QUICKLINK (victim->GetName ());
ch->fearing->who = victim;
}
// Get the current armor class for a vampire based on time of day
short VAMP_AC (CCharacter * ch)
{
if (ch->IsVampire () && ch->IsOutside ()) {
switch (weather_info.sunlight) {
case SUN_DARK:
return -10;
case SUN_RISE:
return 5;
case SUN_LIGHT:
return 10;
case SUN_SET:
return 2;
default:
return 0;
}
}
return 0;
}
int max_fight (CCharacter *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];
CCharacter *ch;
CCharacter *lst_ch;
CCharacter *victim;
CCharacter *rch, *rch_next;
CTimerData *timer, *timer_next;
ch_ret retcode;
CSkill *skill;
try {
lst_ch = NULL;
for (ch = last_char; ch; lst_ch = ch, ch = gch_prev) {
set_cur_char (ch);
gch_prev = ch->GetPrev ();
if (gch_prev && gch_prev->GetNext () != ch) {
bug ("FATAL: violence_update: %s->GetPrev ()->GetNext () "
"doesn't point to ch.", ch->GetName ());
bug ("Short-cutting here");
ch->SetPrev (NULL);
gch_prev = NULL;
sprintf (buf, "%s says, 'Prepare for the worst!'",
SysData.GetSupremeEntity ());
do_shout (ch, buf);
}
// 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->GetInRoom () || ! ch->GetName ()) {
gpDoc->LogString ("violence_update: bad ch record! "
"(Shortcutting.)", LOG_BUG);
sprintf (buf, "ch: %d ch->GetInRoom (): %d ch->GetPrev (): "
"%d ch->GetNext (): %d", (int) ch, (int) ch->GetInRoom (),
(int) ch->GetPrev (), (int) ch->GetNext ());
gpDoc->LogString (buf, LOG_BUG);
gpDoc->LogString (lastplayercmd, LOG_BUG);
if (lst_ch)
sprintf (buf, "lst_ch: %d lst_ch->GetPrev (): "
"%d lst_ch->GetNext (): %d", (int) lst_ch,
(int) lst_ch->GetPrev (), (int) lst_ch->GetNext ());
else
strcpy (buf, "lst_ch: NULL");
gpDoc->LogString (buf, LOG_BUG);
gch_prev = NULL;
continue;
}
// Experience gained during battle deceases as battle drags on
if (ch->GetFightData ())
if ((++ch->GetFightData ()->duration % 24) == 0)
ch->GetFightData ()->xp =
((ch->GetFightData ()->xp * 9) / 10);
for (timer = ch->first_timer; timer; timer = timer_next) {
timer_next = timer->GetNext ();
if (timer->CountDown () <= 0) {
if (timer->GetType () == TIMER_ASUPRESSED) {
if (timer->GetValue () == -1) {
timer->SetCount (1000);
continue;
}
}
if (timer->GetType () == TIMER_DO_FUN) {
int tempsub;
tempsub = ch->GetSubstate ();
ch->SetSubstate (timer->GetValue ());
timer->DoFun (ch, "");
if (char_died (ch))
break;
ch->SetSubstate (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
POSITION pos = ch->m_AffectList.GetHeadPosition ();
while (pos) {
CAffectData *paf = ch->m_AffectList.GetNext (pos);
if (paf->duration > 0) {
--paf->duration;
continue;
}
if (paf->duration == 0) {
CAffectData *pNext = NULL;
if (pos)
pNext = ch->m_AffectList.GetAt (pos);
if (! pNext || pNext->type != paf->type
|| pNext->duration > 0) {
skill = SkillTable.GetValidSkill (paf->type);
if (paf->type > 0 && skill && skill->ValidOffMsg ()) {
set_char_color (AT_WEAROFF, ch);
ch->SendText (skill->GetOffMsg ());
ch->SendText ("\n\r");
}
}
if (paf->type == gsn_possess) {
ch->GetDesc ()->m_pCharacter = ch->GetDesc ()->m_pOriginal;
ch->GetDesc ()->m_pOriginal = NULL;
ch->GetDesc ()->m_pCharacter->SetDesc (ch->GetDesc ());
ch->GetDesc ()->m_pCharacter->switched = NULL;
ch->SetDesc (NULL);
}
affect_remove (ch, paf);
}
}
if (char_died (ch))
continue;
// check for exits moving players around
// if ((retcode = pullcheck (ch, pulse)) == rCHAR_DIED
// || char_died (ch))
// continue;
if ((victim = ch->GetFightWho ()) == NULL || ch->IsParalysed ())
continue;
// Let the battle begin!
retcode = rNONE;
if (ch->GetInRoom ()->IsSafe ()) {
sprintf (buf, "violence_update: %s fighting %s in a SAFE room.",
ch->GetName (), victim->GetName ());
gpDoc->LogString (buf);
stop_fighting (ch, TRUE);
}
else
if (ch->IsAwake () && ch->GetInRoom () == victim->GetInRoom ())
retcode = multi_hit (ch, victim, TYPE_UNDEFINED);
else
stop_fighting (ch, FALSE);
if (char_died (ch))
continue;
if (retcode == rCHAR_DIED || (victim = ch->GetFightWho ()) == 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;
if (ch->IsNpc ()) {
// NPC special attack flags -Thoric
if (ch->HasAttacks ())
if (! HandleAttackFlags (ch, victim)) then continue;
// NPC special defense flags -Thoric
if (ch->HasDefenses ())
if (! HandleDefenseFlags (ch, victim)) then continue;
}
// Fun for the whole family!
for (rch = ch->GetInRoom ()->first_person; rch; rch = rch_next) {
rch_next = rch->GetNextInRoom ();
// Group Fighting Styles Support:
// If ch is tanking
// If rch is using a more aggressive style than ch
// Then rch is the new tank -h
if ((! ch->IsNpc () && ! rch->IsNpc ())
&& rch != ch
&& rch->GetFightWho () == ch
&& ! rch->GetFightWho ()->IsAutonomous ()
&& rch->GetStyle () < ch->GetStyle ())
rch->GetFightWho ()->SetFightWho (rch);
if (rch->IsAwake () && !rch->GetFightData ()) {
// PC's auto-assist others in their group.
if (! ch->IsNpc () || ch->IsCharmed ()) {
if ((!rch->IsNpc () || rch->IsCharmed ())
&& 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 (rch->IsNpc () && !rch->IsCharmed ()
&& ! rch->IsNoAssist ()) {
if (char_died (ch))
break;
if (rch->GetMobIndex () == ch->GetMobIndex ()
|| number_bits (3) == 0) {
CCharacter *vch;
CCharacter *target;
int number;
target = NULL;
number = 0;
vch = ch->GetInRoom ()->first_person;
for ( ; vch; vch = vch->GetNext ()) {
if (can_see (rch, vch)
&& is_same_group (vch, victim)
&& number_range (0, number) == 0) {
if (vch->mount && vch->mount == rch)
target = NULL;
else {
target = vch;
number++;
}
}
}
if (target)
multi_hit (rch, target, TYPE_UNDEFINED);
}
}
}
}
}
}
catch (...) {
CSwException ex;
ex.Printf ("violence_update Exception.");
throw ex;
}
}
BOOL HandleAttackFlags (CCharacter* ch, CCharacter* victim)
{
if (30 + (ch->GetLevel () / 4) < number_percent ()) then return TRUE;
int attacktype = -1;
int cnt = 0;
int retcode;
for (;;) {
if (cnt++ > 10) {
attacktype = -1;
break;
}
attacktype = number_range (7, MAX_ATTACK_TYPE-1);
if (ch->CanAttack (attacktype))
break;
}
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 (SkillTable.Lookup ("energy drain"), ch->GetLevel (), ch, victim);
break;
case ATCK_FIREBREATH:
retcode = spell_fire_breath (SkillTable.Lookup ("fire breath"), ch->GetLevel (), ch, victim);
break;
case ATCK_FROSTBREATH:
retcode = spell_frost_breath (SkillTable.Lookup ("frost breath"), ch->GetLevel (), ch, victim);
break;
case ATCK_ACIDBREATH:
retcode = spell_acid_breath (SkillTable.Lookup ("acid breath"), ch->GetLevel (), ch, victim);
break;
case ATCK_LIGHTNBREATH:
retcode = spell_lightning_breath (SkillTable.Lookup ("lightning breath"), ch->GetLevel (), ch, victim);
break;
case ATCK_GASBREATH:
retcode = spell_gas_breath (SkillTable.Lookup ("gas breath"), ch->GetLevel (), ch, victim);
break;
case ATCK_SPIRALBLAST:
retcode = spell_spiral_blast (SkillTable.Lookup ("spiral blast"),
ch->GetLevel (), ch, victim);
break;
case ATCK_POISON:
retcode = spell_poison (gsn_poison, ch->GetLevel (), ch, victim);
break;
case ATCK_NASTYPOISON:
/*
retcode = spell_nasty_poison (SkillTable.Lookup ("nasty poison"), ch->GetLevel (), ch, victim);
*/
break;
case ATCK_GAZE:
/*
retcode = spell_gaze (SkillTable.Lookup ("gaze"), ch->GetLevel (), ch, victim);
*/
break;
case ATCK_BLINDNESS:
retcode = spell_blindness (gsn_blindness, ch->GetLevel (), ch, victim);
break;
case ATCK_CAUSESERIOUS:
retcode = spell_cause_serious (SkillTable.Lookup ("cause serious"), ch->GetLevel (), ch, victim);
break;
case ATCK_EARTHQUAKE:
retcode = spell_earthquake (SkillTable.Lookup ("earthquake"), ch->GetLevel (), ch, victim);
break;
case ATCK_CAUSECRITICAL:
retcode = spell_cause_critical (SkillTable.Lookup ("cause critical"), ch->GetLevel (), ch, victim);
break;
case ATCK_CURSE:
retcode = spell_curse (SkillTable.Lookup ("curse"), ch->GetLevel (), ch, victim);
break;
case ATCK_FLAMESTRIKE:
retcode = spell_flamestrike (SkillTable.Lookup ("flamestrike"), ch->GetLevel (), ch, victim);
break;
case ATCK_HARM:
retcode = spell_harm (SkillTable.Lookup ("harm"), ch->GetLevel (), ch, victim);
break;
case ATCK_FIREBALL:
retcode = spell_fireball (SkillTable.Lookup ("fireball"), ch->GetLevel (), ch, victim);
break;
case ATCK_COLORSPRAY:
retcode = spell_colour_spray (SkillTable.Lookup ("colour spray"), ch->GetLevel (), ch, victim);
break;
case ATCK_WEAKEN:
retcode = spell_weaken (SkillTable.Lookup ("weaken"), ch->GetLevel (), ch, victim);
break;
}
if (attacktype != -1 && retcode == rCHAR_DIED || (char_died (ch)))
return FALSE;
return TRUE;
}
BOOL HandleDefenseFlags (CCharacter* ch, CCharacter* victim)
{
if (50 + (ch->GetLevel () / 4) < number_percent ()) then return TRUE;
int attacktype = -1;
int cnt = 0;
int retcode;
for (;;) {
if (cnt++ > 10) {
attacktype = -1;
break;
}
attacktype = number_range (2, MAX_DEFENSE_TYPE-1);
if (ch->CanDefend (attacktype))
break;
}
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 (SkillTable.Lookup ("cure light"),
ch->GetLevel (), 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 (SkillTable.Lookup ("cure serious"),
ch->GetLevel (), 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 (SkillTable.Lookup ("cure critical"),
ch->GetLevel (), 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 (SkillTable.Lookup (
"dispel magic"), ch->GetLevel (), 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 (SkillTable.Lookup (
"dispel evil"), ch->GetLevel (), ch, victim);
break;
case DFND_SHOCKSHIELD:
if (! ch->HasShockShield ()) {
act (AT_MAGIC, "$n utters a few incantations...", ch,
NULL, NULL, TO_ROOM);
retcode = spell_smaug (SkillTable.Lookup ("shockshield"),
ch->GetLevel (), ch, ch);
}
else retcode = rNONE;
break;
case DFND_FIRESHIELD:
if (! ch->HasFireShield ()) {
act (AT_MAGIC, "$n utters a few incantations...", ch,
NULL, NULL, TO_ROOM);
retcode = spell_smaug (SkillTable.Lookup ("fireshield"),
ch->GetLevel (), ch, ch);
}
else retcode = rNONE;
break;
case DFND_ICESHIELD:
if (! ch->HasIceShield ()) {
act (AT_MAGIC, "$n utters a few incantations...", ch,
NULL, NULL, TO_ROOM);
retcode = spell_smaug (SkillTable.Lookup ("iceshield"),
ch->GetLevel (), ch, ch);
}
else retcode = rNONE;
break;
case DFND_TRUESIGHT:
if (! ch->HasTrueSight ()) {
act (AT_MAGIC, "$n utters a few incantations...", ch,
NULL, NULL, TO_ROOM);
retcode = spell_smaug (SkillTable.Lookup ("true"),
ch->GetLevel (), ch, ch);
}
else retcode = rNONE;
break;
case DFND_SANCTUARY:
if (! victim->HasSanctuary ()) {
act (AT_MAGIC, "$n mutters a few incantations...",
ch, NULL, NULL, TO_ROOM);
retcode = spell_smaug (SkillTable.Lookup ("sanctuary"),
ch->GetLevel (), ch, ch);
}
else retcode = rNONE;
break;
}
if (attacktype != -1 && retcode == rCHAR_DIED || (char_died (ch)))
return FALSE;
return TRUE;
}
// Do one group of attacks.
ch_ret multi_hit (CCharacter *ch, CCharacter *victim, int dt)
{
int chance;
int dual_bonus;
ch_ret retcode;
/* add timer if player is attacking another player */
if (!ch->IsNpc () && !victim->IsNpc ())
add_timer (ch, TIMER_RECENTFIGHT, 20, NULL, 0);
if (!ch->IsNpc () && ch->IsNice () && !victim->IsNpc ())
return rNONE;
if ((retcode = one_hit (ch, victim, dt)) != rNONE)
return retcode;
if (ch->GetFightWho () != 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 = ch->IsNpc () ? 100 : (ch->GetPcData ()->learned[gsn_berserk]*5/2);
if (ch->IsBeserk () && number_percent () < chance)
if ((retcode = one_hit (ch, victim, dt)) != rNONE ||
ch->GetFightWho () != victim)
return retcode;
if (get_eq_char (ch, WEAR_DUAL_WIELD))
{
dual_bonus = ch->IsNpc () ? (ch->GetLevel () / 10) : (ch->GetPcData ()->learned[gsn_dual_wield] / 10);
chance = ch->IsNpc () ? ch->GetLevel () : ch->GetPcData ()->learned[gsn_dual_wield];
if (number_percent () < chance)
{
learn_from_success (ch, gsn_dual_wield);
retcode = one_hit (ch, victim, dt);
if (retcode != rNONE || ch->GetFightWho () != victim)
return retcode;
}
else
learn_from_failure (ch, gsn_dual_wield);
}
else
dual_bonus = 0;
if (ch->GetMove () < 10)
dual_bonus = -20;
/*
* NPC predetermined number of attacks -Thoric
*/
if (ch->IsNpc () && ch->numattacks > 0)
{
for (chance = 0; chance <= ch->numattacks; chance++)
{
retcode = one_hit (ch, victim, dt);
if (retcode != rNONE || ch->GetFightWho () != victim)
return retcode;
}
return retcode;
}
chance = ch->IsNpc () ? ch->GetLevel ()
: (int) ((ch->GetPcData ()->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 || ch->GetFightWho () != victim)
return retcode;
}
else
learn_from_failure (ch, gsn_second_attack);
chance = ch->IsNpc () ? ch->GetLevel ()
: (int) ((ch->GetPcData ()->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 || ch->GetFightWho () != victim)
return retcode;
}
else
learn_from_failure (ch, gsn_third_attack);
chance = ch->IsNpc () ? ch->GetLevel ()
: (int) ((ch->GetPcData ()->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 || ch->GetFightWho () != victim)
return retcode;
}
else
learn_from_failure (ch, gsn_fourth_attack);
chance = ch->IsNpc () ? ch->GetLevel ()
: (int) ((ch->GetPcData ()->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 || ch->GetFightWho () != victim)
return retcode;
}
else
learn_from_failure (ch, gsn_fifth_attack);
retcode = rNONE;
chance = ch->IsNpc () ? (int) (ch->GetLevel () / 2) : 0;
if (number_percent () < chance)
retcode = one_hit (ch, victim, dt);
if (retcode == rNONE)
{
int move;
if (! ch->IsFlying ()
&& ! ch->IsFloating ())
move = encumbrance (ch, movement_loss[UMIN (SECT_MAX-1, ch->GetInRoom ()->sector_type)]);
else
move = encumbrance (ch, 1);
if (ch->GetMove ())
ch->SetMove (UMAX (0, ch->GetMove () - move));
}
return retcode;
}
/*
* Weapon types, haus
*/
int weapon_prof_bonus_check (CCharacter *ch, CObjData *wield, int *gsn_ptr)
{
int bonus;
bonus = 0; *gsn_ptr = -1;
if (!ch->IsNpc () && ch->GetLevel () > 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->GetPcData ()->learned[*gsn_ptr] -50)/10);
/* Reduce weapon bonuses for misaligned clannies.
if (ch->IsClanned ())
{
bonus = bonus /
(1 + abs (ch->alignment - ch->GetPcData ()->clan->alignment) / 1000);
}*/
if (ch->IsDevoted ()) {
bonus = bonus - abs (ch->GetPcData ()->favor) / -100 ;
}
}
return bonus;
}
// Calculate the tohit bonus on the object and return RIS values.
// -- Altrag
int obj_hitroll (CObjData *obj)
{
int tohit = 0;
CAffectData *paf;
CAffectList &IList = obj->pIndexData->AffList;
POSITION apos = IList.GetHeadPosition ();
while (apos) {
paf = IList.GetNext (apos);
if (paf->location == APPLY_HITROLL)
tohit += paf->modifier;
}
CAffectList &AList = obj->AffList;
apos = AList.GetHeadPosition ();
while (apos) {
paf = AList.GetNext (apos);
if (paf->location == APPLY_HITROLL)
tohit += paf->modifier;
}
return tohit;
}
/*
* Offensive shield level modifier
*/
short off_shld_lvl (CCharacter *ch, CCharacter *victim)
{
short lvl;
if (!ch->IsNpc ()) /* players get much less effect */
{
lvl = UMAX (1, (ch->GetLevel () - 10) / 2);
if (number_percent () + (victim->GetLevel () - lvl) < 35)
return lvl;
else
return 0;
}
else
{
lvl = ch->GetLevel () / 2;
if (number_percent () + (victim->GetLevel () - lvl) < 70)
return lvl;
else
return 0;
}
}
// Hit one guy once.
ch_ret one_hit (CCharacter *ch, CCharacter *victim, int dt)
{
CObjData *wield;
int victim_ac;
int thac0;
int thac0_00;
int thac0_32;
int plusris;
int dam;
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->GetPosition () == POS_DEAD
|| ch->GetInRoom () != victim->GetInRoom ())
return rVICT_DIED;
// Figure out the weapon doing the damage -Thoric
// Dual wield support -- switch weapons each attack
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);
// make sure fight is already started
if (ch->GetFightData () && dt == TYPE_UNDEFINED && ch->IsNpc () && ch->HasAttacks ()) {
cnt = 0;
for (;;) {
attacktype = number_range (0, 6);
if (ch->CanAttack (attacktype))
break;
if (cnt++ > 16) {
attacktype = -1;
break;
}
}
if (attacktype == ATCK_BACKSTAB)
attacktype = -1;
if (wield && number_percent () > 25)
attacktype = -1;
if (! wield && number_percent () > 50)
attacktype = -1;
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 (ch->IsNpc ()) {
thac0_00 = ch->mobthac0;
thac0_32 = 0;
} else {
thac0_00 = ClassTable.GetThac0 (ch->GetClass ());
thac0_32 = ClassTable.GetThac32 (ch->GetClass ());
}
thac0 = interpolate (ch->GetLevel (), thac0_00, thac0_32) -
GET_HITROLL (ch);
victim_ac = UMAX (-19, (int) (victim->GetAc () / 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->GetFightData () && ch->GetFightData ()->who == victim) {
short times = ch->GetFightData ()->timeskilled;
if (times) {
short intdiff = ch->GetIntelligence () -
victim->GetIntelligence ();
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);
return rNONE;
}
// Hit.
// Calc damage.
if (!wield) // bare hand 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;
// Calculate Damage Modifiers from Victim's Fighting Style
switch (victim->GetPosition ()) {
case POS_BERSERK:
dam = (int) (1.2 * dam);
break;
case POS_AGGRESSIVE:
dam = (int) (1.1 * dam);
break;
case POS_DEFENSIVE:
dam = (int) (.85 * dam);
break;
case POS_EVASIVE:
dam = (int) (.8 * dam);
}
// Calculate Damage Modifiers from Attacker's Fighting Style
switch (ch->GetPosition ()) {
case POS_BERSERK:
dam = (int) (1.2 * dam);
break;
case POS_AGGRESSIVE:
dam = (int) (1.1 * dam);
break;
case POS_DEFENSIVE:
dam = (int) (.85 * dam);
break;
case POS_EVASIVE:
dam = (int) (.8 * dam);
}
if (!ch->IsNpc () && ch->GetPcData ()->learned[gsn_enhanced_damage] > 0) {
dam += (int) (dam *
ch->GetPcData ()->learned [gsn_enhanced_damage] / 120);
learn_from_success (ch, gsn_enhanced_damage);
}
if (! victim->IsAwake ())
dam *= 2;
if (dt == gsn_backstab)
dam *= (2 + URANGE (2, ch->GetLevel () -
(victim->GetLevel ()/4), 30) / 8);
if (dt == gsn_circle)
dam *= (2 + URANGE (2, ch->GetLevel () -
(victim->GetLevel ()/4), 30) / 16);
if (dam <= 0)
dam = 1;
plusris = 0;
if (wield) {
if (wield->IsMagic ())
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 (victim->IsImmune (x))
imm = x;
if (victim->CanResist (x))
res = x;
if (victim->IsSusceptible (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 < SkillTable.GetCount ()) {
CSkill *skill = SkillTable.GetSkill (dt);
BOOL found = FALSE;
if (skill->ValidImmuneCharMsg ()) {
act (AT_HIT, skill->GetImmuneCharMsg (), ch, NULL, victim,
TO_CHAR);
found = TRUE;
}
if (skill->ValidImmuneVictMsg ()) {
act (AT_HITME, skill->GetImmuneVictMsg (), ch, NULL, victim,
TO_VICT);
found = TRUE;
}
if (skill->ValidImmuneRoomMsg ()) {
act (AT_ACTION, skill->GetImmuneRoomMsg (), 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
// Each successful hit casts a spell
if (wield && ! victim->IsImmuneMagic ()
&& ! victim->GetInRoom ()->IsNoMagic ()) {
CAffectData *aff;
CAffectList &IList = wield->pIndexData->AffList;
POSITION apos = IList.GetHeadPosition ();
while (apos) {
aff = IList.GetNext (apos);
if (aff->location == APPLY_WEAPONSPELL
&& SkillTable.IsValid (aff->modifier)
&& SkillTable.GetSpellFunction (aff->modifier))
retcode = (*SkillTable.GetSpellFunction (aff->modifier))
(aff->modifier, (wield->level+3)/3, ch, victim);
}
if (retcode != rNONE || char_died (ch) || char_died (victim))
return retcode;
CAffectList &AList = wield->AffList;
apos = AList.GetHeadPosition ();
while (apos) {
aff = AList.GetNext (apos);
if (aff->location == APPLY_WEAPONSPELL
&& SkillTable.IsValid (aff->modifier)
&& SkillTable.GetSpellFunction (aff->modifier))
retcode = (*SkillTable.GetSpellFunction ( aff->modifier))
(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 (victim->IsAffected (AFF_FIRESHIELD)
&& !ch->IsAffected (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 (victim->IsAffected (AFF_ICESHIELD)
&& ! ch->IsAffected (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 (victim->IsAffected (AFF_SHOCKSHIELD)
&& ! ch->IsAffected (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;
return retcode;
}
// Hit one guy with a projectile.
// Handles use of missile weapons (wield = missile weapon)
// or thrown items/weapons
ch_ret projectile_hit (CCharacter *ch, CCharacter *victim, CObjData *wield,
CObjData *projectile, short dist)
{
int victim_ac;
int thac0;
int thac0_00;
int thac0_32;
int plusris;
int dam;
int diceroll;
int prof_bonus;
int prof_gsn = -1;
int proj_bonus;
int dt;
ch_ret retcode;
if (! projectile)
return rNONE;
if (projectile->item_type == ITEM_PROJECTILE
|| projectile->item_type == ITEM_WEAPON) {
dt = TYPE_HIT + projectile->value [3];
proj_bonus = number_range (projectile->value [1],
projectile->value [2]);
} else {
dt = TYPE_UNDEFINED;
proj_bonus = number_range (1, URANGE (2,
get_obj_weight (projectile), 100));
}
// Can't beat a dead char!
if (victim->IsDead () || char_died (victim)) {
extract_obj (projectile);
return rVICT_DIED;
}
if (wield)
prof_bonus = weapon_prof_bonus_check (ch, wield, &prof_gsn);
else
prof_bonus = 0;
if (dt == TYPE_UNDEFINED) {
dt = TYPE_HIT;
if (wield && wield->item_type == ITEM_MISSILE_WEAPON)
dt += wield->value [3];
}
// Calculate to-hit-armor-class-0 versus armor.
if (ch->IsNpc ()) {
thac0_00 = ch->mobthac0;
thac0_32 = 0;
} else {
thac0_00 = ClassTable.GetThac0 (ch->GetClass ());
thac0_32 = ClassTable.GetThac32 (ch->GetClass ());
}
thac0 = interpolate (ch->GetLevel (), thac0_00, thac0_32)
- GET_HITROLL (ch) + (dist*2);
victim_ac = UMAX (-19, (victim->GetAc () / 10));
// if you can't see what's coming...
if (! can_see_obj (victim, *projectile))
++victim_ac;
if (! can_see (ch, victim))
victim_ac -= 4;
// 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);
// Do something with the projectile
if (number_percent () < 50)
extract_obj (projectile);
else {
if (projectile->in_obj)
obj_from_obj (projectile);
if (projectile->carried_by)
obj_from_char (projectile);
obj_to_room (projectile, victim->GetInRoom ());
}
damage (ch, victim, 0, dt);
return rNONE;
}
// Hit, Calc damage.
if (! wield) // dice formula fixed by Thoric
dam = proj_bonus;
else
dam = number_range (wield->value [1],
wield->value [2]) + (proj_bonus / 10);
// Bonuses.
dam += GET_DAMROLL (ch);
if (prof_bonus)
dam += prof_bonus / 4;
// Calculate Damage Modifiers from Victim's Fighting Style
if (victim->GetPosition () == POS_BERSERK)
dam = (int) (dam * 1.2);
else if (victim->GetPosition () == POS_AGGRESSIVE)
dam = (int) (dam * 1.1);
else if (victim->GetPosition () == POS_DEFENSIVE)
dam = (int) (dam * .85);
else if (victim->GetPosition () == POS_EVASIVE)
dam = (int) (dam * .8);
if (! ch->IsNpc ()
&& ch->GetPcData ()->learned [gsn_enhanced_damage] > 0) {
dam += ch->GetLearnedPercent (gsn_enhanced_damage) / 120;
learn_from_success (ch, gsn_enhanced_damage);
}
if (! victim->IsAwake ())
dam *= 2;
if (dam <= 0)
dam = 1;
plusris = 0;
if (projectile->IsMagic ())
dam = ris_damage (victim, dam, RIS_MAGIC);
else
dam = ris_damage (victim, dam, RIS_NONMAGIC);
// Handle PLUS1 - PLUS6 ris bits vs. weapon hitroll -Thoric
if (wield)
plusris = obj_hitroll (wield);
// check for RIS_PLUSx -Thoric
if (dam) {
if (plusris)
plusris = RIS_PLUS1 << UMIN (plusris, 7);
// initialize values to handle a zero plusris
int imm = -1;
int res = -1;
int sus = 1;
// find high ris
for (int x = RIS_PLUS1; x <= RIS_PLUS6; x <<= 1) {
if (victim->IsImmune (x))
imm = x;
if (victim->CanResist (x))
res = x;
if (victim->IsSusceptible (x))
sus = x;
}
int 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) {
CSkill *pSkill = SkillTable.GetSkill (dt);
if (pSkill) {
BOOL bFound = FALSE;
if (pSkill->ValidImmuneCharMsg ()) {
act (AT_HIT, pSkill->GetImmuneCharMsg (), ch, NULL,
victim, TO_CHAR);
bFound = TRUE;
}
if (pSkill->ValidImmuneVictMsg ()) {
act (AT_HITME, pSkill->GetImmuneVictMsg (), ch, NULL,
victim, TO_VICT);
bFound = TRUE;
}
if (pSkill->ValidImmuneRoomMsg ()) {
act (AT_ACTION, pSkill->GetImmuneRoomMsg (), ch, NULL,
victim, TO_NOTVICT);
bFound = TRUE;
}
if (bFound) {
if (number_percent() < 50)
extract_obj (projectile);
else {
if (projectile->in_obj)
obj_from_obj (projectile);
if (projectile->carried_by)
obj_from_char (projectile);
obj_to_room (projectile, victim->GetInRoom ());
}
return rNONE;
}
}
dam = 0;
}
if ((retcode = damage (ch, victim, dam, dt)) != rNONE) {
extract_obj (projectile);
return retcode;
}
if (char_died (ch)) {
extract_obj (projectile);
return rCHAR_DIED;
}
if (char_died (victim)) {
extract_obj (projectile);
return rVICT_DIED;
}
retcode = rNONE;
if (dam == 0) {
if (number_percent() < 50)
extract_obj (projectile);
else {
if (projectile->in_obj)
obj_from_obj (projectile);
if (projectile->carried_by)
obj_from_char (projectile);
obj_to_room (projectile, victim->GetInRoom ());
}
return retcode;
}
// weapon spells -Thoric
if (wield && ! victim->IsImmuneMagic ()
&& ! victim->GetInRoom ()->IsNoMagic ()) {
CAffectList &IList = wield->pIndexData->AffList;
POSITION pos = IList.GetHeadPosition ();
while (pos) {
CAffectData &Af = *IList.GetNext (pos);
if (Af.location == APPLY_WEAPONSPELL
&& SkillTable.IsValid (Af.modifier)
&& SkillTable.GetSpellFunction (Af.modifier))
retcode = (*SkillTable.GetSpellFunction (Af.modifier))
(Af.modifier, (wield->level+3)/3, ch, victim);
}
if (retcode != rNONE || char_died (ch) || char_died (victim)) {
extract_obj (projectile);
return retcode;
}
CAffectList &AList = wield->AffList;
pos = AList.GetHeadPosition ();
while (pos) {
CAffectData &Af = *AList.GetNext (pos);
if (Af.location == APPLY_WEAPONSPELL
&& SkillTable.IsValid (Af.modifier)
&& SkillTable.GetSpellFunction (Af.modifier))
retcode = (*SkillTable.GetSpellFunction (Af.modifier))
(Af.modifier, (wield->level+3)/3, ch, victim);
}
if (retcode != rNONE || char_died (ch) || char_died (victim)) {
extract_obj (projectile);
return retcode;
}
}
extract_obj (projectile);
return retcode;
}
// Calculate damage based on resistances, immunities and suceptibilities
// -Thoric
short ris_damage (CCharacter *ch, short dam, int ris)
{
short modifier;
modifier = 10;
if (ch->IsImmune (ris))
modifier -= 10;
if (ch->CanResist (ris))
modifier -= 2;
if (ch->IsSusceptible (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 (CCharacter *ch, CCharacter *victim, int dam, int dt)
{
char buf1 [MAX_STRING_LENGTH];
short dameq;
short maxdam;
BOOL npcvict;
BOOL loot;
int xp_gain;
CObjData *damobj;
short dampmod;
CCharacter *gch;
int init_gold, new_gold, gold_diff;
ch_ret retcode = rNONE;
if (! ch) {
bug ("Damage: null ch!");
return rERROR;
}
if (! victim) {
bug ("Damage: null victim!");
return rVICT_DIED;
}
if (victim->GetPosition () == POS_DEAD)
return rVICT_DIED;
npcvict = victim->IsNpc ();
// 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 (IS_DRAIN (dt))
dam = ris_damage (victim, dam, RIS_DRAIN);
else if (dt == gsn_poison || IS_POISON (dt))
dam = ris_damage (victim, dam, RIS_POISON);
else if (dt == (TYPE_HIT+DAM_POUND) || dt == (TYPE_HIT+DAM_CRUSH)
|| dt == (TYPE_HIT+DAM_STONE) || dt == (TYPE_HIT+DAM_PEA))
dam = ris_damage (victim, dam, RIS_BLUNT);
else if (dt == (TYPE_HIT+DAM_STAB) || dt == (TYPE_HIT+DAM_PIERCE)
|| dt == (TYPE_HIT+DAM_BITE) || dt == (TYPE_HIT+DAM_BOLT)
|| dt == (TYPE_HIT+DAM_DART) || dt == (TYPE_HIT+DAM_ARROW))
dam = ris_damage (victim, dam, RIS_PIERCE);
else if (dt == (TYPE_HIT+DAM_SLICE) || dt == (TYPE_HIT+DAM_SLASH)
|| dt == (TYPE_HIT+DAM_WHIP) || dt == (TYPE_HIT+DAM_CLAW))
dam = ris_damage (victim, dam, RIS_SLASH);
if (dam == -1) {
if (dt >= 0 && dt < SkillTable.GetCount ()) {
BOOL found = FALSE;
CSkill *skill = SkillTable.GetSkill (dt);
if (skill->ValidImmuneCharMsg ()) {
act (AT_HIT, skill->GetImmuneCharMsg (),
ch, NULL, victim, TO_CHAR);
found = TRUE;
}
if (skill->ValidImmuneVictMsg ()) {
act (AT_HITME, skill->GetImmuneVictMsg (),
ch, NULL, victim, TO_VICT);
found = TRUE;
}
if (skill->ValidImmuneRoomMsg ()) {
act (AT_ACTION, skill->GetImmuneRoomMsg (),
ch, NULL, victim, TO_NOTVICT);
found = TRUE;
}
if (found)
return rNONE;
}
dam = 0;
}
}
// Precautionary step mainly to prevent people in Hell from finding
// a way out. --Shaddai
if (victim->GetInRoom ()->IsSafe ())
dam = 0;
if (dam && npcvict && ch != victim) {
if (! victim->IsAction (ACT_SENTINEL)) {
if (victim->hunting) {
if (victim->hunting->who != ch) {
STRFREE (victim->hunting->name);
victim->hunting->name = QUICKLINK (ch->GetName ());
victim->hunting->who = ch;
}
}
else if (! victim->IsPacifist ()) // Gorog
start_hunting (victim, ch);
}
if (victim->hating) {
if (victim->hating->who != ch) {
STRFREE (victim->hating->name);
victim->hating->name = QUICKLINK (ch->GetName ());
victim->hating->who = ch;
}
}
else if (! victim->IsPacifist ()) // Gorog
start_hating (victim, ch);
}
// Stop up any residual loopholes.
maxdam = (dt == gsn_backstab) ?
ch->GetLevel () * 80 : ch->GetLevel () * 30;
if (dam > maxdam) {
bug ("Damage: %d more than %d points!", dam, maxdam);
bug ("** %s (lvl %d) -> %s **",
ch->GetName (), ch->GetLevel (), victim->GetName ());
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->GetPosition () > POS_STUNNED) {
if (! victim->GetFightData ())
set_fighting (victim, ch);
if (victim->GetFightData ()) {
if (victim->IsNpc ())
victim->SetPosition (POS_FIGHTING);
else switch (victim->GetStyle ()) {
case (STYLE_EVASIVE):
victim->SetPosition (POS_EVASIVE);
break;
case (STYLE_DEFENSIVE):
victim->SetPosition (POS_DEFENSIVE);
break;
case (STYLE_AGGRESSIVE):
victim->SetPosition (POS_AGGRESSIVE);
break;
case (STYLE_BERSERK):
victim->SetPosition (POS_BERSERK);
break;
default:
victim->SetPosition (POS_FIGHTING);
}
}
}
if (victim->GetPosition () > POS_STUNNED) {
if (! ch->GetFightData ())
set_fighting (ch, victim);
// If victim is charmed, ch might attack victim's master.
if (ch->IsNpc ()
&& npcvict
&& victim->IsCharmed ()
&& victim->GetMaster ()
&& victim->GetMaster ()->GetInRoom () == ch->GetInRoom ()
&& number_bits (3) == 0) {
stop_fighting (ch, FALSE);
retcode = multi_hit (ch, victim->GetMaster (), TYPE_UNDEFINED);
return retcode;
}
}
// More charm stuff.
if (victim->GetMaster () == 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.
#ifdef XXXX
/*
short anopc = 0; // # of (non-pkill) pc in a (ch)
short bnopc = 0; // # of (non-pkill) pc in b (victim)
CCharacter *lch; // leader ch
// count the # of non-pkill pc in a (not including == ch)
for (gch = ch->GetInRoom ()->first_person; gch;
gch = gch->GetNextInRoom ())
if (is_same_group (ch, gch) && ! gch->IsNpc ()
&& ! gch->IsPkiller () && (ch != gch))
anopc++;
// count the # of non-pkill pc in b (not including == victim)
for (gch = victim->GetInRoom ()->first_person; gch;
gch = gch->GetNextInRoom ())
if (is_same_group (victim, gch) && ! gch->IsNpc ()
&& ! gch->IsPkiller () && (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->GetLeader () ? ch->GetLeader () : 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->GetInRoom ()->first_person; gch;
gch = gch->GetNextInRoom ()) {
if (is_same_group (lch, gch) && (lch != gch)
&& ! gch->IsNpc () && ! gch->IsPkiller ())
stop_follower (gch);
}
}
}
else for (gch = ch->GetInRoom ()->first_person; gch;
gch = gch->GetNextInRoom ()) {
// ch is leader - disband non-pks from group
if (is_same_group (ch, gch) && (ch != gch)
&& (! gch->IsPkiller () && ! gch->IsNpc ()))
stop_follower (gch);
}
// time to look at the victims group through its leader
lch = victim->GetLeader () ? victim->GetLeader () : 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->GetInRoom ()->first_person; gch;
gch = gch->GetNextInRoom ()) {
if (is_same_group (lch, gch) && (lch != gch)
&& (! gch->IsPkiller () && ! gch->IsNpc ()))
stop_follower (gch);
}
}
} else {
// victim is leader of group - disband non-pks
for (gch = victim->GetInRoom ()->first_person; gch;
gch = gch->GetNextInRoom ()) {
if (is_same_group (victim, gch) && (victim != gch)
&& ! gch->IsPkiller () && ! gch->IsNpc ())
stop_follower (gch);
}
}
}
*/
#endif
short anopc = 0; // # of (non-pkill) pc in a (ch)
short bnopc = 0; // # of (non-pkill) pc in b (victim)
// count the # of non-pkill pc in a (not including == ch)
for (gch = ch->GetInRoom ()->first_person; gch;
gch = gch->GetNextInRoom ())
if (is_same_group (ch, gch) && ! gch->IsNpc ()
&& ! gch->IsPkiller () && (ch != gch))
anopc++;
// count the # of non-pkill pc in b (not including == victim)
for (gch = victim->GetInRoom ()->first_person; gch;
gch = gch->GetNextInRoom ())
if (is_same_group (victim, gch) && ! gch->IsNpc ()
&& ! gch->IsPkiller () && (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 && !ch->IsNpc ())
|| (anopc > 0 && !victim->IsNpc ())) {
// 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->GetLeader () == victim) ? victim : ch, NULL,
(ch->GetLeader () == victim) ?
victim->GetMaster () : ch->GetMaster (),
TO_NOTVICT);
if (ch->GetLeader () == victim)
stop_follower (victim);
else
stop_follower (ch);
}
// if leader isnt pkill, leave the group and disband ch
if (ch->GetLeader () != NULL && ! ch->GetLeader ()->IsNpc () &&
! ch->GetLeader ()->IsPkiller ()) {
act (AT_ACTION, "$n disbands from $N's group.", ch, NULL,
ch->GetMaster (), TO_NOTVICT);
stop_follower (ch);
} else {
for (gch = ch->GetInRoom ()->first_person; gch;
gch = gch->GetNextInRoom ())
if (is_same_group (gch, ch) && !gch->IsNpc () &&
! gch->IsPkiller () && gch != ch) {
act (AT_ACTION, "$n disbands from $N's group.",
ch, NULL,
gch->GetMaster (), TO_NOTVICT);
stop_follower (gch);
}
}
// if leader isnt pkill, leave the group and disband victim
if (victim->GetLeader () != NULL
&& ! victim->GetLeader ()->IsNpc ()
&& ! victim->GetLeader ()->IsPkiller ()) {
act (AT_ACTION, "$n disbands from $N's group.", victim,
NULL, victim->GetMaster (), TO_NOTVICT);
stop_follower (victim);
} else {
for (gch = victim->GetInRoom ()->first_person; gch;
gch = gch->GetNextInRoom ())
if (is_same_group (gch, victim) && !gch->IsNpc () &&
! gch->IsPkiller () && gch != victim) {
act (AT_ACTION, "$n disbands from $N's group.", gch,
NULL, gch->GetMaster (), TO_NOTVICT);
stop_follower (gch);
}
}
}
// Inviso attacks ... not.
if (ch->IsInvis ()) {
affect_strip (ch, gsn_invis);
affect_strip (ch, gsn_mass_invis);
ch->ClrInvis ();
act (AT_MAGIC, "$n fades into existence.", ch, NULL, NULL,
TO_ROOM);
}
// Take away Hide
if (ch->IsHidden ())
ch->ClrHide ();
// Damage modifiers.
if (victim->HasSanctuary ())
dam /= 2;
if (victim->IsProtected () && ch->IsEvil ())
dam -= (int) (dam / 4);
if (dam < 0)
dam = 0;
// Check for disarm, trip, parry, and dodge.
if (dt >= TYPE_HIT && ch->GetInRoom () == victim->GetInRoom ()) {
if (ch->IsNpc () && ch->CanDisarm ()
&& ch->GetLevel () > 9
&& number_percent () < ch->GetLevel () / 3)
disarm (ch, victim);
if (ch->IsNpc () && ch->CanTrip ()
&& ch->GetLevel () > 5
&& number_percent () < ch->GetLevel () / 2)
trip (ch, victim);
if (check_parry (ch, victim))
return rNONE;
if (check_dodge (ch, victim))
return rNONE;
// if (check_tumble (ch, victim))
// return rNONE;
}
// Check control panel settings and modify damage
if (ch->IsNpc ()) {
if (npcvict)
dampmod = SysData.MobMobDamage;
else
dampmod = SysData.MobPlrDamage;
} else {
if (npcvict)
dampmod = SysData.PlrMobDamage;
else
dampmod = SysData.PlrPlrDamage;
}
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) && number_bits (1) == 0) {
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->AddHp (-dam);
// Get experience based on % of damage done -Thoric
if (dam && ch != victim
&& !ch->IsNpc () && ch->GetFightData () && ch->GetFightData ()->xp) {
if (ch->GetFightData ()->who == victim)
xp_gain = (int) (ch->GetFightData ()->xp * dam)
/ victim->GetMaxHp ();
else
xp_gain = (int) (xp_compute (ch, victim) * 0.85 * dam)
/ victim->GetMaxHp ();
gain_exp (ch, xp_gain);
}
if (! victim->IsNpc ()
&& victim->GetLevel () >= LEVEL_IMMORTAL && victim->GetHp () < 1)
victim->SetHp (1);
// Make sure newbies dont die
if (!victim->IsNpc () && !victim->IsAuthed () && victim->GetHp () < 1)
victim->SetHp (1);
if (dam > 0 && dt > TYPE_HIT
&& ! victim->IsPoisoned ()
&& is_wielding_poisoned (ch)
&& ! victim->IsImmunePoison ()
&& !saves_poison_death (ch->GetLevel (), victim)) {
CAffectData af;
af.type = gsn_poison;
af.duration = 20;
af.location = APPLY_STR;
af.modifier = -2;
af.bitvector = AFF_POISON;
affect_join (victim, &af);
victim->SetMentalState (URANGE (20,
victim->GetMentalState () + victim->IsPkiller () ? 1 : 2, 100));
}
// Vampire self preservation -Thoric
if (victim->IsVampire ()) {
if (dam >= (victim->GetMaxHp () / 10)) // get hit hard, lose blood
gain_condition (victim,
COND_BLOODTHIRST, -1 - (victim->GetLevel () / 20));
if (victim->GetHp () <= (victim->GetMaxHp () / 8)
&& victim->GetPcData ()->condition[COND_BLOODTHIRST] > 5) {
gain_condition (victim, COND_BLOODTHIRST,
-URANGE (3, victim->GetLevel () / 10, 8));
victim->AddHp (URANGE (4, (victim->GetMaxHp () / 30), 15));
set_char_color (AT_BLOOD, victim);
victim->SendText ("You howl with rage as the beast within stirs!\n\r");
}
}
if (! npcvict && victim->GetTrustLevel () >= LEVEL_IMMORTAL
&& ch->GetTrustLevel () >= LEVEL_IMMORTAL && victim->GetHp () < 1)
victim->SetHp (1);
update_pos (victim);
switch (victim->GetPosition ()) {
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 (! victim->IsParalysed ()) {
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 < SkillTable.GetCount ()) {
CSkill *skill = SkillTable.GetSkill (dt);
if (skill->ValidDieCharMsg ())
act (AT_DEAD, skill->GetDieCharMsg (), ch, NULL, victim, TO_CHAR);
if (skill->ValidDieVictMsg ())
act (AT_DEAD, skill->GetDieVictMsg (), ch, NULL, victim, TO_VICT);
if (skill->ValidDieRoomMsg ())
act (AT_DEAD, skill->GetDieRoomMsg (), 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->GetMaxHp () / 4) {
act (AT_HURT, "That really did HURT!", victim, 0, 0, TO_CHAR);
if (number_bits (3) == 0)
worsen_mental_state (victim, 1);
}
if (victim->GetHp () < victim->GetMaxHp () / 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 (victim, 1);
}
break;
}
// Sleep spells and extremely wounded folks.
if (! victim->IsAwake () // lets make NPC's not slaughter PC's
&& ! victim->IsParalysed ()) {
if (victim->GetFightData ()
&& victim->GetFightData ()->who->hunting
&& victim->GetFightData ()->who->hunting->who == victim)
stop_hunting (victim->GetFightData ()->who);
if (victim->GetFightData ()
&& victim->GetFightData ()->who->hating
&& victim->GetFightData ()->who->hating->who == victim)
stop_hating (victim->GetFightData ()->who);
if (! npcvict && ch->IsNpc ())
stop_fighting (victim, TRUE);
else
stop_fighting (victim, FALSE);
}
// Payoff for killing things.
if (victim->GetPosition () == POS_DEAD) {
group_gain (ch, victim);
if (! npcvict) {
sprintf (log_buf, "%s killed by %s at %d",
victim->GetName (),
(ch->IsNpc () ? ch->GetShortDescr () : ch->GetName ()),
victim->GetInRoom ()->vnum);
gpDoc->LogString (log_buf, LOG_PLAYER);
to_channel (log_buf, CHANNEL_MONITOR, "Monitor", LEVEL_IMMORTAL);
// Dying penalty:
// 1/2 way back to previous level.
if (victim->GetExp () > exp_level (victim,
victim->GetLevel ()))
gain_exp (victim,
(exp_level (victim,
victim->GetLevel ()) - victim->GetExp ())/2);
// New penalty... go back to the beginning of current level.
// victim->GetExp () = exp_level (victim, victim->GetLevel ());
}
else if (! ch->IsNpc ()) // keep track of mob vnum killed
add_kill (ch, victim);
check_killer (ch, victim);
if (ch->GetInRoom () == victim->GetInRoom ())
loot = legal_loot (ch, victim);
else loot = FALSE;
set_cur_char (victim);
raw_kill (ch, victim);
victim = NULL;
if (! ch->IsNpc () && loot) {
// Autogold by Scryn 8/12
if (ch->IsAction (PLR_AUTOGOLD)) {
init_gold = ch->GetGold ();
do_get (ch, "coins corpse");
new_gold = ch->GetGold ();
gold_diff = (new_gold - init_gold);
if (gold_diff > 0) {
sprintf (buf1,"%d",gold_diff);
do_split (ch, buf1);
}
}
if (ch->IsAction (PLR_AUTOLOOT) && victim != ch)
do_get (ch, "all corpse");
else
do_look (ch, "in corpse");
if (ch->IsAction (PLR_AUTOSAC))
do_sacrifice (ch, "corpse");
}
if (SysData.IsSaveOnKill ())
save_char_obj (ch);
return rVICT_DIED;
}
if (victim == ch)
return rNONE;
// Take care of link dead people.
if (! npcvict && !victim->GetDesc () && ! victim->IsNoRecall ()) {
if (number_range (0, victim->GetWait ()) == 0) {
do_recall (victim, "");
return rNONE;
}
}
// Wimp out?
if (npcvict && dam > 0) {
if ((victim->IsWimpy () && number_bits (1) == 0
&& victim->GetHp () < victim->GetMaxHp () / 2)
|| (victim->IsCharmed () && victim->GetMaster ()
&& victim->GetMaster ()->GetInRoom () != victim->GetInRoom ())) {
start_fearing (victim, ch);
stop_hunting (victim);
do_flee (victim, "");
}
}
if (! npcvict && victim->GetHp () > 0
&& victim->GetHp () <= victim->GetWimpLevel ()
&& victim->GetWait () == 0)
do_flee (victim, "");
else if (! npcvict && victim->IsAction (PLR_FLEE))
do_flee (victim, "");
return rNONE;
}
BOOL is_safe (CCharacter *ch, CCharacter *victim)
{
// Thx Josh!
if (ch->GetFightWho () == ch)
return FALSE;
if (victim->GetInRoom ()->IsSafe ()) {
set_char_color (AT_MAGIC, ch);
ch->SendText ("A magical force prevents you from attacking.\n\r");
return TRUE;
}
if (ch->IsPacifist ()) { // Fireblade
set_char_color (AT_MAGIC, ch);
ch->SendText ("You are a pacifist and will not fight.\n\r");
return TRUE;
}
if (ch->GetLevel () > LEVEL_IMMORTAL)
return FALSE;
if (victim->IsNpc () && victim->IsPacifist ()) { // Gorog
set_char_color (AT_MAGIC, ch);
ch->SendTextf ("%s is a pacifist and will not fight.\n\r",
capitalize (victim->GetShortDescr ()));
return TRUE;
}
if (! ch->IsNpc () && ! victim->IsNpc ()
&& ch != victim
&& victim->GetInRoom ()->GetArea ()->IsNoPkill ()) {
set_char_color (AT_IMMORT, ch);
ch->SendText ("The gods have forbidden player killing in this area.\n\r");
return TRUE;
}
if (ch->IsNpc () || victim->IsNpc ())
return FALSE;
if (ch->GetAge () < 18 || ch->GetLevel () < 5) {
set_char_color (AT_WHITE, ch);
ch->SendText ("You are not yet ready, needing age or experience, "
"if not both. \n\r");
return TRUE;
}
if (victim->GetAge () < 18 || victim->GetLevel () < 5) {
set_char_color (AT_WHITE, ch);
ch->SendText ("They are yet too young to die.\n\r");
return TRUE;
}
if (ch->GetLevel () - victim->GetLevel () > 5
|| victim->GetLevel () - ch->GetLevel () > 5) {
set_char_color (AT_IMMORT, ch);
ch->SendText ("The gods do not allow murder when there is such "
"a difference in level.\n\r");
return TRUE;
}
if (get_timer (victim, TIMER_PKILLED) > 0) {
set_char_color (AT_GREEN, ch);
ch->SendText ("That character has died within the last 5 minutes.\n\r");
return TRUE;
}
if (get_timer (ch, TIMER_PKILLED) > 0) {
set_char_color (AT_GREEN, ch);
ch->SendText ("You have been killed within the last 5 minutes.\n\r");
return TRUE;
}
return FALSE;
}
// just verify that a corpse looting is legal
BOOL legal_loot (CCharacter *ch, CCharacter *victim)
{
// anyone can loot mobs
if (victim->IsNpc ())
return TRUE;
// non-charmed mobs can loot anything
if (ch->IsNpc () && ! ch->GetMaster ())
return TRUE;
// members of different clans can loot too! -Thoric
if (!ch->IsNpc () && !victim->IsNpc () && ch->IsPkiller ()
&& victim->IsPkiller ())
return TRUE;
return FALSE;
}
// See if an attack justifies a KILLER flag.
void check_killer (CCharacter *ch, CCharacter *victim)
{
// NPC's are fair game.
if (victim->IsNpc ()) {
if (! ch->IsNpc ()) {
if (ch->GetPcData ()->GetClan ())
ch->GetPcData ()->GetClan ()->mkills++;
ch->GetPcData ()->mkills++;
ch->GetInRoom ()->GetArea ()->mkills++;
int level_ratio = URANGE (1,
ch->GetLevel () / victim->GetLevel (), 50);
if (ch->GetPcData ()->deity) {
if (victim->GetRace () == ch->GetPcData ()->deity->npcrace)
adjust_favor (ch, 3, level_ratio);
else
if (victim->GetRace () == ch->GetPcData ()->deity->npcfoe)
adjust_favor (ch, 17, level_ratio);
else
adjust_favor (ch, 2, level_ratio);
}
}
return;
}
// If you kill yourself nothing happens.
if (ch == victim || ch->GetLevel () >= LEVEL_IMMORTAL)
return;
// Any character in the arena is ok to kill.
// Added pdeath and pkills here
if (in_arena (ch)) {
if (! ch->IsNpc () && ! victim->IsNpc ()) {
ch->GetPcData ()->pkills++;
victim->GetPcData ()->pdeaths++;
}
return;
}
// So are killers and thieves.
if (victim->IsKiller () || victim->IsThief ()) {
if (! ch->IsNpc ()) {
if (ch->GetPcData ()->GetClan ())
ch->GetPcData ()->GetClan ()->pkills++;
ch->GetPcData ()->pkills++;
ch->GetInRoom ()->GetArea ()->pkills++;
}
return;
}
// clan checks -Thoric
if (! ch->IsNpc () && ! victim->IsNpc () && ch->IsPkiller ()
&& victim->IsPkiller ()) {
// not of same clan? Go ahead and kill!!!
if (! ch->GetPcData ()->GetClan ()
|| !victim->GetPcData ()->GetClan ()
|| (ch->GetPcData ()->GetClan ()->GetType () != CLAN_NOKILL
&& victim->GetPcData ()->GetClan ()->GetType () != CLAN_NOKILL
&& ch->GetPcData ()->GetClan () != victim->GetPcData ()->GetClan ())) {
if (ch->GetPcData ()->GetClan ())
ch->GetPcData ()->GetClan ()->pkills++;
ch->GetPcData ()->pkills++;
ch->SetHp (ch->GetMaxHp ());
ch->SetMana (ch->GetMaxMana ());
ch->SetMove (ch->GetMaxMove ());
if (ch->GetPcData ())
ch->GetPcData ()->condition [COND_BLOODTHIRST] =
10 + ch->GetLevel ();
update_pos (victim);
if (victim != ch) {
act (AT_MAGIC, "Bolts of blue energy rise from the corpse, "
"seeping into $n.", ch, victim->GetName (), NULL, TO_ROOM);
act (AT_MAGIC, "Bolts of blue energy rise from the corpse, "
"seeping into you.", ch, victim->GetName (), NULL, TO_CHAR);
}
if (victim->GetPcData ()->GetClan ())
victim->GetPcData ()->GetClan ()->pdeaths++;
victim->GetPcData ()->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 (ch->IsCharmed ()) {
if (! ch->GetMaster ()) {
bug ("Check_killer: %s bad AFF_CHARM",
ch->IsNpc () ? ch->GetShortDescr () : ch->GetName ());
affect_strip (ch, gsn_charm_person);
ch->ClrCharmed ();
return;
}
// stop_follower (ch);
if (ch->GetMaster ())
check_killer (ch->GetMaster (), 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 (ch->IsNpc ()) {
if (! victim->IsNpc ()) {
if (victim->GetPcData ()->GetClan ())
victim->GetPcData ()->GetClan ()->mdeaths++;
victim->GetPcData ()->mdeaths++;
victim->GetInRoom ()->GetArea ()->mdeaths++;
int level_ratio = URANGE (1,
ch->GetLevel () / victim->GetLevel (), 50);
if (victim->GetPcData ()->deity) {
if (ch->GetRace () == victim->GetPcData ()->deity->npcrace)
adjust_favor (victim, 12, level_ratio);
else
if (ch->GetRace () == victim->GetPcData ()->deity->npcfoe)
adjust_favor (victim, 15, level_ratio);
else
adjust_favor (victim, 11, level_ratio);
}
}
return;
}
if (! ch->IsNpc ()) {
if (ch->GetPcData ()->GetClan ())
ch->GetPcData ()->GetClan ()->illegal_pk++;
ch->GetPcData ()->illegal_pk++;
ch->GetInRoom ()->GetArea ()->illegal_pk++;
}
if (! victim->IsNpc ()) {
if (victim->GetPcData ()->GetClan ())
victim->GetPcData ()->GetClan ()->pdeaths++;
victim->GetPcData ()->pdeaths++;
victim->GetInRoom ()->GetArea ()->pdeaths++;
}
if (ch->IsKiller ())
return;
set_char_color (AT_WHITE, ch);
ch->SendText ("A strange feeling grows deep inside you, and a tingle "
"goes up your spine...\n\r");
set_char_color (AT_IMMORT, ch);
ch->SendText ("A deep voice booms inside your head, 'Thou shall now "
"be known as a deadly murderer!!!'\n\r");
set_char_color (AT_WHITE, ch);
ch->SendText ("You feel as if your soul has been revealed for all to "
"see.\n\r");
ch->SetActBit (PLR_KILLER);
if (ch->IsAttacker ())
ch->ClrAttacker ();
save_char_obj (ch);
}
// See if an attack justifies a ATTACKER flag.
void check_attacker (CCharacter *ch, CCharacter *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 (victim->IsNpc () || victim->IsKiller () || victim->IsThief ())
return;
// deadly char check
if (! ch->IsNpc () && ! victim->IsNpc ()
&& ch->CanPkill () && victim->CanPkill ())
return;
// Charm-o-rama.
if (ch->IsCharmed ()) {
if (! ch->GetMaster ()) {
bug ("Check_attacker: %s bad AFF_CHARM",
ch->IsNpc () ? ch->GetShortDescr () : ch->GetName ());
affect_strip (ch, gsn_charm_person);
ch->ClrCharmed ();
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->GetMaster ()->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 (ch->IsNpc () || ch == victim || ch->IsImmortal ()
|| ch->IsAttacker () || ch->IsKiller ())
return;
ch->SetAttacker ();
save_char_obj (ch);
}
// Set position of a victim.
void update_pos (CCharacter *victim)
{
if (! victim) {
bug ("update_pos: null victim");
return;
}
if (victim->GetHp () > 0) {
if (victim->GetPosition () <= POS_STUNNED)
victim->SetPosition (POS_STANDING);
if (victim->IsParalysed ())
victim->SetPosition (POS_STUNNED);
return;
}
if (victim->IsNpc () || victim->GetHp () <= -11) {
if (victim->mount) {
act (AT_ACTION, "$n falls from $N.",
victim, NULL, victim->mount, TO_ROOM);
victim->mount->ClrActBit (ACT_MOUNTED);
victim->mount = NULL;
}
victim->SetPosition (POS_DEAD);
return;
}
if (victim->GetHp () <= -6)
victim->SetPosition (POS_MORTAL);
else if (victim->GetHp () <= -3)
victim->SetPosition (POS_INCAP);
else
victim->SetPosition (POS_STUNNED);
if (victim->GetPosition () > POS_STUNNED && victim->IsParalysed ())
victim->SetPosition (POS_STUNNED);
if (victim->mount) {
act (AT_ACTION, "$n falls unconscious from $N.",
victim, NULL, victim->mount, TO_ROOM);
victim->mount->ClrActBit (ACT_MOUNTED);
victim->mount = NULL;
}
}
// Start fights.
void set_fighting (CCharacter *ch, CCharacter *victim)
{
CFightData *fight;
if (ch->GetFightData ()) {
bug ("Set_fighting: %s -> %s (already fighting %s)",
ch->GetName (), victim->GetName (),
ch->GetFightData ()->who->GetName ());
return;
}
if (ch->IsSleeping ())
affect_strip (ch, gsn_sleep);
// Limit attackers -Thoric
if (victim->num_fighting > max_fight (victim)) {
ch->SendText ("There are too many people fighting for you to join in.\n\r");
return;
}
fight = new CFightData;
fight->who = victim;
fight->xp = (int) (xp_compute (ch, victim) * 0.85);
fight->align = align_compute (ch, victim);
if (! ch->IsNpc () && victim->IsNpc ())
fight->timeskilled = times_killed (ch, victim);
ch->num_fighting = 1;
ch->SetFightData (fight);
victim->num_fighting++;
if (ch->IsNpc ())
ch->SetPosition (POS_FIGHTING);
else switch (ch->GetStyle ()) {
case STYLE_EVASIVE:
ch->SetPosition (POS_EVASIVE);
break;
case STYLE_DEFENSIVE:
ch->SetPosition (POS_DEFENSIVE);
break;
case STYLE_AGGRESSIVE:
ch->SetPosition (POS_AGGRESSIVE);
break;
case STYLE_BERSERK:
ch->SetPosition (POS_BERSERK);
break;
default:
ch->SetPosition (POS_FIGHTING);
}
if (victim->switched && victim->switched->IsPossessed ()) {
victim->switched->SendText ("You are disturbed!\n\r");
do_return (victim->switched, "");
}
}
void free_fight (CCharacter *ch)
{
if (! ch) {
bug ("Free_fight: null ch!");
return;
}
if (ch->GetFightData ()) {
if (! char_died (ch->GetFightData ()->who))
--ch->GetFightData ()->who->num_fighting;
delete ch->GetFightData ();
}
ch->SetFightData (NULL);
if (ch->mount)
ch->SetPosition (POS_MOUNTED);
else
ch->SetPosition (POS_STANDING);
// Berserk wears off after combat. -- Altrag
if (ch->IsBeserk ()) {
affect_strip (ch, gsn_berserk);
set_char_color (AT_WEAROFF, ch);
ch->SendText (SkillTable.GetSkill (gsn_berserk)->GetOffMsg ());
ch->SendText ("\n\r");
}
}
// Stop fights.
void stop_fighting (CCharacter *ch, BOOL fBoth)
{
CCharacter *fch;
free_fight (ch);
update_pos (ch);
if (!fBoth) // major short cut here by Thoric
return;
for (fch = first_char; fch; fch = fch->GetNext ()) {
if (fch->GetFightWho () == ch) {
free_fight (fch);
update_pos (fch);
}
}
}
// Improved Death_cry contributed by Diavolo.
// Additional improvement by Thoric (and removal of turds... sheesh!)
void death_cry (CCharacter *ch)
{
CRoomIndexData *was_in_room;
char *msg;
CExitData *pexit;
int vnum;
if (! ch) {
bug ("DEATH_CRY: null ch!");
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 (ch->HasBodypart (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 (ch->HasBodypart (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 (ch->HasBodypart (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 (ch->HasBodypart (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 (ch->HasBodypart (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];
CObjData *obj;
const char *name;
name = ch->IsNpc () ? ch->GetShortDescr () : ch->GetName ();
obj = create_object (OIdxTable.GetObj (vnum), 0);
obj->timer = number_range (4, 7);
sprintf (buf, obj->GetShortDescr (), name);
obj->SetShortDescr (buf);
sprintf (buf, obj->GetDescription (), name);
obj->SetDescription (buf);
obj = obj_to_room (obj, ch->GetInRoom ());
}
if (ch->IsNpc ())
msg = "You hear something's death cry.";
else
msg = "You hear someone's death cry.";
was_in_room = ch->GetInRoom ();
for (pexit = was_in_room->first_exit; pexit; pexit = pexit->GetNext ()) {
if (pexit->GetToRoom () && pexit->GetToRoom () != was_in_room) {
ch->SetInRoom (pexit->GetToRoom ());
act (AT_CARNAGE, msg, ch, NULL, NULL, TO_ROOM);
}
}
ch->SetInRoom (was_in_room);
}
void raw_kill (CCharacter *ch, CCharacter *victim)
{
CCharacter *victmp;
if (! victim) {
bug ("raw_kill: null victim!");
return;
}
// backup in case hp goes below 1
if (!victim->IsAuthed ()) {
bug ("raw_kill: killing unauthed");
return;
}
stop_fighting (victim, TRUE);
// Take care of polymorphed chars
if (victim->IsNpc () && victim->IsPolymorphed ()) {
victim->GetDesc ()->m_pOriginal->RemoveFromRoom ();
victim->GetDesc ()->m_pOriginal->SendToRoom (victim->GetInRoom ());
victmp = victim->GetDesc ()->m_pOriginal;
do_revert (victim, "");
raw_kill (ch, victmp);
return;
}
mprog_death_trigger (ch, victim);
if (char_died (victim))
return;
rprog_death_trigger (ch, victim);
if (char_died (victim))
return;
make_corpse (victim, ch);
if (victim->GetSectorType () == SECT_OCEANFLOOR
|| victim->GetSectorType () == SECT_UNDERWATER
|| victim->GetSectorType () == SECT_WATER_SWIM
|| victim->GetSectorType () == SECT_WATER_NOSWIM)
act (AT_BLOOD, "$n's blood slowly clouds the surrounding water.",
victim, NULL, NULL, TO_ROOM);
else if (victim->GetSectorType () == SECT_AIR)
act( AT_BLOOD, "$n's blood sprays wildly through the air.",
victim, NULL, NULL, TO_ROOM);
else
make_blood (victim);
if (victim->IsNpc ()) {
victim->GetMobIndex ()->killed++;
extract_char (victim, TRUE);
victim = NULL;
return;
}
set_char_color (AT_DIEMSG, victim);
if (victim->GetPcData ()->mdeaths + victim->GetPcData ()->pdeaths < 3)
do_help (victim, "new_death");
else
do_help (victim, "_DIEMSG_");
extract_char (victim, FALSE);
if (! victim) {
bug ("oops! raw_kill: extract_char destroyed pc char");
return;
}
while (! victim->m_AffectList.IsEmpty ())
affect_remove (victim, victim->m_AffectList.RemoveTail ());
CRaceData &Ra = *RaceTable.GetRaceData (victim->GetRace ());
victim->SetAffectFlags (Ra.GetAffectFlags ());
victim->SetAttackFlags (Ra.GetAttackFlags ());
victim->SetDefenseFlags (Ra.GetDefenseFlags ());
victim->SetResistFlags (0);
victim->SetSusceptFlags (0);
victim->SetImmuneFlags (0);
victim->SetCarryWeight (0);
victim->SetArmor (100 + Ra.GetAcPlus ());
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->SetDamroll (0);
victim->SetHitroll (0);
victim->SetMentalState (-10);
victim->SetAlignment (URANGE (-1000, victim->GetAlignment (), 1000));
victim->saving_poison_death = Ra.GetSavingPoisonDeath ();
victim->saving_wand = Ra.GetSavingWand ();
victim->saving_para_petri = Ra.GetSavingParaPetri ();
victim->saving_breath = Ra.GetSavingBreath ();
victim->saving_spell_staff = Ra.GetSavingSpellstaff ();
victim->SetPosition (POS_RESTING);
victim->SetHp (UMAX (1, victim->GetHp ()));
victim->SetMana (UMAX (1, victim->GetMana ()));
victim->SetMove (UMAX (1, victim->GetMove ()));
// Pardon crimes... -Thoric
if (victim->IsAction (PLR_KILLER)) {
victim->ClrActBit (PLR_KILLER);
victim->SendText ("The gods have pardoned you for your murderous acts.\n\r");
}
if (victim->IsAction (PLR_THIEF)) {
victim->ClrActBit (PLR_THIEF);
victim->SendText ("The gods have pardoned you for your thievery.\n\r");
}
victim->GetPcData ()->condition [COND_FULL] = 12;
victim->GetPcData ()->condition [COND_THIRST] = 12;
if (victim->IsVampire ())
victim->GetPcData ()->condition [COND_BLOODTHIRST] =
victim->GetLevel () / 2;
if (SysData.IsSaveOnDeath ())
save_char_obj (victim);
}
void group_gain (CCharacter *ch, CCharacter *victim)
{
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 (ch->IsNpc () || victim == ch)
return;
members = 0;
CCharacter *gch = ch->GetInRoom ()->first_person;
for (; gch; gch = gch->GetNextInRoom ()) {
if (is_same_group (gch, ch))
members++;
}
if (members == 0) {
bug ("Group_gain: members = 0.");
members = 1;
}
CCharacter *lch = ch->GetLeader () ? ch->GetLeader () : ch;
gch = ch->GetInRoom ()->first_person;
for (; gch; gch = gch->GetNextInRoom ()) {
CObjData *obj;
if (! is_same_group (gch, ch))
continue;
if (gch->GetLevel () - lch->GetLevel () > 8) {
gch->SendText ("You are too high for this group.\n\r");
continue;
}
if (gch->GetLevel () - lch->GetLevel () < -8) {
gch->SendText ("You are too low for this group.\n\r");
continue;
}
xp = (int) (xp_compute (gch, victim) * 0.1765) / members;
if (! gch->GetFightData ())
xp /= 2;
gch->SetAlignment (align_compute (gch, victim));
gch->SendTextf ("You receive %d experience points.\n\r", xp);
gain_exp (gch, xp);
POSITION Cpos = ch->GetHeadCarryPos ();
while (obj = ch->GetNextCarrying (Cpos)) {
if (obj->wear_loc == WEAR_NONE)
continue;
if ((obj->IsAntiEvil () && ch->IsEvil ())
|| (obj->IsAntiGood () && ch->IsGood ())
|| (obj->IsAntiNeutral () && ch->IsNeutral ())) {
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->GetInRoom ());
oprog_zap_trigger (ch, obj); // mudprogs
if (char_died (ch))
return;
}
}
}
}
int align_compute (CCharacter *gch, CCharacter *victim)
{
int CharAlign = gch->GetAlignment ();
int VictAlign = victim->GetAlignment ();
// 6/12/98 by RCP
// This provides a sliding scale for alignment adjustment. For victims
// with low absolute alignment values, approx 33% of the victims align
// is applied to the killer. As victim alignment increases to 1000, the
// percentage is decreased to 12.5%.
// Note: All numbers except 200 are * 10 to avoid integer rounding errors
// Basic formula is -va / (3 + va/200).
int AlignChange = -10 * VictAlign / (30 + abs (10 * VictAlign)/200);
return URANGE (-1000, CharAlign + AlignChange, 1000);
}
//int align_compute (CCharacter *gch, CCharacter *victim)
//{
// int align, newalign;
//
// align = gch->GetAlignment () - victim->GetAlignment ();
//
// if (align > 500)
// newalign = UMIN (gch->GetAlignment () + (align-500)/4, 1000);
// else if (align < -500)
// newalign = UMAX (gch->GetAlignment () + (align+500)/4, -1000);
// else
// newalign = gch->GetAlignment () - (int) (gch->GetAlignment () / 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 (CCharacter *gch, CCharacter *victim)
{
int align;
int xp;
int xp_ratio;
int gchlev = gch->GetLevel ();
xp = (get_exp_worth (victim)
* URANGE (0, (victim->GetLevel () - gchlev) + 10, 13)) / 10;
align = gch->GetAlignment () - victim->GetAlignment ();
// bonus for attacking opposite alignment
if (align > 990 || align < -990)
xp = (xp*5) >> 2;
else
// penalty for good attacking same alignment
if (gch->GetAlignment () > 300 && align < 250)
xp = (xp*3) >> 2;
xp = number_range ((xp*3) >> 2, (xp*5) >> 2);
// get 1/4 exp for players -Thoric
if (! victim->IsNpc ())
xp /= 4;
// reduce exp for killing the same mob repeatedly -Thoric
else if (! gch->IsNpc ()) {
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 (! gch->IsNpc () && gchlev > 5) {
xp_ratio = (int) gch->GetPlayed () / gchlev;
if (xp_ratio > 20000) // 5/4
xp = (xp*5) >> 2;
else if (xp_ratio < 16000) // 3/4
xp = (xp*3) >> 2;
else if (xp_ratio < 10000) // 1/2
xp >>= 1;
else if (xp_ratio < 5000) // 1/4
xp >>= 2;
else if (xp_ratio < 3500) // 1/8
xp >>= 3;
else if (xp_ratio < 2000) // 1/16
xp >>= 4;
}
// Level based experience gain cap. Cannot get more experience for
// a kill than the amount for your current experience level -Thoric
// This does not seem realistic to me (RCP)
// return URANGE (0, xp, exp_level (gch, gchlev+1) -
// exp_level (gch, gchlev));
return max (0, xp);
}
char * s_blade_messages [24] =
{
"miss", "barely scratch", "scratch", "nick", "cut", "hit", "tear",
"rip", "gash", "lacerate", "hack", "maul", "rend", "decimate",
"_mangle_", "_devastate_", "_cleave_", "_butcher_", "DISEMBOWEL",
"DISFIGURE", "GUT", "EVISCERATE", "* SLAUGHTER *", "*** ANNIHILATE ***"
};
char * p_blade_messages [24] =
{
"misses", "barely scratches", "scratches", "nicks", "cuts", "hits",
"tears", "rips", "gashes", "lacerates", "hacks", "mauls", "rends",
"decimates", "_mangles_", "_devastates_", "_cleaves_", "_butchers_",
"DISEMBOWELS", "DISFIGURES", "GUTS", "EVISCERATES", "* SLAUGHTERS *",
"*** ANNIHILATES ***"
};
char * s_blunt_messages [24] =
{
"miss", "barely scuff", "scuff", "pelt", "bruise", "strike", "thrash",
"batter", "flog", "pummel", "smash", "maul", "bludgeon", "decimate",
"_shatter_", "_devastate_", "_maim_", "_cripple_", "MUTILATE", "DISFIGURE",
"MASSACRE", "PULVERIZE", "* OBLITERATE *", "*** ANNIHILATE ***"
};
char * p_blunt_messages [24] =
{
"misses", "barely scuffs", "scuffs", "pelts", "bruises", "strikes",
"thrashes", "batters", "flogs", "pummels", "smashes", "mauls",
"bludgeons", "decimates", "_shatters_", "_devastates_", "_maims_",
"_cripples_", "MUTILATES", "DISFIGURES", "MASSACRES", "PULVERIZES",
"* OBLITERATES *", "*** ANNIHILATES ***"
};
char * s_generic_messages [24] =
{
"miss", "brush", "scratch", "graze", "nick", "jolt", "wound",
"injure", "hit", "jar", "thrash", "maul", "decimate", "_traumatize_",
"_devastate_", "_maim_", "_demolish_", "MUTILATE", "MASSACRE",
"PULVERIZE", "DESTROY", "* OBLITERATE *", "*** ANNIHILATE ***",
"**** SMITE ****"
};
char * p_generic_messages [24] =
{
"misses", "brushes", "scratches", "grazes", "nicks", "jolts", "wounds",
"injures", "hits", "jars", "thrashes", "mauls", "decimates", "_traumatizes_",
"_devastates_", "_maims_", "_demolishes_", "MUTILATES", "MASSACRES",
"PULVERIZES", "DESTROYS", "* OBLITERATES *", "*** ANNIHILATES ***",
"**** SMITES ****"
};
char ** const s_message_table [18] = {
s_generic_messages, // hit
s_blade_messages, // slice
s_blade_messages, // stab
s_blade_messages, // slash
s_blunt_messages, // whip
s_blade_messages, // claw
s_generic_messages, // blast
s_blunt_messages, // pound
s_blunt_messages, // crush
s_generic_messages, // grep
s_blade_messages, // bite
s_blade_messages, // pierce
s_blunt_messages, // suction
s_generic_messages, // bolt
s_generic_messages, // arrow
s_generic_messages, // dart
s_generic_messages, // stone
s_generic_messages // pea
};
char ** const p_message_table [18] = {
p_generic_messages, // hit
p_blade_messages, // slice
p_blade_messages, // stab
p_blade_messages, // slash
p_blunt_messages, // whip
p_blade_messages, // claw
p_generic_messages, // blast
p_blunt_messages, // pound
p_blunt_messages, // crush
p_generic_messages, // grep
p_blade_messages, // bite
p_blade_messages, // pierce
p_blunt_messages, // suction
p_generic_messages, // bolt
p_generic_messages, // arrow
p_generic_messages, // dart
p_generic_messages, // stone
p_generic_messages // pea
};
// Revamped by Thoric to be more realistic
void dam_message (CCharacter *ch, CCharacter *victim, int dam, int dt,
CObjData* pObj /* = NULL */)
{
char buf1 [256], buf2 [256], buf3 [256];
const char *vs;
const char *vp;
const char *attack;
char punct;
short dampc;
CSkill *skill = NULL;
BOOL gcflag = FALSE;
BOOL gvflag = FALSE;
int d_index, w_index;
if (! dam)
dampc = 0;
else
dampc = ((dam * 1000) / victim->GetMaxHp ()) +
(50 - ((victim->GetHp () * 50) / victim->GetMaxHp ()));
CRoomIndexData *pWasInRoom = NULL;
if (ch->GetInRoom () != victim->GetInRoom ()) {
pWasInRoom = ch->GetInRoom ();
ch->RemoveFromRoom ();
ch->SendToRoom (victim->GetInRoom ());
}
// Get the weapon index
if (dt > 0 && dt < SkillTable.GetCount ())
w_index = 0;
else if (dt >= TYPE_HIT && dt < TYPE_HIT + DIM (attack_table))
w_index = dt - TYPE_HIT;
else {
bug ("Dam_message: bad dt %d from %s in %d.",
dt, ch->GetName (), ch->GetInRoom ()->vnum);
dt = TYPE_HIT;
w_index = 0;
}
// get the damage index
if (dam == 0)
d_index = 0;
else if (dampc < 0)
d_index = 1;
else if (dampc <= 100)
d_index = 1 + dampc/10;
else if (dampc <= 200)
d_index = 11 + (dampc - 100) / 20;
else if (dampc <= 900)
d_index = 16 + (dampc - 200) / 100;
else
d_index = 23;
// Lookup the damage message
vs = s_message_table [w_index][d_index];
vp = p_message_table [w_index][d_index];
punct = (dampc <= 30) ? '.' : '!';
if (dam == 0 && (! ch->IsNpc () && ch->IsGagged ()))
gcflag = TRUE;
if (dam == 0 && (! victim->IsNpc () && victim->IsGagged ()))
gvflag = TRUE;
if (dt >=0 && dt < SkillTable.GetCount ())
skill = SkillTable.GetSkill (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 + DIM (attack_table))
attack = attack_table [dt - TYPE_HIT];
else {
bug ("Dam_message: bad dt %d from %s in %d.", dt,
ch->GetName (), ch->GetInRoom ()->vnum);
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->GetDamageMsg ();
if (dam == 0) {
BOOL found = FALSE;
if (skill->ValidMissCharMsg ()) {
act (AT_HIT, skill->GetMissCharMsg (), ch, NULL,
victim, TO_CHAR);
found = TRUE;
}
if (skill->ValidMissVictMsg ()) {
act (AT_HITME, skill->GetMissVictMsg (), ch, NULL,
victim, TO_VICT);
found = TRUE;
}
if (skill->ValidMissRoomMsg ()) {
if (strcmp (skill->GetMissRoomMsg (), "supress"))
act (AT_ACTION, skill->GetMissRoomMsg (), ch, NULL,
victim, TO_NOTVICT);
found = TRUE;
}
if (found) { // miss message already sent
if (pWasInRoom) {
ch->RemoveFromRoom ();
ch->SendToRoom (pWasInRoom);
}
return;
}
} else {
if (skill->ValidHitCharMsg ())
act (AT_HIT, skill->GetHitCharMsg (), ch, NULL,
victim, TO_CHAR);
if (skill->ValidHitVictMsg ())
act (AT_HITME, skill->GetHitVictMsg (), ch, NULL,
victim, TO_VICT);
if (skill->ValidHitRoomMsg ())
act (AT_ACTION, skill->GetHitRoomMsg (), ch, NULL,
victim, TO_NOTVICT);
}
}
else if (dt >= TYPE_HIT && dt < TYPE_HIT + DIM (attack_table)) {
if (pObj)
attack = pObj->GetShortDescr ();
else
attack = attack_table [dt - TYPE_HIT];
} else {
bug ("Dam_message: bad dt %d from %s in %d.", dt,
ch->GetName (), ch->GetInRoom ()->vnum);
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);
if (pWasInRoom) {
ch->RemoveFromRoom ();
ch->SendToRoom (pWasInRoom);
}
}
void do_kill (CCharacter *ch, char *argument)
{
char arg [MAX_INPUT_LENGTH];
CCharacter *victim;
one_argument (argument, arg);
if (arg [0] == '\0') {
ch->SendText ("Kill whom?\n\r");
return;
}
if ((victim = get_char_room (ch, arg)) == NULL) {
ch->SendText ("They aren't here.\n\r");
return;
}
if (! victim->IsNpc ()) {
if (! victim->IsKiller () && ! victim->IsThief ()) {
ch->SendText ("You must MURDER a player.\n\r");
return;
}
}
if (victim == ch) {
ch->SendText ("You hit yourself. Ouch!\n\r");
multi_hit (ch, ch, TYPE_UNDEFINED);
return;
}
if (is_safe (ch, victim))
return;
if (ch->IsCharmed () && ch->GetMaster () == victim) {
act (AT_PLAIN, "$N is your beloved master.", ch, NULL, victim, TO_CHAR);
return;
}
if (ch->IsFightPosition ()) {
ch->SendText ("You do the best you can!\n\r");
return;
}
WAIT_STATE (ch, 1 * PULSE_VIOLENCE);
check_attacker (ch, victim);
multi_hit (ch, victim, TYPE_UNDEFINED);
}
void do_murde (CCharacter *ch, char *argument)
{
ch->SendText ("If you want to MURDER, spell it out.\n\r");
return;
}
void do_murder (CCharacter *ch, char *argument)
{
char buf [MAX_STRING_LENGTH];
char arg [MAX_INPUT_LENGTH];
CCharacter *victim;
one_argument (argument, arg);
if (arg [0] == '\0') {
ch->SendText ("Murder whom?\n\r");
return;
}
if ((victim = get_char_room (ch, arg)) == NULL) {
ch->SendText ("They aren't here.\n\r");
return;
}
if (victim == ch) {
ch->SendText ("Suicide is a mortal sin.\n\r");
return;
}
if (is_safe (ch, victim))
return;
if (ch->IsCharmed ()) {
if (ch->GetMaster () == victim) {
act (AT_PLAIN, "$N is your beloved master.", ch, NULL,
victim, TO_CHAR);
return;
} else {
if (ch->GetMaster ())
ch->GetMaster ()->SetAttacker ();
}
}
if (ch->IsFightPosition ()) {
ch->SendText ("You do the best you can!\n\r");
return;
}
if (! victim->IsNpc () && ch->IsNice ()) {
ch->SendText ("You feel too nice to do that!\n\r");
return;
}
// if (! victim->IsNpc () && IS_SET (victim->act, PLR_PK))
if (! victim->IsNpc ()) {
sprintf (log_buf, "%s: murder %s.", ch->GetName (),
victim->GetName ());
gpDoc->LogString (log_buf, LOG_NORMAL, ch->GetLevel ());
}
WAIT_STATE (ch, 1 * PULSE_VIOLENCE);
sprintf (buf, "Help! I am being attacked by %s!",
ch->IsNpc () ? ch->GetShortDescr () : ch->GetName ());
if (victim->IsPkiller ())
do_wartalk (victim, buf);
else
do_yell (victim, buf);
check_illegal_pk (ch, victim);
check_attacker (ch, victim);
multi_hit (ch, victim, TYPE_UNDEFINED);
}
BOOL in_arena (CCharacter *ch)
{
// if (IS_SET (ch->in_room->room_flags, ROOM_ARENA))
// return TRUE;
// if (IS_SET (ch->in_room->area->flags, AFLAG_FREEKILL))
// return TRUE;
if (ch->GetInRoom ()->GetArea ()->m_Filename == "arena.are")
return TRUE;
if (ch->GetInRoom ()->vnum < 29 || ch->GetInRoom ()->vnum > 43)
return FALSE;
return TRUE;
}
BOOL check_illegal_pk (CCharacter *ch, CCharacter *victim)
{
if (! victim->IsNpc () && ! ch->IsNpc ()) {
if ((! victim->IsPkiller ()
|| ch->GetLevel () - victim->GetLevel () > 10
|| ! ch->IsPkiller ())
&& !in_arena (ch)
&& ch != victim
&& ! (ch->IsImmortal () && victim->IsImmortal ())) {
sprintf (log_buf, "%s performing illegal pkill on %s at %d",
(ch->IsNpc () ? ch->GetShortDescr () : ch->GetName ()),
victim->GetName (), victim->GetInRoom ()->vnum);
gpDoc->LogString (log_buf, LOG_PLAYER);
to_channel (log_buf, CHANNEL_MONITOR, "Monitor", LEVEL_IMMORTAL);
return TRUE;
}
}
return FALSE;
}
void do_flee (CCharacter *ch, char *argument)
{
CRoomIndexData *was_in;
CRoomIndexData *now_in;
char buf[MAX_STRING_LENGTH];
int attempt, los;
short door;
CExitData *pexit;
if (! ch->GetFightWho ()) {
if (ch->IsFightPosition ()) {
if (ch->mount)
ch->SetPosition (POS_MOUNTED);
else
ch->SetPosition (POS_STANDING);
}
ch->SendText ("You aren't fighting anyone.\n\r");
return;
}
if (ch->IsBeserk ()) {
ch->SendText ("You aren't thinking very clearly...\n\r");
return;
}
if (ch->GetMove () <= 0) {
ch->SendText ("You're too exhausted to flee from combat!\n\r");
return;
}
// No fleeing while more aggressive than standard or hurt. - Haus
if (ch->IsNpc () && ch->GetPosition () < POS_FIGHTING) {
ch->SendText ("Not right now ...\n\r");
return;
}
if (ch->IsNpc () && ch->GetPosition () <= POS_SLEEPING)
return;
was_in = ch->GetInRoom ();
for (attempt = 0; attempt < 8; attempt++) {
door = number_door ();
if ((pexit = get_exit (was_in, door)) == NULL
|| !pexit->GetToRoom ()
// || IS_SET (pexit->exit_info, EX_NOFLEE)
|| (pexit->IsClosed () && ! ch->CanPass ())
|| (ch->IsNpc () && pexit->GetToRoom ()->IsNoMob ()))
continue;
affect_strip (ch, gsn_sneak);
ch->ClrSneak ();
if (ch->mount && ch->mount->GetFightData ())
stop_fighting (ch->mount, TRUE);
move_char (ch, pexit, 0);
if ((now_in = ch->GetInRoom ()) == was_in)
continue;
ch->SetInRoom (was_in);
act (AT_FLEE, "$n flees head over heels!", ch, NULL, NULL, TO_ROOM);
ch->SetInRoom (now_in);
act (AT_FLEE, "$n glances around for signs of pursuit.", ch,
NULL, NULL, TO_ROOM);
if (! ch->IsNpc ()) {
CCharacter *wf = ch->GetFightWho ();
los = (int) ((exp_level (ch, ch->GetLevel ()+1) -
exp_level (ch, ch->GetLevel ())) * 0.03);
if (! ch->IsImmortal ()) {
sprintf (buf, "You flee head over heels from combat, "
"losing %d experience.", los);
act (AT_FLEE, buf, ch, NULL, NULL, TO_CHAR);
}
else
act (AT_FLEE, "You flee head over heels from combat!",
ch, NULL, NULL, TO_CHAR);
gain_exp (ch, 0 - los);
if (wf && ch->GetPcData ()->deity) {
int level_ratio =
URANGE (1, wf->GetLevel () / ch->GetLevel (), 50);
if (wf && wf->GetRace () ==
ch->GetPcData ()->deity->npcrace)
adjust_favor (ch, 1, level_ratio);
else
if (wf && wf->GetRace () ==
ch->GetPcData ()->deity->npcfoe)
adjust_favor (ch, 16, level_ratio);
else
adjust_favor (ch, 0, level_ratio);
}
}
stop_fighting (ch, TRUE);
return;
}
los = (int) ((exp_level (ch, ch->GetLevel ()+1) -
exp_level (ch, ch->GetLevel ())) * 0.01);
if (! ch->IsImmortal ()) {
sprintf (buf, "You attempt to flee from combat, losing %d "
"experience.\n\r", los);
act (AT_FLEE, buf, ch, NULL, NULL, TO_CHAR);
}
else act (AT_FLEE,
"You attempt to flee from combat, but can't escape!",
ch, NULL, NULL, TO_CHAR);
gain_exp (ch, 0 - los);
}
void do_sla (CCharacter *ch, char *argument)
{
ch->SendText ("If you want to SLAY, spell it out.\n\r");
return;
}
void do_slay (CCharacter *ch, char *argument)
{
CCharacter *victim;
char arg [MAX_INPUT_LENGTH];
char arg2 [MAX_INPUT_LENGTH];
argument = one_argument (argument, arg);
one_argument (argument, arg2);
if (arg [0] == '\0') {
ch->SendText ("Slay whom?\n\r");
return;
}
if ((victim = get_char_room (ch, arg)) == NULL) {
ch->SendText ("They aren't here.\n\r");
return;
}
if (ch == victim) {
ch->SendText ("Suicide is a mortal sin.\n\r");
return;
}
if (! victim->IsNpc ()
&& victim->GetTrustLevel () >= ch->GetTrustLevel ()) {
ch->SendText ("You failed.\n\r");
return;
}
if (! str_cmp (arg2, "immolate")) {
act (AT_FIRE, "Your fireball turns $N into a blazing inferno.",
ch, NULL, victim, TO_CHAR );
act (AT_FIRE, "$n releases a searing fireball in your direction.",
ch, NULL, victim, TO_VICT );
act (AT_FIRE, "$n points at $N, who bursts into a flaming inferno.",
ch, NULL, victim, TO_NOTVICT);
}
else if (! str_cmp (arg2, "shatter")) {
act (AT_LBLUE, "You freeze $N with a glance and shatter the "
"frozen corpse into tiny shards.", ch, NULL, victim, TO_CHAR);
act (AT_LBLUE, "$n freezes you with a glance and shatters your "
"frozen body into tiny shards.", ch, NULL, victim, TO_VICT);
act (AT_LBLUE, "$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 if (! str_cmp (arg2, "pounce")
&& ch->GetTrustLevel () >= LEVEL_ASCENDANT) {
act (AT_BLOOD, "Leaping upon $N with bared fangs, you tear open $S "
"throat and toss the corpse to the ground...", ch, NULL, victim, TO_CHAR);
act (AT_BLOOD, "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 (AT_BLOOD, "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, "slit")
&& ch->GetTrustLevel () >= LEVEL_ASCENDANT) {
act (AT_BLOOD, "You calmly slit $N's throat.", ch, NULL, victim, TO_CHAR);
act (AT_BLOOD, "$n reaches out with a clawed finger and calmly "
"slits your throat.", ch, NULL, victim, TO_VICT);
act (AT_BLOOD, "$n calmly slits $N's throat.", ch, NULL, victim, TO_NOTVICT);
}
else if (! str_cmp (arg2, "dog")) {
act( AT_BLOOD, "You order your dogs to rip $N to shreds.", ch, NULL, victim, TO_CHAR );
act( AT_BLOOD, "$n orders $s dogs to rip you apart.", ch, NULL, victim, TO_VICT );
act( AT_BLOOD, "$n orders $s dogs to rip $N to shreds.", 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);
}