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 }