00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00035 #include <assert.h>
00036 #include <global.h>
00037 #include <living.h>
00038 #include <material.h>
00039 #include <skills.h>
00040
00041 #ifndef __CEXTRACT__
00042 #include <sproto.h>
00043 #endif
00044
00045 #include <sounds.h>
00046
00047
00048
00049 static void slow_living(object *op, object *hitter, int dam);
00050 static void deathstrike_living(object *op, object *hitter, int *dam);
00051 static int adj_attackroll(object *hitter, object *target);
00052 static int is_aimed_missile(object *op);
00053 static int did_make_save_item(object *op, int type, object *originator);
00054 static void poison_living(object *op, object *hitter, int dam);
00055
00063 static void cancellation(object *op) {
00064 object *tmp;
00065
00066 if (op->invisible)
00067 return;
00068
00069 if (QUERY_FLAG(op, FLAG_ALIVE) || op->type == CONTAINER || op->type == THROWN_OBJ) {
00070
00071 for (tmp = op->inv; tmp != NULL; tmp = tmp->below)
00072 if (!did_make_save_item(tmp, AT_CANCELLATION, op))
00073 cancellation(tmp);
00074 } else if (FABS(op->magic) <= (rndm(0, 5))) {
00075
00076
00077 op->magic = 0;
00078 CLEAR_FLAG(op, FLAG_DAMNED);
00079 CLEAR_FLAG(op, FLAG_CURSED);
00080 CLEAR_FLAG(op, FLAG_KNOWN_MAGICAL);
00081 CLEAR_FLAG(op, FLAG_KNOWN_CURSED);
00082 if (op->env && op->env->type == PLAYER) {
00083 esrv_update_item(UPD_FLAGS, op->env, op);
00084 }
00085 }
00086 }
00087
00103 static int did_make_save_item(object *op, int type, object *originator) {
00104 int i, roll, saves = 0, attacks = 0, number;
00105 materialtype_t *mt;
00106
00107 if (op->materialname == NULL) {
00108 for (mt = materialt; mt != NULL && mt->next != NULL; mt = mt->next) {
00109 if (op->material&mt->material)
00110 break;
00111 }
00112 } else
00113 mt = name_to_material(op->materialname);
00114 if (mt == NULL)
00115 return TRUE;
00116 roll = rndm(1, 20);
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126 if (type != AT_MAGIC)
00127 type &= ~(AT_CONFUSION|AT_DRAIN|AT_GHOSTHIT|AT_POISON|AT_SLOW|
00128 AT_PARALYZE|AT_TURN_UNDEAD|AT_FEAR|AT_DEPLETE|AT_DEATH|
00129 AT_COUNTERSPELL|AT_HOLYWORD|AT_BLIND|AT_LIFE_STEALING|
00130 AT_MAGIC);
00131
00132 if (type == 0)
00133 return TRUE;
00134 if (roll == 20)
00135 return TRUE;
00136 if (roll == 1)
00137 return FALSE;
00138
00139 for (number = 0; number < NROFATTACKS; number++) {
00140 i = 1<<number;
00141 if (!(i&type))
00142 continue;
00143 attacks++;
00144 if (op->resist[number] == 100)
00145 saves++;
00146 else if (roll >= mt->save[number]-op->magic-op->resist[number]/100)
00147 saves++;
00148 else if ((20-mt->save[number])/3 > originator->stats.dam)
00149 saves++;
00150 }
00151
00152 if (saves == attacks || attacks == 0)
00153 return TRUE;
00154 if ((saves == 0) || (rndm(1, attacks) > saves))
00155 return FALSE;
00156 return TRUE;
00157 }
00158
00171 void save_throw_object(object *op, uint32 type, object *originator) {
00172 if (!did_make_save_item(op, type, originator)) {
00173 object *env = op->env;
00174 object *inv;
00175 int x = op->x, y = op->y;
00176 mapstruct *m = op->map;
00177
00178 op = stop_item(op);
00179 if (op == NULL)
00180 return;
00181
00182
00183 inv = op->inv;
00184 while (inv != NULL) {
00185 if (inv->type == RUNE)
00186 spring_trap(inv, originator);
00187 inv = inv->below;
00188 }
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198 if (type&(AT_FIRE|AT_ELECTRICITY)
00199 && op->other_arch
00200 && QUERY_FLAG(op, FLAG_IS_LIGHTABLE)) {
00201 const char *arch = op->other_arch->name;
00202
00203 op = decrease_ob_nr(op, 1);
00204 if (op)
00205 fix_stopped_item(op, m, originator);
00206 if ((op = create_archetype(arch)) != NULL) {
00207 if (env) {
00208 op->x = env->x,
00209 op->y = env->y;
00210 insert_ob_in_ob(op, env);
00211 } else {
00212 op->x = x,
00213 op->y = y;
00214 insert_ob_in_map(op, m, originator, 0);
00215 }
00216 }
00217 return;
00218 }
00219 if (type&AT_CANCELLATION) {
00220 cancellation(op);
00221 fix_stopped_item(op, m, originator);
00222 return;
00223 }
00224 if (op->nrof > 1) {
00225 op = decrease_ob_nr(op, rndm(0, op->nrof-1));
00226 if (op)
00227 fix_stopped_item(op, m, originator);
00228 } else {
00229 if (!QUERY_FLAG(op, FLAG_REMOVED))
00230 remove_ob(op);
00231 free_object(op);
00232 }
00233 if (type&(AT_FIRE|AT_ELECTRICITY)) {
00234 if (env) {
00235 op = create_archetype("burnout");
00236 op->x = env->x,
00237 op->y = env->y;
00238 insert_ob_in_ob(op, env);
00239 } else {
00240 replace_insert_ob_in_map("burnout", originator);
00241 }
00242 }
00243 return;
00244 }
00245
00246 if (type&AT_COLD
00247 && (op->resist[ATNR_COLD] < 50)
00248 && !QUERY_FLAG(op, FLAG_NO_PICK)
00249 && (RANDOM()&2)) {
00250 object *tmp;
00251 archetype *at = find_archetype("icecube");
00252
00253 if (at == NULL)
00254 return;
00255 op = stop_item(op);
00256 if (op == NULL)
00257 return;
00258 if ((tmp = present_arch(at, op->map, op->x, op->y)) == NULL) {
00259 tmp = arch_to_object(at);
00260 tmp->x = op->x,
00261 tmp->y = op->y;
00262
00263
00264
00265
00266 tmp->move_slow_penalty = 0;
00267 tmp->move_slow = 0;
00268 insert_ob_in_map(tmp, op->map, originator, 0);
00269 }
00270 if (!QUERY_FLAG(op, FLAG_REMOVED))
00271 remove_ob(op);
00272 (void)insert_ob_in_ob(op, tmp);
00273 return;
00274 }
00275 }
00276
00292 int hit_map(object *op, int dir, uint32 type, int full_hit) {
00293 object *tmp, *next;
00294 mapstruct *map;
00295 sint16 x, y;
00296 int retflag = 0;
00297
00298 tag_t op_tag, next_tag = 0;
00299
00300 if (QUERY_FLAG(op, FLAG_FREED)) {
00301 LOG(llevError, "BUG: hit_map(): free object\n");
00302 return 0;
00303 }
00304
00305 if (QUERY_FLAG(op, FLAG_REMOVED) || op->env != NULL) {
00306 LOG(llevError, "BUG: hit_map(): hitter (arch %s, name %s) not on a map\n", op->arch->name, op->name);
00307 return 0;
00308 }
00309
00310 if (!op->map) {
00311 LOG(llevError, "BUG: hit_map(): %s has no map\n", op->name);
00312 return 0;
00313 }
00314
00315 if (op->head)
00316 op = op->head;
00317
00318 op_tag = op->count;
00319
00320 map = op->map;
00321 x = op->x+freearr_x[dir];
00322 y = op->y+freearr_y[dir];
00323 if (get_map_flags(map, &map, x, y, &x, &y)&P_OUT_OF_MAP)
00324 return 0;
00325
00326
00327
00328
00329
00330 if (type&AT_COUNTERSPELL) {
00331 counterspell(op, dir);
00332
00333
00334
00335
00336 if (!(type&~(AT_COUNTERSPELL|AT_MAGIC))) {
00337 return 0;
00338 }
00339 type &= ~AT_COUNTERSPELL;
00340 }
00341
00342 if (type&AT_CHAOS) {
00343 shuffle_attack(op, 1);
00344 update_object(op, UP_OBJ_FACE);
00345 type &= ~AT_CHAOS;
00346 }
00347
00348 next = GET_MAP_OB(map, x, y);
00349 if (next)
00350 next_tag = next->count;
00351
00352 while (next) {
00353 if (was_destroyed(next, next_tag)) {
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364 break;
00365 }
00366 tmp = next;
00367 next = tmp->above;
00368 if (next)
00369 next_tag = next->count;
00370
00371 if (QUERY_FLAG(tmp, FLAG_FREED)) {
00372 LOG(llevError, "BUG: hit_map(): found freed object\n");
00373 break;
00374 }
00375
00376
00377
00378
00379
00380 if (tmp->map != map || tmp->x != x || tmp->y != y)
00381 continue;
00382
00383 if (tmp->head)
00384 tmp = tmp->head;
00385
00386
00387 if (tmp->type == TRANSPORT) {
00388 object *pl;
00389
00390 for (pl = tmp->inv; pl; pl = pl->below) {
00391 if (pl->type == PLAYER)
00392 hit_player(pl, op->stats.dam, op, type, full_hit);
00393 }
00394 }
00395
00396 if (QUERY_FLAG(tmp, FLAG_ALIVE)) {
00397 hit_player(tmp, op->stats.dam, op, type, full_hit);
00398 retflag |= 1;
00399 if (was_destroyed(op, op_tag))
00400 break;
00401 }
00402
00403
00404
00405
00406
00407
00408
00409 else if ((tmp->material || tmp->materialname) && op->stats.dam > 0 && !tmp->move_block) {
00410 save_throw_object(tmp, type, op);
00411 if (was_destroyed(op, op_tag))
00412 break;
00413 }
00414 }
00415 return 0;
00416 }
00417
00433 static void attack_message(int dam, int type, object *op, object *hitter) {
00434 char buf[MAX_BUF], buf1[MAX_BUF], buf2[MAX_BUF];
00435 int i, found = 0;
00436 mapstruct *map;
00437 object *next, *tmp;
00438
00439
00440
00441
00442
00443
00444 if (dam == 9998 && op->type == DOOR) {
00445 sprintf(buf1, "unlock %s", op->name);
00446 sprintf(buf2, " unlocks");
00447 found++;
00448 }
00449 if (dam < 0) {
00450 sprintf(buf1, "hit %s", op->name);
00451 sprintf(buf2, " hits");
00452 found++;
00453 } else if (dam == 0) {
00454 sprintf(buf1, "missed %s", op->name);
00455 sprintf(buf2, " misses");
00456 found++;
00457 } else if ((hitter->type == DISEASE
00458 || hitter->type == SYMPTOM
00459 || hitter->type == POISONING
00460 || (type&AT_POISON && IS_LIVE(op))) && !found) {
00461 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_SUFFER][i].level != -1; i++)
00462 if (dam < attack_mess[ATM_SUFFER][i].level
00463 || attack_mess[ATM_SUFFER][i+1].level == -1) {
00464 sprintf(buf1, "%s %s%s", attack_mess[ATM_SUFFER][i].buf1, op->name, attack_mess[ATM_SUFFER][i].buf2);
00465 sprintf(buf2, "%s", attack_mess[ATM_SUFFER][i].buf3);
00466 found++;
00467 break;
00468 }
00469 } else if (op->type == DOOR && !found) {
00470 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_DOOR][i].level != -1; i++)
00471 if (dam < attack_mess[ATM_DOOR][i].level
00472 || attack_mess[ATM_DOOR][i+1].level == -1) {
00473 sprintf(buf1, "%s %s%s", attack_mess[ATM_DOOR][i].buf1, op->name, attack_mess[ATM_DOOR][i].buf2);
00474 sprintf(buf2, "%s", attack_mess[ATM_DOOR][i].buf3);
00475 found++;
00476 break;
00477 }
00478 } else if (hitter->type == PLAYER && IS_LIVE(op)) {
00479 if (USING_SKILL(hitter, SK_KARATE)) {
00480 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_KARATE][i].level != -1; i++)
00481 if (dam < attack_mess[ATM_KARATE][i].level
00482 || attack_mess[ATM_KARATE][i+1].level == -1) {
00483 sprintf(buf1, "%s %s%s", attack_mess[ATM_KARATE][i].buf1, op->name, attack_mess[ATM_KARATE][i].buf2);
00484 sprintf(buf2, "%s", attack_mess[ATM_KARATE][i].buf3);
00485 found++;
00486 break;
00487 }
00488 } else if (USING_SKILL(hitter, SK_CLAWING)) {
00489 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_CLAW][i].level != -1; i++)
00490 if (dam < attack_mess[ATM_CLAW][i].level
00491 || attack_mess[ATM_CLAW][i+1].level == -1) {
00492 sprintf(buf1, "%s %s%s", attack_mess[ATM_CLAW][i].buf1, op->name, attack_mess[ATM_CLAW][i].buf2);
00493 sprintf(buf2, "%s", attack_mess[ATM_CLAW][i].buf3);
00494 found++;
00495 break;
00496 }
00497 } else if (USING_SKILL(hitter, SK_PUNCHING)) {
00498 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_PUNCH][i].level != -1; i++)
00499 if (dam < attack_mess[ATM_PUNCH][i].level
00500 || attack_mess[ATM_PUNCH][i+1].level == -1) {
00501 sprintf(buf1, "%s %s%s", attack_mess[ATM_PUNCH][i].buf1, op->name, attack_mess[ATM_PUNCH][i].buf2);
00502 sprintf(buf2, "%s", attack_mess[ATM_PUNCH][i].buf3);
00503 found++;
00504 break;
00505 }
00506 } else if (USING_SKILL(hitter, SK_WRAITH_FEED)) {
00507 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_WRAITH_FEED][i].level != -1; i++)
00508 if (dam < attack_mess[ATM_WRAITH_FEED][i].level) {
00509 sprintf(buf1, "%s %s%s", attack_mess[ATM_WRAITH_FEED][i].buf1, op->name, attack_mess[ATM_WRAITH_FEED][i].buf2);
00510 sprintf(buf2, "%s", attack_mess[ATM_WRAITH_FEED][i].buf3);
00511 found++;
00512 break;
00513 }
00514 }
00515 }
00516 if (found) {
00517
00518 } else if (IS_ARROW(hitter) && (type == AT_PHYSICAL || type == AT_MAGIC)) {
00519 sprintf(buf1, "hit");
00520 for (i = 0; i < MAXATTACKMESS; i++)
00521 if (dam < attack_mess[ATM_ARROW][i].level
00522 || attack_mess[ATM_ARROW][i+1].level == -1) {
00523 sprintf(buf2, "%s", attack_mess[ATM_ARROW][i].buf3);
00524 found++;
00525 break;
00526 }
00527 } else if (type&AT_DRAIN && IS_LIVE(op)) {
00528
00529 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_DRAIN][i].level != -1; i++)
00530 if (dam < attack_mess[ATM_DRAIN][i].level
00531 || attack_mess[ATM_DRAIN][i+1].level == -1) {
00532 sprintf(buf1, "%s %s%s", attack_mess[ATM_DRAIN][i].buf1, op->name, attack_mess[ATM_DRAIN][i].buf2);
00533 sprintf(buf2, "%s", attack_mess[ATM_DRAIN][i].buf3);
00534 found++;
00535 break;
00536 }
00537 } else if (type&AT_ELECTRICITY && IS_LIVE(op)) {
00538 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_ELEC][i].level != -1; i++)
00539 if (dam < attack_mess[ATM_ELEC][i].level
00540 || attack_mess[ATM_ELEC][i+1].level == -1) {
00541 sprintf(buf1, "%s %s%s", attack_mess[ATM_ELEC][i].buf1, op->name, attack_mess[ATM_ELEC][i].buf2);
00542 sprintf(buf2, "%s", attack_mess[ATM_ELEC][i].buf3);
00543 found++;
00544 break;
00545 }
00546 } else if (type&AT_COLD && IS_LIVE(op)) {
00547 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_COLD][i].level != -1; i++)
00548 if (dam < attack_mess[ATM_COLD][i].level
00549 || attack_mess[ATM_COLD][i+1].level == -1) {
00550 sprintf(buf1, "%s %s%s", attack_mess[ATM_COLD][i].buf1, op->name, attack_mess[ATM_COLD][i].buf2);
00551 sprintf(buf2, "%s", attack_mess[ATM_COLD][i].buf3);
00552 found++;
00553 break;
00554 }
00555 } else if (type&AT_FIRE) {
00556 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_FIRE][i].level != -1; i++)
00557 if (dam < attack_mess[ATM_FIRE][i].level
00558 || attack_mess[ATM_FIRE][i+1].level == -1) {
00559 sprintf(buf1, "%s %s%s", attack_mess[ATM_FIRE][i].buf1, op->name, attack_mess[ATM_FIRE][i].buf2);
00560 sprintf(buf2, "%s", attack_mess[ATM_FIRE][i].buf3);
00561 found++;
00562 break;
00563 }
00564 } else if (hitter->current_weapon != NULL) {
00565 int mtype;
00566
00567 switch (hitter->current_weapon->weapontype) {
00568 case WEAP_HIT: mtype = ATM_BASIC; break;
00569 case WEAP_SLASH: mtype = ATM_SLASH; break;
00570 case WEAP_PIERCE: mtype = ATM_PIERCE; break;
00571 case WEAP_CLEAVE: mtype = ATM_CLEAVE; break;
00572 case WEAP_SLICE: mtype = ATM_SLICE; break;
00573 case WEAP_STAB: mtype = ATM_STAB; break;
00574 case WEAP_WHIP: mtype = ATM_WHIP; break;
00575 case WEAP_CRUSH: mtype = ATM_CRUSH; break;
00576 case WEAP_BLUD: mtype = ATM_BLUD; break;
00577 default: mtype = ATM_BASIC; break;
00578 }
00579 for (i = 0; i < MAXATTACKMESS && attack_mess[mtype][i].level != -1; i++)
00580 if (dam < attack_mess[mtype][i].level
00581 || attack_mess[mtype][i+1].level == -1) {
00582 sprintf(buf1, "%s %s%s", attack_mess[mtype][i].buf1, op->name, attack_mess[mtype][i].buf2);
00583 sprintf(buf2, "%s", attack_mess[mtype][i].buf3);
00584 found++;
00585 break;
00586 }
00587 } else {
00588 for (i = 0; i < MAXATTACKMESS && attack_mess[ATM_BASIC][i].level != -1; i++)
00589 if (dam < attack_mess[ATM_BASIC][i].level
00590 || attack_mess[ATM_BASIC][i+1].level == -1) {
00591 sprintf(buf1, "%s %s%s", attack_mess[ATM_BASIC][i].buf1, op->name, attack_mess[ATM_BASIC][i].buf2);
00592 sprintf(buf2, "%s", attack_mess[ATM_BASIC][i].buf3);
00593 found++;
00594 break;
00595 }
00596 }
00597
00598 if (!found) {
00599 sprintf(buf1, "hit");
00600 sprintf(buf2, "hits");
00601 }
00602
00603
00604 if (!(hitter->type == PLAYER || (get_owner(hitter) != NULL && hitter->owner->type == PLAYER)))
00605 return;
00606
00607
00608 if (type&AT_MAGIC && rndm(0, 5))
00609 return;
00610
00611
00612
00613 if (op->type == PLAYER
00614 && rndm(0, 1)
00615 && (get_owner(hitter) == NULL ? hitter->type : hitter->owner->type) == PLAYER) {
00616 if (get_owner(hitter) != NULL)
00617 sprintf(buf, "%s's %s %s you.", hitter->owner->name, hitter->name, buf2);
00618 else {
00619 sprintf(buf, "%s%s you.", hitter->name, buf2);
00620 if (dam != 0) {
00621 if (hitter->chosen_skill)
00622 play_sound_player_only(op->contr, SOUND_TYPE_HIT_BY, op, 0, hitter->chosen_skill->name);
00623 else if (dam < 10)
00624 play_sound_player_only(op->contr, SOUND_TYPE_HIT_BY, op, 0, "low");
00625 else if (dam < 20)
00626 play_sound_player_only(op->contr, SOUND_TYPE_HIT_BY, op, 0, "medium");
00627 else
00628 play_sound_player_only(op->contr, SOUND_TYPE_HIT_BY, op, 0, "high");
00629 }
00630 }
00631 draw_ext_info(NDI_BLACK, 0, op, MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_WAS_HIT, buf, NULL);
00632 }
00633
00634
00635 if(hitter->type == PLAYER && rndm(0, 2) == 0) {
00636 sprintf(buf, "You %s.", buf1);
00637 if (dam != 0) {
00638 if (hitter->chosen_skill)
00639 play_sound_player_only(hitter->contr, SOUND_TYPE_HIT, hitter, 0, hitter->chosen_skill->name);
00640 else if (dam < 10)
00641 play_sound_player_only(hitter->contr, SOUND_TYPE_HIT, hitter, 0, "low");
00642 else if (dam < 20)
00643 play_sound_player_only(hitter->contr, SOUND_TYPE_HIT, hitter, 0, "medium");
00644 else
00645 play_sound_player_only(hitter->contr, SOUND_TYPE_HIT, hitter, 0, "high");
00646 }
00647 draw_ext_info(NDI_BLACK, 0, hitter, MSG_TYPE_ATTACK, MSG_TYPE_ATTACK_DID_HIT,
00648 buf, NULL);
00649 } else if (get_owner(hitter) != NULL && hitter->owner->type == PLAYER) {
00650
00651 if (hitter->type == SPELL_EFFECT
00652 && (hitter->subtype == SP_EXPLOSION || hitter->subtype == SP_BULLET || hitter->subtype == SP_CONE)) {
00653 i = 4;
00654 map = hitter->map;
00655 if (out_of_map(map, hitter->x, hitter->y))
00656 return;
00657 next = GET_MAP_OB(map, hitter->x, hitter->y);
00658 if (next)
00659 while (next) {
00660 if (next->type == SPELL_EFFECT
00661 && (next->subtype == SP_EXPLOSION || next->subtype == SP_BULLET || next->subtype == SP_CONE))
00662 i *= 3;
00663 tmp = next;
00664 next = tmp->above;
00665 }
00666 if (i < 0)
00667 return;
00668 if (rndm(0, i) != 0)
00669 return;
00670 } else if (rndm(0, 5) != 0)
00671 return;
00672 play_sound_map(SOUND_TYPE_HIT, hitter->owner, 0, "hit");
00673 draw_ext_info_format(NDI_BLACK, 0, hitter->owner, MSG_TYPE_ATTACK, MSG_TYPE_ATTACK_PET_HIT,
00674 "Your %s%s %s.",
00675 "Your %s%s %s.",
00676 hitter->name, buf2, op->name);
00677 }
00678 }
00679
00691 static int get_attack_mode(object **target, object **hitter,
00692 int *simple_attack) {
00693 if (QUERY_FLAG(*target, FLAG_FREED) || QUERY_FLAG(*hitter, FLAG_FREED)) {
00694 LOG(llevError, "BUG: get_attack_mode(): freed object\n");
00695 return 1;
00696 }
00697 if ((*target)->head)
00698 *target = (*target)->head;
00699 if ((*hitter)->head)
00700 *hitter = (*hitter)->head;
00701 if ((*hitter)->env != NULL || (*target)->env != NULL) {
00702 *simple_attack = 1;
00703 return 0;
00704 }
00705 if (QUERY_FLAG(*target, FLAG_REMOVED)
00706 || QUERY_FLAG(*hitter, FLAG_REMOVED)
00707 || (*hitter)->map == NULL
00708 || !on_same_map((*hitter), (*target))) {
00709 LOG(llevError, "BUG: hitter (arch %s, name %s) with no relation to target\n", (*hitter)->arch->name, (*hitter)->name);
00710 return 1;
00711 }
00712 *simple_attack = 0;
00713 return 0;
00714 }
00715
00729 static int abort_attack(object *target, object *hitter, int simple_attack) {
00730 int new_mode;
00731
00732 if (hitter->env == target || target->env == hitter)
00733 new_mode = 1;
00734 else if (QUERY_FLAG(hitter, FLAG_REMOVED)
00735 || QUERY_FLAG(target, FLAG_REMOVED)
00736 || hitter->map == NULL || !on_same_map(hitter, target))
00737 return 1;
00738 else
00739 new_mode = 0;
00740 return new_mode != simple_attack;
00741 }
00742
00743 static void thrown_item_effect(object *, object *);
00744
00760 static int attack_ob_simple(object *op, object *hitter, int base_dam,
00761 int base_wc) {
00762 int simple_attack, roll, dam = 0;
00763 uint32 type;
00764 const char *op_name = NULL;
00765 tag_t op_tag, hitter_tag;
00766
00767 if (get_attack_mode(&op, &hitter, &simple_attack))
00768 goto error;
00769
00770
00771 if (execute_event(op, EVENT_ATTACK, hitter, hitter->current_weapon ? hitter->current_weapon : hitter, NULL, SCRIPT_FIX_ALL) != 0)
00772 return 0;
00773
00774
00775
00776
00777 if (hitter->type == PLAYER) {
00778 if (hitter->current_weapon != NULL) {
00779
00780 if (execute_event(hitter->current_weapon, EVENT_ATTACK,
00781 hitter, op, NULL, SCRIPT_FIX_ALL) != 0)
00782 return 0;
00783 if (hitter->current_weapon->anim_suffix)
00784 apply_anim_suffix(hitter, hitter->current_weapon->anim_suffix);
00785 }
00786 }
00787 op_tag = op->count;
00788 hitter_tag = hitter->count;
00789
00790
00791
00792
00793 if (!simple_attack && QUERY_FLAG(op, FLAG_MONSTER)
00794 && op->speed_left > -(FABS(op->speed))*0.3) {
00795
00796
00797
00798
00799
00800 op->speed_left--;
00801 process_object(op);
00802 if (was_destroyed(op, op_tag)
00803 || was_destroyed(hitter, hitter_tag)
00804 || abort_attack(op, hitter, simple_attack))
00805 goto error;
00806 }
00807
00808 add_refcount(op_name = op->name);
00809
00810 roll = random_roll(1, 20, hitter, PREFER_HIGH);
00811
00812
00813 if (!simple_attack)
00814 roll += adj_attackroll(hitter, op);
00815
00816
00817 if (roll == 20 || op->stats.ac >= base_wc-roll) {
00818 int hitdam = base_dam;
00819 if (settings.casting_time == TRUE) {
00820 if ((hitter->type == PLAYER)&&(hitter->casting_time > -1)) {
00821 hitter->casting_time = -1;
00822 draw_ext_info(NDI_UNIQUE, 0, hitter, MSG_TYPE_ATTACK, MSG_TYPE_ATTACK_FUMBLE,
00823 "You attacked and lost your spell!", NULL);
00824 }
00825 if ((op->casting_time > -1)&&(hitdam > 0)) {
00826 op->casting_time = -1;
00827 if (op->type == PLAYER) {
00828 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTACK, MSG_TYPE_ATTACK_FUMBLE,
00829 "You were hit and lost your spell!", NULL);
00830 draw_ext_info_format(NDI_ALL|NDI_UNIQUE, 5, NULL,
00831 MSG_TYPE_ATTACK, MSG_TYPE_ATTACK_FUMBLE,
00832 "%s was hit by %s and lost a spell.",
00833 "%s was hit by %s and lost a spell.",
00834 op_name, hitter->name);
00835 }
00836 }
00837 }
00838 if (!simple_attack) {
00839
00840
00841
00842 if (QUERY_FLAG(op, FLAG_SLEEP))
00843 CLEAR_FLAG(op, FLAG_SLEEP);
00844
00845
00846
00847 if (op->type != PLAYER && !can_see_enemy(op, hitter)
00848 && !get_owner(op) && rndm(0, op->stats.Int))
00849 npc_call_help(op);
00850
00851
00852 if (op->hide && QUERY_FLAG(hitter, FLAG_ALIVE)) {
00853 make_visible(op);
00854 if (op->type == PLAYER)
00855 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_WAS_HIT,
00856 "You were hit by a wild attack. You are no longer hidden!",
00857 NULL);
00858 }
00859
00860
00861
00862
00863
00864
00865 thrown_item_effect(hitter, op);
00866 if (was_destroyed(hitter, hitter_tag)
00867 || was_destroyed(op, op_tag)
00868 || abort_attack(op, hitter, simple_attack))
00869 goto leave;
00870 }
00871
00872
00873
00874
00875 if (hitdam <= 0)
00876 hitdam = 1;
00877
00878 type = hitter->attacktype;
00879 if (!type)
00880 type = AT_PHYSICAL;
00881
00882 if (!simple_attack && QUERY_FLAG(op, FLAG_HITBACK)
00883 && QUERY_FLAG(hitter, FLAG_ALIVE)) {
00884 if (op->attacktype&AT_ACID && hitter->type == PLAYER)
00885 draw_ext_info(NDI_UNIQUE, 0, hitter, MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_WAS_HIT,
00886 "You are splashed by acid!\n", NULL);
00887 hit_player(hitter, random_roll(0, (op->stats.dam), hitter, PREFER_LOW), op, op->attacktype, 1);
00888 if (was_destroyed(op, op_tag)
00889 || was_destroyed(hitter, hitter_tag)
00890 || abort_attack(op, hitter, simple_attack))
00891 goto leave;
00892 }
00893
00894
00895
00896
00897 dam = hit_player(op, random_roll(1, hitdam, hitter, PREFER_HIGH), hitter, type, 1);
00898 if (was_destroyed(op, op_tag)
00899 || was_destroyed(hitter, hitter_tag)
00900 || abort_attack(op, hitter, simple_attack))
00901 goto leave;
00902 }
00903
00904
00905
00906
00907 goto leave;
00908
00909 error:
00910 dam = 1;
00911 goto leave;
00912
00913 leave:
00914 if (op_name)
00915 free_string(op_name);
00916
00917 return dam;
00918 }
00919
00929 int attack_ob(object *op, object *hitter) {
00930
00931 if (hitter->head)
00932 hitter = hitter->head;
00933 return attack_ob_simple(op, hitter, hitter->stats.dam, hitter->stats.wc);
00934 }
00935
00946 static int stick_arrow(object *op, object *tmp) {
00947
00948
00949
00950
00951
00952
00953 if (op->weight <= 5000 && tmp->stats.hp >= 0) {
00954 if (tmp->head != NULL)
00955 tmp = tmp->head;
00956 remove_ob(op);
00957 op = insert_ob_in_ob(op, tmp);
00958 return 1;
00959 } else
00960 return 0;
00961 }
00962
00975 object *hit_with_arrow(object *op, object *victim) {
00976 object *container, *hitter;
00977 int hit_something = 0;
00978 tag_t victim_tag, hitter_tag;
00979 sint16 victim_x, victim_y;
00980 mapstruct *victim_map;
00981 const char *old_skill = NULL;
00982
00983
00984 for (hitter = op->inv; hitter; hitter = hitter->below) {
00985 if (hitter->type == EVENT_CONNECTOR)
00986 continue;
00987 container = op;
00988
00989
00990 remove_ob(hitter);
00991 if (free_no_drop(hitter))
00992 return NULL;
00993 insert_ob_in_map(hitter, container->map, hitter, INS_NO_MERGE|INS_NO_WALK_ON);
00994 break;
00995
00996
00997
00998
00999 }
01000 if (!hitter) {
01001 container = NULL;
01002 hitter = op;
01003 if (free_no_drop(hitter))
01004 return NULL;
01005 }
01006
01007
01008 victim_x = victim->x;
01009 victim_y = victim->y;
01010 victim_map = victim->map;
01011 victim_tag = victim->count;
01012 hitter_tag = hitter->count;
01013
01014
01015
01016
01017
01018 if (execute_event(op, EVENT_ATTACK, hitter, victim, NULL, SCRIPT_FIX_ALL) == 0) {
01019
01020
01021
01022
01023
01024
01025 if (container != NULL) {
01026 old_skill = hitter->skill;
01027 hitter->skill = add_refcount(container->skill);
01028 }
01029 hit_something = attack_ob_simple(victim, hitter, op->stats.dam, op->stats.wc);
01030 }
01031
01032
01033
01034
01035
01036
01037 if (was_destroyed(hitter, hitter_tag) || hitter->env != NULL) {
01038 if (container) {
01039 remove_ob(container);
01040 free_object(container);
01041 }
01042 return NULL;
01043 }
01044 if (container != NULL) {
01045 free_string(hitter->skill);
01046 hitter->skill = old_skill;
01047 }
01048
01049
01050
01051
01052 if (hit_something && op->speed <= 10.0) {
01053
01054 if (container == NULL) {
01055 hitter = fix_stopped_arrow(hitter);
01056 if (hitter == NULL)
01057 return NULL;
01058 } else {
01059 remove_ob(container);
01060 free_object(container);
01061 }
01062
01063
01064 if (!was_destroyed(victim, victim_tag)
01065 && stick_arrow(hitter, victim))
01066 return NULL;
01067
01068
01069
01070
01071
01072
01073
01074
01075 if (victim_x != hitter->x || victim_y != hitter->y) {
01076 remove_ob(hitter);
01077 hitter->x = victim_x;
01078 hitter->y = victim_y;
01079 insert_ob_in_map(hitter, victim_map, hitter, 0);
01080 } else {
01081
01082 merge_ob(hitter, NULL);
01083 }
01084 return NULL;
01085 }
01086
01087 if (hit_something && op->speed >= 10.0)
01088 op->speed -= 1.0;
01089
01090
01091 if (container) {
01092 remove_ob(hitter);
01093 insert_ob_in_ob(hitter, container);
01094 }
01095 return op;
01096 }
01104 static void tear_down_wall(object *op) {
01105 int perc = 0;
01106
01107 if (!op->stats.maxhp) {
01108 LOG(llevError, "TEAR_DOWN wall %s had no maxhp.\n", op->name);
01109 perc = 1;
01110 } else if (!GET_ANIM_ID(op)) {
01111
01112 if (op->stats.hp < 0) {
01113 remove_ob(op);
01114 free_object(op);
01115
01116
01117 }
01118 return;
01119 }
01120 perc = NUM_ANIMATIONS(op)-((int)NUM_ANIMATIONS(op)*op->stats.hp)/op->stats.maxhp;
01121 if (perc >= (int)NUM_ANIMATIONS(op))
01122 perc = NUM_ANIMATIONS(op)-1;
01123 else if (perc < 1)
01124 perc = 1;
01125 SET_ANIMATION(op, perc);
01126 update_object(op, UP_OBJ_FACE);
01127 if (perc == NUM_ANIMATIONS(op)-1) {
01128 if (op->face == blank_face) {
01129
01130 remove_ob(op);
01131 free_object(op);
01132
01133
01134
01135
01136 } else {
01137 CLEAR_FLAG(op, FLAG_BLOCKSVIEW);
01138 update_all_los(op->map, op->x, op->y);
01139 op->move_block = 0;
01140 CLEAR_FLAG(op, FLAG_ALIVE);
01141 }
01142 }
01143 }
01144
01152 static void scare_creature(object *target, object *hitter) {
01153 object *owner = get_owner(hitter);
01154
01155 if (!owner)
01156 owner = hitter;
01157
01158 SET_FLAG(target, FLAG_SCARED);
01159 if (!target->enemy)
01160 target->enemy = owner;
01161 }
01162
01179 static int hit_with_one_attacktype(object *op, object *hitter, int dam, uint32 attacknum) {
01180
01181 int doesnt_slay = 1;
01182 char name_hitter[MAX_BUF], name_op[MAX_BUF];
01183
01184
01185 if (attacknum >= NROFATTACKS) {
01186 LOG(llevError, "hit_with_one_attacktype: Invalid attacknumber passed: %u\n", attacknum);
01187 return 0;
01188 }
01189
01190 if (dam < 0) {
01191 LOG(llevError, "hit_with_one_attacktype called with negative damage: %d\n", dam);
01192 return 0;
01193 }
01194
01195 if (hitter->current_weapon && hitter->current_weapon->discrete_damage != NULL)
01196 dam = hitter->current_weapon->discrete_damage[attacknum];
01197 else if (hitter->discrete_damage != NULL)
01198 dam = hitter->discrete_damage[attacknum];
01199
01200
01201
01202 if (attacknum == ATNR_INTERNAL)
01203 return dam;
01204
01205 if (hitter->slaying) {
01206 if (((op->race != NULL) && strstr(hitter->slaying, op->race))
01207 || (op->arch && (op->arch->name != NULL) && strstr(op->arch->name, hitter->slaying))) {
01208 doesnt_slay = 0;
01209 dam *= 3;
01210 }
01211 }
01212
01213
01214 if (op->resist[attacknum]) {
01215
01216
01217 dam *= (100-op->resist[attacknum]);
01218 if (dam >= 100)
01219 dam /= 100;
01220 else
01221 dam = (dam > (random_roll(0, 99, op, PREFER_LOW))) ? 1 : 0;
01222 }
01223
01224
01225
01226
01227
01228
01229 if ((op->resist[attacknum] >= 100)
01230 && doesnt_slay
01231 && (attacknum != ATNR_ACID))
01232 return 0;
01233
01234
01235
01236 switch (attacknum) {
01237 case ATNR_PHYSICAL:
01238
01239 check_physically_infect(op, hitter);
01240 break;
01241
01242
01243
01244
01245
01246
01247
01248 case ATNR_CONFUSION:
01249 case ATNR_POISON:
01250 case ATNR_SLOW:
01251 case ATNR_PARALYZE:
01252 case ATNR_FEAR:
01253 case ATNR_CANCELLATION:
01254 case ATNR_DEPLETE:
01255 case ATNR_BLIND: {
01256
01257
01258
01259 int level_diff = MIN(110, MAX(0, op->level-hitter->level));
01260
01261
01262
01263
01264
01265
01266 if (op->speed
01267 && (QUERY_FLAG(op, FLAG_MONSTER) || op->type == PLAYER)
01268 && !(rndm(0, (attacknum == ATNR_SLOW ? 6 : 3)-1))
01269 && !did_make_save(op, level_diff, op->resist[attacknum]/10)) {
01270
01271 if (attacknum == ATNR_CONFUSION)
01272 confuse_living(op, hitter, dam);
01273 else if (attacknum == ATNR_POISON)
01274 poison_living(op, hitter, dam);
01275 else if (attacknum == ATNR_SLOW)
01276 slow_living(op, hitter, dam);
01277 else if (attacknum == ATNR_PARALYZE)
01278 paralyze_living(op, hitter, dam);
01279 else if (attacknum == ATNR_FEAR)
01280 scare_creature(op, hitter);
01281 else if (attacknum == ATNR_CANCELLATION)
01282 cancellation(op);
01283 else if (attacknum == ATNR_DEPLETE)
01284 drain_stat(op);
01285 else if (attacknum == ATNR_BLIND
01286 && !QUERY_FLAG(op, FLAG_UNDEAD)
01287 && !QUERY_FLAG(op, FLAG_GENERATOR))
01288 blind_living(op, hitter, dam);
01289 }
01290 dam = 0;
01291 }
01292 break;
01293
01294 case ATNR_ACID: {
01295 int flag = 0;
01296
01297
01298
01299 if (!op_on_battleground(op, NULL, NULL, NULL)
01300 && (op->resist[ATNR_ACID] < 50)) {
01301 object *tmp;
01302
01303 for (tmp = op->inv; tmp != NULL; tmp = tmp->below) {
01304 if (tmp->invisible)
01305 continue;
01306 if (!QUERY_FLAG(tmp, FLAG_APPLIED)
01307 || (tmp->resist[ATNR_ACID] >= 10))
01308
01309 continue;
01310 if (!(tmp->material&M_IRON))
01311 continue;
01312 if (tmp->magic < -4)
01313 continue;
01314 if (tmp->type == RING
01315
01316 || tmp->type == GIRDLE
01317 || tmp->type == AMULET
01318 || tmp->type == WAND
01319 || tmp->type == ROD
01320 || tmp->type == HORN)
01321 continue;
01322
01323
01324 if (rndm(0, dam+4) > random_roll(0, 39, op, PREFER_HIGH)+2*tmp->magic) {
01325 if (op->type == PLAYER) {
01326
01327 query_name(hitter, name_hitter, MAX_BUF);
01328 query_name(tmp, name_op, MAX_BUF);
01329 draw_ext_info_format(NDI_UNIQUE|NDI_RED, 0, op,
01330 MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_WAS_HIT,
01331 "The %s's acid corrodes your %s!",
01332 "The %s's acid corrodes your %s!",
01333 name_hitter, name_op);
01334 }
01335 flag = 1;
01336 tmp->magic--;
01337 if (op->type == PLAYER)
01338 esrv_update_item(UPD_NAME, op, tmp);
01339 }
01340 }
01341 if (flag)
01342 fix_object(op);
01343 }
01344 }
01345 break;
01346
01347 case ATNR_DRAIN: {
01348
01349
01350
01351 int rate;
01352
01353 if (op->resist[ATNR_DRAIN] >= 0)
01354 rate = 50+op->resist[ATNR_DRAIN]/2;
01355 else
01356 rate = 5000/(100-op->resist[ATNR_DRAIN]);
01357
01358
01359
01360 if (!rate)
01361 return 0;
01362
01363 if (op->stats.exp <= rate) {
01364 if (op->type == GOLEM)
01365 dam = 999;
01366 else
01367
01368 dam = hit_with_one_attacktype(op, hitter, dam, ATNR_PHYSICAL);
01369 } else {
01370
01371 if (hitter->stats.hp < hitter->stats.maxhp
01372 && (op->level > hitter->level)
01373 && random_roll(0, (op->level-hitter->level+2), hitter, PREFER_HIGH) > 3)
01374 hitter->stats.hp++;
01375
01376
01377
01378
01379
01380
01381
01382
01383 if (!op_on_battleground(hitter, NULL, NULL, NULL) && !QUERY_FLAG(op, FLAG_WAS_WIZ)) {
01384 object *owner = get_owner(hitter);
01385
01386 if (owner && owner != hitter) {
01387 if (op->type != PLAYER || owner->type != PLAYER)
01388 change_exp(owner, op->stats.exp/(rate*2),
01389 hitter->chosen_skill ? hitter->chosen_skill->skill : NULL, SK_EXP_TOTAL);
01390 } else if (op->type != PLAYER || hitter->type != PLAYER) {
01391 change_exp(hitter, op->stats.exp/(rate*2), hitter->chosen_skill ? hitter->chosen_skill->skill : NULL, 0);
01392 }
01393 change_exp(op, -op->stats.exp/rate, NULL, 0);
01394 }
01395 dam = 1;
01396
01397
01398
01399 }
01400 }
01401 break;
01402
01403 case ATNR_TURN_UNDEAD: {
01404 if (QUERY_FLAG(op, FLAG_UNDEAD)) {
01405 object *owner = get_owner(hitter) == NULL ? hitter : get_owner(hitter);
01406 const object *god = find_god(determine_god(owner));
01407 int div = 1;
01408
01409
01410
01411 if (!god
01412 || !god->slaying
01413 || strstr(god->slaying, undead_name) == NULL)
01414 div = 2;
01415
01416 if (op->level*div < (turn_bonus[owner->stats.Wis]+owner->level+(op->resist[ATNR_TURN_UNDEAD]/100)))
01417 scare_creature(op, owner);
01418 } else
01419 dam = 0;
01420 }
01421 break;
01422
01423 case ATNR_DEATH:
01424 deathstrike_living(op, hitter, &dam);
01425 break;
01426
01427 case ATNR_CHAOS:
01428 query_name(op, name_op, MAX_BUF);
01429 query_name(hitter, name_hitter, MAX_BUF);
01430 LOG(llevError, "%s was hit by %s with non-specific chaos.\n", name_op, name_hitter);
01431 dam = 0;
01432 break;
01433
01434 case ATNR_COUNTERSPELL:
01435 query_name(op, name_op, MAX_BUF);
01436 query_name(hitter, name_hitter, MAX_BUF);
01437 LOG(llevError, "%s was hit by %s with counterspell attack.\n", name_op, name_hitter);
01438 dam = 0;
01439
01440
01441
01442
01443 break;
01444
01445 case ATNR_HOLYWORD: {
01446
01447
01448
01449 object *owner = get_owner(hitter) == NULL ? hitter : get_owner(hitter);
01450
01451
01452 if ((op->level+(op->resist[ATNR_HOLYWORD]/100)) < owner->level+turn_bonus[owner->stats.Wis])
01453 scare_creature(op, owner);
01454 }
01455 break;
01456
01457 case ATNR_LIFE_STEALING: {
01458 int new_hp;
01459
01460
01461
01462
01463
01464
01465
01466
01467
01468
01469
01470 int dam_modifier = is_wraith_pl(hitter) ? 200 : 3000;
01471
01472
01473 if (op->type == GOLEM
01474 || (QUERY_FLAG(op, FLAG_UNDEAD))
01475 || !(QUERY_FLAG(op, FLAG_ALIVE))
01476 || (op->type == DOOR))
01477 return 0;
01478
01479 if (op->resist[ATNR_DRAIN] >= op->resist[ATNR_LIFE_STEALING])
01480 dam = (dam*(100-op->resist[ATNR_DRAIN]))/dam_modifier;
01481 else
01482 dam = (dam*(100-op->resist[ATNR_LIFE_STEALING]))/dam_modifier;
01483
01484 if (dam > (op->stats.hp+1))
01485 dam = op->stats.hp+1;
01486 new_hp = hitter->stats.hp+dam;
01487 if (new_hp > hitter->stats.maxhp)
01488 new_hp = hitter->stats.maxhp;
01489 if (new_hp > hitter->stats.hp)
01490 hitter->stats.hp = new_hp;
01491
01492
01493 if (is_wraith_pl(hitter)) {
01494 if (hitter->stats.food+dam >= 999)
01495 hitter->stats.food = 999;
01496 else
01497 hitter->stats.food += dam;
01498 fix_object(hitter);
01499 }
01500 }
01501 }
01502 return dam;
01503 }
01504
01534 static int kill_object(object *op, int dam, object *hitter, int type) {
01535 char buf[MAX_BUF];
01536 const char *skill;
01537 int maxdam = 0;
01538 int battleg = 0;
01539 int pk = 0;
01540 object *owner = NULL;
01541 object *skop = NULL;
01542 sstring death_animation;
01543
01544 if (op->stats.hp >= 0)
01545 return -1;
01546
01547
01548 if (execute_event(op, EVENT_DEATH, hitter, NULL, NULL, SCRIPT_FIX_ALL) != 0)
01549 return 0;
01550
01551 execute_global_event(EVENT_GKILL, op, hitter);
01552
01553 if ((op->map) && (death_animation = get_ob_key_value(op, "death_animation")) != NULL) {
01554 object *death = create_archetype(death_animation);
01555
01556 if (death) {
01557 death->map = op->map;
01558 death->x = op->x;
01559 death->y = op->y;
01560 insert_ob_in_map(death, op->map, op, 0);
01561 }
01562 }
01563
01564
01565
01566
01567
01568 maxdam = dam+op->stats.hp+1;
01569
01570 if (QUERY_FLAG(op, FLAG_BLOCKSVIEW))
01571 update_all_los(op->map, op->x, op->y);
01572
01573 if (op->type == DOOR) {
01574 op->speed = 0.1;
01575 update_ob_speed(op);
01576 op->speed_left = -0.05;
01577 return maxdam;
01578 }
01579 if (QUERY_FLAG(op, FLAG_FRIENDLY) && op->type != PLAYER) {
01580 remove_friendly_object(op);
01581 if (get_owner(op) != NULL
01582 && op->owner->type == PLAYER
01583 && op->owner->contr->ranges[range_golem] == op) {
01584 op->owner->contr->ranges[range_golem] = NULL;
01585 op->owner->contr->golem_count = 0;
01586 } else
01587 LOG(llevError, "BUG: hit_player(): Encountered golem without owner.\n");
01588
01589 remove_ob(op);
01590 free_object(op);
01591 return maxdam;
01592 }
01593
01594
01595
01596 owner = get_owner(hitter);
01597 if (owner == NULL)
01598 owner = hitter;
01599
01600
01601 if (op_on_battleground(op, NULL, NULL, NULL))
01602 battleg = 1;
01603
01604
01605 if (op->type == PLAYER && owner->type == PLAYER)
01606 pk = 1;
01607
01608
01609 if (owner->type == PLAYER) {
01610 char name_op[MAX_BUF], name_hitter[MAX_BUF];
01611
01612 query_name(op, name_op, MAX_BUF);
01613 if (hitter)
01614 query_name(hitter, name_hitter, MAX_BUF);
01615 else
01616 name_hitter[0] = '\0';
01617
01618
01619
01620
01621
01622 if (op->type == PLAYER && !battleg) {
01623 time_t t = time(NULL);
01624 struct tm *tmv;
01625 char buf[256];
01626 char name[MAX_BUF];
01627
01628 tmv = localtime(&t);
01629 strftime(buf, 256, "%a %b %d %H:%M:%S %Y", tmv);
01630 query_name(op, name, MAX_BUF);
01631
01632 LOG(llevInfo, "%s PLAYER_KILL_PLAYER: %s (%s) killed %s\n", buf, owner->name, owner->contr->socket.host, name);
01633 }
01634
01635
01636
01637
01638
01639 if (owner->level < op->level*2|| op->stats.exp > 1000) {
01640 if (owner != hitter) {
01641 char killed[MAX_BUF], with[MAX_BUF];
01642
01643 query_name(op, killed, MAX_BUF);
01644 query_name(hitter, with, MAX_BUF);
01645 draw_ext_info_format(NDI_BLACK, 0, owner, MSG_TYPE_ATTACK, MSG_TYPE_ATTACK_DID_KILL,
01646 "You killed %s with %s.",
01647 "You killed %s with %s.",
01648 killed, with);
01649 } else {
01650 char killed[MAX_BUF];
01651
01652 query_name(op, killed, MAX_BUF);
01653 draw_ext_info_format(NDI_BLACK, 0, owner, MSG_TYPE_ATTACK, MSG_TYPE_ATTACK_DID_KILL,
01654 "You killed %s.",
01655 "You killed %s.",
01656 killed);
01657 }
01658
01659 if (hitter->type == PLAYER)
01660 play_sound_map(SOUND_TYPE_HIT, owner, 0, "kill");
01661 }
01662
01663
01664
01665
01666
01667
01668
01669
01670
01671 if (op->type == PLAYER && owner != op && !battleg)
01672 change_luck(owner, -settings.pk_luck_penalty);
01673
01674
01675
01676
01677
01678 skill = NULL;
01679 if (hitter->skill && hitter->type != PLAYER)
01680 skill = hitter->skill;
01681 else if (owner->chosen_skill) {
01682 skill = owner->chosen_skill->skill;
01683 skop = owner->chosen_skill;
01684 } else if (QUERY_FLAG(owner, FLAG_READY_WEAPON))
01685 skill = owner->current_weapon->skill;
01686 else
01687 LOG(llevError, "kill_object - unable to find skill that killed monster\n");
01688
01689
01690
01691
01692 if ((!skop || skop->type != SKILL) && skill) {
01693 int i;
01694
01695 for (i = 0; i < NUM_SKILLS; i++)
01696 if (owner->contr->last_skill_ob[i]
01697 && !strcmp(owner->contr->last_skill_ob[i]->skill, skill)) {
01698 skop = owner->contr->last_skill_ob[i];
01699 break;
01700 }
01701 }
01702 }
01703 else {
01704 skill = NULL;
01705 }
01706
01707
01708 if (owner != hitter) {
01709 char name_op[MAX_BUF], name_hitter[MAX_BUF];
01710
01711 query_name(op, name_op, MAX_BUF);
01712 query_name(hitter, name_hitter, MAX_BUF);
01713 (void)sprintf(buf, "%s killed %s with %s%s.", owner->name, name_op, name_hitter, battleg ? " (duel)" : (pk ? " (pk)" : ""));
01714 } else {
01715 (void)sprintf(buf, "%s killed %s%s%s.", hitter->name, op->name,
01716 (QUERY_FLAG(hitter, FLAG_MONSTER)) || hitter->type == PLAYER ?
01717 " in hand to hand combat" : "", battleg ? " (duel)" : (pk ? " (pk)" : ""));
01718 }
01719
01720 if (!skop)
01721 skop = hitter->chosen_skill;
01722 if (!skill && skop)
01723 skill = skop->skill;
01724
01725 draw_ext_info(NDI_ALL, op->type == PLAYER ? 1 : 10, NULL, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_PLAYER,
01726 buf, NULL);
01727
01728
01729
01730 if (owner != op && !QUERY_FLAG(op, FLAG_WAS_WIZ)) {
01731 sint64 exp;
01732
01733 exp = calc_skill_exp(owner, op, skop);
01734
01735
01736 if (op->type == PLAYER) {
01737 if (battleg) {
01738 draw_ext_info(NDI_UNIQUE, 0, owner, MSG_TYPE_ATTACK, MSG_TYPE_ATTACK_DID_KILL,
01739 "Your foe has fallen!\nVICTORY!!!", NULL);
01740 } else {
01741 exp = settings.pk_max_experience_percent*exp/100;
01742 if (settings.pk_max_experience >= 0)
01743 exp = MIN(settings.pk_max_experience, exp);
01744
01745 exp = check_exp_loss(op, exp);
01746 }
01747 }
01748
01749
01750
01751
01752
01753 if (!settings.simple_exp)
01754 exp = exp/2;
01755
01756
01757
01758
01759 if (battleg)
01760 exp = 0;
01761
01762 #ifdef PARTY_KILL_LOG
01763 if (owner->type == PLAYER && owner->contr->party != NULL) {
01764 char name[MAX_BUF];
01765
01766 query_name(owner, name, MAX_BUF);
01767 add_kill_to_party(party, name, query_name(op), exp);
01768 }
01769 #endif
01770 share_exp(owner, exp, skill, SK_EXP_TOTAL);
01771
01772 }
01773
01774 if (op->type != PLAYER) {
01775 if (QUERY_FLAG(op, FLAG_FRIENDLY)) {
01776 object *owner1 = get_owner(op);
01777
01778 if (owner1 != NULL && owner1->type == PLAYER) {
01779
01780
01781 draw_ext_info_format(NDI_UNIQUE, 0, owner1, MSG_TYPE_ATTACK, MSG_TYPE_ATTACK_PET_DIED,
01782 "Your pet, the %s, is killed by %s.",
01783 "Your pet, the %s, is killed by %s.",
01784 op->name, hitter->name);
01785 }
01786 remove_friendly_object(op);
01787 }
01788 remove_ob(op);
01789 free_object(op);
01790
01791 } else {
01792 if (owner->type == PLAYER) {
01793 snprintf(op->contr->killer, BIG_NAME, "%s the %s", owner->name, owner->contr->title);
01794 } else {
01795 strncpy(op->contr->killer, hitter->name, BIG_NAME);
01796 op->contr->killer[BIG_NAME-1] = '\0';
01797 }
01798
01799 if (!QUERY_FLAG(op, FLAG_WIZ))
01800 kill_player(op);
01801 }
01802
01803
01804
01805 return maxdam;
01806 }
01807
01818 int friendly_fire(object *op, object *hitter) {
01819 object *owner;
01820 int friendlyfire;
01821
01822 if (hitter->head)
01823 hitter = hitter->head;
01824
01825 friendlyfire = 0;
01826
01827 if (op->type == PLAYER) {
01828 if (hitter->type == PLAYER && hitter->contr->peaceful == 1)
01829 return 1;
01830
01831 if ((owner = get_owner(hitter)) != NULL) {
01832 if (owner->type == PLAYER && owner->contr->peaceful == 1)
01833 friendlyfire = 2;
01834 }
01835
01836 if (hitter->type == SPELL
01837 || hitter->type == POISONING
01838 || hitter->type == DISEASE
01839 || hitter->type == RUNE)
01840 friendlyfire = 0;
01841 }
01842 return friendlyfire;
01843 }
01844
01868 int hit_player(object *op, int dam, object *hitter, uint32 type, int full_hit) {
01869 int maxdam = 0, ndam = 0, attacktype = 1, magic = (type&AT_MAGIC);
01870 int maxattacktype, attacknum;
01871 int body_attack = op && op->head;
01872 int simple_attack;
01873 tag_t op_tag, hitter_tag;
01874 int rtn_kill = 0;
01875 int friendlyfire;
01876
01877 if (get_attack_mode(&op, &hitter, &simple_attack))
01878 return 0;
01879
01880
01881 if (QUERY_FLAG(op, FLAG_WIZ) || QUERY_FLAG(op, FLAG_NO_DAMAGE))
01882 return 0;
01883
01884 op_tag = op->count;
01885 hitter_tag = hitter->count;
01886
01887 if (body_attack) {
01888
01889
01890
01891
01892
01893
01894
01895
01896
01897 if (type&(AT_PARALYZE|AT_SLOW)) {
01898 type &= ~(AT_PARALYZE|AT_SLOW);
01899 if (!type || type == AT_MAGIC)
01900 return 0;
01901 }
01902 }
01903
01904 if (!simple_attack && op->type == DOOR) {
01905 object *tmp;
01906
01907 for (tmp = op->inv; tmp != NULL; tmp = tmp->below)
01908 if (tmp->type == RUNE || tmp->type == TRAP) {
01909 spring_trap(tmp, hitter);
01910 if (was_destroyed(hitter, hitter_tag)
01911 || was_destroyed(op, op_tag)
01912 || abort_attack(op, hitter, simple_attack))
01913 return 0;
01914 break;
01915 }
01916 }
01917
01918 if (!QUERY_FLAG(op, FLAG_ALIVE) || op->stats.hp < 0) {
01919
01920
01921
01922 LOG(llevDebug, "victim (arch %s, name %s) already dead in hit_player()\n", op->arch->name, op->name);
01923 return 0;
01924 }
01925
01926 #ifdef ATTACK_DEBUG
01927 LOG(llevDebug, "hit player: attacktype %d, dam %d\n", type, dam);
01928 #endif
01929
01930 if (magic) {
01931
01932
01933 dam = dam*(100-op->resist[ATNR_MAGIC]);
01934 if (dam >= 100)
01935 dam /= 100;
01936 else
01937 dam = (dam > (rndm(0, 99))) ? 1 : 0;
01938 }
01939
01940
01941
01942
01943
01944
01945 if (type&AT_CHAOS) {
01946 int i;
01947
01948 i = rndm(0, 21);
01949 type = ATTACKS[i].attacktype|AT_MAGIC;
01950 }
01951
01952
01953
01954
01955
01956 if (type&AT_HOLYWORD) {
01957 const object *god;
01958
01959 if ((!hitter->slaying || (!(op->race && strstr(hitter->slaying, op->race))
01960 && !(op->name && strstr(hitter->slaying, op->name))))
01961 && (!QUERY_FLAG(op, FLAG_UNDEAD) || (hitter->title != NULL
01962 && (god = find_god(determine_god(hitter))) != NULL
01963 && god->race != NULL
01964 && strstr(god->race, undead_name) != NULL)))
01965 return 0;
01966 }
01967
01968 maxattacktype = type;
01969 for (attacknum = 0; attacknum < NROFATTACKS; attacknum++, attacktype = 1<<attacknum) {
01970
01971
01972
01973
01974 if ((attacktype == AT_MAGIC) && (type&~AT_MAGIC))
01975 continue;
01976
01977
01978
01979
01980
01981
01982 if (type&attacktype) {
01983 ndam = hit_with_one_attacktype(op, hitter, dam, attacknum);
01984
01985
01986
01987 if (ndam >= maxdam) {
01988 maxdam = ndam;
01989 maxattacktype = 1<<attacknum;
01990 }
01991
01992
01993 if (attacktype == AT_DEATH && ndam > 0)
01994 full_hit = 1;
01995 }
01996 }
01997
01998
01999
02000
02001
02002
02003 friendlyfire = friendly_fire(op, hitter);
02004 if (friendlyfire && maxdam) {
02005 maxdam = ((dam*settings.set_friendly_fire)/100)+1;
02006
02007 #ifdef ATTACK_DEBUG
02008 LOG(llevDebug, "Friendly fire (type:%d setting: %d%) did %d damage dropped to %d\n", friendlyfire, settings.set_friendly_fire, dam, maxdam);
02009 #endif
02010 }
02011
02012 if (!full_hit) {
02013 archetype *at;
02014 int area;
02015 int remainder;
02016
02017 area = 0;
02018 for (at = op->arch; at != NULL; at = at->more)
02019 area++;
02020 assert(area > 0);
02021
02022
02023
02024 remainder = 100*(maxdam%area)/area;
02025 maxdam /= area;
02026 if (RANDOM()%100 < remainder)
02027 maxdam++;
02028 }
02029
02030 #ifdef ATTACK_DEBUG
02031 LOG(llevDebug, "Attacktype %d did %d damage\n", type, maxdam);
02032 #endif
02033
02034 if (get_owner(hitter))
02035 op->enemy = hitter->owner;
02036 else if (QUERY_FLAG(hitter, FLAG_ALIVE))
02037 op->enemy = hitter;
02038
02039 if (QUERY_FLAG(op, FLAG_UNAGGRESSIVE) && op->type != PLAYER) {
02040
02041 CLEAR_FLAG(op, FLAG_UNAGGRESSIVE);
02042 npc_call_help(op);
02043 }
02044
02045 if (magic && did_make_save(op, op->level, 0))
02046 maxdam = maxdam/2;
02047
02048 attack_message(maxdam, maxattacktype, op, hitter);
02049
02050 op->stats.hp -= maxdam;
02051
02052
02053 if ((op->stats.hp >= 0)
02054 && (QUERY_FLAG(op, FLAG_MONSTER) || op->type == PLAYER)
02055 && op->stats.hp < (signed short)(((float)op->run_away/(float)100)*(float)op->stats.maxhp)) {
02056 if (QUERY_FLAG(op, FLAG_MONSTER))
02057 SET_FLAG(op, FLAG_RUN_AWAY);
02058 else
02059 scare_creature(op, hitter);
02060 }
02061
02062 if (QUERY_FLAG(op, FLAG_TEAR_DOWN)) {
02063 if (maxdam)
02064 tear_down_wall(op);
02065 return maxdam;
02066 }
02067
02068
02069 rtn_kill = kill_object(op, maxdam, hitter, type);
02070 if (rtn_kill != -1)
02071 return rtn_kill;
02072
02073
02074
02075
02076
02077 if (QUERY_FLAG(hitter, FLAG_ONE_HIT)) {
02078 if (QUERY_FLAG(hitter, FLAG_FRIENDLY))
02079 remove_friendly_object(hitter);
02080 remove_ob(hitter);
02081 free_object(hitter);
02082
02083 } else if (type&AT_PHYSICAL && !QUERY_FLAG(op, FLAG_FREED) && QUERY_FLAG(op, FLAG_SPLITTING)) {
02084 int i;
02085 int friendly = QUERY_FLAG(op, FLAG_FRIENDLY);
02086 int unaggressive = QUERY_FLAG(op, FLAG_UNAGGRESSIVE);
02087 object *owner = get_owner(op);
02088
02089 if (!op->other_arch) {
02090 LOG(llevError, "SPLITTING without other_arch error.\n");
02091 return maxdam;
02092 }
02093 remove_ob(op);
02094 for (i = 0; i < NROFNEWOBJS(op); i++) {
02095 object *tmp = arch_to_object(op->other_arch);
02096 int j;
02097
02098 tmp->stats.hp = op->stats.hp;
02099 if (friendly) {
02100 SET_FLAG(tmp, FLAG_FRIENDLY);
02101 add_friendly_object(tmp);
02102 tmp->attack_movement = PETMOVE;
02103 if (owner != NULL)
02104 set_owner(tmp, owner);
02105 }
02106 if (unaggressive)
02107 SET_FLAG(tmp, FLAG_UNAGGRESSIVE);
02108 j = find_first_free_spot(tmp, op->map, op->x, op->y);
02109 if (j == -1)
02110 free_object(tmp);
02111 else {
02112 tmp->x = op->x+freearr_x[j],
02113 tmp->y = op->y+freearr_y[j];
02114 insert_ob_in_map(tmp, op->map, NULL, 0);
02115 }
02116 }
02117 if (friendly)
02118 remove_friendly_object(op);
02119 free_object(op);
02120 } else if (type&AT_DRAIN && hitter->type == GRIMREAPER && hitter->value++ > 10) {
02121 remove_ob(hitter);
02122 free_object(hitter);
02123 }
02124 return maxdam;
02125 }
02126
02137 static void poison_living(object *op, object *hitter, int dam) {
02138 archetype *at = find_archetype("poisoning");
02139 object *tmp = present_arch_in_ob(at, op);
02140
02141 if (tmp == NULL) {
02142 if ((tmp = arch_to_object(at)) == NULL)
02143 LOG(llevError, "Failed to clone arch poisoning.\n");
02144 else {
02145 tmp = insert_ob_in_ob(tmp, op);
02146
02147
02148
02149
02150
02151
02152
02153
02154 if (QUERY_FLAG(hitter, FLAG_ALIVE))
02155 tmp->stats.dam += hitter->level/2;
02156 else
02157 tmp->stats.dam = dam;
02158
02159 copy_owner(tmp, hitter);
02160 if (hitter->skill && hitter->skill != tmp->skill) {
02161 if (tmp->skill)
02162 free_string(tmp->skill);
02163 tmp->skill = add_refcount(hitter->skill);
02164 }
02165
02166 tmp->stats.food += dam;
02167
02168 if (op->type == PLAYER) {
02169
02170 tmp->stats.Con = MAX(-(dam/4+1), -10);
02171 tmp->stats.Str = MAX(-(dam/3+2), -10);
02172 tmp->stats.Dex = MAX(-(dam/6+1), -10);
02173 tmp->stats.Int = MAX(-dam/7, -10);
02174 SET_FLAG(tmp, FLAG_APPLIED);
02175 fix_object(op);
02176 draw_ext_info(NDI_UNIQUE, 0, op,
02177 MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START,
02178 "You suddenly feel very ill.", NULL);
02179 }
02180 if (hitter->type == PLAYER)
02181 draw_ext_info_format(NDI_UNIQUE, 0, hitter,
02182 MSG_TYPE_ATTACK, MSG_TYPE_ATTACK_DID_HIT,
02183 "You poison %s.",
02184 "You poison %s.",
02185 op->name);
02186 else if (get_owner(hitter) != NULL && hitter->owner->type == PLAYER)
02187 draw_ext_info_format(NDI_UNIQUE, 0, hitter->owner,
02188 MSG_TYPE_ATTACK, MSG_TYPE_ATTACK_PET_HIT,
02189 "Your %s poisons %s.",
02190 "Your %s poisons %s.",
02191 hitter->name, op->name);
02192 }
02193 tmp->speed_left = 0;
02194 } else
02195 tmp->stats.food++;
02196 }
02197
02208 static void slow_living(object *op, object *hitter, int dam) {
02209 archetype *at = find_archetype("slowness");
02210 object *tmp;
02211
02212 if (at == NULL) {
02213 LOG(llevError, "Can't find slowness archetype.\n");
02214 }
02215 if ((tmp = present_arch_in_ob(at, op)) == NULL) {
02216 tmp = arch_to_object(at);
02217 tmp = insert_ob_in_ob(tmp, op);
02218 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START,
02219 "The world suddenly moves very fast!", NULL);
02220 } else
02221 tmp->stats.food++;
02222 SET_FLAG(tmp, FLAG_APPLIED);
02223 tmp->speed_left = 0;
02224 fix_object(op);
02225 }
02226
02237 void confuse_living(object *op, object *hitter, int dam) {
02238 object *tmp;
02239 int maxduration;
02240
02241 tmp = present_in_ob_by_name(FORCE, "confusion", op);
02242 if (!tmp) {
02243 tmp = create_archetype(FORCE_NAME);
02244 tmp = insert_ob_in_ob(tmp, op);
02245 }
02246
02247
02248
02249
02250 tmp->speed = 0.05;
02251 tmp->subtype = FORCE_CONFUSION;
02252 tmp->duration = 8+MAX(1, 5*(100-op->resist[ATNR_CONFUSION])/100);
02253 if (tmp->name)
02254 free_string(tmp->name);
02255 tmp->name = add_string("confusion");
02256 maxduration = MAX(2, 30*(100-op->resist[ATNR_CONFUSION])/100);
02257 if (tmp->duration > maxduration)
02258 tmp->duration = maxduration;
02259
02260 if (op->type == PLAYER && !QUERY_FLAG(op, FLAG_CONFUSED))
02261 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_START,
02262 "You suddenly feel very confused!", NULL);
02263 SET_FLAG(op, FLAG_CONFUSED);
02264 }
02265
02276 void blind_living(object *op, object *hitter, int dam) {
02277 object *tmp, *owner;
02278 char victim[MAX_BUF];
02279
02280
02281 if (op->resist[ATNR_BLIND] == 100)
02282 return;
02283
02284 tmp = present_in_ob(BLINDNESS, op);
02285 if (!tmp) {
02286 tmp = create_archetype("blindness");
02287 SET_FLAG(tmp, FLAG_BLIND);
02288 SET_FLAG(tmp, FLAG_APPLIED);
02289
02290
02291
02292 tmp->speed = tmp->speed*(100.0-(float)op->resist[ATNR_BLIND])/100;
02293
02294 tmp = insert_ob_in_ob(tmp, op);
02295 change_abil(op, tmp);
02296 fix_object(op);
02297
02298 if (hitter->owner)
02299 owner = get_owner(hitter);
02300 else
02301 owner = hitter;
02302
02303 query_name(op, victim, MAX_BUF);
02304 draw_ext_info_format(NDI_UNIQUE, 0, owner, MSG_TYPE_ATTACK, MSG_TYPE_ATTACK_DID_HIT,
02305 "Your attack blinds %s!",
02306 "Your attack blinds %s!",
02307 victim);
02308 }
02309 tmp->stats.food += dam;
02310 if (tmp->stats.food > 10)
02311 tmp->stats.food = 10;
02312 }
02313
02324 void paralyze_living(object *op, object *hitter, int dam) {
02325 float effect, max;
02326
02327
02328
02329
02330
02331
02332
02333
02334
02335
02336
02337
02338
02339
02340
02341
02342 effect = (float)dam*3.0*(100.0-op->resist[ATNR_PARALYZE])/100;
02343
02344 if (effect == 0)
02345 return;
02346
02347 op->speed_left -= FABS(op->speed)*effect;
02348
02349
02350
02351
02352 max = (100-op->resist[ATNR_PARALYZE])/2;
02353 if (op->speed_left < -(FABS(op->speed)*max))
02354 op->speed_left = (float)-(FABS(op->speed)*max);
02355
02356
02357 }
02358
02379 static void deathstrike_living(object *op, object *hitter, int *dam) {
02380 int atk_lev, def_lev, kill_lev;
02381
02382 if (hitter->slaying) {
02383 if (!((QUERY_FLAG(op, FLAG_UNDEAD) && strstr(hitter->slaying, undead_name))
02384 || (op->race && strstr(hitter->slaying, op->race))))
02385 return;
02386 } else
02387 if (QUERY_FLAG(op, FLAG_UNDEAD))
02388 return;
02389
02390 def_lev = op->level;
02391 if (def_lev < 1) {
02392 LOG(llevError, "BUG: arch %s, name %s with level < 1\n", op->arch->name, op->name);
02393 def_lev = 1;
02394 }
02395 atk_lev = (hitter->chosen_skill ? hitter->chosen_skill->level : hitter->level)/2;
02396
02397
02398 if (atk_lev >= def_lev) {
02399 kill_lev = random_roll(0, atk_lev-1, hitter, PREFER_HIGH);
02400
02401
02402
02403
02404
02405
02406 if (kill_lev >= def_lev) {
02407 *dam = op->stats.hp+10;
02408
02409
02410
02411
02412 *dam *= kill_lev/def_lev;
02413 }
02414 } else {
02415 *dam = 0;
02416 }
02417 }
02418
02431 static void thrown_item_effect(object *hitter, object *victim) {
02432 if (!QUERY_FLAG(hitter, FLAG_ALIVE)) {
02433
02434
02435
02436 switch (hitter->type) {
02437 case POTION:
02438
02439 if (QUERY_FLAG(victim, FLAG_ALIVE)
02440 && !QUERY_FLAG(victim, FLAG_UNDEAD)
02441 && (victim->resist[ATNR_MAGIC] < 60))
02442 (void)ob_apply(hitter, victim, 0);
02443 break;
02444
02445 case POISON:
02446
02447 if (QUERY_FLAG(victim, FLAG_ALIVE)
02448 && !QUERY_FLAG(victim, FLAG_UNDEAD)
02449 && (victim->resist[ATNR_POISON] < 60))
02450 (void)ob_apply(victim, hitter, 0);
02451 break;
02452
02453
02454
02455
02456
02457
02458 }
02459 }
02460 }
02461
02471 static int adj_attackroll(object *hitter, object *target) {
02472 object *attacker = hitter;
02473 int adjust = 0;
02474
02475
02476 if (!target || !hitter || !hitter->map || !target->map || !on_same_map(hitter, target)) {
02477 LOG(llevError, "BUG: adj_attackroll(): hitter and target not on same map\n");
02478 return 0;
02479 }
02480
02481
02482 if (is_aimed_missile(hitter)) {
02483 if ((attacker = get_owner(hitter)) == NULL)
02484 attacker = hitter;
02485
02486
02487
02488 if (QUERY_FLAG(attacker, FLAG_REMOVED))
02489 attacker = hitter;
02490 } else if (!QUERY_FLAG(hitter, FLAG_ALIVE))
02491 return 0;
02492
02493
02494
02495
02496 if (!can_see_enemy(attacker, target)) {
02497
02498 if (target->invisible || QUERY_FLAG(attacker, FLAG_BLIND))
02499 adjust -= 10;
02500
02501 else if (target->map && target->map->darkness > 0 && !stand_in_light(target))
02502 adjust -= target->map->darkness;
02503 }
02504
02505 if (QUERY_FLAG(attacker, FLAG_SCARED))
02506 adjust -= 3;
02507
02508 if (QUERY_FLAG(target, FLAG_UNAGGRESSIVE))
02509 adjust += 1;
02510
02511 if (QUERY_FLAG(target, FLAG_SCARED))
02512 adjust += 1;
02513
02514 if (QUERY_FLAG(attacker, FLAG_CONFUSED))
02515 adjust -= 3;
02516
02517
02518 if ((attacker->move_type&target->move_type) == 0)
02519 adjust -= 2;
02520
02521 return adjust;
02522 }
02523
02532 static int is_aimed_missile(object *op) {
02533
02534
02535
02536 if (op && (op->move_type&MOVE_FLYING)) {
02537 if (op->type == ARROW || op->type == THROWN_OBJ)
02538 return 1;
02539 else if (op->type == SPELL_EFFECT
02540 && (op->subtype == SP_BULLET || op->subtype == SP_EXPLOSION))
02541 return 1;
02542 }
02543 return 0;
02544 }