/*************************************************************************
* File: fight.c Part of CircleMUD *
* Usage: Combat system *
* *
* All rights reserved. See license.doc for complete information. *
* *
* Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. *
************************************************************************ */
#include "conf.h"
#include "sysdep.h"
#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "handler.h"
#include "interpreter.h"
#include "db.h"
#include "spells.h"
#include "screen.h"
#include "dg_scripts.h"
/* Structures */
struct char_data *combat_list = NULL; /* head of l-list of fighting chars */
struct char_data *next_combat_list = NULL;
/* External structures */
extern struct index_data *mob_index;
extern struct str_app_type str_app[];
extern struct dex_app_type dex_app[];
extern struct room_data *world;
extern struct message_list fight_messages[MAX_MESSAGES];
extern struct obj_data *object_list;
extern int pk_allowed; /* see config.c */
extern int auto_save; /* see config.c -- not used in this file */
extern int max_exp_gain; /* see config.c */
extern int max_exp_loss; /* see config.c */
extern int top_of_world;
extern int max_npc_corpse_time, max_pc_corpse_time;
extern sh_int r_death_start_room;
extern sh_int r_mortal_start_room ;
extern sh_int r_ctf_start_room;
/* External procedures */
char *fread_action(FILE * fl, int nr);
ACMD(do_flee);
int backstab_mult(int level);
int thaco(int ch_class, int level);
int ok_damage_shopkeeper(struct char_data * ch, struct char_data * victim);
/* local functions */
void perform_group_gain(struct char_data * ch, int base, struct char_data * victim);
void dam_message(int dam, struct char_data * ch, struct char_data * victim, int w_type);
void appear(struct char_data * ch);
void load_messages(void);
void check_killer(struct char_data * ch, struct char_data * vict);
void make_corpse(struct char_data * ch);
void change_alignment(struct char_data * ch, struct char_data * victim);
void death_cry(struct char_data * ch);
void raw_kill(struct char_data * ch, struct char_data * killer);
void die(struct char_data * ch, struct char_data * killer);
void group_gain(struct char_data * ch, struct char_data * victim);
void solo_gain(struct char_data * ch, struct char_data * victim);
char *replace_string(const char *str, const char *weapon_singular, const char *weapon_plural);
void perform_violence(void);
/* Weapon attack texts */
struct attack_hit_type attack_hit_text[] =
{
{"hit", "hits"}, /* 0 */
{"sting", "stings"},
{"whip", "whips"},
{"slash", "slashes"},
{"bite", "bites"},
{"bludgeon", "bludgeons"}, /* 5 */
{"crush", "crushes"},
{"pound", "pounds"},
{"claw", "claws"},
{"maul", "mauls"},
{"thrash", "thrashes"}, /* 10 */
{"pierce", "pierces"},
{"blast", "blasts"},
{"punch", "punches"},
{"stab", "stabs"}
};
#define IS_WEAPON(type) (((type) >= TYPE_HIT) && ((type) < TYPE_SUFFERING))
/* The Fight related routines */
void appear(struct char_data * ch)
{
if (affected_by_spell(ch, SPELL_INVISIBLE))
affect_from_char(ch, SPELL_INVISIBLE);
REMOVE_BIT(AFF_FLAGS(ch), AFF_INVISIBLE | AFF_HIDE);
if (GET_LEVEL(ch) < LVL_IMMORT)
act("$n slowly fades into existence.", FALSE, ch, 0, 0, TO_ROOM);
else
act("You feel a strange presence as $n appears, seemingly from nowhere.",
FALSE, ch, 0, 0, TO_ROOM);
}
void load_messages(void)
{
FILE *fl;
int i, type;
struct message_type *messages;
char chk[128];
if (!(fl = fopen(MESS_FILE, "r"))) {
sprintf(buf2, "SYSERR: Error reading combat message file %s", MESS_FILE);
perror(buf2);
exit(1);
}
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;
}
fgets(chk, 128, fl);
while (!feof(fl) && (*chk == '\n' || *chk == '*'))
fgets(chk, 128, fl);
while (*chk == 'M') {
fgets(chk, 128, fl);
sscanf(chk, " %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("SYSERR: Too many combat messages. Increase MAX_MESSAGES and recompile.");
exit(1);
}
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_action(fl, i);
messages->die_msg.victim_msg = fread_action(fl, i);
messages->die_msg.room_msg = fread_action(fl, i);
messages->miss_msg.attacker_msg = fread_action(fl, i);
messages->miss_msg.victim_msg = fread_action(fl, i);
messages->miss_msg.room_msg = fread_action(fl, i);
messages->hit_msg.attacker_msg = fread_action(fl, i);
messages->hit_msg.victim_msg = fread_action(fl, i);
messages->hit_msg.room_msg = fread_action(fl, i);
messages->god_msg.attacker_msg = fread_action(fl, i);
messages->god_msg.victim_msg = fread_action(fl, i);
messages->god_msg.room_msg = fread_action(fl, i);
fgets(chk, 128, fl);
while (!feof(fl) && (*chk == '\n' || *chk == '*'))
fgets(chk, 128, fl);
}
fclose(fl);
}
void update_pos(struct char_data * victim)
{
if ((GET_HIT(victim) > 0) && (GET_POS(victim) > POS_STUNNED))
return;
else if (GET_HIT(victim) > 0)
GET_POS(victim) = POS_STANDING;
else if (GET_HIT(victim) <= -11)
GET_POS(victim) = POS_DEAD;
else if (GET_HIT(victim) <= -6)
GET_POS(victim) = POS_MORTALLYW;
else if (GET_HIT(victim) <= -3)
GET_POS(victim) = POS_INCAP;
else
GET_POS(victim) = POS_STUNNED;
}
void check_killer(struct char_data * ch, struct char_data * vict)
{
if (!PLR_FLAGGED(vict, PLR_KILLER) && !PLR_FLAGGED(vict, PLR_icer)
&& !PLR_FLAGGED(ch, PLR_KILLER) && !IS_NPC(ch) && !IS_NPC(vict) &&
(ch != vict)) {
char buf[256];
SET_BIT(PLR_FLAGS(ch), PLR_KILLER);
sprintf(buf, "PC Killer bit set on %s for initiating attack on %s at %s.",
GET_NAME(ch), GET_NAME(vict), world[vict->in_room].name);
mudlog(buf, BRF, LVL_IMMORT, TRUE);
send_to_char("If you want to be a PLAYER KILLER, so be it...\r\n", ch);
}
}
/* start one char fighting another (yes, it is horrible, I know... ) */
void set_fighting(struct char_data * ch, struct char_data * vict)
{
if (ch == vict)
return;
if (FIGHTING(ch)) {
core_dump();
return;
}
ch->next_fighting = combat_list;
combat_list = ch;
if (AFF_FLAGGED(ch, AFF_YOIKOMINMINKEN))
affect_from_char(ch, SPELL_YOIKOMINMINKEN);
FIGHTING(ch) = vict;
GET_POS(ch) = POS_FIGHTING;
if (!pk_allowed)
check_killer(ch, vict);
}
void unentangle(struct char_data *ch)
{
static struct affected_type *af, *next;
extern char *spell_wear_off_msg[];
for (af = ch->affected; af; af = next) {
next = af->next;
if (af->bitvector == AFF_MFROZEN) {
send_to_char(spell_wear_off_msg[af->type], ch);
send_to_char("\r\n", ch);
}
affect_remove(ch, af);
act("$n is free of the mind freeze.",TRUE,ch,0,0,TO_ROOM);
}
}
/* remove a char from the list of fighting chars */
void stop_fighting(struct char_data * ch)
{
struct char_data *temp;
if (ch == next_combat_list)
next_combat_list = ch->next_fighting;
REMOVE_FROM_LIST(ch, combat_list, next_fighting);
ch->next_fighting = NULL;
FIGHTING(ch) = NULL;
GET_POS(ch) = POS_STANDING;
update_pos(ch);
}
void make_corpse(struct char_data * ch)
{
struct obj_data *corpse, *o;
struct obj_data *money;
int i;
corpse = create_obj();
corpse->item_number = NOTHING;
corpse->in_room = NOWHERE;
corpse->name = str_dup("corpse");
sprintf(buf2, "The corpse of %s is lying here.", GET_NAME(ch));
corpse->description = str_dup(buf2);
sprintf(buf2, "the corpse of %s", GET_NAME(ch));
corpse->short_description = str_dup(buf2);
GET_OBJ_TYPE(corpse) = ITEM_CONTAINER;
GET_OBJ_WEAR(corpse) = ITEM_WEAR_TAKE;
GET_OBJ_EXTRA(corpse) = ITEM_NODONATE;
GET_OBJ_VAL(corpse, 0) = 0; /* You can't store stuff in a corpse */
GET_OBJ_VAL(corpse, 3) = 1; /* corpse identifier */
GET_OBJ_WEIGHT(corpse) = GET_WEIGHT(ch) + IS_CARRYING_W(ch);
GET_OBJ_RENT(corpse) = 100000;
if (IS_NPC(ch))
GET_OBJ_TIMER(corpse) = max_npc_corpse_time;
else
GET_OBJ_TIMER(corpse) = max_pc_corpse_time;
/* transfer character's inventory to the corpse */
corpse->contains = ch->carrying;
for (o = corpse->contains; o != NULL; o = o->next_content)
o->in_obj = corpse;
object_list_new_owner(corpse, NULL);
/* transfer character's equipment to the corpse */
for (i = 0; i < NUM_WEARS; i++)
if (GET_EQ(ch, i))
obj_to_obj(unequip_char(ch, i), corpse);
/* transfer gold */
if (GET_GOLD(ch) > 0) {
/* following 'if' clause added to fix gold duplication loophole */
if (IS_NPC(ch) || (!IS_NPC(ch) && ch->desc)) {
money = create_money(GET_GOLD(ch));
obj_to_obj(money, corpse);
}
GET_GOLD(ch) = 0;
}
ch->carrying = NULL;
IS_CARRYING_N(ch) = 0;
IS_CARRYING_W(ch) = 0;
obj_to_room(corpse, ch->in_room);
}
/* When ch kills victim */
void change_alignment(struct char_data * ch, struct char_data * victim)
{
/*
* new alignment change algorithm: if you kill a monster with alignment A,
* you move 1/16th of the way to having alignment -A. Simple and fast.
*/
GET_ALIGNMENT(ch) += (-GET_ALIGNMENT(victim) - GET_ALIGNMENT(ch)) / 16;
}
void death_cry(struct char_data * ch)
{
int door, was_in;
act("Your blood freezes as you hear $n's death cry.", FALSE, ch, 0, 0, TO_ROOM);
was_in = ch->in_room;
for (door = 0; door < NUM_OF_DIRS; door++) {
if (CAN_GO(ch, door)) {
ch->in_room = world[was_in].dir_option[door]->to_room;
act("Your blood freezes as you hear someone's death cry.", FALSE, ch, 0, 0, TO_ROOM);
ch->in_room = was_in;
}
}
}
void raw_kill(struct char_data * ch, struct char_data * killer)
{
if (ROOM_FLAGGED(ch->in_room, ROOM_CTFARENA)) {
char_from_room(ch);
char_to_room(ch, r_ctf_start_room);
look_at_room(ch, 0);
return;
}
if (FIGHTING(ch))
stop_fighting(ch);
while (ch->affected)
affect_remove(ch, ch->affected);
if (killer) {
if (death_mtrigger(ch, killer))
death_cry(ch);
} else
death_cry(ch);
make_corpse(ch);
char_from_room(ch);
char_to_room(ch, r_death_start_room);
GET_HIT(ch) = GET_MAX_HIT(ch)*0.1;
GET_MANA(ch) = GET_MAX_MANA(ch)*0.1;
GET_MOVE(ch) = GET_MAX_MOVE(ch)*0.1;
SET_BIT(PLR_FLAGS(ch), PLR_RARM);
SET_BIT(PLR_FLAGS(ch), PLR_LARM);
SET_BIT(PLR_FLAGS(ch), PLR_RLEG);
SET_BIT(PLR_FLAGS(ch), PLR_LLEG);
if (IS_saiyan(ch)) {
SET_BIT(PLR_FLAGS(ch), PLR_STAIL);
}
if (IS_HALF_BREED(ch)) {
SET_BIT(PLR_FLAGS(ch), PLR_STAIL);
}
if (IS_icer(ch)) {
SET_BIT(PLR_FLAGS(ch), PLR_TAIL);
}
GET_POS(ch) = POS_STANDING;
look_at_room(ch, 0);
if (IS_NPC(ch) || GET_HIT(ch) <= -11)
extract_char(ch);
}
void die(struct char_data * ch, struct char_data * killer)
{
gain_exp(ch, -(GET_EXP(ch) / 2));
if (!IS_NPC(ch))
REMOVE_BIT(PLR_FLAGS(ch), PLR_KILLER | PLR_icer);
raw_kill(ch, killer);
}
void perform_group_gain(struct char_data * ch, int base,
struct char_data * victim)
{
int share;
share = MIN(max_exp_gain, MAX(1, base));
if (share > 1) {
sprintf(buf2, "You receive your share of experience -- %d points.\r\n", share);
REMOVE_BIT(AFF_FLAGS(ch), AFF_HALTED);
send_to_char(buf2, ch);
} else
send_to_char("You receive your share of experience -- one measly little point!\r\n", ch);
REMOVE_BIT(AFF_FLAGS(ch), AFF_HALTED);
gain_exp(ch, share);
change_alignment(ch, victim);
}
void group_gain(struct char_data * ch, struct char_data * victim)
{
int tot_members, base, tot_gain;
struct char_data *k;
struct follow_type *f;
if (!(k = ch->master))
k = ch;
if (AFF_FLAGGED(k, AFF_GROUP) && (k->in_room == ch->in_room))
tot_members = 1;
else
tot_members = 0;
for (f = k->followers; f; f = f->next)
if (AFF_FLAGGED(f->follower, AFF_GROUP) && f->follower->in_room == ch->in_room)
tot_members++;
/* round up to the next highest tot_members */
tot_gain = (GET_EXP(victim)) + tot_members - 1;
/* prevent illegal xp creation when killing players */
if (!IS_NPC(victim))
tot_gain = MIN(max_exp_loss * 2 / 3, tot_gain);
if (tot_members >= 1)
base = MAX(1, tot_gain / tot_members);
else
base = 0;
if (AFF_FLAGGED(k, AFF_GROUP) && k->in_room == ch->in_room)
perform_group_gain(k, base, victim);
for (f = k->followers; f; f = f->next)
if (AFF_FLAGGED(f->follower, AFF_GROUP) && f->follower->in_room == ch->in_room)
perform_group_gain(f->follower, base, victim);
}
void solo_gain(struct char_data * ch, struct char_data * victim)
{
int exp;
exp = MIN(max_exp_gain, GET_EXP(victim));
exp = MAX(1, exp);
if (exp > 1) {
sprintf(buf2, "You receive %d experience points.\r\n", exp);
REMOVE_BIT(AFF_FLAGS(ch), AFF_HALTED);
send_to_char(buf2, ch);
} else
send_to_char("You receive one lousy experience point.\r\n", ch);
REMOVE_BIT(AFF_FLAGS(ch), AFF_HALTED);
gain_exp(ch, exp);
change_alignment(ch, victim);
}
char *replace_string(const char *str, const char *weapon_singular, const char *weapon_plural)
{
static char buf[256];
char *cp = buf;
for (; *str; str++) {
if (*str == '#') {
switch (*(++str)) {
case 'W':
for (; *weapon_plural; *(cp++) = *(weapon_plural++));
break;
case 'w':
for (; *weapon_singular; *(cp++) = *(weapon_singular++));
break;
default:
*(cp++) = '#';
break;
}
} else
*(cp++) = *str;
*cp = 0;
} /* For */
return (buf);
}
/* message for doing damage with a weapon */
void dam_message(int dam, struct char_data * ch, struct char_data * victim,
int w_type)
{
char *buf;
int msgnum;
static struct dam_weapon_type {
const char *to_room;
const char *to_char;
const char *to_victim;
} dam_weapons[] = {
/* use #w for singular (i.e. "slash") and #W for plural (i.e. "slashes") */
{
"&09$n&10 tries to #w &09$N&10, but misses.&00", /* 0: 0 */
"&10You try to #w &09$N&10, but miss.&00",
"&09$n&10 tries to #w you, but misses.&00"
},
{
"&09$n&10 scratches &09$N&10 in the &15right arm&10 as $e #W $M.&00", /* 1: */
"&10You scratch &09$N&10 in the &15right arm&10 as you #w $M.&00",
"&09$n&10 scratches you in the &15right arm&10 as $e #W you.&00"
},
{
"&09$n&10 scratches &09$N&10 in the &15left arm&10 as $e #W $M.&00", /* 2: */
"&10You scratch &09$N&10 in the &15left arm&10 as you #w $M.&00",
"&09$n&10 scratches you in the &15left arm&10 as $e #W you.&00"
},
{
"&09$n&10 scratches &09$N&10 in the &15right leg&10 as $e #W $M.&00", /* 3: */
"&10You scratch &09$N&10 in the &15right leg&10 as you #w $M.&00",
"&09$n&10 scratches you in the &15right leg&10 as $e #W you.&00"
},
{
"&09$n&10 scratches &09$N&10 in the &15left leg&10 as $e #W $M.&00", /* 4: */
"&10You scratch &09$N&10 in the &15left leg&10 as you #w $M.&00",
"&09$n&10 scratches you in the &15left leg&10 as $e #W you.&00"
},
{
"&09$n&10 scratches &09$N&10 in the &15chest&10 as $e #W $M.&00", /* 5: */
"&10You scratch &09$N&10 in the &15chest&10 as you #w $M.&00",
"&09$n&10 scratches you in the &15chest&10 as $e #W you.&00"
},
{
"&09$n&10 scratches &09$N&10 in the &15face&10 as $e #W $M.&00", /* 6: */
"&10You scratch &09$N&10 in the &15face&10 as you #w $M.&00",
"&09$n&10 scratches you in the &15face&10 as $e #W you.&00"
},
{
"&09$n&10 barely #W &09$N&10 in the &15right arm&10.&00", /* 7: */
"&10You barely #w &09$N&10 in the &15right arm&10.&00",
"&09$n&15 barely #W you in the &15right arm&10.&00"
},
{
"&09$n&10 barely #W &09$N&10 in the &15left arm&10.&00", /* 8: */
"&10You barely #w &09$N&10 in the &15left arm&10.&00",
"&09$n&10 barely #W you in the &15left arm&10.&00"
},
{
"&09$n&10 barely #W &09$N&10 in the &15right leg&10.&00", /* 9: */
"&10You barely #w &09$N&10 in the &15right leg&10.&00",
"&09$n&10 barely #W you in the &15right leg&10.&00"
},
{
"&09$n&10 barely #W &09$N&10 in the &15left leg&10.&00", /* 10: */
"&10You barely #w &09$N&10 in the &15left leg&10.&00",
"&09$n&10 barely #W you in the &15left leg&10.&00"
},
{
"&09$n&10 barely #W &09$N&10 in the &15chest&10.&00", /* 11: */
"&10You barely #w &09$N&10 in the &15chest&10.&00",
"&09$n&10 barely #W you in the &15chest&10.&00"
},
{
"&09$n&10 barely #W &09$N&10 in the &15face&10.&00", /* 12: */
"&10You barely #w &09$N&10 in the &15face&10.&00",
"&09$n&10 barely #W you in the &15face&10.&00"
},
{
"&12$n&10 #W &09$N&10 in the &15right arm&10.&00", /* 13 */
"&10You #w &09$N&10 in the &15right arm&10.&00",
"&09$n&10 #W you in &15right arm&10.&00"
},
{
"&12$n&10 #W &09$N&10 in the &15left arm&10.&00", /* 14 */
"&10You #w &09$N&10 in the &15left arm&10.&00",
"&09$n&10 #W you in &15left arm&10.&00"
},
{
"&12$n&10 #W &09$N&10 in the &15right leg&10.&00", /* 15 */
"&10You #w &09$N&10 in the &15right leg&10.&00",
"&09$n&10 #W you in &15right leg&10.&00"
},
{
"&12$n&10 #W &09$N&10 in the &15left leg&10.&00", /* 16 */
"&10You #w &09$N&10 in the &15left leg&10.&00",
"&09$n&10 #W you in &15left leg&10.&00"
},
{
"&12$n&10 #W &09$N&10 in the &15chest&10.&00", /* 17 */
"&10You #w &09$N&10 in the &15chest&10.&00",
"&09$n&10 #W you in &15chest&10.&00"
},
{
"&12$n&10 #W &09$N&10 in the &15face&10.&00", /* 18 */
"&10You #w &09$N&10 in the &15face&10.&00",
"&09$n&10 #W you in &15face&10.&00"
},
{
"&09$n&10 #W &09$N&10 hard in the &15right arm&10.&00", /* 19 */
"&10You #w &09$N&10 hard in the &15right arm&10.&00",
"&09$n&10 #W you hard in the &15right arm&10.&00"
},
{
"&09$n&10 #W &09$N&10 hard in the &15left arm&10.&00", /* 20 */
"&10You #w &09$N&10 hard in the &15left arm&10.&00",
"&09$n&10 #W you hard in the &15left arm&10.&00"
},
{
"&09$n&10 #W &09$N&10 hard in the &15right leg&10.&00", /* 21 */
"&10You #w &09$N&10 hard in the &15right leg&10.&00",
"&09$n&10 #W you hard in the &15right leg&10.&00"
},
{
"&09$n&10 #W &09$N&10 hard in the &15left leg&10.&00", /* 22 */
"&10You #w &09$N&10 hard in the &15left leg&10.&00",
"&09$n&10 #W you hard in the &15left leg&10.&00"
},
{
"&09$n&10 #W &09$N&10 hard in the &15chest&10.&00", /* 23 */
"&10You #w &09$N&10 hard in the &15chest&10.&00",
"&09$n&10 #W you hard in the &15chest&10.&00"
},
{
"&09$n&10 #W &09$N&10 hard in the &15face&10.&00", /* 24 */
"&10You #w &09$N&10 hard in the &15face&10.&00",
"&09$n&10 #W you hard in the &15face&10.&00"
},
{
"&09$n&10 #W &09$N&10 very hard in the &15right arm&10.&00", /* 25 */
"&15You #w &09$N&10 very hard in the &15right arm&10.&00",
"&09$n&10 #W you very hard in the &15right arm&10.&00"
},
{
"&09$n&10 #W &09$N&10 very hard in the &15left arm&10.&00", /* 26 */
"&15You #w &09$N&10 very hard in the &15left arm&10.&00",
"&09$n&10 #W you very hard in the &15left arm&10.&00"
},
{
"&09$n&10 #W &09$N&10 very hard in the &15right leg&10.&00", /* 27 */
"&15You #w &09$N&10 very hard in the &15right leg&10.&00",
"&09$n&10 #W you very hard in the &15right leg&10.&00"
},
{
"&09$n&10 #W &09$N&10 very hard in the &15left leg&10.&00", /* 28 */
"&15You #w &09$N&10 very hard in the &15left leg &10.&00",
"&09$n&10 #W you very hard in the &15left leg&10.&00"
},
{
"&09$n&10 #W &09$N&10 very hard in the &15chest&10.&00", /* 29 */
"&15You #w &09$N&10 very hard in the &15chest&10.&00",
"&09$n&10 #W you very hard in the &15chest&10.&00"
},
{
"&09$n&10 #W &09$N&10 very hard in the &15face&10.&00", /* 30 */
"&15You #w &09$N&10 very hard in the &15face&10.&00",
"&09$n&10 #W you very hard in the &15face&10.&00"
},
{
"&09$n&10 #W &09$N&10 extremely hard in the &15right arm&10.&00", /* 31 */
"&10You #w &09$N&10 extremely hard in the &15right arm&10.&00",
"&09$n&10 #W you extremely hard in the &15right arm&10.&00"
},
{
"&09$n&10 #W &09$N&10 extremely hard in the &15left arm&10.&00", /* 32 */
"&10You #w &09$N&10 extremely hard in the &15left arm&10.&00",
"&09$n&10 #W you extremely hard in the &15left arm&10.&00"
},
{
"&09$n&10 #W &09$N&10 extremely hard in the &15right leg&10.&00", /* 33 */
"&10You #w &09$N&10 extremely hard in the &15right leg&10.&00",
"&09$n&10 #W you extremely hard in the &15right leg&10.&00"
},
{
"&09$n&10 #W &09$N&10 extremely hard in the &15left leg&10.&00", /* 34 */
"&10You #w &09$N&10 extremely hard in the &15left leg&10.&00",
"&09$n&10 #W you extremely hard in the &15left leg&10.&00"
},
{
"&09$n&10 #W &09$N&10 extremely hard in the &15chest&10.&00", /* 35 */
"&10You #w &09$N&10 extremely hard in the &15chest&10.&00",
"&09$n&10 #W you extremely hard in the &15chest&10.&00"
},
{
"&09$n&10 #W &09$N&10 extremely hard in the &15face&10.&00", /* 36 */
"&10You #w &09$N&10 extremely hard in the &15face&10.&00",
"&09$n&10 #W you extremely hard in the &15face&10.&00"
}
};
w_type -= TYPE_HIT; /* Change to base of table with text */
if (dam == 0) msgnum = 0;
else if (dam <= 1) msgnum = 1;
else if (dam <= 2) msgnum = 2;
else if (dam <= 3) msgnum = 3;
else if (dam <= 4) msgnum = 4;
else if (dam <= 5) msgnum = 5;
else if (dam <= 6) msgnum = 6;
else if (dam <= 7) msgnum = 7;
else if (dam <= 8) msgnum = 8;
else if (dam <= 9) msgnum = 9;
else if (dam <= 10) msgnum = 10;
else if (dam <= 11) msgnum = 11;
else if (dam <= 12) msgnum = 12;
else if (dam <= 13) msgnum = 13;
else if (dam <= 14) msgnum = 14;
else if (dam <= 15) msgnum = 15;
else if (dam <= 16) msgnum = 16;
else if (dam <= 17) msgnum = 17;
else if (dam <= 18) msgnum = 18;
else if (dam <= 19) msgnum = 19;
else if (dam <= 20) msgnum = 20;
else if (dam <= 21) msgnum = 21;
else if (dam <= 22) msgnum = 22;
else if (dam <= 23) msgnum = 23;
else if (dam <= 24) msgnum = 24;
else if (dam <= 25) msgnum = 25;
else if (dam <= 26) msgnum = 26;
else if (dam <= 27) msgnum = 27;
else if (dam <= 28) msgnum = 28;
else if (dam <= 29) msgnum = 29;
else if (dam <= 30) msgnum = 30;
else if (dam <= 31) msgnum = 31;
else if (dam <= 32) msgnum = 32;
else if (dam <= 33) msgnum = 33;
else if (dam <= 34) msgnum = 34;
else if (dam <= 35) msgnum = 35;
else if (dam <= 36) msgnum = 36;
else msgnum = 36;
/* damage message to onlookers */
buf = replace_string(dam_weapons[msgnum].to_room,
attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
act(buf, FALSE, ch, NULL, victim, TO_NOTVICT);
/* damage message to damager */
send_to_char(CCYEL(ch, C_CMP), ch);
buf = replace_string(dam_weapons[msgnum].to_char,
attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
act(buf, FALSE, ch, NULL, victim, TO_CHAR);
send_to_char(CCNRM(ch, C_CMP), ch);
/* damage message to damagee */
send_to_char(CCRED(victim, C_CMP), victim);
buf = replace_string(dam_weapons[msgnum].to_victim,
attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
act(buf, FALSE, ch, NULL, victim, TO_VICT | TO_SLEEP);
send_to_char(CCNRM(victim, C_CMP), victim);
}
/*
* message for doing damage with a spell or skill
* C3.0: Also used for weapon damage on miss and death blows
*/
int skill_message(int dam, struct char_data * ch, struct char_data * vict,
int attacktype)
{
int i, j, nr;
struct message_type *msg;
struct obj_data *weap = GET_EQ(ch, WEAR_WIELD);
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, msg = fight_messages[i].msg; (j < nr) && msg; j++)
msg = msg->next;
if (!IS_NPC(vict) && (GET_LEVEL(vict) >= LVL_IMMORT)) {
act(msg->god_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR);
act(msg->god_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT);
act(msg->god_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT);
} else if (dam != 0) {
if (GET_POS(vict) == POS_DEAD) {
send_to_char(CCYEL(ch, C_CMP), ch);
act(msg->die_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR);
send_to_char(CCNRM(ch, C_CMP), ch);
send_to_char(CCRED(vict, C_CMP), vict);
act(msg->die_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT | TO_SLEEP);
send_to_char(CCNRM(vict, C_CMP), vict);
act(msg->die_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT);
} else {
send_to_char(CCYEL(ch, C_CMP), ch);
act(msg->hit_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR);
send_to_char(CCNRM(ch, C_CMP), ch);
send_to_char(CCRED(vict, C_CMP), vict);
act(msg->hit_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT | TO_SLEEP);
send_to_char(CCNRM(vict, C_CMP), vict);
act(msg->hit_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT);
}
} else if (ch != vict) { /* Dam == 0 */
send_to_char(CCYEL(ch, C_CMP), ch);
act(msg->miss_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR);
send_to_char(CCNRM(ch, C_CMP), ch);
send_to_char(CCRED(vict, C_CMP), vict);
act(msg->miss_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT | TO_SLEEP);
send_to_char(CCNRM(vict, C_CMP), vict);
act(msg->miss_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT);
}
return 1;
}
}
return 0;
}
/*
* Alert: As of bpl14, this function returns the following codes:
* < 0 Victim died.
* = 0 No damage.
* > 0 How much damage done.
*/
int damage(struct char_data * ch, struct char_data * victim, int dam, int attacktype)
{
if (GET_POS(victim) <= POS_DEAD) {
log("SYSERR: Attempt to damage corpse '%s' in room #%d by '%s'.",
GET_NAME(victim), GET_ROOM_VNUM(IN_ROOM(victim)), GET_NAME(ch));
die(victim, ch);
return 0; /* -je, 7/7/92 */
}
/* peaceful rooms */
if (ch != victim && ROOM_FLAGGED(ch->in_room, ROOM_PEACEFUL)) {
send_to_char("This room just has such a peaceful, easy feeling...\r\n", ch);
return 0;
}
/* Frozen */
if (IS_AFFECTED(ch, AFF_MFROZEN)) {
send_to_char("You struggle against your mind freeze...\r\n", ch);
return 0;
}
/* shopkeeper protection */
if (!ok_damage_shopkeeper(ch, victim))
return 0;
/* You can't damage an immortal! */
if (victim != ch) {
/* Start the attacker fighting the victim */
if (GET_POS(ch) > POS_STUNNED && (FIGHTING(ch) == NULL))
set_fighting(ch, victim);
/* Start the victim fighting the attacker */
if (GET_POS(victim) > POS_STUNNED && (FIGHTING(victim) == NULL)) {
set_fighting(victim, ch);
if (MOB_FLAGGED(victim, MOB_MEMORY) && !IS_NPC(ch))
remember(victim, ch);
}
}
/* If you attack a pet, it hates your guts */
if (victim->master == ch)
stop_follower(victim);
/* If the attacker is invisible, he becomes visible */
if (AFF_FLAGGED(ch, AFF_INVISIBLE | AFF_HIDE))
appear(ch);
/* Cut damage in half if victim has sanct, to a minimum 1 */
if (AFF_FLAGGED(victim, AFF_BARRIER) && dam >= 2)
dam /= 2;
/* Check for PK if this is not a PK MUD */
if (!pk_allowed) {
check_killer(ch, victim);
if (PLR_FLAGGED(ch, PLR_KILLER) && (ch != victim))
dam = 0;
}
/* Set the maximum damage per round and subtract the hit points */
dam = MAX(MIN(dam, 50000), 0);
GET_HIT(victim) -= dam;
/* Gain exp for the hit */
if (ch != victim)
update_pos(victim);
/*
* skill_message sends a message from the messages file in lib/misc.
* dam_message just sends a generic "You hit $n extremely hard.".
* skill_message is preferable to dam_message because it is more
* descriptive.
*
* If we are _not_ attacking with a weapon (i.e. a spell), always use
* skill_message. If we are attacking with a weapon: If this is a miss or a
* death blow, send a skill_message if one exists; if not, default to a
* dam_message. Otherwise, always send a dam_message.
*/
if (!IS_WEAPON(attacktype))
skill_message(dam, ch, victim, attacktype);
else {
if (GET_POS(victim) == POS_DEAD || dam == 0) {
if (!skill_message(dam, ch, victim, attacktype))
dam_message(dam, ch, victim, attacktype);
} else {
dam_message(dam, ch, victim, attacktype);
}
}
if (GET_HIT(victim) <= -11 && IS_NPC(ch))
{
send_to_char("&12You manage to restrain your opponent and stop combat!&00\n\r", victim);
GET_POS(victim) = POS_STANDING;
GET_HIT(victim) = 10;
stop_fighting(ch);stop_fighting(victim);
if (!IS_NPC(victim))
char_from_room(victim);
char_to_room(victim, r_mortal_start_room );
look_at_room(victim, 0);
return dam;
}
/* Use send_to_char -- act() doesn't send message if you are DEAD. */
switch (GET_POS(victim)) {
case POS_MORTALLYW:
act("$n is mortally wounded, and will die soon, if not aided.", TRUE, victim, 0, 0, TO_ROOM);
send_to_char("You are mortally wounded, and will die soon, if not aided.\r\n", victim);
break;
case POS_INCAP:
act("$n is incapacitated and will slowly die, if not aided.", TRUE, victim, 0, 0, TO_ROOM);
send_to_char("You are incapacitated an will slowly die, if not aided.\r\n", victim);
break;
case POS_STUNNED:
act("$n is stunned, but will probably regain consciousness again.", TRUE, victim, 0, 0, TO_ROOM);
send_to_char("You're stunned, but will probably regain consciousness again.\r\n", victim);
break;
case POS_DEAD:
act("$n is dead! R.I.P.", FALSE, victim, 0, 0, TO_ROOM);
send_to_char("You are dead! You are sent to the Next Dimension.\r\n", victim);
REMOVE_BIT(AFF_FLAGS(victim), AFF_HALTED);
break;
default: /* >= POSITION SLEEPING */
if (dam > (GET_MAX_HIT(victim) / 4))
act("That really did HURT!", FALSE, victim, 0, 0, TO_CHAR);
if (GET_HIT(victim) < (GET_MAX_HIT(victim) / 4)) {
sprintf(buf2, "%sYou wish that your wounds would stop BLEEDING so much!%s\r\n",
CCRED(victim, C_SPR), CCNRM(victim, C_SPR));
send_to_char(buf2, victim);
if (ch != victim && MOB_FLAGGED(victim, MOB_WIMPY))
do_flee(victim, NULL, 0, 0);
}
if (!IS_NPC(victim) && GET_WIMP_LEV(victim) && (victim != ch) &&
GET_HIT(victim) < GET_WIMP_LEV(victim) && GET_HIT(victim) > 0) {
send_to_char("You wimp out, and attempt to flee!\r\n", victim);
REMOVE_BIT(AFF_FLAGS(ch), AFF_HALTED);
do_flee(victim, NULL, 0, 0);
}
break;
}
/* Help out poor linkless people who are attacked */
if (!IS_NPC(victim) && !(victim->desc)) {
do_flee(victim, NULL, 0, 0);
if (!FIGHTING(victim)) {
act("$n is rescued by divine forces.", FALSE, victim, 0, 0, TO_ROOM);
GET_WAS_IN(victim) = victim->in_room;
char_from_room(victim);
char_to_room(victim, 0);
}
}
/* stop someone from fighting if they're stunned or worse */
if ((GET_POS(victim) <= POS_STUNNED) && (FIGHTING(victim) != NULL))
stop_fighting(victim);
/* Uh oh. Victim died. */
if (GET_POS(victim) == POS_DEAD) {
if ((ch != victim) && (IS_NPC(victim) || victim->desc)) {
if (AFF_FLAGGED(ch, AFF_GROUP))
group_gain(ch, victim);
else
solo_gain(ch, victim);
}
if (!IS_NPC(victim)) {
sprintf(buf2, "%s killed by %s at %s", GET_NAME(victim), GET_NAME(ch),
world[victim->in_room].name);
mudlog(buf2, BRF, LVL_IMMORT, TRUE);
if (MOB_FLAGGED(ch, MOB_MEMORY))
forget(ch, victim);
}
die(victim, ch);
return -1;
}
return dam;
}
void hit(struct char_data * ch, struct char_data * victim, int type)
{
struct obj_data *wielded2 = GET_EQ(ch, WEAR_WRIST_L);
struct obj_data *wielded3 = GET_EQ(ch, WEAR_WRIST_R);
struct obj_data *wielded = GET_EQ(ch, WEAR_WIELD);
int w_type, victim_ac, calc_thaco, dam, diceroll;
/* check if the character has a fight trigger */
fight_mtrigger(ch);
if(!ch || !victim)
return;
/* This appears to fix my crash problem, Thanks to Chuck Reed */
/* if you notice anything */
/* please email me about it so I can fix it or if you know of a fix */
/* caminturn@earthlink.net */
/* Do some sanity checking, in case someone flees, etc. */
if (ch->in_room != victim->in_room) {
if (FIGHTING(ch) && FIGHTING(ch) == victim)
stop_fighting(ch);
return;
}
/* Find the weapon type (for display purposes only) */
if (wielded && GET_OBJ_TYPE(wielded) == ITEM_WEAPON)
w_type = GET_OBJ_VAL(wielded, 3) + TYPE_HIT;
else {
if (IS_NPC(ch) && (ch->mob_specials.attack_type != 0))
w_type = ch->mob_specials.attack_type + TYPE_HIT;
else
w_type = TYPE_HIT;
}
if (wielded2 && GET_OBJ_TYPE(wielded2) == ITEM_ATTACH) {
dam += dice(GET_OBJ_VAL(wielded2, 1), GET_OBJ_VAL(wielded2, 2));
}
if (wielded3 && GET_OBJ_TYPE(wielded3) == ITEM_ATTACH) {
dam += dice(GET_OBJ_VAL(wielded3, 1), GET_OBJ_VAL(wielded3, 2));
}
/* Calculate the THAC0 of the attacker */
if (!IS_NPC(ch))
calc_thaco = thaco((int) GET_CLASS(ch), (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);
calc_thaco -= (int) ((GET_INT(ch) - 13) / 1.5);
calc_thaco -= (int) ((GET_WIS(ch) - 13) / 1.5); /* So does wisdom */
/* Calculate the raw armor including magic armor. Lower AC is better. */
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 */
/* roll the die and take your chances... */
diceroll = number(1, 20);
/* decide whether this is a hit or a miss */
if ((((diceroll < 20) && AWAKE(victim)) &&
((diceroll == 1) || ((calc_thaco - diceroll) > victim_ac)))) {
/* the attacker missed the victim */
if (type == SKILL_BACKSTAB)
damage(ch, victim, 0, SKILL_BACKSTAB);
else
damage(ch, victim, 0, w_type);
} else {
/* okay, we know the guy has been hit. now calculate damage. */
/* Start with the damage bonuses: the damroll and strength apply */
dam = str_app[STRENGTH_APPLY_INDEX(ch)].todam;
dam += GET_DAMROLL(ch);
if (wielded) {
/* Add weapon-based damage if a weapon is being wielded */
dam += dice(GET_OBJ_VAL(wielded, 1), GET_OBJ_VAL(wielded, 2));
} else {
/* If no weapon, add bare hand damage instead */
if (IS_NPC(ch)) {
dam += dice(ch->mob_specials.damnodice, ch->mob_specials.damsizedice);
} else {
dam += number(1,GET_STR(ch)*2+GET_MANA(ch)/4000*GET_LEVEL(ch)+GET_DAMROLL(ch) - GET_STR(victim)*2+GET_MANA(victim)/4000*GET_LEVEL(victim)+GET_DAMROLL(victim)); /*Max 2 bare hand damage for players */
}
if(dam <=1)
{
dam += 1;
}
}
/*
* Include a damage multiplier if victim isn't ready to fight:
*
* Position sitting 1.33 x normal
* Position resting 1.66 x normal
* Position sleeping 2.00 x normal
* Position stunned 2.33 x normal
* Position incap 2.66 x normal
* Position mortally 3.00 x normal
*
* Note, this is a hack because it depends on the particular
* values of the POSITION_XXX constants.
*/
if (GET_POS(victim) < POS_FIGHTING)
dam *= 1 + (POS_FIGHTING - GET_POS(victim)) / 3;
/* at least 1 hp damage min per hit */
dam = MAX(1, dam);
if (type == SKILL_BACKSTAB) {
dam *= backstab_mult(GET_LEVEL(ch));
damage(ch, victim, dam, SKILL_BACKSTAB);
} else
damage(ch, victim, dam, w_type);
}
/* check if the victim has a hitprcnt trigger */
hitprcnt_mtrigger(victim);
}
/* control the fights going on. Called every 2 seconds from comm.c. */
void perform_violence(void)
{
struct char_data *ch;
int attacks = 1, i;
for (ch = combat_list; ch; ch = next_combat_list) {
next_combat_list = ch->next_fighting;
attacks = 1; /* needs to be here or all char have same attacks */
if (IS_NPC(ch) && (GET_MOB_ATTACKS(ch) > 0))
attacks = GET_MOB_ATTACKS(ch);
if (!IS_NPC(ch) && (GET_DEX(ch) >= 1))
GET_PC_ATTACKS(ch) = 1;
if (!IS_NPC(ch) && (GET_DEX(ch) >= 20))
GET_PC_ATTACKS(ch) = 2;
if (!IS_NPC(ch) && (GET_DEX(ch) >= 30))
GET_PC_ATTACKS(ch) = 3;
if (!IS_NPC(ch) && (GET_DEX(ch) >= 40))
GET_PC_ATTACKS(ch) = 4;
if (!IS_NPC(ch) && (GET_DEX(ch) >= 50))
GET_PC_ATTACKS(ch) = 5;
if (!IS_NPC(ch) && (GET_DEX(ch) >= 60))
GET_PC_ATTACKS(ch) = 6;
if (!IS_NPC(ch) && (GET_DEX(ch) >= 70))
GET_PC_ATTACKS(ch) = 7;
if (!IS_NPC(ch) && (GET_DEX(ch) >= 80))
GET_PC_ATTACKS(ch) = 8;
if (!IS_NPC(ch) && (GET_DEX(ch) >= 90))
GET_PC_ATTACKS(ch) = 9;
if (!IS_NPC(ch) && (GET_DEX(ch) >= 100))
GET_PC_ATTACKS(ch) = 10;
if (!IS_NPC(ch) && (GET_PC_ATTACKS(ch) > 0))
attacks = GET_PC_ATTACKS(ch);
/* added to ensure that attacks are > 0 if not defaults to 1 */
if (FIGHTING(ch) == NULL || ch->in_room != FIGHTING(ch)->in_room) {
stop_fighting(ch);
continue;
}
if (IS_NPC(ch)) {
if (GET_MOB_WAIT(ch) > 0) {
GET_MOB_WAIT(ch) -= PULSE_VIOLENCE;
continue;
}
GET_MOB_WAIT(ch) = 0;
if (GET_POS(ch) < POS_FIGHTING) {
GET_POS(ch) = POS_FIGHTING;
act("$n scrambles to $s feet!", TRUE, ch, 0, 0, TO_ROOM);
}
}
if (GET_POS(ch) < POS_FIGHTING) {
send_to_char("You can't fight while sitting!!\r\n", ch);
continue;
}
for (i = 0; i < attacks; i++)
hit(ch, FIGHTING(ch), TYPE_UNDEFINED);
/* XXX: Need to see if they can handle "" instead of NULL. */
if (MOB_FLAGGED(ch, MOB_SPEC) && mob_index[GET_MOB_RNUM(ch)].func != NULL)
(mob_index[GET_MOB_RNUM(ch)].func) (ch, ch, 0, "");
}
}