/ circle3.1 / src / fight.c
fight.c
  1  /* ************************************************************************
  2  *   File: fight.c                                       Part of CircleMUD *
  3  *  Usage: Combat system                                                   *
  4  *                                                                         *
  5  *  All rights reserved.  See license.doc for complete information.        *
  6  *                                                                         *
  7  *  Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
  8  *  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
  9  ************************************************************************ */
 10  
 11  #include "conf.h"
 12  #include "sysdep.h"
 13  
 14  
 15  #include "structs.h"
 16  #include "utils.h"
 17  #include "comm.h"
 18  #include "handler.h"
 19  #include "interpreter.h"
 20  #include "db.h"
 21  #include "spells.h"
 22  #include "screen.h"
 23  #include "constants.h"
 24  
 25  /* Structures */
 26  struct char_data *combat_list = NULL;	/* head of l-list of fighting chars */
 27  struct char_data *next_combat_list = NULL;
 28  
 29  /* External structures */
 30  extern struct message_list fight_messages[MAX_MESSAGES];
 31  extern int pk_allowed;		/* see config.c */
 32  extern int max_exp_gain;	/* see config.c */
 33  extern int max_exp_loss;	/* see config.c */
 34  extern int max_npc_corpse_time, max_pc_corpse_time;
 35  
 36  /* External procedures */
 37  char *fread_action(FILE *fl, int nr);
 38  ACMD(do_flee);
 39  int backstab_mult(int level);
 40  int thaco(int ch_class, int level);
 41  int ok_damage_shopkeeper(struct char_data *ch, struct char_data *victim);
 42  
 43  /* local functions */
 44  void perform_group_gain(struct char_data *ch, int base, struct char_data *victim);
 45  void dam_message(int dam, struct char_data *ch, struct char_data *victim, int w_type);
 46  void appear(struct char_data *ch);
 47  void load_messages(void);
 48  void free_messages(void);
 49  void free_messages_type(struct msg_type *msg);
 50  void check_killer(struct char_data *ch, struct char_data *vict);
 51  void make_corpse(struct char_data *ch);
 52  void change_alignment(struct char_data *ch, struct char_data *victim);
 53  void death_cry(struct char_data *ch);
 54  void raw_kill(struct char_data *ch);
 55  void die(struct char_data *ch);
 56  void group_gain(struct char_data *ch, struct char_data *victim);
 57  void solo_gain(struct char_data *ch, struct char_data *victim);
 58  char *replace_string(const char *str, const char *weapon_singular, const char *weapon_plural);
 59  void perform_violence(void);
 60  int compute_armor_class(struct char_data *ch);
 61  int compute_thaco(struct char_data *ch, struct char_data *vict);
 62  
 63  /* Weapon attack texts */
 64  struct attack_hit_type attack_hit_text[] =
 65  {
 66    {"hit", "hits"},		/* 0 */
 67    {"sting", "stings"},
 68    {"whip", "whips"},
 69    {"slash", "slashes"},
 70    {"bite", "bites"},
 71    {"bludgeon", "bludgeons"},	/* 5 */
 72    {"crush", "crushes"},
 73    {"pound", "pounds"},
 74    {"claw", "claws"},
 75    {"maul", "mauls"},
 76    {"thrash", "thrashes"},	/* 10 */
 77    {"pierce", "pierces"},
 78    {"blast", "blasts"},
 79    {"punch", "punches"},
 80    {"stab", "stabs"}
 81  };
 82  
 83  #define IS_WEAPON(type) (((type) >= TYPE_HIT) && ((type) < TYPE_SUFFERING))
 84  
 85  /* The Fight related routines */
 86  
 87  void appear(struct char_data *ch)
 88  {
 89    if (affected_by_spell(ch, SPELL_INVISIBLE))
 90      affect_from_char(ch, SPELL_INVISIBLE);
 91  
 92    REMOVE_BIT(AFF_FLAGS(ch), AFF_INVISIBLE | AFF_HIDE);
 93  
 94    if (GET_LEVEL(ch) < LVL_IMMORT)
 95      act("$n slowly fades into existence.", FALSE, ch, 0, 0, TO_ROOM);
 96    else
 97      act("You feel a strange presence as $n appears, seemingly from nowhere.",
 98  	FALSE, ch, 0, 0, TO_ROOM);
 99  }
100  
101  
102  int compute_armor_class(struct char_data *ch)
103  {
104    int armorclass = GET_AC(ch);
105  
106    if (AWAKE(ch))
107      armorclass += dex_app[GET_DEX(ch)].defensive * 10;
108  
109    return (MAX(-100, armorclass));      /* -100 is lowest */
110  }
111  
112  
113  void free_messages_type(struct msg_type *msg)
114  {
115    if (msg->attacker_msg)	free(msg->attacker_msg);
116    if (msg->victim_msg)		free(msg->victim_msg);
117    if (msg->room_msg)		free(msg->room_msg);
118  }
119  
120  
121  void free_messages(void)
122  {
123    int i;
124  
125    for (i = 0; i < MAX_MESSAGES; i++)
126      while (fight_messages[i].msg) {
127        struct message_type *former = fight_messages[i].msg;
128  
129        free_messages_type(&former->die_msg);
130        free_messages_type(&former->miss_msg);
131        free_messages_type(&former->hit_msg);
132        free_messages_type(&former->god_msg);
133  
134        fight_messages[i].msg = fight_messages[i].msg->next;
135        free(former);
136      }
137  }
138  
139  
140  void load_messages(void)
141  {
142    FILE *fl;
143    int i, type;
144    struct message_type *messages;
145    char chk[128];
146  
147    if (!(fl = fopen(MESS_FILE, "r"))) {
148      log("SYSERR: Error reading combat message file %s: %s", MESS_FILE, strerror(errno));
149      exit(1);
150    }
151  
152    for (i = 0; i < MAX_MESSAGES; i++) {
153      fight_messages[i].a_type = 0;
154      fight_messages[i].number_of_attacks = 0;
155      fight_messages[i].msg = NULL;
156    }
157  
158    fgets(chk, 128, fl);
159    while (!feof(fl) && (*chk == '\n' || *chk == '*'))
160      fgets(chk, 128, fl);
161  
162    while (*chk == 'M') {
163      fgets(chk, 128, fl);
164      sscanf(chk, " %d\n", &type);
165      for (i = 0; (i < MAX_MESSAGES) && (fight_messages[i].a_type != type) &&
166  	 (fight_messages[i].a_type); i++);
167      if (i >= MAX_MESSAGES) {
168        log("SYSERR: Too many combat messages.  Increase MAX_MESSAGES and recompile.");
169        exit(1);
170      }
171      CREATE(messages, struct message_type, 1);
172      fight_messages[i].number_of_attacks++;
173      fight_messages[i].a_type = type;
174      messages->next = fight_messages[i].msg;
175      fight_messages[i].msg = messages;
176  
177      messages->die_msg.attacker_msg = fread_action(fl, i);
178      messages->die_msg.victim_msg = fread_action(fl, i);
179      messages->die_msg.room_msg = fread_action(fl, i);
180      messages->miss_msg.attacker_msg = fread_action(fl, i);
181      messages->miss_msg.victim_msg = fread_action(fl, i);
182      messages->miss_msg.room_msg = fread_action(fl, i);
183      messages->hit_msg.attacker_msg = fread_action(fl, i);
184      messages->hit_msg.victim_msg = fread_action(fl, i);
185      messages->hit_msg.room_msg = fread_action(fl, i);
186      messages->god_msg.attacker_msg = fread_action(fl, i);
187      messages->god_msg.victim_msg = fread_action(fl, i);
188      messages->god_msg.room_msg = fread_action(fl, i);
189      fgets(chk, 128, fl);
190      while (!feof(fl) && (*chk == '\n' || *chk == '*'))
191        fgets(chk, 128, fl);
192    }
193  
194    fclose(fl);
195  }
196  
197  
198  void update_pos(struct char_data *victim)
199  {
200    if ((GET_HIT(victim) > 0) && (GET_POS(victim) > POS_STUNNED))
201      return;
202    else if (GET_HIT(victim) > 0)
203      GET_POS(victim) = POS_STANDING;
204    else if (GET_HIT(victim) <= -11)
205      GET_POS(victim) = POS_DEAD;
206    else if (GET_HIT(victim) <= -6)
207      GET_POS(victim) = POS_MORTALLYW;
208    else if (GET_HIT(victim) <= -3)
209      GET_POS(victim) = POS_INCAP;
210    else
211      GET_POS(victim) = POS_STUNNED;
212  }
213  
214  
215  void check_killer(struct char_data *ch, struct char_data *vict)
216  {
217    if (PLR_FLAGGED(vict, PLR_KILLER) || PLR_FLAGGED(vict, PLR_THIEF))
218      return;
219    if (PLR_FLAGGED(ch, PLR_KILLER) || IS_NPC(ch) || IS_NPC(vict) || ch == vict)
220      return;
221  
222    SET_BIT(PLR_FLAGS(ch), PLR_KILLER);
223    send_to_char(ch, "If you want to be a PLAYER KILLER, so be it...\r\n");
224    mudlog(BRF, LVL_IMMORT, TRUE, "PC Killer bit set on %s for initiating attack on %s at %s.",
225  	    GET_NAME(ch), GET_NAME(vict), world[IN_ROOM(vict)].name);
226  }
227  
228  
229  /* start one char fighting another (yes, it is horrible, I know... )  */
230  void set_fighting(struct char_data *ch, struct char_data *vict)
231  {
232    if (ch == vict)
233      return;
234  
235    if (FIGHTING(ch)) {
236      core_dump();
237      return;
238    }
239  
240    ch->next_fighting = combat_list;
241    combat_list = ch;
242  
243    if (AFF_FLAGGED(ch, AFF_SLEEP))
244      affect_from_char(ch, SPELL_SLEEP);
245  
246    FIGHTING(ch) = vict;
247    GET_POS(ch) = POS_FIGHTING;
248  
249    if (!pk_allowed)
250      check_killer(ch, vict);
251  }
252  
253  
254  
255  /* remove a char from the list of fighting chars */
256  void stop_fighting(struct char_data *ch)
257  {
258    struct char_data *temp;
259  
260    if (ch == next_combat_list)
261      next_combat_list = ch->next_fighting;
262  
263    REMOVE_FROM_LIST(ch, combat_list, next_fighting);
264    ch->next_fighting = NULL;
265    FIGHTING(ch) = NULL;
266    GET_POS(ch) = POS_STANDING;
267    update_pos(ch);
268  }
269  
270  
271  
272  void make_corpse(struct char_data *ch)
273  {
274    char buf2[MAX_NAME_LENGTH + 64];
275    struct obj_data *corpse, *o;
276    struct obj_data *money;
277    int i;
278  
279    corpse = create_obj();
280  
281    corpse->item_number = NOTHING;
282    IN_ROOM(corpse) = NOWHERE;
283    corpse->name = strdup("corpse");
284  
285    snprintf(buf2, sizeof(buf2), "The corpse of %s is lying here.", GET_NAME(ch));
286    corpse->description = strdup(buf2);
287  
288    snprintf(buf2, sizeof(buf2), "the corpse of %s", GET_NAME(ch));
289    corpse->short_description = strdup(buf2);
290  
291    GET_OBJ_TYPE(corpse) = ITEM_CONTAINER;
292    GET_OBJ_WEAR(corpse) = ITEM_WEAR_TAKE;
293    GET_OBJ_EXTRA(corpse) = ITEM_NODONATE;
294    GET_OBJ_VAL(corpse, 0) = 0;	/* You can't store stuff in a corpse */
295    GET_OBJ_VAL(corpse, 3) = 1;	/* corpse identifier */
296    GET_OBJ_WEIGHT(corpse) = GET_WEIGHT(ch) + IS_CARRYING_W(ch);
297    GET_OBJ_RENT(corpse) = 100000;
298    if (IS_NPC(ch))
299      GET_OBJ_TIMER(corpse) = max_npc_corpse_time;
300    else
301      GET_OBJ_TIMER(corpse) = max_pc_corpse_time;
302  
303    /* transfer character's inventory to the corpse */
304    corpse->contains = ch->carrying;
305    for (o = corpse->contains; o != NULL; o = o->next_content)
306      o->in_obj = corpse;
307    object_list_new_owner(corpse, NULL);
308  
309    /* transfer character's equipment to the corpse */
310    for (i = 0; i < NUM_WEARS; i++)
311      if (GET_EQ(ch, i))
312        obj_to_obj(unequip_char(ch, i), corpse);
313  
314    /* transfer gold */
315    if (GET_GOLD(ch) > 0) {
316      /*
317       * following 'if' clause added to fix gold duplication loophole
318       * The above line apparently refers to the old "partially log in,
319       * kill the game character, then finish login sequence" duping
320       * bug. The duplication has been fixed (knock on wood) but the
321       * test below shall live on, for a while. -gg 3/3/2002
322       */
323      if (IS_NPC(ch) || ch->desc) {
324        money = create_money(GET_GOLD(ch));
325        obj_to_obj(money, corpse);
326      }
327      GET_GOLD(ch) = 0;
328    }
329    ch->carrying = NULL;
330    IS_CARRYING_N(ch) = 0;
331    IS_CARRYING_W(ch) = 0;
332  
333    obj_to_room(corpse, IN_ROOM(ch));
334  }
335  
336  
337  /* When ch kills victim */
338  void change_alignment(struct char_data *ch, struct char_data *victim)
339  {
340    /*
341     * new alignment change algorithm: if you kill a monster with alignment A,
342     * you move 1/16th of the way to having alignment -A.  Simple and fast.
343     */
344    GET_ALIGNMENT(ch) += (-GET_ALIGNMENT(victim) - GET_ALIGNMENT(ch)) / 16;
345  }
346  
347  
348  
349  void death_cry(struct char_data *ch)
350  {
351    int door;
352  
353    act("Your blood freezes as you hear $n's death cry.", FALSE, ch, 0, 0, TO_ROOM);
354  
355    for (door = 0; door < NUM_OF_DIRS; door++)
356      if (CAN_GO(ch, door))
357        send_to_room(world[IN_ROOM(ch)].dir_option[door]->to_room, "Your blood freezes as you hear someone's death cry.\r\n");
358  }
359  
360  
361  
362  void raw_kill(struct char_data *ch)
363  {
364    if (FIGHTING(ch))
365      stop_fighting(ch);
366  
367    while (ch->affected)
368      affect_remove(ch, ch->affected);
369  
370    death_cry(ch);
371  
372    make_corpse(ch);
373    extract_char(ch);
374  }
375  
376  
377  
378  void die(struct char_data *ch)
379  {
380    gain_exp(ch, -(GET_EXP(ch) / 2));
381    if (!IS_NPC(ch))
382      REMOVE_BIT(PLR_FLAGS(ch), PLR_KILLER | PLR_THIEF);
383    raw_kill(ch);
384  }
385  
386  
387  
388  void perform_group_gain(struct char_data *ch, int base,
389  			     struct char_data *victim)
390  {
391    int share;
392  
393    share = MIN(max_exp_gain, MAX(1, base));
394  
395    if (share > 1)
396      send_to_char(ch, "You receive your share of experience -- %d points.\r\n", share);
397    else
398      send_to_char(ch, "You receive your share of experience -- one measly little point!\r\n");
399  
400    gain_exp(ch, share);
401    change_alignment(ch, victim);
402  }
403  
404  
405  void group_gain(struct char_data *ch, struct char_data *victim)
406  {
407    int tot_members, base, tot_gain;
408    struct char_data *k;
409    struct follow_type *f;
410  
411    if (!(k = ch->master))
412      k = ch;
413  
414    if (AFF_FLAGGED(k, AFF_GROUP) && (IN_ROOM(k) == IN_ROOM(ch)))
415      tot_members = 1;
416    else
417      tot_members = 0;
418  
419    for (f = k->followers; f; f = f->next)
420      if (AFF_FLAGGED(f->follower, AFF_GROUP) && IN_ROOM(f->follower) == IN_ROOM(ch))
421        tot_members++;
422  
423    /* round up to the next highest tot_members */
424    tot_gain = (GET_EXP(victim) / 3) + tot_members - 1;
425  
426    /* prevent illegal xp creation when killing players */
427    if (!IS_NPC(victim))
428      tot_gain = MIN(max_exp_loss * 2 / 3, tot_gain);
429  
430    if (tot_members >= 1)
431      base = MAX(1, tot_gain / tot_members);
432    else
433      base = 0;
434  
435    if (AFF_FLAGGED(k, AFF_GROUP) && IN_ROOM(k) == IN_ROOM(ch))
436      perform_group_gain(k, base, victim);
437  
438    for (f = k->followers; f; f = f->next)
439      if (AFF_FLAGGED(f->follower, AFF_GROUP) && IN_ROOM(f->follower) == IN_ROOM(ch))
440        perform_group_gain(f->follower, base, victim);
441  }
442  
443  
444  void solo_gain(struct char_data *ch, struct char_data *victim)
445  {
446    int exp;
447  
448    exp = MIN(max_exp_gain, GET_EXP(victim) / 3);
449  
450    /* Calculate level-difference bonus */
451    if (IS_NPC(ch))
452      exp += MAX(0, (exp * MIN(4, (GET_LEVEL(victim) - GET_LEVEL(ch)))) / 8);
453    else
454      exp += MAX(0, (exp * MIN(8, (GET_LEVEL(victim) - GET_LEVEL(ch)))) / 8);
455  
456    exp = MAX(exp, 1);
457  
458    if (exp > 1)
459      send_to_char(ch, "You receive %d experience points.\r\n", exp);
460    else
461      send_to_char(ch, "You receive one lousy experience point.\r\n");
462  
463    gain_exp(ch, exp);
464    change_alignment(ch, victim);
465  }
466  
467  
468  char *replace_string(const char *str, const char *weapon_singular, const char *weapon_plural)
469  {
470    static char buf[256];
471    char *cp = buf;
472  
473    for (; *str; str++) {
474      if (*str == '#') {
475        switch (*(++str)) {
476        case 'W':
477  	for (; *weapon_plural; *(cp++) = *(weapon_plural++));
478  	break;
479        case 'w':
480  	for (; *weapon_singular; *(cp++) = *(weapon_singular++));
481  	break;
482        default:
483  	*(cp++) = '#';
484  	break;
485        }
486      } else
487        *(cp++) = *str;
488  
489      *cp = 0;
490    }				/* For */
491  
492    return (buf);
493  }
494  
495  
496  /* message for doing damage with a weapon */
497  void dam_message(int dam, struct char_data *ch, struct char_data *victim,
498  		      int w_type)
499  {
500    char *buf;
501    int msgnum;
502  
503    static struct dam_weapon_type {
504      const char *to_room;
505      const char *to_char;
506      const char *to_victim;
507    } dam_weapons[] = {
508  
509      /* use #w for singular (i.e. "slash") and #W for plural (i.e. "slashes") */
510  
511      {
512        "$n tries to #w $N, but misses.",	/* 0: 0     */
513        "You try to #w $N, but miss.",
514        "$n tries to #w you, but misses."
515      },
516  
517      {
518        "$n tickles $N as $e #W $M.",	/* 1: 1..2  */
519        "You tickle $N as you #w $M.",
520        "$n tickles you as $e #W you."
521      },
522  
523      {
524        "$n barely #W $N.",		/* 2: 3..4  */
525        "You barely #w $N.",
526        "$n barely #W you."
527      },
528  
529      {
530        "$n #W $N.",			/* 3: 5..6  */
531        "You #w $N.",
532        "$n #W you."
533      },
534  
535      {
536        "$n #W $N hard.",			/* 4: 7..10  */
537        "You #w $N hard.",
538        "$n #W you hard."
539      },
540  
541      {
542        "$n #W $N very hard.",		/* 5: 11..14  */
543        "You #w $N very hard.",
544        "$n #W you very hard."
545      },
546  
547      {
548        "$n #W $N extremely hard.",	/* 6: 15..19  */
549        "You #w $N extremely hard.",
550        "$n #W you extremely hard."
551      },
552  
553      {
554        "$n massacres $N to small fragments with $s #w.",	/* 7: 19..23 */
555        "You massacre $N to small fragments with your #w.",
556        "$n massacres you to small fragments with $s #w."
557      },
558  
559      {
560        "$n OBLITERATES $N with $s deadly #w!!",	/* 8: > 23   */
561        "You OBLITERATE $N with your deadly #w!!",
562        "$n OBLITERATES you with $s deadly #w!!"
563      }
564    };
565  
566  
567    w_type -= TYPE_HIT;		/* Change to base of table with text */
568  
569    if (dam == 0)		msgnum = 0;
570    else if (dam <= 2)    msgnum = 1;
571    else if (dam <= 4)    msgnum = 2;
572    else if (dam <= 6)    msgnum = 3;
573    else if (dam <= 10)   msgnum = 4;
574    else if (dam <= 14)   msgnum = 5;
575    else if (dam <= 19)   msgnum = 6;
576    else if (dam <= 23)   msgnum = 7;
577    else			msgnum = 8;
578  
579    /* damage message to onlookers */
580    buf = replace_string(dam_weapons[msgnum].to_room,
581  	  attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
582    act(buf, FALSE, ch, NULL, victim, TO_NOTVICT);
583  
584    /* damage message to damager */
585    send_to_char(ch, CCYEL(ch, C_CMP));
586    buf = replace_string(dam_weapons[msgnum].to_char,
587  	  attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
588    act(buf, FALSE, ch, NULL, victim, TO_CHAR);
589    send_to_char(ch, CCNRM(ch, C_CMP));
590  
591    /* damage message to damagee */
592    send_to_char(victim, CCRED(victim, C_CMP));
593    buf = replace_string(dam_weapons[msgnum].to_victim,
594  	  attack_hit_text[w_type].singular, attack_hit_text[w_type].plural);
595    act(buf, FALSE, ch, NULL, victim, TO_VICT | TO_SLEEP);
596    send_to_char(victim, CCNRM(victim, C_CMP));
597  }
598  
599  
600  /*
601   *  message for doing damage with a spell or skill
602   *  C3.0: Also used for weapon damage on miss and death blows
603   */
604  int skill_message(int dam, struct char_data *ch, struct char_data *vict,
605  		      int attacktype)
606  {
607    int i, j, nr;
608    struct message_type *msg;
609  
610    struct obj_data *weap = GET_EQ(ch, WEAR_WIELD);
611  
612    for (i = 0; i < MAX_MESSAGES; i++) {
613      if (fight_messages[i].a_type == attacktype) {
614        nr = dice(1, fight_messages[i].number_of_attacks);
615        for (j = 1, msg = fight_messages[i].msg; (j < nr) && msg; j++)
616  	msg = msg->next;
617  
618        if (!IS_NPC(vict) && (GET_LEVEL(vict) >= LVL_IMMORT)) {
619  	act(msg->god_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR);
620  	act(msg->god_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT);
621  	act(msg->god_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT);
622        } else if (dam != 0) {
623          /*
624           * Don't send redundant color codes for TYPE_SUFFERING & other types
625           * of damage without attacker_msg.
626           */
627  	if (GET_POS(vict) == POS_DEAD) {
628            if (msg->die_msg.attacker_msg) {
629              send_to_char(ch, CCYEL(ch, C_CMP));
630              act(msg->die_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR);
631              send_to_char(ch, CCNRM(ch, C_CMP));
632            }
633  
634  	  send_to_char(vict, CCRED(vict, C_CMP));
635  	  act(msg->die_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT | TO_SLEEP);
636  	  send_to_char(vict, CCNRM(vict, C_CMP));
637  
638  	  act(msg->die_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT);
639  	} else {
640            if (msg->hit_msg.attacker_msg) {
641  	    send_to_char(ch, CCYEL(ch, C_CMP));
642  	    act(msg->hit_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR);
643  	    send_to_char(ch, CCNRM(ch, C_CMP));
644            }
645  
646  	  send_to_char(vict, CCRED(vict, C_CMP));
647  	  act(msg->hit_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT | TO_SLEEP);
648  	  send_to_char(vict, CCNRM(vict, C_CMP));
649  
650  	  act(msg->hit_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT);
651  	}
652        } else if (ch != vict) {	/* Dam == 0 */
653          if (msg->miss_msg.attacker_msg) {
654  	  send_to_char(ch, CCYEL(ch, C_CMP));
655  	  act(msg->miss_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR);
656  	  send_to_char(ch, CCNRM(ch, C_CMP));
657          }
658  
659  	send_to_char(vict, CCRED(vict, C_CMP));
660  	act(msg->miss_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT | TO_SLEEP);
661  	send_to_char(vict, CCNRM(vict, C_CMP));
662  
663  	act(msg->miss_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT);
664        }
665        return (1);
666      }
667    }
668    return (0);
669  }
670  
671  /*
672   * Alert: As of bpl14, this function returns the following codes:
673   *	< 0	Victim died.
674   *	= 0	No damage.
675   *	> 0	How much damage done.
676   */
677  int damage(struct char_data *ch, struct char_data *victim, int dam, int attacktype)
678  {
679    if (GET_POS(victim) <= POS_DEAD) {
680      /* This is "normal"-ish now with delayed extraction. -gg 3/15/2001 */
681      if (PLR_FLAGGED(victim, PLR_NOTDEADYET) || MOB_FLAGGED(victim, MOB_NOTDEADYET))
682        return (-1);
683  
684      log("SYSERR: Attempt to damage corpse '%s' in room #%d by '%s'.",
685  		GET_NAME(victim), GET_ROOM_VNUM(IN_ROOM(victim)), GET_NAME(ch));
686      die(victim);
687      return (-1);			/* -je, 7/7/92 */
688    }
689  
690    /* peaceful rooms */
691    if (ch != victim && ROOM_FLAGGED(IN_ROOM(ch), ROOM_PEACEFUL)) {
692      send_to_char(ch, "This room just has such a peaceful, easy feeling...\r\n");
693      return (0);
694    }
695  
696    /* shopkeeper protection */
697    if (!ok_damage_shopkeeper(ch, victim))
698      return (0);
699  
700    /* You can't damage an immortal! */
701    if (!IS_NPC(victim) && (GET_LEVEL(victim) >= LVL_IMMORT))
702      dam = 0;
703  
704    if (victim != ch) {
705      /* Start the attacker fighting the victim */
706      if (GET_POS(ch) > POS_STUNNED && (FIGHTING(ch) == NULL))
707        set_fighting(ch, victim);
708  
709      /* Start the victim fighting the attacker */
710      if (GET_POS(victim) > POS_STUNNED && (FIGHTING(victim) == NULL)) {
711        set_fighting(victim, ch);
712        if (MOB_FLAGGED(victim, MOB_MEMORY) && !IS_NPC(ch))
713  	remember(victim, ch);
714      }
715    }
716  
717    /* If you attack a pet, it hates your guts */
718    if (victim->master == ch)
719      stop_follower(victim);
720  
721    /* If the attacker is invisible, he becomes visible */
722    if (AFF_FLAGGED(ch, AFF_INVISIBLE | AFF_HIDE))
723      appear(ch);
724  
725    /* Cut damage in half if victim has sanct, to a minimum 1 */
726    if (AFF_FLAGGED(victim, AFF_SANCTUARY) && dam >= 2)
727      dam /= 2;
728  
729    /* Check for PK if this is not a PK MUD */
730    if (!pk_allowed) {
731      check_killer(ch, victim);
732      if (PLR_FLAGGED(ch, PLR_KILLER) && (ch != victim))
733        dam = 0;
734    }
735  
736    /* Set the maximum damage per round and subtract the hit points */
737    dam = MAX(MIN(dam, 100), 0);
738    GET_HIT(victim) -= dam;
739  
740    /* Gain exp for the hit */
741    if (ch != victim)
742      gain_exp(ch, GET_LEVEL(victim) * dam);
743  
744    update_pos(victim);
745  
746    /*
747     * skill_message sends a message from the messages file in lib/misc.
748     * dam_message just sends a generic "You hit $n extremely hard.".
749     * skill_message is preferable to dam_message because it is more
750     * descriptive.
751     * 
752     * If we are _not_ attacking with a weapon (i.e. a spell), always use
753     * skill_message. If we are attacking with a weapon: If this is a miss or a
754     * death blow, send a skill_message if one exists; if not, default to a
755     * dam_message. Otherwise, always send a dam_message.
756     */
757    if (!IS_WEAPON(attacktype))
758      skill_message(dam, ch, victim, attacktype);
759    else {
760      if (GET_POS(victim) == POS_DEAD || dam == 0) {
761        if (!skill_message(dam, ch, victim, attacktype))
762  	dam_message(dam, ch, victim, attacktype);
763      } else {
764        dam_message(dam, ch, victim, attacktype);
765      }
766    }
767  
768    /* Use send_to_char -- act() doesn't send message if you are DEAD. */
769    switch (GET_POS(victim)) {
770    case POS_MORTALLYW:
771      act("$n is mortally wounded, and will die soon, if not aided.", TRUE, victim, 0, 0, TO_ROOM);
772      send_to_char(victim, "You are mortally wounded, and will die soon, if not aided.\r\n");
773      break;
774    case POS_INCAP:
775      act("$n is incapacitated and will slowly die, if not aided.", TRUE, victim, 0, 0, TO_ROOM);
776      send_to_char(victim, "You are incapacitated an will slowly die, if not aided.\r\n");
777      break;
778    case POS_STUNNED:
779      act("$n is stunned, but will probably regain consciousness again.", TRUE, victim, 0, 0, TO_ROOM);
780      send_to_char(victim, "You're stunned, but will probably regain consciousness again.\r\n");
781      break;
782    case POS_DEAD:
783      act("$n is dead!  R.I.P.", FALSE, victim, 0, 0, TO_ROOM);
784      send_to_char(victim, "You are dead!  Sorry...\r\n");
785      break;
786  
787    default:			/* >= POSITION SLEEPING */
788      if (dam > (GET_MAX_HIT(victim) / 4))
789        send_to_char(victim, "That really did HURT!\r\n");
790  
791      if (GET_HIT(victim) < (GET_MAX_HIT(victim) / 4)) {
792        send_to_char(victim, "%sYou wish that your wounds would stop BLEEDING so much!%s\r\n",
793  		CCRED(victim, C_SPR), CCNRM(victim, C_SPR));
794        if (ch != victim && MOB_FLAGGED(victim, MOB_WIMPY))
795  	do_flee(victim, NULL, 0, 0);
796      }
797      if (!IS_NPC(victim) && GET_WIMP_LEV(victim) && (victim != ch) &&
798  	GET_HIT(victim) < GET_WIMP_LEV(victim) && GET_HIT(victim) > 0) {
799        send_to_char(victim, "You wimp out, and attempt to flee!\r\n");
800        do_flee(victim, NULL, 0, 0);
801      }
802      break;
803    }
804  
805    /* Help out poor linkless people who are attacked */
806    if (!IS_NPC(victim) && !(victim->desc) && GET_POS(victim) > POS_STUNNED) {
807      do_flee(victim, NULL, 0, 0);
808      if (!FIGHTING(victim)) {
809        act("$n is rescued by divine forces.", FALSE, victim, 0, 0, TO_ROOM);
810        GET_WAS_IN(victim) = IN_ROOM(victim);
811        char_from_room(victim);
812        char_to_room(victim, 0);
813      }
814    }
815  
816    /* stop someone from fighting if they're stunned or worse */
817    if (GET_POS(victim) <= POS_STUNNED && FIGHTING(victim) != NULL)
818      stop_fighting(victim);
819  
820    /* Uh oh.  Victim died. */
821    if (GET_POS(victim) == POS_DEAD) {
822      if (ch != victim && (IS_NPC(victim) || victim->desc)) {
823        if (AFF_FLAGGED(ch, AFF_GROUP))
824  	group_gain(ch, victim);
825        else
826          solo_gain(ch, victim);
827      }
828  
829      if (!IS_NPC(victim)) {
830        mudlog(BRF, LVL_IMMORT, TRUE, "%s killed by %s at %s", GET_NAME(victim), GET_NAME(ch), world[IN_ROOM(victim)].name);
831        if (MOB_FLAGGED(ch, MOB_MEMORY))
832  	forget(ch, victim);
833      }
834      die(victim);
835      return (-1);
836    }
837    return (dam);
838  }
839  
840  
841  /*
842   * Calculate the THAC0 of the attacker.
843   *
844   * 'victim' currently isn't used but you could use it for special cases like
845   * weapons that hit evil creatures easier or a weapon that always misses
846   * attacking an animal.
847   */
848  int compute_thaco(struct char_data *ch, struct char_data *victim)
849  {
850    int calc_thaco;
851  
852    if (!IS_NPC(ch))
853      calc_thaco = thaco(GET_CLASS(ch), GET_LEVEL(ch));
854    else		/* THAC0 for monsters is set in the HitRoll */
855      calc_thaco = 20;
856    calc_thaco -= str_app[STRENGTH_APPLY_INDEX(ch)].tohit;
857    calc_thaco -= GET_HITROLL(ch);
858    calc_thaco -= (int) ((GET_INT(ch) - 13) / 1.5);	/* Intelligence helps! */
859    calc_thaco -= (int) ((GET_WIS(ch) - 13) / 1.5);	/* So does wisdom */
860  
861    return calc_thaco;
862  }
863  
864  
865  void hit(struct char_data *ch, struct char_data *victim, int type)
866  {
867    struct obj_data *wielded = GET_EQ(ch, WEAR_WIELD);
868    int w_type, victim_ac, calc_thaco, dam, diceroll;
869  
870    /* Do some sanity checking, in case someone flees, etc. */
871    if (IN_ROOM(ch) != IN_ROOM(victim)) {
872      if (FIGHTING(ch) && FIGHTING(ch) == victim)
873        stop_fighting(ch);
874      return;
875    }
876  
877    /* Find the weapon type (for display purposes only) */
878    if (wielded && GET_OBJ_TYPE(wielded) == ITEM_WEAPON)
879      w_type = GET_OBJ_VAL(wielded, 3) + TYPE_HIT;
880    else {
881      if (IS_NPC(ch) && ch->mob_specials.attack_type != 0)
882        w_type = ch->mob_specials.attack_type + TYPE_HIT;
883      else
884        w_type = TYPE_HIT;
885    }
886  
887    /* Calculate chance of hit. Lower THAC0 is better for attacker. */
888    calc_thaco = compute_thaco(ch, victim);
889  
890    /* Calculate the raw armor including magic armor.  Lower AC is better for defender. */
891    victim_ac = compute_armor_class(victim) / 10;
892  
893    /* roll the die and take your chances... */
894    diceroll = rand_number(1, 20);
895  
896    /*
897     * Decide whether this is a hit or a miss.
898     *
899     *  Victim asleep = hit, otherwise:
900     *     1   = Automatic miss.
901     *   2..19 = Checked vs. AC.
902     *    20   = Automatic hit.
903     */
904    if (diceroll == 20 || !AWAKE(victim))
905      dam = TRUE;
906    else if (diceroll == 1)
907      dam = FALSE;
908    else
909      dam = (calc_thaco - diceroll <= victim_ac);
910  
911    if (!dam)
912      /* the attacker missed the victim */
913      damage(ch, victim, 0, type == SKILL_BACKSTAB ? SKILL_BACKSTAB : w_type);
914    else {
915      /* okay, we know the guy has been hit.  now calculate damage. */
916  
917      /* Start with the damage bonuses: the damroll and strength apply */
918      dam = str_app[STRENGTH_APPLY_INDEX(ch)].todam;
919      dam += GET_DAMROLL(ch);
920  
921      /* Maybe holding arrow? */
922      if (wielded && GET_OBJ_TYPE(wielded) == ITEM_WEAPON) {
923        /* Add weapon-based damage if a weapon is being wielded */
924        dam += dice(GET_OBJ_VAL(wielded, 1), GET_OBJ_VAL(wielded, 2));
925      } else {
926        /* If no weapon, add bare hand damage instead */
927        if (IS_NPC(ch))
928  	dam += dice(ch->mob_specials.damnodice, ch->mob_specials.damsizedice);
929        else
930  	dam += rand_number(0, 2);	/* Max 2 bare hand damage for players */
931      }
932  
933      /*
934       * Include a damage multiplier if victim isn't ready to fight:
935       *
936       * Position sitting  1.33 x normal
937       * Position resting  1.66 x normal
938       * Position sleeping 2.00 x normal
939       * Position stunned  2.33 x normal
940       * Position incap    2.66 x normal
941       * Position mortally 3.00 x normal
942       *
943       * Note, this is a hack because it depends on the particular
944       * values of the POSITION_XXX constants.
945       */
946      if (GET_POS(victim) < POS_FIGHTING)
947        dam *= 1 + (POS_FIGHTING - GET_POS(victim)) / 3;
948  
949      /* at least 1 hp damage min per hit */
950      dam = MAX(1, dam);
951  
952      if (type == SKILL_BACKSTAB)
953        damage(ch, victim, dam * backstab_mult(GET_LEVEL(ch)), SKILL_BACKSTAB);
954      else
955        damage(ch, victim, dam, w_type);
956    }
957  }
958  
959  
960  
961  /* control the fights going on.  Called every 2 seconds from comm.c. */
962  void perform_violence(void)
963  {
964    struct char_data *ch;
965  
966    for (ch = combat_list; ch; ch = next_combat_list) {
967      next_combat_list = ch->next_fighting;
968  
969      if (FIGHTING(ch) == NULL || IN_ROOM(ch) != IN_ROOM(FIGHTING(ch))) {
970        stop_fighting(ch);
971        continue;
972      }
973  
974      if (IS_NPC(ch)) {
975        if (GET_MOB_WAIT(ch) > 0) {
976  	GET_MOB_WAIT(ch) -= PULSE_VIOLENCE;
977  	continue;
978        }
979        GET_MOB_WAIT(ch) = 0;
980        if (GET_POS(ch) < POS_FIGHTING) {
981  	GET_POS(ch) = POS_FIGHTING;
982  	act("$n scrambles to $s feet!", TRUE, ch, 0, 0, TO_ROOM);
983        }
984      }
985  
986      if (GET_POS(ch) < POS_FIGHTING) {
987        send_to_char(ch, "You can't fight while sitting!!\r\n");
988        continue;
989      }
990  
991      hit(ch, FIGHTING(ch), TYPE_UNDEFINED);
992      if (MOB_FLAGGED(ch, MOB_SPEC) && GET_MOB_SPEC(ch) && !MOB_FLAGGED(ch, MOB_NOTDEADYET)) {
993        char actbuf[MAX_INPUT_LENGTH] = "";
994        (GET_MOB_SPEC(ch)) (ch, ch, 0, actbuf);
995      }
996    }
997  }