/* ************************************************************************
* File: fight.c , Combat module. Part of DIKUMUD *
* Usage: Combat system and messages. *
* Copyright (C) 1990, 1991 - see 'license.doc' for complete information. *
************************************************************************* */
#include "os.h"
#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "handler.h"
#include "interpreter.h"
#include "db.h"
#include "limits.h"
#include "spells.h"
#include "prototypes.h"
/* Structures */
struct char_data *combat_list = 0; /* head of l-list of fighting chars */
struct char_data *combat_next_dude = 0; /* Next dude global trick */
/* External structures */
extern struct room_data *world;
extern struct message_list fight_messages[MAX_MESSAGES];
extern struct obj_data *object_list;
/* External procedures */
char *fread_string (FILE * f1);
void stop_follower (struct char_data *ch);
void do_flee (struct char_data *ch, char *argument, int cmd);
void hit (struct char_data *ch, struct char_data *victim, int type);
/* Weapon attack texts */
struct attack_hit_type attack_hit_text[] = {
{"hit", "hits"}, /* TYPE_HIT */
{"pound", "pounds"}, /* TYPE_BLUDGEON */
{"pierce", "pierces"}, /* TYPE_PIERCE */
{"slash", "slashes"}, /* TYPE_SLASH */
{"whip", "whips"}, /* TYPE_WHIP */
{"claw", "claws"}, /* TYPE_CLAW */
{"bite", "bites"}, /* TYPE_BITE */
{"sting", "stings"}, /* TYPE_STING */
{"crush", "crushes"} /* TYPE_CRUSH */
};
/* The Fight related routines */
void appear (struct char_data *ch)
{
act ("$n slowly fade into existence.", FALSE, ch, 0, 0, TO_ROOM);
if (affected_by_spell (ch, SPELL_INVISIBLE))
affect_from_char (ch, SPELL_INVISIBLE);
REMOVE_BIT (ch->specials.affected_by, AFF_INVISIBLE);
}
void load_messages (void)
{
FILE *f1;
int i, type;
struct message_type *messages;
char chk[100];
if (!(f1 = fopen (MESS_FILE, "rb"))) {
perror ("read messages");
WIN32CLEANUP
exit (0);
}
for (i = 0; i < MAX_MESSAGES; i++) {
fight_messages[i].a_type = 0;
fight_messages[i].number_of_attacks = 0;
fight_messages[i].msg = 0;
}
fscanf (f1, " %s \n", chk);
while (*chk == 'M') {
fscanf (f1, " %d\n", &type);
for (i = 0; (i < MAX_MESSAGES) && (fight_messages[i].a_type != type) &&
(fight_messages[i].a_type); i++);
if (i >= MAX_MESSAGES) {
log ("Too many combat messages.");
WIN32CLEANUP
exit (0);
}
CREATE (messages, struct message_type, 1);
fight_messages[i].number_of_attacks++;
fight_messages[i].a_type = type;
messages->next = fight_messages[i].msg;
fight_messages[i].msg = messages;
messages->die_msg.attacker_msg = fread_string (f1);
messages->die_msg.victim_msg = fread_string (f1);
messages->die_msg.room_msg = fread_string (f1);
messages->miss_msg.attacker_msg = fread_string (f1);
messages->miss_msg.victim_msg = fread_string (f1);
messages->miss_msg.room_msg = fread_string (f1);
messages->hit_msg.attacker_msg = fread_string (f1);
messages->hit_msg.victim_msg = fread_string (f1);
messages->hit_msg.room_msg = fread_string (f1);
messages->god_msg.attacker_msg = fread_string (f1);
messages->god_msg.victim_msg = fread_string (f1);
messages->god_msg.room_msg = fread_string (f1);
fscanf (f1, " %s \n", chk);
}
fclose (f1);
}
void update_pos (struct char_data *victim)
{
if ((GET_HIT (victim) > 0) && (GET_POS (victim) > POSITION_STUNNED))
return;
else if (GET_HIT (victim) > 0)
GET_POS (victim) = POSITION_STANDING;
else if (GET_HIT (victim) <= -11)
GET_POS (victim) = POSITION_DEAD;
else if (GET_HIT (victim) <= -6)
GET_POS (victim) = POSITION_MORTALLYW;
else if (GET_HIT (victim) <= -3)
GET_POS (victim) = POSITION_INCAP;
else
GET_POS (victim) = POSITION_STUNNED;
}
/* start one char fighting another (yes, it is horrible, I know... ) */
void set_fighting (struct char_data *ch, struct char_data *vict)
{
assert (!ch->specials.fighting);
ch->next_fighting = combat_list;
combat_list = ch;
if (IS_AFFECTED (ch, AFF_SLEEP))
affect_from_char (ch, SPELL_SLEEP);
ch->specials.fighting = vict;
GET_POS (ch) = POSITION_FIGHTING;
}
/* remove a char from the list of fighting chars */
void stop_fighting (struct char_data *ch)
{
struct char_data *tmp;
assert (ch->specials.fighting);
if (ch == combat_next_dude)
combat_next_dude = ch->next_fighting;
if (combat_list == ch)
combat_list = ch->next_fighting;
else {
for (tmp = combat_list; tmp && (tmp->next_fighting != ch);
tmp = tmp->next_fighting);
if (!tmp) {
log ("Char fighting not found Error (fight.c, stop_fighting)");
abort ();
}
tmp->next_fighting = ch->next_fighting;
}
ch->next_fighting = 0;
ch->specials.fighting = 0;
GET_POS (ch) = POSITION_STANDING;
update_pos (ch);
}
#define MAX_NPC_CORPSE_TIME 5
#define MAX_PC_CORPSE_TIME 10
void make_corpse (struct char_data *ch)
{
struct obj_data *corpse, *o;
struct obj_data *money;
char buf[MAX_STRING_LENGTH];
int i;
char *str_dup (char *source);
struct obj_data *create_money (int amount);
CREATE (corpse, struct obj_data, 1);
clear_object (corpse);
corpse->item_number = NOWHERE;
corpse->in_room = NOWHERE;
corpse->name = str_dup ("corpse");
sprintf (buf, "Corpse of %s is lying here.",
(IS_NPC (ch) ? ch->player.short_descr : GET_NAME (ch)));
corpse->description = str_dup (buf);
sprintf (buf, "Corpse of %s",
(IS_NPC (ch) ? ch->player.short_descr : GET_NAME (ch)));
corpse->short_description = str_dup (buf);
corpse->contains = ch->carrying;
if ((GET_GOLD (ch) > 0) && (IS_NPC (ch) || (ch->desc))) {
money = create_money (GET_GOLD (ch));
GET_GOLD (ch) = 0;
obj_to_obj (money, corpse);
}
corpse->obj_flags.type_flag = ITEM_CONTAINER;
corpse->obj_flags.wear_flags = ITEM_TAKE;
corpse->obj_flags.value[0] = 0; /* You can't store stuff in a corpse */
corpse->obj_flags.value[3] = 1; /* corpse identifyer */
corpse->obj_flags.weight = GET_WEIGHT (ch) + IS_CARRYING_W (ch);
corpse->obj_flags.cost_per_day = 100000;
if (IS_NPC (ch))
corpse->obj_flags.timer = MAX_NPC_CORPSE_TIME;
else
corpse->obj_flags.timer = MAX_PC_CORPSE_TIME;
for (i = 0; i < MAX_WEAR; i++)
if (ch->equipment[i])
obj_to_obj (unequip_char (ch, i), corpse);
ch->carrying = 0;
IS_CARRYING_N (ch) = 0;
IS_CARRYING_W (ch) = 0;
corpse->next = object_list;
object_list = corpse;
for (o = corpse->contains; o; o->in_obj = corpse, o = o->next_content);
object_list_new_owner (corpse, 0);
obj_to_room (corpse, ch->in_room);
}
/* When ch kills victim */
void change_alignment (struct char_data *ch, struct char_data *victim)
{
int align;
if ((align = GET_ALIGNMENT (ch) - GET_ALIGNMENT (victim)) > 0) {
if (align > 650)
GET_ALIGNMENT (ch) =
MIN (1000, GET_ALIGNMENT (ch) + ((align - 650) / 4));
else
GET_ALIGNMENT (ch) /= 2;
} else {
if (align < -650)
GET_ALIGNMENT (ch) =
MAX (-1000, GET_ALIGNMENT (ch) + ((align + 650) / 4));
else
GET_ALIGNMENT (ch) /= 2;
}
}
void death_cry (struct char_data *ch)
{
struct char_data *victim;
int door, was_in;
act ("Your blood freezes as you hear $ns death cry.", FALSE, ch, 0, 0,
TO_ROOM);
was_in = ch->in_room;
for (door = 0; door <= 5; door++) {
if (CAN_GO (ch, door)) {
ch->in_room = world[was_in].dir_option[door]->to_room;
act ("Your blood freezes as you hear someones death cry.", FALSE, ch, 0,
0, TO_ROOM);
ch->in_room = was_in;
}
}
}
void raw_kill (struct char_data *ch)
{
if (ch->specials.fighting)
stop_fighting (ch);
death_cry (ch);
make_corpse (ch);
affect_total (ch);
extract_char (ch);
}
void die (struct char_data *ch)
{
gain_exp (ch, -(GET_EXP (ch) / 2));
raw_kill (ch);
}
void group_gain (struct char_data *ch, struct char_data *victim)
{
char buf[256];
int no_members, share;
struct char_data *k;
struct follow_type *f;
if (!(k = ch->master))
k = ch;
if (IS_AFFECTED (k, AFF_GROUP) && (k->in_room == ch->in_room))
no_members = 1;
else
no_members = 0;
for (f = k->followers; f; f = f->next)
if (IS_AFFECTED (f->follower, AFF_GROUP) &&
(f->follower->in_room == ch->in_room))
no_members++;
if (no_members >= 1)
share = MIN (450000 / no_members, (GET_EXP (victim) / 3) / no_members);
else
share = 0;
if (IS_AFFECTED (k, AFF_GROUP) && (k->in_room == ch->in_room)) {
act ("You receive your share of experience.", FALSE, k, 0, 0, TO_CHAR);
gain_exp (k, share);
change_alignment (k, victim);
}
for (f = k->followers; f; f = f->next) {
if (IS_AFFECTED (f->follower, AFF_GROUP) &&
(f->follower->in_room == ch->in_room)) {
act ("You receive your share of experience.", FALSE, f->follower, 0, 0,
TO_CHAR);
gain_exp (f->follower, share);
change_alignment (f->follower, victim);
}
}
}
char *replace_string (char *str, char *weapon)
{
static char buf[256];
char *cp;
cp = buf;
for (; *str; str++) {
if (*str == '#') {
switch (*(++str)) {
case 'W':
for (; *weapon; *(cp++) = *(weapon++));
break;
default:
*(cp++) = '#';
break;
}
} else {
*(cp++) = *str;
}
*cp = 0;
} /* For */
return (buf);
}
void dam_message (int dam, struct char_data *ch, struct char_data *victim,
int w_type)
{
struct obj_data *wield;
char *buf;
static struct dam_weapon_type {
char *to_room;
char *to_char;
char *to_victim;
} dam_weapons[] = {
{
"$n misses $N with $s #W.", /* 0 */
"You miss $N with your #W.", "$n miss you with $s #W."}, {
"$n tickles $N with $s #W.", /* 1.. 2 */
"You tickle $N as you #W $M.", "$n tickle you as $e #W you."}, {
"$n barely #W $N.", /* 3.. 4 */
"You barely #W $N.", "$n barely #W you."}, {
"$n #W $N.", /* 5.. 6 */
"You #W $N.", "$n #W you."}, {
"$n #W $N hard.", /* 7..10 */
"You #W $N hard.", "$n #W you hard."}, {
"$n #W $N very hard.", /* 11..14 */
"You #W $N very hard.", "$n #W you very hard."}, {
"$n #W $N extremely hard.", /* 15..20 */
"You #W $N extremely hard.", "$n #W you extremely hard."}, {
"$n massacre $N to small fragments with $s #W.", /* > 20 */
"You massacre $N to small fragments with your #W.",
"$n massacre you to small fragments with $s #W."}
};
w_type -= TYPE_HIT; /* Change to base of table with text */
wield = ch->equipment[WIELD];
if (dam == 0) {
buf =
replace_string (dam_weapons[0].to_room,
attack_hit_text[w_type].singular);
act (buf, FALSE, ch, wield, victim, TO_NOTVICT);
buf =
replace_string (dam_weapons[0].to_char,
attack_hit_text[w_type].singular);
act (buf, FALSE, ch, wield, victim, TO_CHAR);
buf =
replace_string (dam_weapons[0].to_victim,
attack_hit_text[w_type].singular);
act (buf, FALSE, ch, wield, victim, TO_VICT);
} else if (dam <= 2) {
buf =
replace_string (dam_weapons[1].to_room,
attack_hit_text[w_type].singular);
act (buf, FALSE, ch, wield, victim, TO_NOTVICT);
buf =
replace_string (dam_weapons[1].to_char,
attack_hit_text[w_type].singular);
act (buf, FALSE, ch, wield, victim, TO_CHAR);
buf =
replace_string (dam_weapons[1].to_victim,
attack_hit_text[w_type].plural);
act (buf, FALSE, ch, wield, victim, TO_VICT);
} else if (dam <= 4) {
buf =
replace_string (dam_weapons[2].to_room,
attack_hit_text[w_type].singular);
act (buf, FALSE, ch, wield, victim, TO_NOTVICT);
buf =
replace_string (dam_weapons[2].to_char,
attack_hit_text[w_type].singular);
act (buf, FALSE, ch, wield, victim, TO_CHAR);
buf =
replace_string (dam_weapons[2].to_victim,
attack_hit_text[w_type].plural);
act (buf, FALSE, ch, wield, victim, TO_VICT);
} else if (dam <= 6) {
buf =
replace_string (dam_weapons[3].to_room, attack_hit_text[w_type].plural);
act (buf, FALSE, ch, wield, victim, TO_NOTVICT);
buf =
replace_string (dam_weapons[3].to_char,
attack_hit_text[w_type].singular);
act (buf, FALSE, ch, wield, victim, TO_CHAR);
buf =
replace_string (dam_weapons[3].to_victim,
attack_hit_text[w_type].plural);
act (buf, FALSE, ch, wield, victim, TO_VICT);
} else if (dam <= 10) {
buf =
replace_string (dam_weapons[4].to_room, attack_hit_text[w_type].plural);
act (buf, FALSE, ch, wield, victim, TO_NOTVICT);
buf =
replace_string (dam_weapons[4].to_char,
attack_hit_text[w_type].singular);
act (buf, FALSE, ch, wield, victim, TO_CHAR);
buf =
replace_string (dam_weapons[4].to_victim,
attack_hit_text[w_type].singular);
act (buf, FALSE, ch, wield, victim, TO_VICT);
} else if (dam <= 15) {
buf =
replace_string (dam_weapons[5].to_room, attack_hit_text[w_type].plural);
act (buf, FALSE, ch, wield, victim, TO_NOTVICT);
buf =
replace_string (dam_weapons[5].to_char,
attack_hit_text[w_type].singular);
act (buf, FALSE, ch, wield, victim, TO_CHAR);
buf =
replace_string (dam_weapons[5].to_victim,
attack_hit_text[w_type].plural);
act (buf, FALSE, ch, wield, victim, TO_VICT);
} else if (dam <= 20) {
buf =
replace_string (dam_weapons[6].to_room,
attack_hit_text[w_type].singular);
act (buf, FALSE, ch, wield, victim, TO_NOTVICT);
buf =
replace_string (dam_weapons[6].to_char,
attack_hit_text[w_type].singular);
act (buf, FALSE, ch, wield, victim, TO_CHAR);
buf =
replace_string (dam_weapons[6].to_victim,
attack_hit_text[w_type].singular);
act (buf, FALSE, ch, wield, victim, TO_VICT);
} else {
buf =
replace_string (dam_weapons[7].to_room,
attack_hit_text[w_type].singular);
act (buf, FALSE, ch, wield, victim, TO_NOTVICT);
buf =
replace_string (dam_weapons[7].to_char,
attack_hit_text[w_type].singular);
act (buf, FALSE, ch, wield, victim, TO_CHAR);
buf =
replace_string (dam_weapons[7].to_victim,
attack_hit_text[w_type].singular);
act (buf, FALSE, ch, wield, victim, TO_VICT);
}
}
void damage (struct char_data *ch, struct char_data *victim,
int dam, int attacktype)
{
char buf[MAX_STRING_LENGTH];
struct message_type *messages;
int i, j, nr, max_hit, exp;
int hit_limit (struct char_data *ch);
assert (GET_POS (victim) > POSITION_DEAD);
if ((GET_LEVEL (victim) > 20) && !IS_NPC (victim)) /* You can't damage an immortal! */
dam = 0;
if (victim != ch) {
if (GET_POS (victim) > POSITION_STUNNED) {
if (!(victim->specials.fighting))
set_fighting (victim, ch);
GET_POS (victim) = POSITION_FIGHTING;
}
if (GET_POS (ch) > POSITION_STUNNED) {
if (!(ch->specials.fighting))
set_fighting (ch, victim);
if (IS_NPC (ch) && IS_NPC (victim) &&
victim->master &&
!number (0, 10) && IS_AFFECTED (victim, AFF_CHARM) &&
(victim->master->in_room == ch->in_room)) {
if (ch->specials.fighting)
stop_fighting (ch);
hit (ch, victim->master, TYPE_UNDEFINED);
return;
}
}
}
if (victim->master == ch)
stop_follower (victim);
if (IS_AFFECTED (ch, AFF_INVISIBLE))
appear (ch);
if (IS_AFFECTED (victim, AFF_SANCTUARY))
dam = MIN (dam, 18); /* Max 18 damage when sanctuary */
dam = MIN (dam, 100);
dam = MAX (dam, 0);
GET_HIT (victim) -= dam;
if (ch != victim)
gain_exp (ch, GET_LEVEL (victim) * dam);
update_pos (victim);
if ((attacktype >= TYPE_HIT) && (attacktype <= TYPE_SLASH)) {
if (!ch->equipment[WIELD]) {
dam_message (dam, ch, victim, TYPE_HIT);
} else {
dam_message (dam, ch, victim, attacktype);
}
} else {
for (i = 0; i < MAX_MESSAGES; i++) {
if (fight_messages[i].a_type == attacktype) {
nr = dice (1, fight_messages[i].number_of_attacks);
for (j = 1, messages = fight_messages[i].msg; (j < nr) && (messages);
j++)
messages = messages->next;
if (!IS_NPC (victim) && (GET_LEVEL (victim) > 20)) {
act (messages->god_msg.attacker_msg, FALSE, ch,
ch->equipment[WIELD], victim, TO_CHAR);
act (messages->god_msg.victim_msg, FALSE, ch, ch->equipment[WIELD],
victim, TO_VICT);
act (messages->god_msg.room_msg, FALSE, ch, ch->equipment[WIELD],
victim, TO_NOTVICT);
} else if (dam != 0) {
if (GET_POS (victim) == POSITION_DEAD) {
act (messages->die_msg.attacker_msg, FALSE, ch,
ch->equipment[WIELD], victim, TO_CHAR);
act (messages->die_msg.victim_msg, FALSE, ch,
ch->equipment[WIELD], victim, TO_VICT);
act (messages->die_msg.room_msg, FALSE, ch, ch->equipment[WIELD],
victim, TO_NOTVICT);
} else {
act (messages->hit_msg.attacker_msg, FALSE, ch,
ch->equipment[WIELD], victim, TO_CHAR);
act (messages->hit_msg.victim_msg, FALSE, ch,
ch->equipment[WIELD], victim, TO_VICT);
act (messages->hit_msg.room_msg, FALSE, ch, ch->equipment[WIELD],
victim, TO_NOTVICT);
}
} else { /* Dam == 0 */
act (messages->miss_msg.attacker_msg, FALSE, ch,
ch->equipment[WIELD], victim, TO_CHAR);
act (messages->miss_msg.victim_msg, FALSE, ch, ch->equipment[WIELD],
victim, TO_VICT);
act (messages->miss_msg.room_msg, FALSE, ch, ch->equipment[WIELD],
victim, TO_NOTVICT);
}
}
}
}
switch (GET_POS (victim)) {
case POSITION_MORTALLYW:
act ("$n is mortally wounded, and will die soon, if not aided.", TRUE,
victim, 0, 0, TO_ROOM);
act ("You are mortally wounded, and will die soon, if not aided.", FALSE,
victim, 0, 0, TO_CHAR);
break;
case POSITION_INCAP:
act ("$n is incapacitated and will slowly die, if not aided.", TRUE,
victim, 0, 0, TO_ROOM);
act ("You are incapacitated an will slowly die, if not aided.", FALSE,
victim, 0, 0, TO_CHAR);
break;
case POSITION_STUNNED:
act ("$n is stunned, but will probably regain conscience again.", TRUE,
victim, 0, 0, TO_ROOM);
act ("You're stunned, but will probably regain conscience again.", FALSE,
victim, 0, 0, TO_CHAR);
break;
case POSITION_DEAD:
act ("$n is dead! R.I.P.", TRUE, victim, 0, 0, TO_ROOM);
act ("You are dead! Sorry...", FALSE, victim, 0, 0, TO_CHAR);
break;
default: /* >= POSITION SLEEPING */
max_hit = hit_limit (victim);
if (dam > (max_hit / 5))
act ("That Really did HURT!", FALSE, victim, 0, 0, TO_CHAR);
if (GET_HIT (victim) < (max_hit / 5)) {
act ("You wish that your wounds would stop BLEEDING that much!", FALSE,
victim, 0, 0, TO_CHAR);
if (IS_NPC (victim))
if (IS_SET (victim->specials.act, ACT_WIMPY))
do_flee (victim, "", 0);
}
break;
}
if (!IS_NPC (victim) && !(victim->desc)) {
do_flee (victim, "", 0);
if (!victim->specials.fighting) {
act ("$n is rescued by divine forces.", FALSE, victim, 0, 0, TO_ROOM);
victim->specials.was_in_room = victim->in_room;
char_from_room (victim);
char_to_room (victim, 0);
}
}
if (GET_POS (victim) < POSITION_STUNNED)
if (ch->specials.fighting == victim)
stop_fighting (ch);
if (!AWAKE (victim))
if (victim->specials.fighting)
stop_fighting (victim);
if (GET_POS (victim) == POSITION_DEAD) {
if (IS_NPC (victim) || victim->desc)
if (IS_AFFECTED (ch, AFF_GROUP)) {
group_gain (ch, victim);
} else {
/* Calculate level-difference bonus */
exp = GET_EXP (victim) / 3;
if (IS_NPC (ch))
exp += (exp * MIN (4, (GET_LEVEL (victim) - GET_LEVEL (ch)))) >> 3;
else
exp += (exp * MIN (8, (GET_LEVEL (victim) - GET_LEVEL (ch)))) >> 3;
exp = MAX (exp, 1);
gain_exp (ch, exp);
change_alignment (ch, victim);
}
if (!IS_NPC (victim)) {
sprintf (buf, "%s killed by %s at %s",
GET_NAME (victim),
(IS_NPC (ch) ? ch->player.short_descr : GET_NAME (ch)),
world[victim->in_room].name);
log (buf);
}
die (victim);
}
}
void hit (struct char_data *ch, struct char_data *victim, int type)
{
struct obj_data *wielded = 0;
struct obj_data *held = 0;
int w_type;
int victim_ac, calc_thaco;
int dam;
byte diceroll;
extern int thaco[4][25];
extern byte backstab_mult[];
extern struct str_app_type str_app[];
extern struct dex_app_type dex_app[];
if (ch->in_room != victim->in_room) {
log ("NOT SAME ROOM WHEN FIGHTING!");
return;
}
if (ch->equipment[HOLD])
held = ch->equipment[HOLD];
if (ch->equipment[WIELD] &&
(ch->equipment[WIELD]->obj_flags.type_flag == ITEM_WEAPON)) {
wielded = ch->equipment[WIELD];
switch (wielded->obj_flags.value[3]) {
case 0:
case 1:
case 2:
w_type = TYPE_WHIP;
break;
case 3:
w_type = TYPE_SLASH;
break;
case 4:
case 5:
case 6:
w_type = TYPE_CRUSH;
break;
case 7:
w_type = TYPE_BLUDGEON;
break;
case 8:
case 9:
case 10:
case 11:
w_type = TYPE_PIERCE;
break;
default:
w_type = TYPE_HIT;
break;
}
} else {
if (IS_NPC (ch) && (ch->specials.attack_type >= TYPE_HIT))
w_type = ch->specials.attack_type;
else
w_type = TYPE_HIT;
}
/* Calculate the raw armor including magic armor */
/* The lower AC, the better */
if (!IS_NPC (ch))
calc_thaco = thaco[GET_CLASS (ch) - 1][(int)GET_LEVEL (ch)];
else
/* THAC0 for monsters is set in the HitRoll */
calc_thaco = 20;
calc_thaco -= str_app[STRENGTH_APPLY_INDEX (ch)].tohit;
calc_thaco -= GET_HITROLL (ch);
diceroll = number (1, 20);
victim_ac = GET_AC (victim) / 10;
if (AWAKE (victim))
victim_ac += dex_app[GET_DEX (victim)].defensive;
victim_ac = MAX (-10, victim_ac); /* -10 is lowest */
if ((diceroll < 20) && AWAKE (victim) &&
((diceroll == 1) || ((calc_thaco - diceroll) > victim_ac))) {
if (type == SKILL_BACKSTAB)
damage (ch, victim, 0, SKILL_BACKSTAB);
else
damage (ch, victim, 0, w_type);
} else {
dam = str_app[STRENGTH_APPLY_INDEX (ch)].todam;
dam += GET_DAMROLL (ch);
if (!wielded) {
if (IS_NPC (ch))
dam += dice (ch->specials.damnodice, ch->specials.damsizedice);
else
dam += number (0, 2); /* Max. 2 dam with bare hands */
} else {
dam += dice (wielded->obj_flags.value[1], wielded->obj_flags.value[2]);
}
if (GET_POS (victim) < POSITION_FIGHTING)
dam *= 1 + (POSITION_FIGHTING - GET_POS (victim)) / 3;
/* Position sitting x 1.33 */
/* Position resting x 1.66 */
/* Position sleeping x 2.00 */
/* Position stunned x 2.33 */
/* Position incap x 2.66 */
/* Position mortally x 3.00 */
dam = MAX (1, dam); /* Not less than 0 damage */
if (type == SKILL_BACKSTAB) {
dam *= backstab_mult[(int)GET_LEVEL (ch)];
damage (ch, victim, dam, SKILL_BACKSTAB);
} else
damage (ch, victim, dam, w_type);
}
}
/* control the fights going on */
void perform_violence (void)
{
struct char_data *ch;
for (ch = combat_list; ch; ch = combat_next_dude) {
combat_next_dude = ch->next_fighting;
assert (ch->specials.fighting);
if (AWAKE (ch) && (ch->in_room == ch->specials.fighting->in_room)) {
hit (ch, ch->specials.fighting, TYPE_UNDEFINED);
} else { /* Not in same room */
stop_fighting (ch);
}
}
}