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