1 /*
2 * PROPRIETARY INFORMATION. This software is proprietary to POWDER
3 * Development, and is not to be reproduced, transmitted, or disclosed
4 * in any way without written permission.
5 *
6 * Produced by: Jeff Lait
7 *
8 * POWDER Development
9 *
10 * NAME: ai.cpp ( POWDER Library, C++ )
11 *
12 * COMMENTS:
13 * ai.cpp contains all the AI for MOBs in POWDER.
14 *
15 * The hook for all these routines is doAI(), which is called when
16 * MOBs get an action phase. This will call a series of utility
17 * functions with the "ai" prefix to perform what would be the
18 * best action for this MOB at this time.
19 *
20 * Actually performing the action is done by invoking the correct
21 * "action" prefixed method. These ai methods thus *do* nothing,
22 * they merely determine what should be done, and then call action*
23 * to do it.
24 */
25
26 #include "mygba.h"
27
28 #include <stdio.h>
29 #include <ctype.h>
30 #include "assert.h"
31 #include "creature.h"
32 #include "glbdef.h"
33 #include "rand.h"
34 #include "map.h"
35 #include "item.h"
36 #include "itemstack.h"
37 #include "intrinsic.h"
38 #include "control.h"
39 #include "sramstream.h"
40
41 struct MOB_MEMORY
42 {
43 // Who remembers this fact
44 MOBREF id;
45 // How many turns it will be remembered for.
46 u8 turns;
47 // What element this consists of.
48 u8 element;
49 };
50
51 PTRSTACK<MOB_MEMORY *> glbMobMemory;
52
53 void
ai_init()54 ai_init()
55 {
56 ai_reset();
57 }
58
59 void
ai_reset()60 ai_reset()
61 {
62 int idx;
63
64 for (idx = 0; idx < glbMobMemory.entries(); idx++)
65 {
66 delete glbMobMemory(idx);
67 }
68 glbMobMemory.clear();
69 }
70
71 void
ai_save(SRAMSTREAM & os)72 ai_save(SRAMSTREAM &os)
73 {
74 int idx;
75
76 // FIrst, collapse list so no zeros
77 glbMobMemory.collapse();
78
79 os.write(glbMobMemory.entries(), 32);
80
81 for (idx = 0; idx < glbMobMemory.entries(); idx++)
82 {
83 glbMobMemory(idx)->id.save(os);
84 os.write(glbMobMemory(idx)->turns, 8);
85 os.write(glbMobMemory(idx)->element, 8);
86 }
87 }
88
89 void
ai_load(SRAMSTREAM & is)90 ai_load(SRAMSTREAM &is)
91 {
92 // Reset global list
93 ai_reset();
94
95 int val, i, count;
96
97 is.uread(count, 32);
98 for (i = 0; i < count; i++)
99 {
100 MOB_MEMORY *mem;
101 mem = new MOB_MEMORY;
102
103 mem->id.load(is);
104 is.uread(val, 8);
105 mem->turns = val;
106 is.uread(val, 8);
107 mem->element = val;
108 }
109 }
110
111 static int
sign(int d)112 sign(int d)
113 {
114 if (d == 0)
115 return 0;
116 else if (d < 0)
117 return -1;
118 else
119 return 1;
120 }
121
122 bool
mob_shouldCast(int rarity=0)123 mob_shouldCast(int rarity = 0)
124 {
125 return !rand_choice(3+rarity);
126 }
127
128 //
129 // doAI: This runs the AI script for any MOB. It should handle all
130 // MOB types.
131 //
132 void
doAI()133 MOB::doAI()
134 {
135 int diffx, diffy;
136 int dx, dy;
137 int distx, disty;
138 int range;
139 bool skipmovecheck;
140 int ai = glb_mobdefs[myDefinition].ai;
141 bool targetinfov = false;
142
143 MOB *killtarget;
144 MOB *aitarget;
145 MOB *avatar = MOB::getAvatar();
146 MOB *owner = getMaster();
147
148 // Determine if our FSM is currently on the just-born
149 // delay state. If so, revert to standard command.
150 if (myAIFSM == AI_FSM_JUSTBORN)
151 {
152 myAIFSM = AI_FSM_NORMAL;
153 return;
154 }
155
156 if (hasIntrinsic(INTRINSIC_BRAINDEAD))
157 {
158 // The AI has been deactivated, likely due to having
159 // possessed another creature.
160 return;
161 }
162
163 // Specific types of AI have specific types of actions...
164 if (getAI() == AI_MOUSE)
165 {
166 if (aiDoMouse())
167 return;
168 }
169
170 // These are the highest priority moves. Stuff like healing, etc.
171 if (aiDoUrgentMoves())
172 return;
173
174 // Check to see if we can see our enemy...
175 killtarget = 0;
176
177 // Process our AI state.
178 switch (myAIFSM)
179 {
180 case AI_FSM_JUSTBORN:
181 myAIFSM = AI_FSM_NORMAL;
182 break;
183 case AI_FSM_NORMAL:
184 if (owner)
185 {
186 myAIFSM = AI_FSM_GUARD;
187 }
188 break;
189
190 case AI_FSM_ATTACK:
191 // If our target is dead, return to guarding.
192 aitarget = getAITarget();
193 if (!aitarget || aitarget->hasIntrinsic(INTRINSIC_DEAD))
194 {
195 // Return to guarding.
196 myAIFSM = AI_FSM_GUARD;
197 }
198 break;
199
200 case AI_FSM_GUARD:
201 if (!owner)
202 myAIFSM = AI_FSM_NORMAL;
203 else
204 {
205 // Look at our owner's ai target. We do NOT target ourselves,
206 // even if our owner did.
207 // We also refuse to target any pets, or fellow pets.
208 // (They should have never set their ai target anyways,
209 // but life is full of surprises.)
210 aitarget = owner->getAITarget();
211
212 if (aitarget && aitarget != this &&
213 !aitarget->hasCommonMaster(this))
214 {
215 // Convert to kill!
216 myAIFSM = AI_FSM_ATTACK;
217 setAITarget(aitarget);
218 }
219 }
220 break;
221
222 case AI_FSM_STAY:
223 if (!owner)
224 myAIFSM = AI_FSM_NORMAL;
225
226 // Always stay!
227 break;
228 }
229
230 aitarget = getAITarget();
231 if (aitarget)
232 {
233 // Double check our aitarget lives. It may have died
234 // and we just a have a corpse reference.
235 if (aitarget->hasIntrinsic(INTRINSIC_DEAD))
236 {
237 clearAITarget();
238 aitarget = 0;
239 }
240 else
241 {
242 if (canSense(aitarget))
243 killtarget = aitarget;
244 }
245 }
246
247 // If our ai type attacks the avatar, check that...
248 if (!killtarget && avatar && (avatar != owner))
249 {
250 // Check to see if we are an avatar hunter.
251 // ie, the hostile flag is set.
252 if (getAttitude(avatar) == ATTITUDE_HOSTILE)
253 {
254 // If we are in the avatar's FOV, we will charge...
255 if (canSense(avatar))
256 {
257 // One final test if the avatar is part of the same
258 // chain as we are. This way imps summoned by demons
259 // won't target you.
260 if (!hasCommonMaster(avatar))
261 killtarget = avatar;
262 }
263 }
264 }
265
266 if (killtarget)
267 {
268 // Determine shortest direction...
269 diffx = killtarget->getX() - getX();
270 diffy = killtarget->getY() - getY();
271 dx = sign(diffx);
272 dy = sign(diffy);
273 distx = abs(diffx);
274 disty = abs(diffy);
275 range = MAX(distx, disty);
276
277 targetinfov = true;
278
279 // If the kill target is the avatar,
280 // check to see if we have a pure LOS to the avatar,
281 // ie, if we are in the FOV. If not, we want to use the smell
282 // gradient.
283 if (killtarget->isAvatar() && !glbCurLevel->hasFOV(getX(), getY()))
284 {
285 glbCurLevel->getAvatarSmellGradient(this, getX(), getY(), dx, dy);
286 targetinfov = false;
287 }
288 }
289
290 // If so, we try to kill it.
291 if (killtarget)
292 {
293 // We only get here if we know where the target is, so don't
294 // need to recheck LOS.
295
296 // We now know the location of our enemy, so mark it.
297 if (glb_aidefs[getAI()].markattackpos)
298 {
299 myAIState &= ~AI_LAST_HIT_LOC;
300 myAIState |= killtarget->getX() | killtarget->getY() * MAP_WIDTH;
301 }
302
303 // Check if we have a straight line.
304 if (!dx || !dy || (distx == disty) )
305 {
306 if (targetinfov && aiDoRangedAttack(dx, dy, range))
307 return;
308 }
309
310 // Check to see if any way is passable...
311 // Because we have someone we hate, we try not to make
312 // any new enemies.
313
314 skipmovecheck = false;
315
316 // If we are one distance away, don't need to check.
317 // This allows us to attack people on unmoveable terrain.
318 if (((distx == 1) && (disty == 0)) ||
319 ((distx == 0) && (disty == 1)))
320 {
321 skipmovecheck = true;;
322 }
323
324 // Grid bugs are allowed to skip if both are one.
325 if (canMoveDiabolically() &&
326 distx == 1 && disty == 1)
327 {
328 skipmovecheck = true;
329 }
330
331 // If we are 2x2 in size, we can attack with certain
332 // other combinations.
333 if (getSize() >= SIZE_GARGANTUAN)
334 {
335 // Walk east.
336 if (dx == 1 && distx == 2 && disty <= 1 && (dy == 0 || dy == 1))
337 {
338 skipmovecheck = true;
339 dx = 1;
340 dy = 0;
341 }
342 // Walk west:
343 if (dx == -1 && distx == 1 && disty <= 1 && (dy == 0 || dy == 1))
344 {
345 skipmovecheck = true;
346 dx = -1;
347 dy = 0;
348 }
349 // Walk south:
350 if (dy == 1 && disty == 2 && distx <= 1 && (dx == 0 || dx == 1))
351 {
352 skipmovecheck = true;
353 dx = 0;
354 dy = 1;
355 }
356 // Walk north:
357 if (dy == -1 && disty == 1 && distx <= 1 && (dx == 0 || dx == 1))
358 {
359 skipmovecheck = true;
360 dx = 0;
361 dy = -1;
362 }
363 }
364
365 // If we haven't set skipmove check, we'll be trying to close
366 // in. We, however, don't want to do this if we have battle
367 // prep stuff (Buffs, etc.)
368 if (!skipmovecheck)
369 if (aiDoBattlePrep(diffx, diffy))
370 return;
371
372 // If we are currently in melee, we may be wanting to flee.
373 if (skipmovecheck)
374 {
375 if ( (killtarget->getExpLevel() > getExpLevel() + 2) ||
376 (getHP() < 5 && getMaxHP() > 20) )
377 {
378 if (aiDoFleeMelee(killtarget))
379 return;
380 }
381
382 // Continue good old melee.
383 actionBump(dx, dy);
384 return;
385 }
386
387 // Walk in the dx/dy direction.
388 if (aiMoveInDirection(dx, dy, distx, disty, killtarget))
389 return;
390 }
391
392 if (!killtarget &&
393 aitarget && glb_aidefs[getAI()].markattackpos &&
394 (myAIState & AI_LAST_HIT_LOC))
395 {
396 // Try to move where the attack came from.
397 dx = sign( (myAIState & 31) - getX() );
398 dy = sign( ((myAIState >> 5) & 31) - getY() );
399 distx = abs( (myAIState & 31) - getX() );
400 disty = abs( ((myAIState >> 5) & 31) - getY() );
401 range = MAX(distx, disty);
402
403 if (!dx && !dy)
404 {
405 // We got to the loc, but found no one! Clear out
406 // the loc with 30% chance.
407 // This will have the mob become bored of the loc eventually.
408 if (rand_chance(30))
409 myAIState &= ~AI_LAST_HIT_LOC;
410 }
411 else
412 {
413 MOBREF thismobref;
414
415 thismobref.setMob(this);
416 if (aiMoveInDirection(dx, dy, distx, disty, aitarget))
417 {
418 // We *may* now know the location of our enemy.
419 // By double checking here a mob chasing you can see you
420 // at the end of its turn and thus catch up.
421 MOB *thismob;
422
423 thismob = thismobref.getMob();
424 if (thismob && glb_aidefs[thismob->getAI()].markattackpos)
425 {
426 aitarget = thismob->getAITarget();
427 if (aitarget && thismob->canSense(aitarget))
428 {
429 thismob->myAIState &= ~AI_LAST_HIT_LOC;
430 thismob->myAIState |= aitarget->getX() |
431 aitarget->getY() * MAP_WIDTH;
432 }
433 }
434 return;
435 }
436 }
437 }
438
439 if (aiUseInventory())
440 return;
441
442 if (aiEatStuff())
443 return;
444
445 if (aiBePackrat())
446 return;
447
448 // Pets told to stay will always stay in place.
449 if (myAIFSM == AI_FSM_STAY)
450 return;
451
452 // Follow our master...
453 if (owner)
454 {
455 #if 0
456 dx = sign(owner->getX() - getX());
457 dy = sign(owner->getY() - getY());
458 distx = abs(owner->getX() - getX());
459 disty = abs(owner->getY() - getY());
460 range = MAX(distx, disty);
461
462 // If we are beside our owner, no need to follow. That would
463 // just cause us to swap places, which would be annoying.
464 if (range > 1)
465 {
466 // Cancel out invalid follows.
467 if (dx)
468 {
469 if (!canMove(getX()+dx, getY(), true,
470 aiWillOpenDoors()) ||
471 glbCurLevel->getMob(getX()+dx, getY()))
472 {
473 dx = 0;
474 }
475 }
476 if (dy)
477 {
478 if (!canMove(getX(), getY()+dy, true,
479 aiWillOpenDoors()) ||
480 glbCurLevel->getMob(getX(), getY()+dy))
481 {
482 dy = 0;
483 }
484 }
485
486 // If both set, unset one of them.
487 if (dx && dy && !canMoveDiabolically())
488 {
489 if (rand_choice(2))
490 dx = 0;
491 else
492 dy = 0;
493 }
494
495 // Move in the chosen direction, if it exists.
496 if (dx || dy)
497 {
498 actionBump(dx, dy);
499 return;
500 }
501 }
502 #else
503 distx = abs(owner->getX() - getX());
504 disty = abs(owner->getY() - getY());
505 range = MAX(distx, disty);
506
507 if (range > 1)
508 {
509 if (owner->isAvatar() &&
510 glbCurLevel->getAvatarSmellGradient(this, getX(), getY(), dx, dy))
511 {
512 // Target is 0 because we don't want to swap
513 // with the avatar.
514 if (aiMoveInDirection(dx, dy, distx, disty, 0))
515 {
516 // Only return if we successful use the hint.
517 return;
518 }
519 }
520
521 #if 0
522 // This code works, but is SLOW.
523 if (glbCurLevel->findPath(
524 getX(), getY(),
525 owner->getX(), owner->getY(),
526 (MOVE_NAMES) getMoveType(),
527 false, false,
528 dx, dy))
529 {
530 if (dx || dy)
531 {
532 actionBump(dx, dy);
533 return;
534 }
535 }
536 #endif
537 }
538 #endif
539 }
540
541 // if we are the avatar and are standing on downstairs, dive deeper!
542 if (isAvatar())
543 {
544 SQUARE_NAMES tile;
545 tile = glbCurLevel->getTile(getX(), getY());
546 if (tile == SQUARE_LADDERDOWN)
547 {
548 if (actionClimb())
549 return;
550 }
551
552 // TODO: it would be nice if we tried something a bit more
553 // interesting here.
554 // Like, find nearest unexplored square and go to it.
555 }
556
557
558 // Random movement...
559 // We have a 25% chance of just waiting.
560 // After all, we have no hurry. This allows the avatar
561 // to position himself, and doesn't guarantee a flee/attack
562 // in narrow corridors.
563 if (!rand_choice(4))
564 return;
565
566 findRandomValidMove(dx, dy, owner);
567
568 actionBump(dx, dy);
569 }
570
571 bool
aiMoveInDirection(int dx,int dy,int distx,int disty,MOB * target)572 MOB::aiMoveInDirection(int dx, int dy, int distx, int disty,
573 MOB *target)
574 {
575 bool skipmovecheck = false;
576 MOB *blocker;
577 int delta;
578
579 // Grid bugs need to do a move check on the diagonal.
580 // If diagonal works, skipmovecheck.
581 if (canMoveDiabolically() && dx && dy)
582 {
583 if (canMoveDelta(dx, dy, true, aiWillOpenDoors(), true))
584 {
585 // Check to see if we hit someone other than
586 // our target...
587 blocker = glbCurLevel->getMob(getX() + dx, getY() + dy);
588 if (!blocker || (blocker == target))
589 {
590 // dx+dy is a valid move...
591 skipmovecheck = true;
592 }
593 }
594 }
595
596 if (!skipmovecheck && dx != 0)
597 {
598 if (canMove(getX() + dx, getY(), true, aiWillOpenDoors(), true))
599 {
600 delta = dx;
601 if (getSize() >= SIZE_GARGANTUAN)
602 {
603 if (delta > 0)
604 delta *= 2;
605 }
606 // Check to see if we hit someone other than
607 // our target...
608 blocker = glbCurLevel->getMob(getX() + delta, getY());
609 if (!blocker || (blocker == target))
610 {
611 // dx maybe a valid move.
612 if (getSize() >= SIZE_GARGANTUAN)
613 {
614 blocker = glbCurLevel->getMob(getX() + delta, getY()+1);
615 if (blocker && blocker != target)
616 dx = 0;
617 }
618 }
619 else
620 dx = 0;
621 }
622 else
623 dx = 0;
624 }
625 if (!skipmovecheck && dy != 0)
626 {
627 if (canMove(getX(), getY() + dy, true, aiWillOpenDoors(), true))
628 {
629 delta = dy;
630 if (getSize() >= SIZE_GARGANTUAN)
631 {
632 if (delta > 0)
633 delta *= 2;
634 }
635
636 // Check to see if we hit someone other than
637 // our target...
638 blocker = glbCurLevel->getMob(getX(), getY() + delta);
639 if (!blocker || (blocker == target))
640 {
641 // dy maybe a valid move...
642 if (getSize() >= SIZE_GARGANTUAN)
643 {
644 blocker = glbCurLevel->getMob(getX()+1, getY() + delta);
645 if (blocker && blocker != target)
646 dy = 0;
647 }
648 }
649 else
650 dy = 0;
651 }
652 else
653 dy = 0;
654 }
655
656 // If both dx & dy are set, zero out one of them at random.
657 // Grid bugs are the exception, of course. However, if they did
658 // not skip the movecheck, the diagonal was an invalid direction.
659 if (dx && dy && (!canMoveDiabolically() || !skipmovecheck))
660 {
661 if (rand_choice(2))
662 dx = 0;
663 else
664 dy = 0;
665 }
666
667 // If either is set, bump!
668 if (dx || dy)
669 {
670 // Holy fuck does this get complicated quickly.
671 // I never meant to create this monstrosity of a conditional!
672 // (Those who were just grepping for swear words: Shame!)
673 if (( (distx > 2 && dx) || (disty > 2 && dy) ) &&
674 hasIntrinsic(INTRINSIC_JUMP) &&
675 canMoveDelta(dx * 2, dy * 2, true, false, true) &&
676 !hasIntrinsic(INTRINSIC_BLIND) &&
677 glbCurLevel->isLit(getX() + dx * 2, getY() + dy * 2) &&
678 (!glbCurLevel->getMob(getX() + dx * 2, getY() + dy * 2) ||
679 (glbCurLevel->getMob(getX() + dx * 2, getY() + dy * 2) == this)))
680 {
681 int tile;
682
683 // Verify the ground is solid enough to jump.
684 tile = glbCurLevel->getTile(getX(), getY());
685 if (!(glb_squaredefs[tile].movetype & MOVE_WALK) &&
686 (!hasIntrinsic(INTRINSIC_WATERWALK) ||
687 !(glb_squaredefs[tile].movetype & MOVE_SWIM)))
688 {
689 // The ground isn't solid enough!
690 return actionBump(dx, dy);
691 }
692 else
693 {
694 // Note we will still try and jump from buried or
695 // space walk situations, but these problems aren't
696 // any better off with bumping, so no need to special
697 // case.
698 // Jump to close quickly.
699 return actionJump(dx, dy);
700 }
701 }
702 else
703 return actionBump(dx, dy);
704 }
705
706 return false;
707 }
708
709 bool
aiCanTargetResist(MOB * target,ELEMENT_NAMES element) const710 MOB::aiCanTargetResist(MOB *target, ELEMENT_NAMES element) const
711 {
712 // We only track info on avatar, so early exit if invalid
713 if (!target || !target->isAvatar())
714 return false;
715
716 MOB *master;
717 master = getMaster();
718 if (master)
719 {
720 // Check master's knowledge base.
721 if (master->aiCanTargetResist(target, element))
722 return true;
723 // We may know locally as well, so still check our own setting.
724 }
725
726 // Search through the memory list.
727 int idx;
728
729 for (idx = 0; idx < glbMobMemory.entries(); idx++)
730 {
731 if (!glbMobMemory(idx))
732 continue;
733
734 if (glbMobMemory(idx)->id.getMob() == this &&
735 glbMobMemory(idx)->element == element)
736 {
737 // We have a match!
738 return true;
739 }
740 }
741
742 // Not found, fail
743 return false;
744 }
745
746 void
aiDecayKnowledgeBase()747 MOB::aiDecayKnowledgeBase()
748 {
749 // Currently only avatar has such a table
750 if (!isAvatar())
751 return;
752
753 int idx;
754
755 for (idx = 0; idx < glbMobMemory.entries(); idx++)
756 {
757 if (glbMobMemory(idx))
758 {
759 if (!glbMobMemory(idx)->id.getMob() ||
760 !glbMobMemory(idx)->turns)
761 {
762 delete glbMobMemory(idx);
763 glbMobMemory.set(idx, 0);
764 }
765 else
766 glbMobMemory(idx)->turns--;
767 }
768 }
769
770 // Collpase the list
771 glbMobMemory.collapse();
772 }
773
774 void
aiNoteThatTargetHasIntrinsic(MOB * target,INTRINSIC_NAMES intrinsic)775 MOB::aiNoteThatTargetHasIntrinsic(MOB *target, INTRINSIC_NAMES intrinsic)
776 {
777 switch (intrinsic)
778 {
779 case INTRINSIC_RESISTFIRE:
780 aiNoteThatTargetCanResist(target, ELEMENT_FIRE);
781 break;
782 case INTRINSIC_RESISTCOLD:
783 aiNoteThatTargetCanResist(target, ELEMENT_COLD);
784 break;
785 case INTRINSIC_RESISTACID:
786 aiNoteThatTargetCanResist(target, ELEMENT_ACID);
787 break;
788 case INTRINSIC_RESISTSHOCK:
789 aiNoteThatTargetCanResist(target, ELEMENT_SHOCK);
790 break;
791 case INTRINSIC_BLIND:
792 aiNoteThatTargetCanResist(target, ELEMENT_LIGHT);
793 break;
794 case INTRINSIC_RESISTPHYSICAL:
795 aiNoteThatTargetCanResist(target, ELEMENT_PHYSICAL);
796 break;
797
798 // Fake elements:
799 case INTRINSIC_RESISTPOISON:
800 aiNoteThatTargetCanResist(target, ELEMENT_POISON);
801 break;
802 case INTRINSIC_REFLECTION:
803 aiNoteThatTargetCanResist(target, ELEMENT_REFLECTIVITY);
804 break;
805 case INTRINSIC_RESISTSLEEP:
806 aiNoteThatTargetCanResist(target, ELEMENT_SLEEP);
807 break;
808
809 default:
810 // Everything else is ignored.
811 break;
812 }
813 }
814
815 void
aiNoteThatTargetCanResist(MOB * target,ELEMENT_NAMES element)816 MOB::aiNoteThatTargetCanResist(MOB *target, ELEMENT_NAMES element)
817 {
818 // If the target isn't the avatar, we don't track
819 if (!target || !target->isAvatar())
820 return;
821
822 // 50% chance of noting resistance
823 if (!rand_chance(50))
824 return;
825
826 aiTrulyNoteThatTargetCanResist(target, element);
827 }
828
829 void
aiTrulyNoteThatTargetCanResist(MOB * target,ELEMENT_NAMES element)830 MOB::aiTrulyNoteThatTargetCanResist(MOB *target, ELEMENT_NAMES element)
831 {
832 // Pass on to our master, if any.
833 MOB *master;
834
835 master = getMaster();
836 if (master)
837 {
838 master->aiTrulyNoteThatTargetCanResist(target, element);
839 }
840
841 // Verify we are not the avatar, as we don't care about our own
842 // references :>
843 if (isAvatar())
844 return;
845
846 // Check the avatar resist list for this mob.
847 int idx;
848 for (idx = 0; idx < glbMobMemory.entries(); idx++)
849 {
850 if (glbMobMemory(idx))
851 {
852 if (glbMobMemory(idx)->id.getMob() == this &&
853 glbMobMemory(idx)->element == element)
854 {
855 // Restore the counter.
856 glbMobMemory(idx)->turns = 100;
857 return;
858 }
859 }
860 }
861
862 // Failed to find on list, append.
863 MOB_MEMORY *mem = new MOB_MEMORY;
864 mem->id.setMob(this);
865 mem->element = element;
866 mem->turns = 100;
867 glbMobMemory.append(mem);
868 }
869
870 bool
aiDoMouse()871 MOB::aiDoMouse()
872 {
873 int lastdir, dir, newdir;
874 int dx, dy;
875 bool found = false;
876
877 // Least two bits track our last direction.
878 lastdir = myAIState & 3;
879
880 // We want to try and track a wall. We want this wall to
881 // be on our right hand side.
882 // Mob looking right:
883 // ?@ <- turn south to keep wall.
884 // X.
885 //
886 // ?@. <= Bad state, no old wall! Keep going straight.
887 // ..?
888 //
889 // ?@. <= Go straight
890 // ?X?
891 //
892 // ?.?
893 // ?@X <= Turn and head north.
894 // XX?
895 //
896 // ?X?
897 // .@X <= Turn around.
898 // ?X?
899
900 // Is there a wall to the right?
901 getDirection((lastdir + 1) & 3, dx, dy);
902
903 if (!canMoveDelta(dx, dy, true, aiWillOpenDoors()) ||
904 glbCurLevel->getMob(getX() + dx, getY() + dy))
905 {
906 // Got a wall to the right. Try first available direction.
907 dir = lastdir;
908
909 while (1)
910 {
911 getDirection(dir, dx, dy);
912 if (canMoveDelta(dx, dy, true, aiWillOpenDoors()) &&
913 !glbCurLevel->getMob(getX() + dx, getY() + dy))
914 {
915 newdir = dir;
916 break;
917 }
918 // Keeping wall on right means turning left first!
919 dir--;
920 dir &= 3;
921 if (dir == lastdir)
922 {
923 newdir = -1;
924 break;
925 }
926 }
927 }
928 else
929 {
930 // No wall to the right. If there is a wall to the right &
931 // behind us, we want to turn right.
932 // Otherwise, we're in a bad state and keep going.
933 int bx, by;
934
935 getDirection( (lastdir + 2) & 3, bx, by);
936
937 // This neat trick gets us the back right wall:
938 bx |= dx;
939 by |= dy;
940
941 if (!canMoveDelta(bx, by, true, aiWillOpenDoors()) ||
942 glbCurLevel->getMob(getX() + bx, getY() + by))
943 {
944 // There is a wall, want to turn right.
945 newdir = lastdir + 1;
946 newdir &= 3;
947 }
948 else
949 {
950 // Go straight.
951 newdir = lastdir;
952 }
953 }
954
955 if (newdir == -1)
956 newdir = lastdir;
957 else
958 found = true;
959
960 // Store our last direction.
961 myAIState &= ~3;
962 myAIState |= newdir;
963
964 if (found)
965 {
966 return actionBump(dx, dy);
967 }
968
969 // Mouse is cornered! Return to normal AI.
970 return false;
971 }
972
973 bool
aiDoUrgentMoves()974 MOB::aiDoUrgentMoves()
975 {
976 // This determines if there is any condition that should
977 // trump everything else.
978
979 // 0) If turning to stone, stop it.
980 if (hasIntrinsic(INTRINSIC_STONING))
981 {
982 if (aiDoStopStoningSelf())
983 return true;
984 }
985
986 // 1) If health is low, heal.
987 int safelevel;
988 safelevel = (getMaxHP() >> 3) + 1;
989 if ((safelevel < getMaxHP()) && (getHP() <= safelevel))
990 {
991 if (aiDoHealSelf())
992 return true;
993 }
994
995 // 2) If poisoned, cure it!
996 if (aiIsPoisoned())
997 {
998 if (aiDoCurePoisonSelf())
999 return true;
1000 }
1001
1002 // 2.5, if on dangerous square, deal with it!
1003 SQUARE_NAMES tile = glbCurLevel->getTile(getX(), getY());
1004 bool escape = false;
1005
1006 switch (tile)
1007 {
1008 case SQUARE_LAVA:
1009 case SQUARE_FORESTFIRE:
1010 if (!hasIntrinsic(INTRINSIC_RESISTFIRE) ||
1011 hasIntrinsic(INTRINSIC_VULNFIRE))
1012 {
1013 if (aiDoFreezeMySquare())
1014 return true;
1015 escape = true;
1016 }
1017 break;
1018 case SQUARE_WATER:
1019 if (!(getMoveType() & MOVE_FLY) &&
1020 !hasIntrinsic(INTRINSIC_NOBREATH))
1021 {
1022 escape = true;
1023 }
1024 break;
1025 case SQUARE_ACID:
1026 if (!(getMoveType() & MOVE_FLY) &&
1027 (!hasIntrinsic(INTRINSIC_RESISTACID) ||
1028 hasIntrinsic(INTRINSIC_VULNACID)))
1029 {
1030 escape = true;
1031 }
1032 break;
1033 case SQUARE_STARS1:
1034 case SQUARE_STARS2:
1035 case SQUARE_STARS3:
1036 case SQUARE_STARS4:
1037 case SQUARE_STARS5:
1038 if (!hasIntrinsic(INTRINSIC_NOBREATH))
1039 {
1040 escape = true;
1041 }
1042 break;
1043
1044 default:
1045 break;
1046 }
1047
1048 if (escape)
1049 {
1050 if (aiDoEscapeMySquare())
1051 return true;
1052 }
1053
1054 // 3) If on fire, douse ourselves with a bottle.
1055 if (hasIntrinsic(INTRINSIC_AFLAME))
1056 {
1057 if (aiDoDouseSelf())
1058 return true;
1059 }
1060
1061 // Nothing urgent.
1062 return false;
1063 }
1064
1065 bool
aiDoFleeMelee(MOB * target)1066 MOB::aiDoFleeMelee(MOB *target)
1067 {
1068 ITEM *item;
1069 WAND_NAMES wand;
1070 MAGICTYPE_NAMES mtype;
1071 SQUARE_NAMES tile;
1072 TRAP_NAMES trap;
1073
1074 int dx = 0, dy = 0;
1075
1076 if (target)
1077 {
1078 dx = SIGN(target->getX()-getX());
1079 dy = SIGN(target->getY()-getY());
1080 }
1081 // Check to see if we can jump down a hole or climb stairs.
1082 tile = glbCurLevel->getTile(getX(), getY());
1083 trap = (TRAP_NAMES) glb_squaredefs[tile].trap;
1084 switch (trap)
1085 {
1086 case TRAP_NONE:
1087 case TRAP_PIT:
1088 case TRAP_SPIKEDPIT:
1089 case TRAP_SMOKEVENT:
1090 case TRAP_POISONVENT:
1091 case NUM_TRAPS:
1092 break;
1093
1094 case TRAP_TELEPORT:
1095 return actionClimbDown();
1096
1097 case TRAP_HOLE:
1098 return actionClimbDown();
1099 }
1100
1101 switch (tile)
1102 {
1103 case SQUARE_LADDERUP:
1104 // We don't want this triggered in stress test.
1105 if (isAvatar())
1106 break;
1107 return actionClimbUp();
1108
1109 case SQUARE_LADDERDOWN:
1110 return actionClimbDown();
1111
1112 default:
1113 break;
1114 }
1115
1116 // Search our items for a way out.
1117 if (glb_aidefs[getAI()].zapwands)
1118 {
1119 for (item = myInventory; item; item = item->getNext())
1120 {
1121 mtype = item->getMagicType();
1122 if (mtype == MAGICTYPE_WAND && (item->getCharges() > 0))
1123 {
1124 wand = (WAND_NAMES) item->getMagicClass();
1125
1126 switch (wand)
1127 {
1128 case WAND_TELEPORT:
1129 if (target &&
1130 (hasIntrinsic(INTRINSIC_TELEFIXED) ||
1131 rand_chance(60)))
1132 {
1133 // Teleport away the attacker.
1134 // This is preferable as it is more annoying
1135 // to the player and better matches how players
1136 // react to a single powerful threat.
1137 return actionZap(item->getX(), item->getY(),
1138 dx, dy, 0);
1139 }
1140 // Zap at self.
1141 return actionZap(item->getX(), item->getY(), 0, 0, 0);
1142
1143 case WAND_DIGGING:
1144 // Zap downwards.
1145 return actionZap(item->getX(), item->getY(), 0, 0, -1);
1146
1147 case WAND_SLEEP:
1148 // If target isn't sleep resist and not already
1149 // asleep, zap to escape.
1150 if (target &&
1151 !target->hasIntrinsic(INTRINSIC_ASLEEP) &&
1152 !aiCanTargetResist(target, ELEMENT_SLEEP))
1153 {
1154 // Try to put our enemy asleep.
1155 return actionZap(item->getX(), item->getY(),
1156 dx, dy, 0);
1157 }
1158 break;
1159
1160 case WAND_FIRE:
1161 case WAND_ICE:
1162 case WAND_LIGHT:
1163 case WAND_NOTHING:
1164 case WAND_CREATETRAP:
1165 case WAND_INVISIBLE:
1166 case WAND_CREATEMONSTER:
1167 case WAND_POLYMORPH:
1168 case WAND_SPEED:
1169 case WAND_SLOW:
1170 // Nothing to do with these wands.
1171 break;
1172
1173 case NUM_WANDS:
1174 UT_ASSERT(!"Invalid wand!");
1175 break;
1176 }
1177 }
1178 }
1179 }
1180
1181 // Try different spells.
1182 if (canCastSpell(SPELL_TELEPORT))
1183 {
1184 return actionCast(SPELL_TELEPORT, 0, 0, 0);
1185 }
1186 if (canCastSpell(SPELL_TELEWITHCONTROL))
1187 {
1188 return actionCast(SPELL_TELEWITHCONTROL, 0, 0, 0);
1189 }
1190 // TODO: Support blinking!
1191
1192 // We gotta stand and fight.
1193 return false;
1194 }
1195
1196 bool
aiUseInventory()1197 MOB::aiUseInventory()
1198 {
1199 // Only search if we have something new.
1200 if (!(myAIState & AI_DIRTY_INVENTORY))
1201 return false;
1202
1203 if (glb_aidefs[getAI()].throwitems)
1204 {
1205 ITEM *item;
1206 bool anythrowable = false;
1207
1208 for (item = myInventory; item; item = item->getNext())
1209 {
1210 if (aiIsItemThrowable(item))
1211 {
1212 anythrowable = true;
1213 break;
1214 }
1215 }
1216
1217 if (anythrowable)
1218 myAIState |= AI_HAS_THROWABLE;
1219 else
1220 myAIState &= ~AI_HAS_THROWABLE;
1221 }
1222 if (glb_aidefs[getAI()].zapwands)
1223 {
1224 ITEM *item;
1225 bool anyzappable = false;
1226
1227 for (item = myInventory; item; item = item->getNext())
1228 {
1229 if (aiIsItemZapAttack(item))
1230 {
1231 anyzappable = true;
1232 break;
1233 }
1234 }
1235
1236 if (anyzappable)
1237 myAIState |= AI_HAS_ATTACKWAND;
1238 else
1239 myAIState &= ~AI_HAS_ATTACKWAND;
1240 }
1241
1242 if (!glb_aidefs[getAI()].useitems)
1243 return false;
1244
1245 // Check to see if there is anything to equip.
1246 ITEM *item;
1247 ITEMSLOT_NAMES slot;
1248
1249 for (item = myInventory; item; item = item->getNext())
1250 {
1251 // Ignore already equipped items.
1252 if (!item->getX())
1253 continue;
1254
1255 // Determine the optimal slot.
1256 slot = NUM_ITEMSLOTS;
1257 if (item->isHelmet())
1258 slot = ITEMSLOT_HEAD;
1259 if (item->isBoots())
1260 slot = ITEMSLOT_FEET;
1261 if (item->isShield())
1262 slot = ITEMSLOT_LHAND;
1263 if (item->isJacket())
1264 slot = ITEMSLOT_BODY;
1265 if (item->isAmulet())
1266 slot = ITEMSLOT_AMULET;
1267 if (item->isRing())
1268 {
1269 slot = ITEMSLOT_RRING;
1270 if (getEquippedItem(ITEMSLOT_RRING)
1271 && !getEquippedItem(ITEMSLOT_LRING))
1272 slot = ITEMSLOT_LRING;
1273 }
1274
1275 if (slot == NUM_ITEMSLOTS &&
1276 item->getAttackName() != ATTACK_MISUSED &&
1277 item->getAttackName() != ATTACK_MISUSED_BUTWEAPON
1278 )
1279 {
1280 // Consider as a weapon.
1281 slot = ITEMSLOT_RHAND;
1282 }
1283
1284 // Check to see if they could actually equip there...
1285 if (slot != NUM_ITEMSLOTS &&
1286 !ableToEquip(item->getX(), item->getY(), slot, true))
1287 {
1288 slot = NUM_ITEMSLOTS;
1289 }
1290
1291 if (slot != NUM_ITEMSLOTS)
1292 {
1293 // Verify that the new item is better.
1294 if (aiIsItemCooler(item, slot))
1295 {
1296 return actionEquip(item->getX(), item->getY(), slot);
1297 }
1298 }
1299 }
1300
1301 // Nothing found, clear our ai state.
1302 myAIState &= ~AI_DIRTY_INVENTORY;
1303
1304 return false;
1305 }
1306
1307 bool
aiEatStuff()1308 MOB::aiEatStuff()
1309 {
1310 if (getHungerLevel() > HUNGER_FORAGE)
1311 {
1312 // Full, so don't eat
1313 return false;
1314 }
1315
1316 if (!(myAIState & AI_HAS_EDIBLE))
1317 {
1318 // We don't know of anything...
1319 return false;
1320 }
1321
1322 ITEM *item;
1323
1324 for (item = myInventory; item; item = item->getNext())
1325 {
1326 if (canDigest(item) && safeToDigest(item))
1327 {
1328 return actionEat(item->getX(), item->getY());
1329 }
1330 }
1331
1332 // We must not have any edible food after all.
1333 myAIState &= ~AI_HAS_EDIBLE;
1334
1335 return false;
1336 }
1337
1338 bool
aiBePackrat()1339 MOB::aiBePackrat()
1340 {
1341 bool hungry = true;
1342 bool packrat;
1343
1344 packrat = glb_aidefs[getAI()].packrat;
1345
1346 if (getHungerLevel() > HUNGER_FORAGE)
1347 hungry = false;
1348 if (!hungry && !packrat)
1349 return false;
1350
1351 // Pick stuff up.
1352 ITEM *item;
1353 bool submerged;
1354
1355 if (hasIntrinsic(INTRINSIC_INTREE))
1356 return false;
1357
1358 submerged = hasIntrinsic(INTRINSIC_INPIT) ||
1359 hasIntrinsic(INTRINSIC_SUBMERGED);
1360 item = glbCurLevel->getItem(getX(), getY(), submerged);
1361
1362 if (item)
1363 {
1364 // If we are not a packrat, we only pick up food
1365 if (!packrat)
1366 {
1367 if (!canDigest(item))
1368 return false;
1369 }
1370 // Check to see if our inventory is full.
1371 int sx, sy;
1372 if (!findItemSlot(sx, sy))
1373 return false;
1374
1375 return actionPickUp(item);
1376 }
1377
1378 return false;
1379 }
1380
1381 bool
aiDoBattlePrep(int diffx,int diffy)1382 MOB::aiDoBattlePrep(int diffx, int diffy)
1383 {
1384 // We have sighted the enemy, but don't have a straight LOS.
1385 // Thus it is time to do some cool stuff.
1386 MOB *target = glbCurLevel->getMob(getX()+diffx, getY()+diffy);
1387 int range = ABS(diffx) + ABS(diffy);
1388
1389 // Try to use our items:
1390 if (glb_aidefs[getAI()].useitems)
1391 {
1392 ITEM *item;
1393 MAGICTYPE_NAMES mtype;
1394 WAND_NAMES wand;
1395
1396 for (item = myInventory; item; item = item->getNext())
1397 {
1398 mtype = item->getMagicType();
1399
1400 if (mtype == MAGICTYPE_POTION)
1401 {
1402 // TODO: Potion cases?
1403 }
1404 else if (mtype == MAGICTYPE_SCROLL)
1405 {
1406 // TODO: Scroll cases?
1407 }
1408 else if (mtype == MAGICTYPE_WAND && item->getCharges() > 0 &&
1409 glb_aidefs[getAI()].zapwands)
1410 {
1411 wand = (WAND_NAMES) item->getMagicClass();
1412 switch (wand)
1413 {
1414 case WAND_INVISIBLE:
1415 // Zap at ourself.
1416 // If we are already invisble, no need to duplicate.
1417 if (hasIntrinsic(INTRINSIC_INVISIBLE))
1418 break;
1419
1420 return actionZap(item->getX(), item->getY(), 0, 0, 0);
1421
1422 case WAND_SPEED:
1423 // Zap at ourself.
1424 // If we are already fast, don't redo.
1425 if (hasIntrinsic(INTRINSIC_QUICK))
1426 break;
1427
1428 return actionZap(item->getX(), item->getY(), 0, 0, 0);
1429
1430 case WAND_CREATEMONSTER:
1431 {
1432 // Find a valid spot to make a mob.
1433 int dx, dy;
1434
1435 // Evil laugh.
1436 for (dy = -1; dy <= 1; dy++)
1437 {
1438 if (getY() + dy < 0 || getY() + dy > MAP_HEIGHT)
1439 continue;
1440 for (dx = -1; dx <= 1; dx++)
1441 {
1442 if (getX() + dx < 0 || getX() + dx > MAP_WIDTH)
1443 continue;
1444
1445 if (!dx && !dy)
1446 continue;
1447
1448 if (glbCurLevel->canMove(getX() + dx,
1449 getY() + dy, MOVE_WALK) &&
1450 !glbCurLevel->getMob(getX() + dx,
1451 getY() + dy))
1452 {
1453 return actionZap(item->getX(), item->getY(),
1454 dx, dy, 0);
1455 }
1456 }
1457 }
1458 }
1459
1460 case WAND_SLOW:
1461 case WAND_TELEPORT:
1462 case WAND_DIGGING:
1463 case WAND_SLEEP:
1464 case WAND_FIRE:
1465 case WAND_ICE:
1466 case WAND_LIGHT:
1467 case WAND_NOTHING:
1468 case WAND_CREATETRAP:
1469 case WAND_POLYMORPH:
1470 // Nothing to do with these wands.
1471 break;
1472
1473 case NUM_WANDS:
1474 UT_ASSERT(!"Invalid wand!");
1475 break;
1476 }
1477 }
1478 }
1479 }
1480
1481 // Try to use spells.
1482
1483 // Keep the target visible to us!
1484 if (canCastSpell(SPELL_TRACK) && mob_shouldCast())
1485 {
1486 // Only do it if our target isn't already
1487 if (target && !target->hasIntrinsic(INTRINSIC_POSITIONREVEALED))
1488 {
1489 return actionCast(SPELL_TRACK, diffx, diffy, 0);
1490 }
1491 }
1492
1493 // Bring the target into melee range
1494 if (target && canCastSpell(SPELL_FETCH) && mob_shouldCast(6))
1495 {
1496 return actionCast(SPELL_FETCH, diffx, diffy, 0);
1497 }
1498
1499 if (canCastSpell(SPELL_SUMMON_DEMON) && mob_shouldCast())
1500 {
1501 return actionCast(SPELL_SUMMON_DEMON, 0, 0, 0);
1502 }
1503
1504 if (canCastSpell(SPELL_SUMMON_IMP) && mob_shouldCast())
1505 {
1506 return actionCast(SPELL_SUMMON_IMP, 0, 0, 0);
1507 }
1508
1509 if (canCastSpell(SPELL_LIVINGFROST) && mob_shouldCast())
1510 {
1511 return actionCast(SPELL_LIVINGFROST, 0, 0, 0);
1512 }
1513
1514 // Poison our item.
1515 if (canCastSpell(SPELL_POISONITEM))
1516 {
1517 ITEM *item;
1518
1519 item = getEquippedItem(ITEMSLOT_RHAND);
1520 if (item && !item->isPoisoned())
1521 {
1522 return actionCast(SPELL_POISONITEM, 0, 0, 0);
1523 }
1524 }
1525
1526 if (!hasIntrinsic(INTRINSIC_REGENERATION) && canCastSpell(SPELL_REGENERATE))
1527 {
1528 return actionCast(SPELL_REGENERATE, 0, 0, 0);
1529 }
1530
1531 if (!hasIntrinsic(INTRINSIC_RESISTPOISON) && canCastSpell(SPELL_SLOWPOISON))
1532 {
1533 return actionCast(SPELL_SLOWPOISON, 0, 0, 0);
1534 }
1535
1536 if (canCastSpell(SPELL_FLAMESTRIKE) && mob_shouldCast() &&
1537 !aiCanTargetResist(target, ELEMENT_FIRE))
1538 {
1539 if (target && canSense(target))
1540 return actionCast(SPELL_FLAMESTRIKE, diffx, diffy, 0);
1541 }
1542
1543 // Fire elementals get a special surprise :>
1544 if (target && target->getDefinition() == MOB_FIREELEMENTAL &&
1545 canCastSpell(SPELL_DOWNPOUR) && mob_shouldCast() &&
1546 canSense(target))
1547 {
1548 return actionCast(SPELL_DOWNPOUR, diffx, diffy, 0);
1549 }
1550
1551 // If the target is in a pit, consider drowning...
1552 if (target && target->hasIntrinsic(INTRINSIC_INPIT) &&
1553 canCastSpell(SPELL_DOWNPOUR) && mob_shouldCast() &&
1554 canSense(target))
1555 {
1556 return actionCast(SPELL_DOWNPOUR, diffx, diffy, 0);
1557 }
1558
1559 // Yes, I am evil.
1560 if (canCastSpell(SPELL_FORCEWALL) &&
1561 range < 4 &&
1562 mob_shouldCast(6) &&
1563 !aiCanTargetResist(target, ELEMENT_PHYSICAL))
1564 {
1565 return actionCast(SPELL_FORCEWALL, diffx, diffy, 0);
1566 }
1567
1568 if (canCastSpell(SPELL_ROLLINGBOULDER) && mob_shouldCast())
1569 {
1570 // This is a little bit tricky...
1571 const static int sdx[4] = { -1, 1, 0, 0 };
1572 const static int sdy[4] = { 0, 0, -1, 1 };
1573
1574 int i, r;
1575 int bx, by;
1576 bool found;
1577 SQUARE_NAMES tile;
1578 ITEM *boulder;
1579
1580 for (i = 0; i < 4; i++)
1581 {
1582 // Check to see if there is a boulder or earth tile
1583 // in range...
1584 bx = getX() + diffx;
1585 by = getY() + diffy;
1586
1587 found = false;
1588 for (r = 0; r < 5; r++)
1589 {
1590 bx += sdx[i];
1591 by += sdy[i];
1592
1593 if (bx < 0 || bx >= MAP_WIDTH)
1594 break;
1595 if (by < 0 || by >= MAP_WIDTH)
1596 break;
1597
1598 // See if we found something good.
1599 tile = glbCurLevel->getTile(bx, by);
1600
1601 switch (tile)
1602 {
1603 case SQUARE_EMPTY:
1604 case SQUARE_WALL:
1605 case SQUARE_SECRETDOOR:
1606 found = true;
1607 break;
1608 default:
1609 // Not a rolling boulder candidate.
1610 // If it is not possible to fly through
1611 // this square, it will block any potential
1612 // boulders so should stop us...
1613 if (!glbCurLevel->canMove(bx, by, MOVE_STD_FLY))
1614 {
1615 r = 100;
1616 }
1617 break;
1618 }
1619
1620 // See if there is a boulder.
1621 boulder = glbCurLevel->getItem(bx, by);
1622 if (boulder && boulder->getDefinition() == ITEM_BOULDER)
1623 found = true;
1624
1625 if (found)
1626 break;
1627 }
1628
1629 // If we found a valid source, see if we can start
1630 // a boulder from here...
1631 if (found)
1632 {
1633 // Determine what the rolling boulder spell
1634 // would do if we triggered from here...
1635 int bdx, bdy;
1636
1637 bdx = bx - getX();
1638 bdy = by - getY();
1639
1640 // If one is bigger than the other, zero the other.
1641 // (ie, usually do horizontal moves)
1642 if (abs(bdx) > abs(bdy))
1643 bdy = 0;
1644 else if (abs(bdx) < abs(bdy))
1645 bdx = 0;
1646
1647 bdx = SIGN(bdx);
1648 bdy = SIGN(bdy);
1649
1650 // bdx & bdy must match our signed direction
1651 // or we won't send the boulder in the right direction.
1652 if (bdx == sdx[i] && bdy == sdy[i])
1653 {
1654 // Finally, we have to be able to see the potential
1655 // boulder source...
1656 if (glbCurLevel->hasLOS(getX(), getY(), bx, by))
1657 {
1658 // Woohoo! All passes, go for it!
1659 return actionCast(SPELL_ROLLINGBOULDER,
1660 bx - getX(), by - getY(), 0);
1661 }
1662 }
1663 }
1664 } // for each direction.
1665 }
1666
1667 if (canCastSpell(SPELL_CREATEPIT) && mob_shouldCast())
1668 {
1669 // Determine if target isn't already in a pit.
1670 switch (glbCurLevel->getTile(getX() + diffx, getY() + diffy))
1671 {
1672 case SQUARE_CORRIDOR:
1673 case SQUARE_FLOOR:
1674 case SQUARE_FLOORPIT:
1675 case SQUARE_PATHPIT:
1676 {
1677 // Determine if target mob is in a pit.
1678 MOB *target;
1679
1680 target = glbCurLevel->getMob(getX() + diffx, getY() + diffy);
1681 if (target && !target->hasIntrinsic(INTRINSIC_INPIT))
1682 return actionCast(SPELL_CREATEPIT, diffx, diffy, 0);
1683 break;
1684 }
1685 default:
1686 break;
1687 }
1688 }
1689
1690 // No further preperation needed.
1691 return false;
1692 }
1693
1694 bool
aiDoHealSelf()1695 MOB::aiDoHealSelf()
1696 {
1697 // Check for items that can heal...
1698 ITEM *item, *polywand = 0;
1699 MAGICTYPE_NAMES mtype;
1700 POTION_NAMES potion;
1701 SCROLL_NAMES scroll;
1702 WAND_NAMES wand;
1703
1704 if (glb_aidefs[getAI()].useitems)
1705 {
1706 for (item = myInventory; item; item = item->getNext())
1707 {
1708 mtype = item->getMagicType();
1709 if (mtype == MAGICTYPE_POTION)
1710 {
1711 potion = (POTION_NAMES) item->getMagicClass();
1712 if (potion == POTION_HEAL)
1713 {
1714 return actionEat(item->getX(), item->getY());
1715 }
1716 }
1717 else if (mtype == MAGICTYPE_SCROLL)
1718 {
1719 if (!hasIntrinsic(INTRINSIC_BLIND))
1720 {
1721 scroll = (SCROLL_NAMES) item->getMagicClass();
1722 if (scroll == SCROLL_HEAL)
1723 {
1724 return actionRead(item->getX(), item->getY());
1725 }
1726 }
1727 }
1728 else if (mtype == MAGICTYPE_WAND && (item->getCharges() > 0))
1729 {
1730 wand = (WAND_NAMES) item->getMagicClass();
1731 if (wand == WAND_POLYMORPH)
1732 {
1733 // This is a very desperate measure...
1734 // Of course, it is pointless if we are already
1735 // polymorphed or have unchanging.
1736 if (!hasIntrinsic(INTRINSIC_UNCHANGING) &&
1737 !isPolymorphed())
1738 {
1739 polywand = item;
1740 }
1741 }
1742 }
1743 }
1744 }
1745
1746 // Check for spells.
1747 if (canCastSpell(SPELL_MAJORHEAL))
1748 {
1749 return actionCast(SPELL_MAJORHEAL, 0, 0, 0);
1750 }
1751 if (canCastSpell(SPELL_HEAL))
1752 {
1753 return actionCast(SPELL_HEAL, 0, 0, 0);
1754 }
1755 if (!hasIntrinsic(INTRINSIC_REGENERATION) && canCastSpell(SPELL_REGENERATE))
1756 {
1757 return actionCast(SPELL_REGENERATE, 0, 0, 0);
1758 }
1759
1760 // OKay, this is a pretty desperate action, but I think it's reasonable.
1761 // Note that if we do this every time we fall into this path,
1762 // creatures with poly wands are *very* annoying. They will keep
1763 // re-polying themselves until out of charges. We thus throw
1764 // a 30% chance in so you can get a few more hits in before they
1765 // try again.
1766 if (polywand && rand_chance(30))
1767 {
1768 return actionZap(polywand->getX(), polywand->getY(), 0, 0, 0);
1769 }
1770
1771 // No other methods.
1772 return false;
1773 }
1774
1775 bool
aiDoCurePoisonSelf()1776 MOB::aiDoCurePoisonSelf()
1777 {
1778 // Check for items that can heal...
1779 ITEM *item;
1780 MAGICTYPE_NAMES mtype;
1781 POTION_NAMES potion;
1782
1783 if (hasIntrinsic(INTRINSIC_RESISTPOISON))
1784 return false;
1785
1786 if (glb_aidefs[getAI()].useitems)
1787 {
1788 for (item = myInventory; item; item = item->getNext())
1789 {
1790 mtype = item->getMagicType();
1791 if (mtype == MAGICTYPE_POTION)
1792 {
1793 potion = (POTION_NAMES) item->getMagicClass();
1794 if (potion == POTION_CURE)
1795 {
1796 return actionEat(item->getX(), item->getY());
1797 }
1798 }
1799 }
1800 }
1801
1802 // Check for spells.
1803 if (canCastSpell(SPELL_CUREPOISON))
1804 {
1805 return actionCast(SPELL_CUREPOISON, 0, 0, 0);
1806 }
1807
1808 if (!hasIntrinsic(INTRINSIC_RESISTPOISON))
1809 {
1810 if (canCastSpell(SPELL_SLOWPOISON))
1811 return actionCast(SPELL_SLOWPOISON, 0, 0, 0);
1812 }
1813
1814 // No other methods.
1815 return false;
1816 }
1817
1818 bool
aiDoDouseSelf()1819 MOB::aiDoDouseSelf()
1820 {
1821 // Check for water to douse self with.
1822 ITEM *item;
1823
1824 if (glb_aidefs[getAI()].useitems)
1825 {
1826 for (item = myInventory; item; item = item->getNext())
1827 {
1828 if (item->getDefinition() == ITEM_WATER)
1829 {
1830 // If the water is holy, and we are undead, we don't
1831 // want to do this.
1832 if (item->isBlessed() && getMobType() == MOBTYPE_UNDEAD)
1833 continue;
1834
1835 // Throw at the roof to douse ourselves.
1836 return actionThrow(item->getX(), item->getY(),
1837 0, 0, 1);
1838 }
1839 }
1840 }
1841
1842 // Check if we can case downpour on ourself
1843 if (canCastSpell(SPELL_DOWNPOUR))
1844 {
1845 return actionCast(SPELL_DOWNPOUR, 0, 0, 0);
1846 }
1847
1848 // TODO: Consider diving into pools?
1849
1850 // No way to deal with it.
1851 return false;
1852 }
1853
1854 bool
aiDoFreezeMySquare()1855 MOB::aiDoFreezeMySquare()
1856 {
1857 // Look for wand to use
1858 ITEM *item;
1859
1860 // If we are submerged, bad idea to freeze!
1861 if (hasIntrinsic(INTRINSIC_SUBMERGED))
1862 return false;
1863
1864 if (glb_aidefs[getAI()].zapwands)
1865 {
1866 for (item = myInventory; item; item = item->getNext())
1867 {
1868 if (item->getMagicType() == MAGICTYPE_WAND &&
1869 item->getMagicClass() == WAND_ICE)
1870 {
1871 // Verify there are charges
1872 if (item->getCharges() > 0)
1873 {
1874 // Zap at the ground.
1875 return actionZap(item->getX(), item->getY(),
1876 0, 0, -1);
1877 }
1878 }
1879 }
1880 }
1881
1882 // Check if we can cast downpour on ourself
1883 if (canCastSpell(SPELL_DOWNPOUR))
1884 {
1885 return actionCast(SPELL_DOWNPOUR, 0, 0, 0);
1886 }
1887
1888 // Check if we can cast frost bolt
1889 if (canCastSpell(SPELL_FROSTBOLT))
1890 {
1891 return actionCast(SPELL_FROSTBOLT, 0, 0, -1);
1892 }
1893
1894 // No way to deal with it.
1895 return false;
1896 }
1897
1898 bool
aiDoEscapeMySquare()1899 MOB::aiDoEscapeMySquare()
1900 {
1901 int dx, dy;
1902
1903 findRandomValidMove(dx, dy, 0);
1904
1905 if (dx || dy)
1906 {
1907 // We need to bump here so we are willing to open doors!
1908 // Be embarassing to die on lava with a door beside you that
1909 // you are banging your head on.
1910 return actionBump(dx, dy);
1911 }
1912 return false;
1913 }
1914
1915 bool
aiDoStopStoningSelf()1916 MOB::aiDoStopStoningSelf()
1917 {
1918 // Check for ways to stop stoning.
1919 ITEM *item;
1920 MAGICTYPE_NAMES mtype;
1921 POTION_NAMES potion;
1922 WAND_NAMES wand;
1923
1924 // Check to see if we care about stoning.
1925 if (getMaterial() != MATERIAL_FLESH)
1926 return false;
1927 if (hasIntrinsic(INTRINSIC_UNCHANGING))
1928 return false;
1929 if (hasIntrinsic(INTRINSIC_RESISTSTONING))
1930 return false;
1931
1932 // Flesh golem's *want* to stone.
1933 if (getDefinition() == MOB_FLESHGOLEM)
1934 return false;
1935
1936 // As do trolls.
1937 if (getDefinition() == MOB_TROLL)
1938 return false;
1939
1940 // The easiest cure is just to cast preservation
1941 if (canCastSpell(SPELL_PRESERVE))
1942 {
1943 return actionCast(SPELL_PRESERVE, 0, 0, 0);
1944 }
1945
1946 // Check if we have the unpetrify spell.
1947 if (canCastSpell(SPELL_STONETOFLESH))
1948 {
1949 // Cast stone to flesh on ourselves to prevent the transformation.
1950 return actionCast(SPELL_STONETOFLESH, 0, 0, 0);
1951 }
1952
1953 if (glb_aidefs[getAI()].useitems)
1954 {
1955 for (item = myInventory; item; item = item->getNext())
1956 {
1957 mtype = item->getMagicType();
1958 if (mtype == MAGICTYPE_POTION)
1959 {
1960 potion = (POTION_NAMES) item->getMagicClass();
1961
1962 // Quaff acid.
1963 if (potion == POTION_ACID)
1964 {
1965 return actionEat(item->getX(), item->getY());
1966 }
1967 }
1968 else if (mtype == MAGICTYPE_WAND)
1969 {
1970 wand = (WAND_NAMES) item->getMagicClass();
1971
1972 if (wand == WAND_POLYMORPH && item->getCharges() > 0)
1973 {
1974 // Polymorph ourselves!
1975 // We do not need to check for unchanging, as if we
1976 // had it, we wouldn't be concerned about stoning.
1977 return actionZap(item->getX(), item->getY(), 0, 0, 0);
1978 }
1979 }
1980 }
1981 }
1982
1983 // Last ditch resorts:
1984 if (canCastSpell(SPELL_ACIDSPLASH))
1985 {
1986 return actionCast(SPELL_ACIDSPLASH, 0, 0, 0);
1987 }
1988
1989 return false;
1990 }
1991
1992 bool
aiDoRangedAttack(int dx,int dy,int range)1993 MOB::aiDoRangedAttack(int dx, int dy, int range)
1994 {
1995 // Check for a breath attack...
1996 if (hasBreathAttack() && isBreathAttackCharged())
1997 {
1998 if (range <= getBreathRange())
1999 {
2000 // Check main damage of breath attack.
2001 ATTACK_NAMES attack;
2002 MOB *target = glbCurLevel->getMob(getX() + dx * range, getY() + dy * range);
2003
2004 attack = getBreathAttack();
2005
2006 if (!aiCanTargetResist(target,
2007 (ELEMENT_NAMES) glb_attackdefs[attack].element))
2008 return actionBreathe(dx, dy);
2009 }
2010 }
2011
2012 // Check for wand zap.
2013 if (aiDoWandAttack(dx, dy, range))
2014 return true;
2015
2016 // Check for offensive spell.
2017 if (aiDoSpellAttack(dx, dy, range))
2018 return true;
2019
2020 // Check for throwable.
2021 // We only throw with a positive range increment.
2022 if (((dx && dy) || (range > 1)) && aiDoThrownAttack(dx, dy, range))
2023 return true;
2024
2025 // Nothing to do.
2026 return false;
2027 }
2028
2029 bool
aiDoWandAttack(int dx,int dy,int range)2030 MOB::aiDoWandAttack(int dx, int dy, int range)
2031 {
2032 // Range is the desired range. Check to see if we have
2033 // anything in range. If we don't have anything to throw
2034 // at all, clear the flag.
2035 if (!glb_aidefs[getAI()].zapwands)
2036 {
2037 myAIState &= ~AI_HAS_ATTACKWAND;
2038 return false;
2039 }
2040
2041 // Abandon if no attack wands present.
2042 if (!(myAIState & AI_HAS_ATTACKWAND))
2043 return false;
2044
2045 ITEM *item;
2046 bool anygood = false;
2047
2048 for (item = myInventory; item; item = item->getNext())
2049 {
2050 if (aiIsItemZapAttack(item))
2051 {
2052 // Guestimate of wand ranges.
2053 if (item->getCharges() > 0)
2054 {
2055 anygood = true;
2056
2057 // Default range of zap attacks is 6.
2058 int rangeguess = 6;
2059 MOB *target;
2060
2061 target = glbCurLevel->getMob(getX() + dx*range,
2062 getY() + dy*range);
2063
2064 if (item->getMagicType() == MAGICTYPE_WAND)
2065 {
2066 switch (item->getMagicClass())
2067 {
2068 case WAND_SLOW:
2069 // Slow wands only have a one square effect.
2070 rangeguess = 1;
2071
2072 // Check if target is slowed.
2073 if (target && target->hasIntrinsic(INTRINSIC_SLOW))
2074 rangeguess = -1;
2075 break;
2076 case WAND_SLEEP:
2077 // Check if target is asleep.
2078 if (target && target->hasIntrinsic(INTRINSIC_ASLEEP))
2079 rangeguess = -1;
2080 break;
2081 case WAND_FIRE:
2082 if (aiCanTargetResist(target, ELEMENT_FIRE) ||
2083 aiCanTargetResist(target, ELEMENT_REFLECTIVITY))
2084 rangeguess = -1;
2085 break;
2086 case WAND_ICE:
2087 if (aiCanTargetResist(target, ELEMENT_COLD) ||
2088 aiCanTargetResist(target, ELEMENT_REFLECTIVITY))
2089 rangeguess = -1;
2090 break;
2091 }
2092 }
2093
2094 // We don't ALWAYS use the wand. Usually do, however.
2095 if (range <= rangeguess && !rand_choice(3))
2096 {
2097 // We have the one we want!
2098 return actionZap(item->getX(), item->getY(),
2099 dx, dy, 0);
2100 }
2101 }
2102 }
2103 }
2104
2105 // We could be abandoning due to range. Thus, we only clear
2106 // if nothing triggered throwable.
2107 if (!anygood)
2108 {
2109 myAIState &= ~AI_HAS_ATTACKWAND;
2110 }
2111
2112 return false;
2113 }
2114
2115 bool
aiDoSpellAttack(int dx,int dy,int range)2116 MOB::aiDoSpellAttack(int dx, int dy, int range)
2117 {
2118 // No magic points? Early exit.
2119 if (!getMP())
2120 return false;
2121
2122 // We do not want to ALWAYS cast the spells! Otherwise, we
2123 // never do a variety. We should sometimes decide to cast
2124 // no spell...
2125 // We thus use our shouldCast() function...
2126 MOB *target;
2127 target = glbCurLevel->getMob(getX() + dx*range, getY() + dy*range);
2128
2129 // Grab the soul if possible
2130 if (canCastSpell(SPELL_SOULSUCK) && mob_shouldCast())
2131 {
2132 if (target && canSense(target) &&
2133 !target->hasIntrinsic(INTRINSIC_AMNESIA))
2134 {
2135 return actionCast(SPELL_SOULSUCK, dx*range, dy*range, 0);
2136 }
2137 }
2138
2139 // If our target is one unit away & isn't aflame, set them aflame.
2140 if (range == 1 && mob_shouldCast() && canCastSpell(SPELL_STICKYFLAMES) &&
2141 !aiCanTargetResist(target, ELEMENT_FIRE))
2142 {
2143
2144 if (target && !target->hasIntrinsic(INTRINSIC_AFLAME))
2145 return actionCast(SPELL_STICKYFLAMES, dx, dy, 0);
2146 }
2147
2148 #if 0
2149 // I have retreated from allowing this. It is too close to
2150 // one-shot kills - anything less than half health can be killed
2151 // by two purple tridudes doing a fetch/disintegrate pair.
2152 if (range == 1 && mob_shouldCast() && canCastSpell(SPELL_DISINTEGRATE) &&
2153 !aiCanTargetResist(target, ELEMENT_ACID))
2154 {
2155 // Yep, I'm evil.
2156 return actionCast(SPELL_DISINTEGRATE, dx, dy, 0);
2157 }
2158 #endif
2159
2160 if (range == 1 && mob_shouldCast() && canCastSpell(SPELL_FLASH) &&
2161 target && !target->hasIntrinsic(INTRINSIC_BLIND))
2162 {
2163 return actionCast(SPELL_FLASH, dx, dy, 0);
2164 }
2165
2166 if (range == 1 && mob_shouldCast() && canCastSpell(SPELL_CHILL) &&
2167 !aiCanTargetResist(target, ELEMENT_COLD))
2168 {
2169 if (target && !target->hasIntrinsic(INTRINSIC_SLOW))
2170 return actionCast(SPELL_CHILL, dx, dy, 0);
2171 }
2172
2173 if (range == 1 && mob_shouldCast() && canCastSpell(SPELL_ACIDSPLASH) &&
2174 !aiCanTargetResist(target, ELEMENT_ACID))
2175 {
2176 return actionCast(SPELL_ACIDSPLASH, dx, dy, 0);
2177 }
2178
2179 // Spells in order of coolness.
2180 if (canCastSpell(SPELL_CHAINLIGHTNING) && mob_shouldCast(3) &&
2181 !aiCanTargetResist(target, ELEMENT_SHOCK))
2182 {
2183 return actionCast(SPELL_CHAINLIGHTNING, dx, dy, 0);
2184 }
2185
2186 if (canCastSpell(SPELL_LIGHTNINGBOLT) && mob_shouldCast() &&
2187 !aiCanTargetResist(target, ELEMENT_SHOCK))
2188 {
2189 return actionCast(SPELL_LIGHTNINGBOLT, dx, dy, 0);
2190 }
2191
2192 // Don't blow ourselves up with the fireball.
2193 if ((range > 1 || hasIntrinsic(INTRINSIC_RESISTFIRE))
2194 && canCastSpell(SPELL_FIREBALL) && mob_shouldCast() &&
2195 !aiCanTargetResist(target, ELEMENT_FIRE))
2196 {
2197 return actionCast(SPELL_FIREBALL, dx, dy, 0);
2198 }
2199
2200 if (canCastSpell(SPELL_POISONBOLT) && mob_shouldCast() &&
2201 !aiCanTargetResist(target, ELEMENT_POISON))
2202 {
2203 return actionCast(SPELL_POISONBOLT, dx, dy, 0);
2204 }
2205
2206 // Good old frost bolt.
2207 if (canCastSpell(SPELL_FROSTBOLT) && mob_shouldCast() &&
2208 !aiCanTargetResist(target, ELEMENT_COLD))
2209 {
2210 return actionCast(SPELL_FROSTBOLT, dx, dy, 0);
2211 }
2212
2213 // Magic missile, the stand by of mages.
2214 if (canCastSpell(SPELL_MAGICMISSILE) && mob_shouldCast() &&
2215 !aiCanTargetResist(target, ELEMENT_PHYSICAL))
2216 {
2217 return actionCast(SPELL_MAGICMISSILE, dx, dy, 0);
2218 }
2219
2220 if (range == 1 && mob_shouldCast() && canCastSpell(SPELL_SPARK) &&
2221 !aiCanTargetResist(target, ELEMENT_SHOCK))
2222 {
2223 return actionCast(SPELL_SPARK, 0, 0, 0);
2224 }
2225
2226 if (range == 1 && mob_shouldCast() && canCastSpell(SPELL_FORCEBOLT) &&
2227 !aiCanTargetResist(target, ELEMENT_PHYSICAL))
2228 {
2229 return actionCast(SPELL_FORCEBOLT, dx, dy, 0);
2230 }
2231
2232 // No offensive spells.
2233 return false;
2234 }
2235
2236 bool
aiDoThrownAttack(int dx,int dy,int range)2237 MOB::aiDoThrownAttack(int dx, int dy, int range)
2238 {
2239 // Range is the desired range. Check to see if we have
2240 // anything in range. If we don't have anything to throw
2241 // at all, clear the flag.
2242 if (!glb_aidefs[getAI()].throwitems)
2243 {
2244 myAIState &= ~AI_HAS_THROWABLE;
2245 return false;
2246 }
2247
2248 // Abandon if nothing is throwable in our inventory.
2249 if (!(myAIState & AI_HAS_THROWABLE))
2250 return false;
2251
2252 ITEM *item;
2253 bool anygood = false;
2254 MOB *target = glbCurLevel->getMob(getX() + dx*range, getY() + dy * range);
2255
2256 for (item = myInventory; item; item = item->getNext())
2257 {
2258 // If the item is equipped, we can't throw it (PC can't so,
2259 // why should we be able to?)
2260 if (!item->getX())
2261 continue;
2262
2263 if (aiIsItemThrowable(item, target))
2264 {
2265 anygood = true;
2266
2267 const ATTACK_DEF *attack;
2268
2269 attack = item->getThrownAttack();
2270 if (attack->range >= range)
2271 {
2272 // We have the one we want!
2273 return actionThrow(item->getX(), item->getY(),
2274 dx, dy, 0);
2275 }
2276 }
2277 }
2278
2279 // We could be abandoning due to range. Thus, we only clear
2280 // if nothing triggered throwable.
2281 if (!anygood)
2282 {
2283 myAIState &= ~AI_HAS_THROWABLE;
2284 }
2285
2286 return false;
2287 }
2288
2289 bool
aiIsItemCooler(ITEM * item,ITEMSLOT_NAMES slot)2290 MOB::aiIsItemCooler(ITEM *item, ITEMSLOT_NAMES slot)
2291 {
2292 ITEM *old;
2293 bool defresult = false;
2294
2295 // If the item is silver and we are vulnerable to silver,
2296 // the item is never cooler.
2297 if (item->getMaterial() == MATERIAL_SILVER)
2298 {
2299 if (hasIntrinsic(INTRINSIC_VULNSILVER))
2300 return false;
2301 }
2302
2303 old = getEquippedItem(slot);
2304
2305 // Something is better than nothing.
2306 if (!old)
2307 defresult = true;
2308
2309 // Smarts:
2310 // FOOL: Equips whatever is first.
2311 // HUMAN: Picks best AC.
2312 // DRAGON: Picks best effect.
2313
2314 // Fools never give up their first pick.
2315 if (getSmarts() <= SMARTS_FOOL)
2316 return defresult;
2317
2318 switch (slot)
2319 {
2320 case ITEMSLOT_HEAD:
2321 case ITEMSLOT_LHAND:
2322 case ITEMSLOT_BODY:
2323 case ITEMSLOT_FEET:
2324 {
2325 // Pick best AC.
2326 int newac, oldac;
2327
2328 newac = item->getAC();
2329 oldac = old ? old->getAC() : -1;
2330 if (newac > oldac)
2331 {
2332 // Go for swap.
2333 return true;
2334 }
2335 // Must be strictly better.
2336 return defresult;
2337 }
2338
2339 // Pick best type, so long as smart enough.
2340 case ITEMSLOT_LRING:
2341 case ITEMSLOT_RRING:
2342 case ITEMSLOT_AMULET:
2343 if (getSmarts() <= SMARTS_HUMAN)
2344 {
2345 // Never trade up, no idea what it is.
2346 return defresult;
2347 }
2348 else
2349 {
2350 int oldcool, newcool;
2351
2352 newcool = item->getCoolness();
2353 oldcool = old ? old->getCoolness() : 0;
2354
2355 if (!newcool)
2356 {
2357 // Never trade to this.
2358 return false;
2359 }
2360 else if (newcool > oldcool)
2361 return true;
2362
2363 return defresult;
2364 }
2365
2366 case ITEMSLOT_RHAND:
2367 {
2368 // Weapon: Compare damages.
2369 int newdam, olddam;
2370
2371 newdam = item->calcAverageDamage();
2372 olddam = old ? old->calcAverageDamage() : -1;
2373 if (newdam > olddam)
2374 return true;
2375 else
2376 return defresult;
2377 }
2378
2379 case NUM_ITEMSLOTS:
2380 UT_ASSERT(!"BAD SLOT NAME");
2381 break;
2382 }
2383
2384 // No idea? Then do nothing!
2385 return defresult;
2386 }
2387
2388 bool
aiIsItemThrowable(ITEM * item,MOB * target) const2389 MOB::aiIsItemThrowable(ITEM *item, MOB *target) const
2390 {
2391 ATTACK_NAMES attack;
2392
2393 attack = item->getThrownAttackName();
2394 // We never really want to throw rings.
2395 if (attack == ATTACK_MISTHROWN || attack == ATTACK_NONE ||
2396 attack == ATTACK_RINGTHROWN)
2397 return false;
2398
2399 // Until we fix launcher ai, we disallow anything that needs
2400 // a special launcher.
2401 if (item->requiresLauncher())
2402 return false;
2403
2404 // Potions get this far, but we don't want to throw healing
2405 // potions at the avatar!
2406 if (item->getMagicType() == MAGICTYPE_POTION)
2407 {
2408 if (!glb_potiondefs[item->getMagicClass()].isgrenade)
2409 return false;
2410
2411 if (aiCanTargetResist(target,
2412 (ELEMENT_NAMES)glb_potiondefs[item->getMagicClass()].grenadeelement))
2413 return false;
2414 }
2415
2416 return true;
2417 }
2418
2419 bool
aiIsItemZapAttack(ITEM * item) const2420 MOB::aiIsItemZapAttack(ITEM *item) const
2421 {
2422 MAGICTYPE_NAMES mtype;
2423
2424 mtype = item->getMagicType();
2425 if (mtype != MAGICTYPE_WAND)
2426 {
2427 return false;
2428 }
2429
2430 WAND_NAMES wand;
2431
2432 wand = (WAND_NAMES) item->getMagicClass();
2433 switch (wand)
2434 {
2435 case WAND_FIRE:
2436 case WAND_ICE:
2437 case WAND_SLEEP:
2438 case WAND_SLOW:
2439 // These can be used as offensive weapons.
2440 return true;
2441
2442 case WAND_SPEED:
2443 case WAND_LIGHT:
2444 case WAND_NOTHING:
2445 case WAND_CREATETRAP:
2446 case WAND_TELEPORT:
2447 case WAND_INVISIBLE:
2448 case WAND_DIGGING: // Technically....
2449 case WAND_CREATEMONSTER:
2450 case WAND_POLYMORPH:
2451 return false;
2452
2453 case NUM_WANDS:
2454 UT_ASSERT(!"INVALID WAND TYPE");
2455 break;
2456 }
2457 return false;
2458 }
2459
2460 bool
aiIsPoisoned() const2461 MOB::aiIsPoisoned() const
2462 {
2463 int i;
2464 bool haspoison = false;
2465
2466 // If we can resist poison, we don't consider ourselves poisoned.
2467 // This also means gods won't waste piety curing you if you resist.
2468 // This could be a bad thing if the final damage would kill the
2469 // mob and they could have killed. Likely the use is ready to
2470 // reapply the poison anyways, though.
2471 if (hasIntrinsic(INTRINSIC_RESISTPOISON))
2472 {
2473 return false;
2474 }
2475
2476 for (i = 0; i < NUM_POISONS; i++)
2477 {
2478 if (hasIntrinsic((INTRINSIC_NAMES) glb_poisondefs[i].intrinsic))
2479 {
2480 haspoison = true;
2481 }
2482 }
2483
2484 return haspoison;
2485 }
2486
2487 bool
aiWillOpenDoors() const2488 MOB::aiWillOpenDoors() const
2489 {
2490 return glb_aidefs[getAI()].opendoors;
2491 }
2492