00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
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
00068
00083 object *check_enemy(object *npc, rv_vector *rv) {
00084
00085
00086
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
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105 if (npc->enemy) {
00106
00107
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
00127
00128
00129
00130
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
00172
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 }
00197 }
00198 return NULL;
00199 }
00200
00216 static object *find_enemy(object *npc, rv_vector *rv) {
00217 object *attacker, *tmp = NULL;
00218
00219 attacker = npc->attacked_by;
00220 npc->attacked_by = NULL;
00221
00222
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
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
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
00249 if ((tmp = check_enemy(npc, rv)) == NULL) {
00250 if (attacker) {
00251
00252 if (attacker->count == npc->attacked_by_count) {
00253
00254
00255 if (QUERY_FLAG(npc, FLAG_NEUTRAL)
00256 || QUERY_FLAG(attacker, FLAG_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);
00260 else if (on_same_map(npc, attacker)) {
00261 CLEAR_FLAG(npc, FLAG_SLEEP);
00262 npc->enemy = attacker;
00263 get_rangevector(npc, attacker, rv, 0);
00264 return attacker;
00265 }
00266 }
00267 }
00268
00269
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
00302 if (!enemy)
00303 return 0;
00304
00305
00306 if (QUERY_FLAG(op, FLAG_BLIND))
00307 radius = MIN_MON_RADIUS;
00308
00309
00310
00311
00312
00313
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
00327
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
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
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
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
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
00440 if (op->stats.Con > 0 && op->stats.hp < op->stats.maxhp) {
00441
00442
00443
00444
00445
00446
00447
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);
00452 op->last_heal %= 32;
00453
00454
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
00464 if (op->stats.Pow > 0 && op->stats.sp < op->stats.maxsp) {
00465
00466
00467
00468
00469
00470
00471
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);
00476 op->last_sp %= 128;
00477 }
00478
00479
00480
00481
00482 if (QUERY_FLAG(op, FLAG_SCARED) && !(RANDOM()%20)) {
00483 CLEAR_FLAG(op, FLAG_SCARED);
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
00505
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 }
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
00572
00573
00574 if (!op->map)
00575 return 0;
00576
00577
00578 if (oph->head)
00579 oph = oph->head;
00580
00581 if (QUERY_FLAG(op, FLAG_NO_ATTACK))
00582 enemy = op->enemy = NULL;
00583 else if ((enemy = find_enemy(op, &rv))) {
00584
00585 enemy->attacked_by = op;
00586 enemy->attacked_by_count = op->count;
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
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);
00605
00606 monster_do_living(op);
00607
00608
00609 if (!enemy) {
00610 return monster_move_no_enemy(op);
00611 }
00612
00613
00614 if ((op->attack_movement&HI4) == PETMOVE && (owner = get_owner(op)) != NULL && !on_same_map(op, owner)) {
00615 follow_owner(op, owner);
00616
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
00626
00627
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
00637
00638
00639
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
00650
00651
00652
00653 if (!QUERY_FLAG(op, FLAG_SCARED)) {
00654 rv_vector rv1;
00655
00656
00657 for (part = op; part != NULL; part = part->more) {
00658 get_rangevector(part, enemy, &rv1, 0x1);
00659 dir = rv1.direction;
00660
00661
00662
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 }
00692 }
00693
00694
00695
00696
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:
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))
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
00755 int maxdiff = (QUERY_FLAG(op, FLAG_ONLY_ATTACK) || RANDOM()&1) ? 1 : 2;
00756 for (diff = 1; diff <= maxdiff; diff++) {
00757
00758 int m = 1-(RANDOM()&2);
00759 if (move_object(op, absdir(dir+diff*m))
00760 || move_object(op, absdir(dir-diff*m)))
00761 return 0;
00762 }
00763 }
00764 }
00765
00766
00767
00768
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
00777
00778
00779
00780
00781
00782
00783
00784
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
00797
00798
00799
00800
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 }
00809
00810 if (QUERY_FLAG(part, FLAG_FREED))
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
00846
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
00925
00926
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
00963
00964
00965
00966
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;
00975 }
00976 }
00977
00978 if (QUERY_FLAG(head, FLAG_CONFUSED))
00979 dir = absdir(dir+RANDOM()%3+RANDOM()%3-2);
00980
00981
00982
00983
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);
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
01005 if (spell_item->range <= 1 || spell_item->stats.dam < 0)
01006 dir = 0;
01007
01008
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
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
01046
01047
01048
01049
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;
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
01069 if (!scroll) {
01070 CLEAR_FLAG(head, FLAG_READY_SCROLL);
01071 return 0;
01072 }
01073
01074
01075 if (scroll->inv->range == 0)
01076 dir = 0;
01077
01078
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;
01125 }
01126 if (QUERY_FLAG(head, FLAG_CONFUSED))
01127 dir = absdir(dir+RANDOM()%3+RANDOM()%3-2);
01128
01129
01130
01131
01132
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
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;
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
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
01190 return 1;
01191 } else if (wand->type == ROD || wand->type == HORN) {
01192
01193 at_least_one = 1;
01194 if (wand->stats.hp < MAX(wand->inv->stats.sp, wand->inv->stats.grace))
01195 continue;
01196
01197
01198
01199
01200
01201 drain_rod_charge(wand);
01202 cast_spell(head, wand, dir, wand->inv, NULL);
01203
01204
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
01242 return 0;
01243 if (rv.distance_x != 0 && rv.distance_y != 0 && abs(rv.distance_x) != abs(rv.distance_y))
01244
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
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
01269 return 0;
01270 }
01271
01272
01273
01274 if (QUERY_FLAG(head, FLAG_CONFUSED))
01275 dir = absdir(dir+RANDOM()%3+RANDOM()%3-2);
01276
01277
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)
01302 return 1;
01303
01304
01305
01306
01307
01308
01309
01310 val = item->stats.dam-other_weap->stats.dam;
01311 val += (item->magic-other_weap->magic)*3;
01312
01313
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)
01345 return 1;
01346
01347
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
01353
01354
01355
01356
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
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
01406
01407
01408 if (next && was_destroyed(next, next_tag))
01409 return;
01410 }
01411 }
01412
01413
01414
01415
01416
01417
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)
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
01478
01479
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
01496
01497
01498
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
01524
01525
01526
01527
01528
01529
01530
01531
01532
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
01545 if (QUERY_FLAG(item, FLAG_APPLIED))
01546 return;
01547
01548
01549
01550
01551
01552 if (QUERY_FLAG(mon, FLAG_USE_BOW) && item->type == ARROW) {
01553
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;
01561 }
01562 }
01563
01564 if (item->type == TREASURE && mon->will_apply&WILL_APPLY_TREASURE)
01565 flag = 1;
01566
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
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
01581 else if (item->type == RING)
01582 flag = 1;
01583 else if (item->type == WAND || item->type == ROD || item->type == HORN) {
01584
01585
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
01594
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
01602
01603
01604
01605 SET_FLAG(mon, FLAG_READY_SKILL);
01606 return;
01607 }
01608
01609
01610
01611
01612
01613
01614 if (!flag)
01615 return;
01616
01617
01618
01619
01620
01621 if (can_apply_object(mon, item)&CAN_APPLY_NOT_MASK)
01622 return;
01623
01624
01625
01626
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
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
01701
01702
01703
01704
01705
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
01844
01845
01846
01847
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
01868
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
01898
01899
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
01956
01957
01958 if (execute_event(npc, EVENT_SAY, op, NULL, txt, SCRIPT_FIX_ALL) != 0)
01959 return 0;
01960
01961
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
01972
01973
01974
01975
01976
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
01987
01988
01989
01990 for (tmp = op->inv; tmp; tmp = tmp->below) {
01991
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
02012
02013
02014
02015
02016
02017
02018
02019
02020 int can_detect_enemy(object *op, object *enemy, rv_vector *rv) {
02021 int radius = MIN_MON_RADIUS, hide_discovery;
02022
02023
02024 if (!op || !enemy || !op->map || !enemy->map)
02025 return 0;
02026
02027
02028 if (!on_same_map(op, enemy))
02029 return 0;
02030
02031 get_rangevector(op, enemy, rv, 0);
02032
02033
02034 if ((op->type != PLAYER) && QUERY_FLAG(enemy, FLAG_WIZ))
02035 return 0;
02036
02037
02038 if (can_see_enemy(op, enemy))
02039 return 1;
02040
02041
02042
02043
02044 if (op->type == PLAYER)
02045 return 0;
02046
02047
02048
02049
02050 if (enemy->invisible && (!enemy->contr || (!enemy->contr->tmp_invis && !enemy->contr->hidden)))
02051 return 0;
02052
02053
02054 hide_discovery = op->stats.Int/5;
02055
02056
02057 if (!enemy->hide)
02058 radius = MAX((op->stats.Wis/5)+1, MIN_MON_RADIUS);
02059 else {
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
02072 bonus -= enemy->level;
02073
02074 radius += bonus/5;
02075 hide_discovery += bonus*5;
02076 }
02077
02078
02079
02080 if (QUERY_FLAG(enemy, FLAG_STEALTH))
02081 radius = radius/2, hide_discovery = hide_discovery/3;
02082
02083
02084 if (op->map->darkness > 0 && !stand_in_light(enemy)) {
02085
02086
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
02094
02095
02096 if (radius < MIN_MON_RADIUS && op->map->darkness < 5 && rv->distance <= 1)
02097 radius = MIN_MON_RADIUS;
02098 }
02099
02100
02101
02102
02103
02104
02105
02106
02107
02108 if (radius > 13)
02109 radius = 13;
02110
02111
02112 if ((int)rv->distance <= radius) {
02113
02114 if (!enemy->invisible)
02115 return 1;
02116
02117
02118 if (enemy->hide && (rv->distance <= 1) && (RANDOM()%100 <= hide_discovery)) {
02119 make_visible(enemy);
02120
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;
02127 } else if (enemy->invisible) {
02128
02129
02130
02131
02132
02133
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 }
02149
02150
02151 return 0;
02152 }
02153
02154
02155
02156
02157
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
02172
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
02195
02196
02197
02198
02199 int can_see_enemy(object *op, object *enemy) {
02200 object *looker = op->head ? op->head : op;
02201
02202
02203 if (!looker || !enemy || !QUERY_FLAG(looker, FLAG_ALIVE))
02204 return 0;
02205
02206
02207
02208
02209
02210 if (QUERY_FLAG(looker, FLAG_BLIND) && !QUERY_FLAG(looker, FLAG_XRAYS))
02211 return 0;
02212
02213
02214 if (enemy->invisible) {
02215
02216
02217
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
02230
02231
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)
02238 if (player_can_view(looker, enemy))
02239 return 1;
02240
02241
02242
02243
02244
02245
02246
02247
02248
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 }