/*
* $Id: fight.c,v 1.165 1999/04/24 11:08:19 kostik Exp $
*/
/***************************************************************************
* ANATOLIA 2.1 is copyright 1996-1997 Serdar BULUT, Ibrahim CANPUNAR *
* ANATOLIA has been brought to you by ANATOLIA consortium *
* Serdar BULUT {Chronos} bulut@rorqual.cc.metu.edu.tr *
* Ibrahim Canpunar {Asena} canpunar@rorqual.cc.metu.edu.tr *
* Murat BICER {KIO} mbicer@rorqual.cc.metu.edu.tr *
* D.Baris ACAR {Powerman} dbacar@rorqual.cc.metu.edu.tr *
* By using this code, you have agreed to follow the terms of the *
* ANATOLIA license, in the file Anatolia/anatolia.licence *
***************************************************************************/
/***************************************************************************
* Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. *
* *
* Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* *
* In order to use any part of this Merc Diku Mud, you must comply with *
* both the original Diku license in 'license.doc' as well the Merc *
* license in 'license.txt'. In particular, you may not remove either of *
* these copyright notices. *
* *
* Much time and thought has gone into this software and you are *
* benefitting. We hope that you share your changes too. What goes *
* around, comes around. *
***************************************************************************/
/***************************************************************************
* ROM 2.4 is copyright 1993-1995 Russ Taylor *
* ROM has been brought to you by the ROM consortium *
* Russ Taylor (rtaylor@pacinfo.com) *
* Gabrielle Taylor (gtaylor@pacinfo.com) *
* Brian Moore (rom@rom.efn.org) *
* By using this code, you have agreed to follow the terms of the *
* ROM license, in the file Rom24/doc/rom.license *
***************************************************************************/
#include <sys/types.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <math.h>
#if !defined (WIN32)
# include <unistd.h>
#endif
#include "merc.h"
#include "quest.h"
#include "fight.h"
#include "rating.h"
#include "update.h"
#include "mob_prog.h"
#include "obj_prog.h"
DECLARE_DO_FUN(do_crush );
DECLARE_DO_FUN(do_emote );
DECLARE_DO_FUN(do_dismount );
DECLARE_DO_FUN(do_bash );
DECLARE_DO_FUN(do_berserk );
DECLARE_DO_FUN(do_disarm );
DECLARE_DO_FUN(do_kick );
DECLARE_DO_FUN(do_dirt );
DECLARE_DO_FUN(do_trip );
DECLARE_DO_FUN(do_tail );
DECLARE_DO_FUN(do_look_in );
DECLARE_DO_FUN(do_get );
DECLARE_DO_FUN(do_sacrifice );
DECLARE_DO_FUN(do_visible );
DECLARE_DO_FUN(do_recall );
DECLARE_DO_FUN(do_flee );
DECLARE_DO_FUN(do_clan );
/*
* Local functions.
*/
void check_assist (CHAR_DATA *ch, CHAR_DATA *victim);
bool check_dodge (CHAR_DATA *ch, CHAR_DATA *victim);
bool check_parry (CHAR_DATA *ch, CHAR_DATA *victim, int loc);
bool check_block (CHAR_DATA *ch, CHAR_DATA *victim, int loc);
bool check_blink (CHAR_DATA *ch, CHAR_DATA *victim);
bool check_hand_block (CHAR_DATA *ch, CHAR_DATA *victim);
void dam_message (CHAR_DATA *ch, CHAR_DATA *victim, int dam,
int dt, bool immune, int dam_type);
void death_cry (CHAR_DATA *ch);
void death_cry_org (CHAR_DATA *ch, int part);
void group_gain (CHAR_DATA *ch, CHAR_DATA *victim);
int xp_compute (CHAR_DATA *gch, CHAR_DATA *victim,
int total_levels, int members);
bool is_safe (CHAR_DATA *ch, CHAR_DATA *victim);
void make_corpse (CHAR_DATA *ch);
void one_hit (CHAR_DATA *ch, CHAR_DATA *victim, int dt,
int loc);
void mob_hit (CHAR_DATA *ch, CHAR_DATA *victim, int dt);
void set_fighting (CHAR_DATA *ch, CHAR_DATA *victim);
void disarm (CHAR_DATA *ch, CHAR_DATA *victim,
int disarm_second);
int critical_strike (CHAR_DATA *ch, CHAR_DATA *victim, int dam);
void check_eq_damage (CHAR_DATA *ch, CHAR_DATA *victim, int loc);
void check_shield_damage (CHAR_DATA *ch, CHAR_DATA *victim, int loc);
void check_weapon_damage (CHAR_DATA *ch, CHAR_DATA *victim, int loc);
int check_forest (CHAR_DATA *ch);
#define FOREST_ATTACK 1
#define FOREST_DEFENCE 2
#define FOREST_NONE 0
void handle_death (CHAR_DATA *ch, CHAR_DATA *victim);
/*
* Gets all money from the corpse.
*/
void get_gold_corpse(CHAR_DATA *ch, OBJ_DATA *corpse)
{
OBJ_DATA *tmp, *tmp_next;
for (tmp = corpse->contains; tmp; tmp = tmp_next) {
tmp_next = tmp->next_content;
if (tmp->pIndexData->item_type == ITEM_MONEY)
get_obj(ch, tmp, corpse);
}
}
int check_forest(CHAR_DATA* ch)
{
AFFECT_DATA* paf;
if (ch->in_room->sector_type != SECT_FOREST
&& ch->in_room->sector_type != SECT_HILLS
&& ch->in_room->sector_type != SECT_MOUNTAIN)
return FOREST_NONE;
for (paf = ch->affected; paf; paf = paf->next) {
if (paf->type == gsn_forest_fighting) {
if (paf->location == APPLY_AC)
return FOREST_DEFENCE;
else
return FOREST_ATTACK;
}
}
return FOREST_NONE;
}
/*
* Control the fights going on.
* Called periodically by update_handler.
*/
void violence_update(void)
{
CHAR_DATA *ch;
CHAR_DATA *ch_next;
CHAR_DATA *victim;
OBJ_DATA *obj;
OBJ_DATA *obj_next;
for (ch = char_list; ch; ch = ch_next) {
ch_next = ch->next;
/* decrement the wait */
if (ch->desc == NULL)
ch->wait = UMAX(0, ch->wait - PULSE_VIOLENCE);
if ((victim = ch->fighting) == NULL || ch->in_room == NULL)
continue;
if (IS_AWAKE(ch) && ch->in_room == victim->in_room)
multi_hit(ch, victim, TYPE_UNDEFINED);
else
stop_fighting(ch, FALSE);
if ((victim = ch->fighting) == NULL)
continue;
if (!IS_NPC(victim))
ch->last_fought = victim;
SET_FIGHT_TIME(ch);
if (victim->in_room != ch->in_room)
continue;
for (obj = ch->carrying; obj; obj = obj_next) {
obj_next = obj->next_content;
if (ch->fighting == NULL)
break;
oprog_call(OPROG_FIGHT, obj, ch, NULL);
}
if ((victim = ch->fighting) == NULL
|| victim->in_room != ch->in_room)
continue;
/*
* Fun for the whole family!
*/
check_assist(ch, victim);
if (IS_NPC(ch)) {
if (HAS_TRIGGER(ch, TRIG_FIGHT))
mp_percent_trigger(ch, victim, NULL, NULL,
TRIG_FIGHT);
if (HAS_TRIGGER(ch, TRIG_HPCNT))
mp_hprct_trigger(ch, victim);
}
}
}
/* for auto assisting */
void check_assist(CHAR_DATA *ch,CHAR_DATA *victim)
{
CHAR_DATA *rch, *rch_next;
for (rch = ch->in_room->people; rch != NULL; rch = rch_next) {
rch_next = rch->next_in_room;
if (IS_AWAKE(rch) && rch->fighting == NULL) {
/* quick check for ASSIST_PLAYER */
if (!IS_NPC(ch) && IS_NPC(rch)
&& IS_SET(rch->pIndexData->off_flags, ASSIST_PLAYERS)
&& rch->level + 6 > victim->level) {
do_emote(rch, "screams and attacks!");
multi_hit(rch,victim,TYPE_UNDEFINED);
continue;
}
/* PCs next */
if (!IS_NPC(rch) || IS_AFFECTED(rch, AFF_CHARM)) {
if (((!IS_NPC(rch) &&
IS_SET(rch->plr_flags, PLR_AUTOASSIST)) ||
IS_AFFECTED(rch, AFF_CHARM))
&& is_same_group(ch,rch)
&& !is_safe_nomessage(rch, victim))
multi_hit (rch,victim,TYPE_UNDEFINED);
continue;
}
if (!IS_NPC(ch) && RIDDEN(rch) == ch)
{
multi_hit(rch,victim,TYPE_UNDEFINED);
continue;
}
/* now check the NPC cases */
if (IS_NPC(ch)) {
if ((IS_NPC(rch) && IS_SET(rch->pIndexData->off_flags,ASSIST_ALL))
|| (IS_NPC(rch) && rch->race == ch->race
&& IS_SET(rch->pIndexData->off_flags,ASSIST_RACE))
|| (IS_NPC(rch) && IS_SET(rch->pIndexData->off_flags,ASSIST_ALIGN)
&& ((IS_GOOD(rch) && IS_GOOD(ch))
|| (IS_EVIL(rch) && IS_EVIL(ch))
|| (IS_NEUTRAL(rch) && IS_NEUTRAL(ch))))
|| (rch->pIndexData == ch->pIndexData
&& IS_SET(rch->pIndexData->off_flags,ASSIST_VNUM))) {
CHAR_DATA *vch;
CHAR_DATA *target;
int number;
if (number_bits(1) == 0)
continue;
target = NULL;
number = 0;
for (vch = ch->in_room->people; vch; vch = vch->next_in_room)
{
if (can_see(rch,vch)
&& is_same_group(vch,victim)
&& number_range(0,number) == 0)
{
target = vch;
number++;
}
}
if (target != NULL)
{
do_emote(rch,"screams and attacks!");
multi_hit(rch,target,TYPE_UNDEFINED);
}
}
}
}
}
}
/*
* Do one group of attacks.
*/
void multi_hit(CHAR_DATA *ch, CHAR_DATA *victim, int dt)
{
int chance;
/* no attacks for stunnies -- just a check */
if (ch->position < POS_RESTING)
return;
#if 0
/* become CRIMINAL in Law rooms */
if (!IS_NPC(ch) && !IS_NPC(victim)
&& IS_SET(ch->in_room->room_flags, ROOM_LAW)
&& !IS_SET(victim->plr_flags, PLR_WANTED)
&& !IS_SET(ch->plr_flags, PLR_WANTED)) {
char_puts("This room is under supervision of the law! "
"Now you're {RCRIMINAL{x!\n", ch);
SET_BIT(ch->plr_flags, PLR_WANTED);
}
#endif
/* ridden's adjustment */
if (RIDDEN(victim) && !IS_NPC(victim->mount)) {
if (victim->mount->fighting == NULL
|| victim->mount->fighting == ch)
victim = victim->mount;
else
do_dismount(victim->mount, str_empty);
}
if (IS_AFFECTED(ch,AFF_WEAK_STUN)) {
act_puts("You are too stunned to respond $N's attack.",
ch, NULL, victim, TO_CHAR, POS_FIGHTING);
act_puts("$n is too stunned to respond your attack.",
ch, NULL, victim, TO_VICT, POS_FIGHTING);
REMOVE_BIT(ch->affected_by, AFF_WEAK_STUN);
return;
}
if (IS_AFFECTED(ch,AFF_STUN)) {
act_puts("You are too stunned to respond $N's attack.",
ch, NULL, victim, TO_CHAR, POS_FIGHTING);
act_puts("$n is too stunned to respond your attack.",
ch, NULL, victim, TO_VICT, POS_FIGHTING);
act_puts("$n seems to be stunned.",
ch, NULL, victim, TO_NOTVICT, POS_FIGHTING);
REMOVE_BIT(ch->affected_by, AFF_STUN);
affect_bit_strip(ch, TO_AFFECTS, AFF_STUN);
SET_BIT(ch->affected_by, AFF_WEAK_STUN);
return;
}
if (IS_NPC(ch)) {
mob_hit(ch, victim, dt);
return;
}
one_hit(ch, victim, dt, WEAR_WIELD);
if (ch->fighting != victim)
return;
if ((chance = get_skill(ch, gsn_area_attack))
&& number_percent() < chance) {
int count = 0, max_count;
CHAR_DATA *vch, *vch_next;
check_improve(ch, gsn_area_attack, TRUE, 6);
if (LEVEL(ch) < 70)
max_count = 1;
else if (LEVEL(ch) < 80)
max_count = 2;
else if (LEVEL(ch) < 90)
max_count = 3;
else
max_count = 4;
for (vch = ch->in_room->people; vch; vch = vch_next) {
vch_next = vch->next_in_room;
if (vch != victim && vch->fighting == ch) {
one_hit(ch, vch, dt, WEAR_WIELD);
if (++count == max_count)
break;
}
}
}
if (IS_AFFECTED(ch, AFF_HASTE))
one_hit(ch, victim, dt, WEAR_WIELD);
if (ch->fighting != victim || dt == gsn_backstab || dt == gsn_cleave
|| dt == gsn_ambush || dt == gsn_dual_backstab || dt == gsn_circle
|| dt == gsn_assassinate || dt == gsn_vampiric_bite || dt == gsn_knife)
return;
chance = get_skill(ch, gsn_second_attack) / 2;
if (number_percent() < chance) {
one_hit(ch, victim, dt, WEAR_WIELD);
check_improve(ch, gsn_second_attack, TRUE, 5);
if (ch->fighting != victim)
return;
}
chance = get_skill(ch,gsn_third_attack)/3;
if (number_percent() < chance) {
one_hit(ch, victim, dt, WEAR_WIELD);
check_improve(ch, gsn_third_attack, TRUE, 6);
if (ch->fighting != victim)
return;
}
chance = get_skill(ch,gsn_fourth_attack)/6;
if (number_percent() < chance) {
one_hit(ch, victim, dt, WEAR_WIELD);
check_improve(ch, gsn_fourth_attack, TRUE, 7);
if (ch->fighting != victim)
return;
}
chance = get_skill(ch,gsn_fifth_attack)/8;
if (number_percent() < chance) {
one_hit(ch, victim, dt, WEAR_WIELD);
check_improve(ch,gsn_fifth_attack,TRUE,8);
if (ch->fighting != victim)
return;
}
if (check_forest(ch) == FOREST_ATTACK) {
chance = get_skill(ch, gsn_forest_fighting);
while (number_percent() < chance) {
one_hit(ch, victim, dt, WEAR_WIELD);
check_improve (ch, gsn_forest_fighting, TRUE, 8);
if (ch->fighting != victim)
return;
chance /= 3;
}
}
chance = get_skill(ch, gsn_second_weapon) / 2;
if (number_percent() < chance)
if (get_eq_char(ch, WEAR_SECOND_WIELD)) {
one_hit(ch, victim, dt, WEAR_SECOND_WIELD);
check_improve(ch, gsn_second_weapon, TRUE, 2);
if (ch->fighting != victim)
return;
}
chance = get_skill(ch,gsn_secondary_attack) / 8;
if (number_percent() < chance)
if (get_eq_char(ch, WEAR_SECOND_WIELD)) {
one_hit(ch, victim, dt, WEAR_SECOND_WIELD);
check_improve(ch, gsn_secondary_attack, TRUE, 2);
if (ch->fighting != victim)
return;
}
}
/* procedure for all mobile attacks */
void mob_hit(CHAR_DATA *ch, CHAR_DATA *victim, int dt)
{
CHAR_DATA *vch, *vch_next;
flag64_t act = ch->pIndexData->act;
flag64_t off = ch->pIndexData->off_flags;
/* no attack by ridden mobiles except spec_casts */
if (RIDDEN(ch)) {
if (ch->fighting != victim) {
stop_fighting(ch, FALSE);
set_fighting(ch, victim);
}
return;
}
one_hit(ch, victim, dt, WEAR_WIELD);
if (ch->fighting != victim)
return;
/* Area attack -- BALLS nasty! */
if (IS_SET(off, OFF_AREA_ATTACK)) {
for (vch = ch->in_room->people; vch != NULL; vch = vch_next) {
vch_next = vch->next_in_room;
if ((vch != victim && vch->fighting == ch))
one_hit(ch, vch, dt, WEAR_WIELD);
}
}
if (IS_AFFECTED(ch, AFF_HASTE) || IS_SET(off, OFF_FAST))
one_hit(ch, victim, dt, WEAR_WIELD);
if (ch->fighting != victim || dt == gsn_backstab || dt == gsn_circle ||
dt == gsn_dual_backstab || dt == gsn_cleave || dt == gsn_ambush
|| dt == gsn_vampiric_bite || dt == gsn_knife)
return;
if (number_percent() < get_skill(ch, gsn_second_attack) / 2) {
one_hit(ch, victim, dt, WEAR_WIELD);
if (ch->fighting != victim)
return;
}
if (number_percent() < get_skill(ch, gsn_third_attack) / 4) {
one_hit(ch, victim, dt, WEAR_WIELD);
if (ch->fighting != victim)
return;
}
if (number_percent() < get_skill(ch, gsn_fourth_attack) / 6) {
one_hit(ch, victim, dt, WEAR_WIELD);
if (ch->fighting != victim)
return;
}
/* PC waits */
if (ch->wait > 0)
return;
#if 0
switch (number_range(0, 2)) {
case 1:
if (IS_SET(act, ACT_MAGE)) {
mob_cast_mage(ch, victim);
return;
}
break;
case 2:
if (IS_SET(act, ACT_CLERIC)) {
mob_cast_cleric(ch, victim);
return;
}
break;
}
#endif
/* now for the skills */
switch (number_range(0, 7)) {
case 0:
if (IS_SET(off, OFF_BASH))
do_bash(ch, str_empty);
break;
case 1:
if (IS_SET(off, OFF_BERSERK)
&& !IS_AFFECTED(ch, AFF_BERSERK))
do_berserk(ch, str_empty);
break;
case 2:
if (IS_SET(off, OFF_DISARM)
|| IS_SET(act, ACT_WARRIOR | ACT_THIEF)) {
if (number_range(0, 1)
&& get_eq_char(victim, WEAR_SECOND_WIELD))
do_disarm(ch, "second");
else if (get_eq_char(victim, WEAR_WIELD))
do_disarm(ch, str_empty);
}
break;
case 3:
if (IS_SET(off, OFF_KICK))
do_kick(ch, str_empty);
break;
case 4:
if (IS_SET(off, OFF_DIRT_KICK))
do_dirt(ch, str_empty);
break;
case 5:
if (IS_SET(off, OFF_TAIL))
do_tail(ch, str_empty);
break;
case 6:
if (IS_SET(off, OFF_TRIP))
do_trip(ch, str_empty);
break;
case 7:
if (IS_SET(off, OFF_CRUSH))
do_crush(ch, str_empty);
break;
}
}
int get_dam_type(CHAR_DATA *ch, OBJ_DATA *wield, int *dt)
{
int dam_type;
if (*dt == TYPE_UNDEFINED) {
*dt = TYPE_HIT;
if (wield && wield->pIndexData->item_type == ITEM_WEAPON)
*dt += wield->value[3];
else
*dt += ch->dam_type;
}
if (*dt < TYPE_HIT)
if (wield)
dam_type = attack_table[wield->value[3]].damage;
else
dam_type = attack_table[ch->dam_type].damage;
else
dam_type = attack_table[*dt - TYPE_HIT].damage;
if (dam_type == TYPE_UNDEFINED)
dam_type = DAM_BASH;
return dam_type;
}
/*
* Hit one guy once.
*/
void one_hit(CHAR_DATA *ch, CHAR_DATA *victim, int dt, int loc)
{
OBJ_DATA *wield;
int victim_ac;
int thac0;
int thac0_00;
int thac0_32;
int dam;
int diceroll;
int sn, sk, sk2;
int dam_type;
bool counter;
bool result;
int sercount;
int dam_flags;
sn = -1;
counter = FALSE;
/* just in case */
if (victim == ch || ch == NULL || victim == NULL)
return;
/*
* Can't beat a dead char!
* Guard against weird room-leavings.
*/
if (victim->position == POS_DEAD || ch->in_room != victim->in_room)
return;
/*
* Figure out the type of damage message.
*/
wield = get_eq_char(ch, loc);
dam_flags = DAMF_SHOW;
if (loc == WEAR_SECOND_WIELD)
dam_flags |= DAMF_SECOND;
dam_type = get_dam_type(ch, wield, &dt);
/* get the weapon skill */
sn = get_weapon_sn(wield);
sk = 20 + get_weapon_skill(ch, sn);
/*
* Calculate to-hit-armor-class-0 versus armor.
*/
if (IS_NPC(ch)) {
flag64_t act = ch->pIndexData->act;
thac0_00 = 20;
thac0_32 = -4; /* as good as a thief */
if (IS_SET(act, ACT_WARRIOR))
thac0_32 = -10;
else if (IS_SET(act, ACT_THIEF))
thac0_32 = -4;
else if (IS_SET(act, ACT_CLERIC))
thac0_32 = 2;
else if (IS_SET(act, ACT_MAGE))
thac0_32 = 6;
}
else {
class_t *cl;
if ((cl = class_lookup(ch->class)) == NULL)
return;
thac0_00 = cl->thac0_00;
thac0_32 = cl->thac0_32;
}
thac0 = interpolate(LEVEL(ch), thac0_00, thac0_32);
if (thac0 < 0)
thac0 = thac0/2;
if (thac0 < -5)
thac0 = -5 + (thac0 + 5) / 2;
thac0 -= GET_HITROLL(ch) * sk / 100;
thac0 += 5 * (100 - sk) / 100;
if (dt == gsn_backstab)
thac0 -= 10 * (100 - get_skill(ch, gsn_backstab));
else if (dt == gsn_dual_backstab)
thac0 -= 10 * (100 - get_skill(ch, gsn_dual_backstab));
else if (dt == gsn_cleave)
thac0 -= 10 * (100 - get_skill(ch, gsn_cleave));
else if (dt == gsn_ambush)
thac0 -= 10 * (100 - get_skill(ch, gsn_ambush));
else if (dt == gsn_vampiric_bite)
thac0 -= 10 * (100 - get_skill(ch, gsn_vampiric_bite));
else if (dt == gsn_charge)
thac0 -= 10 * (100 - get_skill(ch, gsn_charge));
switch(dam_type) {
case DAM_PIERCE:victim_ac = GET_AC(victim,AC_PIERCE)/10; break;
case DAM_BASH: victim_ac = GET_AC(victim,AC_BASH)/10; break;
case DAM_SLASH: victim_ac = GET_AC(victim,AC_SLASH)/10; break;
default: victim_ac = GET_AC(victim,AC_EXOTIC)/10; break;
}
if (victim_ac < -15)
victim_ac = (victim_ac + 15) / 5 - 15;
if (get_skill(victim, gsn_armor_use) > 70) {
check_improve(victim, gsn_armor_use, TRUE, 8);
victim_ac -= (victim->level) / 2;
}
if (!can_see(ch, victim)) {
if ((sk2 = get_skill(ch, gsn_blind_fighting))
&& number_percent() < sk2)
check_improve(ch,gsn_blind_fighting,TRUE,16);
else
victim_ac -= 4;
}
if (victim->position < POS_FIGHTING)
victim_ac += 4;
if (victim->position < POS_RESTING)
victim_ac += 6;
/*
* The moment of excitement!
*/
while ((diceroll = number_bits(5)) >= 20)
;
if (diceroll == 0
|| (diceroll != 19 && diceroll < thac0 - victim_ac)) {
/* Miss. */
damage(ch, victim, 0, dt, dam_type, dam_flags);
tail_chain();
return;
}
/*
* Hit.
* Calc damage.
*/
if (IS_NPC(ch) && wield == NULL)
dam = dice(ch->damage[DICE_NUMBER], ch->damage[DICE_TYPE]);
else {
if (sn != -1)
check_improve(ch, sn, TRUE, 5);
if (wield != NULL) {
dam = dice(wield->value[1],
wield->value[2]) * sk / 100;
/* no shield = more */
if (get_eq_char(ch, WEAR_SHIELD) == NULL)
dam = dam * 21/20;
/* sharpness! */
if (IS_WEAPON_STAT(wield, WEAPON_SHARP)) {
int percent;
if ((percent = number_percent()) <= (sk / 8))
dam = 2 * dam + (dam * 2 * percent / 100);
}
/* holy weapon */
if (IS_WEAPON_STAT(wield, WEAPON_HOLY)
&& IS_GOOD(ch) && IS_EVIL(victim)
&& number_percent() < 30) {
act("$n's flesh is burned with the holy aura of $p.", victim, wield, NULL, TO_ROOM);
act_puts("Your flesh is burned with the holy aura of $p.", victim, wield, NULL, TO_CHAR, POS_DEAD);
dam += dam * 120 / 100;
}
}
else {
dam = number_range(1 + 4 * sk / 100,
2 * LEVEL(ch) / 3 * sk / 100);
if ((sk2 = get_skill(ch, gsn_master_hand))
&& number_percent() <= sk2) {
check_improve(ch, gsn_master_hand, TRUE, 6);
dam += dam * 110 /100;
if (number_percent() < sk2/5+LEVEL(ch)-LEVEL(victim)) {
SET_BIT(victim->affected_by,
AFF_WEAK_STUN);
act_puts("You hit $N with a stunning "
"force!", ch, NULL, victim,
TO_CHAR, POS_DEAD);
act_puts("$n hit you with a stunning "
"force!", ch, NULL, victim,
TO_VICT, POS_DEAD);
act_puts("$n hits $N with a stunning "
"force!", ch, NULL, victim,
TO_NOTVICT, POS_DEAD);
check_improve(ch, gsn_master_hand,
TRUE, 6);
}
}
}
}
/*
* Bonuses.
*/
if ((sk2 = get_skill(ch, gsn_enhanced_damage))
&& (diceroll = number_percent()) <= sk2) {
check_improve(ch, gsn_enhanced_damage, TRUE, 6);
dam += dam * diceroll * sk2 / 10000;
}
if (sn == gsn_sword
&& (sk2 = get_skill(ch, gsn_mastering_sword))
&& number_percent() <= sk2) {
OBJ_DATA *katana;
check_improve(ch, gsn_mastering_sword, TRUE, 6);
dam += dam * 110 /100;
if (((katana = get_eq_char(ch,WEAR_WIELD)) ||
(katana = get_eq_char(ch, WEAR_SECOND_WIELD)))
&& IS_WEAPON_STAT(katana, WEAPON_KATANA)
&& strstr(mlstr_mval(katana->ed->description), ch->name)) {
AFFECT_DATA *paf;
if ((katana->cost = ++katana->cost % 250) == 0
&& (paf = affect_find(katana->affected, gsn_katana))) {
int old_mod = paf->modifier;
paf->modifier = UMIN(paf->modifier+1,
ch->level / 3);
if (paf->next != NULL) {
paf->next->modifier = paf->modifier;
ch->hitroll += paf->modifier - old_mod;
if (paf->next->next) {
paf->next->next->modifier =
paf->modifier;
ch->damroll +=
paf->modifier - old_mod;
}
}
act("$n's katana glows blue.\n",
ch, NULL, NULL, TO_ROOM);
char_puts("Your katana glows blue.\n",ch);
}
}
}
if (!IS_AWAKE(victim))
dam *= 2;
else if (victim->position < POS_FIGHTING)
dam = dam * 3 / 2;
sercount = number_percent();
if (dt == gsn_backstab || dt == gsn_vampiric_bite)
sercount += 40;
if (!IS_IMMORTAL(ch) && IS_PUMPED(ch))
sercount += 10;
sercount *= 2;
if (victim->fighting == NULL && !IS_NPC(victim)
&& !is_safe_nomessage(victim, ch)
&& !is_safe_nomessage(ch,victim)
&& (victim->position == POS_SITTING ||
victim->position == POS_STANDING)
&& dt != gsn_assassinate
&& (sercount <= get_skill(victim, gsn_counter))) {
counter = TRUE;
check_improve(victim,gsn_counter,TRUE,1);
act("$N turns your attack against you!",
ch, NULL, victim, TO_CHAR);
act("You turn $n's attack against $m!",
ch, NULL, victim, TO_VICT);
act("$N turns $n's attack against $m!",
ch, NULL, victim, TO_NOTVICT);
ch->fighting = victim;
}
else if (!victim->fighting)
check_improve(victim, gsn_counter, FALSE, 1);
if (dt == gsn_backstab && (IS_NPC(ch) || wield))
dam = LEVEL(ch) / 11 * dam + LEVEL(ch);
else if (dt == gsn_dual_backstab && (IS_NPC(ch) || wield))
dam = LEVEL(ch) / 14 * dam + LEVEL(ch);
else if (dt == gsn_circle)
dam = (LEVEL(ch)/40 + 1) * dam + LEVEL(ch);
else if (dt == gsn_knife)
dam = (LEVEL(ch)/28 + 1) * dam + LEVEL(ch);
else if (dt == gsn_vampiric_bite && is_affected(ch, gsn_vampire))
dam = (LEVEL(ch)/13 + 1) * dam + LEVEL(ch);
else if (dt == gsn_cleave && wield != NULL) {
if (number_percent() <
(URANGE(4, 5+LEVEL(ch)-LEVEL(victim), 10)
+ (wield->value[0]==WEAPON_AXE) ? 2:0 +
(get_curr_stat(ch,STAT_STR)-21)/2)
&& !counter && !IS_IMMORTAL(victim)) {
act_puts("Your cleave chops $N IN HALF!",
ch, NULL, victim, TO_CHAR, POS_RESTING);
act_puts("$n's cleave chops you IN HALF!",
ch, NULL, victim, TO_VICT, POS_RESTING);
act_puts("$n's cleave chops $N IN HALF!",
ch, NULL, victim, TO_NOTVICT, POS_RESTING);
char_puts("You have been KILLED!\n", victim);
act("$n is DEAD!", victim, NULL, NULL, TO_ROOM);
WAIT_STATE(ch, 2);
victim->position = POS_DEAD;
handle_death(ch, victim);
return;
}
else
dam = (dam * 2 + ch->level);
}
if (dt == gsn_assassinate) {
if (number_percent() <=
URANGE(10, 20+(LEVEL(ch) - LEVEL(victim))*2, 50)
&& !counter && !IS_IMMORTAL(victim)) {
act_puts("You {R+++ASSASSINATE+++{x $N!",
ch, NULL, victim, TO_CHAR, POS_RESTING);
act_puts("$n {R+++ASSASSINATES+++{x $N!",
ch, NULL, victim, TO_NOTVICT, POS_RESTING);
act_puts("$n {R+++ASSASSINATES+++{x you!",
ch, NULL, victim, TO_VICT, POS_DEAD);
char_puts("You have been KILLED!\n", victim);
act("$n is DEAD!", victim, NULL, victim, TO_ROOM);
check_improve(ch, gsn_assassinate, TRUE, 1);
victim->position = POS_DEAD;
handle_death(ch, victim);
return;
}
else {
check_improve(ch, gsn_assassinate, FALSE, 1);
dam *= 2;
}
}
if (dt == gsn_charge)
dam *= LEVEL(ch)/12;
dam += GET_DAMROLL(ch) * UMIN(100, sk) / 100;
if (dt == gsn_ambush)
dam *= UMAX(3, LEVEL(ch)/12);
if ((sk2 = get_skill(ch, gsn_deathblow)) > 1
&& dt != gsn_backstab
&& dt != gsn_dual_backstab
&& dt != gsn_cleave
&& dt != gsn_assassinate
&& dt != gsn_ambush
&& dt != gsn_vampiric_bite
&& dt != gsn_knife) {
if (number_percent() < (sk2/8)) {
act("You deliver a blow of deadly force!",
ch, NULL, NULL, TO_CHAR);
act("$n delivers a blow of deadly force!",
ch, NULL, NULL, TO_ROOM);
dam = LEVEL(ch)*dam/20;
check_improve(ch, gsn_deathblow, TRUE, 1);
}
else
check_improve(ch, gsn_deathblow, FALSE, 3);
}
if (dam <= 0)
dam = 1;
if (counter) {
result = damage(ch, ch, 2*dam, dt, dam_type, DAMF_SHOW);
multi_hit(victim, ch, TYPE_UNDEFINED);
}
else
result = damage(ch, victim, dam, dt, dam_type, dam_flags);
/* vampiric bite gives hp to ch from victim */
if (dt == gsn_vampiric_bite) {
int hit_ga = UMIN((dam / 2), victim->max_hit);
ch->hit += hit_ga;
ch->hit = UMIN(ch->hit, ch->max_hit);
update_pos(ch);
char_puts("Your health increases as you suck "
"your victim's blood.\n", ch);
}
/* but do we have a funky weapon? */
if (result && wield != NULL && ch->fighting == victim) {
int dam;
if (IS_WEAPON_STAT(wield, WEAPON_VORPAL)) {
int chance;
chance = get_skill(ch, get_weapon_sn(wield)) +
get_curr_stat(ch, STAT_STR) * 4;
if (chance > number_range(1, 200000)
&& !IS_IMMORTAL(victim)) {
act("$p makes an huge arc in the air, "
"chopping $n's head OFF!",
victim, wield, NULL, TO_ROOM);
act("$p whistles in the air, "
"chopping your head OFF!",
victim, wield, NULL, TO_CHAR);
act("$n is DEAD!", victim, NULL, NULL, TO_ROOM);
char_puts("You have been KILLED!\n", victim);
victim->position = POS_DEAD;
handle_death(ch, victim);
return;
}
}
if (IS_WEAPON_STAT(wield, WEAPON_POISON)) {
int level;
AFFECT_DATA *poison, af;
if ((poison = affect_find(wield->affected,
gsn_poison)) == NULL)
level = wield->level;
else
level = poison->level;
if (!saves_spell(level / 2,victim, DAM_POISON)) {
char_puts("You feel poison coursing "
"through your veins.", victim);
act("$n is poisoned by the venom on $p.",
victim, wield, NULL, TO_ROOM);
af.where = TO_AFFECTS;
af.type = gsn_poison;
af.level = level * 3/4;
af.duration = level / 2;
af.location = APPLY_STR;
af.modifier = -1;
af.bitvector = AFF_POISON;
affect_join(victim, &af);
}
/* weaken the poison if it's temporary */
if (poison != NULL) {
poison->level = UMAX(0,poison->level - 2);
poison->duration = UMAX(0,poison->duration - 1);
if (poison->level == 0
|| poison->duration == 0)
act("The poison on $p has worn off.",
ch, wield, NULL, TO_CHAR);
}
}
if (IS_WEAPON_STAT(wield, WEAPON_VAMPIRIC)) {
dam = number_range(1, wield->level / 5 + 1);
act("$p draws life from $n.",
victim, wield, NULL, TO_ROOM);
act("You feel $p drawing your life away.",
victim, wield, NULL, TO_CHAR);
damage(ch, victim, dam, 0, DAM_NEGATIVE, DAMF_NONE);
ch->hit += dam/2;
}
if (IS_WEAPON_STAT(wield, WEAPON_FLAMING)) {
dam = number_range(1,wield->level / 4 + 1);
act("$n is burned by $p.", victim, wield, NULL, TO_ROOM);
act("$p sears your flesh.",
victim, wield, NULL, TO_CHAR);
fire_effect((void *) victim, wield->level/2, dam,
TARGET_CHAR);
damage(ch, victim, dam, 0, DAM_FIRE, DAMF_NONE);
}
if (IS_WEAPON_STAT(wield, WEAPON_FROST)) {
dam = number_range(1,wield->level / 6 + 2);
act("$p freezes $n.", victim, wield, NULL, TO_ROOM);
act("The cold touch of $p surrounds you with ice.",
victim, wield, NULL, TO_CHAR);
cold_effect(victim, wield->level/2, dam, TARGET_CHAR);
damage(ch, victim, dam, 0, DAM_COLD, DAMF_NONE);
}
if (IS_WEAPON_STAT(wield, WEAPON_SHOCKING)) {
dam = number_range(1, wield->level/5 + 2);
act("$n is struck by lightning from $p.",
victim, wield, NULL, TO_ROOM);
act("You are shocked by $p.",
victim, wield, NULL, TO_CHAR);
shock_effect(victim, wield->level/2, dam, TARGET_CHAR);
damage(ch, victim, dam, 0, DAM_LIGHTNING, DAMF_NONE);
}
}
tail_chain();
}
/*
* handle_death - called from `damage' if `ch' has killed `victim'
*/
void handle_death(CHAR_DATA *ch, CHAR_DATA *victim)
{
bool vnpc = IS_NPC(victim);
ROOM_INDEX_DATA *vroom = victim->in_room;
bool is_duel = !IS_NPC(victim)
&& (!IS_NPC(ch) || IS_AFFECTED(ch, AFF_CHARM))
&& IS_SET(victim->in_room->room_flags, ROOM_BATTLE_ARENA);
OBJ_DATA *corpse;
class_t *cl;
group_gain(ch, victim);
/*
* Death trigger
*/
if (vnpc && HAS_TRIGGER(victim, TRIG_DEATH)) {
victim->position = POS_STANDING;
mp_percent_trigger(victim, ch, NULL, NULL, TRIG_DEATH);
}
raw_kill(ch, victim);
/* RT new auto commands */
if (!IS_NPC(ch) && vnpc && vroom == ch->in_room
&& (corpse = get_obj_list(ch, "corpse", ch->in_room->contents))) {
if (HAS_SKILL(ch, gsn_vampire)) {
act_puts("$n suck {Rblood{x from $N's corpse!!",
ch, NULL,victim,TO_ROOM,POS_SLEEPING);
char_puts("You suck {Rblood{x "
"from the corpse!!\n\n", ch);
gain_condition(ch, COND_BLOODLUST, 3);
}
if (IS_SET(ch->plr_flags, PLR_AUTOLOOK))
do_look_in(ch, "corpse");
if (corpse->contains)
/* corpse exists and not empty */
if (IS_SET(ch->plr_flags, PLR_AUTOLOOT))
do_get(ch, "all corpse");
else if (IS_SET(ch->plr_flags, PLR_AUTOGOLD))
get_gold_corpse(ch, corpse);
if (IS_SET(ch->plr_flags, PLR_AUTOSAC))
do_sacrifice(ch, "corpse");
}
if (vnpc || victim->position == POS_STANDING)
return;
if (is_duel)
return;
/* Dying penalty: 2/3 way back. */
if (IS_SET(victim->plr_flags, PLR_WANTED)
&& victim->level > 1) {
REMOVE_BIT(victim->plr_flags, PLR_WANTED);
victim->level--;
victim->pcdata->plevels++;
victim->exp = exp_for_level(victim, victim->level);
victim->exp_tl = 0;
}
else
if (victim->exp_tl > 0)
gain_exp(victim, -victim->exp_tl*2/3);
if ((++victim->pcdata->death % 3) != 2)
return;
/* Die too much and is deleted ... :( */
if ((cl = class_lookup(victim->class))
&& !CAN_FLEE(ch, cl)) {
victim->perm_stat[STAT_CHA]--;
if (victim->pcdata->death > cl->death_limit) {
char msg[MAX_STRING_LENGTH];
snprintf(msg, sizeof(msg),
"%d deaths limit for %s",
cl->death_limit, cl->name);
delete_player(victim, msg);
return;
}
}
else if (--victim->perm_stat[STAT_CON] < 3) {
delete_player(victim, "lack of CON");
return;
}
else
char_puts("You feel your life power has decreased "
"with this death.\n", victim);
}
/*
* Inflict damage from a hit.
*/
bool damage(CHAR_DATA *ch, CHAR_DATA *victim,
int dam, int dt, int dam_type, int dam_flags)
{
bool immune;
int dam2;
int loc;
if (JUST_KILLED(victim))
return FALSE;
if (victim != ch) {
/*
* Certain attacks are forbidden.
* Most other attacks are returned.
*/
if (victim->position > POS_STUNNED) {
if (victim->fighting == NULL) {
set_fighting(victim, ch);
if (IS_NPC(victim)
&& HAS_TRIGGER(victim, TRIG_KILL))
mp_percent_trigger(victim, ch, NULL,
NULL, TRIG_KILL);
}
if (victim->timer <= 4)
victim->position = POS_FIGHTING;
}
if (victim->position > POS_STUNNED) {
if (ch->fighting == NULL)
set_fighting(ch, victim);
/*
* If victim is charmed, ch might attack
* victim's master.
*/
if (IS_NPC(ch)
&& IS_NPC(victim)
&& IS_AFFECTED(victim, AFF_CHARM)
&& victim->master
&& victim->master->in_room == ch->in_room
&& !victim->master->fighting
&& number_bits(2) == 0) {
stop_fighting(ch, FALSE);
multi_hit(ch, victim->master, TYPE_UNDEFINED);
return FALSE;
}
}
/*
* More charm and group stuff.
*/
if (victim->master == ch)
stop_follower(victim);
if (MOUNTED(victim) == ch || RIDDEN(victim) == ch)
victim->riding = ch->riding = 0;
}
/*
* No one in combat can hide, be invis or camoed.
*/
do_visible(ch, str_empty);
/*
* Damage modifiers.
*/
if (IS_AFFECTED(victim, AFF_SANCTUARY)
&& !((dt == gsn_cleave) && (number_percent() < 50)))
dam /= 2;
if (IS_AFFECTED(victim, AFF_BLACK_SHROUD))
dam = (4*dam)/7;
if (IS_AFFECTED(victim, AFF_PROTECT_EVIL) && IS_EVIL(ch))
dam -= dam / 4;
if (IS_AFFECTED(victim, AFF_PROTECT_GOOD) && IS_GOOD(ch))
dam -= dam / 4;
if (is_affected(victim,gsn_resistance))
dam = (3 * dam)/5;
if (is_affected(victim, gsn_protection_heat) && (dam_type == DAM_FIRE))
dam -= dam / 4;
if (is_affected(victim, gsn_protection_cold) && (dam_type == DAM_COLD))
dam -= dam / 4;
immune = FALSE;
loc = IS_SET(dam_flags, DAMF_SECOND) ? WEAR_SECOND_WIELD : WEAR_WIELD;
/*
* Check for parry, and dodge.
*/
if (dt >= TYPE_HIT && ch != victim) {
/*
* some funny stuff
*/
if (is_affected(victim, gsn_mirror)) {
act("$n shatters into tiny fragments of glass.",
victim, NULL, NULL, TO_ROOM);
extract_char(victim, 0);
return FALSE;
}
if (check_parry(ch, victim, loc))
return FALSE;
if (check_block(ch, victim, loc))
return FALSE;
if (check_dodge(ch, victim))
return FALSE;
if (check_blink(ch, victim))
return FALSE;
if (check_hand_block(ch, victim))
return FALSE;
}
switch(check_immune(victim, dam_type)) {
case IS_IMMUNE:
immune = TRUE;
dam = 0;
break;
case IS_RESISTANT:
dam -= dam/3;
break;
case IS_VULNERABLE:
dam += dam/2;
break;
}
if (dt >= TYPE_HIT && ch != victim) {
if ((dam2 = critical_strike(ch, victim, dam)) != 0)
dam = dam2;
}
if (IS_SET(dam_flags, DAMF_SHOW))
dam_message(ch, victim, dam, dt, immune, dam_type);
if (dam == 0)
return FALSE;
if (dt >= TYPE_HIT && ch != victim)
check_eq_damage(ch, victim, loc);
/*
* Hurt the victim.
* Inform the victim of his new state.
*/
victim->hit -= dam;
if (IS_IMMORTAL(victim) && victim->hit < 1)
victim->hit = 1;
update_pos(victim);
switch(victim->position) {
case POS_MORTAL:
if (dam_type == DAM_HUNGER || dam_type == DAM_THIRST) break;
act("$n is mortally wounded, and will die soon, if not aided.",
victim, NULL, NULL, TO_ROOM);
char_puts( "You are mortally wounded, and will die soon, if not aided.\n", victim);
break;
case POS_INCAP:
if (dam_type == DAM_HUNGER || dam_type == DAM_THIRST) break;
act("$n is incapacitated and will slowly die, if not aided.",
victim, NULL, NULL, TO_ROOM);
char_puts( "You are incapacitated and will slowly die, if not aided.\n", victim);
break;
case POS_STUNNED:
if (dam_type == DAM_HUNGER || dam_type == DAM_THIRST) break;
act("$n is stunned, but will probably recover.",
victim, NULL, NULL, TO_ROOM);
char_puts("You are stunned, but will probably recover.\n",
victim);
break;
case POS_DEAD:
act("$n is DEAD!!", victim, 0, 0, TO_ROOM);
char_puts("You have been KILLED!!\n\n", victim);
break;
default:
if (dam_type == DAM_HUNGER || dam_type == DAM_THIRST) break;
if (dam > victim->max_hit / 4)
char_puts("That really did HURT!\n", victim);
if (victim->hit < victim->max_hit / 4)
char_puts("You sure are BLEEDING!\n", victim);
break;
}
/*
* Sleep spells and extremely wounded folks.
*/
if (!IS_AWAKE(victim) && victim->fighting)
victim->fighting = NULL;
/*
* Payoff for killing things.
*/
if (victim->position == POS_DEAD) {
handle_death(ch, victim);
return TRUE;
}
if (victim == ch)
return TRUE;
/*
* Take care of link dead people.
*/
if (!IS_NPC(victim)
&& victim->desc == NULL
&& !IS_SET(victim->comm, COMM_NOFLEE)) {
if (number_range(0, victim->wait) == 0) {
do_flee(victim, str_empty);
return TRUE;
}
}
/*
* Wimp out?
*/
if (IS_NPC(victim) && dam > 0 && victim->wait < PULSE_VIOLENCE / 2) {
flag64_t act = victim->pIndexData->act;
if ((IS_SET(act, ACT_WIMPY) && number_bits(2) == 0 &&
victim->hit < victim->max_hit / 5)
|| (IS_AFFECTED(victim, AFF_CHARM) &&
victim->master != NULL &&
victim->master->in_room != victim->in_room)
|| (IS_AFFECTED(victim, AFF_DETECT_FEAR) &&
!IS_SET(act, ACT_NOTRACK))) {
do_flee(victim, str_empty);
victim->last_fought = NULL;
}
}
if (!IS_NPC(victim)
&& victim->hit > 0
&& (victim->hit <= victim->wimpy || IS_AFFECTED(victim, AFF_DETECT_FEAR))
&& victim->wait < PULSE_VIOLENCE / 2)
do_flee(victim, str_empty);
tail_chain();
return TRUE;
}
static bool inline
is_safe_raw(CHAR_DATA *ch, CHAR_DATA *victim)
{
/*
* ghosts are safe
* this check must be done first to avoid
* suicyco muttafuckas who recite 'leather-bound book' (#5743)
* without any target specified
* extracted NPCs are safe too
*/
if (!IS_NPC(victim)) {
int clan;
/* ghost cannot attack anyone */
if (ch != victim
&& !IS_NPC(ch)
&& IS_SET(ch->plr_flags, PLR_GHOST))
return TRUE;
/* clan defenders can attack anyone in their clan */
if (victim->in_room
&& (clan = victim->in_room->area->clan)
&& victim->clan != clan
&& ch->clan == clan)
return FALSE;
/* otherwise ghosts are safe */
if (IS_SET(victim->plr_flags, PLR_GHOST))
return TRUE;
}
else if (victim->extracted)
return TRUE;
if (victim->fighting == ch
|| ch == victim
|| IS_IMMORTAL(ch))
return FALSE;
/* handle ROOM_PEACE flags */
if ((victim->in_room && IS_SET(victim->in_room->room_flags, ROOM_PEACE))
|| (ch->in_room && IS_SET(ch->in_room->room_flags, ROOM_PEACE)))
return TRUE;
/* link dead players whose adrenalin is not gushing are safe */
if (!IS_NPC(victim) && !IS_PUMPED(victim) && victim->desc == NULL)
return TRUE;
return !in_PK(ch, victim);
}
/*
* generic safe-checking function wrapper
*
* all the checks are done is_safe_raw to properly strip PLR_GHOST
* flag if victim is not safe. add you checks there
*/
bool is_safe_nomessage(CHAR_DATA *ch, CHAR_DATA *victim)
{
bool safe;
CHAR_DATA *mount;
if (IS_NPC(ch)
&& IS_AFFECTED(ch, AFF_CHARM)
&& ch->master
&& ch->in_room == ch->master->in_room)
return is_safe_nomessage(ch->master, victim);
if (IS_NPC(victim)
&& IS_AFFECTED(victim, AFF_CHARM)
&& victim->master)
return is_safe_nomessage(ch, victim->master);
if ((mount = RIDDEN(victim)))
return is_safe_nomessage(ch, mount);
if ((safe = is_safe_raw(ch, victim)) || IS_NPC(ch))
return safe;
if (victim != ch && IS_SET(ch->plr_flags, PLR_GHOST)) {
char_puts("You return to your normal form.\n", ch);
REMOVE_BIT(ch->plr_flags, PLR_GHOST);
}
return safe;
}
bool is_safe(CHAR_DATA *ch, CHAR_DATA *victim)
{
if (is_safe_nomessage(ch, victim)) {
act("The gods protect $N.",ch,NULL,victim,TO_CHAR);
act("The gods protect $N from $n.",ch,NULL,victim,TO_ROOM);
return TRUE;
}
return FALSE;
}
bool is_safe_spell(CHAR_DATA *ch, CHAR_DATA *victim, bool area)
{
#if 0
if (ch == victim && !area)
return TRUE;
#endif
if (area) {
if (IS_IMMORTAL(victim)
|| is_same_group(ch, victim)
|| ch == victim
|| RIDDEN(ch) == victim
|| MOUNTED(ch) == victim)
return TRUE;
}
return is_safe(ch, victim);
}
/*
* Check for parry.
*/
bool check_parry(CHAR_DATA *ch, CHAR_DATA *victim, int loc)
{
int chance;
if (!IS_AWAKE(victim))
return FALSE;
if (IS_NPC(victim))
chance = UMIN(35, victim->level);
else {
if (get_eq_char(victim, WEAR_WIELD) == NULL)
return FALSE;
chance = get_skill(victim, gsn_parry) / 2;
}
if (check_forest(victim) == FOREST_DEFENCE
&& (number_percent() < get_skill(victim, gsn_forest_fighting))) {
chance = chance * 120 / 100;
check_improve (victim, gsn_forest_fighting, TRUE, 7);
}
if (number_percent() >= chance + LEVEL(victim) - LEVEL(ch))
return FALSE;
act("You parry $n's attack.", ch, NULL, victim, TO_VICT | ACT_VERBOSE);
act("$N parries your attack.", ch, NULL, victim, TO_CHAR | ACT_VERBOSE);
check_weapon_damage(ch, victim, loc);
if (number_percent() > chance) {
/* size and weight */
chance += ch->carry_weight / 25;
chance -= victim->carry_weight / 20;
if (ch->size < victim->size)
chance += (ch->size - victim->size) * 25;
else
chance += (ch->size - victim->size) * 10;
/* stats */
chance += get_curr_stat(ch, STAT_STR);
chance -= get_curr_stat(victim, STAT_DEX) * 4/3;
if (IS_AFFECTED(ch, AFF_FLYING))
chance -= 10;
/* speed */
if (IS_NPC(ch) && IS_SET(ch->pIndexData->off_flags, OFF_FAST))
chance += 10;
if (IS_NPC(victim) && IS_SET(victim->pIndexData->off_flags,
OFF_FAST))
chance -= 20;
/* level */
chance += (LEVEL(ch) - LEVEL(victim)) * 2;
/* now the attack */
if (number_percent() < (chance / 20 )) {
act("You couldn't manage to keep your position!",
ch, NULL, victim, TO_VICT);
act("You fall down!", ch, NULL, victim, TO_VICT);
act("$N couldn't manage to hold your attack "
"and falls down!",
ch, NULL, victim, TO_CHAR);
act("$n stunning force makes $N falling down.",
ch, NULL, victim, TO_NOTVICT);
WAIT_STATE(victim, SKILL(gsn_bash)->beats);
victim->position = POS_RESTING;
}
}
check_improve(victim, gsn_parry, TRUE, 6);
return TRUE;
}
/*
* check blink
*/
bool check_blink(CHAR_DATA *ch, CHAR_DATA *victim)
{
int chance;
if (!IS_SET(victim->plr_flags, PLR_BLINK))
return FALSE;
if (IS_NPC(victim))
return FALSE;
else
chance = get_skill(victim, gsn_blink) / 2;
if (number_percent() >= chance + LEVEL(victim) - LEVEL(ch)
|| number_percent() < 50
|| victim->mana < 10)
return FALSE;
victim->mana -= UMAX(victim->level / 10, 1);
act("You blink out $n's attack.",
ch, NULL, victim, TO_VICT | ACT_VERBOSE);
act("$N blinks out your attack.",
ch, NULL, victim, TO_CHAR | ACT_VERBOSE);
check_improve(victim, gsn_blink, TRUE, 6);
return TRUE;
}
/*
* Check for shield block.
*/
bool check_block(CHAR_DATA *ch, CHAR_DATA *victim, int loc)
{
int chance;
if (!IS_AWAKE(victim))
return FALSE;
if (get_eq_char(victim, WEAR_SHIELD) == NULL)
return FALSE;
if (IS_NPC(victim))
chance = 10;
else {
chance = get_skill(victim, gsn_shield_block) / 2;
if (chance <= 1)
return FALSE;
}
if (check_forest(victim) == FOREST_DEFENCE
&& (number_percent() < get_skill(victim, gsn_forest_fighting))) {
chance *= 1.2;
check_improve (victim, gsn_forest_fighting, TRUE, 7);
}
if (MOUNTED(victim))
chance *= 1.2;
if (number_percent() >= chance + LEVEL(victim) - LEVEL(ch))
return FALSE;
act("Your shield blocks $n's attack.",
ch, NULL, victim, TO_VICT | ACT_VERBOSE);
act("$N deflects your attack with $S shield.",
ch, NULL, victim, TO_CHAR | ACT_VERBOSE);
check_shield_damage(ch, victim, loc);
check_improve(victim, gsn_shield_block, TRUE, 6);
return TRUE;
}
/*
* Check for hand block
*/
bool check_hand_block(CHAR_DATA *ch, CHAR_DATA *victim)
{
int chance;
if (!IS_AWAKE(victim)
|| IS_NPC(victim)
|| get_eq_char(victim, WEAR_WIELD)
|| get_eq_char(victim, WEAR_SECOND_WIELD)
|| (chance=get_skill(victim, gsn_hand_block)<=0))
return FALSE;
chance = URANGE(5, chance*2/5 + LEVEL(victim) - LEVEL(ch), 55);
if (number_percent() < chance) {
act("Your hand blocks $n's attack.",
ch, NULL, victim, TO_VICT|ACT_VERBOSE);
act("$N blocks your attack with $S hand.",
ch, NULL, victim, TO_CHAR|ACT_VERBOSE);
check_improve(victim, gsn_hand_block, TRUE, 5);
return TRUE;
}
return FALSE;
}
/*
* Check for dodge.
*/
bool check_dodge(CHAR_DATA *ch, CHAR_DATA *victim)
{
int chance;
if (!IS_AWAKE(victim))
return FALSE;
if (MOUNTED(victim))
return FALSE;
if (IS_NPC(victim))
chance = UMIN(30, victim->level);
else {
chance = get_skill(victim, gsn_dodge) / 2;
/* chance for high dex. */
chance += 2 * (get_curr_stat(victim,STAT_DEX) - 20);
}
if (check_forest(victim) == FOREST_DEFENCE
&& (get_skill(victim, gsn_forest_fighting) > number_percent())) {
chance *= 1.2;
check_improve (victim, gsn_forest_fighting, TRUE, 7);
}
if (number_percent() >= chance + (victim->level - ch->level) / 2)
return FALSE;
act("You dodge $n's attack.", ch, NULL, victim, TO_VICT | ACT_VERBOSE);
act("$N dodges your attack.", ch, NULL, victim, TO_CHAR | ACT_VERBOSE);
if (number_percent() < get_skill(victim,gsn_dodge) / 20
&& !(IS_AFFECTED(ch, AFF_FLYING) || ch->position < POS_FIGHTING)) {
/* size */
if (victim->size < ch->size)
/* bigger = harder to trip */
chance += (victim->size - ch->size) * 10;
/* dex */
chance += get_curr_stat(victim, STAT_DEX);
chance -= get_curr_stat(ch, STAT_DEX) * 3 / 2;
if (IS_AFFECTED(victim, AFF_FLYING))
chance -= 10;
/* speed */
if ((IS_NPC(victim) && IS_SET(victim->pIndexData->off_flags,
OFF_FAST))
|| IS_AFFECTED(victim, AFF_HASTE))
chance += 10;
if ((IS_NPC(ch) && IS_SET(ch->pIndexData->off_flags, OFF_FAST))
|| IS_AFFECTED(ch, AFF_HASTE))
chance -= 20;
/* level */
chance += (victim->level - ch->level) * 2;
/* now the attack */
if (number_percent() < (chance / 20)) {
act("$n lost his postion and fall down!",
ch, NULL, victim, TO_VICT);
act("As $N moves you lost your position fall down!",
ch, NULL, victim, TO_CHAR);
act("As $N dodges $N's attack, $N lost his position "
"and falls down.", ch, NULL, victim, TO_NOTVICT);
WAIT_STATE(ch, SKILL(gsn_trip)->beats);
ch->position = POS_RESTING;
}
}
check_improve(victim, gsn_dodge, TRUE, 6);
return TRUE;
}
/*
* Set position of a victim.
*/
void update_pos(CHAR_DATA *victim)
{
if (victim->hit > 0) {
if (victim->position <= POS_STUNNED) {
if (IS_AFFECTED(victim, AFF_SLEEP)) {
REMOVE_BIT(victim->affected_by, AFF_SLEEP);
affect_bit_strip(victim, TO_AFFECTS, AFF_SLEEP);
}
victim->position = POS_STANDING;
}
return;
}
if (IS_NPC(victim) && victim->hit < 1) {
victim->position = POS_DEAD;
return;
}
if (victim->hit <= -11) {
victim->position = POS_DEAD;
return;
}
if (victim->hit <= -6)
victim->position = POS_MORTAL;
else if (victim->hit <= -3)
victim->position = POS_INCAP;
else
victim->position = POS_STUNNED;
}
/*
* Start fights.
*/
void set_fighting(CHAR_DATA *ch, CHAR_DATA *victim)
{
if (ch->fighting != NULL) {
bug("Set_fighting: already fighting", 0);
return;
}
if (IS_AFFECTED(ch, AFF_SLEEP)) {
REMOVE_BIT(ch->affected_by, AFF_SLEEP);
affect_bit_strip(ch, TO_AFFECTS, AFF_SLEEP);
}
ch->fighting = victim;
ch->position = POS_FIGHTING;
}
static void STOP_FIGHTING(CHAR_DATA *ch)
{
ch->fighting = NULL;
ch->position = IS_NPC(ch) ? ch->default_pos : POS_STANDING;
update_pos(ch);
}
/*
* Stop fights.
*/
void stop_fighting(CHAR_DATA *ch, bool fBoth)
{
CHAR_DATA *fch;
STOP_FIGHTING(ch);
if (!fBoth)
return;
for (fch = char_list; fch; fch = fch->next) {
if (fch->fighting == ch)
STOP_FIGHTING(fch);
}
}
/*
* Make a corpse out of a character.
*/
void make_corpse(CHAR_DATA *ch)
{
OBJ_DATA *corpse;
OBJ_DATA *obj;
OBJ_DATA *obj_next;
int i;
if (IS_NPC(ch)) {
corpse = create_obj_of(get_obj_index(OBJ_VNUM_CORPSE_NPC),
ch->short_descr);
corpse->timer = number_range(3, 6);
if (ch->gold > 0 || ch->silver > 0) {
OBJ_DATA *money = create_money(ch->gold, ch->silver);
if (IS_SET(ch->form,FORM_INSTANT_DECAY))
obj_to_room(money, ch->in_room);
else
obj_to_obj(money, corpse);
}
}
else {
corpse = create_obj_of(get_obj_index(OBJ_VNUM_CORPSE_PC),
ch->short_descr);
if (IS_GOOD(ch))
i = 0;
if (IS_EVIL(ch))
i = 2;
else
i = 1;
corpse->timer= number_range(25, 40);
corpse->altar = get_altar(ch);
if (ch->gold > 0 || ch->silver > 0)
obj_to_obj(create_money(ch->gold, ch->silver), corpse);
}
corpse->owner = mlstr_dup(ch->short_descr);
corpse->level = ch->level;
ch->gold = 0;
ch->silver = 0;
for (obj = ch->carrying; obj != NULL; obj = obj_next) {
obj_next = obj->next_content;
obj_from_char(obj);
if (obj->pIndexData->item_type == ITEM_POTION)
obj->timer = number_range(500,1000);
if (obj->pIndexData->item_type == ITEM_SCROLL)
obj->timer = number_range(1000,2500);
if (IS_SET(obj->extra_flags,ITEM_ROT_DEATH)) {
obj->timer = number_range(5,10);
if (obj->pIndexData->item_type == ITEM_POTION)
obj->timer += obj->level * 20;
}
REMOVE_BIT(obj->extra_flags,ITEM_VIS_DEATH);
REMOVE_BIT(obj->extra_flags,ITEM_ROT_DEATH);
if (IS_SET(obj->extra_flags, ITEM_INVENTORY) ||
(obj->pIndexData->limit != -1 &&
(obj->pIndexData->count > obj->pIndexData->limit)))
{
extract_obj(obj, 0);
continue;
}
else if (IS_SET(ch->form,FORM_INSTANT_DECAY))
obj_to_room(obj, ch->in_room);
else
obj_to_obj(obj, corpse);
}
obj_to_room(corpse, ch->in_room);
}
void death_cry(CHAR_DATA *ch)
{
death_cry_org(ch, -1);
}
/*
* Improved Death_cry contributed by Diavolo.
*/
void death_cry_org(CHAR_DATA *ch, int part)
{
ROOM_INDEX_DATA *was_in_room;
char *msg;
int door;
int vnum;
vnum = 0;
msg = "You hear $n's death cry.";
if (part == -1)
part = number_bits(4);
switch (part) {
case 0:
msg = "$n hits the ground ... DEAD.";
break;
case 1:
if (ch->material == 0) {
msg = "$n splatters blood on your armor.";
break;
}
/* FALLTHRU */
case 2:
if (IS_SET(ch->parts, PART_GUTS)) {
msg = "$n spills $s guts all over the floor.";
vnum = OBJ_VNUM_GUTS;
}
break;
case 3:
if (IS_SET(ch->parts, PART_HEAD)) {
msg = "$n's severed head plops on the ground.";
vnum = OBJ_VNUM_SEVERED_HEAD;
}
break;
case 4:
if (IS_SET(ch->parts, PART_HEART)) {
msg = "$n's heart is torn from $s chest.";
vnum = OBJ_VNUM_TORN_HEART;
}
break;
case 5:
if (IS_SET(ch->parts, PART_ARMS)) {
msg = "$n's arm is sliced from $s dead body.";
vnum = OBJ_VNUM_SLICED_ARM;
}
break;
case 6:
if (IS_SET(ch->parts, PART_LEGS)) {
msg = "$n's leg is sliced from $s dead body.";
vnum = OBJ_VNUM_SLICED_LEG;
}
break;
case 7:
if (IS_SET(ch->parts, PART_BRAINS)) {
msg = "$n's head is shattered, and $s brains splash all over you.";
vnum = OBJ_VNUM_BRAINS;
}
break;
}
act(msg, ch, NULL, NULL, TO_ROOM);
if (vnum) {
OBJ_DATA *obj;
obj = create_obj_of(get_obj_index(vnum), ch->short_descr);
obj->level = ch->level;
obj->owner = mlstr_dup(ch->short_descr);
obj->timer = number_range(4, 7);
if (obj->pIndexData->item_type == ITEM_FOOD) {
if (IS_SET(ch->form,FORM_POISON))
obj->value[3] = 1;
else if (!IS_SET(ch->form,FORM_EDIBLE))
SET_BIT(obj->extra_flags, ITEM_NOT_EDIBLE);
}
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.";
if ((was_in_room = ch->in_room)) {
for (door = 0; door <= 5; door++) {
EXIT_DATA *pexit;
if ((pexit = was_in_room->exit[door]) != NULL
&& pexit->to_room.r != NULL
&& pexit->to_room.r != was_in_room) {
ch->in_room = pexit->to_room.r;
act(msg, ch, NULL, NULL, TO_ROOM);
}
}
ch->in_room = was_in_room;
}
}
void raw_kill_org(CHAR_DATA *ch, CHAR_DATA *victim, int part)
{
CHAR_DATA *tmp_ch, *tmp_ch_next;
OBJ_DATA *obj,*obj_next;
int i;
OBJ_DATA *tattoo, *clanmark;
for (obj = victim->carrying;obj != NULL;obj = obj_next) {
obj_next = obj->next_content;
if (obj->wear_loc != WEAR_NONE
&& oprog_call(OPROG_DEATH, obj, victim, NULL)) {
victim->position = POS_STANDING;
return;
}
}
/* don't remember killed victims anymore */
if (IS_NPC(ch))
remove_mind(ch, victim->name);
stop_fighting(victim, TRUE);
rating_update(ch, victim);
quest_handle_death(ch, victim);
RESET_FIGHT_TIME(victim);
victim->last_death_time = current_time;
death_cry_org(victim, part);
tattoo = get_eq_char(victim, WEAR_TATTOO);
clanmark = get_eq_char(victim, WEAR_CLANMARK);
if (tattoo != NULL)
obj_from_char(tattoo);
if (clanmark != NULL)
obj_from_char(clanmark);
make_corpse(victim);
if (IS_NPC(victim)) {
victim->pIndexData->killed++;
extract_char(victim, 0);
return;
}
SET_BIT(victim->plr_flags, PLR_GHOST);
char_puts("You turn into an invincible ghost for a few minutes.\n"
"As long as you don't attack anything.\n",
victim);
extract_char(victim, XC_F_INCOMPLETE);
while (victim->affected)
affect_remove(victim, victim->affected);
victim->affected_by = 0;
for (i = 0; i < 4; i++)
victim->armor[i] = 100;
victim->position = POS_RESTING;
victim->hit = victim->max_hit / 10;
victim->mana = victim->max_mana / 10;
victim->move = victim->max_move;
update_pos(victim);
/* RT added to prevent infinite deaths */
REMOVE_BIT(victim->plr_flags, PLR_BOUGHT_PET);
victim->pcdata->condition[COND_THIRST] = 40;
victim->pcdata->condition[COND_HUNGER] = 40;
victim->pcdata->condition[COND_FULL] = 40;
victim->pcdata->condition[COND_BLOODLUST] = 40;
victim->pcdata->condition[COND_DESIRE] = 40;
if (tattoo != NULL) {
obj_to_char(tattoo, victim);
equip_char(victim, tattoo, WEAR_TATTOO);
}
if (clanmark != NULL) {
obj_to_char(clanmark, victim);
equip_char(victim, clanmark, WEAR_CLANMARK);
}
if (victim->level > 1)
save_char_obj(victim, FALSE);
/*
* Calm down the tracking mobiles
*/
for (tmp_ch = npc_list; tmp_ch; tmp_ch = tmp_ch_next) {
tmp_ch_next = tmp_ch->next;
if (tmp_ch->last_fought == victim)
tmp_ch->last_fought = NULL;
remove_mind(tmp_ch, victim->name);
if (tmp_ch->target == victim
&& tmp_ch->pIndexData->vnum == MOB_VNUM_STALKER) {
doprintf(do_clan, tmp_ch,
"%s is dead and I can leave the realm.",
PERS(victim, tmp_ch));
extract_char(tmp_ch, 0);
}
}
}
void group_gain(CHAR_DATA *ch, CHAR_DATA *victim)
{
CHAR_DATA *lch;
CHAR_DATA *gch;
int xp;
int members;
int group_levels;
if (!IS_NPC(victim) || victim == ch)
return;
if (IS_SET(victim->pIndexData->act, ACT_PET)
|| victim->pIndexData->vnum < 100
|| victim->master
|| victim->leader)
return;
lch = ch->leader ? ch->leader : ch;
members = 0;
group_levels = 0;
for (gch = ch->in_room->people; gch; gch = gch->next_in_room) {
if (is_same_group(gch, ch)) {
if (IS_NPC(gch)) {
if (IS_SET(gch->pIndexData->act, ACT_SUMMONED))
continue;
}
else {
if (abs(gch->level - lch->level) <= 8)
members++;
}
group_levels += gch->level;
}
}
for (gch = ch->in_room->people; gch; gch = gch->next_in_room) {
if (!is_same_group(gch, ch) || IS_NPC(gch))
continue;
if (gch->level - lch->level > 8) {
char_puts("You are too high for this group.\n", gch);
continue;
}
if (gch->level - lch->level < -8) {
char_puts("You are too low for this group.\n", gch);
continue;
}
xp = xp_compute(gch, victim, group_levels, members);
if (gch->level < LEVEL_HERO) {
char_printf(gch, "You receive %d experience points.\n", xp);
gain_exp(gch, xp);
}
}
}
/*
* Compute xp for a kill.
* Also adjust alignment of killer.
* Edit this function to change xp computations.
*/
int xp_compute(CHAR_DATA *gch, CHAR_DATA *victim, int total_levels, int members)
{
int xp;
int base_exp;
int level_range = victim->level - gch->level;
int neg_cha = 0, pos_cha = 0;
/* base exp */
switch (level_range) {
case -9: base_exp = 1; break;
case -8: base_exp = 2; break;
case -7: base_exp = 5; break;
case -6: base_exp = 9; break;
case -5: base_exp = 11; break;
case -4: base_exp = 22; break;
case -3: base_exp = 33; break;
case -2: base_exp = 43; break;
case -1: base_exp = 60; break;
case 0: base_exp = 74; break;
case 1: base_exp = 84; break;
case 2: base_exp = 99; break;
case 3: base_exp = 121; break;
case 4: base_exp = 143; break;
default:
if (level_range > 4)
base_exp = 140 + 20 * (level_range - 4);
else
base_exp = 0;
}
/* calculate exp multiplier */
#if 0
if (IS_NPC(victim) && IS_SET(victim->pIndexData->act, ACT_NOALIGN))
xp = base_exp;
else
#endif
if ((IS_EVIL(gch) && IS_GOOD(victim))
|| (IS_EVIL(victim) && IS_GOOD(gch)))
xp = base_exp * 8/5;
else if (IS_GOOD(gch) && IS_GOOD(victim))
xp = 0;
else if (!IS_NEUTRAL(gch) && IS_NEUTRAL(victim))
xp = base_exp * 1.1;
else if (IS_NEUTRAL(gch) && !IS_NEUTRAL(victim))
xp = base_exp * 1.3;
else
xp = base_exp;
/* more exp at the low levels */
if (gch->level < 6)
xp = 15 * xp / (gch->level + 4);
/* randomize the rewards */
xp = number_range(xp * 3/4, xp * 5/4);
/* adjust for grouping */
xp = xp * gch->level/total_levels;
if (members == 2 || members == 3) {
xp *= members;
if (members == 2)
xp = xp * 120 / 100;
else
xp = xp * 125 / 100;
}
#if 0
xp += (xp * (gch->max_hit - gch->hit)) / (gch->max_hit * 5);
#endif
if (IS_GOOD(gch)) {
if (IS_GOOD(victim)) {
gch->pcdata->anti_killed++;
neg_cha = 1;
}
else if (IS_NEUTRAL(victim)) {
gch->pcdata->has_killed++;
pos_cha = 1;
}
else if (IS_EVIL(victim)) {
gch->pcdata->has_killed++;
pos_cha = 1;
}
}
else if (IS_NEUTRAL(gch)) {
if (IS_GOOD(victim)) {
gch->pcdata->has_killed++;
pos_cha = 1;
}
else if (IS_NEUTRAL(victim)) {
gch->pcdata->anti_killed++;
neg_cha = 1;
}
else if (IS_EVIL(victim)) {
gch->pcdata->has_killed++;
pos_cha =1;
}
}
else if (IS_EVIL(gch)) {
if (IS_GOOD(victim)) {
gch->pcdata->has_killed++;
pos_cha = 1;
}
else if (IS_NEUTRAL(victim)) {
gch->pcdata->has_killed++;
pos_cha = 1;
}
else if (IS_EVIL(victim)) {
gch->pcdata->anti_killed++;
neg_cha = 1;
}
}
if (neg_cha) {
if ((gch->pcdata->anti_killed % 100) == 99) {
char_printf(gch, "You have killed %d %s up to now.\n",
gch->pcdata->anti_killed,
IS_GOOD(gch) ? "goods" :
IS_EVIL(gch) ? "evils" :
"neutrals");
if (gch->perm_stat[STAT_CHA] > 3 && IS_GOOD(gch)) {
char_puts("So your charisma "
"has reduced by one.\n", gch);
gch->perm_stat[STAT_CHA] -= 1;
}
}
}
else if (pos_cha) {
if ((gch->pcdata->has_killed % 200) == 199) {
char_printf(gch, "You have killed %d %s up to now.\n",
gch->pcdata->anti_killed,
IS_GOOD(gch) ? "anti-goods" :
IS_EVIL(gch) ? "anti-evils" :
"anti-neutrals");
if (gch->perm_stat[STAT_CHA] <
get_max_train(gch, STAT_CHA)
&& IS_GOOD(gch)) {
char_puts("So your charisma "
"has increased by one.\n", gch);
gch->perm_stat[STAT_CHA] += 1;
}
}
}
return xp;
}
void dam_message(CHAR_DATA *ch, CHAR_DATA *victim,
int dam, int dt, bool immune, int dam_type)
{
const char *vs;
const char *vp;
const char *msg_char;
const char *msg_vict = NULL;
const char *msg_notvict;
const char *attack = str_empty;
if (dam == 0) {
vs = "miss";
vp = "misses";
}
else if (dam <= 4) {
vs = "{cscratch{x";
vp = "{cscratches{x";
}
else if (dam <= 8) {
vs = "{cgraze{x";
vp = "{cgrazes{x";
}
else if (dam <= 12) {
vs = "{chit{x";
vp = "{chits{x";
}
else if (dam <= 16) {
vs = "{cinjure{x";
vp = "{cinjures{x";
}
else if (dam <= 20) {
vs = "{cwound{x";
vp = "{cwounds{x";
}
else if (dam <= 24) {
vs = "{cmaul{x";
vp = "{cmauls{x";
}
else if (dam <= 28) {
vs = "{cdecimate{x";
vp = "{cdecimates{x";
}
else if (dam <= 32) {
vs = "{cdevastate{x";
vp = "{cdevastates{x";
}
else if (dam <= 36) {
vs = "{cmaim{x";
vp = "{cmaims{x";
}
else if (dam <= 42) {
vs = "{MMUTILATE{x";
vp = "{MMUTILATES{x";
}
else if (dam <= 52) {
vs = "{MDISEMBOWEL{x";
vp = "{MDISEMBOWELS{x";
}
else if (dam <= 65) {
vs = "{MDISMEMBER{x";
vp = "{MDISMEMBERS{x";
}
else if (dam <= 80) {
vs = "{MMASSACRE{x";
vp = "{MMASSACRES{x";
}
else if (dam <= 100) {
vs = "{MMANGLE{x";
vp = "{MMANGLES{x";
}
else if (dam <= 130) {
vs = "{y*** DEMOLISH ***{x";
vp = "{y*** DEMOLISHES ***{x";
}
else if (dam <= 175) {
vs = "{y*** DEVASTATE ***{x";
vp = "{y*** DEVASTATES ***{x";
}
else if (dam <= 250) {
vs = "{y=== OBLITERATE ==={x";
vp = "{y=== OBLITERATES ==={x";
}
else if (dam <= 325) {
vs = "{y=== ATOMIZE ==={x";
vp = "{y=== ATOMIZES ==={x";
}
else if (dam <= 400) {
vs = "{R>>> ANNIHILATE <<<{x";
vp = "{R>>> ANNIHILATES <<<{x";
}
else if (dam <= 500) {
vs = "{R>>> ERADICATE <<<{x";
vp = "{R>>> ERADICATES <<<{x";
}
else if (dam <= 650) {
vs = "{R-==> ELECTRONIZE <==-{x";
vp = "{R-==> ELECTRONIZES <==-{x";
}
else if (dam <= 800) {
vs = "{R-==> SKELETONIZE <==-{x";
vp = "{R-==> SKELETONIZES <==-{x";
}
else if (dam <= 1000) {
vs = "{R### NUKE ###{x";
vp = "{R### NUKES ###{x";
}
else if (dam <= 1250) {
vs = "{R### TERMINATE ###{x";
vp = "{R### TERMINATES ###{x";
}
else if (dam <= 1500) {
vs = "{R[*] TEAR UP [*]{x";
vp = "{R[*] TEARS UP [*]{x";
}
else {
vs = "{*{R[*] POWER HIT [*]{x";
vp = "{*{R[*] POWER HITS [*]{x";
}
if (dt == TYPE_HIT || dt == TYPE_HUNGER) {
if (ch == victim) {
switch (dam_type) {
case DAM_HUNGER:
msg_notvict = "$n's hunger $u $mself!";
msg_char = "Your hunger $u yourself!";
break;
case DAM_THIRST:
msg_notvict = "$n's thirst $u $mself!";
msg_char = "Your thirst $u yourself!";
break;
case DAM_LIGHT_V:
msg_notvict = "The light of room $u $n!";
msg_char = "The light of room $u you!";
break;
case DAM_TRAP_ROOM:
msg_notvict = "The trap at room $u $n!";
msg_char = "The trap at room $u you!";
break;
default:
msg_notvict = "$n $u $mself!";
msg_char = "You $u yourself!";
break;
}
}
else {
msg_notvict = "$n $u $N.";
msg_char = "You $u $N.";
msg_vict = "$n $u you.";
}
}
else {
skill_t *sk;
/* XXX */
#define MAX_DAMAGE_MESSAGE 40
if ((sk = skill_lookup(dt)))
attack = sk->noun_damage;
else if (dt >= TYPE_HIT && dt <= TYPE_HIT + MAX_DAMAGE_MESSAGE)
attack = attack_table[dt - TYPE_HIT].noun;
else {
bug("Dam_message: bad dt %d.", dt);
dt = TYPE_HIT;
attack = attack_table[0].name;
}
if (immune) {
if (ch == victim) {
msg_notvict = "$n is unaffected by $s own $U.";
msg_char = "Luckily, you are immune to that.";
}
else {
msg_notvict = "$N is unaffected by $n's $U!";
msg_char = "$N is unaffected by your $U!";
msg_vict = "$n's $U is powerless against you.";
}
}
else {
vs = vp;
if (ch == victim) {
msg_notvict = "$n's $U $u $m.";
msg_char = "Your $U $u you.";
}
else {
msg_notvict = "$n's $U $u $N.";
msg_char = "Your $U $u $N.";
msg_vict = "$n's $U $u you.";
}
}
}
if (ch == victim) {
act_puts3(msg_notvict, ch, vp, NULL, attack,
TO_ROOM | ACT_TRANS, POS_RESTING);
act_puts3(msg_char, ch, vs, NULL, attack,
TO_CHAR | ACT_TRANS, POS_RESTING);
}
else {
act_puts3(msg_notvict, ch, vp, victim, attack,
TO_NOTVICT | ACT_TRANS, POS_RESTING);
act_puts3(msg_char, ch, vs, victim, attack,
TO_CHAR | ACT_TRANS, POS_RESTING);
act_puts3(msg_vict, ch, vp, victim, attack,
TO_VICT | ACT_TRANS, POS_RESTING);
}
}
void do_kill(CHAR_DATA *ch, const char *argument)
{
char arg[MAX_INPUT_LENGTH];
CHAR_DATA *victim;
int chance;
one_argument(argument, arg, sizeof(arg));
if (arg[0] == '\0') {
char_puts("Kill whom?\n", ch);
return;
}
WAIT_STATE(ch, 1 * PULSE_VIOLENCE);
if ((victim = get_char_room(ch, arg)) == NULL) {
char_puts("They aren't here.\n", ch);
return;
}
if (ch->position == POS_FIGHTING) {
if (victim == ch->fighting)
char_puts("You do the best you can!\n", ch);
else if (victim->fighting != ch)
char_puts("One battle at a time, please.\n",ch);
else {
act("You start aiming at $N.",ch,NULL,victim,TO_CHAR);
ch->fighting = victim;
}
return;
}
if (!IS_NPC(victim)) {
char_puts("You must MURDER a player.\n", ch);
return;
}
if (victim == ch) {
char_puts("You hit yourself. Ouch!\n", ch);
multi_hit(ch, ch, TYPE_UNDEFINED);
return;
}
if (IS_AFFECTED(ch, AFF_CHARM) && ch->master == victim) {
act("$N is your beloved master.", ch, NULL, victim, TO_CHAR);
return;
}
if (is_safe(ch, victim))
return;
if ((chance = get_skill(ch, gsn_mortal_strike))
&& get_eq_char(ch, WEAR_WIELD)
&& ch->level > (victim->level - 5)) {
chance /= 30;
chance += 1 + (ch->level - victim->level) / 2;
if (number_percent() < chance) {
act_puts("Your flash strike instantly slays $N!",
ch, NULL, victim, TO_CHAR, POS_RESTING);
act_puts("$n flash strike instantly slays $N!",
ch, NULL, victim, TO_NOTVICT,
POS_RESTING);
act_puts("$n flash strike instantly slays you!",
ch, NULL, victim, TO_VICT, POS_DEAD);
damage(ch, victim, (victim->hit + 1),
gsn_mortal_strike, DAM_NONE, DAMF_SHOW);
check_improve(ch, gsn_mortal_strike, TRUE, 1);
return;
} else
check_improve(ch, gsn_mortal_strike, FALSE, 3);
}
multi_hit(ch, victim, TYPE_UNDEFINED);
}
void do_murde(CHAR_DATA *ch, const char *argument)
{
char_puts("If you want to MURDER, spell it out.\n", ch);
return;
}
void do_murder(CHAR_DATA *ch, const char *argument)
{
char arg[MAX_INPUT_LENGTH];
CHAR_DATA *victim;
int chance;
one_argument(argument, arg, sizeof(arg));
if (arg[0] == '\0') {
char_puts("Murder whom?\n", ch);
return;
}
if (IS_AFFECTED(ch, AFF_CHARM)
|| (IS_NPC(ch) && IS_SET(ch->pIndexData->act, ACT_PET)))
return;
if ((victim = get_char_room(ch, arg)) == NULL) {
WAIT_STATE(ch, MISSING_TARGET_DELAY);
char_puts("They aren't here.\n", ch);
return;
}
if (victim == ch) {
char_puts("Suicide is a mortal sin.\n", ch);
return;
}
if (IS_AFFECTED(ch, AFF_CHARM) && ch->master == victim) {
act("$N is your beloved master.", ch, NULL, victim, TO_CHAR);
return;
}
if (ch->position == POS_FIGHTING) {
char_puts("You do the best you can!\n", ch);
return;
}
if (is_safe(ch, victim))
return;
WAIT_STATE(ch, 1 * PULSE_VIOLENCE);
if ((chance = get_skill(ch, gsn_mortal_strike))
&& get_eq_char(ch, WEAR_WIELD)
&& ch->level > (victim->level - 5)) {
chance /= 30;
chance += 1 + (ch->level - victim->level) / 2;
if (number_percent() < chance) {
act_puts("Your flash strike instantly slays $N!",
ch, NULL, victim, TO_CHAR, POS_RESTING);
act_puts("$n flash strike instantly slays $N!",
ch, NULL, victim, TO_NOTVICT,
POS_RESTING);
act_puts("$n flash strike instantly slays you!",
ch, NULL, victim, TO_VICT, POS_DEAD);
damage(ch, victim, (victim->hit + 1),
gsn_mortal_strike, DAM_NONE, DAMF_SHOW);
check_improve(ch, gsn_mortal_strike, TRUE, 1);
return;
} else
check_improve(ch, gsn_mortal_strike, FALSE, 3);
}
multi_hit(ch, victim, TYPE_UNDEFINED);
}
void do_flee(CHAR_DATA *ch, const char *argument)
{
ROOM_INDEX_DATA *was_in;
ROOM_INDEX_DATA *now_in;
CHAR_DATA *victim;
int attempt;
class_t *cl;
if (RIDDEN(ch)) {
char_puts("You should ask to your rider!\n", ch);
return;
}
if (MOUNTED(ch))
do_dismount(ch, str_empty);
if ((victim = ch->fighting) == NULL) {
if (ch->position == POS_FIGHTING)
ch->position = POS_STANDING;
char_puts("You aren't fighting anyone.\n", ch);
return;
}
if ((cl = class_lookup(ch->class))
&& !CAN_FLEE(ch, cl)) {
char_puts("Your honour doesn't let you flee, "
"try dishonoring yourself.\n", ch);
return;
}
was_in = ch->in_room;
for (attempt = 0; attempt < 6; attempt++) {
EXIT_DATA *pexit;
int door;
door = number_door();
if ((pexit = was_in->exit[door]) == 0
|| pexit->to_room.r == NULL
|| (IS_SET(pexit->exit_info, EX_CLOSED)
&& (!IS_AFFECTED(ch, AFF_PASS_DOOR)
|| IS_SET(pexit->exit_info,EX_NOPASS))
&& !IS_TRUSTED(ch,ANGEL))
|| (IS_SET(pexit->exit_info , EX_NOFLEE))
|| (IS_NPC(ch)
&& IS_SET(pexit->to_room.r->room_flags, ROOM_NOMOB)))
continue;
move_char(ch, door, FALSE);
if ((now_in = ch->in_room) == was_in)
continue;
ch->in_room = was_in;
act("$n has fled!", ch, NULL, NULL, TO_ROOM);
ch->in_room = now_in;
if (!IS_NPC(ch)) {
act_puts("You fled from combat!",
ch, NULL, NULL, TO_CHAR, POS_DEAD);
if (ch->level < LEVEL_HERO) {
char_printf(ch, "You lose %d exps.\n", 10);
gain_exp(ch, -10);
}
} else
ch->last_fought = NULL;
stop_fighting(ch, TRUE);
return;
}
char_puts("PANIC! You couldn't escape!\n", ch);
return;
}
void do_sla(CHAR_DATA *ch, const char *argument)
{
char_puts("If you want to SLAY, spell it out.\n", ch);
return;
}
void do_slay(CHAR_DATA *ch, const char *argument)
{
CHAR_DATA *victim;
char arg[MAX_INPUT_LENGTH];
one_argument(argument, arg, sizeof(arg));
if (arg[0] == '\0') {
char_puts("Slay whom?\n", ch);
return;
}
if ((victim = get_char_room(ch, arg)) == NULL) {
char_puts("They aren't here.\n", ch);
return;
}
if (ch == victim) {
char_puts("Suicide is a mortal sin.\n", ch);
return;
}
if (IS_IMMORTAL(victim)) {
char_puts("You failed.\n", ch);
return;
}
act("You slay $M in cold blood!", ch, NULL, victim, TO_CHAR);
act("$n slays you in cold blood!", ch, NULL, victim, TO_VICT);
act("$n slays $N in cold blood!", ch, NULL, victim, TO_NOTVICT);
raw_kill(ch, victim);
}
/*
* Check for obj dodge.
*/
bool check_obj_dodge(CHAR_DATA *ch, CHAR_DATA *victim, OBJ_DATA *obj, int bonus)
{
int chance;
if (!IS_AWAKE(victim) || MOUNTED(victim))
return FALSE;
if (!IS_NPC(victim) && HAS_SKILL(victim, gsn_spellbane)) {
if (victim->pcdata->clan_status) {
act("You catch $p that had been shot to you.",
ch, obj, victim, TO_VICT);
act("$N catches $p that had been shot to $M.",
ch, obj, victim, TO_CHAR);
act("$n catches $p that had been shot to $m.",
victim, obj, ch, TO_NOTVICT);
obj_to_char(obj, victim);
}
return TRUE;
}
if (IS_NPC(victim))
chance = UMIN(30, victim->level);
else {
chance = get_skill(victim, gsn_dodge) / 2;
/* chance for high dex. */
chance += 2 * (get_curr_stat(victim, STAT_DEX) - 20);
}
chance -= (bonus - 90);
chance /= 2;
if (number_percent() >= chance)
return FALSE;
act("You dodge $p that had been shot to you.",
ch, obj, victim, TO_VICT);
act("$N dodges $p that had been shot to $M.",
ch, obj, victim, TO_CHAR);
act("$n dodges $p that had been shot to $m.",
victim, obj, ch, TO_NOTVICT);
obj_to_room(obj, victim->in_room);
check_improve(victim, gsn_dodge, TRUE, 6);
return TRUE;
}
void do_dishonor(CHAR_DATA *ch, const char *argument)
{
ROOM_INDEX_DATA *was_in;
ROOM_INDEX_DATA *now_in;
CHAR_DATA *gch;
int attempt, level = 0;
int sn_dishonor;
int chance;
if (RIDDEN(ch)) {
char_puts("You should ask to your rider!\n", ch);
return;
}
if ((sn_dishonor = sn_lookup("dishonor")) < 0
|| !HAS_SKILL(ch, sn_dishonor)) {
char_puts("Which honor?\n", ch);
return;
}
if (ch->fighting == NULL) {
if (ch->position == POS_FIGHTING)
ch->position = POS_STANDING;
char_puts("You aren't fighting anyone.\n", ch);
return;
}
for (gch = char_list; gch; gch = gch->next)
if (is_same_group(gch, ch->fighting) || gch->fighting == ch)
level += gch->level;
if ((ch->fighting->level - ch->level) < 5 && ch->level > (level / 3)) {
char_puts("Your fighting doesn't worth "
"to dishonor yourself.\n", ch);
return;
}
was_in = ch->in_room;
chance = get_skill(ch, sn_dishonor);
for (attempt = 0; attempt < 6; attempt++) {
EXIT_DATA *pexit;
int door;
if (number_percent() >= chance)
continue;
door = number_door();
if ((pexit = was_in->exit[door]) == 0
|| pexit->to_room.r == NULL
|| (IS_SET(pexit->exit_info, EX_CLOSED) &&
(!IS_AFFECTED(ch, AFF_PASS_DOOR) ||
IS_SET(pexit->exit_info,EX_NOPASS)) &&
!IS_TRUSTED(ch,ANGEL))
|| IS_SET(pexit->exit_info, EX_NOFLEE)
|| (IS_NPC(ch) &&
IS_SET(pexit->to_room.r->room_flags, ROOM_NOMOB)))
continue;
move_char(ch, door, FALSE);
if ((now_in = ch->in_room) == was_in)
continue;
ch->in_room = was_in;
act("$n has dishonored $mself!",
ch, NULL, NULL, TO_ROOM);
ch->in_room = now_in;
if (!IS_NPC(ch)) {
char_puts("You dishonored yourself "
"and flee from combat.\n",ch);
if (ch->level < LEVEL_HERO) {
char_printf(ch, "You lose %d exps.\n",
ch->level);
gain_exp(ch, -(ch->level));
}
}
else
ch->last_fought = NULL;
stop_fighting(ch, TRUE);
if (MOUNTED(ch))
do_dismount(ch,str_empty);
check_improve(ch, sn_dishonor, TRUE, 1);
return;
}
char_puts("PANIC! You couldn't escape!\n", ch);
check_improve(ch, sn_dishonor, FALSE, 1);
}
void do_surrender(CHAR_DATA *ch, const char *argument)
{
CHAR_DATA *mob;
if (!IS_NPC(ch)) {
char_puts("Huh?\n", ch);
return;
}
if ((mob = ch->fighting) == NULL) {
char_puts("But you're not fighting!\n", ch);
return;
}
act("You surrender to $N!", ch, NULL, mob, TO_CHAR);
act("$n surrenders to you!", ch, NULL, mob, TO_VICT);
act("$n tries to surrender to $N!", ch, NULL, mob, TO_NOTVICT);
stop_fighting(ch, TRUE);
if (!IS_NPC(ch) && IS_NPC(mob)
&& (!HAS_TRIGGER(mob, TRIG_SURR) ||
!mp_percent_trigger(mob, ch, NULL, NULL, TRIG_SURR))) {
act("$N seems to ignore your cowardly act!",
ch, NULL, mob, TO_CHAR);
multi_hit(mob, ch, TYPE_UNDEFINED);
}
}