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:        action.cpp ( POWDER Library, C++ )
11  *
12  * COMMENTS:
13  *	This file is responsible for implementing all the ui-independent
14  *	actions that a MOB can perform.  These actions should encompass
15  *	any action which the Player can perform.  Their arguments should
16  *	fully qualify the players choice (Ie: which item, what direction,
17  *	etc).
18  *
19  *	The theory is that if the actions are properly abstracted, it
20  *	will be equally easy to perform them from the AI routines as
21  *	from the User Interface routines.
22  *
23  *	Note that most actions are implicitly stacked.  actionClimb
24  *	will resolve itself to actionClimbUp or actionClimbDown depending
25  *	on the context.
26  */
27 
28 #include "mygba.h"
29 
30 #include <stdio.h>
31 #include <ctype.h>
32 #include "assert.h"
33 #include "artifact.h"
34 #include "creature.h"
35 #include "glbdef.h"
36 #include "rand.h"
37 #include "map.h"
38 #include "gfxengine.h"
39 #include "msg.h"
40 #include "grammar.h"
41 #include "item.h"
42 #include "itemstack.h"
43 #include "intrinsic.h"
44 #include "control.h"
45 #include "victory.h"
46 #include "piety.h"
47 #include "encyc_support.h"
48 #include "stylus.h"
49 
50 void writeGlobalActionBar(bool useemptyslot);
51 void hideSideActionBar();
52 
53 extern bool glbSuppressAutoClimb;
54 extern void attemptunlock();
55 
56 //
57 // ACTIONS
58 //
59 
60 bool
actionBump(int dx,int dy)61 MOB::actionBump(int dx, int dy)
62 {
63     int		nx, ny;
64 
65     nx = getX() + dx;
66     ny = getY() + dy;
67 
68     // Check if a monster is here.
69     // Is it hostile?  Kill!
70     // Is it friendly?  Shove.
71     // Is it neutral?  Kill!
72     MOB			*mob;
73     ITEM		*item;
74     mob = glbCurLevel->getMob(nx, ny);
75     if (mob == this)
76 	mob = 0;
77 
78     // This is very fucked up.
79     // It should be rewritten by someone not drunk.
80     // This has now been rewritten by someone not drunk.  It doesn't
81     // necessarily work any better.
82     if (!mob && getSize() >= SIZE_GARGANTUAN)
83     {
84 	if (dx)
85 	{
86 	    if (dx < 0)
87 		mob = glbCurLevel->getMob(nx, ny+1);
88 	    else
89 	    {
90 		mob = glbCurLevel->getMob(nx+1, ny);
91 		if (!mob)
92 		    mob = glbCurLevel->getMob(nx+1, ny+1);
93 	    }
94 	}
95 	else
96 	{
97 	    if (dy < 0)
98 		mob = glbCurLevel->getMob(nx+1, ny);
99 	    else
100 	    {
101 		mob = glbCurLevel->getMob(nx, ny+1);
102 		if (!mob)
103 		    mob = glbCurLevel->getMob(nx+1, ny+1);
104 	    }
105 	}
106 	if (mob == this)
107 	    mob = 0;
108 	if (mob)
109 	{
110 	    dx = mob->getX() - getX();
111 	    dy = mob->getY() - getY();
112 	}
113     }
114     if (mob)
115     {
116 	if (getAttitude(mob) == ATTITUDE_HOSTILE)
117 	{
118 	    // Attack!
119 	    return actionAttack(dx, dy);
120 	}
121 	else if (getAttitude(mob) == ATTITUDE_NEUTRAL)
122 	{
123 	    // Attack!  THis is bloodthirsty game, after all.
124 	    // Only the avatar is bloodthirsty.  Other people
125 	    // are nice and kind.
126 	    if (isAvatar())
127 		return actionAttack(dx, dy);
128 	    else
129 		return false;		// No bump, no swap.
130 	}
131 	else if (getAttitude(mob) == ATTITUDE_FRIENDLY)
132 	{
133 	    // Check if the friendship is reciprocated.  If so,
134 	    // we can swap position.
135 	    if (mob->getAttitude(this) == ATTITUDE_FRIENDLY)
136 	    {
137 		// swap.
138 		// Okay, this is extradorinarily frustrating!
139 		// Dancing grid bugs: No more!
140 		// Only the avatar can swap by defualt (if he is
141 		// polyied into a critter which is swappable, etc)
142 		if (isAvatar())
143 		{
144 		    // Check if we are to talk
145 		    if (mob->isTalkative())
146 			return actionTalk(dx, dy);
147 		    else
148 			return actionSwap(dx, dy);
149 		}
150 		else
151 		    return false;
152 	    }
153 	    else
154 	    {
155 		// Nothing to do.
156 		return false;
157 	    }
158 	}
159     }
160 
161     // Check if creature can physically move in that direction...
162     if (canMoveDelta(dx, dy, false))
163     {
164 	// We can physically move that way.  However, this is no
165 	// guarantee of freedom.
166 
167 	// Check for item here.
168 	item = glbCurLevel->getItem(nx, ny);
169 	if (item && !item->isPassable())
170 	{
171 	    // Can we push it?
172 	    // Boulder is in the way, try and push.
173 	    return actionPush(dx, dy);
174 	}
175 
176 	// An empty, valid square.
177 	// Will it hurt us?  Are we smart enough to know that?
178 
179 	// All clear!  Move with no embellishments.
180 	return actionWalk(dx, dy);
181     }
182     else
183     {
184 	// Find out why.  Perhaps the creature has an alternative action?
185 	if (nx < 0 || nx >= MAP_WIDTH ||
186 	    ny < 0 || ny >= MAP_HEIGHT)
187 	{
188 	    // Simple enough reason, abort!
189 	    // Add a funny message depending on direction
190 	    if (isAvatar())
191 	    {
192 		if (dx)
193 		{
194 		    formatAndReport("%U <hear> the sound of someone mashing buttons.");
195 		}
196 		else
197 		{
198 		    formatAndReport("Do not go there!  You will break the backlight!");
199 		}
200 	    }
201 	    return false;
202 	}
203 
204 	SQUARE_NAMES		tile;
205 
206 	tile = glbCurLevel->getTile(nx, ny);
207 	switch (tile)
208 	{
209 	    case SQUARE_BLOCKEDDOOR:
210 		// Only avatar can open these
211 		if (!isAvatar())
212 		    break;
213 		// FALL THROUGH
214 
215 	    case SQUARE_DOOR:
216 	    case SQUARE_MAGICDOOR:
217 		// If we are smart enough, open it.
218 		if (getSmarts() >= SMARTS_FOOL)
219 		{
220 		    return actionOpen(dx, dy);
221 		}
222 		break;
223 
224 	    default:
225 		break;
226 	}
227 
228 	// Check for the ability to dig...
229 	if (hasIntrinsic(INTRINSIC_DIG))
230 	{
231 	    return actionDig(dx, dy, 0, 1, false);
232 	}
233 
234 	// Report nothing, just fail.
235 	return false;
236     }
237 }
238 
239 bool
actionTalk(int dx,int dy)240 MOB::actionTalk(int dx, int dy)
241 {
242     applyConfusion(dx, dy);
243 
244     MOB		*mob;
245     mob = glbCurLevel->getMob(getX()+dx, getY()+dy);
246 
247     if (!mob)
248     {
249 	formatAndReport("%U <talk> into empty air.");
250 	return false;
251     }
252     if (mob == this)
253     {
254 	formatAndReport("%U <talk> to %A.");
255 	return false;
256     }
257     if (!mob->isTalkative())
258     {
259 	formatAndReport("%U <talk> to %MA.", mob);
260 	formatAndReport("%MU does not answer.", mob);
261 	return false;
262     }
263 
264     // Well, for now it means the village elder!
265     if (!isAvatar())
266 	return false;
267 
268     // No talking to the elder!
269     if (glbStressTest)
270 	return false;
271 
272     encyc_viewentry("TALK", TALK_ELDER);
273     if (gfx_yesnomenu("Leave for the feast?"))
274     {
275 	glbVictoryScreen(true, 0, this, 0);
276 	glbHasAlreadyWon = true;
277 
278 	// Remove the avatar and elder to the feast
279 	glbCurLevel->unregisterMob(this);
280 	glbCurLevel->unregisterMob(mob);
281 
282 	setAvatar(0);
283 
284 	delete mob;
285 	delete this;
286 
287 	// required as we're dead
288 	return false;
289     }
290     return false;
291 }
292 
293 bool
actionBreathe(int dx,int dy)294 MOB::actionBreathe(int dx, int dy)
295 {
296     ATTACK_NAMES		attack;
297     BUF	buf;
298 
299     applyConfusion(dx, dy);
300 
301     attack = getBreathAttack();
302 
303     if (attack == ATTACK_NONE)
304     {
305 	if (hasIntrinsic(INTRINSIC_NOBREATH))
306 	    formatAndReport("%U <do> not need to breathe.");
307 	else if (hasIntrinsic(INTRINSIC_STRANGLE))
308 	    formatAndReport("%U <be> unable to breathe.");
309 	else if (hasIntrinsic(INTRINSIC_SUBMERGED))
310 	    formatAndReport("%U <choke>.");
311 	else
312 	    formatAndReport("%U <breathe> heavily.");
313 	return true;
314     }
315 
316     if (!dx && !dy)
317     {
318 	// Breathing on self.
319 	formatAndReport("%U <check> %r breath.");
320 	return true;
321     }
322 
323     // Check if we are out of breath...
324     if (hasIntrinsic(INTRINSIC_TIRED))
325     {
326 	formatAndReport("%U <belch>");
327 	return true;
328     }
329 
330     buf = formatToString("%U <breathe> %B1.",
331 		    this, 0, 0, 0,
332 		    getBreathSubstance(), 0);
333     reportMessage(buf);
334 
335     // Breathing fire may kill ourselves, so we must set the
336     // intrinsic prior to sending the ray.
337     // After this, we want to time out for the breath delay time...
338     setTimedIntrinsic(this, INTRINSIC_TIRED,
339 		      rand_dice(glb_mobdefs[myDefinition].breathdelay));
340 
341     // Now, run the attack with the breath...
342     MOBREF		myself;
343 
344     myself.setMob(this);
345 
346     ourEffectAttack = getBreathAttack();
347     glbCurLevel->fireRay(getX(), getY(),
348 		         dx, dy,
349 			 getBreathRange(),
350 			 MOVE_STD_FLY,
351 			 glb_attackdefs[ourEffectAttack].reflect,
352 			 areaAttackCBStatic,
353 			 &myself);
354     return true;
355 }
356 
357 bool
actionJump(int dx,int dy)358 MOB::actionJump(int dx, int dy)
359 {
360     int		nx, ny, mx, my;
361     int		tile;
362     bool	leapattack = false;
363     bool	singlestep = false;
364 
365     if (!hasIntrinsic(INTRINSIC_JUMP))
366     {
367 	formatAndReport("%U cannot jump very far.");
368 	return false;
369     }
370 
371     // If we are standing on liquid, we can only jump if we have water walk
372     // or we don't have enough traction.
373     tile = glbCurLevel->getTile(getX(), getY());
374     if (!(glb_squaredefs[tile].movetype & MOVE_WALK))
375     {
376 	if (!hasIntrinsic(INTRINSIC_WATERWALK) ||
377 	    !(glb_squaredefs[tile].movetype & MOVE_SWIM))
378 	{
379 	    // The ground isn't solid enough!
380 	    formatAndReport("%U <lack> traction to jump.");
381 	    return false;
382 	}
383     }
384     else if (hasIntrinsic(INTRINSIC_SUBMERGED))
385     {
386 	// We are buried alive.
387 	formatAndReport("%U <lack> room to jump.");
388 	return false;
389     }
390 
391     if (glb_squaredefs[tile].isstars && !defn().spacewalk)
392     {
393 	// Can't jump in space
394 	formatAndReport("%U <fail> to brace yourself against vacuum.");
395 	return false;
396     }
397 
398     if (hasIntrinsic(INTRINSIC_INPIT))
399     {
400 	// Jumping out of a pit only moves you a single square.
401 	singlestep = true;
402     }
403 
404     applyConfusion(dx, dy);
405 
406     // Verify the square they will jump over is passable.
407     nx = getX() + dx;
408     ny = getY() + dy;
409     mx = nx;
410     my = ny;
411 
412     tile = glbCurLevel->getTile(nx, ny);
413     if (!(glb_squaredefs[tile].movetype & MOVE_STD_FLY))
414     {
415 	// You try and jump through a wall or something.
416 	formatAndReport("%U <prepare> to jump, then %U <think> better of it.");
417 	return false;
418     }
419 
420     if (!singlestep)
421     {
422 	nx = nx + dx;
423 	ny = ny + dy;
424     }
425 
426     // Note we want to not jump to unmapped squares.  However, that is
427     // avatar only.  The only time isLit does not suffice is if we
428     // are jumping blind.  Hence we prohibit blind jumping.
429     if (!canMove(nx, ny, true) ||
430 	!glbCurLevel->isLit(nx, ny) ||
431 	hasIntrinsic(INTRINSIC_BLIND))
432     {
433 	formatAndReport("%U <balk> at the leap!");
434 	return false;
435     }
436 
437     // Prohibit you jumping onto a creature if a creature is in the way.
438     if (glbCurLevel->getMob(nx, ny) && (glbCurLevel->getMob(nx, ny) != this))
439     {
440 	// Check if we have the leap attack skill.
441 	if (!hasSkill(SKILL_LEAPATTACK) ||
442 	    getSize() >= SIZE_GARGANTUAN ||
443 	    glbCurLevel->getMob(nx, ny)->getSize() >= SIZE_GARGANTUAN)
444 	{
445 	    formatAndReport("%U <balk> at the leap!");
446 	    return false;
447 	}
448 
449 	// Do a leap attack!
450 	leapattack = true;
451     }
452 
453     // Report that we are leaping.
454     formatAndReport("%U <leap>.");
455 
456     // Clearly jumping should noise your feet.
457     makeEquipNoise(ITEMSLOT_FEET);
458 
459 
460     if (leapattack)
461     {
462 	// Push the creature at our destination out of the way.
463 	// First knock back the intermediate creature into our own square.
464 	if (glbCurLevel->getMob(mx, my))
465 	{
466 	    // Allow us to overwrite our own position as we'll
467 	    // be moving shortly anyways.
468 	    glbCurLevel->knockbackMob(mx, my, -dx, -dy, true, true);
469 	}
470 	// Next, knockback our target creature into the middle square.
471 	glbCurLevel->knockbackMob(nx, ny, -dx, -dy, true);
472     }
473 
474     // Move!  We may die here, hence the test.
475     if (!move(nx, ny))
476 	return true;
477 
478     // Attack the vacated creature if still alive.
479     if (leapattack && glbCurLevel->getMob(mx, my))
480     {
481 	// Note that because we attack backwards I think you can
482 	// trigger a charge by running away and then leaping back...
483 	// Most curious... I wonder if anyone will notice, and if they
484 	// do, consider it a bug?
485 	actionAttack(-dx, -dy, 1);
486     }
487 
488     // Report what we stepped on...
489     // We recheck isAvatar() here as it will go false if this is deleted.
490     if (isAvatar())
491     {
492 	glbCurLevel->describeSquare(getX(), getY(),
493 				    hasIntrinsic(INTRINSIC_BLIND),
494 				    false);
495     }
496 
497     return true;
498 }
499 
500 bool
actionWalk(int dx,int dy,bool spacewalk)501 MOB::actionWalk(int dx, int dy, bool spacewalk)
502 {
503     int		nx, ny;
504     ITEM	*item;
505     MOB		*mob;
506 
507     applyConfusion(dx, dy);
508 
509     nx = getX() + dx;
510     ny = getY() + dy;
511 
512     if (!canMoveDelta(dx, dy, true))
513 	return false;
514 
515     // If there is a monster, can't move.
516     mob = glbCurLevel->getMob(nx, ny);
517     if (mob && (mob != this))
518     {
519 	// Check to see if we can swap
520 	if (getAttitude(mob) == ATTITUDE_FRIENDLY)
521 	{
522 	    // Check if the friendship is reciprocated.  If so,
523 	    // we can swap position.
524 	    if (mob->getAttitude(this) == ATTITUDE_FRIENDLY)
525 	    {
526 		// swap.
527 		// Okay, this is extradorinarily frustrating!
528 		// Dancing grid bugs: No more!
529 		// Only the avatar can swap by defualt (if he is
530 		// polyied into a critter which is swappable, etc)
531 		if (isAvatar())
532 		    return actionSwap(dx, dy);
533 	    }
534 	}
535 	return false;
536     }
537 
538     // If there is an item, and it is not steppable, no move.
539     item = glbCurLevel->getItem(nx, ny);
540     if (item && !item->isPassable())
541 	return false;
542 
543     // If we are on a star tile, we can only move if we can space
544     // walk
545     if (!spacewalk && !defn().spacewalk)
546     {
547 	SQUARE_NAMES		oldtile;
548 
549 	oldtile = glbCurLevel->getTile(getX(), getY());
550 	if (glb_squaredefs[oldtile].isstars)
551 	{
552 	    formatAndReport("%U <flail> against unyielding vacuum!");
553 	    return true;
554 	}
555     }
556 
557     // If we are in a pit, we first must climb out of it.
558     if (hasIntrinsic(INTRINSIC_INPIT))
559     {
560 	// The user must first climb out of the pit.  Thus, we try
561 	// to climb:
562 	return actionClimbUp();
563     }
564 
565     if (hasIntrinsic(INTRINSIC_INTREE))
566     {
567 	SQUARE_NAMES		newtile;
568 	// The user must first climb out of the tree.  Thus, we try
569 	// to climb:
570 	// We are allowed to travel from tree to tree, however.
571 	newtile = glbCurLevel->getTile(nx, ny);
572 	if (newtile != SQUARE_FOREST &&
573 	    newtile != SQUARE_FORESTFIRE)
574 	    return actionClimbDown();
575     }
576 
577     // If we are submerged, and our destination isn't something
578     // you can be submerged in, we must first climb out.
579     if (hasIntrinsic(INTRINSIC_SUBMERGED))
580     {
581 	SQUARE_NAMES		newtile;
582 
583 	newtile = glbCurLevel->getTile(nx, ny);
584 	if (newtile != SQUARE_WATER &&
585 	    newtile != SQUARE_LAVA &&
586 	    newtile != SQUARE_ACID)
587 	{
588 	    return actionClimbUp();
589 	}
590     }
591 
592     // Because we move, make any noise that stems from our
593     // footwear.
594     makeEquipNoise(ITEMSLOT_FEET);
595 
596     // Move.
597     if (!move(nx, ny))
598 	return true;
599 
600     // Report what we stepped on...
601     if (isAvatar())
602     {
603 	// Store the delta.
604 	ourAvatarDx = dx;
605 	ourAvatarDy = dy;
606 	ourAvatarMoveOld = false;
607 	glbCurLevel->describeSquare(getX(), getY(),
608 				    hasIntrinsic(INTRINSIC_BLIND),
609 				    false);
610     }
611 
612     return true;
613 }
614 
615 bool
actionPush(int dx,int dy)616 MOB::actionPush(int dx, int dy)
617 {
618     int		 nx, ny, px, py;
619     MOB		*mob;
620     ITEM	*item, *blockitem;
621     BUF		 buf;
622 
623     applyConfusion(dx, dy);
624 
625     nx = getX() + dx;
626     ny = getY() + dy;
627 
628     mob = glbCurLevel->getMob(nx, ny);
629     if (mob)
630     {
631 	// TODO: Aggro the victim?
632 	// TODO: Strength/size check, and allow pushing into water?
633 	formatAndReport("%U <push> %MU.", mob);
634 	return true;
635     }
636 
637     item = glbCurLevel->getItem(nx, ny);
638 
639     if (item)
640     {
641 	// Check to see if there is room.
642 	px = nx + dx;
643 	py = ny + dy;
644 	if (px < 0 || px >= MAP_WIDTH ||
645 	    py < 0 || py >= MAP_HEIGHT ||
646 	    !glbCurLevel->canMove(px, py, MOVE_STD_FLY)
647 	    )
648 	{
649 	    // Can't move as no room for the item.
650 	    formatAndReport("%U <strain>, but %IU <I:do> not budge.", item);
651 	    return true;
652 	}
653 
654 	mob = glbCurLevel->getMob(px, py);
655 	if (mob)
656 	{
657 	    formatAndReportExplicit("%IU <I:be> blocked by %MU.", mob, item);
658 	    return true;
659 	}
660 
661 	// Determine if it is blocked by another item.
662 	blockitem = glbCurLevel->getItem(px, py);
663 	if (blockitem && !blockitem->isPassable())
664 	{
665 	    buf = MOB::formatToString("%U <be> blocked by %IU.", 0, item, 0, blockitem);
666 	    reportMessage(buf);
667 	    return true;
668 	}
669 
670 	// We report prior to moving as moving may cause it to be deleted,
671 	// for example if it falls into a hole.
672 	formatAndReport("%U <push> %IU.", item);
673 
674 	// Move the item.
675 	glbCurLevel->dropItem(item);
676 	glbCurLevel->acquireItem(item, px, py, this);
677 
678 	return true;
679     }
680 
681     // Reduced to pushing map features.
682     // TODO: Pushing doors, walls, etc.
683     formatAndReport("%U <push> thin air.");
684     return false;
685 }
686 
687 bool
actionAttack(int dx,int dy,int multiplierbonus)688 MOB::actionAttack(int dx, int dy, int multiplierbonus)
689 {
690     int		 nx, ny;
691     MOB		*mob, *thismob;
692     ITEM	*weapon;
693     BUF		 buf;
694     int		 ox, oy, pdx, pdy;
695     bool	 didhit = false;
696     bool	 firstweapon = true;
697     bool	 improvised = true;
698     bool	 ischarge = false;
699 
700     applyConfusionNoSelf(dx, dy);
701 
702     ox = getX();
703     oy = getY();
704     nx = getX() + dx;
705     ny = getY() + dy;
706 
707     mob = glbCurLevel->getMob(nx, ny);
708 
709     if (!mob)
710     {
711 	formatAndReport("%U <swing> wildly at the air!");
712 	return true;
713     }
714 
715     getLastMoveDirection(pdx, pdy);
716     // See if this is a charge
717     if (pdx == dx && pdy == dy && (dx || dy))
718     {
719 	if (hasSkill(SKILL_CHARGE))
720 	    ischarge = true;
721     }
722 
723     if (ischarge)
724     {
725 	const char *chargemsg[] =
726 	{
727 	    "%U <charge>!",
728 	    "%U <yell> a warcry!",
729 	    "%U <brace> your weapon!",
730 	    "%U <rush> into battle!",
731 	    0
732 	};
733 	formatAndReport(rand_string(chargemsg));
734 
735 	multiplierbonus++;
736     }
737 
738     // Apply damage to mob.
739     const ATTACK_DEF		*attack;
740 
741     // Determining attack is simple.  If we have anything in our right
742     // hand, we use that.  Otherwise, we use our native attack
743     // from our definition file.
744     weapon = getEquippedItem(ITEMSLOT_RHAND);
745     if (weapon)
746     {
747 	attack = weapon->getAttack();
748 
749 	// We have a chance of iding a weapon any time it is used.
750 	if (isAvatar())
751 	{
752 	    if (!weapon->isKnownEnchant())
753 	    {
754 		int		skill, chance;
755 
756 		skill = getWeaponSkillLevel(weapon, ATTACKSTYLE_MELEE);
757 		chance = 5;
758 		if (skill >= 1)
759 		    chance += 5;
760 		if (skill >= 2)
761 		    chance += 10;
762 
763 		// About 20 hits to figure it out with 0 skill, 10 for
764 		// one level skill, and 5 for 2 levels skill.
765 		if (rand_chance(chance))
766 		{
767 		    weapon->markEnchantKnown();
768 		    buf = MOB::formatToString("%U <know> %r %Iu better.  ", this, 0, 0, weapon);
769 		    // This is important enough to be broadcast.
770 		    msg_announce(gram_capitalize(buf));
771 		}
772 	    }
773 	}
774     }
775     else
776 	attack = &glb_attackdefs[glb_mobdefs[myDefinition].attack];
777 
778     thismob = this;
779 
780     while (attack)
781     {
782 	mob->receiveAttack(attack, thismob, weapon, 0,
783 			    ATTACKSTYLE_MELEE, &didhit, multiplierbonus);
784 
785 	// Refetch the mob as it may have died, teleported, or what
786 	// have you...
787 	if (mob != glbCurLevel->getMob(nx, ny))
788 	    break;
789 
790 	// Refetch ourselves, as we may have died in the attack.
791 	if (thismob != glbCurLevel->getMob(ox, oy))
792 	    break;
793 
794 	if (attack->nextattack != ATTACK_NONE)
795 	    attack = &glb_attackdefs[attack->nextattack];
796 	else
797 	    attack = 0;
798 
799 	// If we have run out of attacks with this weapon, see if
800 	// we should break it!
801 	if (!attack && weapon && didhit &&
802 	    (thismob == glbCurLevel->getMob(ox, oy)))
803 	{
804 	    bool		didbreak;
805 
806 	    didbreak = weapon->doBreak(10, this, this, glbCurLevel, nx, ny);
807 
808 	    // Don't want to use special abilities of broken weapons :>
809 	    if (didbreak)
810 		weapon = 0;
811 	}
812 
813 	// If we have run out of attacks with a weapon, see if we
814 	// should invoke its special skill.
815 	if (!attack && weapon && didhit &&
816 	    (thismob == glbCurLevel->getMob(ox, oy)) &&
817 	    (mob == glbCurLevel->getMob(nx, ny)))
818 	{
819 	    // 10% chance of power move.
820 	    if (skillProc(weapon->getSpecialSkill()) && hasSkill(weapon->getSpecialSkill()))
821 	    {
822 		// Perform special attack.
823 		switch (weapon->getSpecialSkill())
824 		{
825 		    case SKILL_WEAPON_BLEEDINGWOUND:
826 			if (!mob->isBloodless())
827 			{
828 			    formatAndReport("%U <stab> deeply!");
829 			    mob->setTimedIntrinsic(thismob, INTRINSIC_BLEED, rand_range(4,6));
830 			}
831 			break;
832 
833 		    case SKILL_WEAPON_DISARM:
834 		    {
835 			weapon = mob->getEquippedItem(ITEMSLOT_RHAND);
836 			if (weapon)
837 			{
838 			    const char *slotname = mob->getSlotName(ITEMSLOT_RHAND);
839 			    formatAndReportExplicit(
840 					"%U <knock> %IU from %Mr %B1!",
841 					mob, weapon,
842 					(slotname ? slotname : "grasp"));
843 			    weapon = mob->dropItem(weapon->getX(), weapon->getY());
844 			    UT_ASSERT(weapon != 0);
845 			    if (weapon)
846 			    {
847 				glbCurLevel->acquireItem(weapon, mob->getX(), mob->getY(), this);
848 
849 			    }
850 			}
851 			else
852 			{
853 			    // Weaponless creaturs are thrown
854 			    // off balance.
855 			    formatAndReport("%U <trip> %MU.", mob);
856 			    mob->setTimedIntrinsic(thismob, INTRINSIC_OFFBALANCE, 3);
857 			}
858 			break;
859 		    }
860 
861 		    case SKILL_WEAPON_KNOCKOUT:
862 			// Only can knock unconscious conscious critters :>
863 			if (mob->getSmarts() > SMARTS_NONE &&
864 			    !mob->hasIntrinsic(INTRINSIC_RESISTSLEEP) &&
865 			    !mob->hasIntrinsic(INTRINSIC_ASLEEP))
866 			{
867 			    formatAndReport("%U <knock> %MU unconscious!", mob);
868 			    mob->setTimedIntrinsic(thismob, INTRINSIC_ASLEEP, rand_range(6,10));
869 			}
870 			break;
871 
872 		    case SKILL_WEAPON_STUN:
873 			// We confuse the poor critter.
874 			if (mob->getSmarts() > SMARTS_NONE)
875 			{
876 			    formatAndReport("%U <stun> %MU!", mob);
877 			    mob->setTimedIntrinsic(thismob, INTRINSIC_CONFUSED, rand_range(3,5));
878 			}
879 			break;
880 
881 		    case SKILL_WEAPON_KNOCKBACK:
882 			// Knock the target back
883 			// Infinite mass is set as this is an attack.
884 			glbCurLevel->knockbackMob(nx, ny, dx, dy, true);
885 			break;
886 
887 		    case SKILL_WEAPON_IMPALE:
888 		    {
889 			// We drive our weapon straight through the
890 			// creature, possibly impaling whoever is standing
891 			// behind!
892 			MOB		*nextvictim = 0;
893 			int		 nnx, nny;
894 
895 			nnx = nx + SIGN(dx);
896 			nny = ny + SIGN(dy);
897 
898 			if (nnx >= 0 && nnx < MAP_WIDTH &&
899 			    nny >= 0 && nny < MAP_HEIGHT)
900 			{
901 			    nextvictim = glbCurLevel->getMob(nnx, nny);
902 			}
903 
904 			if (nextvictim)
905 			{
906 			    formatAndReportExplicit("%U <drive> %r %Iu straight through %MU!", mob, weapon);
907 
908 			    // Do a second attack..
909 			    actionAttack(nnx-ox, nny-oy);
910 			}
911 			break;
912 		    }
913 
914 		    default:
915 			break;
916 		}
917 	    }
918 	}
919 
920 	if (!attack && firstweapon &&
921 	    (thismob == glbCurLevel->getMob(ox, oy)))
922 	{
923 	    // Try switching to our second weapon.
924 	    firstweapon = false;
925 	    didhit = false;
926 
927 	    // Get the second weapon
928 	    weapon = getEquippedItem(ITEMSLOT_LHAND);
929 	    if (weapon)
930 	    {
931 		// Set up our new attack.
932 		attack = weapon->getAttack();
933 	    }
934 
935 	    // See if we pass the chance.
936 	    if (!rand_chance(getSecondWeaponChance()))
937 		attack = 0;
938 
939 	    // We will now go on to the next attack..
940 	}
941 
942 	// Check for the chance to proc on some artifact equipment.
943 	if (!attack && !firstweapon && improvised &&
944 	    (thismob == glbCurLevel->getMob(ox, oy)))
945 	{
946 	    int		procchance = 5;
947 	    // See if we are eligible to proc.
948 	    if (hasSkill(SKILL_WEAPON_IMPROVISE))
949 	    {
950 		procchance = 20;
951 	    }
952 	    improvised = false;
953 
954 	    ITEMSLOT_NAMES		slot;
955 	    ITEMSLOT_NAMES		validchoice[NUM_ITEMSLOTS];
956 	    int				numchoice = 0;
957 	    ITEM			*item;
958 
959 	    // The right way would be FOREACH_ITEMSLOT followed
960 	    // by getEquippedItem, but we happen to know we have
961 	    // O(n) search inside getEquippedItem, so might as well
962 	    // make this 8 times faster.
963 	    for (item = myInventory; item; item = item->getNext())
964 	    {
965 		// Ignore not equipped
966 		if (item->getX())
967 		    continue;
968 
969 		// Cast over slot number
970 		slot = (ITEMSLOT_NAMES) item->getY();
971 
972 		switch (slot)
973 		{
974 		    // The shield is only valid if it is a
975 		    // shield.  Artifact shields can get double
976 		    // weapon proc by both this code path
977 		    // and the two handed code path.
978 		    case ITEMSLOT_LHAND:
979 		    {
980 			if (!item->isShield())
981 			    break;
982 			// FALL THROUGH
983 		    }
984 		    case ITEMSLOT_HEAD:
985 		    case ITEMSLOT_BODY:
986 		    case ITEMSLOT_RRING:
987 		    case ITEMSLOT_LRING:
988 		    case ITEMSLOT_FEET:
989 		    {
990 			const ATTACK_DEF	*attack;
991 
992 			// Check that the attack is something
993 			// weird.
994 			attack = item->getAttack();
995 			if (attack == &glb_attackdefs[ATTACK_MISUSED] ||
996 			    attack == &glb_attackdefs[ATTACK_MISUSED_BUTWEAPON] ||
997 			    attack == &glb_attackdefs[ATTACK_MISTHROWN])
998 			{
999 			    break;
1000 			}
1001 
1002 			// Valid choice!
1003 			validchoice[numchoice++] = slot;
1004 			break;
1005 		    }
1006 
1007 		    // I really can't think of a way that the
1008 		    // amulet can be used.
1009 		    case ITEMSLOT_AMULET:
1010 		    // The right hand is covered by normal combat.
1011 		    case ITEMSLOT_RHAND:
1012 		    // This ensures we cover all valid code paths
1013 		    case NUM_ITEMSLOTS:
1014 			break;
1015 		}
1016 	    }
1017 
1018 	    // Given our base procchance of procchance,
1019 	    // and a number of choices of numchoice, we want a combined
1020 	    // choice total.
1021 	    // This is so that the probability of procing with both
1022 	    // a helmet and a shield is greater than with just one.
1023 	    // Our formula is:
1024 	    // 1 - (1 - procchance)^numchoice
1025 	    // This is slightly unfair as we don't allow two attacks
1026 	    // in a single round so you get a penalty for double equipping,
1027 	    // but I'm hoping it is close enough.
1028 
1029 	    // First, we handle common & trivial cases.
1030 	    if (!numchoice)
1031 		procchance = 0;
1032 	    else if (numchoice > 1)	// Equality leaves it unchanged.
1033 	    {
1034 		int		negchance, iter, basechance;
1035 
1036 		negchance = 100 - procchance;
1037 		// Make base 1024.
1038 		negchance *= 1024;
1039 		negchance += 50;
1040 		negchance /= 100;
1041 		basechance = negchance;
1042 		// Repeatedly exponentiate and normalize the base.
1043 		// Start at 1 as we already have a single chance.
1044 		for (iter = 1; iter < numchoice; iter++)
1045 		{
1046 		    negchance *= basechance;
1047 		    // Remove the 1024 base.
1048 		    negchance >>= 10;
1049 		}
1050 		// Back to base 100
1051 		negchance *= 100;
1052 		negchance += 512;
1053 		negchance >>= 10;
1054 		// And invert...
1055 		procchance = 100 - negchance;
1056 	    }
1057 
1058 	    // Choose a random value if possible
1059 	    if (rand_chance(procchance))
1060 	    {
1061 		slot = validchoice[rand_choice(numchoice)];
1062 		ITEM		*weapon;
1063 
1064 		weapon = getEquippedItem(slot);
1065 		UT_ASSERT(weapon != 0);
1066 
1067 		// Create some pretty text.
1068 		switch (slot)
1069 		{
1070 		    case ITEMSLOT_HEAD:
1071 			formatAndReport("%U head <butt> with %r %Iu.", weapon);
1072 			break;
1073 		    case ITEMSLOT_BODY:
1074 			formatAndReport("%U body <slam> with %r %Iu.", weapon);
1075 			break;
1076 		    case ITEMSLOT_LHAND:
1077 			formatAndReport("%U shield <slam> with %r %Iu.", weapon);
1078 			break;
1079 		    case ITEMSLOT_RRING:
1080 		    case ITEMSLOT_LRING:
1081 			formatAndReport("%U <punch> with %r %Iu.", weapon);
1082 			break;
1083 		    case ITEMSLOT_FEET:
1084 			formatAndReport("%U <kick> with %r %Iu.", weapon);
1085 			break;
1086 
1087 		    // These should not occur.
1088 		    case ITEMSLOT_AMULET:
1089 		    case ITEMSLOT_RHAND:
1090 		    case NUM_ITEMSLOTS:
1091 			UT_ASSERT(!"Invalid itemslot selected");
1092 			break;
1093 		}
1094 
1095 		// This triggers us to try once more with the
1096 		// new weapon.
1097 		attack = weapon->getAttack();
1098 	    }
1099 	}
1100     }
1101 
1102     return true;
1103 }
1104 
1105 bool
actionSwap(int dx,int dy)1106 MOB::actionSwap(int dx, int dy)
1107 {
1108     int		 nx, ny;
1109     MOB		*mob;
1110     bool	 doswap, taketurn;
1111 
1112     applyConfusion(dx, dy);
1113 
1114     nx = getX() + dx;
1115     ny = getY() + dy;
1116 
1117     if (!canMoveDelta(dx, dy, false))
1118     {
1119 	formatAndReport("%U cannot go there, so cannot swap places.");
1120 	return false;
1121     }
1122 
1123     if (hasIntrinsic(INTRINSIC_INPIT))
1124     {
1125 	formatAndReport("%U cannot swap places while in a pit.");
1126 	return false;
1127     }
1128     if (hasIntrinsic(INTRINSIC_INTREE))
1129     {
1130 	formatAndReport("%U cannot swap places while up a tree.");
1131 	return false;
1132     }
1133     if (hasIntrinsic(INTRINSIC_SUBMERGED))
1134     {
1135 	formatAndReport("%U cannot swap places while submerged.");
1136 	return false;
1137     }
1138     // Check to see if there is an item in the way.  Note that
1139     // you can swap places with a boulder!
1140     if (!canMoveDelta(dx, dy, true))
1141     {
1142 	ITEM		*boulder;
1143 
1144 	boulder = glbCurLevel->getItem(nx, ny);
1145 	if (boulder && boulder->getDefinition() == ITEM_BOULDER)
1146 	{
1147 	    // Check to see if you have anything equipped.
1148 	    if (hasAnyEquippedItems())
1149 	    {
1150 		formatAndReport("%U <be> wearing too much to swap places with %IU.", boulder);
1151 		return false;
1152 	    }
1153 
1154 	    // We can swap with the boulder.
1155 	    // Should be strength check?
1156 	    formatAndReport("%U <swap> positions with %IU.", boulder);
1157 
1158 	    // Move the boulder.
1159 	    glbCurLevel->dropItem(boulder);
1160 	    glbCurLevel->acquireItem(boulder, getX(), getY(), this);
1161 
1162 	    // Move the avatar.
1163 	    move(nx, ny);
1164 
1165 	    // Report what we stepped on...
1166 	    // As both mob & this moved, we report if either of them is
1167 	    // the avatar.
1168 	    // If either died, the MOB::getAvatar should be reset to 0,
1169 	    // so neither of these paths should be taken.
1170 	    if (isAvatar())
1171 	    {
1172 		glbCurLevel->describeSquare(getX(), getY(),
1173 					    hasIntrinsic(INTRINSIC_BLIND),
1174 					    false);
1175 	    }
1176 	    return true;
1177 	}
1178 	else if (boulder)
1179 	    formatAndReport("%U cannot swap places with %IU.", boulder);
1180 	else
1181 	    formatAndReport("%U cannot go there, so cannot swap places.");
1182 
1183 	return false;
1184     }
1185 
1186     // We can only swap with a monster.
1187     mob = glbCurLevel->getMob(nx, ny);
1188     if (!mob)
1189     {
1190 	reportMessage("There is no one there to swap places with.");
1191 	return false;
1192     }
1193 
1194     // If either ourselves or the MOB is giant sized, we can't swap.
1195     if (getSize() >= SIZE_GARGANTUAN)
1196     {
1197 	formatAndReport("%U <be> too big to swap places.");
1198 	return false;
1199     }
1200     if (mob->getSize() >= SIZE_GARGANTUAN)
1201     {
1202 	formatAndReport("%MU <M:be> too big to swap places.", mob);
1203 	return false;
1204     }
1205 
1206     // Check that the mob can move onto our square.
1207     if (!mob->canMove(getX(), getY(), true))
1208     {
1209 	formatAndReport("%MU cannot step here!", mob);
1210 	return false;
1211     }
1212 
1213     // If either you or the mob is in a pit, you can't swap,
1214     // no free exits!
1215     // We check for yourself at the start so we don't allow swapping
1216     // with boulders to escape.
1217     if (mob->hasIntrinsic(INTRINSIC_INPIT))
1218     {
1219 	formatAndReport("%U cannot swap places with %MU because %Mp <M:be> in a pit.", mob);
1220 	return false;
1221     }
1222     if (mob->hasIntrinsic(INTRINSIC_INTREE))
1223     {
1224 	formatAndReport("%U cannot swap places with %MU because %Mp <M:be> up a tree.", mob);
1225 	return false;
1226     }
1227     if (mob->hasIntrinsic(INTRINSIC_SUBMERGED))
1228     {
1229 	formatAndReport("%U cannot swap places with %MU because %Mp <M:be> submerged.", mob);
1230 	return false;
1231     }
1232 
1233     // If mob is friendly, swap for free.
1234     // If neutral, swap with strength check
1235     // If hostile, no go.
1236     doswap = false;
1237     taketurn = false;
1238     switch (mob->getAttitude(this))
1239     {
1240 	case ATTITUDE_FRIENDLY:
1241 	    doswap = true;
1242 	    taketurn = true;
1243 	    formatAndReport("%U <swap> places with %MU.", mob);
1244 	    break;
1245 	case ATTITUDE_NEUTRAL:
1246 	    taketurn = true;
1247 	    // TODO: Chance of becoming hostile?
1248 	    switch (strengthCheck(mob->getStrength()))
1249 	    {
1250 		case -1:
1251 		    formatAndReport("%U cannot budge %MU.", mob);
1252 		    doswap = false;
1253 		    break;
1254 		case 0:
1255 		    formatAndReport("%U cannot quite shift %MU.", mob);
1256 		    doswap = false;
1257 		    break;
1258 		case 1:
1259 		    formatAndReport("%U <push> %MU out of the way.", mob);
1260 		    doswap = true;
1261 		    break;
1262 		case 2:
1263 		    formatAndReport("%U <muscle> past %MU.", mob);
1264 		    doswap = true;
1265 		    break;
1266 	    }
1267 	    break;
1268 
1269 	case ATTITUDE_HOSTILE:
1270 	    taketurn = false;
1271 	    doswap = false;
1272 	    formatAndReport("%U cannot swap places with someone %p <be> fighting!");
1273 	    break;
1274 
1275 	case NUM_ATTITUDES:
1276 	    UT_ASSERT(!"UNKNOWN ATTITUDE!");
1277 	    taketurn = false;
1278 	    doswap = false;
1279 	    break;
1280     }
1281 
1282     if (doswap)
1283     {
1284 	// Swap...
1285 	// Note either party may die here, but we don't use them
1286 	// herein.
1287 	mob->move(getX(), getY());
1288 	move(nx, ny);
1289 	// Report what we stepped on...
1290 	// As both mob & this moved, we report if either of them is
1291 	// the avatar.
1292 	// If either died, the MOB::getAvatar should be reset to 0,
1293 	// so neither of these paths should be taken.
1294 	if (isAvatar())
1295 	{
1296 	    glbCurLevel->describeSquare(getX(), getY(),
1297 					hasIntrinsic(INTRINSIC_BLIND),
1298 					false);
1299 	}
1300 	if (mob->isAvatar())
1301 	{
1302 	    glbCurLevel->describeSquare(mob->getX(), mob->getY(),
1303 					mob->hasIntrinsic(INTRINSIC_BLIND),
1304 					false);
1305 	}
1306     }
1307     return taketurn;
1308 }
1309 
1310 bool
actionSleep()1311 MOB::actionSleep()
1312 {
1313     formatAndReport("%U <lie> down to sleep.");
1314 
1315     if (hasIntrinsic(INTRINSIC_RESISTSLEEP))
1316     {
1317 	formatAndReport("%U <stay> wide awake!");
1318 	return true;
1319     }
1320 
1321     // Set our sleep flag.
1322     setTimedIntrinsic(this, INTRINSIC_ASLEEP, 50);
1323 
1324     return true;
1325 }
1326 
1327 static MAGICTYPE_NAMES
getSudokuType(int idx,unsigned int seed)1328 getSudokuType(int idx, unsigned int seed)
1329 {
1330     int			x, y, i, j;
1331     MAGICTYPE_NAMES	type;
1332 
1333     int		pattern[4][4] =
1334     {
1335 	{ 0, 1,   2, 3 },
1336 	{ 2, 3,   0, 1 },
1337 
1338 	{ 1, 0,   3, 2 },
1339 	{ 3, 2,   1, 0 }
1340     };
1341 
1342     MAGICTYPE_NAMES		reorder[4] =
1343     { MAGICTYPE_WAND, MAGICTYPE_SCROLL, MAGICTYPE_POTION, MAGICTYPE_RING };
1344 
1345     // Reorder according to reorder pattern.
1346     for (i = 0; i < 4; i++)
1347     {
1348 	j = i + (seed % (4 - i));
1349 	seed = rand_wanginthash(seed);
1350 	if (i != j)
1351 	{
1352 	    type = reorder[i];
1353 	    reorder[i] = reorder[j];
1354 	    reorder[j] = type;
1355 	}
1356     }
1357     for (y = 0; y < 4; y++)
1358 	for (x = 0; x < 4; x++)
1359 	    pattern[y][x] = reorder[pattern[y][x]];
1360 
1361     // Now, permute our pattern according to the seed.
1362     if (seed & 1)
1363     {
1364 	for (y = 0; y < 4; y++)
1365 	{
1366 	    i = pattern[y][0];
1367 	    pattern[y][0] = pattern[y][1];
1368 	    pattern[y][1] = i;
1369 	}
1370     }
1371     if (seed & 2)
1372     {
1373 	for (y = 0; y < 4; y++)
1374 	{
1375 	    i = pattern[y][2];
1376 	    pattern[y][2] = pattern[y][3];
1377 	    pattern[y][3] = i;
1378 	}
1379     }
1380     if (seed & 4)
1381     {
1382 	for (x = 0; x < 4; x++)
1383 	{
1384 	    i = pattern[0][x];
1385 	    pattern[0][x] = pattern[1][x];
1386 	    pattern[1][x] = i;
1387 	}
1388     }
1389     if (seed & 8)
1390     {
1391 	for (x = 0; x < 4; x++)
1392 	{
1393 	    i = pattern[2][x];
1394 	    pattern[2][x] = pattern[3][x];
1395 	    pattern[3][x] = i;
1396 	}
1397     }
1398 
1399     // We should now permute entire squares, but I am drunk now.
1400     // But, I just realized that swapping squares == flipping on x
1401     // plus two column flips.
1402     x = idx & 3;
1403     y = idx >> 2;
1404 
1405     // Flip/flop
1406     if (seed & 16)
1407 	x = 3 - x;
1408     if (seed & 32)
1409 	y = 3 - y;
1410     if (seed & 64)
1411     {
1412 	i = x;
1413 	x = y;
1414 	y = i;
1415     }
1416 
1417     return (MAGICTYPE_NAMES) pattern[y][x];
1418 }
1419 
1420 static int
consumeSudokuKeys(ITEMSTACK & stack,int idx,long seed,int & eaten)1421 consumeSudokuKeys(ITEMSTACK &stack, int idx, long seed, int &eaten)
1422 {
1423     int		 i;
1424     int		 valid = 0;
1425     ITEM	*item;
1426     MAGICTYPE_NAMES	type;
1427     BUF			buf;
1428 
1429     type = getSudokuType(idx, seed);
1430 
1431     for (i = 0; i < stack.entries(); i++)
1432     {
1433 	item = stack(i);
1434 	if (item->getMagicType() != type)
1435 	{
1436 	    item->formatAndReport("%U <disappear> in a puff of smoke.");
1437 	    glbCurLevel->dropItem(item);
1438 	    delete item;
1439 	    eaten++;
1440 	}
1441 	else
1442 	{
1443 	    // A valid item, vibrate and set true
1444 	    item->formatAndReport("%U <vibrate>.");
1445 	    valid = 1;
1446 	}
1447     }
1448 
1449     return valid;
1450 }
1451 
1452 bool
actionMagicDoorEffect(int x,int y)1453 MOB::actionMagicDoorEffect(int x, int y)
1454 {
1455     // Search the level for the sudoku keys (ie, altars...)
1456     int		numaltar = 0, numused, eaten;
1457     int		altarx[16], altary[16];
1458     int		mx, my;
1459     int		idx;
1460     ITEMSTACK	items;
1461 
1462     for (my = 0; my < MAP_HEIGHT; my++)
1463     {
1464 	if (numaltar >= 16)
1465 	    break;
1466 	for (mx = 0; mx < MAP_WIDTH; mx++)
1467 	{
1468 	    if (numaltar >= 16)
1469 		break;
1470 	    if (glbCurLevel->getTile(mx, my) == SQUARE_FLOORALTAR)
1471 	    {
1472 		altarx[numaltar] = mx;
1473 		altary[numaltar] = my;
1474 		numaltar++;
1475 	    }
1476 	}
1477     }
1478 
1479     // If we don't have 16 altars, nothing to do.
1480     if (numaltar != 16)
1481     {
1482 	encyc_viewentry("MAGICDOOR", MAGICDOOR_NOTENOUGHDOORS);
1483 	return false;
1484     }
1485 
1486     // Inspect the contents of each altar.
1487     numused = 0;
1488     eaten = 0;
1489     for (idx = 0; idx < 16; idx++)
1490     {
1491 	items.clear();
1492 	glbCurLevel->getItemStack(items, altarx[idx], altary[idx]);
1493 
1494 	numused += consumeSudokuKeys(items, idx, glbCurLevel->getSeed(), eaten);
1495     }
1496 
1497     if (numused < 16 && eaten)
1498     {
1499 	encyc_viewentry("MAGICDOOR", MAGICDOOR_INSUFFICIENTITEMS);
1500 	return true;
1501     }
1502     else if (numused < 16)
1503     {
1504 	encyc_viewentry("MAGICDOOR", MAGICDOOR_NOITEMS);
1505 	return true;
1506     }
1507 
1508     // Door opens!
1509     glbCurLevel->setTile(x, y, SQUARE_OPENDOOR);
1510     encyc_viewentry("MAGICDOOR", MAGICDOOR_SUCCESS);
1511 
1512     return true;
1513 }
1514 
1515 bool
actionOpen(int dx,int dy)1516 MOB::actionOpen(int dx, int dy)
1517 {
1518     SQUARE_NAMES		tile;
1519     bool	consumed = false, doopen = false;
1520 
1521     applyConfusion(dx, dy);
1522 
1523     // Now check to see if the square itself is openable.
1524     tile = glbCurLevel->getTile(getX() + dx, getY() + dy);
1525 
1526     switch (tile)
1527     {
1528 	case SQUARE_BLOCKEDDOOR:
1529 	    if (!isAvatar())
1530 	    {
1531 		formatAndReport("$U <find> the door latched.");
1532 		break;
1533 	    }
1534 	    // FALL THROUGH
1535 	case SQUARE_DOOR:
1536 	    // Check if we were strong enough.
1537 	    switch (strengthCheck(STRENGTH_HUMAN))
1538 	    {
1539 		case -1:
1540 		    formatAndReport("%U cannot budge the door!");
1541 		    break;
1542 
1543 		case 0:
1544 		    formatAndReport("%U cannot quite shift the door.");
1545 		    break;
1546 
1547 		case 1:
1548 		    doopen = true;
1549 		    formatAndReport("%U <force> the door open.");
1550 		    break;
1551 
1552 		case 2:
1553 		    doopen = true;
1554 		    formatAndReport("%U <open> the door smoothly.");
1555 		    break;
1556 	    }
1557 
1558 	    if (doopen)
1559 	    {
1560 		glbCurLevel->setTile(getX() + dx, getY() + dy, SQUARE_OPENDOOR);
1561 	    }
1562 	    consumed = true;
1563 	    break;
1564 	case SQUARE_OPENDOOR:
1565 	    // Can't reopen...
1566 	    reportMessage("The door is already open.");
1567 	    break;
1568 	case SQUARE_MAGICDOOR:
1569 	    if (!isAvatar())
1570 	    {
1571 		// Non-avatar's can't trigger this door.
1572 		return false;
1573 	    }
1574 	    consumed = actionMagicDoorEffect(getX() + dx, getY() + dy);
1575 	    break;
1576 	default:
1577 	    reportMessage("That is not openable!");
1578 	    break;
1579     }
1580     return consumed;
1581 }
1582 
1583 bool
actionClose(int dx,int dy)1584 MOB::actionClose(int dx, int dy)
1585 {
1586     SQUARE_NAMES		tile;
1587     bool	consumed = false, doclose = false;
1588     MOB		*mob;
1589     ITEM	*item;
1590 
1591     applyConfusion(dx, dy);
1592 
1593     // Now check to see if the square itself is openable.
1594     tile = glbCurLevel->getTile(getX() + dx, getY() + dy);
1595 
1596     switch (tile)
1597     {
1598 	case SQUARE_OPENDOOR:
1599 	    // Check if there is a mob in the way.
1600 	    mob = glbCurLevel->getMob(getX() + dx, getY() + dy);
1601 	    if (mob)
1602 	    {
1603 		formatAndReport("%MU <M:hold> the door open!", mob);
1604 		return true;
1605 	    }
1606 
1607 	    // Check to see if there is an item.
1608 	    item = glbCurLevel->getItem(getX() + dx, getY() + dy);
1609 	    if (item)
1610 	    {
1611 		formatAndReport("The door is blocked by %IU.", item);
1612 		return true;
1613 	    }
1614 
1615 	    // Check if we were strong enough.
1616 	    // Doors are much easier to close than open.
1617 	    switch (strengthCheck(STRENGTH_WEAK))
1618 	    {
1619 		case -1:
1620 		    formatAndReport("%U cannot budge the door!");
1621 		    break;
1622 
1623 		case 0:
1624 		    formatAndReport("%U cannot quite shift the door!");
1625 		    break;
1626 
1627 		case 1:
1628 		    doclose = true;
1629 		    formatAndReport("%U <close> the door.");
1630 		    break;
1631 
1632 		case 2:
1633 		    doclose = true;
1634 		    formatAndReport("%U <slam> the door shut.");
1635 		    break;
1636 	    }
1637 
1638 	    if (doclose)
1639 	    {
1640 		glbCurLevel->setTile(getX() + dx, getY() + dy, SQUARE_DOOR);
1641 	    }
1642 	    consumed = true;
1643 	    break;
1644 	case SQUARE_BLOCKEDDOOR:
1645 	case SQUARE_DOOR:
1646 	    // Can't reclose...
1647 	    reportMessage("The door is already closed.");
1648 	    break;
1649 	default:
1650 	    reportMessage("That is not closeable!");
1651 	    break;
1652     }
1653     return consumed;
1654 }
1655 
1656 extern bool glbHasJustSearched;
1657 
1658 bool
actionSearch()1659 MOB::actionSearch()
1660 {
1661     int		 dx, dy;
1662     bool	 found = false;
1663     ITEM	*item;
1664     bool	 shouldreveal = false;
1665 
1666     if (isAvatar())
1667     {
1668 	glbHasJustSearched = true;
1669     }
1670 
1671     // We always map everything around us.
1672     // No need for LOS here.
1673     // No need for LOS?
1674     // Really?
1675     // Even if an NPC decides to invoke actionSearch?
1676     // Because of, perhaps, wielding a ring of searching?
1677     // Past-Jeff was stupid.
1678     // The easy answer is to just make this test if we are the avatar,
1679     // but I know those people at armoredtactis will be reading this change,
1680     // so I might as well throw in an exception whereby your pets will
1681     // be allowed to map the level for you.
1682     // Not that is particularly useful since they either follow or stay,
1683     // but some glorious future they may get an explore command?
1684     if (isAvatar() || hasCommonMaster(MOB::getAvatar()))
1685 	shouldreveal = true;
1686 
1687     if (shouldreveal)
1688 	glbCurLevel->markMapped(getX(), getY(), 1, 1, false);
1689 
1690     // Now, examine every square around us...
1691     for (dy = -1; dy <= 1; dy++)
1692     {
1693 	if (getY() + dy < 0 || getY() + dy >= MAP_HEIGHT)
1694 	    continue;
1695 	for (dx = -1; dx <= 1; dx++)
1696 	{
1697 	    if (getX() + dx < 0 || getX() + dx >= MAP_WIDTH)
1698 		continue;
1699 
1700 	    // We want to mark any items on this square as
1701 	    // known.
1702 	    // For speed, we just mark the top item as mapped.
1703 	    // Really, we should mark the entire stack...
1704 	    if (shouldreveal)
1705 	    {
1706 		item = glbCurLevel->getItem(getX()+dx, getY()+dy);
1707 		if (item)
1708 		    item->markMapped();
1709 	    }
1710 
1711 	    // Don't examine own square, may kill us!
1712 	    if (!dx && !dy)
1713 		continue;
1714 
1715 	    found |= searchSquare(getX() + dx, getY() + dy, true);
1716 	}
1717     }
1718 
1719     // Last to search is our own square, in case we die.
1720     found |= searchSquare(getX(), getY(), true);
1721 
1722     if (!found)
1723     {
1724 	// I think this is too noisy so have axed it.
1725 	// NOTE: Not safe to enable it, because we may be dead now!
1726 #if 0
1727 	formatAndReport("%U <search> and find nothing.");
1728 #endif
1729     }
1730 
1731     // Consume...
1732     return true;
1733 }
1734 
1735 bool
actionClimb()1736 MOB::actionClimb()
1737 {
1738     SQUARE_NAMES		tile;
1739     int				trap;
1740 
1741     tile = glbCurLevel->getTile(getX(), getY());
1742 
1743     trap = glb_squaredefs[tile].trap;
1744     switch ((TRAP_NAMES) trap)
1745     {
1746 	case TRAP_TELEPORT:
1747 	    return actionClimbDown();
1748 
1749 	case TRAP_NONE:
1750 	case TRAP_POISONVENT:
1751 	case TRAP_SMOKEVENT:
1752 	case NUM_TRAPS:
1753 	    break;
1754 
1755 	case TRAP_HOLE:
1756 	    return actionClimbDown();
1757 
1758 	case TRAP_PIT:
1759 	case TRAP_SPIKEDPIT:
1760 	    if (hasIntrinsic(INTRINSIC_INPIT))
1761 		return actionClimbUp();
1762 	    else
1763 		return actionClimbDown();
1764     }
1765 
1766     // If we are submerged, it is straight forward: climbup.
1767     if (hasIntrinsic(INTRINSIC_SUBMERGED))
1768 	return actionClimbUp();
1769 
1770     switch (tile)
1771     {
1772 	case SQUARE_LADDERUP:
1773 	    return actionClimbUp();
1774 
1775 	case SQUARE_LADDERDOWN:
1776 	    return actionClimbDown();
1777 
1778 	case SQUARE_DIMDOOR:
1779 	    return actionClimbDown();
1780 
1781 	// If you are in the relevant liquid, you will have already
1782 	// gone to climb up.
1783 	case SQUARE_LAVA:
1784 	    return actionClimbDown();
1785 	case SQUARE_WATER:
1786 	    return actionClimbDown();
1787 	case SQUARE_ACID:
1788 	    return actionClimbDown();
1789 
1790 	case SQUARE_FOREST:
1791 	case SQUARE_FORESTFIRE:
1792 	    if (hasIntrinsic(INTRINSIC_INTREE))
1793 		return actionClimbDown();
1794 	    else
1795 		return actionClimbUp();
1796 
1797 	default:
1798 	    formatAndReport("%U <try> to climb thin air!");
1799 	    return false;
1800     }
1801 }
1802 
1803 bool
actionClimbUp()1804 MOB::actionClimbUp()
1805 {
1806     SQUARE_NAMES tile;
1807     int		trap;
1808     BUF		buf;
1809 
1810     tile = glbCurLevel->getTile(getX(), getY());
1811 
1812     trap = glb_squaredefs[tile].trap;
1813 
1814     switch ((TRAP_NAMES) trap)
1815     {
1816 	case TRAP_HOLE:
1817 	case TRAP_NONE:
1818 	case TRAP_TELEPORT:
1819 	case TRAP_POISONVENT:
1820 	case TRAP_SMOKEVENT:
1821 	case NUM_TRAPS:
1822 	    break;
1823 
1824 	case TRAP_PIT:
1825 	case TRAP_SPIKEDPIT:
1826 	{
1827 	    if (hasIntrinsic(INTRINSIC_INPIT))
1828 	    {
1829 		if (canFly())
1830 		{
1831 		    // Trivially escape the pit.
1832 		    formatAndReport("%U <fly> out of the pit.");
1833 		    clearIntrinsic(INTRINSIC_INPIT);
1834 		    return true;
1835 		}
1836 		// Try to climb out...
1837 		makeEquipNoise(ITEMSLOT_FEET);
1838 		if (rand_chance(50))
1839 		{
1840 		    // Success.
1841 		    formatAndReport("%U <climb> out of the pit.");
1842 		    clearIntrinsic(INTRINSIC_INPIT);
1843 		}
1844 		else
1845 		{
1846 		    formatAndReport("%U <slip> and <fall> back into the pit.");
1847 		}
1848 		return true;
1849 	    }
1850 	    else
1851 	    {
1852 		// You are already out of the pit!
1853 		// Will fall through to thin air.
1854 	    }
1855 	}
1856     }
1857 
1858     if (hasIntrinsic(INTRINSIC_SUBMERGED))
1859     {
1860 	// Try to swim to the surface...
1861 	int		chance;
1862 	BUF		success;
1863 	bool		haswaterwalk;
1864 
1865 	haswaterwalk = hasIntrinsic(INTRINSIC_WATERWALK);
1866 
1867 	switch (tile)
1868 	{
1869 	    case SQUARE_LAVA:
1870 		chance = 90;		// Easy to get out of lava.
1871 		buf = formatToString("%U <struggle> in the lava.",
1872 			    this, 0, 0, 0);
1873 		if (haswaterwalk)
1874 		    success = formatToString("%U <step> out of the lava.",
1875 				this, 0, 0, 0);
1876 		else
1877 		    success = formatToString("%U <pull> out of the lava.",
1878 				this, 0, 0, 0);
1879 		if (haswaterwalk)
1880 		    chance = 100;
1881 		break;
1882 	    case SQUARE_WATER:
1883 		chance = 50;		// Harder to keep on top of.
1884 		buf = formatToString("%U <flounder> in the water.",
1885 			    this, 0, 0, 0);
1886 		if (haswaterwalk)
1887 		    success = formatToString("%U <step> to the surface.",
1888 				this, 0, 0, 0);
1889 		else
1890 		    success = formatToString("%U <swim> to the surface.",
1891 				this, 0, 0, 0);
1892 		if (haswaterwalk)
1893 		    chance = 100;
1894 		break;
1895 	    case SQUARE_ACID:
1896 		chance = 50;		// Harder to keep on top of.
1897 		buf = formatToString("%U <flounder> in the acid.",
1898 			    this, 0, 0, 0);
1899 		if (haswaterwalk)
1900 		    success = formatToString("%U <step> to the surface.",
1901 				this, 0, 0, 0);
1902 		else
1903 		    success = formatToString("%U <swim> to the surface.",
1904 				this, 0, 0, 0);
1905 		if (haswaterwalk)
1906 		    chance = 100;
1907 		break;
1908 	    case SQUARE_ICE:
1909 		chance = 0;
1910 		buf = formatToString("%U <pound> on the ice in vain.",
1911 				this, 0, 0, 0);
1912 		break;
1913 	    default:
1914 		// Buried alive.
1915 		chance = 0;
1916 		buf = formatToString("The enclosing rock fastly imprisons %U.",
1917 				this, 0, 0, 0);
1918 		break;
1919 	}
1920 
1921 	if (chance && rand_chance(chance))
1922 	{
1923 	    reportMessage(success);
1924 	    clearIntrinsic(INTRINSIC_SUBMERGED);
1925 	}
1926 	else
1927 	{
1928 	    reportMessage(buf);
1929 	}
1930 
1931 	return true;
1932     }
1933 
1934     switch (tile)
1935     {
1936 	case SQUARE_LADDERUP:
1937 	{
1938 	    // Climb to the given map...
1939 	    MAP		*nextmap;
1940 	    int		 x, y, ox, oy;
1941 
1942 	    if (!canFly())
1943 		makeEquipNoise(ITEMSLOT_FEET);
1944 
1945 	    // Special case of climbing top stair case...
1946 	    if (glbCurLevel->getDepth() == 1)
1947 	    {
1948 		if (isAvatar())
1949 		{
1950 		    // Determine if we have the heart of baezl'bub...
1951 		    bool		 hasheart = false;
1952 
1953 		    hasheart = hasItem(ITEM_BLACKHEART);
1954 
1955 		    if (hasheart)
1956 		    {
1957 			formatAndReport("%U <return> to the sweet air of the surface world.");
1958 		    }
1959 		    else
1960 		    {
1961 			formatAndReport("Until %U <have> slain Baezl'bub and "
1962 				        "have his black heart, the surface "
1963 					"world cannot help %p.");
1964 			return true;
1965 		    }
1966 		}
1967 		else
1968 		{
1969 		    // Some critter is trying to escape, they think
1970 		    // better of it.
1971 		    formatAndReport("%U <recoil> from the light of the surface world.");
1972 		    return true;
1973 		}
1974 	    }
1975 
1976 	    nextmap = glbCurLevel->getMapUp();
1977 	    if (!nextmap)
1978 	    {
1979 		formatAndReport("%U <bonk> %r head on an invisible barrier.");
1980 		return true;
1981 	    }
1982 
1983 	    // See if there is room...
1984 	    if (!nextmap->findTile(SQUARE_LADDERDOWN, x, y) ||
1985 		!nextmap->findCloseTile(x, y, getMoveType()))
1986 	    {
1987 		formatAndReport("%U cannot find room at the end of the ladder.");
1988 		return true;
1989 	    }
1990 
1991 	    if (canFly())
1992 		formatAndReport("%U <fly> up the ladder.");
1993 	    else
1994 		formatAndReport("%U <climb> the ladder.");
1995 
1996 	    ox = getX();
1997 	    oy = getY();
1998 
1999 	    // Move to it...
2000 	    glbCurLevel->unregisterMob(this);
2001 	    if (!move(x, y, true))
2002 		return true;
2003 	    nextmap->registerMob(this);
2004 
2005 	    glbCurLevel->chaseMobOnStaircase(SQUARE_LADDERDOWN,
2006 					     ox, oy, this, nextmap);
2007 
2008 	    if (isAvatar())
2009 	    {
2010 		glbSuppressAutoClimb = true;
2011 
2012 		MAP::changeCurrentLevel(nextmap);
2013 		// Need to rebuild our screen!  Refreshing will be handled
2014 		// by the message wait code.
2015 		gfx_scrollcenter(getX(), getY());
2016 		glbCurLevel->buildFOV(getX(), getY(), 7, 5);
2017 		if (!hasIntrinsic(INTRINSIC_BLIND))
2018 		    glbCurLevel->markMapped(getX(), getY(), 7, 5);
2019 
2020 		glbCurLevel->describeSquare(getX(), getY(),
2021 				    hasIntrinsic(INTRINSIC_BLIND),
2022 				    false);
2023 	    }
2024 
2025 	    return true;
2026 	}
2027 
2028 	case SQUARE_FOREST:
2029 	case SQUARE_FORESTFIRE:
2030 	{
2031 	    if (!hasIntrinsic(INTRINSIC_INTREE))
2032 	    {
2033 		if (canFly())
2034 		    formatAndReport("%U <fly> up into the tree.");
2035 		else
2036 		    formatAndReport("%U <climb> up the tree.");
2037 
2038 		setIntrinsic(INTRINSIC_INTREE);
2039 
2040 		return true;
2041 	    }
2042 	    else
2043 	    {
2044 		formatAndReport("%U <be> already at the top of the tree.");
2045 		return false;
2046 	    }
2047 	}
2048 
2049 	default:
2050 	    if (canFly())
2051 		formatAndReport("%U <fly> up and hit the roof.");
2052 	    else
2053 		formatAndReport("%U <try> to climb up thin air!");
2054 	    return false;
2055     }
2056 }
2057 
2058 bool
actionClimbDown()2059 MOB::actionClimbDown()
2060 {
2061     SQUARE_NAMES tile;
2062     TRAP_NAMES	trap;
2063 
2064     tile = glbCurLevel->getTile(getX(), getY());
2065 
2066     trap = (TRAP_NAMES) glb_squaredefs[tile].trap;
2067 
2068     switch (trap)
2069     {
2070 	case TRAP_NONE:
2071 	case TRAP_POISONVENT:
2072 	case TRAP_SMOKEVENT:
2073 	case NUM_TRAPS:
2074 	    break;
2075 
2076 	case TRAP_TELEPORT:
2077 	{
2078 	    // Trigger the teleport trap.
2079 	    if (canFly())
2080 		formatAndReport("%U <fly> into the teleporter.");
2081 	    else
2082 		formatAndReport("%U <climb> into the teleporter.");
2083 	    return actionTeleport();
2084 	}
2085 
2086 	case TRAP_HOLE:
2087 	{
2088 	    // Climb to the given map...
2089 	    MAP		*nextmap;
2090 	    int		 x, y;
2091 
2092 	    makeEquipNoise(ITEMSLOT_FEET);
2093 	    nextmap = glbCurLevel->getMapDown();
2094 	    if (!nextmap || !nextmap->isUnlocked())
2095 	    {
2096 		formatAndReport("%U <be> stopped by an invisible barrier.");
2097 		return true;
2098 	    }
2099 
2100 	    // See if there is room...
2101 	    // We just go somewhere random (but moveable!)
2102 	    if (!nextmap->findRandomLoc(x, y, getMoveType(), false,
2103 					getSize() >= SIZE_GARGANTUAN,
2104 					false, false, false, true))
2105 	    {
2106 		formatAndReport("%U <find> the hole blocked.");
2107 		return true;
2108 	    }
2109 
2110 	    // Move to it...
2111 	    glbCurLevel->unregisterMob(this);
2112 	    if (!move(x, y, true))
2113 		return true;
2114 	    nextmap->registerMob(this);
2115 
2116 	    if (canFly())
2117 		formatAndReport("%U <fly> down the hole.");
2118 	    else
2119 		formatAndReport("%U <jump> into the hole.");
2120 
2121 	    if (isAvatar())
2122 	    {
2123 		MAP::changeCurrentLevel(nextmap);
2124 		glbCurLevel->describeSquare(getX(), getY(),
2125 				    hasIntrinsic(INTRINSIC_BLIND),
2126 				    false);
2127 	    }
2128 
2129 	    return true;
2130 	}
2131 
2132 	case TRAP_PIT:
2133 	case TRAP_SPIKEDPIT:
2134 	{
2135 	    if (hasIntrinsic(INTRINSIC_INPIT))
2136 	    {
2137 		// Already inside the pit, can't dig deeper.
2138 		// Will fall through to scramble on ground.
2139 	    }
2140 	    else
2141 	    {
2142 		makeEquipNoise(ITEMSLOT_FEET);
2143 		// You can safely descend into the pit with
2144 		// no chance of damage.
2145 		formatAndReport("%U carefully <climb> into the pit.");
2146 		setIntrinsic(INTRINSIC_INPIT);
2147 
2148 		// Report what we stepped on...
2149 		if (isAvatar())
2150 		{
2151 		    glbCurLevel->describeSquare(getX(), getY(),
2152 						hasIntrinsic(INTRINSIC_BLIND),
2153 						false);
2154 		}
2155 
2156 		return true;
2157 	    }
2158 	}
2159     }
2160 
2161     switch (tile)
2162     {
2163 	case SQUARE_LADDERDOWN:
2164 	{
2165 	    // Climb to the given map...
2166 	    MAP		*nextmap;
2167 	    int		 x, y;
2168 	    int		 ox, oy;
2169 
2170 	    makeEquipNoise(ITEMSLOT_FEET);
2171 	    nextmap = glbCurLevel->getMapDown();
2172 	    if (!nextmap)
2173 	    {
2174 		formatAndReport("%U <be> stopped by an invisible barrier.");
2175 		return true;
2176 	    }
2177 
2178 	    if (!nextmap->isUnlocked())
2179 	    {
2180 		formatAndReport("%U <find> that the ladder is locked.");
2181 
2182 		attemptunlock();
2183 		return false;
2184 	    }
2185 
2186 	    // See if there is room...
2187 	    if (!nextmap->findTile(SQUARE_LADDERUP, x, y) ||
2188 		!nextmap->findCloseTile(x, y, getMoveType()))
2189 	    {
2190 		formatAndReport("%U cannot find room at the end of the ladder.");
2191 		return true;
2192 	    }
2193 
2194 	    if (canFly())
2195 		formatAndReport("%U <fly> down the ladder.");
2196 	    else
2197 		formatAndReport("%U <climb> the ladder.");
2198 
2199 	    ox = getX();
2200 	    oy = getY();
2201 
2202 	    // Move to it...
2203 	    glbCurLevel->unregisterMob(this);
2204 	    if (!move(x, y, true))
2205 		return true;
2206 	    nextmap->registerMob(this);
2207 
2208 	    glbCurLevel->chaseMobOnStaircase(SQUARE_LADDERUP,
2209 					     ox, oy, this, nextmap);
2210 
2211 	    if (isAvatar())
2212 	    {
2213 		glbSuppressAutoClimb = true;
2214 
2215 		MAP::changeCurrentLevel(nextmap);
2216 		// Need to rebuild our screen!  Refreshing will be handled
2217 		// by the message wait code.
2218 		gfx_scrollcenter(getX(), getY());
2219 		glbCurLevel->buildFOV(getX(), getY(), 7, 5);
2220 		if (!hasIntrinsic(INTRINSIC_BLIND))
2221 		    glbCurLevel->markMapped(getX(), getY(), 7, 5);
2222 
2223 		glbCurLevel->describeSquare(getX(), getY(),
2224 				    hasIntrinsic(INTRINSIC_BLIND),
2225 				    false);
2226 	    }
2227 
2228 	    return true;
2229 	}
2230 
2231 	case SQUARE_DIMDOOR:
2232 	{
2233 	    // Climb to the portal map.
2234 	    MAP		*nextmap;
2235 	    int		 x, y;
2236 	    int		 ox, oy;
2237 
2238 	    // Make sure you have the dimdoor key!
2239 	    if (!hasItem(ITEM_YRUNE))
2240 	    {
2241 		formatAndReport("The portal refuses to open without the key.");
2242 		return true;
2243 	    }
2244 
2245 	    makeEquipNoise(ITEMSLOT_FEET);
2246 	    nextmap = glbCurLevel->getMapBranch();
2247 	    if (!nextmap || !nextmap->isUnlocked())
2248 	    {
2249 		formatAndReport("%U <recoil> from a seeming infinite abyss.");
2250 		return true;
2251 	    }
2252 
2253 	    // See if there is room...
2254 	    if (!nextmap->findTile(SQUARE_DIMDOOR, x, y) ||
2255 		!nextmap->findCloseTile(x, y, getMoveType()))
2256 	    {
2257 		formatAndReport("%U cannot find room at the end of the portal.");
2258 		return true;
2259 	    }
2260 
2261 	    if (canFly())
2262 		formatAndReport("%U <fly> into the portal.");
2263 	    else
2264 		formatAndReport("%U <climb> into the portal.");
2265 
2266 	    ox = getX();
2267 	    oy = getY();
2268 
2269 	    // Move to it...
2270 	    glbCurLevel->unregisterMob(this);
2271 	    if (!move(x, y, true))
2272 		return true;
2273 	    nextmap->registerMob(this);
2274 
2275 	    glbCurLevel->chaseMobOnStaircase(SQUARE_DIMDOOR,
2276 					     ox, oy, this, nextmap);
2277 
2278 	    if (isAvatar())
2279 	    {
2280 		MAP::changeCurrentLevel(nextmap);
2281 		// Need to rebuild our screen!  Refreshing will be handled
2282 		// by the message wait code.
2283 		gfx_scrollcenter(getX(), getY());
2284 		glbCurLevel->buildFOV(getX(), getY(), 7, 5);
2285 		if (!hasIntrinsic(INTRINSIC_BLIND))
2286 		    glbCurLevel->markMapped(getX(), getY(), 7, 5);
2287 
2288 		glbCurLevel->describeSquare(getX(), getY(),
2289 				    hasIntrinsic(INTRINSIC_BLIND),
2290 				    false);
2291 		BRANCH_NAMES	branch = glbCurLevel->branchName();
2292 		formatAndReport(glb_branchdefs[branch].welcome);
2293 	    }
2294 
2295 	    return true;
2296 	}
2297 
2298 	case SQUARE_LAVA:
2299 	{
2300 	    formatAndReport("%U <dive> into the lava.");
2301 	    setIntrinsic(INTRINSIC_SUBMERGED);
2302 	    // Report what we stepped on...
2303 	    if (isAvatar())
2304 	    {
2305 		glbCurLevel->describeSquare(getX(), getY(),
2306 					    hasIntrinsic(INTRINSIC_BLIND),
2307 					    false);
2308 	    }
2309 
2310 	    return true;
2311 	}
2312 
2313 	case SQUARE_WATER:
2314 	{
2315 	    formatAndReport("%U <dive> into the water.");
2316 	    setIntrinsic(INTRINSIC_SUBMERGED);
2317 	    // Report what we stepped on...
2318 	    if (isAvatar())
2319 	    {
2320 		glbCurLevel->describeSquare(getX(), getY(),
2321 					    hasIntrinsic(INTRINSIC_BLIND),
2322 					    false);
2323 	    }
2324 
2325 	    return true;
2326 	}
2327 
2328 	case SQUARE_ACID:
2329 	{
2330 	    formatAndReport("%U <dive> into the acid.");
2331 	    setIntrinsic(INTRINSIC_SUBMERGED);
2332 	    // Report what we stepped on...
2333 	    if (isAvatar())
2334 	    {
2335 		glbCurLevel->describeSquare(getX(), getY(),
2336 					    hasIntrinsic(INTRINSIC_BLIND),
2337 					    false);
2338 	    }
2339 
2340 	    return true;
2341 	}
2342 
2343 	case SQUARE_FOREST:
2344 	case SQUARE_FORESTFIRE:
2345 	    if (hasIntrinsic(INTRINSIC_INTREE))
2346 	    {
2347 		if (canFly())
2348 		    formatAndReport("%U <fly> down from the tree.");
2349 		else
2350 		    formatAndReport("%U <climb> down the tree.");
2351 
2352 		clearIntrinsic(INTRINSIC_INTREE);
2353 
2354 		return true;
2355 	    }
2356 	    else
2357 	    {
2358 		// FALLTHROUGH
2359 	    }
2360 
2361 	default:
2362 	    formatAndReport("%U <scrabble> at the floor!");
2363 	    return false;
2364     }
2365 }
2366 
2367 bool
actionPickUp(ITEM * item,ITEM ** newitem)2368 MOB::actionPickUp(ITEM *item, ITEM **newitem)
2369 {
2370     BUF		buf;
2371     bool		 submerged;
2372 
2373     submerged = hasIntrinsic(INTRINSIC_INPIT) ||
2374 		hasIntrinsic(INTRINSIC_SUBMERGED);
2375 
2376     if (hasIntrinsic(INTRINSIC_INTREE))
2377     {
2378 	// Items never stay in tree branches, so people in tree
2379 	// can't get anything.
2380 	formatAndReport("%U cannot reach %IU.", item);
2381 	return false;
2382     }
2383 
2384     if (!item)
2385     {
2386 	formatAndReport("%U <grope> on the floor foolishly!");
2387 	return false;
2388     }
2389 
2390     // Verify the item is in square.
2391     if (item->getX() != getX() || item->getY() != getY())
2392     {
2393 	formatAndReport("%U cannot reach %IU.", item);
2394 	return false;
2395     }
2396 
2397     // Verify we can reach it.
2398     if (item->isBelowGrade() != submerged)
2399     {
2400 	formatAndReport("%U cannot reach %IU.", item);
2401 	return false;
2402     }
2403 
2404     glbCurLevel->dropItem(item);
2405 
2406     // Try to acquire the item ourselves....
2407     // We must get the item name first as the item may be deleted by
2408     // acquireItem.
2409     BUF		itemname = item->getName();
2410 
2411     if (acquireItem(item, newitem))
2412     {
2413 	// Success!
2414 	buf = formatToString("%U <pick> up %B1.", this, 0, 0, 0,
2415 		itemname.buffer());
2416 	reportMessage(buf);
2417     }
2418     else
2419     {
2420 	// Drop back on the floor.
2421 	glbCurLevel->acquireItem(item, getX(), getY(), this, newitem);
2422 	buf = formatToString("%U <try> to pick up %B1 and <fail>.",
2423 		this, 0, 0, 0,
2424 		itemname.buffer());
2425 	reportMessage(buf);
2426 
2427 	return false;
2428     }
2429 
2430     return true;
2431 }
2432 
2433 bool
actionDrop(int ix,int iy)2434 MOB::actionDrop(int ix, int iy)
2435 {
2436     ITEM		*item, *drop;
2437 
2438     item = getItem(ix, iy);
2439 
2440     // Verify there is an item.
2441     if (!item)
2442     {
2443 	formatAndReport("%U <drop> nothing.");
2444 	return true;
2445     }
2446 
2447     // Verify the item is not equipped.
2448     if (!ix)
2449     {
2450 	formatAndReport("%U cannot drop %IU because it equipped.", item);
2451 	return true;
2452     }
2453 
2454     // Drop the item...
2455     drop = dropItem(ix, iy);
2456     UT_ASSERT(drop == item);
2457 
2458     // Report the item dropped before acquiring to the ground,
2459     // this prevents out of order messages if it then sinks.
2460     formatAndReport("%U <drop> %IU.", item);
2461 
2462     // Mark the item below grade if we are.
2463     if (hasIntrinsic(INTRINSIC_INPIT) ||
2464 	hasIntrinsic(INTRINSIC_SUBMERGED))
2465     {
2466 	drop->markBelowGrade(true);
2467     }
2468 
2469     // Check if item falls to the ground
2470     if (hasIntrinsic(INTRINSIC_INTREE))
2471     {
2472 	formatAndReport("%IU <I:drop> to the ground.", item);
2473 	// Try to break the item
2474 	if (item->doBreak(10, this, 0, glbCurLevel, getX(), getY()))
2475 	    return true;
2476     }
2477 
2478     glbCurLevel->acquireItem(drop, getX(), getY(), this);
2479     return true;
2480 }
2481 
2482 static int
glb_itemsortcompare(const void * v1,const void * v2)2483 glb_itemsortcompare(const void *v1, const void *v2)
2484 {
2485     const ITEM	*i1, *i2;
2486 
2487     i1 = *((const ITEM **) v1);
2488     i2 = *((const ITEM **) v2);
2489 
2490     // Favorite items always go first.
2491     if (i1->isFavorite() && !i2->isFavorite())
2492 	return -1;
2493     if (!i1->isFavorite() && i2->isFavorite())
2494 	return 1;
2495 
2496     if (i1->getItemType() < i2->getItemType())
2497 	return -1;
2498     if (i1->getItemType() > i2->getItemType())
2499 	return 1;
2500 
2501     // Itemtypes are equal, sort by exact item.
2502     if (i1->getDefinition() < i2->getDefinition())
2503 	return -1;
2504     if (i1->getDefinition() > i2->getDefinition())
2505 	return 1;
2506 
2507     int			comp;
2508 
2509     // Compare our item names.
2510     comp = i1->getName().strcmp(i2->getName());
2511     if (comp != 0)
2512 	return comp;
2513     return 0;
2514 }
2515 
2516 bool
actionSort()2517 MOB::actionSort()
2518 {
2519     formatAndReport("%U <rummage> through %r backpack.");
2520 
2521     // Sort all the items...
2522     ITEM		*item;
2523     int			 numitems, i, j;
2524 
2525     // Count how many items we have so we can make a flat array.
2526     numitems = 0;
2527     for (item = myInventory; item; item = item->getNext())
2528     {
2529 	// Only counts if item is not equipped, as we don't sort
2530 	// equipped items.
2531 	if (item->getX() == 0)
2532 	    continue;
2533 
2534 	numitems++;
2535     }
2536 
2537     // This is a trivially true path.
2538     if (!numitems)
2539 	return false;
2540 
2541     ITEM		**flatarray;
2542 
2543     // Create a flat array of all the items.
2544     flatarray = new ITEM *[numitems];
2545 
2546     i = 0;
2547     for (item = myInventory; item; item = item->getNext())
2548     {
2549 	// Only counts if item is not equipped, as we don't sort
2550 	// equipped items.
2551 	if (item->getX() == 0)
2552 	    continue;
2553 
2554 	flatarray[i] = item;
2555 	i++;
2556     }
2557 
2558     // Now, we sort the item pointers according to our rules:
2559     qsort(flatarray, numitems, sizeof(ITEM *), glb_itemsortcompare);
2560 
2561     // Now merge adjacent items.  We rely on identical items
2562     // getting sorted together.  This likely fails for id vs non id
2563     // cases.
2564     // We thus escape this by continuing to look for merge potentials
2565     // until we get the first item with a different definition.
2566 #if 1
2567     for (i = 0; i < numitems; i++)
2568     {
2569 	// If this entry is null, we already merged it
2570 	if (!flatarray[i])
2571 	    continue;
2572 
2573 	for (j = i+1; j < numitems; j++)
2574 	{
2575 	    // Skip already merged items.
2576 	    if (!flatarray[j])
2577 		continue;
2578 
2579 	    // If the items have different definitions, we must be done.
2580 	    if (flatarray[i]->getDefinition() != flatarray[j]->getDefinition())
2581 		break;
2582 
2583 	    // Check to see if we can merge item j onto item i.
2584 	    if (flatarray[i]->canMerge(flatarray[j]))
2585 	    {
2586 		ITEM		*drop;
2587 
2588 		drop = dropItem(flatarray[j]->getX(), flatarray[j]->getY());
2589 		if (drop != flatarray[j])
2590 		{
2591 		    UT_ASSERT(!"Drop failed!");
2592 		    break;
2593 		}
2594 		else
2595 		{
2596 		    flatarray[i]->mergeStack(flatarray[j]);
2597 		    delete flatarray[j];
2598 		    flatarray[j] = 0;
2599 		}
2600 	    }
2601 	}
2602     }
2603 #endif
2604 
2605     // Now, using the flat array, assign new item positions for everything.
2606     j = 0;
2607     for (i = 0; i < numitems; i++)
2608     {
2609 	if (flatarray[i])
2610 	{
2611 	    flatarray[i]->setPos( (j / MOBINV_HEIGHT) + 1,
2612 				   j % MOBINV_HEIGHT );
2613 	    j++;
2614 	}
2615     }
2616 
2617     delete [] flatarray;
2618 
2619     return false;
2620 }
2621 
2622 bool
actionEat(int ix,int iy)2623 MOB::actionEat(int ix, int iy)
2624 {
2625     ITEM		*item, *drop, *newitem = 0, *stackitem = 0;
2626     BUF		buf;
2627     bool		 doeat = false, triedeat = false;
2628     bool		 skippoison = false;
2629     int			 foodval = 0;
2630 
2631     item = getItem(ix, iy);
2632 
2633     // Verify there is an item.
2634     if (!item)
2635     {
2636 	formatAndReport("%U <eat> thin air.");
2637 	return false;
2638     }
2639 
2640     // Verify the item is not equipped.
2641     if (!ix)
2642     {
2643 	// Exception - you can eat from left and right hands.
2644 	if (iy != ITEMSLOT_LHAND && iy != ITEMSLOT_RHAND)
2645 	{
2646 	    formatAndReport("%U cannot eat %IU because it is equipped.", item);
2647 	    return false;
2648 	}
2649     }
2650 
2651     if (getHungerLevel() >= HUNGER_FULL)
2652     {
2653 	formatAndReport("%U <be> too full to eat anything more.");
2654 	return false;
2655     }
2656 
2657     if (item->getStackCount() > 1)
2658     {
2659 	stackitem = item;
2660 	item = stackitem->splitStack(1);
2661     }
2662 
2663     switch (item->getMagicType())
2664     {
2665 	case MAGICTYPE_POTION:
2666 	{
2667 	    int		magicclass;
2668 
2669 	    doeat = true;
2670 	    triedeat = true;
2671 
2672 	    // Magic potions have a food value of 10.
2673 	    foodval = 10;
2674 
2675 	    newitem = ITEM::create(ITEM_BOTTLE, false, true);
2676 
2677 	    // Print the drink message....
2678 	    formatAndReport("%U <drink> %IU.", item);
2679 
2680 	    // Get the magic type...
2681 	    magicclass = item->getMagicClass();
2682 	    UT_ASSERT(item->getMagicType() == MAGICTYPE_POTION);
2683 
2684 	    // If it is ided on drinking, make it so!
2685 	    // If it is ided on drinking, also id it if the avatar
2686 	    // can see the drinker.
2687 	    if (glb_potiondefs[magicclass].autoid &&
2688 		(isAvatar() ||
2689 		 (MOB::getAvatar() && MOB::getAvatar()->canSense(this))))
2690 		item->markIdentified();
2691 
2692 	    // Apply any bonus effects due to it being an artifact.
2693 	    if (item->isArtifact())
2694 	    {
2695 		const ARTIFACT		*art;
2696 		const char		*intrinsics;
2697 		int  			 min_range, max_range;
2698 
2699 		if (item->isBlessed())
2700 		    max_range = 2000;
2701 		else
2702 		    max_range = 500;
2703 		if (item->isCursed())
2704 		    min_range = 2;
2705 		else
2706 		    min_range = 300;
2707 
2708 		art = item->getArtifact();
2709 		intrinsics = art->intrinsics;
2710 		while (intrinsics && *intrinsics)
2711 		{
2712 		    setTimedIntrinsic(this,
2713 			    (INTRINSIC_NAMES) *intrinsics,
2714 			    rand_range(min_range, max_range));
2715 		    intrinsics++;
2716 		}
2717 	    }
2718 
2719 	    // Apply the effect:
2720 	    switch ((POTION_NAMES) magicclass)
2721 	    {
2722 		case POTION_HEAL:
2723 		    {
2724 			int		heal;
2725 
2726 			if (item->isBlessed())
2727 			    heal = rand_dice(1, 10, 10);
2728 			else if (item->isCursed())
2729 			    heal = rand_dice(1, 10, 0);
2730 			else
2731 			    heal = rand_dice(1, 20, 0);
2732 
2733 			if (receiveHeal(heal, this))
2734 			{
2735 			    formatAndReport("The %Iu <I:heal> %U.", item);
2736 
2737 			    if (isAvatar() ||
2738 				(MOB::getAvatar() &&
2739 				 MOB::getAvatar()->canSense(this)))
2740 				item->markIdentified();
2741 			}
2742 			else
2743 			{
2744 			    // Grant bonus magic...
2745 			    if (item->isBlessed())
2746 				heal = rand_dice(3, 2, -2);
2747 			    else if (item->isCursed())
2748 				heal = 1;
2749 			    else
2750 				heal = rand_dice(2, 2, -1);
2751 
2752 			    incrementMaxHP(heal);
2753 
2754 			    formatAndReport("%U <feel> more robust.");
2755 			    if (isAvatar() ||
2756 				(MOB::getAvatar() &&
2757 				 MOB::getAvatar()->canSense(this)))
2758 				item->markIdentified();
2759 			}
2760 			break;
2761 		    }
2762 
2763 		case POTION_ACID:
2764 		    if (!receiveDamage(ATTACK_ACIDPOTION, 0, item, 0,
2765 					ATTACKSTYLE_MISC))
2766 		    {
2767 			// Do nothing more if we die.
2768 			// TODO: This will then drop the bottle we ate???
2769 			if (stackitem)
2770 			    delete item;
2771 			return true;
2772 		    }
2773 		    break;
2774 
2775 		case POTION_BLIND:
2776 		    {
2777 			int		turns;
2778 
2779 			// You get 3d20 turns of blindness.
2780 			if (item->isBlessed())
2781 			    turns = rand_dice(3, 10, 30);
2782 			else if (item->isCursed())
2783 			    turns = rand_dice(3, 10, 0);
2784 			else
2785 			    turns = rand_dice(3, 20, 0);
2786 
2787 			// Determine if we notice anything...
2788 			if (isAvatar() &&
2789 			    !hasIntrinsic(INTRINSIC_BLIND))
2790 			{
2791 			    item->markIdentified();
2792 			}
2793 
2794 			setTimedIntrinsic(this, INTRINSIC_BLIND, turns);
2795 		    }
2796 		    break;
2797 
2798 		case POTION_POISON:
2799 		    {
2800 			int		turns;
2801 			POISON_NAMES	poison;
2802 
2803 			turns = rand_dice(4, 5, 0);
2804 			if (item->isBlessed())
2805 			    poison = POISON_STRONG;
2806 			else if (item->isCursed())
2807 			    poison = POISON_MILD;
2808 			else
2809 			    poison = POISON_NORMAL;
2810 
2811 			setTimedIntrinsic(this, (INTRINSIC_NAMES)
2812 				          glb_poisondefs[poison].intrinsic,
2813 					  turns);
2814 			break;
2815 		    }
2816 
2817 		case POTION_CURE:
2818 		    {
2819 			bool		add_resist, cured;
2820 
2821 			add_resist = item->isBlessed();
2822 			cured = receiveCure();
2823 
2824 			if (add_resist)
2825 			{
2826 			    // We grant poison resistance for a 5 + 1d5 turns.
2827 			    formatAndReport("%R metabolism stabilizes.");
2828 			    setTimedIntrinsic(this, INTRINSIC_RESISTPOISON,
2829 						    rand_dice(1, 5, 6));
2830 			}
2831 			if (cured)
2832 			{
2833 			    formatAndReport("The poison is expunged from %R veins.");
2834 			}
2835 
2836 			// Check for identification
2837 			if (cured || add_resist)
2838 			{
2839 			    if (isAvatar() ||
2840 				(MOB::getAvatar() &&
2841 				 MOB::getAvatar()->canSense(this)))
2842 				item->markIdentified();
2843 			}
2844 			else
2845 			{
2846 			    formatAndReport("Nothing happens.");
2847 			}
2848 			break;
2849 		    }
2850 
2851 		case POTION_GREEKFIRE:
2852 		    {
2853 			formatAndReport("The liquid catches on fire as it leaves the bottle!  Burning liquid covers %U!");
2854 
2855 			if (!receiveDamage(ATTACK_GREEKFIREPOTION, 0, item, 0,
2856 					    ATTACKSTYLE_MISC))
2857 			{
2858 			    // Do nothing more if we die.
2859 			    // TODO: Does this get rid of the bottle?
2860 			    if (stackitem)
2861 				delete item;
2862 			    return true;
2863 			}
2864 			break;
2865 		    }
2866 
2867 		case POTION_ENLIGHTENMENT:
2868 		    {
2869 			formatAndReport("%U <gain> insight.");
2870 
2871 			if (isAvatar())
2872 			{
2873 			    glbShowIntrinsic(this);
2874 			}
2875 			break;
2876 		    }
2877 
2878 		case POTION_SMOKE:
2879 		    {
2880 			formatAndReport("Smoke billows out of %IU.", item);
2881 
2882 			ITEM::setZapper(this);
2883 
2884 			// Identification is handled by the grenadecallback.
2885 			glbCurLevel->fireBall(getX(), getY(), 1, true,
2886 					ITEM::grenadeCallbackStatic,
2887 					item);
2888 			break;
2889 		    }
2890 
2891 		case POTION_MANA:
2892 		    {
2893 			int		mana;
2894 
2895 			if (item->isBlessed())
2896 			    mana = rand_dice(3, 10, 30);
2897 			else if (item->isCursed())
2898 			    mana = rand_dice(3, 10, 0);
2899 			else
2900 			    mana = rand_dice(3, 20, 0);
2901 
2902 			if (receiveMana(mana, this))
2903 			{
2904 			    formatAndReport("The %Iu <I:recharge> %U.", item);
2905 
2906 			    if (isAvatar() ||
2907 				(MOB::getAvatar() &&
2908 				 MOB::getAvatar()->canSense(this)))
2909 				item->markIdentified();
2910 			}
2911 			else
2912 			{
2913 			    // Grant bonus magic...
2914 			    if (item->isBlessed())
2915 				mana = rand_dice(3, 2, -2);
2916 			    else if (item->isCursed())
2917 				mana = 1;
2918 			    else
2919 				mana = rand_dice(2, 2, -1);
2920 
2921 			    incrementMaxMP(mana);
2922 
2923 			    formatAndReport("%U <receive> a boost.");
2924 			    if (isAvatar() ||
2925 				(MOB::getAvatar() &&
2926 				 MOB::getAvatar()->canSense(this)))
2927 				item->markIdentified();
2928 			}
2929 			break;
2930 		    }
2931 
2932 		case NUM_POTIONS:
2933 		    UT_ASSERT(!"Unknown potion class!");
2934 		    break;
2935 	    }
2936 
2937 	    break;
2938 	}
2939 
2940 	case MAGICTYPE_SPELLBOOK:
2941 	{
2942 	    SPELLBOOK_NAMES	book = (SPELLBOOK_NAMES) item->getMagicClass();
2943 
2944 	    const u8		*spells;
2945 	    int  		 min_range, max_range;
2946 
2947 	    if (!canDigest(item) || glb_itemdefs[item->getDefinition()].isquest)
2948 	    {
2949 		// If you can't actually eat the book, you shouldn't
2950 		// gain the bonus.
2951 		break;
2952 	    }
2953 
2954 	    if (item->isBlessed())
2955 		max_range = 200;
2956 	    else
2957 		max_range = 80;
2958 	    if (item->isCursed())
2959 		min_range = 2;
2960 	    else
2961 		min_range = 30;
2962 
2963 	    min_range *= item->getCharges();
2964 	    max_range *= item->getCharges();
2965 
2966 	    spells = (const u8 *) glb_spellbookdefs[book].spells;
2967 	    while (spells && *spells)
2968 	    {
2969 		setTimedIntrinsic(this,
2970 			(INTRINSIC_NAMES) glb_spelldefs[*spells].intrinsic,
2971 			rand_range(min_range, max_range));
2972 		spells++;
2973 	    }
2974 	    spells = (const u8 *) glb_spellbookdefs[book].skills;
2975 	    while (spells && *spells)
2976 	    {
2977 		setTimedIntrinsic(this,
2978 			(INTRINSIC_NAMES) glb_skilldefs[*spells].intrinsic,
2979 			rand_range(min_range, max_range));
2980 		spells++;
2981 	    }
2982 	    break;
2983 	}
2984 
2985 	case MAGICTYPE_NONE:
2986 	case MAGICTYPE_SCROLL:
2987 	case MAGICTYPE_RING:
2988 	case MAGICTYPE_HELM:
2989 	case MAGICTYPE_WAND:
2990 	case MAGICTYPE_AMULET:
2991 	case MAGICTYPE_BOOTS:
2992 	case MAGICTYPE_STAFF:
2993 	    break;
2994 
2995 	case NUM_MAGICTYPES:
2996 	    UT_ASSERT(!"Invalid magic type");
2997 	    break;
2998     }
2999 
3000     switch (item->getDefinition())
3001     {
3002 	case ITEM_WATER:
3003 	{
3004 	    doeat = true;
3005 	    triedeat = true;
3006 	    foodval = 1;
3007 
3008 	    newitem = ITEM::create(ITEM_BOTTLE, false, true);
3009 
3010 	    // Print the drink message....
3011 	    formatAndReport("%U <drink> %IU.", item);
3012 
3013 	    // Apply any bonus effects due to it being an artifact.
3014 	    if (item->isArtifact())
3015 	    {
3016 		const ARTIFACT		*art;
3017 		const char		*intrinsics;
3018 		int  			 min_range, max_range;
3019 
3020 		if (item->isBlessed())
3021 		    max_range = 2000;
3022 		else
3023 		    max_range = 500;
3024 		if (item->isCursed())
3025 		    min_range = 2;
3026 		else
3027 		    min_range = 300;
3028 
3029 		art = item->getArtifact();
3030 		intrinsics = art->intrinsics;
3031 		while (intrinsics && *intrinsics)
3032 		{
3033 		    setTimedIntrinsic(this,
3034 			    (INTRINSIC_NAMES) *intrinsics,
3035 			    rand_range(min_range, max_range));
3036 		    intrinsics++;
3037 		}
3038 	    }
3039 
3040 	    // If you are undead, should not be a pleasant experience.
3041 	    if (item->isBlessed() && getMobType() == MOBTYPE_UNDEAD)
3042 	    {
3043 		if (!receiveDamage(ATTACK_ACIDPOTION, 0, item, 0,
3044 				ATTACKSTYLE_MISC))
3045 		{
3046 		    // Do nothing more if we die.
3047 		    // TODO: This will then drop the bottle we ate???
3048 		    if (stackitem)
3049 			delete item;
3050 		    return true;
3051 		}
3052 	    }
3053 	    break;
3054 	}
3055 
3056 	case ITEM_BOTTLE:
3057 	    doeat = true;
3058 	    triedeat = true;
3059 	    foodval = 0;
3060 	    formatAndReport("%U <munch> on broken glass.");
3061 	    if (!receiveDamage(ATTACK_GLASSSHARDS, 0, item, 0,
3062 				ATTACKSTYLE_MISC))
3063 	    {
3064 		// Do nothing more if we die.
3065 		// TODO: This will then drop the bottle we ate???
3066 		if (stackitem)
3067 		    delete item;
3068 		return true;
3069 	    }
3070 	    break;
3071 
3072 	case ITEM_BLACKHEART:
3073 	    {
3074 		formatAndReport("%U <consider> eating %IU, but sanity prevails.", item);
3075 		doeat = false;
3076 		triedeat = true;
3077 		break;
3078 	    }
3079 
3080 	default:
3081 	    break;
3082     }
3083 
3084     if (!triedeat)
3085     {
3086 	// Determine if it is the sort of thing we
3087 	// can digest.
3088 	if (canDigest(item) && item->defn().isquest)
3089 	{
3090 	    triedeat = true;
3091 	    formatAndReport("Powerful magic forces prevent %U from eating %IU.", item);
3092 	}
3093 	else if (canDigest(item))
3094 	{
3095 	    MOB		*corpse;
3096 	    const char	*verb;
3097 	    const char	*verbchoice[] =
3098 	    {
3099 		"%U <consume> %IU.",
3100 		"%U <eat> %IU.",
3101 		"%U <dine> on %IU.",
3102 		"%U <munch> on %IU.",
3103 		0
3104 	    };
3105 
3106 	    verb = rand_string(verbchoice);
3107 
3108 	    triedeat = true;
3109 	    formatAndReport(verb, item);
3110 
3111 	    // How much health do we get?  Depends on the item...
3112 	    // Using weight seemed clever at the time, but tiny corpses
3113 	    // like mice have a foodval of 50!
3114 	    // Thus the 10 times multiplier
3115 	    foodval = item->getWeight() * 10;
3116 	    corpse = item->getCorpseMob();
3117 	    if (corpse)
3118 	    {
3119 		bool		isbones;
3120 		INTRINSIC_NAMES	intrinsic;
3121 
3122 		isbones = item->getDefinition() == ITEM_BONES;
3123 
3124 		foodval = glb_sizedefs[corpse->getSize()].foodval;
3125 		if (isbones)
3126 		    foodval /= 2;
3127 
3128 		// Bonus food if you have butchery.
3129 		if (hasSkill(SKILL_BUTCHERY))
3130 		    foodval += foodval/2;
3131 
3132 		if (corpse->defn().acidiccorpse)
3133 		{
3134 		    clearIntrinsic(INTRINSIC_STONING);
3135 		    if (!hasIntrinsic(INTRINSIC_RESISTACID) ||
3136 			 hasIntrinsic(INTRINSIC_VULNACID))
3137 		    {
3138 			if (isAvatar())
3139 			{
3140 			    formatAndReport("%r stomach burns!");
3141 			}
3142 
3143 			if (!receiveDamage(ATTACK_ACIDICCORPSE, 0, item, 0,
3144 					    ATTACKSTYLE_MISC))
3145 			{
3146 			    // Do nothing more if we die.
3147 			    if (stackitem)
3148 				delete item;
3149 			    return true;
3150 			}
3151 		    }
3152 		}
3153 
3154 		for (intrinsic = INTRINSIC_NONE; intrinsic < NUM_INTRINSICS;
3155 		     intrinsic = (INTRINSIC_NAMES) (intrinsic+1))
3156 		{
3157 		    if (INTRINSIC::hasIntrinsic(
3158 				corpse->defn().eatgrant,
3159 				intrinsic))
3160 		    {
3161 			// Don't grant poison if we can butcher
3162 			// properly.
3163 			if (hasSkill(SKILL_BUTCHERY))
3164 			{
3165 			    if (glb_intrinsicdefs[intrinsic].ispoison)
3166 				continue;
3167 			}
3168 			if (hasSkill(SKILL_BUTCHERY) || rand_chance(50))
3169 			{
3170 			    int		duration;
3171 
3172 			    // We cap poison duration to something sensible,
3173 			    // most inflicted poison attacks are 5d3, so
3174 			    // we just go with the 5d5
3175 			    if (glb_intrinsicdefs[intrinsic].ispoison)
3176 				duration = rand_dice(5, 5, 0);
3177 			    else
3178 				duration = foodval + rand_choice(foodval);
3179 
3180 			    setTimedIntrinsic(this, intrinsic, duration);
3181 			}
3182 		    }
3183 		}
3184 
3185 		// If we have the butchery skill, we can also
3186 		// remove any poison from the surface
3187 		if (hasSkill(SKILL_BUTCHERY))
3188 		    skippoison = true;
3189 
3190 		// If the corpse grants magic, add a bonus.
3191 		int 	mpbonus;
3192 
3193 		mpbonus = rand_dice(corpse->defn().eatmp);
3194 		if (mpbonus)
3195 		{
3196 		    incrementMaxMP(mpbonus);
3197 		    formatAndReport("%U <receive> a boost.");
3198 		}
3199 	    }
3200 
3201 	    doeat = true;
3202 	}
3203 	else
3204 	{
3205 	    formatAndReport("%U cannot digest %IU.", item);
3206 	}
3207     }
3208 
3209     if (doeat)
3210     {
3211 	pietyEat(item, foodval);
3212 	if (!skippoison)
3213 	    item->applyPoison(this, this, true);
3214 
3215 	// Remove the item from our inventory.
3216 	// If it was stacked, we can just delete item.
3217 	if (stackitem)
3218 	{
3219 	    delete item;
3220 	}
3221 	else
3222 	{
3223 	    drop = dropItem(ix, iy);
3224 	    UT_ASSERT(drop == item);
3225 	    delete drop;
3226 	}
3227 
3228 	// Place the result, if any.
3229 	if (newitem)
3230 	{
3231 	    if (!acquireItem(newitem))
3232 	    {
3233 		// We failed to acquire the item.  Likely out of room.
3234 		formatAndReport("%U <discard> %IU on the ground.", newitem);
3235 
3236 		glbCurLevel->acquireItem(newitem, getX(), getY(), this);
3237 	    }
3238 	}
3239 
3240 	// Feed the mob the foodvalue.
3241 	feed(foodval);
3242     }
3243     else
3244     {
3245 	// Failed to eat.  If we ripped this off a stack,
3246 	// we need to add it back to a stack.
3247 	if (stackitem)
3248 	{
3249 	    stackitem->mergeStack(item);
3250 	    delete item;
3251 	}
3252     }
3253 
3254     return true;
3255 }
3256 
3257 bool
actionRead(int ix,int iy)3258 MOB::actionRead(int ix, int iy)
3259 {
3260     ITEM		*item, *drop, *newitem = 0, *itemstack = 0;
3261     BUF			 buf;
3262     bool		 doread = false;
3263     bool		 costturn = true;
3264 
3265     item = getItem(ix, iy);
3266 
3267     // Verify there is an item.
3268     if (!item)
3269     {
3270 	formatAndReport("%U <read> thin air.");
3271 	return false;
3272     }
3273 
3274     // Check to ensure we are not blind.  Blind people can't read.
3275     if (hasIntrinsic(INTRINSIC_BLIND))
3276     {
3277 	formatAndReport("%U cannot read %IU, as %p <be> blind.", item);
3278 	return false;
3279     }
3280 
3281     // There is no objection to readding equipped scrolls.
3282     // Perhaps we should give a bonus some day?
3283 
3284     if (item->getStackCount() > 1)
3285     {
3286 	itemstack = item;
3287 	item = itemstack->splitStack(1);
3288     }
3289 
3290     // Check if its magic class is scroll..
3291     if (item->getMagicType() == MAGICTYPE_SCROLL)
3292     {
3293 	int		magicclass;
3294 
3295 	doread = true;
3296 
3297 	// Print the read message...
3298 	formatAndReport("%U <read> %IU.", item);
3299 
3300 	// Apply any bonus effects due to it being an artifact.
3301 	if (item->isArtifact())
3302 	{
3303 	    const ARTIFACT		*art;
3304 	    const char		*intrinsics;
3305 	    int  			 min_range, max_range;
3306 
3307 	    if (item->isBlessed())
3308 		max_range = 2000;
3309 	    else
3310 		max_range = 500;
3311 	    if (item->isCursed())
3312 		min_range = 2;
3313 	    else
3314 		min_range = 300;
3315 
3316 	    art = item->getArtifact();
3317 	    intrinsics = art->intrinsics;
3318 	    while (intrinsics && *intrinsics)
3319 	    {
3320 		setTimedIntrinsic(this,
3321 			(INTRINSIC_NAMES) *intrinsics,
3322 			rand_range(min_range, max_range));
3323 		intrinsics++;
3324 	    }
3325 	}
3326 
3327 	magicclass = item->getMagicClass();
3328 
3329 	// If it autoids on reading do so.
3330 	if (glb_scrolldefs[magicclass].autoid &&
3331 	    (isAvatar() ||
3332 	     (MOB::getAvatar() && MOB::getAvatar()->canSense(this))) )
3333 	    item->markIdentified();
3334 
3335 	// Apply any contact poison.
3336 	item->applyPoison(this, this);
3337 
3338 	// Apply the effect:
3339 	switch ((SCROLL_NAMES) magicclass)
3340 	{
3341 	    case SCROLL_ID:
3342 	    {
3343 		formatAndReport("%U <be> more aware of %r possessions!");
3344 
3345 		if (isAvatar())
3346 		{
3347 		    // ID all items...
3348 		    ITEM		*id;
3349 		    int		 x, y;
3350 
3351 		    for (y = 0; y < MOBINV_HEIGHT; y++)
3352 			for (x = 0; x < MOBINV_WIDTH; x++)
3353 			{
3354 			    id = getItem(x, y);
3355 			    if (id)
3356 				id->markIdentified();
3357 			}
3358 		}
3359 		break;
3360 	    }
3361 	    case SCROLL_REMOVECURSE:
3362 	    {
3363 		if (item->isCursed())
3364 		{
3365 		    receiveCurse(false, false);
3366 		}
3367 		else
3368 		{
3369 		    receiveUncurse(item->isBlessed());
3370 		}
3371 		break;
3372 	    }
3373 	    case SCROLL_FIRE:
3374 	    {
3375 		int		x, y;
3376 		MOB		*oldself;
3377 		MOBREF		myself;
3378 
3379 		formatAndReport("A pillar of fire engulfs %U!");
3380 
3381 		x = getX();	y = getY();
3382 		oldself = this;
3383 		myself.setMob(this);
3384 		ourEffectAttack = ATTACK_FIRESCROLL;
3385 		glbCurLevel->fireBall(getX(), getY(), 1, true,
3386 				      areaAttackCBStatic, &myself);
3387 
3388 		// If die, return immediately.
3389 		if (oldself != glbCurLevel->getMob(x, y))
3390 		{
3391 		    if (itemstack)
3392 			delete item;
3393 		    return true;
3394 		}
3395 		break;
3396 	    }
3397 	    case SCROLL_LIGHT:
3398 	    {
3399 		int		radius = 5;
3400 		bool		islight = true;
3401 
3402 		if (item->isCursed())
3403 		{
3404 		    radius = 7;
3405 		    islight = false;
3406 		}
3407 		if (item->isBlessed())
3408 		    radius = 7;
3409 
3410 		if (islight)
3411 		{
3412 		    formatAndReport("A warm light spreads from %U.");
3413 		}
3414 		else
3415 		{
3416 		    formatAndReport("An inky blackness spreads from %U.");
3417 		}
3418 
3419 		// Draw a square radius light.
3420 		glbCurLevel->applyFlag(SQUAREFLAG_LIT, getX(), getY(), radius,
3421 					false, islight);
3422 		break;
3423 	    }
3424 	    case SCROLL_MAP:
3425 	    {
3426 		if (item->isCursed())
3427 		    formatAndReport("%U <be> less aware of %r surroundings.");
3428 		else if (item->isBlessed())
3429 		    formatAndReport("%U <be> very aware of %r surroundings.");
3430 		else
3431 		    formatAndReport("%U <be> more aware of %r surroundings.");
3432 
3433 		// If blessed, get entire level.
3434 		// If regular, get 10 radius (almost entire level)
3435 		// If cursed, lose level :>
3436 		if (isAvatar())
3437 		{
3438 		    if (item->isCursed())
3439 			glbCurLevel->applyFlag(SQUAREFLAG_MAPPED,
3440 				    getX(), getY(), MAP_WIDTH, false,
3441 				    false);
3442 		    else if (item->isBlessed())
3443 			glbCurLevel->markMapped(getX(), getY(),
3444 						MAP_WIDTH, MAP_HEIGHT,
3445 						false);
3446 		    else
3447 			glbCurLevel->markMapped(getX(), getY(),
3448 						10, 10,
3449 						false);
3450 		}
3451 		break;
3452 	    }
3453 	    case SCROLL_HEAL:
3454 	    {
3455 		formatAndReport("%R wounds are healed.");
3456 		receiveHeal(15 - (item->isCursed() * 5) + (item->isBlessed() * 5), this);
3457 		break;
3458 	    }
3459 	    case SCROLL_TELEPORT:
3460 	    {
3461 		actionTeleport(!item->isCursed(), item->isBlessed());
3462 		break;
3463 	    }
3464 	    case SCROLL_ENCHANTARMOUR:
3465 	    {
3466 		int		 enchantment;
3467 
3468 		enchantment = 1;
3469 		if (item->isCursed())
3470 		{
3471 		    enchantment = -1;
3472 		}
3473 		if (item->isBlessed())
3474 		{
3475 		    enchantment += rand_choice(2);	// 50% chance of +2.
3476 		}
3477 
3478 		receiveArmourEnchant((ITEMSLOT_NAMES) -1, enchantment, true);
3479 
3480 		break;
3481 	    }
3482 	    case SCROLL_ENCHANTWEAPON:
3483 	    {
3484 		int		 enchantment;
3485 
3486 		enchantment = 1;
3487 		if (item->isCursed())
3488 		{
3489 		    enchantment = -1;
3490 		}
3491 		if (item->isBlessed())
3492 		{
3493 		    if (rand_choice(2))
3494 		    {
3495 			enchantment++;
3496 		    }
3497 		}
3498 
3499 		receiveWeaponEnchant(enchantment, true);
3500 		break;
3501 	    }
3502 	    case SCROLL_MAKEARTIFACT:
3503 	    {
3504 		ITEM		*item;
3505 
3506 		item = getEquippedItem(ITEMSLOT_RHAND);
3507 		if (!item)
3508 		{
3509 		    // Oops!  This was wasted :>
3510 		    formatAndReport("%U <feel> omnipotent!");
3511 		    formatAndReport("The feeling passes.");
3512 		    // Do not consume the scroll!  That is just evil!
3513 		    doread = false;
3514 		    break;
3515 		}
3516 
3517 		// We have a valid item to make into an artifact.
3518 		// If it is already an artifact, nothing happens.
3519 		if (item->isArtifact())
3520 		{
3521 		    formatAndReport("%R %IU is already as all-powerful as it can get.", item);
3522 		    // Do not consume the scroll!  That is just evil!
3523 		    doread = false;
3524 		    break;
3525 		}
3526 
3527 		// Transform the item into an artifact.
3528 		formatAndReport("Eldritch energies from planes both higher and lower swirl through the air.");
3529 		formatAndReport("They condense into a thick plasma which surrounds %R %Iu!", item);
3530 		formatAndReport("%IU <I:be> now named by the gods!", item);
3531 
3532 		item->makeArtifact();
3533 
3534 		formatAndReport("%Ip <I:be> now known as %IU!", item);
3535 
3536 		break;
3537 	    }
3538 	    case NUM_SCROLLS:
3539 	    {
3540 		UT_ASSERT(!"INVALID SCROLL NUMBER!");
3541 		break;
3542 	    }
3543 	}
3544     }
3545     else if (item->getMagicType() == MAGICTYPE_SPELLBOOK)
3546     {
3547 	int		magicclass;
3548 
3549 	doread = true;
3550 
3551 	// Print the read message...
3552 	formatAndReport("%U <read> %IU.", item);
3553 
3554 	magicclass = item->getMagicClass();
3555 
3556 	// Apply any contact poison.
3557 	item->applyPoison(this, this);
3558 
3559 	// Ask the user what to read.
3560 	if (!isAvatar())
3561 	{
3562 	    UT_ASSERT(!"Reading not supported for non-avatar!");
3563 	    doread = false;
3564 	}
3565 	else
3566 	{
3567 	    int		 num_options;
3568 	    SKILL_NAMES	 skill = SKILL_NONE;
3569 	    SPELL_NAMES  spell = SPELL_NONE;
3570 
3571 	    num_options = strlen(glb_spellbookdefs[magicclass].spells) +
3572 			  strlen(glb_spellbookdefs[magicclass].skills);
3573 
3574 	    if (!num_options)
3575 	    {
3576 		// Nothing you can learn from this book!
3577 		doread = false;
3578 	    }
3579 	    else
3580 	    {
3581 		char	**options;
3582 		int	 *enumvalue;
3583 		bool	 *isspell;
3584 		int	  i;
3585 
3586 		// Remind the user the number of free slots.
3587 		if (strlen(glb_spellbookdefs[magicclass].spells))
3588 		{
3589 		    buf.sprintf("You have %d free %s %s.",
3590 			    getFreeSpellSlots(),
3591 			    "spell",
3592 			    ((getFreeSpellSlots() == 1) ? "slot" : "slots"));
3593 		}
3594 		else
3595 		{
3596 		    buf.sprintf("You have %d free %s %s.",
3597 			    getFreeSkillSlots(),
3598 			    "skill",
3599 			    ((getFreeSkillSlots() == 1) ? "slot" : "slots"));
3600 		}
3601 		reportMessage(buf);
3602 
3603 		options = new char *[num_options+2];
3604 		enumvalue = new int[num_options];
3605 		isspell = new bool[num_options];
3606 
3607 		const u8	*stuff;
3608 
3609 		i = 0;
3610 		for (stuff = (const u8 *)glb_spellbookdefs[magicclass].spells;
3611 		     *stuff;
3612 		     stuff++)
3613 		{
3614 		    // Add a spell.
3615 		    spell = (SPELL_NAMES) *stuff;
3616 
3617 		    options[i] = new char [strlen(glb_spelldefs[spell].name) + 5];
3618 
3619 		    strcpy(options[i], intrinsicFromWhatLetter((INTRINSIC_NAMES) glb_spelldefs[spell].intrinsic));
3620 
3621 		    strcat(options[i], glb_spelldefs[spell].name);
3622 		    isspell[i] = true;
3623 		    enumvalue[i] = spell;
3624 
3625 		    i++;
3626 		}
3627 		for (stuff = (const u8 *)glb_spellbookdefs[magicclass].skills;
3628 		     *stuff;
3629 		     stuff++)
3630 		{
3631 		    // Add a skill.
3632 		    skill = (SKILL_NAMES) *stuff;
3633 
3634 		    options[i] = new char [strlen(glb_skilldefs[skill].name) + 5];
3635 		    strcpy(options[i], intrinsicFromWhatLetter((INTRINSIC_NAMES) glb_skilldefs[skill].intrinsic));
3636 
3637 		    strcat(options[i], glb_skilldefs[skill].name);
3638 		    isspell[i] = false;
3639 		    enumvalue[i] = skill;
3640 
3641 		    i++;
3642 		}
3643 
3644 		UT_ASSERT(i == num_options);
3645 
3646 		// Add in the cancel option.
3647 		options[i++] = "cancel";
3648 
3649 		// Null terminate
3650 		options[i] = 0;
3651 
3652 		// And prompt for choice:
3653 		spell = SPELL_NONE;
3654 		skill = SKILL_NONE;
3655 
3656 		bool		tryagain = true;
3657 
3658 		while (tryagain)
3659 		{
3660 		    static int		lastchoice = 0;
3661 		    int			choice;
3662 		    int			aorb;
3663 
3664 		    tryagain = false;
3665 
3666 		    choice = gfx_selectmenu(15, 3, (const char **) options, aorb, lastchoice, true);
3667 		    if (choice == num_options)
3668 			choice = -1;
3669 
3670 		    if (choice >= 0)
3671 			lastchoice = choice;
3672 
3673 		    if (choice >= 0 &&
3674 			(aorb == BUTTON_SELECT || aorb == BUTTON_X))
3675 		    {
3676 			// Give long description..
3677 			if (isspell[choice])
3678 			    encyc_viewSpellDescription((SPELL_NAMES) enumvalue[choice]);
3679 			else
3680 			    encyc_viewSkillDescription((SKILL_NAMES) enumvalue[choice]);
3681 			tryagain = true;
3682 		    }
3683 
3684 		    if (aorb != BUTTON_A)
3685 			choice = -1;
3686 
3687 		    if (choice >= 0)
3688 		    {
3689 			if (isspell[choice])
3690 			    spell = (SPELL_NAMES) enumvalue[choice];
3691 			else
3692 			    skill = (SKILL_NAMES) enumvalue[choice];
3693 		    }
3694 		}
3695 
3696 		// Delete everything
3697 		for (i = 0; i < num_options; i++)
3698 		{
3699 		    delete [] options[i];
3700 		}
3701 		delete [] options;
3702 		delete [] isspell;
3703 		delete [] enumvalue;
3704 
3705 		{
3706 		    int		y;
3707 		    for (y = 3; y < 19; y++)
3708 			gfx_cleartextline(y);
3709 		}
3710 
3711 		// Give long description..
3712 		// Lets the user know what they are in for.
3713 		const char *name = 0;
3714 		if (spell != SPELL_NONE)
3715 		{
3716 		    encyc_viewSpellDescription(spell);
3717 		    name = glb_spelldefs[spell].name;
3718 		}
3719 		else if (skill != SKILL_NONE)
3720 		{
3721 		    encyc_viewSkillDescription(skill);
3722 		    name = glb_skilldefs[skill].name;
3723 		}
3724 
3725 		if (name)
3726 		{
3727 
3728 		    BUF		spelltext;
3729 
3730 		    spelltext.sprintf("Learn %s?", name);
3731 
3732 		    if (!gfx_yesnomenu(spelltext))
3733 		    {
3734 			// A request to cancel
3735 			spell = SPELL_NONE;
3736 			skill = SKILL_NONE;
3737 		    }
3738 
3739 		}
3740 
3741 		// Learn the relevant spell or skill
3742 		doread = false;
3743 		if (spell != SPELL_NONE)
3744 		{
3745 		    if (canLearnSpell(spell, true))
3746 		    {
3747 			doread = true;
3748 			learnSpell(spell);
3749 		    }
3750 		}
3751 		if (skill != SKILL_NONE)
3752 		{
3753 		    if (canLearnSkill(skill, true))
3754 		    {
3755 			doread = true;
3756 			learnSkill(skill);
3757 		    }
3758 		}
3759 
3760 		if (spell == SPELL_NONE && skill == SKILL_NONE)
3761 		{
3762 		    // If the user aborts reading the spell book,
3763 		    // ie: just glances to see the titles,
3764 		    // we want to not cost a turn.
3765 		    costturn = false;
3766 		}
3767 	    }
3768 	}
3769 
3770 	// Autoid spellbooks...
3771 	// We don't want to markIdentified as we don't insta-id the
3772 	// charges.
3773 	// Because the avatar sees the menu of choices, there is no
3774 	// case in which we *don't* want to identify.
3775 	if (isAvatar())
3776 	    item->markClassKnown();
3777 
3778 	if (doread)
3779 	{
3780 	    // Use a charge from the spellbook:
3781 	    item->useCharge();
3782 	    // If no charges, fade away.
3783 	    if (item->getCharges())
3784 	    {
3785 		doread = false;
3786 		// Chance of figuring out the number of pages left.
3787 		if (isAvatar() && !item->isKnownCharges())
3788 		{
3789 		    if (!rand_choice(20))
3790 		    {
3791 			item->markChargesKnown();
3792 			formatAndReport("%U <determine> the number of pages in %IU.", item);
3793 		    }
3794 		}
3795 	    }
3796 	    else
3797 	    {
3798 		doread = true;
3799 		formatAndReport("%IU <I:fade> away.", item);
3800 	    }
3801 	}
3802     }
3803     else
3804     {
3805 	// Try to read the specific object type.
3806 	switch (item->getDefinition())
3807 	{
3808 	    default:
3809 		doread = false;
3810 		formatAndReport("%U <look> at %IU but cannot find any writing.", item);
3811 		break;
3812 	}
3813     }
3814 
3815     if (doread)
3816     {
3817 	// Remove the item from our inventory.
3818 	if (itemstack)
3819 	{
3820 	    delete item;
3821 	}
3822 	else
3823 	{
3824 	    drop = dropItem(ix, iy);
3825 	    UT_ASSERT(drop == item);
3826 	    delete drop;
3827 	}
3828 
3829 	// Place the result, if any.
3830 	if (newitem)
3831 	    acquireItem(newitem);
3832     }
3833     else
3834     {
3835 	// Merge the scroll back if required.
3836 	// Doing a mergeStack will be wrong, as spell books
3837 	// would be falsely merged and fail to decrement
3838 	// their usage.  Instead, we reacquire the item.
3839 	if (itemstack)
3840 	{
3841 	    if (!acquireItem(item))
3842 	    {
3843 		// Not enough room, drop on the ground.
3844 		glbCurLevel->acquireItem(item, getX(), getY(), this);
3845 	    }
3846 	}
3847     }
3848 
3849     return costturn;
3850 }
3851 
3852 bool
actionDip(int potionx,int potiony,int ix,int iy)3853 MOB::actionDip(int potionx, int potiony, int ix, int iy)
3854 {
3855     ITEM		*potion, *item, *drop, *newpotion, *newitem;
3856     ITEM		*potionstack = 0;
3857     bool		 consumed = false;
3858 
3859     // First, get the items and ensure they exist (and are of the right
3860     // type)
3861 
3862     potion = getItem(potionx, potiony);
3863     if (!potion)
3864     {
3865 	formatAndReport("%U <try> to dip into thin air!");
3866 	return false;
3867     }
3868 
3869     item = getItem(ix, iy);
3870     if (!item)
3871     {
3872 	formatAndReport("%U <dip> nothing into %IU.  Suprisingly, nothing happens!", potion);
3873 	return false;
3874     }
3875 
3876     // If the potion is the same as the item, bad stuff will happen.
3877     // Thus we avert that with a humerous message.
3878     if (item == potion)
3879     {
3880 	formatAndReport("%IU <I:be> not a klein bottle!", potion);
3881 	return false;
3882     }
3883 
3884     // If the potion is being wielded, we forbid the dip.
3885     // This avoids being able to dequip items by dipping stuff into them.
3886     if (!potionx)
3887     {
3888 	formatAndReport("%U cannot dip anything into an equipped item.");
3889 	return false;
3890     }
3891 
3892     // Now, note that we can dip a stack.  However, we always dip
3893     // into one potion.
3894 
3895     // Remove the items from our inventory for the duration of the dip...
3896     if (potion->getStackCount() > 1)
3897     {
3898 	potionstack = potion;
3899 	potion = potionstack->splitStack(1);
3900     }
3901     else
3902     {
3903 	drop = dropItem(potionx, potiony);
3904 	UT_ASSERT(drop == potion);
3905     }
3906     drop = dropItem(ix, iy);
3907     UT_ASSERT(drop == item);
3908 
3909     // Do the dip....
3910     consumed = potion->actionDip(this, item, newpotion, newitem);
3911 
3912     // Acquire the new items!
3913     // If the actionDip failed to dip and the same potion was returned,
3914     // this will also implicitly merge it into our stack.  In the
3915     // case it generates an empty bottle, it will have deleted our
3916     // split potion for us.
3917     if (newitem)
3918     {
3919 	if (!acquireItem(newitem, ix, iy))
3920 	{
3921 	    formatAndReport("Lacking room, %U <drop> %IU", newitem);
3922 
3923 	    // Mark the item below grade if we are.
3924 	    if (hasIntrinsic(INTRINSIC_INPIT) ||
3925 		hasIntrinsic(INTRINSIC_SUBMERGED))
3926 	    {
3927 		newitem->markBelowGrade(true);
3928 	    }
3929 
3930 	    glbCurLevel->acquireItem(newitem, getX(), getY(), this);
3931 	}
3932     }
3933     if (newpotion)
3934     {
3935 	if (!acquireItem(newpotion))
3936 	{
3937 	    formatAndReport("Lacking room, %U <drop> %IU.", newpotion);
3938 
3939 	    // Mark the item below grade if we are.
3940 	    if (hasIntrinsic(INTRINSIC_INPIT) ||
3941 		hasIntrinsic(INTRINSIC_SUBMERGED))
3942 	    {
3943 		newpotion->markBelowGrade(true);
3944 	    }
3945 
3946 	    glbCurLevel->acquireItem(newpotion, getX(), getY(), this);
3947 	}
3948     }
3949 
3950     // If ix == 0, this is an equpped item.  Dipping may thus
3951     // change what we have equipped, so we must rebuild appearance.
3952     if (ix == 0)
3953 	rebuildAppearance();
3954 
3955     return consumed;
3956 }
3957 
3958 bool
actionThrow(int ix,int iy,int dx,int dy,int dz)3959 MOB::actionThrow(int ix, int iy, int dx, int dy, int dz)
3960 {
3961     ITEM		*missile, *launcher, *stack = 0;
3962     BUF			 buf;
3963     const ATTACK_DEF	*missileattack;
3964 
3965     applyConfusion(dx, dy, dz);
3966 
3967     missile = getItem(ix, iy);
3968 
3969     if (!missile)
3970     {
3971 	formatAndReport("%U <mime> throwing something.");
3972 	return false;
3973     }
3974 
3975     // Can't throw items you have equipped.
3976     if (!ix)
3977     {
3978 	formatAndReport("%U cannot throw equipped items.");
3979 	return false;
3980     }
3981 
3982     if (!dx && !dy && !dz)
3983     {
3984 	formatAndReport("%U <toss> %IU from hand to hand.", missile);
3985 	return false;
3986     }
3987 
3988     if (missile->getStackCount() > 1)
3989     {
3990 	stack = missile;
3991 	missile = stack->splitStack(1);
3992     }
3993     else
3994     {
3995 	missile = dropItem(ix, iy);
3996     }
3997 
3998     // Figure out what our launcher, if any, is.
3999     launcher = 0;
4000 
4001     if (missile->requiresLauncher())
4002     {
4003 	launcher = getEquippedItem(ITEMSLOT_RHAND);
4004 	if (launcher)
4005 	{
4006 	    if (!missile->canLaunchThis(launcher))
4007 		launcher = 0;
4008 	}
4009 	// Try also our left hand.  Either hand is allowed to have the launcher.
4010 	if (!launcher)
4011 	{
4012 	    launcher = getEquippedItem(ITEMSLOT_LHAND);
4013 	    if (launcher)
4014 	    {
4015 		if (!missile->canLaunchThis(launcher))
4016 		    launcher = 0;
4017 	    }
4018 	}
4019     }
4020 
4021     // If we should have a launcher, but don't, we need to use misused.
4022     missileattack = missile->getThrownAttack();
4023     if (missile->requiresLauncher() && !launcher)
4024 	missileattack = &glb_attackdefs[ATTACK_MISTHROWN];
4025 
4026     if (!dz)
4027     {
4028 	// Throwing outwards.  If this is cursed, we want a chance
4029 	// to fumble.
4030 	if (missile->isCursed() &&
4031 	    (missile->getItemType() == ITEMTYPE_WEAPON ||
4032 	     missile->getItemType() == ITEMTYPE_POTION))
4033 	{
4034 	    // Check we don't have misthrown, don't want to id
4035 	    // swords this way.
4036 	    if (missile->defn().thrownattack != ATTACK_MISTHROWN)
4037 	    {
4038 		if (rand_chance(10))
4039 		{
4040 		    formatAndReport("%U <fumble>!");
4041 		    // Reset throw direction to the ground
4042 		    dx = dy = 0;
4043 		    dz = -1;
4044 		    // If thrown by avatar, note
4045 		    if (isAvatar())
4046 		    {
4047 			missile->markCursedKnown();
4048 			if (stack)
4049 			    stack->markCursedKnown();
4050 		    }
4051 		}
4052 	    }
4053 	}
4054     }
4055 
4056     if (dz)
4057     {
4058 	if (dz < 0)
4059 	{
4060 	    formatAndReport("%U <throw> %IU onto the ground.", missile);
4061 
4062 	    if (missile->doBreak(10, this, 0, glbCurLevel, getX(), getY()))
4063 		return true;
4064 
4065 	    glbCurLevel->acquireItem(missile, getX(), getY(), this);
4066 	    return true;
4067 	}
4068 	else
4069 	{
4070 	    int		dropx, dropy;
4071 	    bool	selflives;
4072 
4073 	    // Throwing into the air, it lands on you :>
4074 	    formatAndReport("%U <throw> %IU into the air.", missile);
4075 
4076 	    dropx = getX();
4077 	    dropy = getY();
4078 
4079 	    formatAndReport("%IU <I:fall> on %A!", missile);
4080 
4081 	    selflives = receiveDamage(missileattack, this, missile, launcher,
4082 			    ATTACKSTYLE_THROWN);
4083 
4084 	    if (missile->doBreak(10, (selflives ? this : 0), 0, glbCurLevel, dropx, dropy))
4085 		return true;
4086 
4087 	    glbCurLevel->acquireItem(missile, dropx, dropy, this);
4088 
4089 	    return true;
4090 	}
4091     }
4092 
4093     if (launcher)
4094 	formatAndReport("%U <fire> %IU.", missile);
4095     else
4096 	formatAndReport("%U <throw> %IU.", missile);
4097 
4098     bool		ricochet = false;
4099 
4100     // Check for special skill...
4101     if (missile->defn().specialskill == SKILL_WEAPON_RICOCHET)
4102     {
4103 	// Requires a launcher
4104 	if (launcher && hasSkill(SKILL_WEAPON_RICOCHET))
4105 	{
4106 	    ricochet = true;
4107 	}
4108     }
4109 
4110     MOBREF		myself;
4111 
4112     myself.setMob(this);
4113 
4114     glbCurLevel->throwItem(getX(), getY(), dx, dy,
4115 		missile, stack, launcher,
4116 		this,
4117 		missileattack,
4118 		ricochet);
4119 
4120     // If we died, there is no point worrying about floating back
4121     // on star squares.
4122     // This is also necessary if we de-poly with the splash damage.
4123     MOB		*mob = myself.getMob();
4124     if (!mob || mob->hasIntrinsic(INTRINSIC_DEAD))
4125 	return true;
4126 
4127     // Check if you are on a star square, in which case you float
4128     // in opposite direction
4129     SQUARE_NAMES	tile = glbCurLevel->getTile(mob->getX(), mob->getY());
4130     if (glb_squaredefs[tile].isstars)
4131     {
4132 	mob->formatAndReport("%U <float> in the opposite direction.");
4133 	mob->actionWalk(-dx, -dy, true);
4134     }
4135 
4136     return true;
4137 }
4138 
4139 bool
actionFire(int dx,int dy,int dz)4140 MOB::actionFire(int dx, int dy, int dz)
4141 {
4142     // Find an item in the quiver.
4143     ITEM		*ammo;
4144 
4145     for (ammo = myInventory; ammo; ammo = ammo->getNext())
4146     {
4147 	// Ignore worn items.
4148 	if (ammo->getX() == 0)
4149 	    continue;
4150 
4151 	if (ammo->isQuivered())
4152 	    break;
4153     }
4154 
4155     if (!ammo)
4156     {
4157 	formatAndReport("%U <have> nothing quivered.");
4158 	return false;
4159     }
4160 
4161     // We succeeded!  Throw!
4162     return actionThrow(ammo->getX(), ammo->getY(), dx, dy, dz);
4163 }
4164 
4165 bool
actionQuiver(int ix,int iy)4166 MOB::actionQuiver(int ix, int iy)
4167 {
4168     ITEM	*ammo;
4169 
4170     ammo = getItem(ix, iy);
4171 
4172     if (!ammo)
4173     {
4174 	formatAndReport("%U <quiver> empty air!");
4175 	return false;
4176     }
4177 
4178     // Toggle state.
4179     if (ammo->isQuivered())
4180     {
4181 	formatAndReport("%U <dequiver> %IU.", ammo);
4182 	ammo->markQuivered(false);
4183     }
4184     else
4185     {
4186 	formatAndReport("%U <quiver> %IU.", ammo);
4187 	ammo->markQuivered(true);
4188     }
4189     return false;
4190 }
4191 
4192 bool
ableToZap(int ix,int iy,int dx,int dy,int dz,bool quiet)4193 MOB::ableToZap(int ix, int iy, int dx, int dy, int dz, bool quiet)
4194 {
4195     ITEM		*wand;
4196 
4197     wand = getItem(ix, iy);
4198     if (!wand)
4199     {
4200 	if (!quiet)
4201 	    formatAndReport("%U <try> to zap nothing!");
4202 
4203 	return false;
4204     }
4205 
4206     return true;
4207 }
4208 
4209 bool
actionZap(int ix,int iy,int dx,int dy,int dz)4210 MOB::actionZap(int ix, int iy, int dx, int dy, int dz)
4211 {
4212     ITEM		*wand;
4213     bool		 consumed = false;
4214 
4215     applyConfusion(dx, dy, dz);
4216 
4217     // First, get the items and ensure they exist (and are of the right
4218     // type)
4219     if (!ableToZap(ix, iy, dx, dy, dz, false))
4220 	return false;
4221 
4222     wand = getItem(ix, iy);
4223 
4224     // If the wand was in a stack, unpeel it from the stack before zapping.
4225     if (wand->getStackCount() > 1)
4226     {
4227 	int		sx, sy;
4228 
4229 	if (findItemSlot(sx, sy))
4230 	{
4231 	    wand = wand->splitStack();
4232 	    acquireItem(wand, sx, sy);
4233 	}
4234 	else
4235 	{
4236 	    formatAndReport("%U <have> no free space!");
4237 	    return false;
4238 	}
4239     }
4240 
4241     // Do the zap....
4242     consumed = wand->actionZap(this, dx, dy, dz);
4243 
4244     return consumed;
4245 }
4246 
4247 void
cancelSpell(SPELL_NAMES spell)4248 MOB::cancelSpell(SPELL_NAMES spell)
4249 {
4250     // Un-Pay the spell cost...
4251     myHP += glb_spelldefs[spell].hpcost;
4252     myMP += glb_spelldefs[spell].mpcost;
4253     myExp += glb_spelldefs[spell].xpcost;
4254 }
4255 
4256 bool
actionCast(SPELL_NAMES spell,int dx,int dy,int dz)4257 MOB::actionCast(SPELL_NAMES spell, int dx, int dy, int dz)
4258 {
4259     MOB		*target;
4260     BUF		buf;
4261     int		 i, n;
4262 
4263     const char	*directionflavour = 0;
4264     bool	 targetself = false;
4265     bool	 istargetlit = false;
4266 
4267     applyConfusion(dx, dy, dz);
4268 
4269     // Targetted square.
4270     int		tx = getX() + dx;
4271     int		ty = getY() + dy;
4272     MOBREF	myself;
4273 
4274     myself.setMob(this);
4275 
4276     tx &= MAP_WIDTH-1;
4277     ty &= MAP_HEIGHT-1;
4278 
4279     // Determine if our target is lit...
4280     if ((abs(dx) <= 1 && abs(dy) <= 1) ||
4281 	glbCurLevel->isLit(tx, ty))
4282     {
4283 	istargetlit = true;
4284     }
4285 
4286     // TODO: Sanity test...
4287 
4288     // Text for casting the spell...
4289     buf = formatToString("%U <cast> %B1.",
4290 		this, 0, 0, 0,
4291 		glb_spelldefs[spell].name);
4292     reportMessage(buf);
4293 
4294     // Pay the spell cost...
4295     myHP -= glb_spelldefs[spell].hpcost;
4296     myMP -= glb_spelldefs[spell].mpcost;
4297     myExp -= glb_spelldefs[spell].xpcost;
4298 
4299     target = glbCurLevel->getMob(tx, ty);
4300 
4301     if (!dx && !dy && !dz)
4302     {
4303 	targetself = true;
4304 	directionflavour = getReflexive();
4305     }
4306     if (dz)
4307     {
4308 	if (dz < 0)
4309 	    directionflavour = "the floor";
4310 	else
4311 	    directionflavour = "the ceiling";
4312     }
4313 
4314     // Switch on spell...
4315     switch (spell)
4316     {
4317 	case SPELL_NONE:
4318 	{
4319 	    formatAndReport("%U <gesture> in vain.");
4320 	    return true;
4321 	}
4322 	case SPELL_FLASH:
4323 	    // Even if no creature was targeted, the square
4324 	    // hit becomes lit.
4325 	    glbCurLevel->setFlag(tx, ty, SQUAREFLAG_LIT, true);
4326 	    if (!target)
4327 	    {
4328 		formatAndReport("%U <illuminate> empty air.");
4329 		return true;
4330 	    }
4331 
4332 	    formatAndReport("%U <direct> a blinding flash at %MU.", target);
4333 
4334 	    pietyCastSpell(spell);
4335 	    target->receiveDamage(ATTACK_SPELL_FLASH, this, 0, 0, ATTACKSTYLE_SPELL);
4336 	    return true;
4337 
4338 	case SPELL_STICKYFLAMES:
4339 	    if (!target)
4340 	    {
4341 		formatAndReport("%U <cast> at thin air.");
4342 		return true;
4343 	    }
4344 
4345 	    formatAndReport("%U <set> %MU alight.", target);
4346 
4347 	    target->setTimedIntrinsic(this,
4348 				INTRINSIC_AFLAME, rand_dice(3, 2, 3));
4349 
4350 	    pietyCastSpell(spell);
4351 
4352 	    return true;
4353 
4354 	case SPELL_CHILL:
4355 	    if (!target)
4356 	    {
4357 		formatAndReport("%U <cast> at thin air.");
4358 		return true;
4359 	    }
4360 
4361 	    formatAndReport("%U <touch> %MU.", target);
4362 
4363 	    pietyCastSpell(spell);
4364 	    target->receiveDamage(ATTACK_SPELL_CHILL, this, 0, 0, ATTACKSTYLE_SPELL);
4365 	    return true;
4366 
4367 	case SPELL_SPARK:
4368 	    formatAndReport("Electrical sparks fly from %U.");
4369 	    pietyCastSpell(spell);
4370 
4371 	    ourEffectAttack = ATTACK_SPELL_SPARK;
4372 	    glbCurLevel->fireBall(getX(), getY(), 1, false,
4373 				areaAttackCBStatic, &myself);
4374 	    return true;
4375 
4376 	case SPELL_MAGICMISSILE:
4377 	    formatAndReport("%U <send> a magic missile from %r fingertip.");
4378 
4379 	    ourZapSpell = spell;
4380 	    ourFireBallCount = getMagicDie();
4381 
4382 	    pietyCastSpell(spell);
4383 
4384 	    if (targetself || dz)
4385 	    {
4386 		if (dz)
4387 		    reportMessage("The ray bounces.");
4388 		zapCallbackStatic(getX(), getY(), false, &myself);
4389 	    }
4390 	    else
4391 	    {
4392 		glbCurLevel->fireRay(getX(), getY(),
4393 				 dx, dy,
4394 				 5 + getMagicDie(),
4395 				 MOVE_STD_FLY, true,
4396 				 zapCallbackStatic,
4397 				 &myself);
4398 	    }
4399 	    return true;
4400 
4401 	case SPELL_ACIDSPLASH:
4402 	    if (!target)
4403 	    {
4404 		formatAndReport("%U <cast> at thin air.");
4405 		return true;
4406 	    }
4407 
4408 	    formatAndReport("%U <spray> corrosive acid.");
4409 	    pietyCastSpell(spell);
4410 	    target->receiveDamage(ATTACK_SPELL_ACIDSPLASH, this,
4411 				  0, 0, ATTACKSTYLE_SPELL);
4412 
4413 	    // Destroy a random inventory item.
4414 	    // If we targeted ourself, we may be dead, however.
4415 	    if ((myself.getMob() == this) && !hasIntrinsic(INTRINSIC_DEAD))
4416 	    {
4417 		int		 ix, iy;
4418 		ITEM		*item;
4419 
4420 		ix = rand_range(0, MOBINV_WIDTH-1);
4421 		iy = rand_range(0, MOBINV_HEIGHT-1);
4422 		item = getItem(ix, iy);
4423 
4424 		if (item)
4425 		{
4426 		    formatAndReport("The backsplash drenches %r %Iu!", item);
4427 
4428 		    if (item->canDissolve())
4429 		    {
4430 			// Peel off one instance if stacked, otherwise
4431 			// take the whole thing.
4432 			if (item->getStackCount() > 1)
4433 			{
4434 			    item = item->splitStack(1);
4435 			}
4436 			else
4437 			{
4438 			    item = dropItem(item->getX(), item->getY());
4439 			}
4440 			formatAndReport("%IU <I:be> dissolved.", item);
4441 
4442 			delete item;
4443 
4444 			// Rebuild our appearance.
4445 			if (!ix)
4446 			    rebuildAppearance();
4447 		    }
4448 		    else if (item->getDefinition() == ITEM_BOTTLE)
4449 		    {
4450 			// Fill bottle with acid!
4451 			formatAndReport("Acid fills %IU.", item);
4452 			item->setDefinition(ITEM::lookupMagicItem(MAGICTYPE_POTION, POTION_ACID));
4453 
4454 			// If we can see this happening, we now know
4455 			// what acid potions are.
4456 			if (MOB::getAvatar() && MOB::getAvatar()->canSense(this))
4457 			    item->markClassKnown();
4458 		    }
4459 		    else
4460 		    {
4461 			formatAndReport("%IU <I:be> unaffected.", item);
4462 		    }
4463 		}
4464 	    }
4465 	    return true;
4466 
4467 	case SPELL_ACIDICMIST:
4468 	    // Determine if we can see the coords.
4469 	    if (!hasIntrinsic(INTRINSIC_BLIND) &&
4470 		glbCurLevel->canMove(tx, ty, MOVE_STD_FLY) &&
4471 		istargetlit &&
4472 		glbCurLevel->hasLOS(getX(), getY(),
4473 				    tx, ty))
4474 	    {
4475 		pietyCastSpell(spell);
4476 		formatAndReport("%U <condense> an acidic mist.");
4477 
4478 		glbCurLevel->setSmoke(tx, ty, SMOKE_ACID, this);
4479 		return true;
4480 	    }
4481 
4482 	    // The creature cannot see its destination - acidic mist is
4483 	    // illegal!
4484 	    formatAndReport("%U <concentrate> in vain.");
4485 	    cancelSpell(spell);
4486 	    return true;
4487 
4488 	case SPELL_CORROSIVEEXPLOSION:
4489 	    if (!hasIntrinsic(INTRINSIC_BLIND) &&
4490 		glbCurLevel->canMove(tx, ty, MOVE_STD_FLY) &&
4491 		istargetlit &&
4492 		glbCurLevel->hasLOS(getX(), getY(),
4493 				    tx, ty))
4494 	    {
4495 		ITEMSTACK		itemstack;
4496 		int			numitems, i;
4497 
4498 		glbCurLevel->getItemStack(itemstack, tx, ty);
4499 
4500 		formatAndReport("%U <create> a highly volatile blob of acid.");
4501 
4502 		numitems = 0;
4503 		for (i = 0; i < itemstack.entries(); i++)
4504 		{
4505 		    if (itemstack(i)->canDissolve())
4506 		    {
4507 			const char *flavour[] =
4508 			{
4509 			    "devours",
4510 			    "consumes",
4511 			    "dissolves",
4512 			    "eats",
4513 			    0
4514 			};
4515 			BUF		itemname = itemstack(i)->getName();
4516 			// Stacks of arrows become very powerful  :>
4517 			numitems += itemstack(i)->getStackCount();
4518 			buf.sprintf("The blob %s %s.",
4519 				    rand_string(flavour),
4520 				    itemname.buffer());
4521 			glbCurLevel->reportMessage(buf, tx, ty);
4522 			glbCurLevel->dropItem(itemstack(i));
4523 			delete itemstack(i);
4524 		    }
4525 		}
4526 
4527 		if (!numitems)
4528 		{
4529 		    glbCurLevel->reportMessage("With nothing to sustain it, the blob fizzles away.", tx, ty);
4530 		    return true;
4531 		}
4532 
4533 		// Successful cast!
4534 		pietyCastSpell(spell);
4535 
4536 		// Explosion!!!
4537 		int		radius;
4538 		int		reps;
4539 		radius = numitems - 1;
4540 		if (radius > 10)
4541 		    radius = 10;
4542 		reps = (numitems / 4) + 1;
4543 
4544 		for (i = 0; i < reps; i++)
4545 		{
4546 		    glbCurLevel->reportMessage("The acid blob explodes violently!", tx, ty);
4547 		    ourEffectAttack = ATTACK_SPELL_CORROSIVEEXPLOSION;
4548 		    glbCurLevel->fireBall(tx, ty, radius, true,
4549 					areaAttackCBStatic, &myself);
4550 		    // See if we are done.
4551 		    if (reps > 1 && (i == reps-1))
4552 			glbCurLevel->reportMessage("The acid blob has dissipated.", tx, ty);
4553 		}
4554 
4555 		return true;
4556 	    }
4557 	    // The creature cannot see its destination - corrosive explosion is
4558 	    // illegal!
4559 	    // (I had a very promising character die to a potion of blindness
4560 	    // on Klaskov's level when he had leap-attacked into the middle
4561 	    // of the orcs in preparation for a massive corrosive
4562 	    // explosion...)
4563 	    formatAndReport("%U <concentrate> in vain.");
4564 	    cancelSpell(spell);
4565 	    return true;
4566 
4567 	case SPELL_ACIDPOOL:
4568 	    if (!hasIntrinsic(INTRINSIC_BLIND) &&
4569 		istargetlit &&
4570 		glbCurLevel->hasLOS(getX(), getY(),
4571 				    tx, ty))
4572 	    {
4573 		formatAndReport("%U <summon> a fountain of acid.");
4574 		pietyCastSpell(spell);
4575 		glbCurLevel->floodSquare(SQUARE_ACID, tx, ty, this);
4576 		return true;
4577 	    }
4578 
4579 	    // Creature can't see where to put the pool, not possible.
4580 	    formatAndReport("%U <concentrate> in vain.");
4581 	    cancelSpell(spell);
4582 
4583 	    return true;
4584 
4585 	case SPELL_MINDACID:
4586 	{
4587 	    ITEM		*item;
4588 
4589 	    // Determine if we have hands.
4590 	    if (!getSlotName(ITEMSLOT_RHAND))
4591 	    {
4592 		formatAndReport("A blob of mind-acid appears beside %U and then dissipates.");
4593 		return true;
4594 	    }
4595 
4596 	    // Determine if we can disarm the main weapon.
4597 	    if (getEquippedItem(ITEMSLOT_RHAND))
4598 	    {
4599 		actionDequip(ITEMSLOT_RHAND);
4600 		item = getEquippedItem(ITEMSLOT_RHAND);
4601 		if (item)
4602 		{
4603 		    // Failed to dequip, likely due to curse or
4604 		    // out of inventory.
4605 		    formatAndReport("%R mind acid is blocked by %r %Iu.", item);
4606 		    return true;
4607 		}
4608 	    }
4609 
4610 	    pietyCastSpell(spell);
4611 
4612 	    // The right hand is now free.  Create our mind-acid
4613 	    // object.
4614 	    // We do not want artifact mind acid!
4615 	    item = ITEM::create(ITEM_MINDACIDHAND, false);
4616 
4617 	    // 5 turns to dispell it.
4618 	    item->addCharges(5);
4619 	    // We don't want to have an enchanted hand, as that
4620 	    // may create a hand that does no damage.
4621 	    item->enchant(-item->getEnchantment());
4622 	    // Let user count down the charges.
4623 	    item->markChargesKnown();
4624 
4625 	    buf.sprintf("Mind acid coats %s %s.",
4626 			getPossessive(),
4627 			getSlotName(ITEMSLOT_RHAND));
4628 	    reportMessage(buf);
4629 	    acquireItem(item, 0, ITEMSLOT_RHAND);
4630 
4631 	    return true;
4632 	}
4633 
4634 	case SPELL_DISINTEGRATE:
4635 	{
4636 	    // Much like the hoe of destruction, does half damage to all
4637 	    // foes.
4638 	    if (!target)
4639 	    {
4640 		reportMessage("The air boils as its contituent atoms are rent asunder.");
4641 		return true;
4642 	    }
4643 
4644 	    pietyCastSpell(spell);
4645 	    formatAndReport("%MR atoms are rent asunder by %R dread magic.", target);
4646 
4647 	    // Build our own attack def.
4648 	    ATTACK_DEF		attack;
4649 	    DICE		dice;
4650 	    attack = glb_attackdefs[ATTACK_SPELL_DISINTEGRATE];
4651 
4652 	    dice.myNumdie = 0;
4653 	    dice.mySides = 1;
4654 	    dice.myBonus = (target->getMaxHP() + 1) / 2;
4655 	    attack.damage = dice;
4656 
4657 	    target->receiveDamage(&attack, this, 0, 0, ATTACKSTYLE_SPELL);
4658 
4659 	    return true;
4660 	}
4661 
4662 	case SPELL_FORCEBOLT:
4663 	    formatAndReport("A bolt of force slams forward.");
4664 	    ourZapSpell = spell;
4665 	    pietyCastSpell(spell);
4666 
4667 	    if (targetself || dz)
4668 	    {
4669 		if (dz)
4670 		{
4671 		    buf.sprintf("The %s seems unaffected.",
4672 				((dz < 0) ? "floor" : "ceiling"));
4673 		    reportMessage(buf);
4674 		}
4675 		else
4676 		{
4677 		    zapCallbackStatic(getX(), getY(), false, &myself);
4678 		}
4679 	    }
4680 	    else
4681 	    {
4682 		glbCurLevel->fireRay(getX(), getY(),
4683 				    dx, dy,
4684 				    1,
4685 				    MOVE_ALL, false,
4686 				    zapCallbackStatic,
4687 				    &myself);
4688 
4689 		glbCurLevel->knockbackMob(tx, ty, dx, dy, false);
4690 	    }
4691 
4692 	    return true;
4693 
4694 	case SPELL_FORCEWALL:
4695 	{
4696 	    formatAndReport("A wall of force builds around %U and then slams outwards.");
4697 	    ourZapSpell = spell;
4698 
4699 	    pietyCastSpell(spell);
4700 
4701 	    // No target, always run in all directions.
4702 	    int			 rad, numwalls;
4703 	    bool		*lastwallstatus;
4704 	    int			 rdx = 0, rdy = 0, i, circle;
4705 	    int			 wdx, wdy, symbol = TILE_VOID;
4706 	    int			 cwdx = 0, cwdy = 0;
4707 	    int			 maxwalls = 25;
4708 
4709 	    numwalls = 0;
4710 
4711 	    lastwallstatus = new bool[11*11];
4712 	    memset(lastwallstatus, 0, sizeof(bool) * 11 * 11);
4713 	    // The current position of the avatar is marked as
4714 	    // having a force wall to start from.
4715 	    lastwallstatus[5*11+5] = true;
4716 	    for (rad = 1; rad < 6 && numwalls < maxwalls; rad++)
4717 	    {
4718 		// Circle around...
4719 		//        11112
4720 		// 112    4   2
4721 		// 4 2 -> 4   2
4722 		// 433    4   2
4723 		//        43333
4724 		for (circle = 0; circle < 4 && numwalls < maxwalls; circle++)
4725 		{
4726 		    for (i = 0; i < rad * 2 && numwalls < maxwalls; i++)
4727 		    {
4728 			switch (circle)
4729 			{
4730 			    case 0:
4731 				rdy = 5 - rad;
4732 				rdx = 5 - rad + i;
4733 				break;
4734 			    case 1:
4735 				rdx = 5 + rad;
4736 				rdy = 5 - rad + i;
4737 				break;
4738 			    case 2:
4739 				rdx = 5 + rad - i;
4740 				rdy = 5 + rad;
4741 				break;
4742 			    case 3:
4743 				rdx = 5 - rad;
4744 				rdy = 5 + rad - i;
4745 				break;
4746 			}
4747 
4748 			// Disable the ray if it can't move into this square.
4749 			if (!glbCurLevel->canRayMove(getX()+rdx-5,
4750 						     getY()+rdy-5,
4751 						     MOVE_STD_FLY,
4752 						     false))
4753 			{
4754 			    continue;
4755 			}
4756 
4757 			// rdx, rdy are now our entry into lastwallstatus.
4758 			// We need to check if any previous direction
4759 			// has a wall, and if so, what symbol we should use.
4760 			// TODO: We should bias our direction based
4761 			// on circle!
4762 			symbol = TILE_VOID;
4763 			for (wdy = -1; wdy <= 1; wdy++)
4764 			{
4765 			    for (wdx = -1; wdx <= 1; wdx++)
4766 			    {
4767 				if (!(wdy || wdx))
4768 				    continue;
4769 				// Test if in bounds...
4770 				if (rdx + wdx < 0 || rdx + wdx >= 11)
4771 				    continue;
4772 				if (rdy + wdy < 0 || rdy + wdy >= 11)
4773 				    continue;
4774 
4775 				// Valid test, see if there was a wall here!
4776 				if (lastwallstatus[rdx + wdx + (rdy+wdy)*11])
4777 				{
4778 				    // Aesthetics: always replace slashes
4779 				    // with non slashes.
4780 				    if (wdx && wdy)
4781 				    {
4782 					if (symbol != TILE_VOID)
4783 					    continue;
4784 					if (wdx * wdy < 0)
4785 					    symbol = TILE_RAYSLASH;
4786 					else
4787 					    symbol = TILE_RAYBACKSLASH;
4788 				    }
4789 				    else
4790 				    {
4791 					if (wdx)
4792 					    symbol = TILE_RAYPIPE;
4793 					else
4794 					    symbol = TILE_RAYDASH;
4795 				    }
4796 				    cwdx = wdx;
4797 				    cwdy = wdy;
4798 				}
4799 			    }
4800 			}
4801 
4802 
4803 			if (symbol != TILE_VOID)
4804 			{
4805 			    // We found a direction that is stored in
4806 			    // cwdx, cwdy.  Note this is the *from*
4807 			    // direction.
4808 			    glbCurLevel->fireRay(getX() + rdx - 5 + cwdx,
4809 				    getY() + rdy - 5 + cwdy,
4810 				    -cwdx, -cwdy,
4811 				    1,
4812 				    MOVE_STD_FLY, false,
4813 				    zapCallbackStatic,
4814 				    &myself);
4815 
4816 			    // Knock back mobs so they get hit again!
4817 			    glbCurLevel->knockbackMob(
4818 				    getX() + rdx - 5,
4819 				    getY() + rdy - 5,
4820 				    -cwdx, -cwdy,
4821 				    true);
4822 
4823 			    // Mark this square as a valid source
4824 			    lastwallstatus[rdx+rdy*11] = true;
4825 
4826 			    // Increment number of dudes
4827 			    numwalls++;
4828 			}
4829 		    }
4830 		}
4831 	    }
4832 
4833 	    delete [] lastwallstatus;
4834 
4835 	    return true;
4836 	}
4837 
4838 	case SPELL_FROSTBOLT:
4839 	    formatAndReport("A bolt of ice speeds from %r hands.");
4840 	    ourZapSpell = spell;
4841 
4842 	    pietyCastSpell(spell);
4843 
4844 	    if (targetself || dz)
4845 	    {
4846 		if (dz)
4847 		    reportMessage("The ray fizzles out.");
4848 		else
4849 		    zapCallbackStatic(getX(), getY(), false, &myself);
4850 	    }
4851 	    else
4852 	    {
4853 		glbCurLevel->fireRay(getX(), getY(),
4854 				 dx, dy,
4855 				 4,
4856 				 MOVE_STD_FLY, false,
4857 				 zapCallbackStatic,
4858 				 &myself);
4859 	    }
4860 	    return true;
4861 
4862 	case SPELL_LIVINGFROST:
4863 	{
4864 	    // Determine if an frost could be there...
4865 	    tx = getX();
4866 	    ty = getY();
4867 
4868 	    if (!glbCurLevel->findCloseTile(tx, ty, MOVE_STD_FLY))
4869 	    {
4870 		glbCurLevel->reportMessage(
4871 		    "The air chills briefly.",
4872 		    tx, ty);
4873 		return true;
4874 	    }
4875 
4876 	    MOB		*frost;
4877 
4878 	    pietyCastSpell(spell);
4879 
4880 	    frost = MOB::create(MOB_LIVINGFROST);
4881 	    // Summoned creatures never have inventory.
4882 	    frost->destroyInventory();
4883 
4884 	    frost->move(tx, ty, true);
4885 
4886 	    glbCurLevel->registerMob(frost);
4887 
4888 	    frost->formatAndReport("%U <freeze> into existence!");
4889 
4890 	    frost->makeSlaveOf(this);
4891 	    frost->setTimedIntrinsic(this, INTRINSIC_SUMMONED, 8);
4892 
4893 	    return true;
4894 	}
4895 	case SPELL_BLIZZARD:
4896 	{
4897 	    bool		candetonate = false;
4898 
4899 	    // Determine if the given coordinates are visable..
4900 	    // If we can sense the target by any means, we are allowed
4901 	    // to detonate it!
4902 	    if (target && ((target == this) || canSense(target)))
4903 		candetonate = true;
4904 
4905 	    if (!candetonate &&
4906 		(!istargetlit ||
4907 		 !glbCurLevel->hasLOS(getX(), getY(),
4908 				     tx, ty)))
4909 	    {
4910 		// Cannot see the position, illegal.
4911 		formatAndReport("%U <gesture> into the darkness.");
4912 		cancelSpell(spell);
4913 		return true;
4914 	    }
4915 
4916 	    // Check if no creature is there.
4917 	    if (!target)
4918 	    {
4919 		formatAndReport("%U <try> to detonate thin air.");
4920 		cancelSpell(spell);
4921 		return true;
4922 	    }
4923 
4924 	    // Verify the target is an undead.
4925 	    if (target->getDefinition() != MOB_LIVINGFROST)
4926 	    {
4927 		formatAndReport("%MU <M:be> insufficiently cold to be a source.", target);
4928 		return true;
4929 	    }
4930 
4931 	    // Note we do *not* need to command the living frost!
4932 	    // Ice daemons are our friends :>
4933 
4934 	    // Phew!  We have a winner...
4935 	    formatAndReport("%U <unravel> the magics that stablize %MU.", target);
4936 
4937 	    // Destroy the living frost.
4938 	    glbCurLevel->unregisterMob(target);
4939 	    target->triggerAsDeath(ATTACK_BLIZZARD_SOURCE, this, 0, false);
4940 	    delete target;
4941 
4942 	    // Success!
4943 	    pietyCastSpell(spell);
4944 
4945 	    // Create the blizzard.
4946 	    glbCurLevel->reportMessage("A large blizzard fills the air with sleet and snow!", tx, ty);
4947 	    ourEffectAttack = ATTACK_SPELL_BLIZZARD;
4948 	    glbCurLevel->fireBall(tx, ty, 3, true, areaAttackCBStatic, &myself);
4949 
4950 	    return true;
4951 	}
4952 
4953 	case SPELL_DIG:
4954 	    formatAndReport("%U magically <excavate> earth.");
4955 	    pietyCastSpell(spell);
4956 
4957 	    if (!dz && (dx || dy) &&
4958 		glbCurLevel->isDiggable(getX()+dx, getY()+dy))
4959 	    {
4960 		MOBREF		myself;
4961 		myself.setMob(this);
4962 
4963 		ourEffectAttack = ATTACK_SPELL_SANDBLAST;
4964 
4965 		int	angle = rand_dirtoangle(dx, dy);
4966 		int	rdx, rdy;
4967 		int	sx = getX(), sy = getY();
4968 		rand_angletodir(angle+3, rdx, rdy);
4969 		glbCurLevel->fireRay(sx+dx, sy+dy,
4970 				rdx, rdy,
4971 				1,
4972 				MOVE_STD_FLY, false,
4973 				areaAttackCBStatic,
4974 				&myself);
4975 		rand_angletodir(angle+5, rdx, rdy);
4976 		glbCurLevel->fireRay(sx+dx, sy+dy,
4977 				rdx, rdy,
4978 				1,
4979 				MOVE_STD_FLY, false,
4980 				areaAttackCBStatic,
4981 				&myself);
4982 		if (!myself.getMob())
4983 		    return true;
4984 	    }
4985 
4986 	    return actionDig(dx, dy, dz, 1, true);
4987 
4988 	case SPELL_CREATEPIT:
4989 	    // You can excavate anything you have LOS on, regardless
4990 	    // of sight rays.
4991 	    if (glbCurLevel->hasLOS(getX(), getY(), tx, ty))
4992 	    {
4993 		pietyCastSpell(spell);
4994 		formatAndReport("Dirt flies as %U <focus> %r will.");
4995 
4996 		glbCurLevel->digPit(tx, ty, this);
4997 		return true;
4998 	    }
4999 
5000 	    // The creature cannot see its destination, excavate
5001 	    // is illegal.
5002 	    formatAndReport("%U <concentrate> in vain.");
5003 	    cancelSpell(spell);
5004 	    return true;
5005 
5006 	case SPELL_SANDSTORM:
5007 	{
5008 	    if ((!istargetlit ||
5009 		 !glbCurLevel->hasLOS(getX(), getY(),
5010 				     tx, ty)))
5011 	    {
5012 		// Cannot see the position, illegal.
5013 		formatAndReport("%U <gesture> into the darkness.");
5014 		cancelSpell(spell);
5015 		return true;
5016 	    }
5017 
5018 	    // Determine if the given coordinates are valid..
5019 	    if (!glbCurLevel->isDiggable(tx, ty))
5020 	    {
5021 		formatAndReport("There is nothing for %U to build a sandstorm from.");
5022 		cancelSpell(spell);
5023 		return true;
5024 	    }
5025 
5026 	    // Phew!  We have a winner...
5027 	    formatAndReport("%U <detonate> the rock and <wrap> it in a cyclone of air.");
5028 
5029 	    // Success!
5030 	    pietyCastSpell(spell);
5031 
5032 	    // Create the sandstorm.
5033 	    glbCurLevel->reportMessage("A sandstorm fills the air with abrasive dirt!", tx, ty);
5034 	    ourEffectAttack = ATTACK_SPELL_SANDSTORM;
5035 	    int	wx, wy;
5036 	    glbCurLevel->getAmbientWindDirection(wx, wy);
5037 	    glbCurLevel->fireBall(tx+wx, ty+wy, 1, true, areaAttackCBStatic, &myself);
5038 
5039 	    // Now dig!
5040 	    glbCurLevel->digSquare(tx, ty, myself.getMob());
5041 
5042 	    return true;
5043 	}
5044 
5045 	case SPELL_GROWFOREST:
5046 	    // You can grow anywhere you have LOS.
5047 	    if (glbCurLevel->hasLOS(getX(), getY(), tx, ty))
5048 	    {
5049 		pietyCastSpell(spell);
5050 		formatAndReport("Verdant forest springs from the ground.");
5051 		glbCurLevel->growForest(tx, ty, this);
5052 		return true;
5053 	    }
5054 
5055 	    // The creature cannot see its destination, excavate
5056 	    // is illegal.
5057 	    formatAndReport("%U <concentrate> in vain.");
5058 	    cancelSpell(spell);
5059 	    return true;
5060 
5061 	case SPELL_ANIMATEFOREST:
5062 	    // You can grow anywhere you have LOS.
5063 	    if (glbCurLevel->hasLOS(getX(), getY(), tx, ty))
5064 	    {
5065 		SQUARE_NAMES		 tile;
5066 
5067 		tile = glbCurLevel->getTile(tx, ty);
5068 		if (tile == SQUARE_FORESTFIRE)
5069 		{
5070 		    formatAndReport("The flames prevent %R magic from taking effect.");
5071 		    return true;
5072 		}
5073 		if (tile != SQUARE_FOREST)
5074 		{
5075 		    formatAndReport("Without trees to focus on, %R animation magic fizzles.");
5076 		    return true;
5077 		}
5078 
5079 		if (glbCurLevel->getMob(tx, ty))
5080 		{
5081 		    formatAndReport("A creature's presences prevents the forest from awakening.");
5082 		    return true;
5083 		}
5084 
5085 		formatAndReport("The forest comes to life!");
5086 		glbCurLevel->setTile(tx, ty, SQUARE_CORRIDOR);
5087 		MOB		*tree;
5088 
5089 		// LIVINGTREE... as opposed to DEADTREE?
5090 		// (This nitpick is made whilst half way between
5091 		// TO and SF at some inordinate altitude)
5092 		tree = MOB::create(MOB_LIVINGTREE);
5093 		tree->destroyInventory();
5094 		tree->move(tx, ty, true);
5095 		glbCurLevel->registerMob(tree);
5096 		tree->makeSlaveOf(this);
5097 		tree->setTimedIntrinsic(this, INTRINSIC_SUMMONED, 30);
5098 
5099 		pietyCastSpell(spell);
5100 		return true;
5101 	    }
5102 
5103 	    // The creature cannot see its destination, excavate
5104 	    // is illegal.
5105 	    formatAndReport("%U <focus> in vain.");
5106 	    cancelSpell(spell);
5107 	    return true;
5108 
5109 	case SPELL_DOWNPOUR:
5110 	    // You can create a downpour where you have LOS.
5111 	    if (glbCurLevel->hasLOS(getX(), getY(), tx, ty))
5112 	    {
5113 		pietyCastSpell(spell);
5114 		formatAndReport("%U <summon> thick clouds that release a torrential downpour.");
5115 		glbCurLevel->downPour(tx, ty, this);
5116 		return true;
5117 	    }
5118 
5119 	    // The creature cannot see its destination, excavate
5120 	    // is illegal.
5121 	    formatAndReport("%U <concentrate> in vain.");
5122 	    cancelSpell(spell);
5123 	    return true;
5124 
5125 	case SPELL_ROLLINGBOULDER:
5126 	{
5127 	    SQUARE_NAMES		 tile;
5128 	    bool		 	 hadboulder = false;
5129 	    ITEM			*boulder;
5130 	    MOB				*victim;
5131 	    MOB				*rollee = 0;
5132 
5133 	    // You need to have LOS on the target wall.
5134 	    if (!glbCurLevel->hasLOS(getX(), getY(), tx, ty))
5135 	    {
5136 		formatAndReport("%U <waste> %r energy.");
5137 		return true;
5138 	    }
5139 
5140 	    // Check to see that it is a wall...
5141 	    tile = glbCurLevel->getTile(tx, ty);
5142 	    switch (tile)
5143 	    {
5144 		case SQUARE_EMPTY:
5145 		case SQUARE_WALL:
5146 		case SQUARE_SECRETDOOR:
5147 		    break;
5148 		default:
5149 		    // Check to see if we have a boulder.
5150 		    boulder = glbCurLevel->getItem(tx, ty);
5151 		    if (boulder && boulder->getDefinition() == ITEM_BOULDER)
5152 		    {
5153 			hadboulder = true;
5154 			// Describe that the boulder starts to move..
5155 			glbCurLevel->reportMessage("A large boulder starts to roll.", tx, ty);
5156 
5157 		    }
5158 		    else
5159 		    {
5160 			// Might be able to roll the monster
5161 			rollee = glbCurLevel->getMob(tx, ty);
5162 			if (!rollee)
5163 			{
5164 			    formatAndReport("%U <try> to get rock from air.");
5165 			    return true;
5166 			}
5167 			if (rollee->getMaterial() != MATERIAL_STONE)
5168 			{
5169 			    formatAndReport("%R magic slips off %MU.", rollee);
5170 			    return true;
5171 			}
5172 			// Pull them out of pits or water
5173 			rollee->clearIntrinsic(INTRINSIC_INPIT);
5174 			rollee->clearIntrinsic(INTRINSIC_SUBMERGED);
5175 		    }
5176 		    break;
5177 	    }
5178 
5179 	    pietyCastSpell(spell);
5180 
5181 	    // The wall in question turns into a boulder.
5182 	    // Unless there was already a boulder.
5183 	    if (!hadboulder && !rollee)
5184 	    {
5185 		boulder = ITEM::create(ITEM_BOULDER, false, true);
5186 		glbCurLevel->setTile(tx, ty, SQUARE_CORRIDOR);
5187 
5188 		// We do the message after the change so we might get
5189 		// a map update...
5190 		glbCurLevel->reportMessage("A large boulder is carved out of the wall.", tx, ty);
5191 
5192 		// In case acquiring does something odd..
5193 		glbCurLevel->acquireItem(boulder, tx, ty, this);
5194 	    }
5195 
5196 	    // Now, try to roll the boulder.
5197 	    // First, determine the likely direction.
5198 	    int			 bdx, bdy, range;
5199 	    bool		 cansee;
5200 	    MOBREF		 thismob;
5201 	    ITEM		*blocker;
5202 	    int			 oldoverlay;
5203 
5204 	    thismob.setMob(this);
5205 
5206 	    bdx = getX() - tx;
5207 	    bdy = getY() - ty;
5208 
5209 	    // If one is bigger than the other, zero the other.
5210 	    // (ie, usually do horizontal moves)
5211 	    if (abs(bdx) > abs(bdy))
5212 		bdy = 0;
5213 	    else if (abs(bdx) < abs(bdy))
5214 		bdx = 0;
5215 
5216 	    bdx = SIGN(bdx);
5217 	    bdy = SIGN(bdy);
5218 
5219 	    // If we are casting on ourself (perhaps to escape
5220 	    // entombment?) point randomly.
5221 	    if (!bdx && !bdy)
5222 		rand_direction(bdx, bdy);
5223 
5224 	    oldoverlay = -1;
5225 
5226 	    for (range = 0; range < 5; range++)
5227 	    {
5228 		// Let the user see the boulder here...
5229 		cansee = glbCurLevel->getFlag(tx, ty, SQUAREFLAG_FOV);
5230 
5231 		// Retrieve the boulder...
5232 		if (rollee)
5233 		{
5234 		    MOB *nmob = glbCurLevel->getMob(tx, ty);
5235 		    // Check if died or fell in a hole
5236 		    if (nmob != rollee)
5237 			break;
5238 		    // If they are in a pit or submerged stop them
5239 		    if (rollee->hasIntrinsic(INTRINSIC_INPIT) ||
5240 			rollee->hasIntrinsic(INTRINSIC_SUBMERGED))
5241 			break;
5242 		}
5243 		else
5244 		{
5245 		    boulder = glbCurLevel->getItem(tx, ty);
5246 		    // The boulder may have falling into a pit or what not...
5247 		    if (!boulder || boulder->getDefinition() != ITEM_BOULDER)
5248 			break;
5249 		}
5250 
5251 		victim = glbCurLevel->getMob(tx+bdx, ty+bdy);
5252 		blocker = glbCurLevel->getItem(tx+bdx, ty+bdy);
5253 
5254 		// Verify the boulder can move...
5255 		// Huge creatures just have boulder hit their feet
5256 		// for no damage!
5257 		if (!glbCurLevel->canMove(tx+bdx, ty+bdy, MOVE_STD_FLY) ||
5258 		    (victim && victim->getSize() >= SIZE_GARGANTUAN) ||
5259 		    (blocker && !blocker->isPassable()))
5260 		{
5261 		    if (rollee)
5262 			rollee->formatAndReport("%U <shudder> to a stop.");
5263 		    else
5264 			glbCurLevel->reportMessage("The boulder shudders to a stop.", tx, ty);
5265 		    break;
5266 		}
5267 
5268 		// Erase old overlay...
5269 		if (oldoverlay >= 0)
5270 		{
5271 		    gfx_setoverlay(tx, ty, oldoverlay);
5272 		}
5273 
5274 		// Move the boulder...
5275 		if (rollee)
5276 		{
5277 		    tx += bdx;
5278 		    ty += bdy;
5279 
5280 		    // And put the item in as the new overlay.
5281 		    if (cansee)
5282 		    {
5283 			oldoverlay = gfx_getoverlay(tx, ty);
5284 			gfx_setoverlay(tx, ty, rollee->getTile());
5285 			gfx_sleep(6);
5286 		    }
5287 		    else
5288 			oldoverlay = -1;
5289 
5290 		    // Crush the poor person!
5291 		    victim = glbCurLevel->getMob(tx, ty);
5292 		    if (victim)
5293 		    {
5294 			rollee->actionAttack(bdx, bdy, 1);
5295 			break;
5296 		    }
5297 		    if (!rollee->move(tx, ty))
5298 			break;
5299 		}
5300 		else
5301 		{
5302 		    glbCurLevel->dropItem(boulder);
5303 		    tx += bdx;
5304 		    ty += bdy;
5305 
5306 		    // And put the item in as the new overlay.
5307 		    if (cansee)
5308 		    {
5309 			oldoverlay = gfx_getoverlay(tx, ty);
5310 			gfx_setoverlay(tx, ty, TILE_BOULDER);
5311 			gfx_sleep(6);
5312 		    }
5313 		    else
5314 			oldoverlay = -1;
5315 
5316 		    // Crush the poor person!
5317 		    victim = glbCurLevel->getMob(tx, ty);
5318 		    if (victim)
5319 		    {
5320 			victim->receiveAttack(ATTACK_ROLLINGBOULDER,
5321 					    thismob.getMob(), boulder, 0,
5322 					    ATTACKSTYLE_MISC);
5323 		    }
5324 
5325 		    glbCurLevel->acquireItem(boulder, tx, ty, thismob.getMob());
5326 		}
5327 	    }
5328 	    // Clean up...
5329 	    if (oldoverlay >= 0)
5330 	    {
5331 		gfx_setoverlay(tx, ty, oldoverlay);
5332 	    }
5333 
5334 	    return true;
5335 	}
5336 
5337 	case SPELL_ENTOMB:
5338 	{
5339 	    int		tile;
5340 	    bool	anyeffect = false;
5341 
5342 	    for (dy = -1; dy <= 1; dy++)
5343 	    {
5344 		if (ty+dy < 0 || ty+dy >= MAP_HEIGHT)
5345 		    continue;
5346 		for (dx = -1; dx <= 1; dx++)
5347 		{
5348 		    if (tx+dx < 0 || tx+dx >= MAP_HEIGHT)
5349 			continue;
5350 
5351 		    if (!dx && !dy)
5352 			continue;
5353 
5354 		    // Change this tile into solid rock, if possible.
5355 
5356 		    // Can't entomb critters.
5357 		    if (glbCurLevel->getMob(tx + dx, ty + dy))
5358 			continue;
5359 
5360 		    tile = glbCurLevel->getTile(tx+dx, ty+dy);
5361 
5362 		    if (glb_squaredefs[tile].invulnerable)
5363 			continue;
5364 
5365 		    // Set the tile.
5366 		    if (tile != SQUARE_EMPTY)
5367 			anyeffect = true;
5368 		    glbCurLevel->setTile(tx+dx, ty+dy, SQUARE_EMPTY);
5369 		}
5370 	    }
5371 
5372 	    if (!anyeffect)
5373 	    {
5374 		formatAndReport("%U <feel> the ground shudder.  Blocked, the rock summoned from the plane of earth returs whence it came.");
5375 	    }
5376 	    else
5377 	    {
5378 		formatAndReport("Solid rock rises from the ground, entombing %U.");
5379 		pietyCastSpell(spell);
5380 	    }
5381 
5382 	    return true;
5383 	}
5384 
5385 
5386 	case SPELL_FIREBALL:
5387 	    ourZapSpell = spell;
5388 	    ourFireBallCount = 0;
5389 
5390 	    pietyCastSpell(spell);
5391 
5392 	    if (targetself || dz)
5393 	    {
5394 		if (dz)
5395 		{
5396 		    buf.sprintf("The ray hits the %s.", directionflavour);
5397 		    reportMessage(buf);
5398 		}
5399 		zapCallbackStatic(getX(), getY(), true, &myself);
5400 	    }
5401 	    else
5402 	    {
5403 		// After much soul searching, the bouncing fireballs
5404 		// have been axed during a flight over the atlantic, this
5405 		// time on return form IRDC'09 which was held at CERN.
5406 		// For reasons of economy, however, my own departure is a few
5407 		// weeks later and via Paris.
5408 		// Due to this airbus 320 lacking any power outlet, I am
5409 		// at the mercy of the p1610's 5 hours of battery life.
5410 		// Since I also plan on playing some Civ 4, this session
5411 		// must be truncated to fit withing the 8 hour flight.
5412 		glbCurLevel->fireRay(getX(), getY(),
5413 				 dx, dy,
5414 				 6,
5415 				 MOVE_STD_FLY, false,
5416 				 zapCallbackStatic,
5417 				 &myself);
5418 	    }
5419 	    return true;
5420 
5421 	case SPELL_LIGHTNINGBOLT:
5422 	    formatAndReport("Lightning flies from %r hand.");
5423 
5424 	    pietyCastSpell(spell);
5425 
5426 	    ourZapSpell = spell;
5427 	    if (targetself || dz)
5428 	    {
5429 		if (dz)
5430 		{
5431 		    reportMessage("The ray bounces.");
5432 		}
5433 		zapCallbackStatic(getX(), getY(), false, &myself);
5434 	    }
5435 	    else
5436 	    {
5437 		glbCurLevel->fireRay(getX(), getY(),
5438 				 dx, dy,
5439 				 6,
5440 				 MOVE_STD_FLY, true,
5441 				 zapCallbackStatic,
5442 				 &myself);
5443 	    }
5444 	    return true;
5445 
5446 	case SPELL_CHAINLIGHTNING:
5447 	    pietyCastSpell(spell);
5448 
5449 	    ourZapSpell = spell;
5450 	    ourFireBallCount = 0;
5451 	    if (targetself || dz)
5452 	    {
5453 		if (dz)
5454 		{
5455 		    reportMessage("The ray bounces.");
5456 		}
5457 		zapCallbackStatic(getX(), getY(), false, &myself);
5458 	    }
5459 	    else
5460 	    {
5461 		glbCurLevel->fireRay(getX(), getY(),
5462 				 dx, dy,
5463 				 6,
5464 				 MOVE_STD_FLY, true,
5465 				 zapCallbackStatic,
5466 				 &myself);
5467 	    }
5468 	    return true;
5469 
5470 	case SPELL_SUNFIRE:
5471 	{
5472 	    int		delay;
5473 
5474 	    formatAndReport("%U <talk> to the sun...");
5475 
5476 	    if (hasIntrinsic(INTRINSIC_TIRED))
5477 	    {
5478 		formatAndReport("The sun ignores %r prayers!");
5479 		return true;
5480 	    }
5481 
5482 	    pietyCastSpell(spell);
5483 
5484 	    // Report that the sun answers...
5485 	    formatAndReport("The sun answers %r prayers!");
5486 	    reportMessage("Darkness falls on the surface world as the sun's "
5487 			  "energies are focussed.");
5488 
5489 	    reportMessage("A blinding blast of light falls from the roof.");
5490 
5491 	    // First we blast everyone in the area with the sunfire
5492 	    // blast.
5493 	    ourEffectAttack = ATTACK_SUNFIREBLAST;
5494 	    glbCurLevel->fireBall(getX(), getY(), 3, false,
5495 				 areaAttackCBStatic, &myself);
5496 
5497 	    // Next, melt the rocks.
5498 	    reportMessage("The very rocks melt!");
5499 	    glbCurLevel->fireBall(getX(), getY(), 3, false,
5500 				meltRocksCBStatic, &myself);
5501 
5502 	    delay = rand_dice(20, 20, 20);
5503 	    setTimedIntrinsic(this, INTRINSIC_TIRED, delay);
5504 	    return true;
5505 	}
5506 
5507 	case SPELL_REGENERATE:
5508 	    if (!target || ((target != this) && !canSense(target)))
5509 	    {
5510 		formatAndReport("%U <cast> at thin air.");
5511 		cancelSpell(spell);
5512 		return true;
5513 	    }
5514 
5515 	    // You get 30 + 3d10 turns of REGENERATION.
5516 	    // You get 5 + 1d10 turns of MAGICDRAIN.
5517 	    target->setTimedIntrinsic(this, INTRINSIC_REGENERATION,
5518 				      rand_dice(3, 10, 30));
5519 	    target->setTimedIntrinsic(this, INTRINSIC_MAGICDRAIN,
5520 				      rand_dice(1, 10, 5));
5521 
5522 	    pietyCastSpell(spell);
5523 
5524 	    target->formatAndReport("%U <be> surrounded by a healthy aura.");
5525 	    return true;
5526 
5527 	case SPELL_HEAL:
5528 	    if (!target || ((target != this) && !canSense(target)))
5529 	    {
5530 		formatAndReport("%U <cast> at thin air.");
5531 		cancelSpell(spell);
5532 		return true;
5533 	    }
5534 	    if (target->receiveHeal(10, this))
5535 	    {
5536 		target->formatAndReport("%R wounds close.");
5537 
5538 		pietyCastSpell(spell);
5539 	    }
5540 	    else
5541 	    {
5542 		target->formatAndReport("%U <look> no better.");
5543 	    }
5544 	    return true;
5545 
5546 	case SPELL_SLOWPOISON:
5547 	    if (!target || ((target != this) && !canSense(target)))
5548 	    {
5549 		formatAndReport("%U <cast> at thin air.");
5550 		cancelSpell(spell);
5551 		return true;
5552 	    }
5553 #if 0
5554 	    if (target->receiveSlowPoison())
5555 	    {
5556 		target->formatAndReport("The poison slows in %R veins.");
5557 
5558 		pietyCastSpell(spell);
5559 	    }
5560 	    else
5561 	    {
5562 		target->formatAndReport("%U <be> not poisoned.");
5563 	    }
5564 #else
5565 	    // We grant poison resistance for a 5 + 1d5 turns.
5566 	    target->formatAndReport("%R metabolism stabilizes.");
5567 	    target->setTimedIntrinsic(this, INTRINSIC_RESISTPOISON,
5568 				    rand_dice(1, 5, 6));
5569 #endif
5570 	    return true;
5571 
5572 	case SPELL_MAJORHEAL:
5573 	    if (!target || ((target != this) && !canSense(target)))
5574 	    {
5575 		formatAndReport("%U <cast> at thin air.");
5576 		cancelSpell(spell);
5577 		return true;
5578 	    }
5579 	    if (target->receiveHeal(30, this))
5580 	    {
5581 		target->formatAndReport("%R wounds close.");
5582 
5583 		pietyCastSpell(spell);
5584 	    }
5585 	    else
5586 	    {
5587 		target->formatAndReport("%U <look> no better.");
5588 	    }
5589 	    return true;
5590 
5591 	case SPELL_CUREPOISON:
5592 	    if (!target || ((target != this) && !canSense(target)))
5593 	    {
5594 		formatAndReport("%U <cast> at thin air.");
5595 		cancelSpell(spell);
5596 		return true;
5597 	    }
5598 	    if (target->receiveCure())
5599 	    {
5600 		formatAndReport("The poison is expunged from %MR veins.", target);
5601 		pietyCastSpell(spell);
5602 	    }
5603 	    else
5604 	    {
5605 		formatAndReport("%M <M:be> not poisoned.", target);
5606 	    }
5607 	    return true;
5608 
5609 	case SPELL_RESURRECT:
5610 	    // Determine the corpse that is pointed at...
5611 	    if (target)
5612 	    {
5613 		target->formatAndReport("%U <be> not in need of aid.");
5614 		cancelSpell(spell);
5615 		return true;
5616 	    }
5617 
5618 	    // Resurrect all corpses at the target square...
5619 	    if (!glbCurLevel->resurrectCorpses(tx, ty, this))
5620 	    {
5621 		formatAndReport("The stones fail to come to life.");
5622 		return true;
5623 	    }
5624 	    else
5625 		pietyCastSpell(spell);
5626 
5627 	    return true;
5628 
5629 	case SPELL_SUMMON_FAMILIAR:
5630 	{
5631 	    // Find a location for the familiar
5632 	    tx = getX();
5633 	    ty = getY();
5634 
5635 	    if (!glbCurLevel->findCloseTile(tx, ty, MOVE_WALK))
5636 	    {
5637 		glbCurLevel->reportMessage(
5638 		    "A famliar briefly appears before thinking better of it.",
5639 		    tx, ty);
5640 		return true;
5641 	    }
5642 
5643 	    MOB		*familiar;
5644 
5645 	    familiar = MOB::create(MOB_BAT);
5646 
5647 	    // Bats always start with their full possible health.
5648 	    // No reason to be evil with 1 HP bats :>
5649 	    familiar->forceHP(10);
5650 
5651 	    // Likewise, they get some magic points to play with
5652 	    familiar->incrementMaxMP(10 - familiar->getMaxMP());
5653 
5654 	    // Summoned creatures never have inventory.
5655 	    familiar->destroyInventory();
5656 
5657 	    familiar->move(tx, ty, true);
5658 
5659 	    glbCurLevel->registerMob(familiar);
5660 
5661 	    familiar->formatAndReport("%U <come> to %MR call!", this);
5662 
5663 	    familiar->makeSlaveOf(this);
5664 	    familiar->setIntrinsic(INTRINSIC_FAMILIAR);
5665 
5666 	    pietyCastSpell(spell);
5667 
5668 	    return true;
5669 	}
5670 
5671 	case SPELL_TRANSFER_KNOWLEDGE:
5672 	{
5673 	    if (!target || !canSense(target))
5674 	    {
5675 		// Unable to transfer to destination.
5676 		formatAndReport("%U watch helplessly as %R hard earned experience evaporates.");
5677 		return true;
5678 	    }
5679 	    target->formatAndReport("%U <be> wiser.");
5680 	    // Thanks to the pyramid scheme, you will get half the experience
5681 	    // back. This might thus be too powerful.
5682 	    target->receiveExp(250);
5683 
5684 	    pietyCastSpell(spell);
5685 	    return true;
5686 	}
5687 
5688 	case SPELL_FETCH:
5689 	{
5690 	    bool		somethinghappened = false;
5691 	    int			i;
5692 	    ITEM		*item;
5693 
5694 	    // You don't need visible coordinates.
5695 	    // You do need to either recall the item at the coordinates
5696 	    // or sense the creature at the coordinates.
5697 	    if (target && canSense(target))
5698 	    {
5699 		int		fx, fy;
5700 
5701 		// Summon the creature to your presence!
5702 		formatAndReport("%U <summon> %MU.", target);
5703 
5704 		// We also want the summoned creature to know what happened.
5705 		target->formatAndReport("%U <be> summoned to %MR presence.", this);
5706 
5707 		// Find the closest valid square to us.
5708 		fx = getX();
5709 		fy = getY();
5710 		glbCurLevel->findCloseTile(fx, fy, target->getMoveType());
5711 
5712 		target->actionTeleport(false, true, fx, fy);
5713 		somethinghappened = true;
5714 	    }
5715 
5716 	    ITEMSTACK	stack;
5717 
5718 	    // Return a list of all mapped items.
5719 	    glbCurLevel->getItemStack(stack, tx, ty, -1, true);
5720 	    for (i = 0; i < stack.entries(); i++)
5721 	    {
5722 		item = stack(i);
5723 		if (item->hasIntrinsic(INTRINSIC_TELEFIXED))
5724 		{
5725 		    item->formatAndReport("%U <shudder>.");
5726 		    continue;
5727 		}
5728 		formatAndReport("%U <fetch> %IU.", item);
5729 		glbCurLevel->dropItem(item);
5730 		glbCurLevel->acquireItem(item, getX(), getY(), this);
5731 		somethinghappened = true;
5732 	    }
5733 
5734 	    if (!somethinghappened)
5735 	    {
5736 		formatAndReport("%R summons go unheeded.");
5737 	    }
5738 	    else
5739 	    {
5740 		pietyCastSpell(spell);
5741 	    }
5742 	    return true;
5743 	}
5744 
5745 
5746 	case SPELL_BLINK:
5747 	    // Determine if the given coordinates are visable..
5748 	    if (!hasIntrinsic(INTRINSIC_BLIND) &&
5749 		istargetlit &&
5750 		glbCurLevel->hasLOS(getX(), getY(),
5751 				    tx, ty))
5752 	    {
5753 		if (target && target != this)
5754 		{
5755 		    formatAndReport("%U <stare> at %MU.", target);
5756 		    return true;
5757 		}
5758 		else
5759 		{
5760 		    pietyCastSpell(spell);
5761 		    return actionTeleport(false, true,
5762 					  tx, ty);
5763 		}
5764 	    }
5765 
5766 	    // The creature cannot see its destination - blink is
5767 	    // illegal!
5768 	    formatAndReport("%U <stare> into darkness.");
5769 	    cancelSpell(spell);
5770 	    return true;
5771 
5772 	case SPELL_FLAMESTRIKE:
5773 	    if (target && ((target == this) || canSense(target)))
5774 	    {
5775 		formatAndReport("%U <call> on %r god's power!");
5776 
5777 		pietyCastSpell(spell);
5778 		if (isAvatar())
5779 		{
5780 		    // Compute our piety...
5781 		    ATTACK_DEF		attack;
5782 		    memcpy(&attack, &glb_attackdefs[ATTACK_FLAMESTRIKE], sizeof(ATTACK_DEF));
5783 		    // Every doubling of piety adds another die.
5784 		    // Zero or less piety gets no dice
5785 		    // We know we are capped at 10k, or 2^13 for an awesome
5786 		    // 13d6 attack.
5787 		    int		piety = piety_chosengodspiety();
5788 		    attack.damage.myNumdie = 0;
5789 		    while (piety > 1)
5790 		    {
5791 			attack.damage.myNumdie++;
5792 			piety >>= 1;
5793 		    }
5794 		    target->receiveAttack(&attack, this, 0, 0,
5795 					    ATTACKSTYLE_SPELL);
5796 		}
5797 		else
5798 		    target->receiveAttack(ATTACK_FLAMESTRIKE, this, 0, 0,
5799 					    ATTACKSTYLE_SPELL);
5800 	    }
5801 	    else
5802 	    {
5803 		formatAndReport("%U foolishly <ask> %r god to strike thin air.");
5804 		cancelSpell(spell);
5805 	    }
5806 	    return true;
5807 
5808 	case SPELL_TELEPORT:
5809 	    pietyCastSpell(spell);
5810 	    return actionTeleport();
5811 
5812 	case SPELL_TELEWITHCONTROL:
5813 	    pietyCastSpell(spell);
5814 	    return actionTeleport(true, true);
5815 
5816 	case SPELL_DETECTCURSE:
5817 	{
5818 	    // All items in our inventory have their status marked.
5819 	    // Only get credit if this changed something.
5820 	    if (!isAvatar())
5821 		return true;
5822 	    ITEM		*item;
5823 	    bool		 didid = false;
5824 
5825 	    formatAndReport("%U <become> attuned to %r possessions...");
5826 
5827 	    for (item = myInventory; item; item = item->getNext())
5828 	    {
5829 		if (!item->isKnownCursed())
5830 		{
5831 		    const char		*colour = "grey";
5832 		    if (item->isBlessed())
5833 			colour = "white";
5834 		    else if (item->isCursed())
5835 			colour = "black";
5836 
5837 		    formatAndReport("%R %Iu <I:glow> %B1.", item, colour);
5838 		    item->markCursedKnown();
5839 		    didid = true;
5840 		}
5841 	    }
5842 	    if (didid)
5843 	    {
5844 		pietyCastSpell(spell);
5845 	    }
5846 	    else
5847 	    {
5848 		formatAndReport("%U <sense> nothing new.");
5849 	    }
5850 	    return true;
5851 	}
5852 
5853 	case SPELL_IDENTIFY:
5854 	{
5855 	    // This had better be the player.
5856 	    UT_ASSERT(isAvatar());
5857 	    if (!isAvatar())
5858 		return true;
5859 
5860 	    // No UI during stress test.
5861 	    if (glbStressTest)
5862 		return true;
5863 
5864 	    // Select item to identify.
5865 	    int			selectx, selecty;
5866 	    ITEM		*item = 0;
5867 
5868 	    gfx_showinventory(this);
5869 	    gfx_getinvcursor(selectx, selecty);
5870 	    if (gfx_selectinventory(selectx, selecty))
5871 	    {
5872 		item = getItem(selectx, selecty);
5873 	    }
5874 
5875 	    gfx_hideinventory();
5876 	    writeGlobalActionBar(true);
5877 
5878 	    // We perform the identify after hiding the inventory
5879 	    // so we don't lose the final line of the identification
5880 	    // to the inventory clear.
5881 	    if (item)
5882 	    {
5883 		item->markIdentified();
5884 		formatAndReport("%U <identify> %IU.", item);
5885 		pietyCastSpell(spell);
5886 	    }
5887 	    else
5888 	    {
5889 		// Undo cost...
5890 		cancelSpell(spell);
5891 		return false;
5892 	    }
5893 
5894 	    return true;
5895 	}
5896 
5897 	case SPELL_LIGHT:
5898 	    formatAndReport("Light spreads out from %U.");
5899 
5900 	    pietyCastSpell(spell);
5901 	    glbCurLevel->applyFlag(SQUAREFLAG_LIT,
5902 		    getX() + dx,
5903 		    getY() + dy,
5904 		    5, false, true);
5905 	    return true;
5906 
5907 	case SPELL_DARKNESS:
5908 	    formatAndReport("Darkness spreads out from %U.");
5909 
5910 	    pietyCastSpell(spell);
5911 	    glbCurLevel->applyFlag(SQUAREFLAG_LIT,
5912 		    getX() + dx,
5913 		    getY() + dy,
5914 		    5, false, false);
5915 	    return true;
5916 
5917 	case SPELL_MAGICMAP:
5918 	    // Only applicable to avatar.
5919 	    if (!isAvatar())
5920 		return true;
5921 
5922 	    formatAndReport("A map forms in %r mind");
5923 	    pietyCastSpell(spell);
5924 	    glbCurLevel->markMapped(getX() + dx, getY() + dy,
5925 				    10, 10,
5926 				    false);
5927 	    return true;
5928 
5929 	case SPELL_KNOCK:
5930 	    if (!hasIntrinsic(INTRINSIC_BLIND) &&
5931 		istargetlit &&
5932 		glbCurLevel->hasLOS(getX(), getY(),
5933 				    tx, ty))
5934 	    {
5935 		SQUARE_NAMES	tile;
5936 
5937 		// Determine if there is a door or secret door
5938 		// in the targetted square...
5939 		tile = glbCurLevel->getTile(tx, ty);
5940 
5941 		switch (tile)
5942 		{
5943 		    case SQUARE_OPENDOOR:
5944 		    {
5945 			// Find a dx/dy.
5946 			int	dx, dy;
5947 			MOB	*mob;
5948 			ITEM	*item;
5949 
5950 			// Knock is always away from caster
5951 			dx = SIGN(tx - getX());
5952 			dy = SIGN(ty - getY());
5953 
5954 			if (glbCurLevel->canMove(tx+dx,ty,MOVE_STD_FLY,false))
5955 			{
5956 			    dy = 0;
5957 			}
5958 			else if (glbCurLevel->canMove(tx,ty+dy,MOVE_STD_FLY,false))
5959 			{
5960 			    dx = 0;
5961 			}
5962 			else
5963 			{
5964 			    dy = dx = 0;
5965 			}
5966 
5967 			if (dx || dy)
5968 			{
5969 			    // Check to see if a creature or item
5970 			    // blocks the square.
5971 			    mob = glbCurLevel->getMob(tx, ty);
5972 			    if (mob)
5973 			    {
5974 				if (glbCurLevel->knockbackMob(tx, ty, dx, dy, true))
5975 				{
5976 				    mob = glbCurLevel->getMob(tx+dx, ty+dy);
5977 				    if (mob)
5978 					mob->receiveDamage(
5979 						ATTACK_DOORSLAM,
5980 						this,
5981 						0, 0,
5982 						ATTACKSTYLE_SPELL);
5983 				}
5984 			    }
5985 			}
5986 
5987 			// Check for any blockers.
5988 			mob = glbCurLevel->getMob(tx, ty);
5989 			if (mob)
5990 			{
5991 			    mob->formatAndReport("%U <hold> the door open!");
5992 			    return true;
5993 			}
5994 
5995 			// Check to see if there is an item.
5996 			item = glbCurLevel->getItem(tx, ty);
5997 			if (item)
5998 			{
5999 			    formatAndReport("The door is blocked by %IU.", item);
6000 			    return true;
6001 			}
6002 
6003 			reportMessage("The door closes.");
6004 			glbCurLevel->setTile(tx, ty, SQUARE_DOOR);
6005 			pietyCastSpell(spell);
6006 			break;
6007 		    }
6008 		    case SQUARE_DOOR:
6009 		    case SQUARE_BLOCKEDDOOR:
6010 			reportMessage("The door opens.");
6011 			glbCurLevel->setTile(tx, ty, SQUARE_OPENDOOR);
6012 			pietyCastSpell(spell);
6013 			break;
6014 		    case SQUARE_MAGICDOOR:
6015 			reportMessage("A voice booms: \"My test is not so easily bypassed!\"");
6016 			break;
6017 		    case SQUARE_SECRETDOOR:
6018 			reportMessage("A secret door opens.");
6019 			glbCurLevel->setTile(tx, ty, SQUARE_OPENDOOR);
6020 			pietyCastSpell(spell);
6021 			break;
6022 
6023 		    default:
6024 			reportMessage("Nothing happens.");
6025 			break;
6026 		}
6027 		return true;
6028 	    }
6029 	    formatAndReport("%U <hear> a distant knock.");
6030 	    return true;
6031 
6032 	case SPELL_TRACK:
6033 	    // Determine if there is a creature there that
6034 	    // we can sense.
6035 	    if (target && ((target == this) || canSense(target)))
6036 	    {
6037 		formatAndReport("%U <build> a standing wave of magic.");
6038 		target->formatAndReport("%U <be> revealed to all.");
6039 
6040 		target->setTimedIntrinsic(this,
6041 				INTRINSIC_POSITIONREVEALED,
6042 				100);
6043 
6044 		pietyCastSpell(spell);
6045 	    }
6046 	    else
6047 	    {
6048 		formatAndReport("%U <try> to track the wind.");
6049 		cancelSpell(spell);
6050 	    }
6051 	    return true;
6052 
6053 	case SPELL_DIAGNOSE:
6054 	    // Determine if there is a creature there that
6055 	    // we can sense.
6056 	    if (target && ((target == this) || canSense(target)))
6057 	    {
6058 		formatAndReport("%U <peer> into %MU with your magic.", target);
6059 
6060 		target->characterDump(false, false, false);
6061 
6062 		pietyCastSpell(spell);
6063 	    }
6064 	    else
6065 	    {
6066 		formatAndReport("%U <find> the empty air in surprisingly good condition.");
6067 		cancelSpell(spell);
6068 	    }
6069 	    return true;
6070 
6071 	case SPELL_POSSESS:
6072 	{
6073 	    if (target)
6074 	    {
6075 		// Control target
6076 		// Attempt a mental roll.
6077 		int		check = smartsCheck(target->getSmarts());
6078 
6079 		switch (check)
6080 		{
6081 		    case -1:
6082 			// Force failure.
6083 			formatAndReport("%MU easily <M:rebuff> %R attempt at control.", target);
6084 			break;
6085 		    case 0:
6086 			// Failure, but could work.
6087 			formatAndReport("%MU <M:rebuff> %R attempt at control.", target);
6088 			break;
6089 		    case 1:
6090 			// Success, but could fail.
6091 			formatAndReport("%U <overpower> %MR mental defences.", target);
6092 			break;
6093 		    case 2:
6094 			// Success always
6095 			formatAndReport("%U easily <overpower> %MR mental defences.", target);
6096 			break;
6097 		    default:
6098 			UT_ASSERT(!"Invalid smarts check");
6099 			break;
6100 		}
6101 		if (check >= 1)
6102 		{
6103 		    actionPossess(target, rand_range(50, 100));
6104 		    target->pietyCastSpell(spell);
6105 		}
6106 		else
6107 		{
6108 		    // Still get noticed by the gods, as this is equivalent
6109 		    // to missing.
6110 		    pietyCastSpell(spell);
6111 		}
6112 	    }
6113 	    else
6114 	    {
6115 		formatAndReport("%U <find> nothing to possess.");
6116 		cancelSpell(spell);
6117 	    }
6118 	    break;
6119 	}
6120 
6121 	case SPELL_PRESERVE:
6122 	{
6123 	    ITEMSTACK	stack;
6124 	    bool	anythinghappen = false;
6125 
6126 	    formatAndReport("%U <form> a stasis field.");
6127 
6128 	    if (target)
6129 	    {
6130 		// Preserve the target.
6131 		target->formatAndReport("%U <be> encased in a purple glow.");
6132 
6133 		// Designed to be long enough to prevent stoning.
6134 		target->setTimedIntrinsic(this,
6135 					INTRINSIC_UNCHANGING,
6136 					6);
6137 
6138 		anythinghappen = true;
6139 	    }
6140 
6141 	    glbCurLevel->getItemStack(stack, tx, ty);
6142 	    n = stack.entries();
6143 	    for (i = 0; i < n; i++)
6144 	    {
6145 		// If this is a corpse, set its charges to 255
6146 		// to preserve it.
6147 		if (stack(i)->getDefinition() == ITEM_BONES ||
6148 		    stack(i)->getDefinition() == ITEM_CORPSE)
6149 		{
6150 		    stack(i)->formatAndReport("%U <be> encased in a purple glow.");
6151 		    stack(i)->setAsPreserved();
6152 		    anythinghappen = true;
6153 		}
6154 	    }
6155 
6156 	    if (!anythinghappen)
6157 		reportMessage("Nothing happens.");
6158 	    else
6159 		pietyCastSpell(spell);
6160 	    return true;
6161 	}
6162 
6163 	case SPELL_PETRIFY:
6164 	{
6165 	    bool		anythinghappen = false;
6166 	    ITEMSTACK		stack;
6167 
6168 	    if (target)
6169 	    {
6170 		// Petrify the target.
6171 		anythinghappen = true;
6172 		formatAndReport("A layer of dust covers %MU.", target);
6173 		if (target->hasIntrinsic(INTRINSIC_RESISTSTONING))
6174 		{
6175 		    formatAndReport("The dust falls to the floor.");
6176 		}
6177 		else
6178 		    target->setTimedIntrinsic(this,
6179 				INTRINSIC_STONING,
6180 				3);
6181 	    }
6182 
6183 	    glbCurLevel->getItemStack(stack, tx, ty);
6184 	    n = stack.entries();
6185 	    for (i = 0; i < n; i++)
6186 		anythinghappen |= stack(i)->petrify(glbCurLevel, this);
6187 
6188 	    if (!anythinghappen)
6189 		reportMessage("Nothing happens.");
6190 	    else pietyCastSpell(spell);
6191 
6192 	    return true;
6193 	}
6194 
6195 	case SPELL_STONETOFLESH:
6196 	{
6197 	    bool		anythinghappen = false;
6198 	    ITEMSTACK		stack;
6199 	    SQUARE_NAMES	tile;
6200 
6201 	    if (target)
6202 	    {
6203 		anythinghappen = true;
6204 		formatAndReport("A layer of dandruff covers %MU.", target);
6205 
6206 		// Grant temporary stoning resistance.
6207 		target->setTimedIntrinsic(this, INTRINSIC_RESISTSTONING,
6208 					rand_dice(1, 5, 6));
6209 
6210 		// UnPetrify the target.  This is bad news
6211 		// for earth elementals :>
6212 		target->actionUnPetrify();
6213 	    }
6214 
6215 	    glbCurLevel->getItemStack(stack, tx, ty);
6216 	    n = stack.entries();
6217 	    for (i = 0; i < n; i++)
6218 		anythinghappen |= stack(i)->unpetrify(glbCurLevel, this);
6219 
6220 	    // Check to see if the tile is stone, in which case
6221 	    // we turn it to a mound of flesh.
6222 	    tile = glbCurLevel->getTile(tx, ty);
6223 
6224 	    switch (tile)
6225 	    {
6226 		case SQUARE_WALL:
6227 		case SQUARE_EMPTY:
6228 		case SQUARE_SECRETDOOR:
6229 		{
6230 		    ITEM		*flesh;
6231 
6232 		    anythinghappen = true;
6233 		    glbCurLevel->reportMessage("The wall slumps into a large mound of flesh.", tx, ty);
6234 		    glbCurLevel->setTile(tx, ty, SQUARE_CORRIDOR);
6235 		    flesh = ITEM::create(ITEM_MOUNDFLESH, false, true);
6236 		    glbCurLevel->acquireItem(flesh, tx, ty, this);
6237 
6238 		    break;
6239 		}
6240 
6241 		default:
6242 		    break;
6243 	    }
6244 
6245 	    if (!anythinghappen)
6246 		reportMessage("Nothing happens.");
6247 	    else pietyCastSpell(spell);
6248 
6249 	    return true;
6250 	}
6251 
6252 	case SPELL_DIRECTWIND:
6253 	{
6254 	    // I have never understood wind names.
6255 	    const char *direction[9] =
6256 		    { "southeast", "south", "southwest",
6257 		      "east", "invalid", "west",
6258 		      "northeast", "north", "northwest" };
6259 	    const char *windname;
6260 	    int		windidx;
6261 
6262 	    windidx = (dx + 1) + (dy + 1) * 3;
6263 	    windname = direction[windidx];
6264 
6265 	    if (dx || dy)
6266 	    {
6267 		buf.sprintf("A strong wind blows from the %s.  ",
6268 			     windname);
6269 	    }
6270 	    else
6271 	    {
6272 		buf.reference("The air stills to an unnatural calm.  ");
6273 	    }
6274 	    // Global message.
6275 	    msg_report(buf);
6276 	    glbCurLevel->setWindDirection(dx, dy, 20 + rand_choice(20));
6277 
6278 	    pietyCastSpell(spell);
6279 	    break;
6280 	}
6281 
6282 	case SPELL_RAISE_UNDEAD:
6283 	{
6284 	    // Determine if the given coordinates are visable..
6285 	    if (hasIntrinsic(INTRINSIC_BLIND) ||
6286 		!istargetlit ||
6287 		!glbCurLevel->hasLOS(getX(), getY(),
6288 				     tx, ty))
6289 	    {
6290 		// Cannot see the position, illegal.
6291 		formatAndReport("%U <gesture> into the darkness.");
6292 		cancelSpell(spell);
6293 		return true;
6294 	    }
6295 
6296 	    // Check to see if a living creature is there.
6297 	    if (target)
6298 	    {
6299 		formatAndReport("%MU <M:be> still twitching.", target);
6300 		return true;
6301 	    }
6302 
6303 	    // Try to raise a zombie first.
6304 	    if (!glbCurLevel->raiseZombie(tx, ty, this))
6305 	    {
6306 		// If that fails, raise a skeleton.
6307 		if (!glbCurLevel->raiseSkeleton(tx, ty, this))
6308 		{
6309 		    // All failed.
6310 		    formatAndReport("Without any corpses or bones, the magic is wasted.");
6311 		    return true;
6312 		}
6313 	    }
6314 
6315 	    // Success!
6316 	    pietyCastSpell(spell);
6317 	    return true;
6318 	}
6319 
6320 	case SPELL_RECLAIM_SOUL:
6321 	{
6322 	    // Determine if the given coordinates are visable..
6323 	    if (hasIntrinsic(INTRINSIC_BLIND) ||
6324 		!istargetlit ||
6325 		!glbCurLevel->hasLOS(getX(), getY(),
6326 				     tx, ty))
6327 	    {
6328 		// Cannot see the position, illegal.
6329 		formatAndReport("%U <gesture> into the darkness.");
6330 		cancelSpell(spell);
6331 		return true;
6332 	    }
6333 
6334 	    // Check if no creature is there.
6335 	    if (!target)
6336 	    {
6337 		formatAndReport("%U <try> to extract a soul from empty air.");
6338 		return true;
6339 	    }
6340 
6341 	    // Verify the target is an undead.
6342 	    if (target->defn().mobtype != MOBTYPE_UNDEAD)
6343 	    {
6344 		formatAndReport("The soul of %MU is still bound too tightly.", target);
6345 		return true;
6346 	    }
6347 
6348 	    // Verify we are not oneself.
6349 	    if (target == this)
6350 	    {
6351 		formatAndReport("The laws of thermodynamics forbid %U from using %MU as a power source.", target);
6352 		return true;
6353 	    }
6354 
6355 	    // Verify we command the undead.
6356 	    if (!target->isSlave(this))
6357 	    {
6358 		formatAndReport("%MU is not under the control of %U.", target);
6359 		return true;
6360 	    }
6361 
6362 	    // Phew!  We have a winner...
6363 	    formatAndReport("%U <send> vile tendrils that envelope %MU and yank free the last vestiges of unlife.", target);
6364 
6365 	    // Increase our health.
6366 	    int		extrahp, maxhp;
6367 
6368 	    // Maximum heal is the target's strength
6369 	    extrahp = target->getHP();
6370 
6371 	    // We cap again according to the undead type.
6372 	    switch (target->getDefinition())
6373 	    {
6374 		case MOB_ZOMBIE:
6375 		    maxhp = 10;
6376 		    break;
6377 		case MOB_SKELETON:
6378 		    maxhp = 5;
6379 		    break;
6380 		case MOB_GHAST:
6381 		    maxhp = 20;
6382 		    break;
6383 		default:
6384 		    // I don't think any other type can fall through to here,
6385 		    // but let us prepare for inevitable programmer error.
6386 		    maxhp = 30;
6387 		    break;
6388 	    }
6389 
6390 	    if (extrahp > maxhp)
6391 		extrahp = maxhp;
6392 
6393 	    // Heal ourself.
6394 	    receiveHeal(extrahp, this, true);
6395 
6396 	    // Destroy the target.
6397 	    glbCurLevel->unregisterMob(target);
6398 	    target->triggerAsDeath(ATTACK_RECLAIM_SOUL, this, 0, false);
6399 	    delete target;
6400 
6401 	    // Success!
6402 	    pietyCastSpell(spell);
6403 	    return true;
6404 	}
6405 
6406 	case SPELL_DARK_RITUAL:
6407 	{
6408 	    int		dx, dy;
6409 	    ATTACK_NAMES     datk[4] = { ATTACK_SPELL_DARK_RITUAL_EAST,
6410 					 ATTACK_SPELL_DARK_RITUAL_SOUTH,
6411 					 ATTACK_SPELL_DARK_RITUAL_WEST,
6412 					 ATTACK_SPELL_DARK_RITUAL_NORTH };
6413 	    int		dir;
6414 	    int		numkilled = 0;
6415 	    MOBREF	myself;
6416 
6417 	    formatAndReport("%U <scatter> blood in a rough pentagram and start the unspeakable ritual.");
6418 
6419 	    myself.setMob(this);
6420 
6421 	    for (dir = 0; dir < 4; dir++)
6422 	    {
6423 		getDirection(dir, dx, dy);
6424 
6425 		// Determine if their is an owned undead in this direction.
6426 		target = glbCurLevel->getMob(getX() + dx, getY() + dy);
6427 		if (!target)
6428 		    continue;
6429 
6430 		// See if undead & owned.
6431 		if (target->defn().mobtype != MOBTYPE_UNDEAD)
6432 		    continue;
6433 
6434 		if (!target->isSlave(this))
6435 		    continue;
6436 
6437 		numkilled++;
6438 
6439 		// Transform the undead & empower the attack.
6440 		formatAndReport("%U <power> a deadly attack by consuming %MU.", target);
6441 
6442 		// Destroy the target.
6443 		glbCurLevel->unregisterMob(target);
6444 		target->triggerAsDeath(ATTACK_DARK_RITUAL_CONSUME, this, 0, false);
6445 		delete target;
6446 
6447 		// Apply the attack.
6448 		ourEffectAttack = datk[dir];
6449 		// Todo, maybe cone attack instead?
6450 		glbCurLevel->fireBall(getX() + dx * 2,
6451 				      getY() + dy * 2,
6452 				      1, true,
6453 				      areaAttackCBStatic, &myself);
6454 
6455 		// Check if we died...
6456 		if (!myself.getMob() ||
6457 		    myself.getMob() != this ||
6458 		    myself.getMob()->hasIntrinsic(INTRINSIC_DEAD))
6459 		{
6460 		    return true;
6461 		}
6462 	    }
6463 	    if (numkilled)
6464 	    {
6465 		pietyCastSpell(spell);
6466 		if (numkilled == 4)
6467 		{
6468 		    // Charge us up!
6469 		    setTimedIntrinsic(0, INTRINSIC_LICHFORM, 2);
6470 		}
6471 	    }
6472 	    else
6473 	    {
6474 		// No visual effect.
6475 		formatAndReport("With no willing victims, %R ritual has no effect.", target);
6476 		return true;
6477 	    }
6478 	    return true;
6479 	}
6480 
6481 	case SPELL_GHASTIFY:
6482 	    // You must be targetting a creature.
6483 	    if (!target)
6484 	    {
6485 		formatAndReport("Cruel magics envelope thin air and dissipate.");
6486 		cancelSpell(spell);
6487 		return true;
6488 	    }
6489 
6490 	    // The target must have some life force.
6491 	    if (glb_mobdefs[target->getDefinition()].mobtype == MOBTYPE_UNDEAD)
6492 	    {
6493 		formatAndReport("%MU <M:have> no life force to convert.", target);
6494 		return true;
6495 	    }
6496 
6497 	    // The target must be at death's gate.
6498 	    if ((target->getHP() >= target->getMaxHP()) ||
6499 		(target->getHP() != 1))
6500 	    {
6501 		formatAndReport("%MU, not being at death's gate, <M:resist>.", target);
6502 		return true;
6503 	    }
6504 
6505 	    if (target->hasIntrinsic(INTRINSIC_UNCHANGING))
6506 	    {
6507 		target->formatAndReport("%U <shudder> as malevolent magics dissipate.");
6508 		return true;
6509 	    }
6510 
6511 	    // GHASTIFY!
6512 	    formatAndReport("Malevolent magics surround %MU, twisting %MA into a ghast!", target);
6513 
6514 	    pietyCastSpell(spell);
6515 
6516 	    // Yes, they keep all of their powers.  Perhaps this is too evil
6517 	    // We'll see...
6518 	    target->myDefinition = MOB_GHAST;
6519 
6520 	    // All of their health is instantly returned.
6521 	    // This allows this to be used as a "save self" spell.
6522 	    target->setMaximalHP();
6523 
6524 	    target->makeSlaveOf(this);
6525 
6526 	    return true;
6527 
6528 	case SPELL_BINDSOUL:
6529 	{
6530 	    ITEMSTACK		 stack;
6531 	    ITEM		*item, *corpse = 0;
6532 	    int			 i, n;
6533 
6534 	    glbCurLevel->getItemStack(stack, tx, ty);
6535 
6536 	    n = stack.entries();
6537 	    for (i = 0; i < n; i++)
6538 	    {
6539 		item = stack(i);
6540 		if (item->getDefinition() == ITEM_BONES ||
6541 		    item->getDefinition() == ITEM_CORPSE)
6542 		{
6543 		    corpse = item;
6544 		}
6545 	    }
6546 
6547 	    if (!corpse)
6548 	    {
6549 		formatAndReport("With no corpse, %R energies dissipate.");
6550 		return true;
6551 	    }
6552 
6553 	    if (!corpse->getCorpseMob())
6554 	    {
6555 		reportMessage("The soul has left the corpse.");
6556 		return true;
6557 	    }
6558 
6559 	    // If there is target, we want to poly it into
6560 	    // the given corpse tyoe
6561 	    if (target)
6562 	    {
6563 		bool		forcetame = false;
6564 		MOBREF		newpet;
6565 
6566 		formatAndReport("Dark energies swirl around %MU.", target);
6567 		pietyCastSpell(spell);
6568 
6569 		if (target != this)
6570 		    forcetame = true;
6571 
6572 		newpet.setMob(target);
6573 
6574 		target->actionPolymorph(false, false,
6575 				    corpse->getCorpseMob()->getDefinition());
6576 
6577 		// WARNING this is now invalid if we self-polymorphed!
6578 
6579 		// Only enslave if poly success.
6580 		if (target == newpet.getMob())
6581 		    forcetame = false;
6582 
6583 		target = newpet.getMob();
6584 		if (forcetame)
6585 		{
6586 		    target->makeSlaveOf(this);
6587 		}
6588 
6589 		// Destroy the corpse.
6590 		glbCurLevel->dropItem(corpse);
6591 		delete corpse;
6592 
6593 		return true;
6594 	    }
6595 
6596 	    // Check to see if there is a mound of flesh.  Then
6597 	    // we can make a Flesh Golem.
6598 	    // In case of a boulder, we get a stone golem.
6599 	    {
6600 		ITEM		*mound = 0;
6601 		bool		 isboulder = false;
6602 
6603 		n = stack.entries();
6604 		for (i = 0; i < n; i++)
6605 		{
6606 		    item = stack(i);
6607 		    if (item->getDefinition() == ITEM_MOUNDFLESH)
6608 		    {
6609 			mound = item;
6610 			isboulder = false;
6611 		    }
6612 		    if (item->getDefinition() == ITEM_BOULDER)
6613 		    {
6614 			mound = item;
6615 			isboulder = true;
6616 		    }
6617 		}
6618 
6619 		if (mound)
6620 		{
6621 		    BUF		corpsename = corpse->getName();
6622 		    BUF		moundname = mound->getName();
6623 		    buf.sprintf("Shadowy tendrils yank the soul from %s and infuse it in %s.",
6624 			    corpsename.buffer(), moundname.buffer());
6625 
6626 		    reportMessage(buf);
6627 
6628 		    pietyCastSpell(spell);
6629 
6630 		    // Destroy the corpse.
6631 		    glbCurLevel->dropItem(corpse);
6632 		    delete corpse;
6633 
6634 		    // Destory the mound of flesh.
6635 		    glbCurLevel->dropItem(mound);
6636 		    delete mound;
6637 
6638 		    // Create the appropriate golem.
6639 		    MOB		*golem;
6640 
6641 		    if (isboulder)
6642 			golem = MOB::create(MOB_STONEGOLEM);
6643 		    else
6644 			golem = MOB::create(MOB_FLESHGOLEM);
6645 
6646 		    // Make tame and summoned.
6647 		    golem->makeSlaveOf(this);
6648 		    golem->setTimedIntrinsic(this, INTRINSIC_SUMMONED,
6649 					    isboulder ? 500 : 2000);
6650 
6651 		    // Add to the map.
6652 		    golem->move(tx, ty, true);
6653 		    glbCurLevel->registerMob(golem);
6654 
6655 		    return true;
6656 		}
6657 	    }
6658 
6659 	    // Check if we can create a stone golem out of
6660 	    // solid rock.  These are permament.
6661 	    SQUARE_NAMES		tile;
6662 
6663 	    tile = glbCurLevel->getTile(tx, ty);
6664 	    if (tile == SQUARE_WALL || tile == SQUARE_EMPTY)
6665 	    {
6666 		BUF		corpsename = corpse->getName();
6667 		buf.sprintf("The wall collapses, reforming itself with the soul of %s.",
6668 			    corpsename.buffer());
6669 		reportMessage(buf);
6670 
6671 		pietyCastSpell(spell);
6672 
6673 		glbCurLevel->setTile(tx, ty, SQUARE_CORRIDOR);
6674 
6675 		// Destroy the corpse.
6676 		glbCurLevel->dropItem(corpse);
6677 		delete corpse;
6678 
6679 		// Create the permament golem.
6680 		MOB		*golem;
6681 
6682 		golem = MOB::create(MOB_STONEGOLEM);
6683 
6684 		// Make tame
6685 		golem->makeSlaveOf(this);
6686 
6687 		// Add to the map.
6688 		golem->move(tx, ty, true);
6689 		glbCurLevel->registerMob(golem);
6690 
6691 		return true;
6692 	    }
6693 
6694 	    if ((tile == SQUARE_CORRIDOR || tile == SQUARE_FLOOR) &&
6695 		corpse->isBelowGrade())
6696 	    {
6697 		// Dig a pit for the golem.
6698 		formatAndReport("The ground churns as %IU <I:infuse> it with power.", corpse);
6699 
6700 		pietyCastSpell(spell);
6701 
6702 		// Destroy the corpse.
6703 		glbCurLevel->dropItem(corpse);
6704 		delete corpse;
6705 
6706 		glbCurLevel->digPit(tx, ty, 0);
6707 
6708 		// Create the permament golem.
6709 		MOB		*golem;
6710 
6711 		golem = MOB::create(MOB_STONEGOLEM);
6712 
6713 		// Make tame
6714 		golem->makeSlaveOf(this);
6715 
6716 		// Add to the map.
6717 		golem->move(tx, ty, true);
6718 		glbCurLevel->registerMob(golem);
6719 
6720 		return true;
6721 	    }
6722 
6723 	    formatAndReport("%IU <I:twitch>.", corpse);
6724 	    return true;
6725 	}
6726 
6727 	case SPELL_SOULSUCK:
6728 	{
6729 	    if (target && ((target == this) || canSense(target)))
6730 	    {
6731 		target->formatAndReport("A black cloud surrounds %U.");
6732 		formatAndReport("Oily smoke, inky black, streams to %U.");
6733 
6734 		// Check if target already amnesiac
6735 		if (target->hasIntrinsic(INTRINSIC_AMNESIA))
6736 		{
6737 		    formatAndReport("The smoke has no substance.");
6738 		    return true;
6739 		}
6740 
6741 		// Steal all of the target's spells for the duration
6742 		int		duration;
6743 		duration = rand_range(100, 250);
6744 
6745 		if (target != this)
6746 		{
6747 		    SPELL_NAMES		spell;
6748 		    SKILL_NAMES		skill;
6749 
6750 		    FOREACH_SPELL(spell)
6751 		    {
6752 			if (target->hasSpell(spell, false))
6753 			{
6754 			    learnSpell(spell, duration);
6755 			}
6756 		    }
6757 		    FOREACH_SKILL(skill)
6758 		    {
6759 			if (target->hasSkill(skill, false))
6760 			{
6761 			    learnSkill(skill, duration);
6762 			}
6763 		    }
6764 		}
6765 
6766 		// Apply amnesia
6767 		target->setTimedIntrinsic(this, INTRINSIC_AMNESIA, duration);
6768 		target->setTimedIntrinsic(this, INTRINSIC_CONFUSED, 5);
6769 
6770 		pietyCastSpell(spell);
6771 	    }
6772 	    else
6773 	    {
6774 		formatAndReport("%U <fail> in %r attempt to extract a soul from thin air.");
6775 		cancelSpell(spell);
6776 	    }
6777 	    return true;
6778 	}
6779 
6780 	case SPELL_WIZARDSEYE:
6781 	{
6782 	    // Determine if an imp could be there...
6783 	    tx = getX();
6784 	    ty = getY();
6785 
6786 	    if (!glbCurLevel->findCloseTile(tx, ty, MOVE_STD_FLY))
6787 	    {
6788 		glbCurLevel->reportMessage(
6789 		    "A floating eye briefly appears before thinking better of it.",
6790 		    tx, ty);
6791 		return true;
6792 	    }
6793 
6794 	    MOB		*eye;
6795 
6796 	    eye = MOB::create(MOB_WIZARDSEYE);
6797 	    // Summoned creatures never have inventory.
6798 	    eye->destroyInventory();
6799 
6800 	    eye->move(tx, ty, true);
6801 
6802 	    glbCurLevel->registerMob(eye);
6803 
6804 	    eye->formatAndReport("%U <appear> in a puff of smoke!");
6805 
6806 	    // Possess the eye for a few turns
6807 	    eye->setTimedIntrinsic(this, INTRINSIC_SUMMONED, 30);
6808 	    // Also make it tame.
6809 	    eye->makeSlaveOf(this);
6810 	    actionPossess(eye, 29);
6811 
6812 	    eye->pietyCastSpell(spell);
6813 
6814 	    return true;
6815 	}
6816 	case SPELL_SUMMON_IMP:
6817 	{
6818 	    // Determine if an imp could be there...
6819 	    tx = getX();
6820 	    ty = getY();
6821 
6822 	    if (!glbCurLevel->findCloseTile(tx, ty, MOVE_WALK))
6823 	    {
6824 		glbCurLevel->reportMessage(
6825 		    "An imp briefly appears before thinking better of it.",
6826 		    tx, ty);
6827 		return true;
6828 	    }
6829 
6830 	    MOB		*imp;
6831 
6832 	    imp = MOB::create(MOB_IMP);
6833 	    // Summoned creatures never have inventory.
6834 	    imp->destroyInventory();
6835 
6836 	    imp->move(tx, ty, true);
6837 
6838 	    glbCurLevel->registerMob(imp);
6839 
6840 	    imp->formatAndReport("%U <appear> in a puff of smoke!");
6841 
6842 	    imp->makeSlaveOf(this);
6843 	    imp->setTimedIntrinsic(this, INTRINSIC_SUMMONED, 30);
6844 
6845 	    pietyCastSpell(spell);
6846 
6847 	    return true;
6848 	}
6849 
6850 	case SPELL_SUMMON_DEMON:
6851 	{
6852 	    // Determine if an demon could be there...
6853 	    tx = getX();
6854 	    ty = getY();
6855 
6856 	    if (!glbCurLevel->findCloseTile(tx, ty, MOVE_STD_FLY))
6857 	    {
6858 		glbCurLevel->reportMessage(
6859 		    "A daemon briefly appears before thinking better of it.",
6860 		    tx, ty);
6861 		return true;
6862 	    }
6863 
6864 	    MOB		*imp;
6865 
6866 	    imp = MOB::create(MOB_DAEMON);
6867 	    // Summoned creatures never have inventory.
6868 	    imp->destroyInventory();
6869 
6870 	    imp->move(tx, ty, true);
6871 
6872 	    glbCurLevel->registerMob(imp);
6873 
6874 	    imp->formatAndReport("%U <appear> in a puff of smoke!");
6875 
6876 	    imp->makeSlaveOf(this);
6877 	    imp->setTimedIntrinsic(this, INTRINSIC_SUMMONED, 100);
6878 
6879 	    pietyCastSpell(spell);
6880 
6881 	    return true;
6882 	}
6883 
6884 	case SPELL_POISONITEM:
6885 	{
6886 	    ITEM		*item;
6887 
6888 	    item = getEquippedItem(ITEMSLOT_RHAND);
6889 
6890 	    if (!item)
6891 	    {
6892 		// No item to poison.
6893 		buf = formatToString("A greenish glow appears around %R %B1.",
6894 			    this, 0, 0, 0,
6895 			    (getSlotName(ITEMSLOT_RHAND) ?
6896 				getSlotName(ITEMSLOT_RHAND) :
6897 				"vicinity"),
6898 			    0);
6899 		reportMessage(buf);
6900 		return true;
6901 	    }
6902 
6903 	    // If the item is an empty bottle, make a poison potion.
6904 	    if (item->getDefinition() == ITEM_BOTTLE)
6905 	    {
6906 		formatAndReport("A thick slime fills %IU.", item);
6907 		pietyCastSpell(spell);
6908 		item->setDefinition(ITEM::lookupMagicItem(MAGICTYPE_POTION, POTION_POISON));
6909 
6910 		// It is safe to assume the user knows what casting
6911 		// poison item on a bottle would fill the bottle with.
6912 		if (MOB::getAvatar() && MOB::getAvatar()->canSense(this))
6913 		    item->markClassKnown();
6914 
6915 		return true;
6916 	    }
6917 
6918 	    // Poison the item.
6919 	    formatAndReport("A thick slime coats %R %Iu.", item);
6920 	    pietyCastSpell(spell);
6921 	    item->makePoisoned(POISON_NORMAL, 5);
6922 	    if (MOB::getAvatar() && MOB::getAvatar()->canSense(this))
6923 		item->markPoisonKnown();
6924 
6925 	    return true;
6926 	}
6927 
6928 	case SPELL_POISONBOLT:
6929 	    formatAndReport("A poison bolt shoots from %r fingertip.");
6930 
6931 	    pietyCastSpell(spell);
6932 
6933 	    ourZapSpell = spell;
6934 
6935 	    if (targetself || dz)
6936 	    {
6937 		if (dz)
6938 		    reportMessage("The ray fizzles.");
6939 		else
6940 		    zapCallbackStatic(getX(), getY(), false, &myself);
6941 	    }
6942 	    else
6943 	    {
6944 		glbCurLevel->fireRay(getX(), getY(),
6945 				 dx, dy,
6946 				 6,
6947 				 MOVE_STD_FLY, false,
6948 				 zapCallbackStatic,
6949 				 &myself);
6950 	    }
6951 	    return true;
6952 
6953 	case SPELL_CLOUDKILL:
6954 	    formatAndReport("%U <spit>.");
6955 
6956 	    pietyCastSpell(spell);
6957 	    ourZapSpell = spell;
6958 
6959 	    if (targetself || dz)
6960 	    {
6961 		zapCallbackStatic(getX(), getY(), true, &myself);
6962 	    }
6963 	    else
6964 	    {
6965 		glbCurLevel->fireRay(getX(), getY(),
6966 				 dx, dy,
6967 				 6,
6968 				 MOVE_STD_FLY, false,
6969 				 zapCallbackStatic,
6970 				 &myself);
6971 	    }
6972 	    return true;
6973 
6974 	case SPELL_FINGEROFDEATH:
6975 	    formatAndReport("A jet black ray flies from %r eyes.");
6976 
6977 	    pietyCastSpell(spell);
6978 
6979 	    ourZapSpell = spell;
6980 
6981 	    if (targetself || dz)
6982 	    {
6983 		if (dz)
6984 		    reportMessage("The ray bounces.");
6985 		zapCallbackStatic(getX(), getY(), false, &myself);
6986 	    }
6987 	    else
6988 	    {
6989 		glbCurLevel->fireRay(getX(), getY(),
6990 				 dx, dy,
6991 				 6,
6992 				 MOVE_STD_FLY, true,
6993 				 zapCallbackStatic,
6994 				 &myself);
6995 	    }
6996 	    return true;
6997 
6998 	case NUM_SPELLS:
6999 	    UT_ASSERT(!"UNHANDLED SPELL!");
7000 	    return false;
7001     }
7002 
7003     return false;
7004 }
7005 
7006 bool
actionDig(int dx,int dy,int dz,int range,bool magical)7007 MOB::actionDig(int dx, int dy, int dz, int range, bool magical)
7008 {
7009     applyConfusion(dx, dy, dz);
7010 
7011     // TODO: Handle mundane digging.
7012     if (!magical)
7013     {
7014 	// Special messages for special equipment...
7015 	ITEM	*item = getSourceOfIntrinsic(INTRINSIC_DIG);
7016 
7017 	if (item)
7018 	{
7019 	    formatAndReport("%U <dig> with %r %Iu.", item);
7020 	}
7021 	else
7022 	{
7023 	    formatAndReport("%U <dig>.");
7024 	}
7025     }
7026 
7027     // Do actual excavation:
7028 
7029     // Dig yourself.
7030     if (!dx && !dy && !dz)
7031     {
7032 	formatAndReport("%r stomach feels empty.");
7033 	starve(500);
7034 	return true;
7035     }
7036 
7037     // Dig upwards.
7038     if (dz > 0)
7039     {
7040 	bool		fancymove = false;
7041 
7042 	// If you are submerged, digging upwards will
7043 	// clear a path.  If you are buried alive,
7044 	// it will turn your square into a pit.
7045 	// If you are under ice, it will break the ice
7046 	// and raise you up.
7047 	if (hasIntrinsic(INTRINSIC_SUBMERGED))
7048 	{
7049 	    SQUARE_NAMES		tile, newtile;
7050 	    bool	setinpit = false;
7051 
7052 	    tile = glbCurLevel->getTile(getX(), getY());
7053 	    newtile = tile;
7054 
7055 	    // We let the creature escape first.  This way you see the report
7056 	    // message even if they were invisible when entombed.
7057 	    clearIntrinsic(INTRINSIC_SUBMERGED);
7058 
7059 	    switch (tile)
7060 	    {
7061 		case SQUARE_WATER:
7062 		    formatAndReport("The water parts above %U.");
7063 		    fancymove = true;
7064 		    break;
7065 		case SQUARE_ACID:
7066 		    formatAndReport("The acid parts above %U.");
7067 		    fancymove = true;
7068 		    break;
7069 		case SQUARE_LAVA:
7070 		    formatAndReport("The lava parts above %U.");
7071 		    fancymove = true;
7072 		    break;
7073 		case SQUARE_ICE:
7074 		    formatAndReport("The ice breaks above %U.");
7075 		    newtile = SQUARE_WATER;
7076 		    fancymove = true;
7077 		    break;
7078 		default:
7079 		    formatAndReport("The rocks shatter above %U.");
7080 		    setinpit = true;
7081 		    fancymove = true;
7082 		    break;
7083 	    }
7084 
7085 	    if (newtile != tile)
7086 	    {
7087 		glbCurLevel->setTile(getX(),
7088 				     getY(),
7089 				     newtile);
7090 		glbCurLevel->dropItems(getX(),
7091 				    getY(),
7092 				    this);
7093 	    }
7094 	    if (setinpit)
7095 	    {
7096 		// Don't identify us as zapper as
7097 		// not normal type.
7098 		glbCurLevel->digPit(getX(), getY(), 0);
7099 		setIntrinsic(INTRINSIC_INPIT);
7100 	    }
7101 	}
7102 
7103 	if (!fancymove)
7104 	{
7105 	    if (glbCurLevel->branchName() == BRANCH_TRIDUDE)
7106 	    {
7107 		formatAndReport("The metal roof is unaffected.");
7108 	    }
7109 	    else if (glbCurLevel->getDepth() == 0)
7110 	    {
7111 		formatAndReport("The clear blue sky remains unimpressed.");
7112 	    }
7113 	    else
7114 	    {
7115 		// Zap at the ceiling, rocks fall down.
7116 		formatAndReport("Rocks fall from the ceiling onto %r head.");
7117 
7118 		ITEM *rocks = ITEM::create(ITEM_ROCK, false);
7119 		glbCurLevel->acquireItem(rocks, getX(), getY(), this);
7120 		// TODO: We would like a nicer message?
7121 		receiveDamage(ATTACK_CEILINGROCKS, this,
7122 					0, 0,
7123 					ATTACKSTYLE_MISC);
7124 	    }
7125 	}
7126 
7127 	return true;
7128     }
7129 
7130     if (dz < 0)
7131     {
7132 	// If we are digging into water or lava, we have a no-op.
7133 	// If we are digging into ice, we break the ice and end up
7134 	// in the drink.  (Unless we are submerged.)
7135 	switch (glbCurLevel->getTile(getX(), getY()))
7136 	{
7137 	    case SQUARE_WATER:
7138 		formatAndReport("The water briefly parts below %U..");
7139 		// Another chance to drop due to the air being revealed.
7140 		glbCurLevel->dropMobs(getX(), getY(), false, this);
7141 		return true;
7142 	    case SQUARE_ACID:
7143 		formatAndReport("The acid briefly parts below %U.");
7144 		// Another chance to drop due to the air being revealed.
7145 		glbCurLevel->dropMobs(getX(), getY(), false, this);
7146 		return true;
7147 	    case SQUARE_LAVA:
7148 		formatAndReport("The lava briefly parts below %U.");
7149 		// Another chance to drop due to the air being revealed.
7150 		glbCurLevel->dropMobs(getX(), getY(), false, this);
7151 		return true;
7152 	    case SQUARE_ICE:
7153 		if (!hasIntrinsic(INTRINSIC_SUBMERGED))
7154 		{
7155 		    formatAndReport("The ice breaks below %U.");
7156 		    glbCurLevel->setTile(getX(),
7157 					 getY(),
7158 					 SQUARE_WATER);
7159 		    glbCurLevel->dropMobs(getX(), getY(), false, this);
7160 		}
7161 		else
7162 		{
7163 		    formatAndReport("The water briefly parts below %U..");
7164 		}
7165 		return true;
7166 	    default:
7167 		break;
7168 	}
7169 
7170 	// Zap at the floor.  We make a pit, and fall down it.
7171 	// The map handles falling through it.
7172 	// The exception is if we are digging a pit, ie: short range.
7173 	// The exception with that is if you are already in a pit.
7174 	if (range > 1 || hasIntrinsic(INTRINSIC_INPIT))
7175 	    glbCurLevel->digHole(getX(), getY(), this);
7176 	else
7177 	    glbCurLevel->digPit(getX(), getY(), this);
7178 
7179 	return true;
7180     }
7181 
7182     // Now, the theoritically easy part...
7183     ourZapSpell = SPELL_DIG;
7184 
7185     MOBREF		myself;
7186     myself.setMob(this);
7187 
7188     glbCurLevel->fireRay(getX(), getY(),
7189 		    dx, dy,
7190 		    range,
7191 		    MOVE_ALL, false,
7192 		    zapCallbackStatic,
7193 		    &myself);
7194 
7195     return true;
7196 }
7197 
7198 bool
actionTeleport(bool allowcontrol,bool givecontrol,int xloc,int yloc)7199 MOB::actionTeleport(bool allowcontrol, bool givecontrol, int xloc, int yloc)
7200 {
7201     int			tx, ty;
7202     BUF		buf;
7203 
7204     if (hasIntrinsic(INTRINSIC_TELEFIXED))
7205     {
7206 	formatAndReport("%U <shake>.");
7207 	return true;
7208     }
7209 
7210     // Check for failure due to being on a level that prohibits
7211     // teleportation
7212     if (!glbCurLevel->allowTeleportation())
7213     {
7214 	// We allow people to blink as it isn't quite so bad a form
7215 	// of cheating...  This may change :>
7216 	if (xloc >= 0 && yloc >= 0)
7217 	{
7218 	    // Blink, allow.
7219 	}
7220 	else
7221 	{
7222 	    formatAndReport("The dungeon briefly distorts around %U.");
7223 	    return true;
7224 	}
7225     }
7226 
7227     if (allowcontrol &&
7228         (givecontrol || hasIntrinsic(INTRINSIC_TELEPORTCONTROL)))
7229     {
7230 	if (!glbStressTest && isAvatar())
7231 	{
7232 	    buf.sprintf("Where do you want to teleport?");
7233 	    reportMessage(buf);
7234 	    tx = getX();
7235 	    ty = getY();
7236 	    if (gfx_selecttile(tx, ty))
7237 	    {
7238 		// Check to see if that is a legal location.
7239 		if (canMove(tx, ty, true) && !glbCurLevel->getMob(tx, ty)
7240 		    && !glbCurLevel->getFlag(tx, ty, SQUAREFLAG_NOMOB))
7241 		{
7242 		    // A legal move!
7243 		    formatAndReport("%U <teleport>.");
7244 		}
7245 		else
7246 		{
7247 		    // If you decide to stay put, don't report that you
7248 		    // are blocked (since you will, of course, fail to
7249 		    // move into your own square!)
7250 		    if (tx == getX() && ty == getY())
7251 		    {
7252 			formatAndReport("%U <decide> to stay put.");
7253 		    }
7254 		    else
7255 		    {
7256 			formatAndReport("%U <be> blocked!");
7257 			tx = getX();
7258 			ty = getY();
7259 		    }
7260 		}
7261 	    }
7262 	    else
7263 	    {
7264 		tx = getX();
7265 		ty = getY();
7266 		formatAndReport("%U <decide> to stay put.");
7267 	    }
7268 	}
7269 	else
7270 	{
7271 	    // Use AI.  Try to teleport to the stairs.
7272 	    if (glbCurLevel->findTile(SQUARE_LADDERUP, tx, ty) &&
7273 		glbCurLevel->findCloseTile(tx, ty, getMoveType()))
7274 	    {
7275 		formatAndReport("%U <teleport> away.");
7276 	    }
7277 	    else
7278 	    {
7279 		// Go somewhere random instead of the stairs as they
7280 		// are blocked.
7281 		if (glbCurLevel->findRandomLoc(tx, ty, getMoveType(),
7282 			    false, getSize() >= SIZE_GARGANTUAN,
7283 			    false, true, true, true))
7284 		{
7285 		    formatAndReport("%U <teleport> away.");
7286 		}
7287 		else
7288 		{
7289 		    // Teleport to where we are.
7290 		    formatAndReport("%U <blink> out of sight briefly.");
7291 		    tx = getX();
7292 		    ty = getY();
7293 		}
7294 	    }
7295 	}
7296     }
7297     else
7298     {
7299 	// Determine a random destination...
7300 	if (xloc >= 0 && yloc >= 0)
7301 	{
7302 	    formatAndReport("%U <blink>.");
7303 	    tx = xloc;
7304 	    ty = yloc;
7305 	}
7306 	else if (glbCurLevel->findRandomLoc(tx, ty, getMoveType(),
7307 		    false, getSize() >= SIZE_GARGANTUAN,
7308 		    false, false, false, true))
7309 	{
7310 	    formatAndReport("%U <teleport>.");
7311 	}
7312 	else
7313 	{
7314 	    // Failed to teleport, you shudder...
7315 	    tx = getX();
7316 	    ty = getY();
7317 	    formatAndReport("%U <shudder> violently.");
7318 	}
7319     }
7320 
7321     bool		didmove = false;
7322 
7323     if (tx != getX() || ty != getY())
7324 	didmove = true;
7325 
7326     // Move to the given destination.
7327     if (!move(tx, ty, !didmove))
7328 	return true;
7329 
7330     // Report what we stepped on...
7331     if (didmove && isAvatar())
7332     {
7333 	// Need to rebuild our screen!  Refreshing will be handled
7334 	// by the message wait code.
7335 	gfx_scrollcenter(getX(), getY());
7336 	glbCurLevel->buildFOV(getX(), getY(), 7, 5);
7337 
7338 	glbCurLevel->describeSquare(getX(), getY(),
7339 				    hasIntrinsic(INTRINSIC_BLIND),
7340 				    false);
7341     }
7342 
7343     return true;
7344 }
7345 
7346 bool
actionUnPolymorph(bool silent,bool systemshock)7347 MOB::actionUnPolymorph(bool silent, bool systemshock)
7348 {
7349     MOB		*origmob;
7350 
7351     if (hasIntrinsic(INTRINSIC_UNCHANGING))
7352     {
7353 	// Explicitly prevent polymorphing...
7354 	if (!silent)
7355 	    formatAndReport("%U <shudder>.");
7356 	return true;
7357     }
7358 
7359     origmob = myBaseType.getMob();
7360 
7361     if (!origmob)
7362     {
7363 	// Nothing to do!
7364 	return true;
7365     }
7366 
7367     // Be paranoid and trigger a mobref check.
7368 #ifdef STRESS_TEST
7369     if (glbCurLevel)
7370     {
7371 	if (!glbCurLevel->verifyMob())
7372 	{
7373 	    msg_report("MOBREF corruption: pre-unpoly.  ");
7374 	}
7375     }
7376 #endif
7377 
7378     if (!silent)
7379 	formatAndReport("%U <return> to %r original shape!");
7380 
7381     bool	shouldwallcrush = false;
7382 
7383     if (origmob->getSize() >= SIZE_GARGANTUAN &&
7384 	getSize() < SIZE_GARGANTUAN)
7385     {
7386 	shouldwallcrush = true;
7387     }
7388 
7389     // Transfer the name of the mob to the base level.  This accounts
7390     // for cases where the player has named the mob.
7391     origmob->myName = myName;
7392 
7393     // Transfer all the items.
7394     // We could just steal the inventory pointer, but that would be
7395     // evil if items ever get back references.  There is also no
7396     // guarantee that the intrinsics/rebuild will work correctly.
7397     ITEM		*item, *next, *drop;
7398     bool		 anydrop = false;
7399     for (item = myInventory; item; item = next)
7400     {
7401 	next = item->getNext();
7402 
7403 	drop = dropItem(item->getX(), item->getY());
7404 	UT_ASSERT(drop == item);
7405 
7406 	if (!origmob->acquireItem(item, item->getX(), item->getY()))
7407 	{
7408 	    // We have to report the dropping before doing it
7409 	    // as dropping may merge stacks and delete item.
7410 	    // Note that we don't want to use newitem as that
7411 	    // may report an unusual new stack count.
7412 	    formatAndReport("%IU <I:drop> on the ground!", item);
7413 	    // If we fail to acquire, we have to drop on the ground.
7414 	    glbCurLevel->acquireItem(item, getX(), getY(), this);
7415 	    anydrop = true;
7416 	}
7417     }
7418     // Make sure stuff ends up in the water, etc.
7419     if (anydrop)
7420 	glbCurLevel->dropItems(getX(), getY(), 0);
7421 
7422     UT_ASSERT(!myInventory);
7423 
7424     // Transfer the raw mob pointers...
7425     MOBREF		tmpref;
7426     INTRINSIC_NAMES	intrinsic;
7427 
7428     tmpref = myOwnRef;
7429     myOwnRef = origmob->myOwnRef;
7430     origmob->myOwnRef = tmpref;
7431 
7432     // Fix up the underlying pointers to match the global level.
7433     if (!myOwnRef.transferMOB(this))
7434     {
7435 	UT_ASSERT(!"UnPoly Fail Transfer To");
7436     }
7437     if (!origmob->myOwnRef.transferMOB(origmob))
7438     {
7439 	UT_ASSERT(!"UnPoly Fail Transfer From");
7440     }
7441 
7442     origmob->move(getX(), getY(), true);
7443 
7444     // Clear out our base type.  (It points to ourself now,
7445     // so would be rather disasterous to delete it as is!)
7446     myBaseType.setMob(0);
7447 
7448     glbCurLevel->unregisterMob(this);
7449     glbCurLevel->registerMob(origmob);
7450 
7451     // We don't need to transfer back experience as it has been
7452     // accumulating for us.
7453 
7454     // Transfer food level.
7455     origmob->myFoodLevel = myFoodLevel;
7456 
7457     // Transfer all the intrinsics that should survive.
7458     for (intrinsic = INTRINSIC_NONE; intrinsic < NUM_INTRINSICS;
7459 	 intrinsic = (INTRINSIC_NAMES) (intrinsic + 1))
7460     {
7461 	if (glb_intrinsicdefs[intrinsic].surviveunpoly &&
7462 	    hasIntrinsic(intrinsic))
7463 	{
7464 	    INTRINSIC_COUNTER		*counter;
7465 
7466 	    // We may already have the intrinsic, so don't want
7467 	    // to double count things.
7468 	    origmob->clearIntrinsic(intrinsic);
7469 
7470 	    counter = getCounter(intrinsic);
7471 	    if (counter)
7472 	    {
7473 		origmob->setTimedIntrinsic(counter->myInflictor.getMob(),
7474 					    intrinsic,
7475 					    counter->myTurns);
7476 	    }
7477 	    else
7478 		origmob->setIntrinsic(intrinsic);
7479 	}
7480 
7481 	// If the intrinsic shouldn't survive, remove ourselves.
7482 	if (glb_intrinsicdefs[intrinsic].clearonpoly)
7483 	    clearIntrinsic(intrinsic);
7484     }
7485 
7486     // Transfer the ai target.
7487     origmob->myAITarget.setMob(getAITarget());
7488 
7489     delete this;
7490 
7491     origmob->rebuildAppearance();
7492     origmob->rebuildWornIntrinsic();
7493 
7494     // Put the system shock to the end so the message makes sense.
7495     // Apply system shock: The base types max hit points are damaged
7496     // as a result of the pain.
7497     if (systemshock)
7498     {
7499 	origmob->systemshock();
7500     }
7501 
7502     // Crush surrounding walls if we return to gargantuan size
7503     if (shouldwallcrush)
7504     {
7505 	glbCurLevel->wallCrush(origmob);
7506     }
7507 
7508 #ifdef STRESS_TEST
7509     // Be paranoid and trigger a mobref check.
7510     if (glbCurLevel)
7511     {
7512 	if (!glbCurLevel->verifyMob())
7513 	{
7514 	    msg_report("MOBREF corruption: unpoly.  ");
7515 	}
7516     }
7517 #endif
7518 
7519     // All done!  No one should be the wiser!
7520     return true;
7521 }
7522 
7523 bool
actionPolymorph(bool allowcontrol,bool forcecontrol,MOB_NAMES newdef)7524 MOB::actionPolymorph(bool allowcontrol, bool forcecontrol, MOB_NAMES newdef)
7525 {
7526     MOB		*newmob;
7527     BUF		buf;
7528 
7529     if (hasIntrinsic(INTRINSIC_UNCHANGING))
7530     {
7531 	// Explicitly prevent polymorphing...
7532 	formatAndReport("%U <shudder>.");
7533 	return true;
7534     }
7535 
7536 #ifdef STRESS_TEST
7537     // Be paranoid and trigger a mobref check.
7538     if (glbCurLevel)
7539     {
7540 	if (!glbCurLevel->verifyMob())
7541 	{
7542 	    msg_report("MOBREF corruption: pre-poly.  ");
7543 	}
7544     }
7545 #endif
7546 
7547     // If we are already polyed, we first return to our original state
7548     // before going on to polymorph...
7549     if (myBaseType.getMob())
7550     {
7551 	newmob = myBaseType.getMob();
7552 
7553 	actionUnPolymorph(true);
7554 
7555 	return newmob->actionPolymorph(allowcontrol, forcecontrol, newdef);
7556     }
7557 
7558     // newdef = MOB_CRETAN_MINOTAUR;
7559 
7560     if (allowcontrol && (forcecontrol || hasIntrinsic(INTRINSIC_POLYCONTROL)))
7561     {
7562 	formatAndReport("%U <take> control of the transformation!");
7563 	if (!glbStressTest && isAvatar())
7564 	{
7565 	    // Make a list of all killed critters and prompt.
7566 	    MOB_NAMES		 mobtypes[NUM_MOBS];
7567 	    const char 		*moblist[NUM_MOBS+2]; // room for null, nochange
7568 	    int			 i, numtype, aorb, selection;
7569 
7570 	    victory_buildSortedKillList(mobtypes, &numtype);
7571 
7572 	    // Option to remain unchanged...
7573 	    moblist[0] = "yourself";
7574 
7575 	    // Create names
7576 	    for (i = 0; i < numtype; i++)
7577 	    {
7578 		moblist[i+1] = glb_mobdefs[mobtypes[i]].name;
7579 	    }
7580 	    // Null terminate
7581 	    moblist[numtype+1] = 0;
7582 
7583 	    while (1)
7584 	    {
7585 		int		y;
7586 
7587 		gfx_printtext(0, 3, "Transform into what?");
7588 		selection = gfx_selectmenu(5, 4, (const char **) moblist, aorb);
7589 		for (y = 3; y < 19; y++)
7590 		    gfx_cleartextline(y);
7591 
7592 		// You are not allowed to cancel.
7593 		if (!aorb)
7594 		    break;
7595 	    }
7596 	    if (!selection)
7597 	    {
7598 		// Person chose to resist.
7599 		// No shudder message as this might involve turning
7600 		// *back* to your original form.
7601 		return true;
7602 	    }
7603 
7604 	    selection--;
7605 	    newdef = mobtypes[selection];
7606 	}
7607 	else
7608 	{
7609 	    // Find the end of our advancment and turn into it.
7610 	    newdef = getDefinition();
7611 	    while (glb_mobdefs[newdef].evolvetarget != MOB_NONE)
7612 	    {
7613 		newdef = (MOB_NAMES) glb_mobdefs[newdef].evolvetarget;
7614 	    }
7615 
7616 	    // If I have no evolve target, go for random poly unless
7617 	    // we are in good health.
7618 	    if (newdef == getDefinition())
7619 	    {
7620 		if (getHP() >= (getMaxHP() >> 3) + 1)
7621 		{
7622 		    // We are happy with what we are.
7623 		    return true;
7624 		}
7625 		else
7626 		{
7627 		    // We may be seeking escape.
7628 		    newdef = MOB_NONE;
7629 		}
7630 	    }
7631 	}
7632     }
7633 
7634     // If newdef is not MOB_NONE, that is the forced value.
7635     // We manually add an upper bound as one may have locked
7636     // the NPC to only be of a certain type, that will then infinite
7637     // loop
7638     int maxtries = 100;
7639     while (newdef == MOB_NONE)
7640     {
7641 	newdef = MOB::chooseNPC(100);
7642 
7643 	// Reject self-polymorph as it is embarrassing.
7644 	if (newdef == getDefinition() && (maxtries --> 0))
7645 	    newdef = MOB_NONE;
7646     }
7647 
7648     newmob = MOB::create(newdef);
7649 
7650     // This bit of logic was cut and paste on the Narita Express whilst
7651     // somewhat drunk on beer and sake.  Not enough beer and sake, obviously,
7652     // since I'm still able to cut and paste.  Not much to see outside at
7653     // 8:15, however, it is already very dark.  Clearly I'm preparing
7654     // for my more northerly summers and expectations of the sun setting
7655     // some time after dusk.
7656     bool	shouldwallcrush = false;
7657 
7658     if (newmob->getSize() >= SIZE_GARGANTUAN &&
7659 	getSize() < SIZE_GARGANTUAN)
7660     {
7661 	shouldwallcrush = true;
7662     }
7663 
7664     // The new mob doesn't get to keep any cool possessions!
7665     newmob->destroyInventory();
7666 
7667     // Transfer all the items.
7668     // We could just steal the inventory pointer, but that would be
7669     // evil if items ever get back references.  There is also no
7670     // guarantee that the intrinsics/rebuild will work correctly.
7671     ITEM		*item, *next, *drop;
7672     bool		 anydrop = false;
7673 
7674     for (item = myInventory; item; item = next)
7675     {
7676 	next = item->getNext();
7677 
7678 	drop = dropItem(item->getX(), item->getY());
7679 	UT_ASSERT(drop == item);
7680 
7681 	if (!newmob->acquireItem(item, item->getX(), item->getY()))
7682 	{
7683 	    // We have to report the dropping before doing it
7684 	    // as dropping may merge stacks and delete item.
7685 	    // Note that we don't want to use newitem as that
7686 	    // may report an unusual new stack count.
7687 	    // If we fail to acquire, we have to drop on the ground.
7688 	    formatAndReport("%IU <I:drop> on the ground!", item);
7689 	    glbCurLevel->acquireItem(item, getX(), getY(), this);
7690 	    anydrop = true;
7691 	}
7692     }
7693     // Make sure stuff ends up in the water, etc.
7694     if (anydrop)
7695 	glbCurLevel->dropItems(getX(), getY(), 0);
7696 
7697     UT_ASSERT(!myInventory);
7698 
7699     // Move the getX()/getY() so we don't get "transforms into it".
7700     newmob->move(getX(), getY(), true);
7701     // Set the mob's dlevel so we don't get transforms into it.
7702     newmob->setDLevel(getDLevel());
7703     // Set the mob's name to match our own name.
7704     newmob->myName = myName;
7705 
7706     // Spam the effect!
7707     // If we are the avatar, we force the new form to be visible.  Otherwise,
7708     // polying whilst invisible and no see invisible would be
7709     // "you turn into it"
7710     // Except.... That is a feature.  After all, you have no
7711     // idea of knowing what your new form is?
7712     // Bah, disable for now until we properly use "something".
7713     // We now use soemething, so it is a feature again.
7714     formatAndReport("%U <transform> into %B1!",
7715 		    newmob->getName(true, false, false /*isAvatar()*/));
7716 
7717     // We want all mob references to go to newmob rather than this:
7718     MOBREF		tmpref;
7719     INTRINSIC_NAMES	intrinsic;
7720 
7721     tmpref = myOwnRef;
7722     myOwnRef = newmob->myOwnRef;
7723     newmob->myOwnRef = tmpref;
7724 
7725     // Fix up the underlying pointers to match the global level.
7726     if (!myOwnRef.transferMOB(this))
7727     {
7728 	UT_ASSERT(!"Poly transfer to");
7729     }
7730     if (!newmob->myOwnRef.transferMOB(newmob))
7731     {
7732 	UT_ASSERT(!"Poly transfer from");
7733     }
7734 
7735     // Important to do this AFTER the transfer!
7736     newmob->myBaseType.setMob(this);
7737 
7738     glbCurLevel->unregisterMob(this);
7739     glbCurLevel->registerMob(newmob);
7740 
7741     // Transfer any experience to the new mob.
7742     newmob->myExp = myExp;
7743 
7744     // Transfer food level.
7745     newmob->myFoodLevel = myFoodLevel;
7746 
7747     // Transfer all the intrinsics that should survive.
7748     for (intrinsic = INTRINSIC_NONE; intrinsic < NUM_INTRINSICS;
7749 	 intrinsic = (INTRINSIC_NAMES) (intrinsic + 1))
7750     {
7751 	if (glb_intrinsicdefs[intrinsic].survivepoly &&
7752 	    hasIntrinsic(intrinsic))
7753 	{
7754 	    INTRINSIC_COUNTER		*counter;
7755 
7756 	    counter = getCounter(intrinsic);
7757 	    if (counter)
7758 	    {
7759 		newmob->setTimedIntrinsic(counter->myInflictor.getMob(),
7760 					    intrinsic,
7761 					    counter->myTurns);
7762 	    }
7763 	    else
7764 		newmob->setIntrinsic(intrinsic);
7765 	}
7766 
7767 	// If the intrinsic shouldn't survive, remove ourselves.
7768 	if (glb_intrinsicdefs[intrinsic].clearonpoly)
7769 	    clearIntrinsic(intrinsic);
7770     }
7771 
7772     // Transfer the ai target.
7773     newmob->myAITarget.setMob(getAITarget());
7774 
7775     newmob->rebuildAppearance();
7776     newmob->rebuildWornIntrinsic();
7777 
7778     // Crush surrounding walls if we return to gargantuan size
7779     if (shouldwallcrush)
7780     {
7781 	glbCurLevel->wallCrush(newmob);
7782     }
7783 
7784     // All done!  No one should be the wiser!
7785 
7786 #ifdef STRESS_TEST
7787     // Be paranoid and trigger a mobref check.
7788     if (glbCurLevel)
7789     {
7790 	if (!glbCurLevel->verifyMob())
7791 	{
7792 	    msg_report("MOBREF corruption: poly.  ");
7793 	}
7794     }
7795 #endif
7796 
7797     return true;
7798 }
7799 
7800 bool
actionPetrify()7801 MOB::actionPetrify()
7802 {
7803     // Ensure we are made of flesh.  Otherwise, not much to do.
7804     if (getMaterial() != MATERIAL_FLESH || hasIntrinsic(INTRINSIC_UNCHANGING))
7805     {
7806 	formatAndReport("%U <feel> momentarily stony.");
7807 	return false;
7808     }
7809 
7810     // Special case: Flesh golems become stone golems.
7811     if (getDefinition() == MOB_FLESHGOLEM)
7812     {
7813 	formatAndReport("%U <transform> into a stone golem!");
7814 	myDefinition = MOB_STONEGOLEM;
7815 	return true;
7816     }
7817 
7818     // Special case: Trolls become cave trolls.
7819     if (getDefinition() == MOB_TROLL)
7820     {
7821 	formatAndReport("%U <transform> into a cave troll!");
7822 	myDefinition = MOB_CAVETROLL;
7823 	return true;
7824     }
7825 
7826     // Determine if the user can save their life.
7827     if (attemptLifeSaving("%U <harden> into a statue!"))
7828     {
7829 	return true;
7830     }
7831 
7832 #ifdef STRESS_TEST
7833     if (glbCurLevel)
7834     {
7835 	if (!glbCurLevel->verifyMob())
7836 	{
7837 	    msg_report("MOBREF corruption: pre-petrify.  ");
7838 	}
7839     }
7840 #endif
7841 
7842     formatAndReport("%U <harden> into a statue!");
7843 
7844     // Release any possession
7845     if (hasIntrinsic(INTRINSIC_POSSESSED))
7846 	actionReleasePossession(true);
7847 
7848     // Create the new statue.
7849     ITEM		*statue;
7850 
7851     glbCurLevel->unregisterMob(this);
7852     statue = ITEM::createStatue(this);
7853     glbCurLevel->acquireItem(statue, getX(), getY(), this);
7854 
7855     if (isAvatar())
7856     {
7857 	// Note we want to auto-map our own statue so the user
7858 	// gets to see it.
7859 	statue->markMapped();
7860     }
7861     triggerAsDeath(ATTACK_TURNED_TO_STONE, 0, 0, true);
7862 
7863     // Clear their death intrinsics so if they are ressed, they
7864     // don't keep burning...
7865     clearDeathIntrinsics(true);
7866 
7867     // One may be better categorized as in stasis, but this does
7868     // mean that people should stop following you.
7869     setIntrinsic(INTRINSIC_DEAD);
7870 
7871 #ifdef STRESS_TEST
7872     // Be paranoid and trigger a mobref check.
7873     if (glbCurLevel)
7874     {
7875 	if (!glbCurLevel->verifyMob())
7876 	{
7877 	    msg_report("MOBREF corruption: petrify.  ");
7878 	}
7879     }
7880 #endif
7881 
7882     return true;
7883 }
7884 
7885 bool
actionUnPetrify()7886 MOB::actionUnPetrify()
7887 {
7888     // Clear out the stoning intrinsic
7889     clearIntrinsic(INTRINSIC_STONING);
7890 
7891     // Ensure we are made of stone.  Otherwise, not much to do.
7892     if (getMaterial() != MATERIAL_STONE || hasIntrinsic(INTRINSIC_UNCHANGING))
7893     {
7894 	formatAndReport("%U <feel> momentarily fleshy.");
7895 	return false;
7896     }
7897 
7898     // Special case: Flesh golems become stone golems.
7899     if (getDefinition() == MOB_STONEGOLEM)
7900     {
7901 	formatAndReport("%U <transform> into a flesh golem!");
7902 	myDefinition = MOB_FLESHGOLEM;
7903 	return true;
7904     }
7905 
7906     // Special case: Cave trolls become trolls.
7907     if (getDefinition() == MOB_CAVETROLL)
7908     {
7909 	formatAndReport("%U <soften> into a troll!");
7910 	myDefinition = MOB_TROLL;
7911 	return true;
7912     }
7913 
7914     // Check if we can save our life...
7915     if (attemptLifeSaving("%U <sag> into a mound of featureless flesh!"))
7916     {
7917 	return true;
7918     }
7919 
7920     // Ew!  Icky!
7921     formatAndReport("%U <collapse> into a mound of flesh!");
7922 
7923     // Create the new flesh mound.
7924     ITEM		*flesh;
7925 
7926     glbCurLevel->unregisterMob(this);
7927 
7928     // Drop our inventory on the ground.
7929     {
7930 	ITEM *cur;
7931 
7932 	// Drop our inventory on this square...
7933 	while ((cur = myInventory))
7934 	{
7935 	    // We manually drop the item for efficiency.
7936 	    // (Creature is dead, so no need for efficiency)
7937 	    myInventory = cur->getNext();
7938 	    cur->setNext(0);
7939 	    glbCurLevel->acquireItem(cur, getX(), getY(), this);
7940 	}
7941     }
7942 
7943     flesh = ITEM::create(ITEM_MOUNDFLESH, false, true);
7944     glbCurLevel->acquireItem(flesh, getX(), getY(), this);
7945 
7946     if (isAvatar())
7947     {
7948 	// Note we want to auto-map our own mound of flesh so the user
7949 	// gets to see it.
7950 	flesh->markMapped();
7951     }
7952     // WHile you do become an item, there is no restoration possibility...
7953     triggerAsDeath(ATTACK_TURNED_TO_FLESH, 0, 0, false);
7954 
7955     // Self is most definitely dead.
7956     delete this;
7957 
7958     return true;
7959 }
7960 
7961 bool
ableToEquip(int ix,int iy,ITEMSLOT_NAMES slot,bool quiet)7962 MOB::ableToEquip(int ix, int iy, ITEMSLOT_NAMES slot, bool quiet)
7963 {
7964     // First, ensure there is an item...
7965     ITEM		*item, *olditem, *otheritem;
7966     BUF		buf;
7967     bool		 fit;
7968     bool		 dualdequip = false;
7969 
7970     // Determine if we have the given slot...
7971     if (!hasSlot(slot))
7972     {
7973 	if (!quiet)
7974 	{
7975 	    formatAndReport("%U <have> no %B1!",
7976 			glb_itemslotdefs[slot].bodypart);
7977 	}
7978 	return false;
7979     }
7980 
7981     item = getItem(ix, iy);
7982     if (!item)
7983     {
7984 	if (!quiet)
7985 	    formatAndReport("%U <try> to equip nothing!");
7986 	return false;
7987     }
7988 
7989     // Check to see if it can fit.  Note anything will fit in the right
7990     // hand, as anything can be used as a weapon.
7991     fit = false;
7992     switch (slot)
7993     {
7994 	case ITEMSLOT_HEAD:
7995 	    fit = item->isHelmet();
7996 	    break;
7997 	case ITEMSLOT_RHAND:
7998 	    fit = true;
7999 
8000 	    // We need two hands to equip large items.
8001 	    if (item->getSize() > getSize())
8002 	    {
8003 		// Check to see if the right hand is free.  Specifically,
8004 		// if the left hand has an item, we can't fit a large weapon.
8005 		otheritem = getEquippedItem(ITEMSLOT_LHAND);
8006 		if ((otheritem && otheritem->getDefinition() != ITEM_BUCKLER)
8007 			|| !hasSlot(ITEMSLOT_LHAND))
8008 		{
8009 		    // We demand a double dequip
8010 		    dualdequip = true;
8011 		}
8012 	    }
8013 	    break;
8014 	case ITEMSLOT_LHAND:
8015 	    // Anything fits in the right hand.
8016 	    // However, we still prohibit stacking (as that is only
8017 	    // allowed for doing enchantments)
8018 	    // We only idac if it is a shield.
8019 	    fit = true;
8020 	    // Check to see if the left hand is free.  Specifically,
8021 	    // if the right hand has a large weapon in it, we can't
8022 	    // equip in the left hand.
8023 	    // Note: If you have a left hand, you always have a right hand.
8024 	    // Note: Bucklers are special because you strap them to
8025 	    // your forearm.
8026 	    otheritem = getEquippedItem(ITEMSLOT_RHAND);
8027 	    if (otheritem && (otheritem->getSize() > getSize()) &&
8028 		(item->getDefinition() != ITEM_BUCKLER))
8029 	    {
8030 		if (!quiet)
8031 		{
8032 		    formatAndReport("%U <need> two hands to wield %IU!", otheritem);
8033 		}
8034 		return false;
8035 	    }
8036 	    break;
8037 	case ITEMSLOT_BODY:
8038 	    fit = item->isJacket();
8039 	    break;
8040 	case ITEMSLOT_FEET:
8041 	    fit = item->isBoots();
8042 	    break;
8043 	case ITEMSLOT_AMULET:
8044 	    fit = item->isAmulet();
8045 	    break;
8046 	case ITEMSLOT_RRING:
8047 	case ITEMSLOT_LRING:
8048 	{
8049 	    // If we have the missing finger intrinsic, this will
8050 	    // only work if we are replacing the ring or have no
8051 	    // rings, ie, the other slot is free.
8052 	    if (hasIntrinsic(INTRINSIC_MISSINGFINGER))
8053 	    {
8054 		if (getEquippedItem(
8055 			(slot==ITEMSLOT_RRING)
8056 			    ? ITEMSLOT_LRING
8057 			    : ITEMSLOT_RRING))
8058 		{
8059 		    if (!quiet)
8060 		    {
8061 			formatAndReport("%U <be> surprised to find no %B1!",
8062 				getSlotName(slot));
8063 		    }
8064 		    // Yes, you get to find out for free...
8065 		    return false;
8066 		}
8067 	    }
8068 	    fit = item->isRing();
8069 	    break;
8070 	}
8071 	default:
8072 	{
8073 	    UT_ASSERT(!"Unhandled ITEMSLOT");
8074 	    fit = false;
8075 	    break;
8076 	}
8077     }
8078 
8079     if (!fit)
8080     {
8081 	if (!quiet)
8082 	{
8083 	    buf = formatToString("%U <fail> to put %IU %B1 %r %B2.",
8084 		    this, 0, 0, item,
8085 		    glb_itemslotdefs[slot].preposition,
8086 		    getSlotName(slot));
8087 	    reportMessage(buf);
8088 	}
8089 	return false;
8090     }
8091 
8092     // Verify we are able to dequip the old slot.
8093     olditem = getEquippedItem(slot);
8094     if (olditem)
8095     {
8096 	if (!ableToDequip(slot, quiet))
8097 	    return false;
8098     }
8099     if (dualdequip)
8100     {
8101 	if (!ableToDequip(ITEMSLOT_LHAND, quiet))
8102 	    return false;
8103     }
8104 
8105     return true;
8106 }
8107 
8108 bool
actionEquip(int ix,int iy,ITEMSLOT_NAMES slot,bool quiet)8109 MOB::actionEquip(int ix, int iy, ITEMSLOT_NAMES slot, bool quiet)
8110 {
8111     // First, ensure there is an item...
8112     ITEM		*item, *olditem, *offhand;
8113     BUF		buf;
8114     bool		 idac = false;	// Do we now know the ac?
8115     bool		 allowstack = false;
8116 
8117     // Verify this is a legal operation.
8118     if (!ableToEquip(ix, iy, slot, quiet))
8119 	return false;
8120 
8121     item = getItem(ix, iy);
8122 
8123     switch (slot)
8124     {
8125 	case ITEMSLOT_HEAD:
8126 	    idac = true;
8127 	    break;
8128 	case ITEMSLOT_RHAND:
8129 	    allowstack = item->defn().equipstack;
8130 	    break;
8131 	case ITEMSLOT_LHAND:
8132 	    idac = item->isShield();
8133 	    break;
8134 	case ITEMSLOT_BODY:
8135 	    idac = true;
8136 	    break;
8137 	case ITEMSLOT_FEET:
8138 	    idac = true;
8139 	    break;
8140 	case ITEMSLOT_AMULET:
8141 	    break;
8142 	case ITEMSLOT_RRING:
8143 	case ITEMSLOT_LRING:
8144 	    break;
8145 	default:
8146 	{
8147 	    UT_ASSERT(!"Unhandled ITEMSLOT");
8148 	    break;
8149 	}
8150     }
8151 
8152     // It fits.  First, we remove whatever was there...
8153     olditem = getEquippedItem(slot);
8154     if (olditem)
8155     {
8156 	actionDequip(slot);
8157     }
8158     // It is possible dequiping failed.  In that case, we abort early.
8159     // (We do check that we are ableToDequip, but let's still be paranoid)
8160     if (getEquippedItem(slot))
8161 	return true;
8162 
8163     // Check to see if we need secondary dequip.
8164     if (slot == ITEMSLOT_RHAND &&
8165 	item->getSize() > getSize())
8166     {
8167 	offhand = getEquippedItem(ITEMSLOT_LHAND);
8168 
8169 	// See if we need to dequip the left hand or not.
8170 	// Bucklers are special.
8171 	// Only do so if an item is present to avoid spam.
8172 	// (Written on GO train, less prestigous than first class...)
8173 	if (offhand && offhand->getDefinition() != ITEM_BUCKLER)
8174 	{
8175 	    // Force double dequip.
8176 	    actionDequip(ITEMSLOT_LHAND, quiet);
8177 	    // Check for failure, possibly full inventory?
8178 	    // (Cursed should be handled in ableToDequip...)
8179 	    if (getEquippedItem(ITEMSLOT_LHAND))
8180 		return true;
8181 	}
8182     }
8183 
8184     // Strip off the top item of a stack.
8185     ITEM		*itemstack = 0;
8186     if (!allowstack)
8187     {
8188 	if (item->getStackCount() > 1)
8189 	{
8190 	    itemstack = item;
8191 	    item = itemstack->splitStack(1);
8192 	}
8193     }
8194 
8195     // Move the old item into the slot which we equipped from.
8196     // Note that we require a spare slot to dequip into, but we
8197     // will ensure the cursor is over the dequipped item, so one
8198     // can easily swap weapons.
8199     // Note that if we equipped from a stack, the stack will
8200     // remain in the old pos, so thus we don't want to do this.
8201     if (olditem && !itemstack)
8202     {
8203 	// If we are equipping from a slot, we have to verify
8204 	// it is legal to put the old item into that slot.
8205 	// Or you can end up wearing a sword.
8206 	if (ix != 0 || ableToEquip(olditem->getX(), olditem->getY(), (ITEMSLOT_NAMES) iy, true))
8207 	    olditem->setPos(item->getX(), item->getY());
8208     }
8209 
8210     // Move this item into the slot...
8211     if (!itemstack)
8212 	item->setPos(0, slot);
8213     else
8214     {
8215 	// As we have verified above that the slot is empty,
8216 	// this will not fail nor result in item being deleted.
8217 	acquireItem(item, 0, slot);
8218     }
8219 
8220     if (!quiet)
8221     {
8222 	buf = formatToString("%U <put> %IU %B1 %r %B2.",
8223 		this, 0, 0, item,
8224 		glb_itemslotdefs[slot].preposition,
8225 		getSlotName(slot));
8226 	reportMessage(buf);
8227     }
8228 
8229     // Poison ourselves if the item is poisoned:
8230     item->applyPoison(this, this);
8231 
8232     // If we now can figure out the ac, we mark it as known.
8233     if (idac && isAvatar())
8234 	item->markEnchantKnown();
8235 
8236     // Auto identify the item if it is cursed and we didn't know
8237     // the status.
8238     if (!item->isKnownCursed() && item->isCursed() && isAvatar())
8239     {
8240 	if (!quiet)
8241 	{
8242 	    formatAndReport("%IU <I:chill> %r %B1!", item,
8243 			getSlotName(slot));
8244 	}
8245 	item->markCursedKnown();
8246     }
8247 
8248     // If the avatar equips something that doesn't curse them,
8249     // the avatar can note it is now not cursed.
8250     if (!item->isKnownNotCursed() && !item->isCursed() && isAvatar())
8251     {
8252 	// No spam for this.
8253 	item->markNotCursedKnown();
8254     }
8255 
8256     // If the new item grants missing finger, we should ensure we have
8257     // a free finger...
8258     if (item->hasIntrinsic(INTRINSIC_MISSINGFINGER))
8259     {
8260 	dropOneRing();
8261     }
8262 
8263     rebuildAppearance();
8264     rebuildWornIntrinsic();
8265 
8266     return true;
8267 }
8268 
8269 bool
ableToDequip(ITEMSLOT_NAMES slot,bool quiet)8270 MOB::ableToDequip(ITEMSLOT_NAMES slot, bool quiet)
8271 {
8272     ITEM		*item;
8273     BUF		buf;
8274     int			 ix, iy;
8275 
8276     item = getEquippedItem(slot);
8277 
8278     if (!item)
8279     {
8280 	// Trying to dequip something you don't have on.
8281 	if (!hasSlot(slot))
8282 	{
8283 	    buf = formatToString("%U <have> no %B1!",
8284 			this, 0, 0, 0,
8285 			glb_itemslotdefs[slot].bodypart);
8286 	}
8287 	else
8288 	{
8289 	    buf = formatToString("%U <have> nothing %B1 %r %B2!",
8290 			this, 0, 0, 0,
8291 			glb_itemslotdefs[slot].preposition,
8292 			getSlotName(slot));
8293 	}
8294 	if (!quiet)
8295 	    reportMessage(buf);
8296 	return false;
8297     }
8298 
8299     // Check if the item is cursed.  Can't remove cursed items.
8300     if (item->isCursed())
8301     {
8302 	if (!quiet)
8303 	{
8304 	    formatAndReport("%U <try> to remove %IU, but %Ip <I:be> evil!", item);
8305 	}
8306 	if (isAvatar())
8307 	    item->markCursedKnown();
8308 
8309 	return false;
8310     }
8311 
8312     // Find a slot for it to go to.
8313     if (!findItemSlot(ix, iy))
8314     {
8315 	if (!quiet)
8316 	{
8317 	    formatAndReport("%U <have> no room to hold %IU.", item);
8318 	}
8319 
8320 	return false;
8321     }
8322 
8323     return true;
8324 }
8325 
8326 bool
actionDequip(ITEMSLOT_NAMES slot,bool quiet)8327 MOB::actionDequip(ITEMSLOT_NAMES slot, bool quiet)
8328 {
8329     ITEM		*item;
8330     BUF		buf;
8331     int			 ix, iy;
8332 
8333     if (!ableToDequip(slot, quiet))
8334 	return false;
8335 
8336     item = getEquippedItem(slot);
8337 
8338     // Find a slot for it to go to.
8339     findItemSlot(ix, iy);
8340 
8341     // Take the item off...
8342     item->setPos(ix, iy);
8343     if (!quiet)
8344 	formatAndReport("%U <remove> %IU from %r %B1.",
8345 		    item, getSlotName(slot));
8346 
8347     rebuildAppearance();
8348     rebuildWornIntrinsic();
8349 
8350     return true;
8351 }
8352 
8353 bool
actionPray()8354 MOB::actionPray()
8355 {
8356     // Non avatars have it simple.
8357     if (!isAvatar())
8358     {
8359 	formatAndReport("%U <utter> a quick prayer.");
8360 	return true;
8361     }
8362 
8363     formatAndReport("%U <query> the gods...");
8364 
8365     pietyReport();
8366 
8367     return true;
8368 }
8369 
8370 bool
actionPossess(MOB * target,int duration)8371 MOB::actionPossess(MOB *target, int duration)
8372 {
8373     if (target == this)
8374     {
8375 	formatAndReport("%U already <control> %MU.", target);
8376 	return true;
8377     }
8378     target->setTimedIntrinsic(this, INTRINSIC_POSSESSED, duration);
8379     formatAndReport("%U <gain> control of %MU.", target);
8380     // Transfer avatarhood.
8381     if (isAvatar())
8382 	setAvatar(target);
8383     // The source creature is marked as braindead.
8384     setIntrinsic(INTRINSIC_BRAINDEAD);
8385     return true;
8386 }
8387 
8388 bool
actionReleasePossession(bool systemshock)8389 MOB::actionReleasePossession(bool systemshock)
8390 {
8391     if (!hasIntrinsic(INTRINSIC_POSSESSED))
8392     {
8393 	formatAndReport("%U <be> already of one mind.");
8394 	return true;
8395     }
8396 
8397     MOB		*orig;
8398 
8399     orig = getInflictorOfIntrinsic(INTRINSIC_POSSESSED);
8400     if (!orig || orig->hasIntrinsic(INTRINSIC_DEAD))
8401     {
8402 	formatAndReport("%U <realize> %r original body is no more!");
8403 	// Clear status to return original controller
8404 	clearIntrinsic(INTRINSIC_POSSESSED);
8405 	// This counts as another death - which handles the avatar
8406 	// suiciding here.
8407 	// Hmm... If our orig exists we technically should be becameitem
8408 	// since we are now a corpse...  Though, on further thought,
8409 	// tha twas handled *when* it became a corpse and this is
8410 	// about the victory screen.  If we allow the user to wait
8411 	// for their body to decay, this needs to be tightned
8412 	// up anyways.
8413 	triggerAsDeath(ATTACK_LOSTBODY, 0, 0, false);
8414 	return true;
8415     }
8416 
8417     // Remove any braindead status
8418     orig->clearIntrinsic(INTRINSIC_BRAINDEAD);
8419 
8420     // If we are the avatar, restore avatar pointer
8421     if (isAvatar())
8422     {
8423 	MAP		*avatarmap;
8424 
8425 	avatarmap = glbCurLevel->findMapWithMob(orig);
8426 	setAvatar(orig);
8427 	if (avatarmap && avatarmap != glbCurLevel)
8428 	{
8429 	    // Technically, we want to change levels right away
8430 	    // However, that is a very dangerous thing to do inside of
8431 	    // actionReleasePossession as people like receiveDamage
8432 	    // naively assume that glbCurLevel remains constant.  You
8433 	    // can't just fix those guys either, as they are called
8434 	    // multiple times - say if an area attack fireball kills
8435 	    // the possessed critter.
8436 	    // MAP::changeCurrentLevel(avatarmap);
8437 	    MAP::delayedLevelChange(avatarmap);
8438 	}
8439     }
8440 
8441     // Clear status to return original controller
8442     orig->formatAndReport("%U <release> control of %MU.", this);
8443     clearIntrinsic(INTRINSIC_POSSESSED);
8444 
8445     // Apply system shock now.
8446     if (systemshock)
8447 	orig->systemshock();
8448 
8449     return true;
8450 }
8451 
8452 bool
actionForget(SPELL_NAMES spell,SKILL_NAMES skill)8453 MOB::actionForget(SPELL_NAMES spell, SKILL_NAMES skill)
8454 {
8455     bool		 canforget = false;
8456     BUF		buf;
8457     ITEM		*item;
8458     int			 i;
8459     const char		*prereq;
8460 
8461     if (spell == SPELL_NONE && skill == SKILL_NONE)
8462     {
8463 	// Nothing to foget.
8464 	formatAndReport("%U <suffer> from deja vu.");
8465 	return true;
8466     }
8467 
8468     // Each in turn.
8469     if (spell != SPELL_NONE)
8470     {
8471 	if (!hasSpell(spell))
8472 	{
8473 	    formatAndReport("%U <forget> %B1 before knowing it.  Impressive.",
8474 		    glb_spelldefs[spell].name);
8475 	}
8476 	else if ((item = getSourceOfIntrinsic((INTRINSIC_NAMES) glb_spelldefs[spell].intrinsic)))
8477 	{
8478 	    formatAndReport("%U cannot forget %B1 while using %IU.",
8479 			item, glb_spelldefs[spell].name);
8480 	}
8481 	else
8482 	{
8483 	    canforget = true;
8484 
8485 	    // Check that nothing we know prevents us from forgetting it.
8486 	    for (i = 0; i < NUM_SPELLS; i++)
8487 	    {
8488 		if (i == spell)
8489 		    continue;
8490 
8491 		if (!hasSpell((SPELL_NAMES) i))
8492 		    continue;
8493 
8494 		prereq = glb_spelldefs[i].prereq;
8495 		while (*prereq)
8496 		{
8497 		    if (*((u8 *) prereq) == spell)
8498 		    {
8499 			canforget = false;
8500 			buf.sprintf("Knowing %s prevents %s from being forgotten.",
8501 				    glb_spelldefs[i].name,
8502 				    glb_spelldefs[spell].name);
8503 			reportMessage(buf);
8504 		    }
8505 		    prereq++;
8506 		}
8507 	    }
8508 
8509 	    if (canforget)
8510 	    {
8511 		formatAndReport("%U <try> to forget %B1.",
8512 			    glb_spelldefs[spell].name);
8513 		clearIntrinsic((INTRINSIC_NAMES) glb_spelldefs[spell].intrinsic);
8514 		// Remove from base type, if present.
8515 		MOB		*base;
8516 		base = getBaseType();
8517 		if (base)
8518 		    base->clearIntrinsic((INTRINSIC_NAMES) glb_spelldefs[spell].intrinsic);
8519 
8520 		// Report new number of slots.
8521 		formatAndReport("%U <have> %B1.",
8522 			gram_createcount("free spell slot",
8523 					 getFreeSpellSlots(),
8524 					 true));
8525 	    }
8526 	}
8527     }
8528 
8529     if (skill != SKILL_NONE)
8530     {
8531 	if (!hasSkill(skill))
8532 	{
8533 	    formatAndReport("%U <forget> %B1 before knowing it.  Impressive.",
8534 		    glb_skilldefs[skill].name);
8535 	}
8536 	else if ((item = getSourceOfIntrinsic((INTRINSIC_NAMES) glb_skilldefs[skill].intrinsic)))
8537 	{
8538 	    formatAndReport("%U cannot forget %B1 while using %IU.",
8539 		    item,
8540 		    glb_skilldefs[skill].name);
8541 	}
8542 	else
8543 	{
8544 	    canforget = true;
8545 
8546 	    // Check that nothing we know prevents us from forgetting it.
8547 	    for (i = 0; i < NUM_SKILLS; i++)
8548 	    {
8549 		if (i == skill)
8550 		    continue;
8551 
8552 		if (!hasSkill((SKILL_NAMES) i))
8553 		    continue;
8554 
8555 		prereq = glb_skilldefs[i].prereq;
8556 		while (*prereq)
8557 		{
8558 		    if (*((u8 *) prereq) == spell)
8559 		    {
8560 			canforget = false;
8561 			buf.sprintf("Knowing %s prevents %s from being forgotten.",
8562 				    glb_skilldefs[i].name,
8563 				    glb_skilldefs[skill].name);
8564 			reportMessage(buf);
8565 		    }
8566 		    prereq++;
8567 		}
8568 	    }
8569 
8570 	    if (canforget)
8571 	    {
8572 		formatAndReport("%U <try> to forget %B1.",
8573 			    glb_skilldefs[skill].name);
8574 		clearIntrinsic((INTRINSIC_NAMES) glb_skilldefs[skill].intrinsic);
8575 		// Remove from base type, if present.
8576 		MOB		*base;
8577 		base = getBaseType();
8578 		if (base)
8579 		    base->clearIntrinsic((INTRINSIC_NAMES) glb_skilldefs[spell].intrinsic);
8580 		// Report new number of slots.
8581 		// Report new number of slots.
8582 		formatAndReport("%U <have> %B1.",
8583 			gram_createcount("free skill slot",
8584 					 getFreeSkillSlots(),
8585 					 true));
8586 	    }
8587 	}
8588     }
8589 
8590     return canforget;
8591 }
8592 
8593 // We store what our action bars are bound to in this packed
8594 // structure.  This both reduces the size required and ensures
8595 // we can store both spells and actions.
8596 //
8597 // It also secretly clamps our number of spells at 127 and number
8598 // of action commands to 127.  The latter is likely not a problem,
8599 // but I look forward to some day running into the former limit
8600 // and cursing the fool that left it here.
8601 
8602 u8
action_packStripButton(ACTION_NAMES action)8603 action_packStripButton(ACTION_NAMES action)
8604 {
8605     return action;
8606 }
8607 
8608 u8
action_packStripButton(SPELL_NAMES spell)8609 action_packStripButton(SPELL_NAMES spell)
8610 {
8611     return spell | 128;
8612 }
8613 
8614 void
action_unpackStripButton(u8 value,ACTION_NAMES & action,SPELL_NAMES & spell)8615 action_unpackStripButton(u8 value, ACTION_NAMES &action, SPELL_NAMES &spell)
8616 {
8617     spell = SPELL_NONE;
8618     action = ACTION_NONE;
8619     if (value & 128)
8620 	spell = (SPELL_NAMES) (value & 127);
8621     else
8622 	action = (ACTION_NAMES) value;
8623 }
8624 
8625 SPRITE_NAMES
action_spriteFromStripButton(u8 value,int * grey)8626 action_spriteFromStripButton(u8 value, int *grey)
8627 {
8628     SPELL_NAMES		spell;
8629     ACTION_NAMES	action;
8630 
8631     action_unpackStripButton(value, action, spell);
8632 
8633     if (grey)
8634 	*grey = 257;
8635 
8636     if (spell != SPELL_NONE)
8637     {
8638 	if (grey)
8639 	{
8640 	    MOB		*avatar = MOB::getAvatar();
8641 	    if (avatar)
8642 	    {
8643 		*grey = avatar->spellCastability(spell);
8644 	    }
8645 	    else
8646 	    {
8647 		// Dead people have no spells!
8648 		*grey = 0;
8649 	    }
8650 	}
8651 	return (SPRITE_NAMES) glb_spelldefs[spell].tile;
8652     }
8653     else
8654 	return (SPRITE_NAMES) glb_actiondefs[action].tile;
8655 }
8656 
8657 void
action_indexToOverlayPos(int index,int & x,int & y)8658 action_indexToOverlayPos(int index, int &x, int &y)
8659 {
8660     if (index < 15)
8661     {
8662 	x = index * 2;
8663 	y = -2;
8664     }
8665     else if (index < 30)
8666     {
8667 	x = index - 15;
8668 	x *= 2;
8669 	y = 20;
8670     }
8671     else if (index < 40)
8672     {
8673 	x = -1;
8674 	y = (index - 30) * 2;
8675     }
8676     else if (index < 50)
8677     {
8678 	x = 29;
8679 	y = (index - 40) * 2;
8680     }
8681     else
8682     {
8683 	UT_ASSERT(!"Invalid buttons strip");
8684 	x = 7 * 2;
8685 	y = 5 * 2;
8686     }
8687 }
8688 
8689