|
Atrinik Server 2.5
|
00001 /************************************************************************ 00002 * Atrinik, a Multiplayer Online Role Playing Game * 00003 * * 00004 * Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team * 00005 * * 00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game) * 00007 * and Crossfire (Multiplayer game for X-windows). * 00008 * * 00009 * This program is free software; you can redistribute it and/or modify * 00010 * it under the terms of the GNU General Public License as published by * 00011 * the Free Software Foundation; either version 2 of the License, or * 00012 * (at your option) any later version. * 00013 * * 00014 * This program is distributed in the hope that it will be useful, * 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00017 * GNU General Public License for more details. * 00018 * * 00019 * You should have received a copy of the GNU General Public License * 00020 * along with this program; if not, write to the Free Software * 00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * 00022 * * 00023 * The author can be reached at admin@atrinik.org * 00024 ************************************************************************/ 00025 00032 #include <global.h> 00033 00034 extern spell_struct spells[NROFREALSPELLS]; 00035 00036 static int can_detect_enemy(object *op, object *enemy, rv_vector *rv); 00037 static object *find_nearest_enemy(object *ob); 00038 static int move_randomly(object *op); 00039 static int can_hit(object *ob1, rv_vector *rv); 00040 static object *monster_choose_random_spell(object *monster, uint32 flags); 00041 static int monster_cast_spell(object *head, object *part, int dir, rv_vector *rv, uint32 flags); 00042 static int monster_use_bow(object *head, object *part, int dir); 00043 static int dist_att(int dir, object *part, rv_vector *rv); 00044 static int run_att(int dir, object *ob, object *part, rv_vector *rv); 00045 static int hitrun_att(int dir, object *ob); 00046 static int wait_att(int dir, object *ob, object *part, rv_vector *rv); 00047 static int disthit_att(int dir, object *ob, object *part, rv_vector *rv); 00048 static int wait_att2(int dir, rv_vector *rv); 00049 static void circ1_move(object *ob); 00050 static void circ2_move(object *ob); 00051 static void pace_movev(object *ob); 00052 static void pace_moveh(object *ob); 00053 static void pace2_movev(object *ob); 00054 static void pace2_moveh(object *ob); 00055 static void rand_move(object *ob); 00056 static int talk_to_wall(object *op, object *npc, char *txt); 00057 00072 void set_npc_enemy(object *npc, object *enemy, rv_vector *rv) 00073 { 00074 object *aggro_wp; 00075 rv_vector rv2; 00076 00077 /* Do nothing if new enemy == old enemy */ 00078 if (enemy == npc->enemy && (enemy == NULL || enemy->count == npc->enemy_count)) 00079 { 00080 return; 00081 } 00082 00083 /* Players don't need waypoints, speed updates or aggro counters */ 00084 if (npc->type == PLAYER) 00085 { 00086 npc->enemy = enemy; 00087 00088 if (enemy) 00089 { 00090 npc->enemy_count = enemy->count; 00091 } 00092 00093 return; 00094 } 00095 00096 /* Non-aggro-waypoint related stuff */ 00097 if (enemy) 00098 { 00099 if (rv == NULL) 00100 { 00101 rv = &rv2; 00102 } 00103 00104 get_rangevector(npc, enemy, rv, RV_DIAGONAL_DISTANCE); 00105 npc->enemy_count = enemy->count; 00106 00107 /* important: that's our "we lose aggro count" - reset to zero here */ 00108 npc->last_eat = 0; 00109 00110 /* Monster has changed status from normal to attack - let's hear it! */ 00111 if (npc->enemy == NULL && !QUERY_FLAG(npc, FLAG_FRIENDLY)) 00112 { 00113 play_sound_map(npc->map, CMD_SOUND_EFFECT, "growl.ogg", npc->x, npc->y, 0, 0); 00114 } 00115 00116 if (QUERY_FLAG(npc, FLAG_UNAGGRESSIVE)) 00117 { 00118 /* The unaggressives look after themselves 8) */ 00119 CLEAR_FLAG(npc, FLAG_UNAGGRESSIVE); 00120 } 00121 } 00122 else 00123 { 00124 object *base = insert_base_info_object(npc); 00125 object *wp = get_active_waypoint(npc); 00126 00127 if (base && !wp && wp_archetype) 00128 { 00129 object *return_wp = get_return_waypoint(npc); 00130 00131 #ifdef DEBUG_PATHFINDING 00132 LOG(llevDebug, "set_npc_enemy(): %s lost aggro and is returning home (%s:%d,%d)\n", STRING_OBJ_NAME(npc), base->slaying, base->x, base->y); 00133 #endif 00134 if (!return_wp) 00135 { 00136 return_wp = arch_to_object(wp_archetype); 00137 insert_ob_in_ob(return_wp, npc); 00138 return_wp->owner = npc; 00139 return_wp->ownercount = npc->count; 00140 FREE_AND_ADD_REF_HASH(return_wp->name, shstr_cons.home); 00141 /* mark as return-home wp */ 00142 SET_FLAG(return_wp, FLAG_REFLECTING); 00143 /* mark as best-effort wp */ 00144 SET_FLAG(return_wp, FLAG_NO_ATTACK); 00145 } 00146 00147 return_wp->stats.hp = base->x; 00148 return_wp->stats.sp = base->y; 00149 FREE_AND_ADD_REF_HASH(return_wp->slaying, base->slaying); 00150 /* Activate wp */ 00151 SET_FLAG(return_wp, FLAG_CURSED); 00152 /* reset best-effort timer */ 00153 return_wp->stats.Int = 0; 00154 00155 /* setup move_type to use waypoints */ 00156 return_wp->move_type = npc->move_type; 00157 npc->move_type = (npc->move_type & LO4) | WPOINT; 00158 00159 wp = return_wp; 00160 } 00161 00162 /* TODO: add a little pause to the active waypoint */ 00163 } 00164 00165 npc->enemy = enemy; 00166 /* Update speed */ 00167 set_mobile_speed(npc, 0); 00168 00169 /* Setup aggro waypoint */ 00170 if (!wp_archetype) 00171 { 00172 #ifdef DEBUG_PATHFINDING 00173 LOG(llevDebug, "set_npc_enemy(): Aggro waypoints disabled\n"); 00174 #endif 00175 return; 00176 } 00177 00178 /* TODO: check intelligence against lower limit to allow pathfind */ 00179 aggro_wp = get_aggro_waypoint(npc); 00180 00181 /* Create a new aggro wp for npc? */ 00182 if (!aggro_wp && enemy) 00183 { 00184 aggro_wp = arch_to_object(wp_archetype); 00185 insert_ob_in_ob(aggro_wp, npc); 00186 /* Mark as aggro WP */ 00187 SET_FLAG(aggro_wp, FLAG_DAMNED); 00188 aggro_wp->owner = npc; 00189 #ifdef DEBUG_PATHFINDING 00190 LOG(llevDebug, "set_npc_enemy(): created wp for '%s'\n", STRING_OBJ_NAME(npc)); 00191 #endif 00192 } 00193 00194 /* Set up waypoint target (if we actually got a waypoint) */ 00195 if (aggro_wp) 00196 { 00197 if (enemy) 00198 { 00199 aggro_wp->enemy_count = npc->enemy_count; 00200 aggro_wp->enemy = enemy; 00201 FREE_AND_ADD_REF_HASH(aggro_wp->name, enemy->name); 00202 #ifdef DEBUG_PATHFINDING 00203 LOG(llevDebug, "set_npc_enemy(): got wp for '%s' -> '%s'\n", npc->name, enemy->name); 00204 #endif 00205 } 00206 else 00207 { 00208 aggro_wp->enemy = NULL; 00209 #ifdef DEBUG_PATHFINDING 00210 LOG(llevDebug, "set_npc_enemy(): cleared aggro wp for '%s'\n", npc->name); 00211 #endif 00212 } 00213 } 00214 } 00215 00221 object *check_enemy(object *npc, rv_vector *rv) 00222 { 00223 if (npc->enemy == NULL) 00224 { 00225 return NULL; 00226 } 00227 00228 if (!OBJECT_VALID(npc->enemy, npc->enemy_count) || npc == npc->enemy || !IS_LIVE(npc->enemy) || is_friend_of(npc, npc->enemy)) 00229 { 00230 set_npc_enemy(npc, NULL, NULL); 00231 return NULL; 00232 } 00233 00234 return can_detect_enemy(npc, npc->enemy, rv) ? npc->enemy : NULL; 00235 } 00236 00243 object *find_enemy(object *npc, rv_vector *rv) 00244 { 00245 object *tmp = NULL; 00246 00247 /* If we are berserk, we don't care about others - we attack all we can 00248 * find. */ 00249 if (QUERY_FLAG(npc, FLAG_BERSERK)) 00250 { 00251 /* Always clear the attacker entry */ 00252 npc->attacked_by = NULL; 00253 tmp = find_nearest_enemy(npc); 00254 00255 if (tmp) 00256 { 00257 get_rangevector(npc, tmp, rv, 0); 00258 } 00259 00260 return tmp; 00261 } 00262 00263 tmp = check_enemy(npc, rv); 00264 00265 if (!tmp) 00266 { 00267 /* If we have an attacker, check him */ 00268 if (OBJECT_VALID(npc->attacked_by, npc->attacked_by_count) && !IS_INVISIBLE(npc->attacked_by, npc) && !QUERY_FLAG(npc->attacked_by, FLAG_INVULNERABLE)) 00269 { 00270 /* We don't want a fight evil vs evil or good against non evil. */ 00271 if (is_friend_of(npc, npc->attacked_by)) 00272 { 00273 /* Skip it, but let's wake up */ 00274 CLEAR_FLAG(npc, FLAG_SLEEP); 00275 } 00276 /* The only thing we must know... */ 00277 else if (on_same_map(npc, npc->attacked_by)) 00278 { 00279 CLEAR_FLAG(npc, FLAG_SLEEP); 00280 set_npc_enemy(npc, npc->attacked_by, rv); 00281 /* Always clear the attacker entry */ 00282 npc->attacked_by = NULL; 00283 00284 /* Face our attacker */ 00285 return npc->enemy; 00286 } 00287 } 00288 00289 /* We have no legal enemy or attacker, so we try to target a new 00290 * one. */ 00291 if (!QUERY_FLAG(npc, FLAG_UNAGGRESSIVE)) 00292 { 00293 tmp = find_nearest_enemy(npc); 00294 00295 if (tmp != npc->enemy) 00296 { 00297 set_npc_enemy(npc, tmp, rv); 00298 } 00299 } 00300 else if (npc->enemy) 00301 { 00302 /* Make sure to clear the enemy, even if FLAG_UNAGRESSIVE is true */ 00303 set_npc_enemy(npc, NULL, NULL); 00304 } 00305 } 00306 00307 /* Always clear the attacker entry */ 00308 npc->attacked_by = NULL; 00309 return tmp; 00310 } 00311 00320 static int can_detect_enemy(object *op, object *enemy, rv_vector *rv) 00321 { 00322 /* Will check for legal maps too */ 00323 if (!op || !enemy || !on_same_map(op, enemy)) 00324 { 00325 return 0; 00326 } 00327 00328 /* We check for sys_invisible and normal */ 00329 if (IS_INVISIBLE(enemy, op) || QUERY_FLAG(enemy, FLAG_INVULNERABLE)) 00330 { 00331 return 0; 00332 } 00333 00334 if (!get_rangevector(op, enemy, rv, 0)) 00335 { 00336 return 0; 00337 } 00338 00339 /* If our enemy is too far away ... */ 00340 if ((int) rv->distance >= MAX(MAX_AGGRO_RANGE, op->stats.Wis)) 00341 { 00342 /* Then start counting until our mob loses aggro... */ 00343 if (++op->last_eat > MAX_AGGRO_TIME) 00344 { 00345 set_npc_enemy(op, NULL, NULL); 00346 return 0; 00347 } 00348 } 00349 /* Our mob is aggroed again - because target is in range again */ 00350 else 00351 { 00352 op->last_eat = 0; 00353 } 00354 00355 return 1; 00356 } 00357 00362 int move_monster(object *op) 00363 { 00364 int dir, special_dir = 0, diff; 00365 object *enemy, *part; 00366 rv_vector rv; 00367 00368 if (op->head) 00369 { 00370 LOG(llevBug, "move_monster(): called from tail part. (%s -- %s)\n", query_name(op, NULL), op->arch->name); 00371 return 0; 00372 } 00373 00374 /* Monsters not on maps don't do anything. */ 00375 if (!op->map) 00376 { 00377 return 0; 00378 } 00379 00380 /* If we are here, we're never paralyzed anymore */ 00381 CLEAR_FLAG(op, FLAG_PARALYZED); 00382 00383 /* For target facing, we copy this value here for fast access */ 00384 op->anim_enemy_dir = -1; 00385 op->anim_moving_dir = -1; 00386 00387 /* Here is the heart of the mob attack and target area. 00388 * find_enemy() checks the old enemy or gets us a new one. */ 00389 00390 /* We never ever attack */ 00391 if (QUERY_FLAG(op, FLAG_NO_ATTACK)) 00392 { 00393 if (op->enemy) 00394 { 00395 set_npc_enemy(op, NULL, NULL); 00396 } 00397 00398 enemy = NULL; 00399 } 00400 else if ((enemy = find_enemy(op, &rv))) 00401 { 00402 CLEAR_FLAG(op, FLAG_SLEEP); 00403 op->anim_enemy_dir = rv.direction; 00404 00405 if (!enemy->attacked_by || (enemy->attacked_by && enemy->attacked_by_distance > (int) rv.distance)) 00406 { 00407 /* We have an enemy, just tell him we want him dead */ 00408 enemy->attacked_by = op; 00409 enemy->attacked_by_count = op->count; 00410 /* Now the attacked foe knows how near we are */ 00411 enemy->attacked_by_distance = (sint16) rv.distance; 00412 } 00413 } 00414 00415 /* Generate hp, if applicable */ 00416 if (op->stats.Con && op->stats.hp < op->stats.maxhp) 00417 { 00418 if (++op->last_heal > 5) 00419 { 00420 op->last_heal = 0; 00421 op->stats.hp += op->stats.Con; 00422 00423 if (op->stats.hp > op->stats.maxhp) 00424 { 00425 op->stats.hp = op->stats.maxhp; 00426 } 00427 } 00428 00429 /* So if the monster has gained enough HP that they are no longer afraid */ 00430 if (QUERY_FLAG(op, FLAG_RUN_AWAY) && op->stats.hp >= (signed short) (((float) op->run_away / (float) 100) * (float) op->stats.maxhp)) 00431 { 00432 CLEAR_FLAG(op, FLAG_RUN_AWAY); 00433 } 00434 } 00435 00436 /* Generate sp, if applicable */ 00437 if (op->stats.Pow && op->stats.sp < op->stats.maxsp) 00438 { 00439 op->last_sp += (int) ((float) (8 * op->stats.Pow) / FABS(op->speed)); 00440 /* causes Pow/16 sp/tick */ 00441 op->stats.sp += op->last_sp / 128; 00442 op->last_sp %= 128; 00443 00444 if (op->stats.sp > op->stats.maxsp) 00445 { 00446 op->stats.sp = op->stats.maxsp; 00447 } 00448 } 00449 00450 /* Time to regain some "guts"... */ 00451 if (QUERY_FLAG(op, FLAG_SCARED) && rndm_chance(20)) 00452 { 00453 CLEAR_FLAG(op, FLAG_SCARED); 00454 } 00455 00456 if (op->behavior & BEHAVIOR_SPELL_FRIENDLY) 00457 { 00458 if (op->last_grace) 00459 { 00460 op->last_grace--; 00461 } 00462 00463 if (op->stats.Dex && rndm_chance(op->stats.Dex)) 00464 { 00465 if (QUERY_FLAG(op, FLAG_CAST_SPELL) && !op->last_grace) 00466 { 00467 if (monster_cast_spell(op, op, 0, NULL, SPELL_DESC_FRIENDLY)) 00468 { 00469 /* Add monster casting delay */ 00470 op->last_grace += op->magic; 00471 } 00472 } 00473 } 00474 } 00475 00476 /* If we don't have an enemy, do special movement or the like */ 00477 if (!enemy) 00478 { 00479 if (QUERY_FLAG(op, FLAG_ONLY_ATTACK)) 00480 { 00481 remove_ob(op); 00482 check_walk_off(op, NULL, MOVE_APPLY_DEFAULT); 00483 return 1; 00484 } 00485 00486 if (!QUERY_FLAG(op, FLAG_STAND_STILL)) 00487 { 00488 if (op->move_type & HI4) 00489 { 00490 switch (op->move_type & HI4) 00491 { 00492 case CIRCLE1: 00493 circ1_move(op); 00494 break; 00495 00496 case CIRCLE2: 00497 circ2_move(op); 00498 break; 00499 00500 case PACEV: 00501 pace_movev(op); 00502 break; 00503 00504 case PACEH: 00505 pace_moveh(op); 00506 break; 00507 00508 case PACEV2: 00509 pace2_movev(op); 00510 break; 00511 00512 case PACEH2: 00513 pace2_moveh(op); 00514 break; 00515 00516 case RANDO: 00517 rand_move(op); 00518 break; 00519 00520 case RANDO2: 00521 move_randomly(op); 00522 break; 00523 00524 case WPOINT: 00525 waypoint_move(op, get_active_waypoint(op)); 00526 break; 00527 } 00528 00529 return 0; 00530 } 00531 else if (QUERY_FLAG(op, FLAG_RANDOM_MOVE)) 00532 { 00533 move_randomly(op); 00534 } 00535 } 00536 00537 return 0; 00538 } 00539 00540 part = rv.part; 00541 dir = rv.direction; 00542 00543 /* Move the check for scared up here - if the monster was scared, 00544 * we were not doing any of the logic below, so might as well save 00545 * a few CPU cycles. */ 00546 if (!QUERY_FLAG(op, FLAG_SCARED)) 00547 { 00548 if (op->last_grace) 00549 { 00550 op->last_grace--; 00551 } 00552 00553 if (op->stats.Dex && rndm_chance(op->stats.Dex)) 00554 { 00555 if (QUERY_FLAG(op, FLAG_CAST_SPELL) && !op->last_grace) 00556 { 00557 if (monster_cast_spell(op, part, dir, &rv, SPELL_DESC_DIRECTION | SPELL_DESC_ENEMY | SPELL_DESC_SELF)) 00558 { 00559 /* Add monster casting delay */ 00560 op->last_grace += op->magic; 00561 return 0; 00562 } 00563 } 00564 00565 if (QUERY_FLAG(op, FLAG_READY_BOW) && rndm_chance(4)) 00566 { 00567 if (monster_use_bow(op, part, dir) && rndm_chance(2)) 00568 { 00569 return 0; 00570 } 00571 } 00572 } 00573 } 00574 00575 if (QUERY_FLAG(op, FLAG_SCARED) || QUERY_FLAG(op, FLAG_RUN_AWAY)) 00576 { 00577 dir = absdir(dir + 4); 00578 } 00579 00580 if (QUERY_FLAG(op, FLAG_CONFUSED)) 00581 { 00582 dir = get_randomized_dir(dir); 00583 } 00584 00585 if (!QUERY_FLAG(op, FLAG_SCARED)) 00586 { 00587 if (op->attack_move_type & LO4) 00588 { 00589 switch (op->attack_move_type & LO4) 00590 { 00591 case DISTATT: 00592 special_dir = dist_att(dir, part, &rv); 00593 break; 00594 00595 case RUNATT: 00596 special_dir = run_att(dir, op, part, &rv); 00597 break; 00598 00599 case HITRUN: 00600 special_dir = hitrun_att(dir, op); 00601 break; 00602 00603 case WAITATT: 00604 special_dir = wait_att(dir, op, part, &rv); 00605 break; 00606 00607 case RUSH: 00608 case ALLRUN: 00609 special_dir = dir; 00610 break; 00611 00612 case DISTHIT: 00613 special_dir = disthit_att(dir, op, part, &rv); 00614 break; 00615 00616 case WAIT2: 00617 special_dir = wait_att2(dir, &rv); 00618 break; 00619 00620 default: 00621 LOG(llevDebug, "Illegal low mon-move: %d\n", op->attack_move_type & LO4); 00622 } 00623 00624 if (!special_dir) 00625 { 00626 return 0; 00627 } 00628 } 00629 } 00630 00631 /* Try to move closer to enemy, or follow whatever special attack behavior is */ 00632 if (!QUERY_FLAG(op, FLAG_STAND_STILL) && (QUERY_FLAG(op, FLAG_SCARED) || QUERY_FLAG(op, FLAG_RUN_AWAY) || !can_hit(part, &rv) || ((op->attack_move_type & LO4) && special_dir != dir))) 00633 { 00634 object *aggro_wp = get_aggro_waypoint(op); 00635 00636 /* TODO: make (intelligent) monsters go to last known position of enemy if out of range/sight */ 00637 00638 /* If special attack move -> follow it instead of going towards enemy */ 00639 if (((op->attack_move_type & LO4) && special_dir != dir)) 00640 { 00641 aggro_wp = NULL; 00642 dir = special_dir; 00643 } 00644 00645 /* If valid aggro wp (and no special attack), and not scared, use it for movement */ 00646 if (aggro_wp && aggro_wp->enemy && aggro_wp->enemy == op->enemy && rv.distance > 1 && !QUERY_FLAG(op, FLAG_SCARED) && !QUERY_FLAG(op, FLAG_RUN_AWAY)) 00647 { 00648 waypoint_move(op, aggro_wp); 00649 return 0; 00650 } 00651 else 00652 { 00653 int maxdiff = (QUERY_FLAG(op, FLAG_ONLY_ATTACK) || RANDOM() & 1) ? 1 : 2; 00654 00655 /* Can the monster move directly toward player? */ 00656 if (move_object(op, dir)) 00657 { 00658 return 0; 00659 } 00660 00661 /* Try move around corners if !close */ 00662 for (diff = 1; diff <= maxdiff; diff++) 00663 { 00664 /* try different detours */ 00665 /* Try left or right first? */ 00666 int m = 1 - (RANDOM() & 2); 00667 00668 if (move_object(op, absdir(dir + diff * m)) || move_object(op, absdir(dir - diff * m))) 00669 { 00670 return 0; 00671 } 00672 } 00673 } 00674 } 00675 00676 /* Eneq(@csd.uu.se): Patch to make RUN_AWAY or SCARED monsters move a random 00677 * direction if they can't move away. */ 00678 if (!QUERY_FLAG(op, FLAG_ONLY_ATTACK) && (QUERY_FLAG(op, FLAG_RUN_AWAY) || QUERY_FLAG(op, FLAG_SCARED))) 00679 { 00680 if (move_randomly(op)) 00681 { 00682 return 0; 00683 } 00684 } 00685 00686 /* Hit enemy if possible */ 00687 if (!QUERY_FLAG(op, FLAG_SCARED) && !QUERY_FLAG(enemy, FLAG_REMOVED) && can_hit(part, &rv)) 00688 { 00689 if (QUERY_FLAG(op, FLAG_RUN_AWAY)) 00690 { 00691 part->stats.wc -= 10; 00692 00693 /* As long we are > 0, we are not ready to swing */ 00694 if (op->weapon_speed_left <= 0) 00695 { 00696 skill_attack(enemy, part, 0, NULL); 00697 op->weapon_speed_left += FABS((int) op->weapon_speed_left) + 1; 00698 } 00699 00700 part->stats.wc += 10; 00701 } 00702 else 00703 { 00704 /* As long we are > 0, we are not ready to swing */ 00705 if (op->weapon_speed_left <= 0) 00706 { 00707 skill_attack(enemy, part, 0, NULL); 00708 op->weapon_speed_left += FABS((int) op->weapon_speed_left) + 1; 00709 } 00710 } 00711 } 00712 00713 /* Might be freed by ghost-attack or hit-back */ 00714 if (OBJECT_FREE(part)) 00715 { 00716 return 1; 00717 } 00718 00719 if (QUERY_FLAG(op, FLAG_ONLY_ATTACK)) 00720 { 00721 destruct_ob(op); 00722 return 1; 00723 } 00724 00725 return 0; 00726 } 00727 00736 static int can_detect_target(object *op, object *target, int range, int srange, rv_vector *rv) 00737 { 00738 /* We check for sys_invisible and normal */ 00739 if (IS_INVISIBLE(target, op) || QUERY_FLAG(target, FLAG_INVULNERABLE)) 00740 { 00741 return 0; 00742 } 00743 00744 if (!get_rangevector(op, target, rv, 0)) 00745 { 00746 return 0; 00747 } 00748 00749 if (QUERY_FLAG(target, FLAG_STEALTH) && !QUERY_FLAG(op, FLAG_XRAYS)) 00750 { 00751 if (srange < (int) rv->distance) 00752 { 00753 return 0; 00754 } 00755 } 00756 else 00757 { 00758 if (range < (int) rv->distance) 00759 { 00760 return 0; 00761 } 00762 } 00763 00764 return 1; 00765 } 00766 00771 static object *find_nearest_enemy(object *ob) 00772 { 00773 object *tmp; 00774 int aggro_range, aggro_stealth; 00775 rv_vector rv; 00776 int i, j, xt, yt; 00777 mapstruct *m; 00778 00779 aggro_range = ob->stats.Wis; 00780 00781 if (ob->enemy || ob->attacked_by) 00782 { 00783 aggro_range += 3; 00784 } 00785 00786 if (QUERY_FLAG(ob, FLAG_SLEEP) || QUERY_FLAG(ob, FLAG_BLIND)) 00787 { 00788 aggro_range /= 2; 00789 aggro_stealth = aggro_range - 2; 00790 } 00791 else 00792 { 00793 aggro_stealth = aggro_range - 2; 00794 } 00795 00796 if (aggro_stealth < MIN_MON_RADIUS) 00797 { 00798 aggro_stealth = MIN_MON_RADIUS; 00799 } 00800 00801 for (i = -aggro_range; i <= aggro_range; i++) 00802 { 00803 for (j = -aggro_range; j <= aggro_range; j++) 00804 { 00805 xt = ob->x + i; 00806 yt = ob->y + j; 00807 00808 if (!(m = get_map_from_coord2(ob->map, &xt, &yt))) 00809 { 00810 continue; 00811 } 00812 00813 /* Nothing alive here? Move on... */ 00814 if (!(GET_MAP_FLAGS(m, xt, yt) & (P_IS_ALIVE | P_IS_PLAYER))) 00815 { 00816 continue; 00817 } 00818 00819 for (tmp = GET_MAP_OB(m, xt, yt); tmp; tmp = tmp->above) 00820 { 00821 /* Get head. */ 00822 if (tmp->head) 00823 { 00824 tmp = tmp->head; 00825 } 00826 00827 /* Skip the monster looking for enemy and not alive objects. */ 00828 if (tmp == ob || !IS_LIVE(tmp)) 00829 { 00830 continue; 00831 } 00832 00833 /* Now check the friend status, whether we can reach the enemy, and LOS. */ 00834 if (!is_friend_of(ob, tmp) && can_detect_target(ob, tmp, aggro_range, aggro_stealth, &rv) && obj_in_line_of_sight(tmp, &rv)) 00835 { 00836 return tmp; 00837 } 00838 } 00839 } 00840 } 00841 00842 return NULL; 00843 } 00844 00849 static int move_randomly(object *op) 00850 { 00851 int i, r; 00852 int dirs[8] = {1, 2, 3, 4, 5, 6, 7, 8}; 00853 mapstruct *basemap = NULL; 00854 rv_vector rv; 00855 00856 if (op->item_race || op->item_level) 00857 { 00858 object *base = find_base_info_object(op); 00859 00860 if ((basemap = ready_map_name(base->slaying, MAP_NAME_SHARED))) 00861 { 00862 if (!get_rangevector_from_mapcoords(basemap, base->x, base->y, op->map, op->x, op->y, &rv, RV_NO_DISTANCE)) 00863 { 00864 basemap = NULL; 00865 } 00866 } 00867 } 00868 00869 /* Give up to 8 chances for a monster to move randomly */ 00870 for (i = 0; i < 8; i++) 00871 { 00872 int t = dirs[i]; 00873 00874 /* Perform a single random shuffle of the remaining directions */ 00875 r = i + (RANDOM() % (8 - i)); 00876 dirs[i] = dirs[r]; 00877 dirs[r] = t; 00878 00879 r = dirs[i]; 00880 00881 /* Check x and y direction of possible move against limit parameters */ 00882 if (basemap) 00883 { 00884 if (abs(rv.distance_x + freearr_x[r]) > op->item_race) 00885 { 00886 continue; 00887 } 00888 00889 if (abs(rv.distance_y + freearr_y[r]) > op->item_level) 00890 { 00891 continue; 00892 } 00893 } 00894 00895 if (HAS_EVENT(op, EVENT_AI)) 00896 { 00897 int ret = trigger_event(EVENT_AI, NULL, op, NULL, NULL, EVENT_AI_RANDOM_MOVE, r, 0, SCRIPT_FIX_NOTHING); 00898 00899 /* Cancel random movement. */ 00900 if (ret == 1) 00901 { 00902 return 0; 00903 } 00904 /* Keep trying. */ 00905 else if (ret == 2) 00906 { 00907 continue; 00908 } 00909 } 00910 00911 if (move_object(op, r)) 00912 { 00913 return 1; 00914 } 00915 } 00916 00917 return 0; 00918 } 00919 00925 static int can_hit(object *ob1, rv_vector *rv) 00926 { 00927 if (QUERY_FLAG(ob1, FLAG_CONFUSED) && !(RANDOM() % 3)) 00928 { 00929 return 0; 00930 } 00931 00932 return abs(rv->distance_x) < 2 && abs(rv->distance_y) < 2; 00933 } 00934 00935 #define MAX_KNOWN_SPELLS 20 00936 00942 static object *monster_choose_random_spell(object *monster, uint32 flags) 00943 { 00944 object *altern[MAX_KNOWN_SPELLS], *tmp; 00945 spell_struct *spell; 00946 int i = 0, j; 00947 00948 for (tmp = monster->inv; tmp != NULL; tmp = tmp->below) 00949 { 00950 if (tmp->type == ABILITY || tmp->type == SPELLBOOK) 00951 { 00952 /* Check and see if it's actually a useful spell */ 00953 if ((spell = find_spell(tmp->stats.sp)) != NULL && !(spell->path & (PATH_INFO | PATH_TRANSMUTE | PATH_TRANSFER | PATH_LIGHT)) && spell->flags & flags) 00954 { 00955 if (tmp->stats.maxsp) 00956 { 00957 for (j = 0; i < MAX_KNOWN_SPELLS && j < tmp->stats.maxsp; j++) 00958 { 00959 altern[i++] = tmp; 00960 } 00961 } 00962 else 00963 { 00964 altern[i++] = tmp; 00965 } 00966 00967 if (i == MAX_KNOWN_SPELLS) 00968 { 00969 break; 00970 } 00971 } 00972 00973 } 00974 } 00975 00976 if (!i) 00977 { 00978 return NULL; 00979 } 00980 00981 return altern[rndm(1, i) - 1]; 00982 } 00983 00989 static int monster_spell_useful(object *target, int spell_id) 00990 { 00991 switch (spell_id) 00992 { 00993 case SP_MINOR_HEAL: 00994 case SP_GREATER_HEAL: 00995 return target->stats.hp != target->stats.maxhp; 00996 } 00997 00998 return 1; 00999 } 01000 01010 static int monster_cast_spell(object *head, object *part, int dir, rv_vector *rv, uint32 flags) 01011 { 01012 object *spell_item, *target = NULL; 01013 spell_struct *sp; 01014 int sp_typ, ability; 01015 01016 if ((spell_item = monster_choose_random_spell(head, flags)) == NULL) 01017 { 01018 LOG(llevDebug, "monster_cast_spell: No spell found! Turned off spells in %s (%s) (%d,%d)\n", query_name(head, NULL), head->map ? (head->map->name ? head->map->name : "<no map name>") : "<no map!>", head->x, head->y ); 01019 /* Will be turned on when picking up book */ 01020 CLEAR_FLAG(head, FLAG_CAST_SPELL); 01021 return 0; 01022 } 01023 01024 /* Only considering long range spells if we're not looking for friendly target. */ 01025 if (spell_item->stats.hp != -1 && rv) 01026 { 01027 /* Alternate long-range spell: check how far away enemy is */ 01028 if (rv->distance > 6) 01029 { 01030 sp_typ = spell_item->stats.hp; 01031 } 01032 else 01033 { 01034 sp_typ = spell_item->stats.sp; 01035 } 01036 } 01037 else 01038 { 01039 sp_typ = spell_item->stats.sp; 01040 01041 /* Not looking for friendly target, but this is a friendly spell, and it's not the 01042 * same as long range one? */ 01043 if (rv && spells[sp_typ].flags & SPELL_DESC_FRIENDLY && sp_typ != spell_item->stats.hp) 01044 { 01045 return 0; 01046 } 01047 } 01048 01049 if (sp_typ == -1 || (sp = find_spell(sp_typ)) == NULL) 01050 { 01051 LOG(llevDebug, "monster_cast_spell: Can't find spell #%d for mob %s (%s) (%d,%d)\n", sp_typ, query_name(head, NULL), head->map ? (head->map->name ? head->map->name : "<no map name>") : "<no map!>", head->x, head->y); 01052 return 0; 01053 } 01054 01055 /* Monster doesn't have enough spellpoints */ 01056 if (head->stats.sp < SP_level_spellpoint_cost(head, sp_typ, -1)) 01057 { 01058 return 0; 01059 } 01060 01061 /* Spell should be cast on caster (ie, heal, strength) */ 01062 if (sp->flags & SPELL_DESC_SELF) 01063 { 01064 dir = 0; 01065 01066 if (rv && !monster_spell_useful(head, sp_typ)) 01067 { 01068 return 0; 01069 } 01070 } 01071 01072 if (!rv) 01073 { 01074 int i, j, xt, yt; 01075 object *tmp; 01076 mapstruct *m; 01077 01078 for (i = -sp->range; !target && i <= sp->range; i++) 01079 { 01080 for (j = -sp->range; !target && j <= sp->range; j++) 01081 { 01082 xt = head->x + i; 01083 yt = head->y + j; 01084 01085 if (!(m = get_map_from_coord2(head->map, &xt, &yt))) 01086 { 01087 continue; 01088 } 01089 01090 /* Nothing alive here? Move on... */ 01091 if (!(GET_MAP_FLAGS(m, xt, yt) & (P_IS_ALIVE | P_IS_PLAYER))) 01092 { 01093 continue; 01094 } 01095 01096 for (tmp = GET_MAP_OB(m, xt, yt); tmp; tmp = tmp->above) 01097 { 01098 /* Get head. */ 01099 if (tmp->head) 01100 { 01101 tmp = tmp->head; 01102 } 01103 01104 /* Skip the monster and not alive objects. */ 01105 if (tmp == head || !IS_LIVE(tmp)) 01106 { 01107 continue; 01108 } 01109 01110 if (is_friend_of(head, tmp) && monster_spell_useful(tmp, sp_typ)) 01111 { 01112 target = tmp; 01113 break; 01114 } 01115 } 01116 } 01117 } 01118 01119 if (!target) 01120 { 01121 return 0; 01122 } 01123 } 01124 01125 ability = (spell_item->type == ABILITY && QUERY_FLAG(spell_item, FLAG_IS_MAGICAL)); 01126 01127 head->stats.sp -= SP_level_spellpoint_cost(head, sp_typ, -1); 01128 /* Add default cast time from spell force to monster */ 01129 head->last_grace += spell_item->last_grace; 01130 01131 /* If we're casting this spell on another object, we need to adjust some 01132 * parameters accordingly... */ 01133 if (target) 01134 { 01135 return cast_spell(target, part, dir, sp_typ, ability, CAST_NPC, NULL); 01136 } 01137 01138 /* Otherwise a normal cast. */ 01139 return cast_spell(part, part, dir, sp_typ, ability, CAST_NORMAL, NULL); 01140 } 01141 01148 static int monster_use_bow(object *head, object *part, int dir) 01149 { 01150 object *bow, *arrow; 01151 int tag; 01152 01153 for (bow = head->inv; bow != NULL; bow = bow->below) 01154 { 01155 if (bow->type == BOW && QUERY_FLAG(bow, FLAG_APPLIED)) 01156 { 01157 break; 01158 } 01159 } 01160 01161 if (bow == NULL) 01162 { 01163 LOG(llevBug, "Monster %s (%d) HAS_READY_BOW() without bow.\n", query_name(head, NULL), head->count); 01164 CLEAR_FLAG(head, FLAG_READY_BOW); 01165 return 0; 01166 } 01167 01168 if ((arrow = find_arrow(head, bow->race)) == NULL) 01169 { 01170 /* Out of arrows */ 01171 manual_apply(head, bow, 0); 01172 CLEAR_FLAG(head, FLAG_READY_BOW); 01173 return 0; 01174 } 01175 01176 /* An infinite arrow, dupe it. */ 01177 if (QUERY_FLAG(arrow, FLAG_SYS_OBJECT)) 01178 { 01179 object *new_arrow = get_object(); 01180 copy_object(arrow, new_arrow, 0); 01181 CLEAR_FLAG(new_arrow, FLAG_SYS_OBJECT); 01182 new_arrow->nrof = 0; 01183 01184 /* Setup the self destruction */ 01185 new_arrow->stats.food = 20; 01186 arrow = new_arrow; 01187 } 01188 else 01189 { 01190 arrow = get_split_ob(arrow, 1, NULL, 0); 01191 } 01192 01193 set_owner(arrow, head); 01194 arrow->direction = dir; 01195 arrow->x = part->x, arrow->y = part->y; 01196 arrow->speed = 1; 01197 update_ob_speed(arrow); 01198 arrow->speed_left = 0; 01199 SET_ANIMATION(arrow, (NUM_ANIMATIONS(arrow) / NUM_FACINGS(arrow)) * dir); 01200 arrow->level = head->level; 01201 /* Save original wc and dam */ 01202 arrow->last_heal = arrow->stats.wc; 01203 arrow->stats.hp = arrow->stats.dam; 01204 arrow->stats.dam += bow->stats.dam + bow->magic + arrow->magic; 01205 arrow->stats.dam = FABS((int) ((float) (arrow->stats.dam * LEVEL_DAMAGE(head->level)))); 01206 arrow->stats.wc = 10 + (bow->magic + bow->stats.wc + arrow->magic + arrow->stats.wc + head->level); 01207 arrow->stats.wc_range = bow->stats.wc_range; 01208 arrow->map = head->map; 01209 /* We use fixed value for mobs */ 01210 arrow->last_sp = 12; 01211 SET_FLAG(arrow, FLAG_FLYING); 01212 SET_FLAG(arrow, FLAG_IS_MISSILE); 01213 SET_FLAG(arrow, FLAG_FLY_ON); 01214 SET_FLAG(arrow, FLAG_WALK_ON); 01215 tag = arrow->count; 01216 arrow->stats.grace = arrow->last_sp; 01217 arrow->stats.maxgrace = 60 + (RANDOM() % 12); 01218 play_sound_map(arrow->map, CMD_SOUND_EFFECT, "throw.ogg", arrow->x, arrow->y, 0, 0); 01219 01220 if (!insert_ob_in_map(arrow, head->map, head, 0)) 01221 { 01222 return 1; 01223 } 01224 01225 if (!was_destroyed(arrow, tag)) 01226 { 01227 move_arrow(arrow); 01228 } 01229 01230 return 1; 01231 } 01232 01239 static int dist_att(int dir, object *part, rv_vector *rv) 01240 { 01241 if (can_hit(part, rv)) 01242 { 01243 return dir; 01244 } 01245 01246 if (rv->distance < 10) 01247 { 01248 return absdir(dir + 4); 01249 } 01250 else if (rv->distance > 18) 01251 { 01252 return dir; 01253 } 01254 01255 return 0; 01256 } 01257 01265 static int run_att(int dir, object *ob, object *part, rv_vector *rv) 01266 { 01267 if ((can_hit(part, rv) && ob->move_status < 20) || ob->move_status < 20) 01268 { 01269 ob->move_status++; 01270 return dir; 01271 } 01272 else if (ob->move_status > 20) 01273 { 01274 ob->move_status = 0; 01275 } 01276 01277 return absdir(dir + 4); 01278 } 01279 01285 static int hitrun_att(int dir, object *ob) 01286 { 01287 if (ob->move_status++ < 25) 01288 { 01289 return dir; 01290 } 01291 else if (ob->move_status < 50) 01292 { 01293 return absdir(dir + 4); 01294 } 01295 else 01296 { 01297 ob->move_status = 0; 01298 } 01299 01300 return absdir(dir + 4); 01301 } 01302 01310 static int wait_att(int dir, object *ob, object *part, rv_vector *rv) 01311 { 01312 if (ob->move_status || can_hit(part, rv)) 01313 { 01314 ob->move_status++; 01315 } 01316 01317 if (ob->move_status == 0) 01318 { 01319 return 0; 01320 } 01321 else if (ob->move_status < 10) 01322 { 01323 return dir; 01324 } 01325 else if (ob->move_status < 15) 01326 { 01327 return absdir(dir + 4); 01328 } 01329 01330 ob->move_status = 0; 01331 return 0; 01332 } 01333 01341 static int disthit_att(int dir, object *ob, object *part, rv_vector *rv) 01342 { 01343 if (ob->stats.maxhp && (ob->stats.hp * 100) / ob->stats.maxhp < ob->run_away) 01344 { 01345 return absdir(dir + 4); 01346 } 01347 01348 return dist_att(dir, part, rv); 01349 } 01350 01356 static int wait_att2(int dir, rv_vector *rv) 01357 { 01358 if (rv->distance < 9) 01359 { 01360 return absdir(dir + 4); 01361 } 01362 01363 return 0; 01364 } 01365 01369 static void circ1_move(object *ob) 01370 { 01371 static const int circle[12] = {3, 3, 4, 5, 5, 6, 7, 7, 8, 1, 1, 2}; 01372 01373 if (++ob->move_status > 11) 01374 { 01375 ob->move_status = 0; 01376 } 01377 01378 if (!(move_object(ob, circle[ob->move_status]))) 01379 { 01380 move_object(ob, rndm(1, 8)); 01381 } 01382 } 01383 01387 static void circ2_move(object *ob) 01388 { 01389 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}; 01390 01391 if (++ob->move_status > 19) 01392 { 01393 ob->move_status = 0; 01394 } 01395 01396 if (!(move_object(ob, circle[ob->move_status]))) 01397 { 01398 move_object(ob, rndm(1, 8)); 01399 } 01400 } 01401 01405 static void pace_movev(object *ob) 01406 { 01407 if (ob->move_status++ > 6) 01408 { 01409 ob->move_status = 0; 01410 } 01411 01412 if (ob->move_status < 4) 01413 { 01414 move_object(ob, 5); 01415 } 01416 else 01417 { 01418 move_object(ob, 1); 01419 } 01420 } 01421 01425 static void pace_moveh(object *ob) 01426 { 01427 if (ob->move_status++ > 6) 01428 { 01429 ob->move_status = 0; 01430 } 01431 01432 if (ob->move_status < 4) 01433 { 01434 move_object(ob, 3); 01435 } 01436 else 01437 { 01438 move_object(ob, 7); 01439 } 01440 } 01441 01445 static void pace2_movev(object *ob) 01446 { 01447 if (ob->move_status++ > 16) 01448 { 01449 ob->move_status = 0; 01450 } 01451 01452 if (ob->move_status < 6) 01453 { 01454 move_object(ob, 5); 01455 } 01456 else if (ob->move_status < 8) 01457 { 01458 return; 01459 } 01460 else if (ob->move_status < 13) 01461 { 01462 move_object(ob, 1); 01463 } 01464 } 01465 01469 static void pace2_moveh(object *ob) 01470 { 01471 if (ob->move_status++ > 16) 01472 { 01473 ob->move_status = 0; 01474 } 01475 01476 if (ob->move_status < 6) 01477 { 01478 move_object(ob, 3); 01479 } 01480 else if (ob->move_status < 8) 01481 { 01482 return; 01483 } 01484 else if (ob->move_status < 13) 01485 { 01486 move_object(ob, 7); 01487 } 01488 } 01489 01493 static void rand_move(object *ob) 01494 { 01495 int i; 01496 01497 if (ob->move_status < 1 || ob->move_status > 8 || !(move_object(ob, ob->move_status || rndm_chance(9)))) 01498 { 01499 for (i = 0; i < 5; i++) 01500 { 01501 if (move_object(ob, ob->move_status = rndm(1, 8))) 01502 { 01503 return; 01504 } 01505 } 01506 } 01507 } 01508 01513 void communicate(object *op, char *txt) 01514 { 01515 object *npc; 01516 mapstruct *m; 01517 int i, xt, yt; 01518 char buf[HUGE_BUF]; 01519 01520 if (!txt) 01521 { 01522 return; 01523 } 01524 01525 /* Makes it possible to do something like this in Python: 01526 * monster.Communicate("/dance") */ 01527 if (*txt == '/' && op->type != PLAYER) 01528 { 01529 CommArray_s *csp; 01530 char *cp; 01531 01532 /* Sanity check: all commands must begin with a slash, and there must be 01533 * something else after the slash. */ 01534 if (txt[0] != '/' || !txt[1]) 01535 { 01536 LOG(llevBug, "communicate(): %s attempted an illegal command '%s'.\n", op->name, txt); 01537 return; 01538 } 01539 01540 /* Jump over the slash. */ 01541 txt++; 01542 01543 /* Remove the command from the parameters. */ 01544 cp = strchr(txt, ' '); 01545 01546 if (cp) 01547 { 01548 *(cp++) = '\0'; 01549 cp = cleanup_string(cp); 01550 01551 if (cp && *cp == '\0') 01552 { 01553 cp = NULL; 01554 } 01555 } 01556 01557 csp = find_command_element(txt, CommunicationCommands, CommunicationCommandSize); 01558 01559 if (csp) 01560 { 01561 csp->func(op, cp); 01562 return; 01563 } 01564 01565 return; 01566 } 01567 01568 snprintf(buf, sizeof(buf), "%s says: %s", query_name(op, NULL), txt); 01569 01570 if (op->type == PLAYER) 01571 { 01572 new_info_map(NDI_PLAYER | NDI_SAY, COLOR_WHITE, op->map, op->x, op->y, MAP_INFO_NORMAL, buf); 01573 } 01574 else 01575 { 01576 new_info_map(0, COLOR_WHITE, op->map, op->x, op->y, MAP_INFO_NORMAL, buf); 01577 } 01578 01579 for (i = 0; i <= SIZEOFFREE2; i++) 01580 { 01581 xt = op->x + freearr_x[i]; 01582 yt = op->y + freearr_y[i]; 01583 01584 if (!(m = get_map_from_coord(op->map, &xt, &yt))) 01585 { 01586 continue; 01587 } 01588 01589 /* Check to see if we have magic ear or monster here. */ 01590 if (!(GET_MAP_FLAGS(m, xt, yt) & (P_MAGIC_EAR | P_IS_ALIVE))) 01591 { 01592 continue; 01593 } 01594 01595 for (npc = get_map_ob(m, xt, yt); npc; npc = npc->above) 01596 { 01597 /* Avoid talking to self. */ 01598 if (op != npc) 01599 { 01600 /* The ear. */ 01601 if (npc->type == MAGIC_EAR) 01602 { 01603 talk_to_wall(op, npc, txt); 01604 } 01605 else if (QUERY_FLAG(npc, FLAG_ALIVE)) 01606 { 01607 talk_to_npc(op, npc, txt); 01608 } 01609 } 01610 } 01611 } 01612 } 01613 01621 static char *find_matching_message(const char *msg, const char *match) 01622 { 01623 const char *cp = msg, *cp1, *cp2; 01624 char regex[MAX_BUF], *cp3; 01625 int gotmatch = 0; 01626 01627 while (1) 01628 { 01629 if (strncmp(cp, "@match ", 7)) 01630 { 01631 LOG(llevDebug, "find_matching_message(): Invalid message: %s\n", msg); 01632 return NULL; 01633 } 01634 else 01635 { 01636 /* Find the end of the line, and copy the regex portion into it */ 01637 cp2 = strchr(cp + 7, '\n'); 01638 01639 if (!cp2) 01640 { 01641 LOG(llevDebug, "find_matching_message(): Found empty match response: %s\n", msg); 01642 return NULL; 01643 } 01644 01645 strncpy(regex, cp + 7, (cp2 - cp - 7)); 01646 regex[cp2 - cp - 7] = '\0'; 01647 01648 /* Find the next match command */ 01649 cp1 = strstr(cp + 6, "\n@match"); 01650 01651 /* Got a match - handle * as special case. */ 01652 if (regex[0] == '*') 01653 { 01654 gotmatch = 1; 01655 } 01656 else 01657 { 01658 char *pipe_sep, *pnext = NULL; 01659 01660 /* Need to parse all the | separators. Our re_cmp isn't 01661 * really a fully blown regex parser. */ 01662 for (pipe_sep = regex; pipe_sep; pipe_sep = pnext) 01663 { 01664 pnext = strchr(pipe_sep, '|'); 01665 01666 if (pnext) 01667 { 01668 *pnext = '\0'; 01669 pnext ++; 01670 } 01671 01672 if (re_cmp(match, pipe_sep)) 01673 { 01674 gotmatch = 1; 01675 break; 01676 } 01677 } 01678 } 01679 01680 if (gotmatch) 01681 { 01682 if (cp1) 01683 { 01684 cp3 = malloc(cp1 - cp2 + 1); 01685 strncpy(cp3, cp2 + 1, cp1 - cp2); 01686 cp3[cp1 - cp2 - 1] = '\0'; 01687 } 01688 /* If no next match, just want the rest of the string */ 01689 else 01690 { 01691 cp3 = strdup_local(cp2 + 1); 01692 } 01693 01694 return cp3; 01695 } 01696 01697 gotmatch = 0; 01698 01699 if (cp1) 01700 { 01701 cp = cp1 + 1; 01702 } 01703 else 01704 { 01705 return NULL; 01706 } 01707 } 01708 } 01709 } 01710 01720 int talk_to_npc(object *op, object *npc, char *txt) 01721 { 01722 object *cobj; 01723 char *cp; 01724 01725 if (HAS_EVENT(npc, EVENT_SAY)) 01726 { 01727 /* Trigger the SAY event */ 01728 trigger_event(EVENT_SAY, op, npc, NULL, txt, 0, 0, 0, SCRIPT_FIX_ACTIVATOR); 01729 return 0; 01730 } 01731 01732 /* Here we let the objects inside inventories hear and answer, too. 01733 * This allows the existence of "intelligent" weapons you can discuss 01734 * with. */ 01735 for (cobj = npc->inv; cobj; cobj = cobj->below) 01736 { 01737 if (HAS_EVENT(cobj, EVENT_SAY)) 01738 { 01739 /* Trigger the SAY event */ 01740 trigger_event(EVENT_SAY, op, cobj, npc, txt, 0, 0, 0, SCRIPT_FIX_ACTIVATOR); 01741 return 0; 01742 } 01743 } 01744 01745 if (!npc->msg || *npc->msg != '@') 01746 { 01747 return 0; 01748 } 01749 01750 cp = find_matching_message(npc->msg, txt); 01751 01752 if (cp) 01753 { 01754 char buf[MAX_BUF]; 01755 01756 if (op->type == PLAYER) 01757 { 01758 new_draw_info_format(0, COLOR_NAVY, op, "\n%s says:\n%s", query_name(npc, NULL), cp); 01759 snprintf(buf, sizeof(buf), "%s talks to %s.", query_name(npc, NULL), query_name(op, NULL)); 01760 new_info_map_except(0, COLOR_WHITE, op->map, op->x, op->y, MAP_INFO_NORMAL, op, op, buf); 01761 } 01762 else 01763 { 01764 snprintf(buf, sizeof(buf), "\n%s says: %s", query_name(npc, NULL), cp); 01765 new_info_map_except(0, COLOR_WHITE, op->map, op->x, op->y, MAP_INFO_NORMAL, op, op, buf); 01766 } 01767 01768 free(cp); 01769 01770 return 1; 01771 } 01772 01773 return 0; 01774 } 01775 01782 static int talk_to_wall(object *op, object *npc, char *txt) 01783 { 01784 char *cp; 01785 01786 if (!npc->msg || *npc->msg != '@') 01787 { 01788 return 0; 01789 } 01790 01791 cp = find_matching_message(npc->msg, txt); 01792 01793 if (!cp) 01794 { 01795 return 0; 01796 } 01797 01798 if (QUERY_FLAG(npc, FLAG_XRAYS)) 01799 { 01800 new_info_map(0, COLOR_NAVY, npc->map, npc->x, npc->y, MAP_INFO_NORMAL, cp); 01801 } 01802 else 01803 { 01804 new_draw_info(0, COLOR_NAVY, op, cp); 01805 } 01806 01807 use_trigger(npc); 01808 free(cp); 01809 01810 return 1; 01811 } 01812 01820 int faction_is_friend_of(object *mon, object *pl) 01821 { 01822 shstr *faction, *faction_rep; 01823 sint64 pl_rep, rep; 01824 01825 faction = object_get_value(mon, "faction"); 01826 01827 if (!faction) 01828 { 01829 return -1; 01830 } 01831 01832 faction_rep = object_get_value(mon, "faction_rep"); 01833 01834 if (!faction_rep) 01835 { 01836 return -1; 01837 } 01838 01839 rep = atoll(faction_rep); 01840 pl_rep = player_faction_reputation(CONTR(pl), faction); 01841 01842 if (rep < 0) 01843 { 01844 return pl_rep <= rep ? 0 : -1; 01845 } 01846 else if (rep > 0) 01847 { 01848 return pl_rep >= rep ? 1 : -1; 01849 } 01850 01851 return -1; 01852 } 01853 01859 int is_friend_of(object *op, object *obj) 01860 { 01861 uint8 friend = 0; 01862 sint8 faction_friend = -1; 01863 01864 /* We are obviously friends with ourselves. */ 01865 if (op == obj) 01866 { 01867 return 1; 01868 } 01869 01870 /* TODO: Add a few other odd types here, such as god and golem */ 01871 if (!obj->type == PLAYER || !obj->type == MONSTER || !op->type == PLAYER || !op->type == MONSTER) 01872 { 01873 return 0; 01874 } 01875 01876 /* Berserk */ 01877 if (QUERY_FLAG(op, FLAG_BERSERK)) 01878 { 01879 return 0; 01880 } 01881 01882 /* If on PVP area, they won't be friendly */ 01883 if (pvp_area(op, obj)) 01884 { 01885 return 0; 01886 } 01887 01888 if ((op->type == MONSTER && op->enemy && OBJECT_VALID(op->enemy, op->enemy_count) && obj == op->enemy) || (obj->type == MONSTER && obj->enemy && OBJECT_VALID(obj->enemy, obj->enemy_count) && op == obj->enemy)) 01889 { 01890 return 0; 01891 } 01892 01893 if (QUERY_FLAG(op, FLAG_FRIENDLY) || op->type == PLAYER) 01894 { 01895 if (!QUERY_FLAG(obj, FLAG_MONSTER) || QUERY_FLAG(obj, FLAG_FRIENDLY) || obj->type == PLAYER) 01896 { 01897 friend = 1; 01898 } 01899 } 01900 else 01901 { 01902 if (!QUERY_FLAG(obj, FLAG_FRIENDLY) && obj->type != PLAYER) 01903 { 01904 friend = 1; 01905 } 01906 } 01907 01908 /* Check factions. */ 01909 if (op->type == PLAYER) 01910 { 01911 faction_friend = faction_is_friend_of(obj, op); 01912 } 01913 else if (obj->type == PLAYER) 01914 { 01915 faction_friend = faction_is_friend_of(op, obj); 01916 } 01917 01918 if (faction_friend != -1) 01919 { 01920 friend = faction_friend; 01921 } 01922 01923 return friend; 01924 } 01925 01931 int check_good_weapon(object *who, object *item) 01932 { 01933 object *other_weap; 01934 int val = 0, i; 01935 01936 /* Cursed or damned; never better. */ 01937 if (QUERY_FLAG(item, FLAG_CURSED) || QUERY_FLAG(item, FLAG_DAMNED)) 01938 { 01939 return 0; 01940 } 01941 01942 for (other_weap = who->inv; other_weap; other_weap = other_weap->below) 01943 { 01944 if (other_weap->type == item->type && QUERY_FLAG(other_weap, FLAG_APPLIED)) 01945 { 01946 break; 01947 } 01948 } 01949 01950 /* No other weapons */ 01951 if (other_weap == NULL) 01952 { 01953 return 1; 01954 } 01955 01956 val = item->stats.dam - other_weap->stats.dam; 01957 val += (item->magic - other_weap->magic) * 3; 01958 01959 /* Monsters don't really get benefits from things like regen rates 01960 * from items. But the bonus for their stats are very important. */ 01961 for (i = 0; i < NUM_STATS; i++) 01962 { 01963 val += (get_attr_value(&item->stats, i) - get_attr_value(&other_weap->stats, i)) * 2; 01964 } 01965 01966 if (val > 0) 01967 { 01968 CLEAR_FLAG(other_weap, FLAG_APPLIED); 01969 return 1; 01970 } 01971 01972 return 0; 01973 } 01974 01980 int check_good_armour(object *who, object *item) 01981 { 01982 object *other_armour; 01983 int val = 0, i; 01984 01985 /* Cursed or damned; never better. */ 01986 if (QUERY_FLAG(item, FLAG_CURSED) || QUERY_FLAG(item, FLAG_DAMNED)) 01987 { 01988 return 0; 01989 } 01990 01991 for (other_armour = who->inv; other_armour; other_armour = other_armour->below) 01992 { 01993 if (other_armour->type == item->type && QUERY_FLAG(other_armour, FLAG_APPLIED)) 01994 { 01995 break; 01996 } 01997 } 01998 01999 /* No other armour, use the new */ 02000 if (other_armour == NULL) 02001 { 02002 return 1; 02003 } 02004 02005 /* See which is better */ 02006 val = item->stats.ac - other_armour->stats.ac; 02007 val += (item->magic - other_armour->magic) * 3; 02008 02009 for (i = 0; i < NROFATTACKS; i++) 02010 { 02011 if (item->protection[i] > other_armour->protection[i]) 02012 { 02013 val++; 02014 } 02015 else if (item->protection[i] < other_armour->protection[i]) 02016 { 02017 val--; 02018 } 02019 } 02020 02021 if (val > 0) 02022 { 02023 CLEAR_FLAG(other_armour, FLAG_APPLIED); 02024 return 1; 02025 } 02026 02027 return 0; 02028 }
1.7.4