Crossfire Server, Branch 1.12
R12190
|
00001 /* 00002 * static char *rcsid_monster_c = 00003 * "$Id: monster.c 11644 2009-04-15 06:24:55Z mwedel $"; 00004 */ 00005 00006 /* 00007 CrossFire, A Multiplayer game for X-windows 00008 00009 Copyright (C) 2002-2007 Mark Wedel & Crossfire Development Team 00010 Copyright (C) 1992 Frank Tore Johansen 00011 00012 This program is free software; you can redistribute it and/or modify 00013 it under the terms of the GNU General Public License as published by 00014 the Free Software Foundation; either version 2 of the License, or 00015 (at your option) any later version. 00016 00017 This program is distributed in the hope that it will be useful, 00018 but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 GNU General Public License for more details. 00021 00022 You should have received a copy of the GNU General Public License 00023 along with this program; if not, write to the Free Software 00024 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00025 00026 The authors can be reached via e-mail at crossfire-devel@real-time.com 00027 */ 00028 00035 #include <assert.h> 00036 #include <global.h> 00037 #ifndef __CEXTRACT__ 00038 #include <sproto.h> 00039 #include <spells.h> 00040 #include <skills.h> 00041 #endif 00042 00043 static int can_hit(object *ob1, object *ob2, rv_vector *rv); 00044 static int monster_cast_spell(object *head, object *part, object *pl, int dir, rv_vector *rv); 00045 static int monster_use_scroll(object *head, object *part, object *pl, int dir, rv_vector *rv); 00046 static int monster_use_skill(object *head, object *part, object *pl, int dir); 00047 static int monster_use_range(object *head, object *part, object *pl, int dir); 00048 static int monster_use_bow(object *head, object *part, object *pl, int dir); 00049 static void monster_check_pickup(object *monster); 00050 static int monster_can_pick(object *monster, object *item); 00051 static void monster_apply_below(object *monster); 00052 static int dist_att(int dir, object *ob, object *enemy, object *part, rv_vector *rv); 00053 static int run_att(int dir, object *ob, object *enemy, object *part, rv_vector *rv); 00054 static int hitrun_att(int dir, object *ob, object *enemy); 00055 static int wait_att(int dir, object *ob, object *enemy, object *part, rv_vector *rv); 00056 static int disthit_att(int dir, object *ob, object *enemy, object *part, rv_vector *rv); 00057 static int wait_att2(int dir, object *ob, object *enemy, object *part, rv_vector *rv); 00058 static void circ1_move(object *ob); 00059 static void circ2_move(object *ob); 00060 static void pace_movev(object *ob); 00061 static void pace_moveh(object *ob); 00062 static void pace2_movev(object *ob); 00063 static void pace2_moveh(object *ob); 00064 static void rand_move(object *ob); 00065 static int talk_to_npc(object *op, object *npc, const char *txt, int *talked); 00066 00067 #define MIN_MON_RADIUS 3 /* minimum monster detection radius */ 00068 00083 object *check_enemy(object *npc, rv_vector *rv) { 00084 /* if this is pet, let him attack the same enemy as his owner 00085 * TODO: when there is no ower enemy, try to find a target, 00086 * which CAN attack the owner. */ 00087 if ((npc->attack_movement&HI4) == PETMOVE) { 00088 if (npc->owner == NULL) 00089 npc->enemy = NULL; 00090 else if (npc->enemy == NULL) 00091 npc->enemy = npc->owner->enemy; 00092 } 00093 00094 /* periodically, a monster mayu change its target. Also, if the object 00095 * has been destroyed, etc, clear the enemy. 00096 * TODO: this should be changed, because it invokes to attack forced or 00097 * attacked monsters to leave the attacker alone, before it is destroyed 00098 */ 00099 /* i had removed the random target leave, this invokes problems with friendly 00100 * objects, getting attacked and defending herself - they don't try to attack 00101 * again then but perhaps get attack on and on 00102 * If we include a aggravated flag in , we can handle evil vs evil and good vs good 00103 * too. */ 00104 00105 if (npc->enemy) { 00106 /* I broke these if's apart to better be able to see what 00107 * the grouping checks are. Code is the same. 00108 */ 00109 if (QUERY_FLAG(npc->enemy, FLAG_REMOVED) 00110 || QUERY_FLAG(npc->enemy, FLAG_FREED) 00111 || !on_same_map(npc, npc->enemy) 00112 || npc == npc->enemy 00113 || QUERY_FLAG(npc, FLAG_NEUTRAL) 00114 || QUERY_FLAG(npc->enemy, FLAG_NEUTRAL)) 00115 npc->enemy = NULL; 00116 00117 else if (QUERY_FLAG(npc, FLAG_FRIENDLY) && ( 00118 (QUERY_FLAG(npc->enemy, FLAG_FRIENDLY) && !(should_arena_attack(npc, npc->owner, npc->enemy))) 00119 || ((npc->enemy->type == PLAYER) && !(should_arena_attack(npc, npc->owner, npc->enemy))) 00120 || npc->enemy == npc->owner)) 00121 npc->enemy = NULL; 00122 else if (!QUERY_FLAG(npc, FLAG_FRIENDLY) 00123 && (!QUERY_FLAG(npc->enemy, FLAG_FRIENDLY) && npc->enemy->type != PLAYER)) 00124 npc->enemy = NULL; 00125 00126 /* I've noticed that pets could sometimes get an arrow as the 00127 * target enemy - this code below makes sure the enemy is something 00128 * that should be attacked. My guess is that the arrow hits 00129 * the creature/owner, and so the creature then takes that 00130 * as the enemy to attack. 00131 */ 00132 else if (!QUERY_FLAG(npc->enemy, FLAG_MONSTER) 00133 && !QUERY_FLAG(npc->enemy, FLAG_GENERATOR) 00134 && npc->enemy->type != PLAYER 00135 && npc->enemy->type != GOLEM) 00136 npc->enemy = NULL; 00137 00138 } 00139 return can_detect_enemy(npc, npc->enemy, rv) ? npc->enemy : NULL; 00140 } 00141 00162 object *find_nearest_living_creature(object *npc) { 00163 int i, mflags; 00164 sint16 nx, ny; 00165 mapstruct *m; 00166 object *tmp; 00167 int search_arr[SIZEOFFREE]; 00168 00169 get_search_arr(search_arr); 00170 for (i = 0; i < SIZEOFFREE; i++) { 00171 /* modified to implement smart searching using search_arr 00172 * guidance array to determine direction of search order 00173 */ 00174 nx = npc->x+freearr_x[search_arr[i]]; 00175 ny = npc->y+freearr_y[search_arr[i]]; 00176 m = npc->map; 00177 00178 mflags = get_map_flags(m, &m, nx, ny, &nx, &ny); 00179 if (mflags&P_OUT_OF_MAP) 00180 continue; 00181 00182 if (mflags&P_IS_ALIVE) { 00183 tmp = GET_MAP_OB(m, nx, ny); 00184 while (tmp != NULL 00185 && !QUERY_FLAG(tmp, FLAG_MONSTER) 00186 && !QUERY_FLAG(tmp, FLAG_GENERATOR) 00187 && tmp->type != PLAYER) 00188 tmp = tmp->above; 00189 00190 if (!tmp) { 00191 LOG(llevDebug, "find_nearest_living_creature: map %s (%d,%d) has is_alive set but did not find a monster?\n", m->path, nx, ny); 00192 } else { 00193 if (can_see_monsterP(m, nx, ny, i)) 00194 return tmp; 00195 } 00196 } /* is something living on this space */ 00197 } 00198 return NULL; /* nothing found */ 00199 } 00200 00216 static object *find_enemy(object *npc, rv_vector *rv) { 00217 object *attacker, *tmp = NULL; 00218 00219 attacker = npc->attacked_by; /* save this for later use. This can be a attacker. */ 00220 npc->attacked_by = NULL; /* always clear the attacker entry */ 00221 00222 /* if we berserk, we don't care about others - we attack all we can find */ 00223 if (QUERY_FLAG(npc, FLAG_BERSERK)) { 00224 tmp = find_nearest_living_creature(npc); 00225 if (tmp) 00226 get_rangevector(npc, tmp, rv, 0); 00227 return tmp; 00228 } 00229 00230 /* Here is the main enemy selection. 00231 * We want this: if there is an enemy, attack him until its not possible or 00232 * one of both is dead. 00233 * If we have no enemy and we are... 00234 * a monster: try to find a player, a pet or a friendly monster 00235 * a friendly: only target a monster which is targeting you first or targeting a player 00236 * a neutral: fight a attacker (but there should be none), then do nothing 00237 * a pet: attack player enemy or a monster 00238 */ 00239 00240 /* pet move */ 00241 if ((npc->attack_movement&HI4) == PETMOVE) { 00242 tmp = get_pet_enemy(npc, rv); 00243 if (tmp) 00244 get_rangevector(npc, tmp, rv, 0); 00245 return tmp; 00246 } 00247 00248 /* we check our old enemy. */ 00249 if ((tmp = check_enemy(npc, rv)) == NULL) { 00250 if (attacker) { /* if we have an attacker, check him */ 00251 /* we want be sure this is the right one! */ 00252 if (attacker->count == npc->attacked_by_count) { 00253 /* TODO: thats not finished */ 00254 /* we don't want a fight evil vs evil or good against non evil */ 00255 if (QUERY_FLAG(npc, FLAG_NEUTRAL) 00256 || QUERY_FLAG(attacker, FLAG_NEUTRAL) /* neutral */ 00257 || (QUERY_FLAG(npc, FLAG_FRIENDLY) && QUERY_FLAG(attacker, FLAG_FRIENDLY)) 00258 || (!QUERY_FLAG(npc, FLAG_FRIENDLY) && (!QUERY_FLAG(attacker, FLAG_FRIENDLY) && attacker->type != PLAYER))) 00259 CLEAR_FLAG(npc, FLAG_SLEEP); /* skip it, but lets wakeup */ 00260 else if (on_same_map(npc, attacker)) { /* thats the only thing we must know... */ 00261 CLEAR_FLAG(npc, FLAG_SLEEP); /* well, NOW we really should wake up! */ 00262 npc->enemy = attacker; 00263 get_rangevector(npc, attacker, rv, 0); 00264 return attacker; /* yes, we face our attacker! */ 00265 } 00266 } 00267 } 00268 00269 /* we have no legal enemy or attacker, so we try to target a new one */ 00270 if (!QUERY_FLAG(npc, FLAG_UNAGGRESSIVE) 00271 && !QUERY_FLAG(npc, FLAG_FRIENDLY) 00272 && !QUERY_FLAG(npc, FLAG_NEUTRAL)) { 00273 npc->enemy = get_nearest_player(npc); 00274 if (npc->enemy) 00275 tmp = check_enemy(npc, rv); 00276 } 00277 00278 } 00279 00280 return tmp; 00281 } 00282 00298 static int check_wakeup(object *op, object *enemy, rv_vector *rv) { 00299 int radius = MAX(op->stats.Wis, MIN_MON_RADIUS); 00300 00301 /* Trim work - if no enemy, no need to do anything below */ 00302 if (!enemy) 00303 return 0; 00304 00305 /* blinded monsters can only find nearby objects to attack */ 00306 if (QUERY_FLAG(op, FLAG_BLIND)) 00307 radius = MIN_MON_RADIUS; 00308 00309 /* This covers the situation where the monster is in the dark 00310 * and has an enemy. If the enemy has no carried light (or isnt 00311 * glowing!) then the monster has trouble finding the enemy. 00312 * Remember we already checked to see if the monster can see in 00313 * the dark. */ 00314 else if (op->map 00315 && op->map->darkness > 0 00316 && enemy 00317 && !enemy->invisible 00318 && !stand_in_light(enemy) 00319 && (!QUERY_FLAG(op, FLAG_SEE_IN_DARK) || !QUERY_FLAG(op, FLAG_SEE_INVISIBLE))) { 00320 int dark = radius/(op->map->darkness); 00321 00322 radius = (dark > MIN_MON_RADIUS) ? (dark+1) : MIN_MON_RADIUS; 00323 } else if (!QUERY_FLAG(op, FLAG_SLEEP)) 00324 return 1; 00325 00326 /* enemy should already be on this map, so don't really need to check 00327 * for that. 00328 */ 00329 if (rv->distance < (QUERY_FLAG(enemy, FLAG_STEALTH) ? (radius/2)+1 : radius)) { 00330 CLEAR_FLAG(op, FLAG_SLEEP); 00331 return 1; 00332 } 00333 return 0; 00334 } 00335 00344 static int move_randomly(object *op) { 00345 int i; 00346 00347 /* Give up to 15 chances for a monster to move randomly */ 00348 for (i = 0; i < 15; i++) { 00349 if (move_object(op, RANDOM()%8+1)) 00350 return 1; 00351 } 00352 return 0; 00353 } 00354 00355 #define MAX_EXPLORE 5000 00356 00371 int compute_path(object *source, object *target, int default_dir) { 00372 char *path; 00373 int explore_x[MAX_EXPLORE], explore_y[MAX_EXPLORE]; 00374 int current = 0, dir, max = 1, size, x, y, check_dir; 00375 00376 if (target->map != source->map) 00377 return default_dir; 00378 00379 /* printf("compute_path (%d, %d) => (%d, %d)\n", source->x, source->y, target->x, target->y);*/ 00380 00381 size = source->map->width*source->map->height; 00382 path = calloc(size, sizeof(char)); 00383 if (path == NULL) { 00384 fatal(OUT_OF_MEMORY); 00385 } 00386 explore_x[0] = target->x; 00387 explore_y[0] = target->y; 00388 00389 while (current < max) { 00390 for (check_dir = 0; check_dir < 8; check_dir++) { 00391 dir = absdir(default_dir+check_dir); 00392 x = explore_x[current]+freearr_x[dir]; 00393 y = explore_y[current]+freearr_y[dir]; 00394 00395 if (x == source->x && y == source->y) { 00396 /* LOG(llevDebug, "compute_path => %d\n", absdir(dir+4));*/ 00397 free(path); 00398 return absdir(dir+4); 00399 } 00400 00401 if (OUT_OF_REAL_MAP(source->map, x, y)) 00402 continue; 00403 if (ob_blocked(source, source->map, x, y)) 00404 continue; 00405 00406 assert(source->map->height*x+y >= 0); 00407 assert(source->map->height*x+y < size); 00408 00409 if (path[source->map->height*x+y] == 0) { 00410 assert(max < MAX_EXPLORE); 00411 explore_x[max] = x; 00412 explore_y[max] = y; 00413 00414 path[source->map->height*x+y] = absdir(dir+4); 00415 /* printf("explore[%d] => (%d, %d) %d\n", max, x, y, path[source->map->height*x+y]);*/ 00416 max++; 00417 if (max == MAX_EXPLORE) { 00418 free(path); 00419 return default_dir; 00420 } 00421 } 00422 } 00423 current++; 00424 } 00425 00426 free(path); 00427 return default_dir; 00428 } 00429 00436 static void monster_do_living(object *op) { 00437 assert(QUERY_FLAG(op, FLAG_MONSTER)); 00438 00439 /* generate hp, if applicable */ 00440 if (op->stats.Con > 0 && op->stats.hp < op->stats.maxhp) { 00441 /* last heal is in funny units. Dividing by speed puts 00442 * the regeneration rate on a basis of time instead of 00443 * #moves the monster makes. The scaling by 8 is 00444 * to capture 8th's of a hp fraction regens 00445 * 00446 * Cast to sint32 before comparing to maxhp since otherwise an (sint16) 00447 * overflow might produce monsters with negative hp. 00448 */ 00449 00450 op->last_heal += (int)((float)(8*op->stats.Con)/FABS(op->speed)); 00451 op->stats.hp = MIN((sint32)op->stats.hp+op->last_heal/32, op->stats.maxhp); /* causes Con/4 hp/tick */ 00452 op->last_heal %= 32; 00453 00454 /* So if the monster has gained enough HP that they are no longer afraid */ 00455 if (QUERY_FLAG(op, FLAG_RUN_AWAY) 00456 && op->stats.hp >= (signed short)(((float)op->run_away/(float)100)*(float)op->stats.maxhp)) 00457 CLEAR_FLAG(op, FLAG_RUN_AWAY); 00458 00459 if (op->stats.hp > op->stats.maxhp) 00460 op->stats.hp = op->stats.maxhp; 00461 } 00462 00463 /* generate sp, if applicable */ 00464 if (op->stats.Pow > 0 && op->stats.sp < op->stats.maxsp) { 00465 /* last_sp is in funny units. Dividing by speed puts 00466 * the regeneration rate on a basis of time instead of 00467 * #moves the monster makes. The scaling by 8 is 00468 * to capture 8th's of a sp fraction regens 00469 * 00470 * Cast to sint32 before comparing to maxhp since otherwise an (sint16) 00471 * overflow might produce monsters with negative sp. 00472 */ 00473 00474 op->last_sp += (int)((float)(8*op->stats.Pow)/FABS(op->speed)); 00475 op->stats.sp = MIN(op->stats.sp+op->last_sp/128, op->stats.maxsp); /* causes Pow/16 sp/tick */ 00476 op->last_sp %= 128; 00477 } 00478 00479 /* this should probably get modified by many more values. 00480 * (eg, creatures resistance to fear, level, etc. ) 00481 */ 00482 if (QUERY_FLAG(op, FLAG_SCARED) && !(RANDOM()%20)) { 00483 CLEAR_FLAG(op, FLAG_SCARED); /* Time to regain some "guts"... */ 00484 } 00485 } 00486 00495 static int monster_move_no_enemy(object *op) { 00496 assert(QUERY_FLAG(op, FLAG_MONSTER)); 00497 00498 if (QUERY_FLAG(op, FLAG_ONLY_ATTACK)) { 00499 remove_ob(op); 00500 free_object(op); 00501 return 1; 00502 } 00503 00504 /* Probably really a bug for a creature to have both 00505 * stand still and a movement type set. 00506 */ 00507 if (!QUERY_FLAG(op, FLAG_STAND_STILL)) { 00508 if (op->attack_movement&HI4) { 00509 switch (op->attack_movement&HI4) { 00510 case(PETMOVE): 00511 pet_move(op); 00512 break; 00513 00514 case(CIRCLE1): 00515 circ1_move(op); 00516 break; 00517 00518 case(CIRCLE2): 00519 circ2_move(op); 00520 break; 00521 00522 case(PACEV): 00523 pace_movev(op); 00524 break; 00525 00526 case(PACEH): 00527 pace_moveh(op); 00528 break; 00529 00530 case(PACEV2): 00531 pace2_movev(op); 00532 break; 00533 00534 case(PACEH2): 00535 pace2_moveh(op); 00536 break; 00537 00538 case(RANDO): 00539 rand_move(op); 00540 break; 00541 00542 case(RANDO2): 00543 move_randomly(op); 00544 break; 00545 } 00546 return 0; 00547 } else if (QUERY_FLAG(op, FLAG_RANDOM_MOVE)) 00548 move_randomly(op); 00549 00550 } /* stand still */ 00551 00552 return 0; 00553 } 00554 00566 int move_monster(object *op) { 00567 int dir, diff; 00568 object *owner, *enemy, *part, *oph = op; 00569 rv_vector rv; 00570 00571 /* Monsters not on maps don't do anything. These monsters are things 00572 * Like royal guards in city dwellers inventories. 00573 */ 00574 if (!op->map) 00575 return 0; 00576 00577 /* for target facing, we copy this value here for fast access */ 00578 if (oph->head) /* force update the head - one arch one pic */ 00579 oph = oph->head; 00580 00581 if (QUERY_FLAG(op, FLAG_NO_ATTACK)) /* we never ever attack */ 00582 enemy = op->enemy = NULL; 00583 else if ((enemy = find_enemy(op, &rv))) { 00584 /* we have an enemy, just tell him we want him dead */ 00585 enemy->attacked_by = op; /* our ptr */ 00586 enemy->attacked_by_count = op->count; /* our tag */ 00587 } 00588 00589 if (QUERY_FLAG(op, FLAG_SLEEP) 00590 || QUERY_FLAG(op, FLAG_BLIND) 00591 || ((op->map->darkness > 0) && !QUERY_FLAG(op, FLAG_SEE_IN_DARK) && !QUERY_FLAG(op, FLAG_SEE_INVISIBLE))) { 00592 if (!check_wakeup(op, enemy, &rv)) 00593 return 0; 00594 } 00595 00596 /* check if monster pops out of hidden spot */ 00597 if (op->hide) 00598 do_hidden_move(op); 00599 00600 if (op->pick_up) 00601 monster_check_pickup(op); 00602 00603 if (op->will_apply) 00604 monster_apply_below(op); /* Check for items to apply below */ 00605 00606 monster_do_living(op); 00607 00608 /* If we don't have an enemy, do special movement or the like */ 00609 if (!enemy) { 00610 return monster_move_no_enemy(op); 00611 } /* no enemy */ 00612 00613 /* We have an enemy. Block immediately below is for pets */ 00614 if ((op->attack_movement&HI4) == PETMOVE && (owner = get_owner(op)) != NULL && !on_same_map(op, owner)) { 00615 follow_owner(op, owner); 00616 /* If the pet was unable to follow the owner, free it */ 00617 if (QUERY_FLAG(op, FLAG_REMOVED) && FABS(op->speed) > MIN_ACTIVE_SPEED) { 00618 remove_friendly_object(op); 00619 free_object(op); 00620 return 1; 00621 } 00622 return 0; 00623 } 00624 00625 /* doppleganger code to change monster facing to that of the nearest 00626 * player. Hmm. The code is here, but no monster in the current 00627 * arch set uses it. 00628 */ 00629 if ((op->race != NULL)&& strcmp(op->race, "doppleganger") == 0) { 00630 op->face = enemy->face; 00631 if (op->name) 00632 free_string(op->name); 00633 add_refcount(op->name = enemy->name); 00634 } 00635 00636 /* Calculate range information for closest body part - this 00637 * is used for the 'skill' code, which isn't that smart when 00638 * it comes to figuring it out - otherwise, giants throw boulders 00639 * into themselves. 00640 */ 00641 get_rangevector(op, enemy, &rv, 0); 00642 if (op->direction != rv.direction) { 00643 op->direction = rv.direction; 00644 op->facing = op->direction; 00645 if (op->animation_id) 00646 animate_object(op, op->direction); 00647 } 00648 00649 /* Move the check for scared up here - if the monster was scared, 00650 * we were not doing any of the logic below, so might as well save 00651 * a few cpu cycles. 00652 */ 00653 if (!QUERY_FLAG(op, FLAG_SCARED)) { 00654 rv_vector rv1; 00655 00656 /* now we test every part of an object .... this is a real ugly piece of code */ 00657 for (part = op; part != NULL; part = part->more) { 00658 get_rangevector(part, enemy, &rv1, 0x1); 00659 dir = rv1.direction; 00660 00661 /* hm, not sure about this part - in original was a scared flag here too 00662 * but that we test above... so can be old code here 00663 */ 00664 if (QUERY_FLAG(op, FLAG_RUN_AWAY)) 00665 dir = absdir(dir+4); 00666 if (QUERY_FLAG(op, FLAG_CONFUSED)) 00667 dir = absdir(dir+RANDOM()%3+RANDOM()%3-2); 00668 00669 if (QUERY_FLAG(op, FLAG_CAST_SPELL) && !(RANDOM()%3)) { 00670 if (monster_cast_spell(op, part, enemy, dir, &rv1)) 00671 return 0; 00672 } 00673 00674 if (QUERY_FLAG(op, FLAG_READY_SCROLL) && !(RANDOM()%3)) { 00675 if (monster_use_scroll(op, part, enemy, dir, &rv1)) 00676 return 0; 00677 } 00678 00679 if (QUERY_FLAG(op, FLAG_READY_RANGE) && !(RANDOM()%3)) { 00680 if (monster_use_range(op, part, enemy, dir)) 00681 return 0; 00682 } 00683 if (QUERY_FLAG(op, FLAG_READY_SKILL) && !(RANDOM()%3)) { 00684 if (monster_use_skill(op, rv.part, enemy, rv.direction)) 00685 return 0; 00686 } 00687 if (QUERY_FLAG(op, FLAG_READY_BOW) && !(RANDOM()%2)) { 00688 if (monster_use_bow(op, part, enemy, dir)) 00689 return 0; 00690 } 00691 } /* for processing of all parts */ 00692 } /* If not scared */ 00693 00694 00695 /* code below is for when we didn't use a range attack or a skill, so 00696 * either move or hit with hth attack. */ 00697 00698 part = rv.part; 00699 dir = rv.direction; 00700 00701 if (QUERY_FLAG(op, FLAG_SCARED) || QUERY_FLAG(op, FLAG_RUN_AWAY)) 00702 dir = absdir(dir+4); 00703 else if (!can_hit(part, enemy, &rv)) 00704 dir = compute_path(op, enemy, rv.direction); 00705 00706 if (QUERY_FLAG(op, FLAG_CONFUSED)) 00707 dir = absdir(dir+RANDOM()%3+RANDOM()%3-2); 00708 00709 if ((op->attack_movement&LO4) && !QUERY_FLAG(op, FLAG_SCARED)) { 00710 switch (op->attack_movement&LO4) { 00711 case DISTATT: 00712 dir = dist_att(dir, op, enemy, part, &rv); 00713 break; 00714 00715 case RUNATT: 00716 dir = run_att(dir, op, enemy, part, &rv); 00717 break; 00718 00719 case HITRUN: 00720 dir = hitrun_att(dir, op, enemy); 00721 break; 00722 00723 case WAITATT: 00724 dir = wait_att(dir, op, enemy, part, &rv); 00725 break; 00726 00727 case RUSH: /* default - monster normally moves towards player */ 00728 case ALLRUN: 00729 break; 00730 00731 case DISTHIT: 00732 dir = disthit_att(dir, op, enemy, part, &rv); 00733 break; 00734 00735 case WAIT2: 00736 dir = wait_att2(dir, op, enemy, part, &rv); 00737 break; 00738 00739 default: 00740 LOG(llevDebug, "Illegal low mon-move: %d\n", op->attack_movement&LO4); 00741 } 00742 } 00743 00744 if (!dir) 00745 return 0; 00746 00747 if (!QUERY_FLAG(op, FLAG_STAND_STILL)) { 00748 if (move_object(op, dir)) /* Can the monster move directly toward player? */ 00749 return 0; 00750 00751 if (QUERY_FLAG(op, FLAG_SCARED) 00752 || !can_hit(part, enemy, &rv) 00753 || QUERY_FLAG(op, FLAG_RUN_AWAY)) { 00754 /* Try move around corners if !close */ 00755 int maxdiff = (QUERY_FLAG(op, FLAG_ONLY_ATTACK) || RANDOM()&1) ? 1 : 2; 00756 for (diff = 1; diff <= maxdiff; diff++) { 00757 /* try different detours */ 00758 int m = 1-(RANDOM()&2); /* Try left or right first? */ 00759 if (move_object(op, absdir(dir+diff*m)) 00760 || move_object(op, absdir(dir-diff*m))) 00761 return 0; 00762 } 00763 } 00764 } /* if monster is not standing still */ 00765 00766 /* 00767 * Eneq(@csd.uu.se): Patch to make RUN_AWAY or SCARED monsters move a random 00768 * direction if they can't move away. 00769 */ 00770 if (!QUERY_FLAG(op, FLAG_ONLY_ATTACK) 00771 && (QUERY_FLAG(op, FLAG_RUN_AWAY) || QUERY_FLAG(op, FLAG_SCARED))) 00772 if (move_randomly(op)) 00773 return 0; 00774 00775 /* 00776 * Try giving the monster a new enemy - the player that is closest 00777 * to it. In this way, it won't just keep trying to get to a target 00778 * that is inaccessible. 00779 * This could be more clever - it should go through a list of several 00780 * enemies, as it is now, you could perhaps get situations where there 00781 * are two players flanking the monster at close distance, but which 00782 * the monster can't get to, and a third one at a far distance that 00783 * the monster could get to - as it is, the monster won't look at that 00784 * third one. 00785 */ 00786 if (!QUERY_FLAG(op, FLAG_FRIENDLY) && enemy == op->enemy) { 00787 object *nearest_player = get_nearest_player(op); 00788 00789 if (nearest_player && nearest_player != enemy && !can_hit(part, enemy, &rv)) { 00790 op->enemy = NULL; 00791 enemy = nearest_player; 00792 } 00793 } 00794 00795 if (!QUERY_FLAG(op, FLAG_SCARED) && can_hit(part, enemy, &rv)) { 00796 /* The adjustement to wc that was here before looked totally bogus - 00797 * since wc can in fact get negative, that would mean by adding 00798 * the current wc, the creature gets better? Instead, just 00799 * add a fixed amount - nasty creatures that are runny away should 00800 * still be pretty nasty. 00801 */ 00802 if (QUERY_FLAG(op, FLAG_RUN_AWAY)) { 00803 part->stats.wc += 10; 00804 (void)skill_attack(enemy, part, 0, NULL, NULL); 00805 part->stats.wc -= 10; 00806 } else 00807 (void)skill_attack(enemy, part, 0, NULL, NULL); 00808 } /* if monster is in attack range */ 00809 00810 if (QUERY_FLAG(part, FLAG_FREED)) /* Might be freed by ghost-attack or hit-back */ 00811 return 1; 00812 00813 if (QUERY_FLAG(op, FLAG_ONLY_ATTACK)) { 00814 remove_ob(op); 00815 free_object(op); 00816 return 1; 00817 } 00818 return 0; 00819 } 00820 00835 static int can_hit(object *ob1, object *ob2, rv_vector *rv) { 00836 object *more; 00837 rv_vector rv1; 00838 00839 if (QUERY_FLAG(ob1, FLAG_CONFUSED)&&!(RANDOM()%3)) 00840 return 0; 00841 00842 if (abs(rv->distance_x) < 2 && abs(rv->distance_y) < 2) 00843 return 1; 00844 00845 /* check all the parts of ob2 - just because we can't get to 00846 * its head doesn't mean we don't want to pound its feet 00847 */ 00848 for (more = ob2->more; more != NULL; more = more->more) { 00849 get_rangevector(ob1, more, &rv1, 0); 00850 if (abs(rv1.distance_x) < 2 && abs(rv1.distance_y) < 2) 00851 return 1; 00852 } 00853 return 0; 00854 } 00855 00880 static int monster_should_cast_spell(object *monster, object *spell_ob) { 00881 if (spell_ob->subtype == SP_BOLT 00882 || spell_ob->subtype == SP_BULLET 00883 || spell_ob->subtype == SP_EXPLOSION 00884 || spell_ob->subtype == SP_CONE 00885 || spell_ob->subtype == SP_BOMB 00886 || spell_ob->subtype == SP_SMITE 00887 || spell_ob->subtype == SP_MAGIC_MISSILE 00888 || spell_ob->subtype == SP_SUMMON_GOLEM 00889 || spell_ob->subtype == SP_MAGIC_WALL 00890 || spell_ob->subtype == SP_SUMMON_MONSTER 00891 || spell_ob->subtype == SP_MOVING_BALL 00892 || spell_ob->subtype == SP_SWARM 00893 || spell_ob->subtype == SP_INVISIBLE) 00894 return 1; 00895 00896 return 0; 00897 } 00898 00900 #define MAX_KNOWN_SPELLS 20 00901 00917 static object *monster_choose_random_spell(object *monster) { 00918 object *altern[MAX_KNOWN_SPELLS]; 00919 object *tmp; 00920 int i = 0; 00921 00922 for (tmp = monster->inv; tmp != NULL; tmp = tmp->below) 00923 if (tmp->type == SPELLBOOK || tmp->type == SPELL) { 00924 /* Check and see if it's actually a useful spell. 00925 * If its a spellbook, the spell is actually the inventory item. 00926 * if it is a spell, then it is just the object itself. 00927 */ 00928 if (monster_should_cast_spell(monster, (tmp->type == SPELLBOOK) ? tmp->inv : tmp)) { 00929 altern[i++] = tmp; 00930 if (i == MAX_KNOWN_SPELLS) 00931 break; 00932 } 00933 } 00934 if (!i) 00935 return NULL; 00936 return altern[RANDOM()%i]; 00937 } 00938 00957 static int monster_cast_spell(object *head, object *part, object *pl, int dir, rv_vector *rv) { 00958 object *spell_item; 00959 object *owner; 00960 rv_vector rv1; 00961 00962 /* If you want monsters to cast spells over friends, this spell should 00963 * be removed. It probably should be in most cases, since monsters still 00964 * don't care about residual effects (ie, casting a cone which may have a 00965 * clear path to the player, the side aspects of the code will still hit 00966 * other monsters) 00967 */ 00968 if (!(dir = path_to_player(part, pl, 0))) 00969 return 0; 00970 00971 if (QUERY_FLAG(head, FLAG_FRIENDLY) && (owner = get_owner(head)) != NULL) { 00972 get_rangevector(head, owner, &rv1, 0x1); 00973 if (dirdiff(dir, rv1.direction) < 2) { 00974 return 0; /* Might hit owner with spell */ 00975 } 00976 } 00977 00978 if (QUERY_FLAG(head, FLAG_CONFUSED)) 00979 dir = absdir(dir+RANDOM()%3+RANDOM()%3-2); 00980 00981 /* If the monster hasn't already chosen a spell, choose one 00982 * I'm not sure if it really make sense to pre-select spells (events 00983 * could be different by the time the monster goes again). 00984 */ 00985 if (head->spellitem == NULL) { 00986 if ((spell_item = monster_choose_random_spell(head)) == NULL) { 00987 LOG(llevMonster, "Turned off spells in %s\n", head->name); 00988 CLEAR_FLAG(head, FLAG_CAST_SPELL); /* Will be turned on when picking up book */ 00989 return 0; 00990 } 00991 if (spell_item->type == SPELLBOOK) { 00992 if (!spell_item->inv) { 00993 LOG(llevError, "spellbook %s does not contain a spell?\n", spell_item->name); 00994 return 0; 00995 } 00996 spell_item = spell_item->inv; 00997 } 00998 } else 00999 spell_item = head->spellitem; 01000 01001 if (!spell_item) 01002 return 0; 01003 01004 /* Best guess this is a defensive/healing spell */ 01005 if (spell_item->range <= 1 || spell_item->stats.dam < 0) 01006 dir = 0; 01007 01008 /* Monster doesn't have enough spell-points */ 01009 if (head->stats.sp < SP_level_spellpoint_cost(head, spell_item, SPELL_MANA)) 01010 return 0; 01011 01012 if (head->stats.grace < SP_level_spellpoint_cost(head, spell_item, SPELL_GRACE)) 01013 return 0; 01014 01015 head->stats.sp -= SP_level_spellpoint_cost(head, spell_item, SPELL_MANA); 01016 head->stats.grace -= SP_level_spellpoint_cost(head, spell_item, SPELL_GRACE); 01017 01018 /* set this to null, so next time monster will choose something different */ 01019 head->spellitem = NULL; 01020 01021 return cast_spell(part, part, dir, spell_item, NULL); 01022 } 01023 01040 static int monster_use_scroll(object *head, object *part, object *pl, int dir, rv_vector *rv) { 01041 object *scroll; 01042 object *owner; 01043 rv_vector rv1; 01044 01045 /* If you want monsters to cast spells over friends, this spell should 01046 * be removed. It probably should be in most cases, since monsters still 01047 * don't care about residual effects (ie, casting a cone which may have a 01048 * clear path to the player, the side aspects of the code will still hit 01049 * other monsters) 01050 */ 01051 if (!(dir = path_to_player(part, pl, 0))) 01052 return 0; 01053 01054 if (QUERY_FLAG(head, FLAG_FRIENDLY) && (owner = get_owner(head)) != NULL) { 01055 get_rangevector(head, owner, &rv1, 0x1); 01056 if (dirdiff(dir, rv1.direction) < 2) { 01057 return 0; /* Might hit owner with spell */ 01058 } 01059 } 01060 01061 if (QUERY_FLAG(head, FLAG_CONFUSED)) 01062 dir = absdir(dir+RANDOM()%3+RANDOM()%3-2); 01063 01064 for (scroll = head->inv; scroll; scroll = scroll->below) 01065 if (scroll->type == SCROLL && monster_should_cast_spell(head, scroll->inv)) 01066 break; 01067 01068 /* Used up all his scrolls, so nothing do to */ 01069 if (!scroll) { 01070 CLEAR_FLAG(head, FLAG_READY_SCROLL); 01071 return 0; 01072 } 01073 01074 /* Spell should be cast on caster (ie, heal, strength) */ 01075 if (scroll->inv->range == 0) 01076 dir = 0; 01077 01078 /* Face the direction that we want to cast. */ 01079 head->direction = dir; 01080 head->facing = head->direction; 01081 if (head->animation_id) 01082 animate_object(head, head->direction); 01083 01084 ob_apply(scroll, part, 0); 01085 return 1; 01086 } 01087 01115 static int monster_use_skill(object *head, object *part, object *pl, int dir) { 01116 object *skill, *owner; 01117 01118 if (!(dir = path_to_player(part, pl, 0))) 01119 return 0; 01120 01121 if (QUERY_FLAG(head, FLAG_FRIENDLY) && (owner = get_owner(head)) != NULL) { 01122 int dir2 = find_dir_2(head->x-owner->x, head->y-owner->y); 01123 if (dirdiff(dir, dir2) < 1) 01124 return 0; /* Might hit owner with skill -thrown rocks for example ?*/ 01125 } 01126 if (QUERY_FLAG(head, FLAG_CONFUSED)) 01127 dir = absdir(dir+RANDOM()%3+RANDOM()%3-2); 01128 01129 /* skill selection - monster will use the next unused skill. 01130 * well...the following scenario will allow the monster to 01131 * toggle between 2 skills. One day it would be nice to make 01132 * more skills available to monsters. 01133 */ 01134 01135 for (skill = head->inv; skill != NULL; skill = skill->below) 01136 if (skill->type == SKILL && skill != head->chosen_skill) { 01137 head->chosen_skill = skill; 01138 break; 01139 } 01140 01141 if (!skill && !head->chosen_skill) { 01142 LOG(llevDebug, "Error: Monster %s (%d) has FLAG_READY_SKILL without skill.\n", head->name, head->count); 01143 CLEAR_FLAG(head, FLAG_READY_SKILL); 01144 return 0; 01145 } 01146 /* use skill */ 01147 return do_skill(head, part, head->chosen_skill, dir, NULL); 01148 } 01149 01164 static int monster_use_range(object *head, object *part, object *pl, int dir) { 01165 object *wand, *owner; 01166 int at_least_one = 0; 01167 01168 if (!(dir = path_to_player(part, pl, 0))) 01169 return 0; 01170 01171 if (QUERY_FLAG(head, FLAG_FRIENDLY) && (owner = get_owner(head)) != NULL) { 01172 int dir2 = find_dir_2(head->x-owner->x, head->y-owner->y); 01173 if (dirdiff(dir, dir2) < 2) 01174 return 0; /* Might hit owner with spell */ 01175 } 01176 if (QUERY_FLAG(head, FLAG_CONFUSED)) 01177 dir = absdir(dir+RANDOM()%3+RANDOM()%3-2); 01178 01179 for (wand = head->inv; wand != NULL; wand = wand->below) { 01180 if (wand->type == WAND) { 01181 /* Found a wand, let's see if it has charges left */ 01182 at_least_one = 1; 01183 if (wand->stats.food <= 0) 01184 continue; 01185 01186 cast_spell(head, wand, dir, wand->inv, NULL); 01187 drain_wand_charge(wand); 01188 01189 /* Success */ 01190 return 1; 01191 } else if (wand->type == ROD || wand->type == HORN) { 01192 /* Found rod/horn, let's use it if possible */ 01193 at_least_one = 1; 01194 if (wand->stats.hp < MAX(wand->inv->stats.sp, wand->inv->stats.grace)) 01195 continue; 01196 01197 /* drain charge before casting spell - can be a case where the 01198 * spell destroys the monster, and rod, so if done after, results 01199 * in crash. 01200 */ 01201 drain_rod_charge(wand); 01202 cast_spell(head, wand, dir, wand->inv, NULL); 01203 01204 /* Success */ 01205 return 1; 01206 } 01207 } 01208 01209 if (at_least_one) 01210 return 0; 01211 01212 LOG(llevError, "Error: Monster %s (%d) HAS_READY_RANG() without wand/horn/rod.\n", head->name, head->count); 01213 CLEAR_FLAG(head, FLAG_READY_RANGE); 01214 return 0; 01215 } 01216 01233 static int monster_use_bow(object *head, object *part, object *pl, int dir) { 01234 object *owner; 01235 rv_vector rv; 01236 sint16 x, y; 01237 mapstruct *map; 01238 01239 get_rangevector(part, pl, &rv, 1); 01240 if (rv.distance > 100) 01241 /* Too far */ 01242 return 0; 01243 if (rv.distance_x != 0 && rv.distance_y != 0 && abs(rv.distance_x) != abs(rv.distance_y)) 01244 /* Player must be on same horizontal, vertical or diagonal line. */ 01245 return 0; 01246 dir = absdir(find_dir_2(rv.distance_x, rv.distance_y)+4); 01247 01248 if (QUERY_FLAG(head, FLAG_FRIENDLY)) 01249 owner = get_owner(head); 01250 else 01251 owner = NULL; 01252 01253 /* The monster can possibly fire, let's see if the path is ok for an arrow. */ 01254 x = part->x; 01255 y = part->y; 01256 map = part->map; 01257 while (x != pl->x || y != pl->y || map != pl->map) { 01258 x += freearr_x[dir]; 01259 y += freearr_y[dir]; 01260 map = get_map_from_coord(map, &x, &y); 01261 if (!map) { 01262 LOG(llevError, "monster_use_bow: no map but still path exists??\n"); 01263 return 0; 01264 } 01265 if ((GET_MAP_MOVE_BLOCK(map, x, y)&MOVE_FLY_LOW) == MOVE_FLY_LOW) 01266 return 0; 01267 if (owner && owner->x == x && owner->y == y && owner->map == map) 01268 /* Don't hit owner! */ 01269 return 0; 01270 } 01271 01272 /* Finally, path is clear, can fire. */ 01273 01274 if (QUERY_FLAG(head, FLAG_CONFUSED)) 01275 dir = absdir(dir+RANDOM()%3+RANDOM()%3-2); 01276 01277 /* in server/player.c */ 01278 return fire_bow(head, NULL, dir, 0, part->x, part->y); 01279 } 01280 01293 static int check_good_weapon(object *who, object *item) { 01294 object *other_weap; 01295 int val = 0, i; 01296 01297 for (other_weap = who->inv; other_weap != NULL; other_weap = other_weap->below) 01298 if (other_weap->type == item->type && QUERY_FLAG(other_weap, FLAG_APPLIED)) 01299 break; 01300 01301 if (other_weap == NULL) /* No other weapons */ 01302 return 1; 01303 01304 /* Rather than go through and apply the new one, and see if it is 01305 * better, just do some simple checks 01306 * Put some multipliers for things that hvae several effects, 01307 * eg, magic affects both damage and wc, so it has more weight 01308 */ 01309 01310 val = item->stats.dam-other_weap->stats.dam; 01311 val += (item->magic-other_weap->magic)*3; 01312 /* Monsters don't really get benefits from things like regen rates 01313 * from items. But the bonus for their stats are very important. 01314 */ 01315 for (i = 0; i < NUM_STATS; i++) 01316 val += (get_attr_value(&item->stats, i)-get_attr_value(&other_weap->stats, i))*2; 01317 01318 if (val > 0) 01319 return 1; 01320 else 01321 return 0; 01322 } 01323 01336 static int check_good_armour(object *who, object *item) { 01337 object *other_armour; 01338 int val = 0, i; 01339 01340 for (other_armour = who->inv; other_armour != NULL; other_armour = other_armour->below) 01341 if (other_armour->type == item->type && QUERY_FLAG(other_armour, FLAG_APPLIED)) 01342 break; 01343 01344 if (other_armour == NULL) /* No other armour, use the new */ 01345 return 1; 01346 01347 /* Like above function , see which is better */ 01348 val = item->stats.ac-other_armour->stats.ac; 01349 val = (item->resist[ATNR_PHYSICAL]-other_armour->resist[ATNR_PHYSICAL])/5; 01350 val += (item->magic-other_armour->magic)*3; 01351 01352 /* for the other protections, do weigh them very much in the equation - 01353 * it is the armor protection which is most important, because there is 01354 * no good way to know what the player may attack the monster with. 01355 * So if the new item has better protection than the old, give that higher 01356 * value. If the reverse, then decrease the value of this item some. 01357 */ 01358 for (i = 1; i < NROFATTACKS; i++) { 01359 if (item->resist[i] > other_armour->resist[i]) 01360 val++; 01361 else if (item->resist[i] < other_armour->resist[i]) 01362 val--; 01363 } 01364 01365 /* Very few armours have stats, so not much need to worry about those. */ 01366 01367 if (val > 0) 01368 return 1; 01369 else 01370 return 0; 01371 01372 } 01373 01393 static void monster_check_pickup(object *monster) { 01394 object *tmp, *next; 01395 int next_tag; 01396 01397 for (tmp = monster->below; tmp != NULL; tmp = next) { 01398 next = tmp->below; 01399 next_tag = next ? next->count : 0; 01400 if (monster_can_pick(monster, tmp)) { 01401 remove_ob(tmp); 01402 tmp = insert_ob_in_ob(tmp, monster); 01403 (void)monster_check_apply(monster, tmp); 01404 } 01405 /* We could try to re-establish the cycling, of the space, but probably 01406 * not a big deal to just bail out. 01407 */ 01408 if (next && was_destroyed(next, next_tag)) 01409 return; 01410 } 01411 } 01412 01413 /* 01414 * monster_can_pick(): If the monster is interested in picking up 01415 * the item, then return 0. Otherwise 0. 01416 * Instead of pick_up, flags for "greed", etc, should be used. 01417 * I've already utilized flags for bows, wands, rings, etc, etc. -Frank. 01418 */ 01419 01420 static int monster_can_pick(object *monster, object *item) { 01421 int flag = 0; 01422 int i; 01423 01424 if (!can_pick(monster, item)) 01425 return 0; 01426 01427 if (QUERY_FLAG(item, FLAG_UNPAID)) 01428 return 0; 01429 01430 if (monster->pick_up&64) /* All */ 01431 flag = 1; 01432 01433 else { 01434 if (IS_WEAPON(item)) 01435 flag = (monster->pick_up&8) || QUERY_FLAG(monster, FLAG_USE_WEAPON); 01436 else if (IS_ARMOR(item)) 01437 flag = (monster->pick_up&16) || QUERY_FLAG(monster, FLAG_USE_ARMOUR); 01438 else if (IS_SHIELD(item)) 01439 flag = (monster->pick_up&16) || QUERY_FLAG(monster, FLAG_USE_SHIELD); 01440 else switch (item->type) { 01441 case MONEY: 01442 case GEM: 01443 flag = monster->pick_up&2; 01444 break; 01445 01446 case FOOD: 01447 flag = monster->pick_up&4; 01448 break; 01449 01450 case SKILL: 01451 flag = QUERY_FLAG(monster, FLAG_CAN_USE_SKILL); 01452 break; 01453 01454 case RING: 01455 flag = QUERY_FLAG(monster, FLAG_USE_RING); 01456 break; 01457 01458 case WAND: 01459 case HORN: 01460 case ROD: 01461 flag = QUERY_FLAG(monster, FLAG_USE_RANGE); 01462 break; 01463 01464 case SPELLBOOK: 01465 flag = (monster->arch != NULL && QUERY_FLAG((&monster->arch->clone), FLAG_CAST_SPELL)); 01466 break; 01467 01468 case SCROLL: 01469 flag = QUERY_FLAG(monster, FLAG_USE_SCROLL); 01470 break; 01471 01472 case BOW: 01473 case ARROW: 01474 flag = QUERY_FLAG(monster, FLAG_USE_BOW); 01475 break; 01476 } 01477 /* Simplistic check - if the monster has a location to equip it, he will 01478 * pick it up. Note that this doesn't handle cases where an item may 01479 * use several locations. 01480 */ 01481 for (i = 0; i < NUM_BODY_LOCATIONS; i++) { 01482 if (monster->body_info[i] && item->body_info[i]) { 01483 flag = 1; 01484 break; 01485 } 01486 } 01487 } 01488 01489 if (((!(monster->pick_up&32)) && flag) || ((monster->pick_up&32) && (!flag))) 01490 return 1; 01491 return 0; 01492 } 01493 01494 /* 01495 * monster_apply_below(): 01496 * Vick's (vick@bern.docs.uu.se) @921107 -> If a monster who's 01497 * eager to apply things, encounters something apply-able, 01498 * then make him apply it 01499 */ 01500 static void monster_apply_below(object *monster) { 01501 object *tmp, *next; 01502 01503 for (tmp = monster->below; tmp != NULL; tmp = next) { 01504 next = tmp->below; 01505 switch (tmp->type) { 01506 case CF_HANDLE: 01507 case TRIGGER: 01508 if (monster->will_apply&WILL_APPLY_HANDLE) 01509 manual_apply(monster, tmp, 0); 01510 break; 01511 01512 case TREASURE: 01513 if (monster->will_apply&WILL_APPLY_TREASURE) 01514 manual_apply(monster, tmp, 0); 01515 break; 01516 } 01517 if (QUERY_FLAG(tmp, FLAG_IS_FLOOR)) 01518 break; 01519 } 01520 } 01521 01522 /* 01523 * monster_check_apply() is meant to be called after an item is 01524 * inserted in a monster. 01525 * If an item becomes outdated (monster found a better item), 01526 * a pointer to that object is returned, so it can be dropped. 01527 * (so that other monsters can pick it up and use it) 01528 * Note that as things are now, monsters never drop something - 01529 * they can pick up all that they can use. 01530 */ 01531 01532 /* Sept 96, fixed this so skills will be readied -b.t.*/ 01533 01534 void monster_check_apply(object *mon, object *item) { 01535 int flag = 0; 01536 01537 if (item->type == SPELLBOOK 01538 && mon->arch != NULL 01539 && (QUERY_FLAG((&mon->arch->clone), FLAG_CAST_SPELL))) { 01540 SET_FLAG(mon, FLAG_CAST_SPELL); 01541 return; 01542 } 01543 01544 /* If for some reason, this item is already applied, no more work to do */ 01545 if (QUERY_FLAG(item, FLAG_APPLIED)) 01546 return; 01547 01548 /* Might be better not to do this - if the monster can fire a bow, 01549 * it is possible in his wanderings, he will find one to use. In 01550 * which case, it would be nice to have ammo for it. 01551 */ 01552 if (QUERY_FLAG(mon, FLAG_USE_BOW) && item->type == ARROW) { 01553 /* Check for the right kind of bow */ 01554 object *bow; 01555 01556 for (bow = mon->inv; bow != NULL; bow = bow->below) 01557 if (bow->type == BOW && bow->race == item->race) { 01558 SET_FLAG(mon, FLAG_READY_BOW); 01559 LOG(llevMonster, "Found correct bow for arrows.\n"); 01560 return; /* nothing more to do for arrows */ 01561 } 01562 } 01563 01564 if (item->type == TREASURE && mon->will_apply&WILL_APPLY_TREASURE) 01565 flag = 1; 01566 /* Eating food gets hp back */ 01567 else if (item->type == FOOD && mon->will_apply&WILL_APPLY_FOOD) 01568 flag = 1; 01569 else if (item->type == SCROLL && QUERY_FLAG(mon, FLAG_USE_SCROLL)) { 01570 if (!item->inv) 01571 LOG(llevDebug, "Monster %d having scroll %d with empty inventory!\n", mon->count, item->count); 01572 else if (monster_should_cast_spell(mon, item->inv)) 01573 SET_FLAG(mon, FLAG_READY_SCROLL); 01574 /* Don't use it right now */ 01575 return; 01576 } else if (item->type == WEAPON) 01577 flag = check_good_weapon(mon, item); 01578 else if (IS_ARMOR(item) || IS_SHIELD(item)) 01579 flag = check_good_armour(mon, item); 01580 /* Should do something more, like make sure this is a better item */ 01581 else if (item->type == RING) 01582 flag = 1; 01583 else if (item->type == WAND || item->type == ROD || item->type == HORN) { 01584 /* We never really 'ready' the wand/rod/horn, because that would mean the 01585 * weapon would get undone. 01586 */ 01587 if (!(can_apply_object(mon, item)&CAN_APPLY_NOT_MASK)) { 01588 SET_FLAG(mon, FLAG_READY_RANGE); 01589 SET_FLAG(item, FLAG_APPLIED); 01590 } 01591 return; 01592 } else if (item->type == BOW) { 01593 /* We never really 'ready' the bow, because that would mean the 01594 * weapon would get undone. 01595 */ 01596 if (!(can_apply_object(mon, item)&CAN_APPLY_NOT_MASK)) 01597 SET_FLAG(mon, FLAG_READY_BOW); 01598 return; 01599 } else if (item->type == SKILL) { 01600 /* 01601 * skills are specials: monsters must have the 'FLAG_READY_SKILL' flag set, 01602 * else they can't use the skill... 01603 * Skills also don't need to get applied, so return now. 01604 */ 01605 SET_FLAG(mon, FLAG_READY_SKILL); 01606 return; 01607 } 01608 01609 /* if we don't match one of the above types, return now. 01610 * can_apply_object will say that we can apply things like flesh, 01611 * bolts, and whatever else, because it only checks against the 01612 * body_info locations. 01613 */ 01614 if (!flag) 01615 return; 01616 01617 /* Check to see if the monster can use this item. If not, no need 01618 * to do further processing. Note that can_apply_object already checks 01619 * for the CAN_USE flags. 01620 */ 01621 if (can_apply_object(mon, item)&CAN_APPLY_NOT_MASK) 01622 return; 01623 01624 /* should only be applying this item, not unapplying it. 01625 * also, ignore status of curse so they can take off old armour. 01626 * monsters have some advantages after all. 01627 */ 01628 manual_apply(mon, item, AP_APPLY|AP_IGNORE_CURSE); 01629 return; 01630 } 01631 01632 void npc_call_help(object *op) { 01633 int x, y, mflags; 01634 object *npc; 01635 sint16 sx, sy; 01636 mapstruct *m; 01637 01638 for (x = -3; x < 4; x++) 01639 for (y = -3; y < 4; y++) { 01640 m = op->map; 01641 sx = op->x+x; 01642 sy = op->y+y; 01643 mflags = get_map_flags(m, &m, sx, sy, &sx, &sy); 01644 /* If nothing alive on this space, no need to search the space. */ 01645 if ((mflags&P_OUT_OF_MAP) || !(mflags&P_IS_ALIVE)) 01646 continue; 01647 01648 for (npc = GET_MAP_OB(m, sx, sy); npc != NULL; npc = npc->above) 01649 if (QUERY_FLAG(npc, FLAG_ALIVE) && QUERY_FLAG(npc, FLAG_UNAGGRESSIVE)) 01650 npc->enemy = op->enemy; 01651 } 01652 } 01653 01654 static int dist_att(int dir, object *ob, object *enemy, object *part, rv_vector *rv) { 01655 if (can_hit(part, enemy, rv)) 01656 return dir; 01657 if (rv->distance < 10) 01658 return absdir(dir+4); 01659 else if (rv->distance > 18) 01660 return dir; 01661 return 0; 01662 } 01663 01664 static int run_att(int dir, object *ob, object *enemy, object *part, rv_vector *rv) { 01665 if ((can_hit(part, enemy, rv) && ob->move_status < 20) || ob->move_status < 20) { 01666 ob->move_status++; 01667 return (dir); 01668 } else if (ob->move_status > 20) 01669 ob->move_status = 0; 01670 return absdir(dir+4); 01671 } 01672 01673 static int hitrun_att(int dir, object *ob, object *enemy) { 01674 if (ob->move_status++ < 25) 01675 return dir; 01676 else if (ob->move_status < 50) 01677 return absdir(dir+4); 01678 else 01679 ob->move_status = 0; 01680 return absdir(dir+4); 01681 } 01682 01683 static int wait_att(int dir, object *ob, object *enemy, object *part, rv_vector *rv) { 01684 int inrange = can_hit(part, enemy, rv); 01685 01686 if (ob->move_status || inrange) 01687 ob->move_status++; 01688 01689 if (ob->move_status == 0) 01690 return 0; 01691 else if (ob->move_status < 10) 01692 return dir; 01693 else if (ob->move_status < 15) 01694 return absdir(dir+4); 01695 ob->move_status = 0; 01696 return 0; 01697 } 01698 01699 static int disthit_att(int dir, object *ob, object *enemy, object *part, rv_vector *rv) { 01700 /* The logic below here looked plain wrong before. Basically, what should 01701 * happen is that if the creatures hp percentage falls below run_away, 01702 * the creature should run away (dir+4) 01703 * I think its wrong for a creature to have a zero maxhp value, but 01704 * at least one map has this set, and whatever the map contains, the 01705 * server should try to be resilant enough to avoid the problem 01706 */ 01707 if (ob->stats.maxhp && (ob->stats.hp*100)/ob->stats.maxhp < ob->run_away) 01708 return absdir(dir+4); 01709 return dist_att(dir, ob, enemy, part, rv); 01710 } 01711 01712 static int wait_att2(int dir, object *ob, object *enemy, object *part, rv_vector *rv) { 01713 if (rv->distance < 9) 01714 return absdir(dir+4); 01715 return 0; 01716 } 01717 01718 static void circ1_move(object *ob) { 01719 static const int circle [12] = { 3, 3, 4, 5, 5, 6, 7, 7, 8, 1, 1, 2 }; 01720 01721 if (++ob->move_status > 11) 01722 ob->move_status = 0; 01723 if (!(move_object(ob, circle[ob->move_status]))) 01724 (void)move_object(ob, RANDOM()%8+1); 01725 } 01726 01727 static void circ2_move(object *ob) { 01728 static const int circle[20] = { 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 1, 1, 1, 2, 2 }; 01729 01730 if (++ob->move_status > 19) 01731 ob->move_status = 0; 01732 if (!(move_object(ob, circle[ob->move_status]))) 01733 (void)move_object(ob, RANDOM()%8+1); 01734 } 01735 01736 static void pace_movev(object *ob) { 01737 if (ob->move_status++ > 6) 01738 ob->move_status = 0; 01739 if (ob->move_status < 4) 01740 (void)move_object(ob, 5); 01741 else 01742 (void)move_object(ob, 1); 01743 } 01744 01745 static void pace_moveh(object *ob) { 01746 if (ob->move_status++ > 6) 01747 ob->move_status = 0; 01748 if (ob->move_status < 4) 01749 (void)move_object(ob, 3); 01750 else 01751 (void)move_object(ob, 7); 01752 } 01753 01754 static void pace2_movev(object *ob) { 01755 if (ob->move_status++ > 16) 01756 ob->move_status = 0; 01757 if (ob->move_status < 6) 01758 (void)move_object(ob, 5); 01759 else if (ob->move_status < 8) 01760 return; 01761 else if (ob->move_status < 13) 01762 (void)move_object(ob, 1); 01763 else 01764 return; 01765 } 01766 01767 static void pace2_moveh(object *ob) { 01768 if (ob->move_status++ > 16) 01769 ob->move_status = 0; 01770 if (ob->move_status < 6) 01771 (void)move_object(ob, 3); 01772 else if (ob->move_status < 8) 01773 return; 01774 else if (ob->move_status < 13) 01775 (void)move_object(ob, 7); 01776 else 01777 return; 01778 } 01779 01780 static void rand_move(object *ob) { 01781 int i; 01782 01783 if (ob->move_status < 1 01784 || ob->move_status > 8 01785 || !(move_object(ob, ob->move_status || !(RANDOM()%9)))) 01786 for (i = 0; i < 5; i++) 01787 if (move_object(ob, ob->move_status = RANDOM()%8+1)) 01788 return; 01789 } 01790 01791 void check_earthwalls(object *op, mapstruct *m, int x, int y) { 01792 object *tmp; 01793 01794 for (tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = tmp->above) { 01795 if (tmp->type == EARTHWALL) { 01796 hit_player(tmp, op->stats.dam, op, AT_PHYSICAL, 1); 01797 return; 01798 } 01799 } 01800 } 01801 01802 void check_doors(object *op, mapstruct *m, int x, int y) { 01803 object *tmp; 01804 01805 for (tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = tmp->above) { 01806 if (tmp->type == DOOR) { 01807 hit_player(tmp, 1000, op, AT_PHYSICAL, 1); 01808 return; 01809 } 01810 } 01811 } 01812 01831 void communicate(object *op, const char *txt) { 01832 object *npc; 01833 int i, mflags, talked = 0; 01834 sint16 x, y; 01835 mapstruct *mp, *orig_map = op->map; 01836 char buf[MAX_BUF]; 01837 01838 snprintf(buf, sizeof(buf), "%s says: %s", op->name, txt); 01839 if (op->type == PLAYER) { 01840 ext_info_map(NDI_WHITE, op->map, MSG_TYPE_COMMUNICATION, MSG_TYPE_COMMUNICATION_SAY, buf, NULL); 01841 } 01842 01843 /* Note that this loop looks pretty inefficient to me - we look and try to talk 01844 * to every object within 2 spaces. It would seem that if we trim this down to 01845 * only try to talk to objects with npc->msg set, things would be a lot more efficient, 01846 * but I'm not sure if there are any objects out there that don't have a message and instead 01847 * rely sorely on events - MSW 2009-04-14 01848 */ 01849 for (i = 0; i <= SIZEOFFREE2; i++) { 01850 mp = op->map; 01851 x = op->x+freearr_x[i]; 01852 y = op->y+freearr_y[i]; 01853 01854 mflags = get_map_flags(mp, &mp, x, y, &x, &y); 01855 if (mflags&P_OUT_OF_MAP) 01856 continue; 01857 01858 for (npc = GET_MAP_OB(mp, x, y); npc != NULL; npc = npc->above) { 01859 talk_to_npc(op, npc, txt, &talked); 01860 if (orig_map != op->map) { 01861 LOG(llevDebug, "Warning: Forced to swap out very recent map - MAX_OBJECTS should probably be increased\n"); 01862 return; 01863 } 01864 } 01865 } 01866 01867 /* if talked is set, then the talk_to_npc() wrote out this information, so 01868 * don't do it again. 01869 */ 01870 if (!talked) { 01871 } 01872 } 01873 01883 static int do_talk_npc(object *op, object *npc, const char *txt, int *talked) { 01884 char buf[MAX_BUF]; 01885 struct_dialog_reply *reply; 01886 struct_dialog_message *message; 01887 01888 if (!get_dialog_message(npc, txt, &message, &reply)) 01889 return 0; 01890 01891 if (reply) { 01892 snprintf(buf, sizeof(buf), "%s %s: %s", op->name, (reply->type == rt_reply ? "replies" : "asks"), reply->message); 01893 ext_info_map(NDI_WHITE, op->map, MSG_TYPE_COMMUNICATION, MSG_TYPE_COMMUNICATION_SAY, buf, NULL); 01894 *talked = 1; 01895 } 01896 #if 0 01897 /* let the caller handle this reply - no reason we need to. Leaving this in for the 01898 * time being, as I don't completely understand what all of this is trying to do. 01899 * MSW 2009-04-14 01900 */ 01901 01902 else if (!*talked) { 01903 *talked = 1; 01904 snprintf(buf, sizeof(buf), "%s says: %s", op->name, txt); 01905 ext_info_map(NDI_WHITE, op->map, MSG_TYPE_COMMUNICATION, MSG_TYPE_COMMUNICATION_SAY, buf, NULL); 01906 } 01907 #endif 01908 01909 if (npc->type == MAGIC_EAR) { 01910 ext_info_map(NDI_NAVY|NDI_UNIQUE, npc->map, MSG_TYPE_DIALOG, MSG_TYPE_DIALOG_MAGIC_MOUTH, message->message, NULL); 01911 use_trigger(npc); 01912 } else { 01913 npc_say(npc, message->message); 01914 reply = message->replies; 01915 01916 if (reply) { 01917 draw_ext_info(NDI_WHITE, 0, op, MSG_TYPE_COMMUNICATION, MSG_TYPE_COMMUNICATION_SAY, "Replies:", NULL); 01918 while (reply) { 01919 draw_ext_info_format(NDI_WHITE, 0, op, MSG_TYPE_COMMUNICATION, MSG_TYPE_COMMUNICATION_SAY, " - %s: %s", NULL, reply->reply, reply->message); 01920 reply = reply->next; 01921 } 01922 } 01923 } 01924 01925 return 1; 01926 } 01927 01933 void npc_say(object *npc, const char *cp) { 01934 char buf[HUGE_BUF], name[MAX_BUF]; 01935 01936 query_name(npc, name, sizeof(name)); 01937 snprintf(buf, sizeof(buf), "%s says: %s", name, cp); 01938 ext_info_map(NDI_NAVY|NDI_UNIQUE, npc->map, MSG_TYPE_DIALOG, MSG_TYPE_DIALOG_NPC, 01939 buf, buf); 01940 } 01941 01952 static int talk_to_npc(object *op, object *npc, const char *txt, int *talked) { 01953 object *cobj; 01954 01955 /* Move this commone area up here - shouldn't cost much extra cpu 01956 * time, and makes the function more readable */ 01957 /* Lauwenmark: Handle for plugin say event */ 01958 if (execute_event(npc, EVENT_SAY, op, NULL, txt, SCRIPT_FIX_ALL) != 0) 01959 return 0; 01960 /* Lauwenmark - Here we let the objects inside inventories hear and answer, too. */ 01961 /* This allows the existence of "intelligent" weapons you can discuss with */ 01962 for (cobj = npc->inv; cobj != NULL; cobj = cobj->below) { 01963 if (execute_event(cobj, EVENT_SAY, npc, NULL, txt, SCRIPT_FIX_ALL) != 0) 01964 return 0; 01965 } 01966 if (op == npc) 01967 return 0; 01968 return do_talk_npc(op, npc, txt, talked); 01969 } 01970 01971 /* find_mon_throw_ob() - modeled on find_throw_ob 01972 * This is probably overly simplistic as it is now - We want 01973 * monsters to throw things like chairs and other pieces of 01974 * furniture, even if they are not good throwable objects. 01975 * Probably better to have the monster throw a throwable object 01976 * first, then throw any non equipped weapon. 01977 */ 01978 object *find_mon_throw_ob(object *op) { 01979 object *tmp = NULL; 01980 01981 if (op->head) 01982 tmp = op->head; 01983 else 01984 tmp = op; 01985 01986 /* New throw code: look through the inventory. Grap the first legal is_thrown 01987 * marked item and throw it to the enemy. 01988 */ 01989 01990 for (tmp = op->inv; tmp; tmp = tmp->below) { 01991 /* Can't throw invisible objects or items that are applied */ 01992 if (tmp->invisible || QUERY_FLAG(tmp, FLAG_APPLIED)) 01993 continue; 01994 01995 if (QUERY_FLAG(tmp, FLAG_IS_THROWN)) 01996 break; 01997 } 01998 01999 #ifdef DEBUG_THROW 02000 { 02001 char what[MAX_BUF]; 02002 02003 query_name(tmp, what, MAX_BUF); 02004 LOG(llevDebug, "%s chooses to throw: %s (%d)\n", op->name, !(tmp) ? "(nothing)" : what, tmp ? tmp->count : -1); 02005 } 02006 #endif 02007 02008 return tmp; 02009 } 02010 02011 /* determine if we can 'detect' the enemy. Check for walls blocking the 02012 * los. Also, just because its hidden/invisible, we may be sensitive/smart 02013 * enough (based on Wis & Int) to figure out where the enemy is. -b.t. 02014 * modified by MSW to use the get_rangevector so that map tiling works 02015 * properly. I also so odd code in place that checked for x distance 02016 * OR y distance being within some range - that seemed wrong - both should 02017 * be within the valid range. MSW 2001-08-05 02018 * Returns 0 if enemy can not be detected, 1 if it is detected 02019 */ 02020 int can_detect_enemy(object *op, object *enemy, rv_vector *rv) { 02021 int radius = MIN_MON_RADIUS, hide_discovery; 02022 02023 /* null detection for any of these condtions always */ 02024 if (!op || !enemy || !op->map || !enemy->map) 02025 return 0; 02026 02027 /* If the monster (op) has no way to get to the enemy, do nothing */ 02028 if (!on_same_map(op, enemy)) 02029 return 0; 02030 02031 get_rangevector(op, enemy, rv, 0); 02032 02033 /* Monsters always ignore the DM */ 02034 if ((op->type != PLAYER) && QUERY_FLAG(enemy, FLAG_WIZ)) 02035 return 0; 02036 02037 /* simple check. Should probably put some range checks in here. */ 02038 if (can_see_enemy(op, enemy)) 02039 return 1; 02040 02041 /* The rest of this is for monsters. Players are on their own for 02042 * finding enemies! 02043 */ 02044 if (op->type == PLAYER) 02045 return 0; 02046 02047 /* Quality invisible? Bah, we wont see them w/o SEE_INVISIBLE 02048 * flag (which was already checked) in can_see_enmy (). Lets get out of here 02049 */ 02050 if (enemy->invisible && (!enemy->contr || (!enemy->contr->tmp_invis && !enemy->contr->hidden))) 02051 return 0; 02052 02053 /* use this for invis also */ 02054 hide_discovery = op->stats.Int/5; 02055 02056 /* Determine Detection radii */ 02057 if (!enemy->hide) /* to detect non-hidden (eg dark/invis enemy) */ 02058 radius = MAX((op->stats.Wis/5)+1, MIN_MON_RADIUS); 02059 else { /* a level/INT/Dex adjustment for hiding */ 02060 object *sk_hide; 02061 int bonus = (op->level/2)+(op->stats.Int/5); 02062 02063 if (enemy->type == PLAYER) { 02064 if ((sk_hide = find_skill_by_number(enemy, SK_HIDING))) 02065 bonus -= sk_hide->level; 02066 else { 02067 LOG(llevError, "can_detect_enemy() got hidden player w/o hiding skill!\n"); 02068 make_visible(enemy); 02069 radius = MAX(radius, MIN_MON_RADIUS); 02070 } 02071 } else /* enemy is not a player */ 02072 bonus -= enemy->level; 02073 02074 radius += bonus/5; 02075 hide_discovery += bonus*5; 02076 } /* else creature has modifiers for hiding */ 02077 02078 /* Radii stealth adjustment. Only if you are stealthy 02079 * will you be able to sneak up closer to creatures */ 02080 if (QUERY_FLAG(enemy, FLAG_STEALTH)) 02081 radius = radius/2, hide_discovery = hide_discovery/3; 02082 02083 /* Radii adjustment for enemy standing in the dark */ 02084 if (op->map->darkness > 0 && !stand_in_light(enemy)) { 02085 /* on dark maps body heat can help indicate location with infravision 02086 * undead don't have body heat, so no benefit detecting them. 02087 */ 02088 if (QUERY_FLAG(op, FLAG_SEE_IN_DARK) && !is_true_undead(enemy)) 02089 radius += op->map->darkness/2; 02090 else 02091 radius -= op->map->darkness/2; 02092 02093 /* op next to a monster (and not in complete darkness) 02094 * the monster should have a chance to see you. 02095 */ 02096 if (radius < MIN_MON_RADIUS && op->map->darkness < 5 && rv->distance <= 1) 02097 radius = MIN_MON_RADIUS; 02098 } /* if on dark map */ 02099 02100 /* Lets not worry about monsters that have incredible detection 02101 * radii, we only need to worry here about things the player can 02102 * (potentially) see. This is 13, as that is the maximum size the player 02103 * may have for their map - in that way, creatures at the edge will 02104 * do something. Note that the distance field in the 02105 * vector is real distance, so in theory this should be 18 to 02106 * find that. 02107 */ 02108 if (radius > 13) 02109 radius = 13; 02110 02111 /* Enemy in range! Now test for detection */ 02112 if ((int)rv->distance <= radius) { 02113 /* ah, we are within range, detected? take cases */ 02114 if (!enemy->invisible) /* enemy in dark squares... are seen! */ 02115 return 1; 02116 02117 /* hidden or low-quality invisible */ 02118 if (enemy->hide && (rv->distance <= 1) && (RANDOM()%100 <= hide_discovery)) { 02119 make_visible(enemy); 02120 /* inform players of new status */ 02121 if (enemy->type == PLAYER && player_can_view(enemy, op)) 02122 draw_ext_info_format(NDI_UNIQUE, 0, enemy, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE, 02123 "You are discovered by %s!", 02124 "You are discovered by %s!", 02125 op->name); 02126 return 1; /* detected enemy */ 02127 } else if (enemy->invisible) { 02128 /* Change this around - instead of negating the invisible, just 02129 * return true so that the mosnter that managed to detect you can 02130 * do something to you. Decreasing the duration of invisible 02131 * doesn't make a lot of sense IMO, as a bunch of stupid creatures 02132 * can then basically negate the spell. The spell isn't negated - 02133 * they just know where you are! 02134 */ 02135 if ((RANDOM()%50) <= hide_discovery) { 02136 if (enemy->type == PLAYER) { 02137 char name[MAX_BUF]; 02138 02139 query_name(op, name, MAX_BUF); 02140 draw_ext_info_format(NDI_UNIQUE, 0, enemy, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE, 02141 "You see %s noticing your position.", 02142 "You see %s noticing your position.", 02143 name); 02144 } 02145 return 1; 02146 } 02147 } 02148 } /* within range */ 02149 02150 /* Wasn't detected above, so still hidden */ 02151 return 0; 02152 } 02153 02154 /* determine if op stands in a lighted square. This is not a very 02155 * intellegent algorithm. For one thing, we ignore los here, SO it 02156 * is possible for a bright light to illuminate a player on the 02157 * other side of a wall (!). 02158 */ 02159 int stand_in_light(object *op) { 02160 sint16 nx, ny; 02161 mapstruct *m; 02162 02163 if (!op) 02164 return 0; 02165 if (op->glow_radius > 0) 02166 return 1; 02167 02168 if (op->map) { 02169 int x, y, x1, y1; 02170 02171 /* Check the spaces with the max light radius to see if any of them 02172 * have lights, and if any of them light the player enough, then return 1. 02173 */ 02174 for (x = op->x-MAX_LIGHT_RADII; x <= op->x+MAX_LIGHT_RADII; x++) { 02175 for (y = op->y-MAX_LIGHT_RADII; y <= op->y+MAX_LIGHT_RADII; y++) { 02176 m = op->map; 02177 nx = x; 02178 ny = y; 02179 02180 if (get_map_flags(m, &m, nx, ny, &nx, &ny)&P_OUT_OF_MAP) 02181 continue; 02182 02183 x1 = abs(x-op->x)*abs(x-op->x); 02184 y1 = abs(y-op->y)*abs(y-op->y); 02185 if (isqrt(x1+y1) < GET_MAP_LIGHT(m, nx, ny)) 02186 return 1; 02187 } 02188 } 02189 } 02190 return 0; 02191 } 02192 02193 /* 02194 * assuming no walls/barriers, lets check to see if its *possible* 02195 * to see an enemy. Note, "detection" is different from "seeing". 02196 * See can_detect_enemy() for more details. -b.t. 02197 * return 0 if can't be seen, 1 if can be 02198 */ 02199 int can_see_enemy(object *op, object *enemy) { 02200 object *looker = op->head ? op->head : op; 02201 02202 /* safety */ 02203 if (!looker || !enemy || !QUERY_FLAG(looker, FLAG_ALIVE)) 02204 return 0; 02205 02206 /* we dont give a full treatment of xrays here (shorter range than normal, 02207 * see through walls). Should we change the code elsewhere to make you 02208 * blind even if you can xray? 02209 */ 02210 if (QUERY_FLAG(looker, FLAG_BLIND) && !QUERY_FLAG(looker, FLAG_XRAYS)) 02211 return 0; 02212 02213 /* checking for invisible things */ 02214 if (enemy->invisible) { 02215 /* HIDDEN ENEMY. by definition, you can't see hidden stuff! 02216 * However, if you carry any source of light, then the hidden 02217 * creature is seeable (and stupid) */ 02218 if (has_carried_lights(enemy)) { 02219 if (enemy->hide) { 02220 make_visible(enemy); 02221 draw_ext_info(NDI_UNIQUE, 0, enemy, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE, 02222 "Your light reveals your hiding spot!", 02223 NULL); 02224 } 02225 return 1; 02226 } else if (enemy->hide) 02227 return 0; 02228 02229 /* Invisible enemy. Break apart the check for invis undead/invis looker 02230 * into more simple checks - the QUERY_FLAG doesn't return 1/0 values, 02231 * and making it a conditional makes the code pretty ugly. 02232 */ 02233 if (!QUERY_FLAG(looker, FLAG_SEE_INVISIBLE)) { 02234 if (makes_invisible_to(enemy, looker)) 02235 return 0; 02236 } 02237 } else if (looker->type == PLAYER) /* for players, a (possible) shortcut */ 02238 if (player_can_view(looker, enemy)) 02239 return 1; 02240 02241 /* ENEMY IN DARK MAP. Without infravision, the enemy is not seen 02242 * unless they carry a light or stand in light. Darkness doesnt 02243 * inhibit the undead per se (but we should give their archs 02244 * CAN_SEE_IN_DARK, this is just a safety 02245 * we care about the enemy maps status, not the looker. 02246 * only relevant for tiled maps, but it is possible that the 02247 * enemy is on a bright map and the looker on a dark - in that 02248 * case, the looker can still see the enemy 02249 */ 02250 if (enemy->map->darkness > 0 02251 && !stand_in_light(enemy) 02252 && (!QUERY_FLAG(looker, FLAG_SEE_IN_DARK) || !is_true_undead(looker) || !QUERY_FLAG(looker, FLAG_XRAYS))) 02253 return 0; 02254 02255 return 1; 02256 }