/* ************************************************************************ * File: fight.c , Combat module. Part of DIKUMUD * * Usage: Combat system and messages. * * Copyright (C) 1990, 1991 - see 'license.doc' for complete information. * ************************************************************************* */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.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" /* 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 byte pk_allowed; /* External procedures */ 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 *ch, struct char_data *victim); void remember(struct char_data *ch, struct char_data *victim); /* Weapon attack texts */ struct attack_hit_type attack_hit_text[] = { { "hit", "hits" }, { "pound", "pounds" }, { "pierce", "pierces" }, { "slash", "slashes" }, { "blast", "blasts" }, { "whip", "whips" }, { "pierce", "pierces" }, { "claw", "claws" }, { "bite", "bites" }, { "sting", "stings" }, { "crush", "crushes" } /* TYPE_CRUSH */ }; /* The Fight related routines */ void appear(struct char_data *ch) { act("$n slowly fades 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, "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) { log("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) > 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; } 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)) { char buf[200]; 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); syslog(buf, BRF, LEVEL_IMMORT, TRUE); send_to_char("If you want to be a PLAYER KILLER, so be it...\n\r", 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; if (IS_AFFECTED(ch, AFF_SLEEP)) affect_from_char(ch, SPELL_SLEEP); ch->specials.fighting = vict; GET_POS(ch) = POSITION_FIGHTING; 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) { log("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; 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; int i; 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(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); corpse->contains = ch->carrying; 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.type_flag = ITEM_CONTAINER; 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 */ 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; align = GET_ALIGNMENT(ch) - GET_ALIGNMENT(victim); if (GET_ALIGNMENT(victim) < 0) { if (align > 650) GET_ALIGNMENT(ch) = MIN(1000, GET_ALIGNMENT(ch) + ((align - 650) / 8) + ((1000 - GET_ALIGNMENT(victim)) / 5)); else GET_ALIGNMENT(ch) = MIN(1000, GET_ALIGNMENT(ch) - (GET_ALIGNMENT(victim) / 8)); } else { if (align < -650) GET_ALIGNMENT(ch) = MAX(-1000, GET_ALIGNMENT(ch) + ((align + 650) / 8) + ((-1000 + GET_ALIGNMENT(victim)) / 5)); else GET_ALIGNMENT(ch) = MAX(-1000, GET_ALIGNMENT(ch) - (GET_ALIGNMENT(victim) / 8)); } } 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 <= 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 someone's 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); while (ch->affected) affect_remove(ch, ch->affected); death_cry(ch); make_corpse(ch); extract_char(ch); } void die(struct char_data *ch) { gain_exp(ch, -(GET_EXP(ch) / 2)); if (!IS_NPC(ch)) REMOVE_BIT(PLR_FLAGS(ch), PLR_KILLER | PLR_THIEF); raw_kill(ch); } void group_gain(struct char_data *ch, struct char_data *victim) { 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)) { if (share > 1) { sprintf(buf2, "You receive your share of experience -- %d points.", share); } else strcpy(buf2, "You receive your share of experience -- 1 measly little point."); act(buf2, FALSE, k, 0, 0, TO_CHAR); gain_exp(k, share); change_alignment(k, victim); if (!(number(0, 3))) do_save(k, "", 0, 0); /* save 25% of the time to avoid big lags */ } for (f = k->followers; f; f = f->next) { if (IS_AFFECTED(f->follower, AFF_GROUP) && (f->follower->in_room == ch->in_room)) { if (share > 1) sprintf(buf2, "You receive your share of experience -- %d points.", share); else strcpy(buf2, "You receive your share of experience -- 1 measly little point."); act(buf2, FALSE, f->follower, 0, 0, TO_CHAR); gain_exp(f->follower, share); change_alignment(f->follower, victim); if (!(number(0, 3))) do_save(f->follower, "", 0, 0); /* save only 25% of the time to avoid big lags */ } } } 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) { struct obj_data *wield; char *buf; int msgnum; 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 tickles $N with $s #w.", /* 1: 1..2 */ "You tickle $N as you #w $M.", "$n tickles 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..19 */ "You #w $N extremely hard.", "$n #W you extremely hard." }, { "$n massacres $N to small fragments with $s #w.",/* 7: 19..23 */ "You massacre $N to small fragments with your #w.", "$n massacres you to small fragments with $s #w." }, { "$n OBLITERATES $N with $s deadly #w!!", /* 8: > 23 */ "You OBLITERATE $N with your deadly #w!!", "$n OBLITERATES you with $s deadly #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 <= 23) msgnum = 7; else msgnum = 8; /* 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, wield, 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, wield, 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, wield, victim, TO_VICT); send_to_char(CCNRM(victim, C_CMP), victim); } void damage(struct char_data *ch, struct char_data *victim, int dam, int attacktype) { struct message_type *messages; int i, j, nr, max_hit, exp; int hit_limit(struct char_data *ch); if (GET_POS(victim) <= POSITION_DEAD) { log("SYSERR: Attempt to damage a corpse."); return; /* -je, 7/7/92 */ } assert(GET_POS(victim) > POSITION_DEAD); if ((GET_LEVEL(victim) >= LEVEL_IMMORT) && !IS_NPC(victim)) /* You can't damage an immortal! */ dam = 0; 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 && !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 (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_IMMORT)) remember(ch, victim); GET_POS(victim) = POSITION_FIGHTING; } } if (victim->master == ch) stop_follower(victim); if (IS_AFFECTED(ch, AFF_INVISIBLE)) appear(ch); if (IS_AFFECTED(victim, AFF_SANCTUARY)) dam = dam / 2; /* 1/2 damage when sanctuary */ if (!pk_allowed) { check_killer(ch, victim); if (PLR_FLAGGED(ch, PLR_KILLER)) dam = 0; } 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_CRUSH)) { 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) >= LEVEL_IMMORT)) { 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)) { /* * Use send_to_char, because act() doesn't send * message if you are DEAD. */ 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.\n\r", 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.\n\r", 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.\n\r", 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...\n\r", victim); 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 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 wimp out, and attempt to flee!\n\r", victim); do_flee(victim, "", 0, 0); } break; } if (!IS_NPC(victim) && !(victim->desc)) { do_flee(victim, "", 0, 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); } } /* this code removed due to popular request -- JE 6/14/93 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); if (exp > 1) sprintf(buf2, "You receive %d experience points.\n\r", exp); else strcpy(buf2, "You receive 1 lousy experience point.\n\r"); send_to_char(buf2, ch); gain_exp(ch, exp); change_alignment(ch, victim); if (!(number(0, 1))) do_save(ch, "", 0, 0); /* individuals saved 50% of the time */ } if (!IS_NPC(victim)) { sprintf(buf2, "%s killed by %s at %s", GET_NAME(victim), GET_NAME(ch), world[victim->in_room].name); syslog(buf2, BRF, LEVEL_IMMORT, TRUE); } if (IS_NPC(ch) && !IS_NPC(victim) && IS_SET(victim->specials2.act, MOB_MEMORY)) forget(victim, ch); 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][35]; 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("SYSERR: 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 : w_type = TYPE_BLAST; break; case 11 : w_type = TYPE_PIERCE; break; case 12 : w_type = TYPE_NO_BS_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][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); /* Smartness helps! */ calc_thaco -= (int)((GET_WIS(ch) - 13) / 1.5); /* So does wisdom */ 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[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); } } }