/************************************************************************* * 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, ""); } }