/* ************************************************************************
* File: fight.c Part of CircleMUD *
* Usage: Combat system *
* *
* All rights reserved. See license.doc for complete information. *
* *
* Copyright (C) 1993 by the Trustees of the Johns Hopkins University *
* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. *
************************************************************************ */
/* Archipelago changes by Alastair J. Neil Copyright (C) 1993, 94, 95, 96 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "handler.h"
#include "interpreter.h"
#include "db.h"
#include "spells.h"
#include "limits.h"
#include "screen.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;
extern int pk_allowed; /* see config.c */
extern int auto_save; /* see config.c */
extern int pulse;
extern struct dual_list_type attack_hit_text[];
extern struct index_data *mob_index;
extern struct str_app_type str_app[];
extern struct dex_app_type dex_app[];
/* External procedures */
void poison(struct char_data *ch, struct char_data *victim, struct obj_data *obj);
char *fread_string(FILE *fl, char *error);
void stop_follower(struct char_data *ch);
ACMD(do_flee);
void hit(struct char_data *ch, struct char_data *victim, int type);
void forget(struct char_data *victim);
void remember(struct char_data *ch, struct char_data *victim);
void make_scraps(struct obj_data *obj, int r_num);
void scrap_item(struct obj_data *obj);
void loose_level(struct char_data *ch);
bool is_two_handed(struct char_data *ch, struct obj_data *weapon);
int spell_lev(struct char_data *caster, int spell);
void add_event(int plse, int event, int inf1, int inf2, int inf3
, int inf4, char *arg, void *subj, void *vict);
void botch_kick(struct char_data *ch, struct char_data *vict);
int stress_die(void);
int get_prof(struct char_data *ch, int *w_type, int type);
void damage_obj_corpse(struct obj_data *corpse, int spell_type, int dam);
void spell_damage_equipment(struct char_data *ch, struct char_data *vict, int spell_no, int dam);
int condition_bonus(struct char_data *ch);
int encumberance_level(struct char_data *ch);
int combat_bonus(struct char_data *ch);
int fatigue_bonus(struct char_data *ch);
int bodylevel_bonus(struct char_data *ch);
int compute_multi_attacks(struct char_data *ch, struct obj_data *weilded, int type);
struct obj_data *die(struct char_data *ch);
/* Weapon attack texts */
/* The Fight related routines */
int combat_bonus(struct char_data *ch)
{
int level=0;
level -= (3*encumberance_level(ch))/4;
level += fatigue_bonus(ch);
level += bodylevel_bonus(ch);
level += condition_bonus(ch);
return(level);
}
int encumberance_level(struct char_data *ch)
{
int weight_worn=0,i, level=0, items_worn = 0;
for (i=0; i < MAX_WEAR; i++)
if (ch->equipment[i]){
weight_worn += ch->equipment[i]->obj_flags.weight;
items_worn++;
}
weight_worn /= 2;
items_worn /= 3;
if ((IS_CARRYING_W(ch) + weight_worn) > (4*CAN_CARRY_W(ch))/5)
level = 8;
else if ((IS_CARRYING_W(ch) + weight_worn) > (3*CAN_CARRY_W(ch))/5)
level = 6;
else if ((IS_CARRYING_W(ch) + weight_worn) > (2*CAN_CARRY_W(ch))/5)
level = 4;
else if ((IS_CARRYING_W(ch) + weight_worn) > (CAN_CARRY_W(ch))/5)
level = 2;
if ((IS_CARRYING_N(ch) + items_worn) > (4*CAN_CARRY_N(ch))/5)
level += 4;
else if ((IS_CARRYING_N(ch) + items_worn) > (3*CAN_CARRY_N(ch))/5)
level += 3;
else if ((IS_CARRYING_N(ch) + items_worn) > (2*CAN_CARRY_N(ch))/5)
level += 2;
else if ((IS_CARRYING_N(ch) + items_worn) > (CAN_CARRY_N(ch))/5)
level += 1;
level /= 2;
return(level);
}
int condition_bonus(struct char_data *ch)
{
int level=0;
if (GET_COND(ch,DRUNK) > 19)
level = -10;
else if (GET_COND(ch,DRUNK) > 14)
level = -7;
else if (GET_COND(ch,DRUNK) > 10)
level = -4;
else if (GET_COND(ch,DRUNK) > 5)
level = -2;
else if (GET_COND(ch,DRUNK) > 0)
level = -1;
if (GET_COND(ch,THIRST) == 0)
level -= 1;
if (GET_COND(ch,FULL) == 0)
level -= 2;
return(level);
}
int fatigue_bonus(struct char_data *ch)
{
int max;
max = GET_MAX_MOVE(ch);
if (GET_MOVE(ch) > (3*max)/5)
return(0);
else if (GET_MOVE(ch) > (2*max)/5)
return(-1);
else if (GET_MOVE(ch) > (max)/5)
return(-3);
else
return(-5);
}
int bodylevel_bonus(struct char_data *ch)
{
int max;
max = GET_MAX_HIT(ch);
if (GET_HIT(ch) > (3*max)/5)
return(0);
else if (GET_HIT(ch) > (2*max)/5)
return(-1);
else if (GET_HIT(ch) > (max)/5)
return(-3);
else
return(-5);
}
void scrap_item(struct obj_data *obj)
{
struct obj_data *ot;
act("$p falls to the ground in scraps!",FALSE,0,obj,0,TO_ROOM);
if(obj->obj_flags.type_flag == ITEM_CONTAINER &&
obj->contains){
send_to_room("It's contents fall on the ground.\r\n",obj->in_room,FALSE);
for(ot = obj->contains;ot;ot = obj->contains){
obj_from_obj(ot);
obj_to_room(ot,obj->in_room, FALSE);
}
}
make_scraps(obj,obj->in_room);
extract_obj(obj,0);
}
void appear(struct char_data *ch)
{
act("$n slowly fades into existence.", FALSE, ch, 0, 0, TO_ROOM);
if (affected_by_spell(ch, SPELL_INVIS3))
affect_from_char(ch, SPELL_INVIS3);
REMOVE_BIT(ch->specials.affected_by, AFF_INVISIBLE);
}
void load_messages(void)
{
FILE * f1;
struct message_type *messages;
int i, type;
char chk[100];
if (!(f1 = fopen(MESS_FILE, "r"))) {
sprintf(buf2, "Error reading combat message file %s", MESS_FILE);
perror(buf2);
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) {
logg("SYSERR: Too many combat messages.");
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;
sprintf(buf2, "combat message #%d in file '%s'", i, MESS_FILE);
messages->die_msg.attacker_msg = fread_string(f1, buf2);
messages->die_msg.victim_msg = fread_string(f1, buf2);
messages->die_msg.room_msg = fread_string(f1, buf2);
messages->miss_msg.attacker_msg = fread_string(f1, buf2);
messages->miss_msg.victim_msg = fread_string(f1, buf2);
messages->miss_msg.room_msg = fread_string(f1, buf2);
messages->hit_msg.attacker_msg = fread_string(f1, buf2);
messages->hit_msg.victim_msg = fread_string(f1, buf2);
messages->hit_msg.room_msg = fread_string(f1, buf2);
messages->god_msg.attacker_msg = fread_string(f1, buf2);
messages->god_msg.victim_msg = fread_string(f1, buf2);
messages->god_msg.room_msg = fread_string(f1, buf2);
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) <= -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 if (GET_HIT(victim) <= 0)
GET_POS(victim) = POSITION_STUNNED;
else
GET_POS(victim) = POSITION_RESTING;
}
void check_killer(struct char_data *ch, struct char_data *vict)
{
if (!PLR_FLAGGED(vict, PLR_KILLER) && !PLR_FLAGGED(vict, PLR_THIEF)
&& !PLR_FLAGGED(ch, PLR_KILLER) && !IS_NPC(ch) && !IS_NPC(vict) &&
(ch != vict)) {
/* don't auto set killer flag
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, LEVEL_BUILDER, 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;
assert(!ch->specials.fighting);
ch->next_fighting = combat_list;
combat_list = ch;
ch->specials.fighting = vict;
if (!pk_allowed)
check_killer(ch, vict);
}
/* 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) {
logg("SYSERR: Char fighting not found Error (fight.c, stop_fighting)");
abort();
}
tmp->next_fighting = ch->next_fighting;
}
ch->next_fighting = 0;
ch->specials.fighting = 0;
update_pos(ch);
}
struct obj_data *make_corpse(struct char_data *ch)
{
struct obj_data *corpse, *o;
struct obj_data *money;
int i;
bool undead = FALSE;
extern int max_npc_corpse_time, max_pc_corpse_time;
struct obj_data *create_money(int amount);
corpse = read_object(1274, VIRTUAL, 1);
corpse->obj_flags.value[5] = GET_SIZE(ch);
corpse->obj_flags.value[4] = GET_LEVEL(ch);
if (IS_UNDEAD(ch))
undead = TRUE;
corpse->obj_flags.value[2] = GET_IDNUM(ch);
if (IS_NPC(ch))
corpse->obj_flags.value[7] = mob_index[ch->nr].virtual;
corpse->in_room = NOWHERE;
if (undead)
sprintf(buf2,"dust pile");
else
sprintf(buf2,"corpse %s",GET_NAME(ch));
corpse->name = str_dup(buf2);
if (undead)
sprintf(buf2, "A pile of dust is lying here.");
else
sprintf(buf2, "The corpse of %s is lying here.", GET_NAME(ch));
corpse->description = str_dup(buf2);
if (undead)
sprintf(buf2, "a pile of dust");
else
sprintf(buf2, "the corpse of %s", GET_NAME(ch));
corpse->short_description = str_dup(buf2);
/* if (!undead){
sprintf(buf2, "The stench rising from the corpse makes your gorge rise.");
corpse->action_description = str_dup(buf2);
}*/
corpse->contains = ch->inventory;
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;
}
corpse->obj_flags.wear_flags = ITEM_TAKE;
corpse->obj_flags.extra_flags = ITEM_NODONATE;
corpse->obj_flags.value[0] = 0; /* You can't store stuff in a corpse */
if (undead)
corpse->obj_flags.value[3] = -2; /* undead corpse identifyer */
else
corpse->obj_flags.value[3] = -1; /* corpse identifyer */
corpse->obj_flags.type_flag = ITEM_CONTAINER;
corpse->obj_flags.weight = GET_WEIGHT(ch) + IS_CARRYING_W(ch);
corpse->obj_flags.cost_per_day = 10;
if (IS_NPC(ch)){
if (undead)
corpse->obj_flags.timer = max_npc_corpse_time/2;
else
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->inventory = 0;
IS_CARRYING_N(ch) = 0;
IS_CARRYING_W(ch) = 0;
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, FALSE);
return(corpse);
}
void make_scraps(struct obj_data *obj, int r_num)
{
struct obj_data *scraps;
CREATE(scraps, struct obj_data, 1);
clear_object(scraps);
scraps->item_number = NOWHERE;
scraps->in_room = NOWHERE;
scraps->name = str_dup("scraps");
sprintf(buf2, "Scraps from %s are lying here.", obj->short_description);
scraps->description = str_dup(buf2);
sprintf(buf2, "scraps from %s", obj->short_description);
scraps->short_description = str_dup(buf2);
scraps->obj_flags.type_flag = ITEM_TRASH;
scraps->obj_flags.wear_flags = ITEM_TAKE;
scraps->obj_flags.extra_flags = ITEM_NODONATE;
scraps->obj_flags.value[0] = 0;
scraps->obj_flags.value[3] = 0;
scraps->obj_flags.weight = 1;
scraps->obj_flags.cost_per_day = 100000;
scraps->obj_flags.timer = 3;
obj_to_room(scraps,r_num, FALSE);
}
/* When ch kills victim */
void change_alignment(struct char_data *ch, struct char_data *victim)
{
#define GT_LEVEL(ch) (((ch)->player.level > 0) ? (ch)->player.level : 1)
int align, lev_dif;
align = GET_ALIGNMENT(ch) - GET_ALIGNMENT(victim);
lev_dif = GT_LEVEL(ch) - GT_LEVEL(victim);
if (GET_ALIGNMENT(victim) < 0) {
if (align > 650) {
if (lev_dif > 0)
GET_ALIGNMENT(ch) =
MIN(1000, GET_ALIGNMENT(ch) + GT_LEVEL(victim)*
(((align - 650) / 8) +
((1000 - GET_ALIGNMENT(victim))/ 5))
/ GT_LEVEL(ch));
else
GET_ALIGNMENT(ch) = MIN(1000, GET_ALIGNMENT(ch) + GT_LEVEL(ch)*
(((align - 650) / 8) +
((1000 - GET_ALIGNMENT(victim))/ 5))
/ GT_LEVEL(victim));
}
}
else {
if (lev_dif > 0)
GET_ALIGNMENT(ch) = MIN(1000, GET_ALIGNMENT(ch) - GT_LEVEL(victim)*
(GET_ALIGNMENT(victim) / 8)
/GT_LEVEL(ch));
else
GET_ALIGNMENT(ch) = MIN(1000, GET_ALIGNMENT(ch) - GT_LEVEL(ch)*
(GET_ALIGNMENT(victim) / 8)
/GT_LEVEL(victim));
}
if (align < -650) {
if (lev_dif > 0)
GET_ALIGNMENT(ch) = MAX(-1000, GET_ALIGNMENT(ch)
+ GT_LEVEL(victim)*
(((align + 650) / 8)
+ ((-1000 + GET_ALIGNMENT(victim))
/ 5))/GT_LEVEL(ch));
else
GET_ALIGNMENT(ch) = MAX(-1000, GET_ALIGNMENT(ch)
+ GT_LEVEL(ch)*
(((align + 650) / 8)
+ ((-1000 + GET_ALIGNMENT(victim))
/ 5))/GT_LEVEL(victim));
}
else {
if (lev_dif > 0)
GET_ALIGNMENT(ch) = MAX(-1000, GET_ALIGNMENT(ch) -
GT_LEVEL(victim)*
(GET_ALIGNMENT(victim) / 8)
/ GT_LEVEL(ch));
else
GET_ALIGNMENT(ch) = MAX(-1000, GET_ALIGNMENT(ch) -
GT_LEVEL(ch)*
(GET_ALIGNMENT(victim) / 8)
/ GT_LEVEL(victim));
}
GET_ALIGNMENT(ch) = MIN(1000, GET_ALIGNMENT(ch));
GET_ALIGNMENT(ch) = MAX(-1000, GET_ALIGNMENT(ch));
}
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 = real_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;
}
}
}
struct obj_data *raw_kill(struct char_data *ch)
{
struct obj_data *corpse;
if (ch->specials.fighting)
stop_fighting(ch);
while (ch->affected)
affect_remove(ch, ch->affected);
death_cry(ch);
corpse = make_corpse(ch);
extract_char(ch, TRUE);
return(corpse);
}
struct obj_data *die(struct char_data *ch)
{
struct obj_data *corpse;
gain_exp(ch, -(GET_EXP(ch) / 5),0);
gain_social_standing(ch, ch->specials.fighting, MODE_DIE);
GET_COND(ch, FULL) = 24;
GET_COND(ch, THIRST) = 24;
GET_COND(ch, DRUNK) = 0;
if (!IS_NPC(ch))
REMOVE_BIT(PLR_FLAGS(ch), PLR_KILLER | PLR_THIEF);
if (!IS_NPC(ch))
forget(ch);
corpse = raw_kill(ch);
return(corpse);
}
void group_gain(struct char_data *ch, struct char_data *victim)
{
int no_members, share, tot_exp, my_share;
struct char_data *k;
struct follow_type *f;
if (!(k = ch->master))
k = ch; /* k is the leader */
if (IS_AFFECTED(k, AFF_GROUP) &&
(k->in_room == victim->in_room)){
no_members = 1;
tot_exp = GET_LEVEL(k);}
else{
no_members = 0;
tot_exp = 0;}
for (f = k->followers; f; f = f->next)
if (IS_AFFECTED(f->follower, AFF_GROUP) &&
(f->follower->in_room == victim->in_room)){
no_members++;
tot_exp += GET_LEVEL(f->follower);
}
share = GET_EXP(victim);
if (IS_AFFECTED(k, AFF_GROUP) &&
(k->in_room == victim->in_room)) {
my_share = share*GET_LEVEL(k)/tot_exp;
my_share = MIN(6000*GET_LEVEL(ch),my_share);
sprintf(buf2, "You receive your share of experience -- %d points.",
my_share);
act(buf2, FALSE, k, 0, 0, TO_CHAR);
gain_exp(k, my_share,1);
gain_social_standing(ch, victim, MODE_GROUP_KILL);
change_alignment(k, victim);
if (auto_save) {
/* save 25% of the time to avoid lag in big groups */
if (!(number(0, 3)))
save_char(k, NOWHERE);
}
}
for (f = k->followers; f; f = f->next) {
if (IS_AFFECTED(f->follower, AFF_GROUP) &&
(f->follower->in_room == victim->in_room)) {
my_share = share*GET_LEVEL(f->follower)/tot_exp;
share = MIN(3000*GET_LEVEL(f->follower),share);
sprintf(buf2, "You receive your share of experience -- %d points.", my_share);
act(buf2, FALSE, f->follower, 0, 0, TO_CHAR);
gain_exp(f->follower, my_share,1);
gain_social_standing(ch, victim, MODE_GROUP_KILL);
change_alignment(f->follower, victim);
if (auto_save) {
/* save only 25% of the time to avoid lag in big groups */
if (!(number(0, 3)))
save_char(f->follower, NOWHERE);
}
}
}
}
char *replace_string(char *str, char *weapon_singular, char *weapon_plural)
{
static char buf[256];
char *cp;
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);
}
void dam_message(int dam, struct char_data *ch, struct char_data *victim,
int w_type,int hit_loc)
{
struct obj_data *wield;
char *buf;
int msgnum;
static char *hit_locs1[] = {
"to the body.",
"to the arms.",
"to the legs.",
"to the head.",
};
static char *hit_locs2[] = {
"in the body.",
"in the arms.",
"in the legs.",
"in the head.",
};
static struct dam_weapon_type {
char *to_room;
char *to_char;
char *to_victim;
} dam_weapons[] = {
/* use #w for singular (i.e. "slash") and #W for plural (i.e. "slashes") */
{ "$n misses $N with $s #w ", /* 0: 0 */
"You miss $N with your #w ",
"$n misses you with $s #w " },
{ "$n bruises $N with $s #w ", /* 1: 1..2 */
"You bruise $N as you #w $M ",
"$n bruises you as $e #W you " },
{ "$n barely #W $N ", /* 2: 3..4 */
"You barely #w $N ",
"$n barely #W you " },
{ "$n #W $N ", /* 3: 5..6 */
"You #w $N ",
"$n #W you " },
{ "$n #W $N hard ", /* 4: 7..10 */
"You #w $N hard ",
"$n #W you hard " },
{ "$n #W $N very hard ", /* 5: 11..14 */
"You #w $N very hard ",
"$n #W you very hard " },
{ "$n #W $N extremely hard ", /* 6: 15..18 */
"You #w $N extremely hard ",
"$n #W you extremely hard " },
{ "$n massacres $N with $s #w ", /* 7: 19..25 */
"You massacre $N with your #w ",
"$n massacres you with $s #w " },
{ "$n pulverises $N with $s #w ", /* 8: 25..32 */
"You pulverise $N with your #w ",
"$n pulverises you with $s #w " },
{ "$n obliterates $N with $s #w ", /* 9: 32..45 */
"You obliterate $N with your #w ",
"$n obliterates you with $s #w " },
{ "$n annihilates $N with $s #w ", /* 10: >45 */
"You annihilate $N with your #w ",
"$n annihilates you with $s #w " }
};
w_type -= TYPE_HIT; /* Change to base of table with text */
wield = ch->equipment[WIELD];
if (dam == 0) msgnum = 0;
else if (dam <= 2) msgnum = 1;
else if (dam <= 4) msgnum = 2;
else if (dam <= 6) msgnum = 3;
else if (dam <= 10) msgnum = 4;
else if (dam <= 14) msgnum = 5;
else if (dam <= 19) msgnum = 6;
else if (dam <= 25) msgnum = 7;
else if (dam <= 32) msgnum = 8;
else if (dam <= 45) msgnum = 9;
else msgnum = 10;
/* damage message to onlookers */
buf = replace_string(dam_weapons[msgnum].to_room,
attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
if (hit_loc >= 0 && hit_loc <= 3)
if (msgnum != 0)
strcat(buf,hit_locs2[hit_loc]);
else
strcat(buf,hit_locs1[hit_loc]);
act(buf, FALSE, ch, wield, victim, TO_NOTVICT);
/* damage message to damager */
buf = replace_string(
dam_weapons[msgnum].to_char,
attack_hit_text[w_type].singular,
attack_hit_text[w_type].plural);
if (hit_loc >= 0 && hit_loc <= 3)
if (msgnum != 0)
strcat(buf,hit_locs2[hit_loc]);
else
strcat(buf,hit_locs1[hit_loc]);
if (dam > 0 || !PRF_FLAGGED(ch, PRF_COMPACT))
act(buf, FALSE, ch, wield, victim, TO_CHAR);
/* damage message to damagee */
buf = replace_string(
dam_weapons[msgnum].to_victim,
attack_hit_text[w_type].singular,
attack_hit_text[w_type].plural);
if (hit_loc >= 0 && hit_loc <= 3)
if (msgnum != 0)
strcat(buf,hit_locs2[hit_loc]);
else
strcat(buf,hit_locs1[hit_loc]);
if (dam > 0 || !PRF_FLAGGED(victim, PRF_COMPACT))
act(buf, FALSE, ch, wield,victim, TO_VICT);
}
void damage(struct char_data *ch, struct char_data *victim, int dam,
int attacktype,int hit_loc, bool saved)
{
struct message_type *messages;
struct msg_type *diemessage;
struct obj_data *obj_tmp;
int i, j, nr, exp,where,atcktype,bhead=0,w_type;
char bufer[100];
char bufer2[100];
char *buf;
ACMD(do_gen_com);
ACMD(do_wake);
static struct behead_message_type {
char *to_room;
char *to_char;
char *to_victim;
} behead_messages =
{ "$n severs $N's head with $s mighty #w.",
"You sever $N's head with a mighty #w.",
"Everything suddenly goes black."};
if (GET_POS(victim) <= POSITION_DEAD) {
logg("SYSERR: Attempt to damage a corpse.");
return; /* -je, 7/7/92 */
}
if (!IS_NPC(ch) && !pk_allowed && !IS_NPC(victim) && victim != ch){
act("You don't think attacking $N such a good idea.",
FALSE,ch,0,victim,TO_CHAR);
return;
}
assert(GET_POS(victim) > POSITION_DEAD);
/* You can't damage an immortal! */
if ((GET_LEVEL(victim) >= LEVEL_BUILDER) && !IS_NPC(victim))
dam = 0;
if (GET_POS(victim) == POSITION_SLEEPING){
if (IS_AFFECTED(victim, AFF_SLEEP))
affect_from_char(victim, SPELL_SLEEP);
do_wake(victim,"",0,0);
}
if (IS_AFFECTED(ch, AFF_SNEAK)
&& (GET_SKILL(ch, SKILL_SNEAK) < number(0,31)))
affect_from_char(ch, SKILL_SNEAK);
if (ch->specials.mount && (GET_SKILL(ch, SKILL_CAVALRY) < number(0,31)))
if (throw_rider(ch))
return;
if (victim != ch) {
if (GET_POS(ch) > POSITION_STUNNED) {
if (!(ch->specials.fighting))
set_fighting(ch, victim);
if (IS_NPC(ch) && IS_NPC(victim) && victim->master &&
((GET_INT(ch) > 10 && !number(0,10))
|| (GET_INT(ch) <= 10 && GET_INT(ch) > 7 && !number(0,70))
|| (GET_INT(ch) <= 7 && GET_INT(ch) > 3 && !number(0,120)) )
&& CAN_SEE(ch, victim->master)
&& IS_AFFECTED(victim, AFF_CHARM) &&
(victim->master->in_room == ch->in_room)) {
if (ch->specials.fighting)
stop_fighting(ch);
act("$n realises who's the boss.",FALSE,ch,0,0,TO_ROOM);
hit(ch, victim->master, TYPE_UNDEFINED);
return;
}
}
if (GET_POS(victim) > POSITION_STUNNED) {
if (!(victim->specials.fighting))
set_fighting(victim, ch);
if (IS_NPC(victim) && IS_SET(victim->specials2.act, MOB_MEMORY) &&
!IS_NPC(ch) && (GET_LEVEL(ch) < LEVEL_BUILDER))
remember(victim, ch);
}
}
if (victim->master == ch)
stop_follower(victim);
/* if (IS_AFFECTED(ch, AFF_INVISIBLE))
appear(ch);*/
if (IS_AFFECTED(victim, AFF_SANCTUARY) && attacktype > MAX_SPELL_NO)
dam = dam / 2; /* 1/2 physical damage when sanctuary */
if (!pk_allowed) {
check_killer(ch, victim);
if (PLR_FLAGGED(ch, PLR_KILLER))
dam = 0;
}
dam = MAX(dam, 0);
w_type = attacktype -TYPE_HIT;
if (!IS_NPC(ch) && victim != ch && hit_loc == 3
&& (attacktype == TYPE_SLASH || attacktype == TYPE_CLEAVE))
bhead = GET_SKILL(ch,SKILL_BEHEAD)*GET_LEVEL(ch)/MAX(2,GET_LEVEL(victim));
if (bhead && (number(GET_LEVEL(victim),600) - GET_LEVEL(ch)/2) < bhead/10)
{
buf = replace_string(behead_messages.to_char,
attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
act(buf, FALSE, ch, ch->equipment[WIELD], victim, TO_CHAR);
buf = replace_string(behead_messages.to_room,
attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
act(buf, FALSE, ch, ch->equipment[WIELD], victim, TO_NOTVICT);
buf = replace_string(behead_messages.to_victim,
attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
act(buf, FALSE, ch, ch->equipment[WIELD], victim, TO_VICT);
GET_HIT(victim) = -100;
update_pos(victim);
}
else if (GET_POS(victim) > POSITION_DEAD){
GET_HIT(victim) -= dam;
if (ch != victim && !IS_NPC(ch))
gain_exp(ch, dam,0);
update_pos(victim);
if (!ch->equipment[WIELD] && !ch->specials.attack_type)
atcktype = TYPE_HIT;
else
atcktype = attacktype;
for (i = 0; i < MAX_MESSAGES; i++)
{
if (fight_messages[i].a_type == atcktype){
diemessage = &fight_messages[i].msg->die_msg;
break;}
}
if ((attacktype >= TYPE_HIT) && (attacktype <= TYPE_WHIP)) {
if (GET_POS(victim) == POSITION_DEAD
&& *diemessage->attacker_msg)
{
act(diemessage->attacker_msg,
FALSE, ch, ch->equipment[WIELD], victim, TO_CHAR);
act(diemessage->victim_msg,
FALSE, ch, ch->equipment[WIELD], victim, TO_VICT);
act(diemessage->room_msg,
FALSE, ch, ch->equipment[WIELD], victim, TO_NOTVICT);
}
else {
dam_message(dam, ch, victim, attacktype, hit_loc);
if (IS_MOB(ch) && MOB_FLAGGED(ch, MOB_POISONOUS)
&& (attacktype >= TYPE_CLAW )){
if (dam && (number(0,10) < MIN(1,GET_LEVEL(ch)/10)))
poison(ch, victim, 0);
}
}
}
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) >= LEVEL_BUILDER)) {
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 (!saved) {
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 { /* tagert saved */
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);
}
}
}
}
/* chance of damaging equipment if blow > 25 points damage) */
if ((IS_NPC(ch) && dam >= 30) || (!IS_NPC(ch) && dam >= 35))
if (dam < number(1,250) && ((attacktype >= TYPE_HIT)
&& (attacktype <= TYPE_WHIP)))
{
where = number(0,17);
if (victim->equipment[where])
{
obj_tmp = unequip_char(victim,where);
if (IS_SET(obj_tmp->obj_flags.extra_flags,ITEM_FRAGILE))
obj_tmp->obj_flags.value[4] += number(1,11);
else
obj_tmp->obj_flags.value[4] += number(0,4);
if (obj_tmp->obj_flags.value[4] >10)
{
obj_to_room(obj_tmp,victim->in_room, FALSE);
scrap_item(obj_tmp);
}
else if (where != WIELD) {
act("$p is crushed.",FALSE,
victim,obj_tmp,0,TO_ROOM);
act("$p is crushed.",FALSE,
victim,obj_tmp,0,TO_CHAR);
equip_char(victim,obj_tmp,where);
}
else
equip_char(victim,obj_tmp,where);
}
}
}
/* Use send_to_char -- act() doesn't send message if you are DEAD. */
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);
send_to_char("You are mortally wounded, and will die soon, if not aided.\r\n", victim);
break;
case POSITION_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 POSITION_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 POSITION_DEAD:
act("$n is dead! R.I.P.", TRUE, victim, 0, 0, TO_ROOM);
send_to_char("You are dead! Sorry...\r\n", victim);
break;
default: /* >= POSITION SLEEPING */
if (dam > (GET_MAX_HIT(victim) / 5))
act("That Really did HURT!", FALSE, victim, 0, 0, TO_CHAR);
if (GET_HIT(victim) < (GET_MAX_HIT(victim) / 5)) {
act("You wish that your wounds would stop BLEEDING so much!", FALSE, victim, 0, 0, TO_CHAR);
if (IS_NPC(victim)) {
if (IS_SET(victim->specials2.act, MOB_WIMPY))
do_flee(victim, "", 0, 0);
}
}
if (!IS_NPC(victim) && WIMP_LEVEL(victim) && victim != ch &&
GET_HIT(victim) < WIMP_LEVEL(victim)) {
send_to_char("You panic, and attempt to flee!\r\n", victim);
if (GET_POS(victim) <= POSITION_STANDING)
do_stand(victim, "", 0, 0);
do_flee(victim, "", 0, 0);
}
break;
}
if (!IS_NPC(victim) && !(victim->desc))
do_flee(victim, "", 0, 0);
if (!AWAKE(victim))
if (victim->specials.fighting)
stop_fighting(victim);
if (GET_POS(victim) == POSITION_DEAD) {
if (IS_NPC(victim) || (victim->desc && victim != ch))
if (IS_AFFECTED(ch, AFF_GROUP)) {
group_gain(ch, victim);
}
else {
exp = GET_EXP(victim);
exp = MIN(exp,3000*GET_LEVEL(ch));
sprintf(buf2, "You receive %d experience points.\r\n", exp);
send_to_char(buf2, ch);
gain_exp(ch, exp,1);
gain_social_standing(ch, victim, MODE_KILL);
change_alignment(ch, victim);
if (auto_save) {
/* individuals saved 50% of the time to avoid lag */
if (!(number(0, 1)))
save_char(ch, NOWHERE);
}
}
if (!IS_NPC(victim)) {
if (IS_NPC(ch)){
sprintf(bufer,"Ha! %s died too easily!",GET_NAME(victim));
sprintf(bufer2,"I need a tougher challenge!");
sprintf(buf2, "%s killed by %s at %s", GET_NAME(victim), GET_NAME(ch),
world[victim->in_room].name);
mudlog(buf2, BRF, LEVEL_BUILDER, TRUE);
}
}
spell_damage_equipment(ch, victim, attacktype, dam);
if (!IS_NPC(victim))
if (IS_NPC(ch) &&
(GET_LEVEL(ch) > 10 || IS_SET(ch->specials2.act,MOB_CAN_SPEAK))){
do_gen_com(ch,bufer,0,5);
do_gen_com(ch,bufer2,0,5);
}
obj_tmp = die(victim);
}
}
int get_prof(struct char_data *ch, int *w_type, int type)
{
int prof;
struct obj_data *wielded = 0;
if (type == SKILL_DUAL_WIELD) {
if (ch->equipment[HOLD] &&
(ch->equipment[HOLD]->obj_flags.type_flag == ITEM_WEAPON))
wielded = ch->equipment[HOLD];
}
else {
if (ch->equipment[WIELD] &&
(ch->equipment[WIELD]->obj_flags.type_flag == ITEM_WEAPON))
wielded = ch->equipment[WIELD];
}
if (wielded) {
switch (wielded->obj_flags.value[3]) {
case 0:
case 1:
*w_type = TYPE_BLUDGEON;
if (!IS_NPC(ch)){
prof = PROF_CLUB;
if (is_two_handed(ch,wielded))
prof++;}
break;
case 2:
*w_type = TYPE_PIERCE;
if (!IS_NPC(ch)){
prof = PROF_DAGGER;
if (is_two_handed(ch,wielded))
prof = PROF_SWORD +1;
else if (GET_SIZE(ch) > GET_OBJ_SIZE(wielded) +1)
prof = PROF_SWORD;}
break;
case 3:
*w_type = TYPE_SLASH;
if (!IS_NPC(ch)){
prof = PROF_SWORD;
if (is_two_handed(ch,wielded))
prof++;}
break;
case 4:
*w_type = TYPE_SMITE;
if (!IS_NPC(ch)){
prof = PROF_HAMMER;
if (is_two_handed(ch,wielded))
prof++; }
break;
case 5:
*w_type = TYPE_CLEAVE;
if (!IS_NPC(ch)){
prof = PROF_AXE;
if (is_two_handed(ch,wielded))
prof++; }
break;
case 6:
*w_type = TYPE_NO_BS_PIERCE;
if (!IS_NPC(ch)){
prof = PROF_SPEAR;
if (!is_two_handed(ch,wielded))
prof = PROF_DAGGER;}
break;
case 7:
*w_type = TYPE_CLAW;
if (!IS_NPC(ch))
prof = PROF_CLAW;
break;
case 8:
*w_type = TYPE_BITE;
break;
case 9:
*w_type = TYPE_STING;
break;
case 10:
*w_type = TYPE_CRUSH;
break;
case 11:
*w_type = TYPE_PECK;
break;
case 12:
*w_type = TYPE_BUTT;
break;
case 13:
*w_type = TYPE_KICK;
break;
case 14:
*w_type = TYPE_WHIP;
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;
}
return prof;
}
int compute_multi_attacks(struct char_data *ch, struct obj_data *wielded, int type)
{
int raw_multi, nattacks=0, weap_multi=0, weap_weight=0, inatt=0;
if (type == SKILL_BACKSTAB)
return(1);
raw_multi = GET_LEVEL(ch)/2; /* raw multiple attach percent chance */
raw_multi = (raw_multi*GET_DEX(ch)*GET_DEX(ch))/255; /* dexterity bonus */
if (wielded) {
weap_weight = GET_OBJ_WEIGHT(wielded)/10;
if (is_two_handed(ch,wielded))
weap_weight /= 2;
if (weap_weight >= 3*GET_STR(ch)/4)
weap_multi = (30 - (40*weap_weight)/GET_STR(ch)) + 30;
else
weap_multi = ((40*weap_weight)/GET_STR(ch) - 30) + 30;
weap_multi = MAX(1,weap_multi);
weap_multi = MIN(9,weap_multi);
raw_multi *= weap_multi;
raw_multi /= 10.;
}
else {
if (IS_NPC(ch))
raw_multi += (3*GET_LEVEL(ch))/20;
else
raw_multi += GET_SKILL(ch, SKILL_FISTICUFFS);
}
/* ok now determine how many attacks we can have max 6 */
raw_multi = MIN(149,raw_multi);
nattacks = 1;
for (inatt=0;inatt<9;inatt++){
if (number(0,150) < raw_multi) /*probability of more attacks goes down*/
nattacks++;
raw_multi *= 2;
raw_multi /= 3;
}
if (type == SKILL_DUAL_WIELD)
if (IS_NPC(ch))
nattacks = (nattacks*(GET_LEVEL(ch)/6))/30;
else
nattacks = (2*nattacks*GET_SKILL(ch, SKILL_DUAL_WIELD))/60;
nattacks = MIN(10,nattacks);
return(nattacks);
}
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,hit_loc,hit_location,die,num;
int victim_ac, thaco, weap_weight,weap_multi;
int dam,raw_multi,nattacks,inatt,icnt,damg,prof=0, prof_factor=0;
int diceroll=0, bonus=0, damroll=0, str_dam_bon=0, pos_dam_bon=0;
int raw_dam=0, dam_stop=0;
if (IS_SET(world[ch->in_room].room_flags, PEACEFULL))
{
act("You feel too peaceful to contemplate violence.",
FALSE,ch,0,0,TO_CHAR);
return;
}
if (ch->in_room != victim->in_room) {
logg("SYSERR: NOT SAME ROOM WHEN FIGHTING!");
return;
}
if (GET_POS(ch) < POSITION_SLEEPING) /* can't attack if stunned */
return;
if (IS_AFFECTED(ch, AFF_HIDE))
REMOVE_BIT(ch->specials.affected_by, AFF_HIDE);
if (ch->equipment[HOLD])
held = ch->equipment[HOLD];
prof = get_prof(ch, &w_type, type);
if (ch->equipment[WIELD] &&
(ch->equipment[WIELD]->obj_flags.type_flag == ITEM_WEAPON))
wielded = ch->equipment[WIELD];
if (type == SKILL_DUAL_WIELD)
wielded = ch->equipment[HOLD];
/* Calculate the raw armor including magic armor */
/* The lower AC, the better */
thaco = 20;
bonus = combat_bonus(ch);
thaco -= str_app[STRENGTH_APPLY_INDEX(ch)].tohit;
if (!affected_by_spell(ch, SPELL_ENDURANCE))
thaco -= bonus;
if (type == SKILL_DUAL_WIELD)
thaco -= GET_HITROLL2(ch);
else
thaco -= GET_HITROLL(ch);
thaco -= (2*(GET_DEX(ch) - 13))/3; /* So does dexterity */
if (!IS_NPC(ch) && (GET_LEVEL(ch) < 5)) /* helpt the newbies out */
thaco -= (GET_LEVEL(ch)*GET_LEVEL(ch))/3;
thaco -= GET_LEVEL(ch)/10;
thaco = MIN(20, thaco);
thaco = MAX(-20, thaco);
/* multiple attacks */
nattacks = compute_multi_attacks(ch, wielded, type);
if (!IS_NPC(ch)){
if ((type == SKILL_DUAL_WIELD)
&& (num = number(0,31) > GET_SKILL(ch, SKILL_DUAL_WIELD))){
if (num == 0) /* botch */
if (!number(0,31) && (GET_SKILL(ch, SKILL_DUAL_WIELD) <= 29)) {
SET_SKILL(ch, SKILL_DUAL_WIELD, GET_SKILL(ch, SKILL_DUAL_WIELD) +1);
send_to_char("You feel more skillful.\r\n",ch);
return;
}
return;
}
}
/* ok where are we going to hit ?*/
if (ch->in_room != victim->in_room) /* victim may have fled or */
return; /* otherwise left combat */
if (!affected_by_spell(ch, SPELL_ENDURANCE))
GET_MOVE(ch) -= 1*(encumberance_level(ch) +1);/*lose movement points per blow */
else
GET_MOVE(ch) -= 1;
hit_location = number(1,60);
if (hit_location <= 60)
hit_loc = 3;
if (hit_location <= 56)
hit_loc = 2;
if (hit_location <= 40)
hit_loc = 1;
if (hit_location <= 30)
hit_loc = 0;
diceroll = number(0, 50);
if (prof >= PROF_SWORD && prof <= PROF_CLAW)
num = GET_SKILL(ch,prof);
else if (!IS_MOB(ch) && !wielded)
num = GET_SKILL(ch,SKILL_FISTICUFFS);
else
num =0;
if(!IS_NPC(ch) && (prof >= PROF_SWORD) && (prof <= PROF_CLAW) && (num < 30)){
if (!diceroll && !number(0,3*num*num + 1))
{
send_to_char("You feel more proficient.\r\n",ch);
SET_SKILL(ch,prof,num + 1);
}
}
else if (!IS_NPC(ch) && !wielded) {
if (!diceroll && !number(0, 2*num*num + 1))
{
send_to_char("You feel more proficient.\r\n",ch);
SET_SKILL(ch,SKILL_FISTICUFFS,num + 1);
}
}
switch (hit_loc){
case 0:
victim_ac = GET_BODY_AC(victim);
break;
case 1:
victim_ac = GET_LEGS_AC(victim);
break;
case 2:
victim_ac = GET_ARMS_AC(victim);
break;
case 3:
victim_ac = GET_HEAD_AC(victim);
break;
default:
victim_ac = GET_BODY_AC(victim);
}
if (AWAKE(victim))
victim_ac += dex_app[GET_DEX(victim)].defensive;
if (!can_see_char(ch,victim))
victim_ac -= 15;
victim_ac = MAX(-100, victim_ac);
victim_ac = MIN(100, victim_ac);
if (!IS_NPC(ch) && num)
prof_factor = 50 - (99*num)/30;
else if (IS_NPC(ch))
prof_factor = 0;
else
prof_factor = 100;
prof_factor /= 4;
if ((GET_POS(victim) < POSITION_SLEEPING)
|| IS_AFFECTED(victim, AFF_PARALYSIS))
diceroll = 50;
if ((diceroll < 50) && AWAKE(victim) && (type != SKILL_BACKSTAB) &&
(!diceroll ||
(diceroll < (thaco + prof_factor + (100 - victim_ac)/4))))
damage(ch, victim, 0, w_type,hit_loc, 0); /* MISSED */
else
{
str_dam_bon = str_app[STRENGTH_APPLY_INDEX(ch)].todam;
dam = str_dam_bon;
if (!affected_by_spell(ch, SPELL_ENDURANCE))
dam += bonus;
if (type == SKILL_DUAL_WIELD)
damroll = GET_DAMROLL2(ch);
else
damroll = GET_DAMROLL(ch);
dam += damroll;
if (!wielded) {
if (IS_NPC(ch) && ch->specials.damsizedice){
raw_dam = dice(ch->specials.damnodice, ch->specials.damsizedice);
}
else
raw_dam = number(0,MIN(1,GET_SKILL(ch, SKILL_FISTICUFFS)/2));
}
else{
damg = MAX(0,wielded->obj_flags.value[4]);
if (wielded->obj_flags.value[2])
raw_dam = (dice(wielded->obj_flags.value[1],
wielded->obj_flags.value[2]))*(10-damg)/10;
raw_dam *= (num + 120);
raw_dam /= 150;
}
dam += raw_dam;
if (GET_POS(victim) < POSITION_STANDING)
pos_dam_bon = (dam*(POSITION_STANDING - GET_POS(victim)))/3.;
dam += pos_dam_bon;
switch (hit_loc){
case 0:
dam_stop = GET_BODY_STOPPING(ch);
break;
case 1:
dam_stop = GET_LEGS_STOPPING(ch);
break;
case 2:
dam_stop = GET_ARMS_STOPPING(ch);
break;
case 3:
dam_stop = GET_HEAD_STOPPING(ch);
break;
default:
dam_stop = GET_BODY_STOPPING(ch);
}
if (type == SKILL_BACKSTAB) {
dam *= MIN(GET_LEVEL(ch) - spell_lev(ch,SKILL_BACKSTAB)
+ GET_SKILL(ch,SKILL_BACKSTAB)/2,75);
dam /= 5;
dam *= GET_DEX(ch);
dam /= 15;
die = stress_die();
dam += die;
sprintf(buf,"Backstab: die = %d, level = %d dam = %d\r\n",
die,spell_lev(ch, SKILL_BACKSTAB),dam);
if (PRF_FLAGGED(ch, PRF_DEBUG))
send_to_char(buf,ch);
if (affected_by_spell(victim, SPELL_ENCASE_IN_ICE)){
if (!number(0,3) && (dam > 5)){
act("$n's backstab shatters the ice encasing $N!",
TRUE,ch,0,victim,TO_NOTVICT);
act("$n's backstab shatters the ice encasing you!",
TRUE,ch,0,victim,TO_VICT);
act("Your backstab shatters the ice encasing $N.",
TRUE,ch,0,victim,TO_CHAR);
damage(ch,victim,dam/4,SKILL_BACKSTAB,-1,0);
affect_from_char(victim, SPELL_ENCASE_IN_ICE);
}
else {
act("$n's backstab glances off the ice encasing $N!",
TRUE,ch,0,victim,TO_NOTVICT);
act("$n's backstab glances off the ice encasing you!",
TRUE,ch,0,victim,TO_VICT);
act("Your backstab glances off the ice encasing $N.",
TRUE,ch,0,victim,TO_CHAR);
if (!(ch->specials.fighting) && (dam > 5))
set_fighting(ch, victim);
}
}
else
damage(ch,victim,dam,SKILL_BACKSTAB,-1,0);
}
else{
dam -= dam_stop;
dam = MAX(1, dam); /* Not less than 0 damage */
if (affected_by_spell(victim, SPELL_ENCASE_IN_ICE)){
if (!number(0,3) && (dam > 5)){
act("$n's blow shatters the ice encasing $N!",
TRUE,ch,0,victim,TO_NOTVICT);
act("$n's blow shatters the ice encasing you!",
TRUE,ch,0,victim,TO_VICT);
act("Your blow shatters the ice encasing $N.",
TRUE,ch,0,victim,TO_CHAR);
dam /= 4;
damage(ch,victim,dam,w_type,hit_loc,0);
affect_from_char(victim, SPELL_ENCASE_IN_ICE);
}
else {
act("$n's blow glances off the ice encasing $N!",
TRUE,ch,0,victim,TO_NOTVICT);
act("$n's blow glances off the ice encasing you!",
TRUE,ch,0,victim,TO_VICT);
act("Your blow glances off the ice encasing $N.",
TRUE,ch,0,victim,TO_CHAR);
if (!(ch->specials.fighting) && (dam > 5))
set_fighting(ch, victim);
}
}
else
damage(ch,victim,dam,w_type,hit_loc,0);
}
}
nattacks--;
if (nattacks > 0)
for (icnt=0;icnt<nattacks;icnt++){
if (type == SKILL_BACKSTAB)
break;
if (!victim)
return;
if (ch->in_room != victim->in_room)/* victim may have fled or */
return; /* otherwise left combat */
GET_MOVE(ch) -= 1; /* loose a movement point per blow */
hit_location = number(1,60);
if (hit_location <= 60)
hit_loc = 3;
if (hit_location <= 56)
hit_loc = 2;
if (hit_location <= 40)
hit_loc = 1;
if (hit_location <= 30)
hit_loc = 0;
diceroll = number(1, 50);
if(!IS_NPC(ch) && (prof >= PROF_SWORD) && (prof <= PROF_CLAW) && (num < 30)){
if (!diceroll && !number(0,3*num*num + 1))
{
send_to_char("You feel more proficient.\r\n",ch);
SET_SKILL(ch,prof,num + 1);
}
}
else if (!IS_NPC(ch) && !wielded) {
if (!diceroll && !number(0, 2*num*num + 1))
{
send_to_char("You feel more proficient.\r\n",ch);
SET_SKILL(ch,SKILL_FISTICUFFS,num + 1);
}
}
switch (hit_loc){
case 0:
victim_ac = GET_BODY_AC(victim);
break;
case 1:
victim_ac = GET_LEGS_AC(victim);
break;
case 2:
victim_ac = GET_ARMS_AC(victim);
break;
case 3:
victim_ac = GET_HEAD_AC(victim);
break;
default:
victim_ac = GET_BODY_AC(victim);
}
if (AWAKE(victim))
victim_ac -= dex_app[GET_DEX(victim)].defensive;
victim_ac = MAX(-100, victim_ac); /* -100 is lowest */
if (!IS_NPC(ch) && num)
prof_factor = 50 - (99*num)/30;
else if (IS_NPC(ch))
prof_factor = 0;
else
prof_factor = 100;
prof_factor /= 4;
if ((((diceroll < 50) && AWAKE(victim)) &&
(!diceroll
|| (diceroll
< (thaco + prof_factor + (100 - victim_ac)/4))))) {
damage(ch, victim, 0, w_type,hit_loc,0);
} else {
str_dam_bon = str_app[STRENGTH_APPLY_INDEX(ch)].todam;
dam = str_dam_bon;
if (type == SKILL_DUAL_WIELD)
damroll = GET_DAMROLL2(ch);
else
damroll = GET_DAMROLL(ch);
dam += damroll;
if (!wielded) {
if (IS_NPC(ch) && ch->specials.damsizedice) {
raw_dam = dice(ch->specials.damnodice, ch->specials.damsizedice);
} else
raw_dam = number(0, MIN(1,GET_SKILL(ch, SKILL_FISTICUFFS)/2));
}
else{
damg = MAX(0,wielded->obj_flags.value[4]);
if (wielded->obj_flags.value[2])
raw_dam = (dice(wielded->obj_flags.value[1],
wielded->obj_flags.value[2]))*(10-damg)/10;
raw_dam *= (num + 120);
raw_dam /= 150;
}
dam += raw_dam;
if (GET_POS(victim) < POSITION_STANDING)
pos_dam_bon = (dam*(POSITION_STANDING - GET_POS(victim)))/3.;
dam += pos_dam_bon;
/* 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 */
switch (hit_loc){
case 0:
dam_stop = GET_BODY_STOPPING(ch);
break;
case 1:
dam_stop = GET_LEGS_STOPPING(ch);
break;
case 2:
dam_stop = GET_ARMS_STOPPING(ch);
break;
case 3:
dam_stop = GET_HEAD_STOPPING(ch);
break;
default:
dam_stop = GET_BODY_STOPPING(ch);
}
dam -= dam_stop;
dam = MAX(1, dam); /* Not less than 0 damage */
add_event(icnt/2, EVENT_COMBAT, dam, w_type, hit_loc,0,0,ch,victim);
}
}
}
/* control the fights going on */
void perform_violence(void)
{
struct char_data *ch;
int i;
bool found = FALSE;
struct obj_data *sheath;
for (ch = combat_list; ch; ch = combat_next_dude) {
combat_next_dude = ch->next_fighting;
assert(ch->specials.fighting);
if (IS_NPC(ch) && ch->specials.timer)
ch->specials.timer--;
if (AWAKE(ch) && (ch->in_room == ch->specials.fighting->in_room)) {
if (!ch->equipment[WIELD])
for (i=0; i<= MAX_WEAR; i++)
if (sheath = ch->equipment[i])
if ((GET_ITEM_TYPE(sheath) == ITEM_SCABBARD) &&
(sheath->contains))
found=TRUE;
if (found)
do_draw(ch,"",0,0);
if (GET_POS(ch) < POSITION_STANDING && IS_NPC(ch))
do_stand(ch,"",0,0);
if (!IS_AFFECTED(ch, AFF_PARALYSIS)
|| (GET_LEVEL(ch) >= LEVEL_BUILDER)) {
hit(ch, ch->specials.fighting, TYPE_UNDEFINED);
if (ch->equipment[HOLD]
&& (ch->equipment[HOLD]->obj_flags.type_flag == ITEM_WEAPON)
&& ch->specials.fighting)
hit(ch, ch->specials.fighting, SKILL_DUAL_WIELD);
}
} else { /* Not in same room */
stop_fighting(ch);
}
}
}
void botch_kick(struct char_data *ch, struct char_data *vict)
{
ACMD(do_say);
int die;
die = stress_die();
if(die < 6){
act("$n kicks out in a very energetic fashion.",FALSE, ch,0,vict,TO_ROOM);
if (ch->specials.fighting && ch->specials.fighting == vict){
if (GET_BODY_STOPPING(vict) > 0){
act("Your kick bounces harmlessly off $N's armour."
,FALSE, ch,0,vict,TO_CHAR);
act("$n's kick bounces harmlessly off $N's armour."
,FALSE, ch,0,vict,TO_NOTVICT);
act("$n's kick bounces harmlessly off your armour."
,FALSE, ch,0,vict,TO_VICT);}
else if ((GET_POS(vict) > POSITION_SLEEPING) &&
!IS_AFFECTED(vict, AFF_PARALYSIS)){
act("$N easily dodges your kick.",FALSE, ch,0,vict,TO_CHAR);
act("$N easily dodges $n's kick.",FALSE, ch,0,vict,TO_NOTVICT);
act("You easily dodge $n's kick.",FALSE, ch,0,vict,TO_VICT);
}
WAIT_STATE(ch, PULSE_VIOLENCE * 3);
return;
}
else{
act("You try to kick $N, but miss by miles.",FALSE, ch,0,vict,TO_CHAR);
if (IS_NPC(vict) && CAN_SPEAK(vict))
do_say(vict,"You'd better watch out. You could hurt yourself doing that.",0,0);
return;
}
}
else {
act("$n kicks out in a very energetic fashion.",FALSE, ch,0,vict,TO_ROOM);
if (ch->specials.fighting && ch->specials.fighting == vict){
if (GET_BODY_STOPPING(vict) > 0){
act("Your kick bounces harmlessly off $N's armour."
,FALSE, ch,0,vict,TO_CHAR);
act("You fall over.",FALSE, ch,0,vict,TO_CHAR);
act("$n falls flat in $s face.",FALSE, ch,0,vict,TO_ROOM);
act("$n's kick bounces harmlessly off $N's armour."
,FALSE, ch,0,vict,TO_NOTVICT);
act("$n's kick bounces harmlessly off your armour."
,FALSE, ch,0,vict,TO_VICT);}
else{
if ((GET_POS(vict) > POSITION_SLEEPING) &&
!IS_AFFECTED(vict, AFF_PARALYSIS)){
act("You easily dodge $n's kick.",FALSE, ch,0,vict,TO_VICT);
act("$N easily dodges your kick.",FALSE, ch,0,vict,TO_CHAR);
act("$N easily dodges $n's kick.",FALSE, ch,0,vict,TO_NOTVICT);
}
act("You fall over.",FALSE, ch,0,vict,TO_CHAR);
act("$n falls flat in $s face.",FALSE, ch,0,vict,TO_ROOM);
}
GET_POS(ch) = POSITION_SITTING;
WAIT_STATE(ch, PULSE_VIOLENCE * 4);
return;
}
else{
act("You try to kick $N, but miss by miles.",FALSE,
ch,0,vict,TO_CHAR);
act("You fall over.",FALSE, ch,0,vict,TO_CHAR);
act("$n falls flat in $s face.",FALSE, ch,0,vict,TO_ROOM);
if (IS_NPC(vict) && CAN_SPEAK(vict))
do_say(vict,"Hey!",0,0);
act("$n is enraged.",TRUE,vict,0,0,TO_ROOM);
GET_POS(ch) = POSITION_SITTING;
hit(vict,ch,TYPE_UNDEFINED);
WAIT_STATE(ch, PULSE_VIOLENCE * 4);
return;
}
}
return;
}
int stress_die(void)
{
int die,bon;
bon=1;
while((die = number(0,9)) == 1 && (bon < (1<< 11)))
bon <<= 1;
die *= bon;
return (MAX(0,die));
}