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:        creature.cpp ( POWDER Library, C++ )
11  *
12  * COMMENTS:
13  *	This would be better named mob.cpp or mob.h, but the change
14  *	to MOB from CREATURE occured a bit late.
15  *
16  *	This handles all the generic MOB manipulation routines.
17  *	Note that action and ai functions can be found in action.cpp
18  *	or ai.cpp.
19  */
20 
21 #include "creature.h"
22 
23 #include "mygba.h"
24 #include <stdio.h>
25 #include <ctype.h>
26 #include "assert.h"
27 #include "gfxengine.h"
28 #include "glbdef.h"
29 #include "map.h"
30 #include "creature.h"
31 #include "rand.h"
32 #include "msg.h"
33 #include "grammar.h"
34 #include "item.h"
35 #include "itemstack.h"
36 #include "intrinsic.h"
37 #include "sramstream.h"
38 #include "victory.h"
39 #include "control.h"
40 #include "piety.h"
41 #include "encyc_support.h"
42 #include "hiscore.h"
43 
44 MOBREF	glbAvatar;
45 
46 int  	MOB::ourFireBallCount = 0;
47 SPELL_NAMES MOB::ourZapSpell;
48 ATTACK_NAMES MOB::ourEffectAttack;
49 bool	MOB::ourAvatarOnFreshSquare = false;
50 bool	MOB::ourAvatarMoveOld = true;
51 u16	glbKillCount[NUM_MOBS];
52 s8	MOB::ourAvatarDx = 0;
53 s8	MOB::ourAvatarDy = 0;
54 
55 int	glbMobCount = 0;
56 
57 // Defined in main.cpp
58 extern bool glbAutoRunEnabled;
59 
60 void
mob_init()61 mob_init()
62 {
63     // Nothing to do here... yet.
64 }
65 
66 //
67 // MOB::Methods...
68 //
MOB()69 MOB::MOB()
70 {
71     myNext = 0;
72     myInventory = 0;
73     myCounters = 0;
74     myWornIntrinsic = 0;
75     glbMobCount++;
76 }
77 
~MOB()78 MOB::~MOB()
79 {
80     INTRINSIC_COUNTER		*counter, *next;
81 
82     // We don't want people pointing to our mob value any more:
83     if (!myOwnRef.transferMOB(0))
84     {
85 	UT_ASSERT(!"Failed transfer in destructor");
86     }
87 
88     // We obviously don't need these anymore.
89     for (counter = myCounters; counter; counter = next)
90     {
91 	next = counter->myNext;
92 	delete counter;
93     }
94     myCounters = 0;
95 
96     delete myWornIntrinsic;
97 
98     MOB		*mob;
99 
100     mob = myBaseType.getMob();
101     if (mob)
102     {
103 	// This is safe as we own it.
104 	delete mob;
105     }
106 
107     // Delete all of our inventory.
108     // Normal in game mob deletions will not have inventory as we'll
109     // drop it first, but when cleaning up after a game we just
110     // delete the mob straight
111     {
112 	ITEM		*cur, *next;
113 
114 	// TODO: This is very dangerous!  We may have a quest
115 	// item or similar?
116 	// Ideally we'll assert false if inventory is present
117 	// except when doing map destruction.
118 	for (cur = myInventory; cur; cur = next)
119 	{
120 	    next = cur->getNext();
121 
122 	    cur->setNext(0);
123 	    delete cur;
124 	}
125 
126 	myInventory = 0;
127     }
128 
129     // Mobs should only be destructed after they have been removed
130     // from the relevant lists.
131     UT_ASSERT(!myNext);
132 
133     glbMobCount--;
134 }
135 
136 void
init()137 MOB::init()
138 {
139     memset(glbKillCount, 0, sizeof(u16) * NUM_MOBS);
140     ourAvatarDx = 0;
141     ourAvatarDy = 0;
142     ourAvatarMoveOld = true;
143 }
144 
145 MOB *
getAvatar()146 MOB::getAvatar()
147 {
148     return glbAvatar.getMob();
149 }
150 
151 void
setAvatar(MOB * mob)152 MOB::setAvatar(MOB *mob)
153 {
154     glbAvatar.setMob(mob);
155 }
156 
157 void
saveAvatar(SRAMSTREAM & os)158 MOB::saveAvatar(SRAMSTREAM &os)
159 {
160     glbAvatar.save(os);
161 }
162 
163 void
loadAvatar(SRAMSTREAM & os)164 MOB::loadAvatar(SRAMSTREAM &os)
165 {
166     glbAvatar.load(os);
167 }
168 
169 
170 void
buildAscensionKit()171 MOB::buildAscensionKit()
172 {
173     ITEM		*item;
174 
175     // This is a recreation of a winning character.
176     item = ITEM::createMagicItem(MAGICTYPE_HELM, HELM_WARNING);
177     acquireItem(item);
178     item = ITEM::createMagicItem(MAGICTYPE_AMULET, AMULET_LIFESAVING);
179     acquireItem(item);
180     item = ITEM::create(ITEM_FLAMESWORD);
181     acquireItem(item);
182     item = ITEM::create(ITEM_REFLECTSHIELD);
183     acquireItem(item);
184     item = ITEM::create(ITEM_PLATEMAIL);
185     acquireItem(item);
186     item = ITEM::createMagicItem(MAGICTYPE_RING, RING_REGENERATE);
187     acquireItem(item);
188     item = ITEM::createMagicItem(MAGICTYPE_BOOTS, BOOTS_SPEED);
189     acquireItem(item);
190 
191     setIntrinsic(INTRINSIC_SKILL_ARMOUR_HELMET);
192     setIntrinsic(INTRINSIC_SKILL_ARMOUR_SHIELD);
193     setIntrinsic(INTRINSIC_SKILL_ARMOUR_BODY);
194     setIntrinsic(INTRINSIC_SKILL_ARMOUR_BOOTS);
195     setIntrinsic(INTRINSIC_SKILL_WEAPON_RANGED);
196     setIntrinsic(INTRINSIC_SKILL_WEAPON_EDGED);
197     setIntrinsic(INTRINSIC_SKILL_WEAPON_MEDIUM);
198     setIntrinsic(INTRINSIC_SKILL_WEAPON_PARRY);
199 
200     setIntrinsic(INTRINSIC_SPELL_MAGICMISSILE);
201     setIntrinsic(INTRINSIC_SPELL_SPARK);
202     setIntrinsic(INTRINSIC_SPELL_LIGHTNINGBOLT);
203     setIntrinsic(INTRINSIC_SPELL_REGENERATE);
204     setIntrinsic(INTRINSIC_SPELL_SLOWPOISON);
205     setIntrinsic(INTRINSIC_SPELL_HEAL);
206     setIntrinsic(INTRINSIC_SPELL_CUREPOISON);
207     setIntrinsic(INTRINSIC_SPELL_MAJORHEAL);
208     setIntrinsic(INTRINSIC_SPELL_RESURRECT);
209     setIntrinsic(INTRINSIC_SPELL_FORCEBOLT);
210     setIntrinsic(INTRINSIC_SPELL_ACIDSPLASH);
211     setIntrinsic(INTRINSIC_SPELL_DISINTEGRATE);
212     setIntrinsic(INTRINSIC_SPELL_DIG);
213     setIntrinsic(INTRINSIC_SPELL_TELEPORT);
214     setIntrinsic(INTRINSIC_SPELL_BLINK);
215     setIntrinsic(INTRINSIC_SPELL_IDENTIFY);
216     setIntrinsic(INTRINSIC_SPELL_DETECTCURSE);
217     setIntrinsic(INTRINSIC_SPELL_PETRIFY);
218 
219     myMP = 147;
220     myMaxMP = 147;
221     myMagicDie = 38;
222     myHitDie = 26;
223     myHP = 103;
224     myMaxHP = 103;
225 }
226 
227 MOB *
create(MOB_NAMES definition)228 MOB::create(MOB_NAMES definition)
229 {
230     MOB	*mob;
231 
232     mob = new MOB();
233 
234     mob->myOwnRef.createAndSetMOB(mob);
235 
236     mob->myDefinition = definition;
237     mob->myOrigDefinition = definition;
238     mob->myExpLevel = glb_mobdefs[definition].explevel;
239     mob->myExp = 0;
240     mob->myHitDie = glb_mobdefs[definition].hitdie * 2;
241     mob->myHP = rand_dice(glb_mobdefs[definition].hp);
242     mob->myMaxHP = mob->myHP;
243     mob->myMagicDie = glb_mobdefs[definition].mpdie * 2;
244     mob->myMP = rand_dice(glb_mobdefs[definition].mp);
245     mob->myMaxMP = mob->myMP;
246     mob->myNoiseLevel = 0;
247 
248     {
249 	GENDERCHANCE_NAMES	genderchance;
250 	int			male, female, neuter, percent;
251 
252 	genderchance = (GENDERCHANCE_NAMES) glb_mobdefs[definition].gender;
253 
254 	male = glb_genderchancedefs[genderchance].male;
255 	female = glb_genderchancedefs[genderchance].female;
256 	neuter = glb_genderchancedefs[genderchance].neuter;
257 
258 	UT_ASSERT(male + female + neuter == 100);
259 
260 	percent = rand_choice(100);
261 
262 	if (percent < male)
263 	    mob->myGender = VERB_HE;
264 	else if (percent < male + female)
265 	    mob->myGender = VERB_SHE;
266 	else
267 	    mob->myGender = VERB_IT;
268 
269 	// After all this, the avatar uses the global glbGender value
270 	// to seed their gender.
271 	if (definition == MOB_AVATAR)
272 	{
273 	    if (glbGender)
274 		mob->myGender = VERB_SHE;
275 	    else
276 		mob->myGender = VERB_HE;
277 	}
278     }
279 
280     // Everyone starts of relatively well off.
281     mob->myFoodLevel = 1500;
282 
283     mob->myIntrinsic.loadFromString(glb_mobdefs[definition].intrinsic);
284 
285     {
286 	// Determine handedness.
287 	ITEMSLOTSET_NAMES	slotset;
288 
289 	slotset = (ITEMSLOTSET_NAMES) defn(definition).slotset;
290 	if (glb_itemslotsetdefs[slotset].lhand_name &&
291 	    glb_itemslotsetdefs[slotset].rhand_name)
292 	{
293 	    // 10% chance of a left handed creature.
294 	    if (rand_chance(10))
295 		mob->setIntrinsic(INTRINSIC_LEFTHANDED);
296 	}
297     }
298 
299     // Set this out of range so we don't munge any old data.
300     mob->myX = (u8) -1;
301     mob->myY = (u8) -1;
302 
303     mob->setDLevel(-1);
304 
305     mob->myAIFSM = AI_FSM_JUSTBORN;
306     mob->myAIState = 0;
307 
308     // Create some treasure...
309     ITEM		*item;
310 
311     if (glbTutorial && definition == MOB_AVATAR)
312     {
313 	// Special case for the tutorial.  You start with a chainmail.
314 	item = ITEM::create(ITEM_CHAINMAIL, false, true);
315 	if (item) mob->acquireItem(item);
316 
317 	// Because it is a tutorial, we take pity and give the player
318 	// some bonus strength.
319 	mob->myMP = 30;
320 	mob->myMaxMP = 30;
321 	mob->myMagicDie = 4;
322 	mob->myHitDie = 4;
323         mob->myHP = 30;
324 	mob->myMaxHP = 30;
325     }
326     else if (definition == MOB_AVATAR)
327     {
328 	// You are lucky!  You always start with a weapon, an armour,
329 	// a book, and a random item.
330 	// Rethink...  You start with a balanced set that
331 	// is always balanced.
332 	//
333 	// You always wear some clothing as it is otherwise
334 	// ugly to see your naked self.  (Some may say the charm
335 	// of POWDER is starting naked, though :>)
336 	// Your weapon strength is the inverse of the body armour
337 	//
338 	// DSR: Don't update the comments much do you?  :)
339 	//
340 	// JML: I believe comments form a place to engage in a dialog
341 	// to better understand not just what the code is, but what it
342 	// was, so people can gain the sense of history without
343 	// resorting to svn blame.  Which is a rationalization for
344 	// my not updating them much.
345 	// The *really* long dialog is in the createNPC function, IIRC.
346 	//
347 	ITEM_NAMES		armour, weapon;
348 	armour = ITEM_PLATEMAIL;
349 	weapon = ITEM_WARHAMMER;
350 
351 	// Did the user pick something specific?  Give him ...
352 	// ...well, more or less what he asked for ;)
353 	//
354 	// The user's pick is judged by the currently worshiped god.
355 	//
356 	// The adventurer and cultist can have the standard random
357 	// selection -- the first is true to original POWDER,
358 	// and the second, well, Xom is the god of "random"...
359 
360 	/////////////////////////////////////////////////////////////////////
361 	//
362 	// The Fighter
363 	//
364 	// Always gets an edged/blunt large or medium weapon.
365 	// Always gets metal 'fabricated' armor.
366 	//
367 	// Never gets the "guile" skill book as his guaranteed skillbook.
368 	// Never gets the "divination" spell book as his guaranteed spellbook.
369 	if (piety_chosengod() == GOD_FIGHTER)
370 	{
371 	    ITEM_NAMES weplist[] =
372 	    {
373 		ITEM_LONGSWORD,
374 		ITEM_SILVERSWORD,
375 		ITEM_WARHAMMER,
376 		ITEM_MACE
377 	    };
378 	    ITEM_NAMES armlist[] =
379 	    {
380 		ITEM_PLATEMAIL,
381 		ITEM_SPLINTMAIL,
382 		ITEM_BANDEDMAIL,
383 		ITEM_CHAINMAIL
384 	    };
385 	    weapon = weplist[rand_choice(sizeof(weplist)/sizeof(ITEM_NAMES))];
386 	    armour = armlist[rand_choice(sizeof(armlist)/sizeof(ITEM_NAMES))];
387 	}
388 
389 	// The Wizard
390 	//
391 	// Always gets a 'small' weapon.
392 	// Always gets a robe.
393 	//
394 	// Gets two spell books (note he still starts with
395 	// one skill and one spell slot, he just gets better choice
396 	// in his starting spells).
397 	else if (piety_chosengod() == GOD_WIZARD)
398 	{
399 	    ITEM_NAMES weplist[] =
400 	    {
401 		ITEM_KNIFE,
402 		ITEM_DAGGER,
403 		ITEM_SILVERDAGGER,
404 		ITEM_CLUB,
405 		ITEM_SHORTSWORD
406 	    };
407 	    weapon = weplist[rand_choice(sizeof(weplist)/sizeof(ITEM_NAMES))];
408 	    armour = ITEM_ROBE;
409 	}
410 
411 	//
412 	// The Ranger
413 	//
414 	// Always gets a bow and some arrows, OR a 'pointed' weapon.
415 	// Always gets cloth or leather armor.
416 	//
417 	// Never receives the book of H'ruth as his guaranteed skillbook.
418 	// Never receives the book of healing as his guaranteed spellbook.
419 	else if (piety_chosengod() == GOD_ROGUE)
420 	{
421 	    ITEM_NAMES weplist[] =
422 	    {
423 		ITEM_BOW,
424 		ITEM_DAGGER,
425 		ITEM_SILVERDAGGER,
426 		ITEM_SPEAR,
427 		ITEM_SILVERSPEAR,
428 		ITEM_RAPIER
429 	    };
430 	    ITEM_NAMES armlist[] =
431 	    {
432 		ITEM_ROBE,
433 		ITEM_LEATHERTUNIC,
434 		ITEM_STUDDEDLEATHER
435 	    };
436 	    weapon = weplist[rand_choice(sizeof(weplist)/sizeof(ITEM_NAMES))];
437 	    armour = armlist[rand_choice(sizeof(armlist)/sizeof(ITEM_NAMES))];
438 	}
439 
440 	// The Cleric
441 	//
442 	// Always gets a blunt weapon.
443 	// Only one who can start with the 'special' armors;
444 	// will never start with leather.
445 	//
446 	// Never receives the book of Necromancy or Death as his guaranteed
447 	// spellbook.
448 	else if (piety_chosengod() == GOD_CLERIC)
449 	{
450 	    ITEM_NAMES weplist[] =
451 	    {
452 		ITEM_CLUB,
453 		ITEM_MACE,
454 		ITEM_WARHAMMER
455 	    };
456 	    ITEM_NAMES armlist[] =
457 	    {
458 		ITEM_ROBE,
459 		ITEM_CHAINMAIL,
460 		ITEM_MITHRILMAIL,
461 		ITEM_BANDEDMAIL,
462 		ITEM_SPLINTMAIL,
463 		ITEM_PLATEMAIL,
464 		ITEM_CRYSTALPLATE
465 	    };
466 	    weapon = weplist[rand_choice(sizeof(weplist)/sizeof(ITEM_NAMES))];
467 	    armour = armlist[rand_choice(sizeof(armlist)/sizeof(ITEM_NAMES))];
468 	}
469 
470 	// The Necromancer
471 	//
472 	// Always starts with a knife and wearing a robe.
473 	//
474 	// Always starts with either the book of Death or Necromancy
475 	// as his guaranteed spellbook, to compensate.
476 	else if (piety_chosengod() == GOD_NECRO)
477 	{
478 	    weapon = ITEM_KNIFE;
479 	    armour = ITEM_ROBE;
480 	}
481 
482 	// The Barbarian
483 	//
484 	// Always starts with a bashing weapon or spear.
485 	// Always starts with leather armor of some form.
486 	//
487 	// Begins with two skill books; note that he still receives
488 	// a single spell and a single skill slot, however.
489 	else if (piety_chosengod() == GOD_BARB)
490 	{
491 	    ITEM_NAMES weplist[] =
492 	    {
493 		ITEM_CLUB,
494 		ITEM_MACE,
495 		ITEM_WARHAMMER,
496 		ITEM_SPEAR,
497 		ITEM_SILVERSPEAR
498 	    };
499 	    ITEM_NAMES armlist[] =
500 	    {
501 		ITEM_LEATHERTUNIC,
502 		ITEM_STUDDEDLEATHER
503 	    };
504 	    weapon = weplist[rand_choice(sizeof(weplist)/sizeof(ITEM_NAMES))];
505 	    armour = armlist[rand_choice(sizeof(armlist)/sizeof(ITEM_NAMES))];
506 	}
507 
508 	// The Cultist
509 	//
510 	// Befitting the random nature, you get a random weapon and
511 	// random armour with no attempt at balance.  Your special
512 	// books are likewise entirely random.
513 	else if (piety_chosengod() == GOD_CULTIST)
514 	{
515 	    ITEM	*temp;
516 	    temp = ITEM::createRandomType(ITEMTYPE_WEAPON);
517 	    weapon = temp->getDefinition();
518 	    delete temp;
519 	    temp = ITEM::createRandomType(ITEMTYPE_ARMOUR);
520 	    armour = temp->getDefinition();
521 	    delete temp;
522 	}
523 	// The Agnostic
524 	//
525 	// The classic weapon set that is good for any problem.  This
526 	// is also the default for new players.
527 	else if (piety_chosengod() == GOD_AGNOSTIC)
528 	{
529 	    switch (rand_choice(3))
530 	    {
531 		case 0:		// Robe
532 		{
533 		    ITEM_NAMES	list[] =
534 		    {
535 			ITEM_LONGSWORD,
536 			ITEM_SPEAR,
537 			ITEM_MACE,
538 			ITEM_RAPIER
539 		    };
540 		    armour = ITEM_ROBE;
541 		    weapon = list[rand_choice(sizeof(list)/sizeof(ITEM_NAMES))];
542 		    break;
543 		}
544 		case 1:		// Leather
545 		{
546 		    ITEM_NAMES	list[] =
547 		    {
548 			ITEM_SHORTSWORD,
549 			ITEM_CLUB
550 		    };
551 		    armour = ITEM_LEATHERTUNIC;
552 		    weapon = list[rand_choice(sizeof(list)/sizeof(ITEM_NAMES))];
553 		    break;
554 		}
555 		case 2:		// Studded Leather
556 		{
557 		    ITEM_NAMES	list[] =
558 		    {
559 			ITEM_DAGGER,
560 			ITEM_KNIFE,
561 			ITEM_BOW
562 		    };
563 		    armour = ITEM_STUDDEDLEATHER;
564 		    weapon = list[rand_choice(sizeof(list)/sizeof(ITEM_NAMES))];
565 		    break;
566 		}
567 	    }
568 	}
569 
570 	item = ITEM::create(armour);
571 	if (item)
572 	{
573 	    item->makeVanilla();
574 	    mob->acquireItem(item);
575 	    mob->actionEquip(item->getX(), item->getY(),
576 			    ITEMSLOT_BODY, true);
577 	}
578 	item = ITEM::create(weapon);
579 	if (item)
580 	{
581 	    item->makeVanilla();
582 	    mob->acquireItem(item);
583 	    mob->actionEquip(item->getX(), item->getY(),
584 			    (item->getDefinition() == ITEM_BOW) ?
585 				ITEMSLOT_LHAND :
586 				ITEMSLOT_RHAND, true);
587 	}
588 	// If the item is a bow, add arrows.  If it is arrows,
589 	// add a bow.
590 	if (item && item->getDefinition() == ITEM_BOW)
591 	{
592 	    item = ITEM::create(ITEM_ARROW, false);
593 	    if (item)
594 	    {
595 		item->makeVanilla();
596 		item->setStackCount(7);
597 		item->markQuivered();
598 		mob->acquireItem(item);
599 	    }
600 	}
601 
602 	// We want to create on spellbook and one skill book.
603 	{
604 	    SPELLBOOK_NAMES		spelllist[NUM_SPELLBOOKS];
605 	    SPELLBOOK_NAMES		skilllist[NUM_SPELLBOOKS];
606 	    int				numspell, numskill;
607 	    SPELLBOOK_NAMES		book;
608 
609 	    numspell = 0;
610 	    numskill = 0;
611 	    FOREACH_SPELLBOOK(book)
612 	    {
613 		if (*glb_spellbookdefs[book].spells)
614 		{
615 		    // Note the funny use of continue
616 		    // inside swtich to abort adding spells/skills
617 		    switch (piety_chosengod())
618 		    {
619 			case GOD_BARB:
620 			    continue;
621 			case GOD_NECRO:
622 			    if (book != SPELLBOOK_DEATH &&
623 				book != SPELLBOOK_NECRO)
624 				continue;
625 			    break;
626 			case GOD_CLERIC:
627 			    if (book == SPELLBOOK_DEATH ||
628 				book == SPELLBOOK_NECRO)
629 				continue;
630 			    break;
631 			case GOD_FIGHTER:
632 			    if (book == SPELLBOOK_DIVINATION)
633 				continue;
634 			    break;
635 			case GOD_ROGUE:
636 			    if (book == SPELLBOOK_HEAL)
637 				continue;
638 			    break;
639 			case GOD_CULTIST:
640 			    // Both books are of any type.
641 			    skilllist[numskill++] = book;
642 			    break;
643 			case NUM_GODS:
644 			case GOD_AGNOSTIC:
645 			    // Nothing special
646 			    break;
647 			case GOD_WIZARD:
648 			    // Get only spell books so add to skill list.
649 			    skilllist[numskill++] = book;
650 			    break;
651 		    }
652 		    // It has spells
653 		    spelllist[numspell++] = book;
654 		}
655 		else
656 		{
657 		    // Note the funny use of continue
658 		    // inside swtich to abort adding spells/skills
659 		    switch (piety_chosengod())
660 		    {
661 			case GOD_BARB:
662 			    // Barbarians get two skill books
663 			    spelllist[numspell++] = book;
664 			    break;
665 			case GOD_NECRO:
666 			case GOD_CLERIC:
667 			    break;
668 			case GOD_FIGHTER:
669 			    // The fighter never gets guile
670 			    if (book == SPELLBOOK_ROGUE)
671 				continue;
672 			    break;
673 			case GOD_ROGUE:
674 			    // The ranger never gets H'ruth
675 			    if (book == SPELLBOOK_BARB)
676 				continue;
677 			    break;
678 			case GOD_CULTIST:
679 			    // Both books are of any type.
680 			    spelllist[numspell++] = book;
681 			    break;
682 			case NUM_GODS:
683 			case GOD_AGNOSTIC:
684 			    // Nothing special
685 			    break;
686 			case GOD_WIZARD:
687 			    // No skill books for wizards
688 			    continue;
689 		    }
690 		    skilllist[numskill++] = book;
691 		}
692 	    }
693 
694 	    // Pick one random of each...
695 	    book = spelllist[rand_choice(numspell)];
696 	    item = ITEM::createMagicItem(MAGICTYPE_SPELLBOOK, book, false);
697 	    if (item) mob->acquireItem(item);
698 
699 	    book = skilllist[rand_choice(numskill)];
700 	    item = ITEM::createMagicItem(MAGICTYPE_SPELLBOOK, book, false);
701 	    if (item) mob->acquireItem(item);
702 	}
703 
704 	// The bonus item!
705 	item = ITEM::createRandom();
706 	if (item) mob->acquireItem(item);
707     }
708     else
709     {
710 	// Standard treasure is a random item...
711 	// We, however, don't give it to every creature all the time.
712 	if (rand_chance(10))
713 	{
714 	    item = ITEM::createRandom();
715 	    mob->acquireItem(item);
716 	}
717     }
718 
719     // Add all the standard loot the critter has.
720     const char		*loot;
721 
722     loot = glb_mobdefs[definition].loot;
723     while (*loot)
724     {
725 	item = ITEM::create((ITEM_NAMES) *loot);
726 	mob->acquireItem(item);
727 	loot++;
728     }
729 
730     // Likewise, any loot types...
731     loot = glb_mobdefs[definition].loottype;
732     while (*loot)
733     {
734 	item = ITEM::createRandomType((ITEMTYPE_NAMES) *loot);
735 	mob->acquireItem(item);
736 	loot++;
737     }
738 
739 //    if (definition == MOB_AVATAR)
740 //	mob->setIntrinsic(INTRINSIC_SPELL_SUMMON_IMP);
741     // Debug insta assign for item testing...
742 #if 0
743     for (int i = 0; i < 15; i++)
744     {
745 	item = ITEM::create(ITEM_WATER);
746 	mob->acquireItem(item);
747     }
748 #endif
749 #if 0
750     if (definition == MOB_AVATAR)
751     {
752 	mob->setIntrinsic(INTRINSIC_LICHFORM);
753 	item = ITEM::create(ITEM_BLACKHEART);
754 	mob->acquireItem(item);
755 	item = ITEM::create(ITEM_YRUNE);
756 	mob->acquireItem(item);
757 	item = ITEM::create(ITEM_ARROW);
758 	mob->acquireItem(item);
759 	item = ITEM::create(ITEM_BOW);
760 	mob->acquireItem(item);
761 	// Test cleric dress.
762 	item = ITEM::create(ITEM_SANDALS);
763 	mob->acquireItem(item);
764 	item = ITEM::create(ITEM_RUBYNECKLACE);
765 	mob->acquireItem(item);
766 	item = ITEM::create(ITEM_CHAINMAIL);
767 	mob->acquireItem(item);
768 	// Verify mini icons.
769 	item = ITEM::create(ITEM_SCROLL_FOOBAR);
770 	mob->acquireItem(item);
771 	item = ITEM::create(ITEM_PURPLEWAND);
772 	mob->acquireItem(item);
773     }
774 #endif
775 
776     // Simple hash.
777     if (definition == MOB_AVATAR)
778     {
779 #ifndef iPOWDER
780 	if (rand_hashstring(glbAvatarName) == 3939879139u)
781 	{
782 	    glbWizard = true;
783 	}
784 	else
785 	{
786 	    glbWizard = false;
787 	}
788 #endif
789     }
790 
791 #if 0
792     // Report the hash to make it easy to change the passwords.
793     if (definition == MOB_AVATAR)
794     {
795 	BUF		buf;
796 
797 	buf.sprintf("%x  ", rand_hashstring(glbAvatarName));
798 	msg_report(buf);
799     }
800 #endif
801 
802 #if 0
803     // You can now wish for all this.
804     if (definition == MOB_AVATAR && glbWizard)
805     {
806 	mob->setIntrinsic(INTRINSIC_SPELL_RESURRECT);
807 	mob->setIntrinsic(INTRINSIC_SPELL_WIZARDSEYE);
808 	mob->setIntrinsic(INTRINSIC_SPELL_POSSESS);
809 	mob->setIntrinsic(INTRINSIC_SPELL_BLINK);
810 	mob->setIntrinsic(INTRINSIC_SPELL_FETCH);
811 	mob->setIntrinsic(INTRINSIC_SPELL_BINDSOUL);
812 	mob->setIntrinsic(INTRINSIC_SPELL_ENTOMB);
813 	mob->setIntrinsic(INTRINSIC_SPELL_PETRIFY);
814 	mob->setIntrinsic(INTRINSIC_SPELL_DETECTCURSE);
815 	mob->setIntrinsic(INTRINSIC_SPELL_IDENTIFY);
816 	mob->setIntrinsic(INTRINSIC_SPELL_MAJORHEAL);
817 	mob->setIntrinsic(INTRINSIC_SPELL_FORCEBOLT);
818 	mob->setIntrinsic(INTRINSIC_SPELL_ACIDSPLASH);
819 	mob->setIntrinsic(INTRINSIC_SPELL_CORROSIVEEXPLOSION);
820 	mob->setIntrinsic(INTRINSIC_SPELL_CREATEPIT);
821 	mob->setIntrinsic(INTRINSIC_SPELL_TRACK);
822 	mob->setIntrinsic(INTRINSIC_SPELL_DIAGNOSE);
823 	mob->setIntrinsic(INTRINSIC_SPELL_ROLLINGBOULDER);
824 	mob->setIntrinsic(INTRINSIC_SPELL_SUMMON_DEMON);
825 	mob->setIntrinsic(INTRINSIC_SPELL_LIVINGFROST);
826 	mob->setIntrinsic(INTRINSIC_SPELL_BLIZZARD);
827 	mob->setIntrinsic(INTRINSIC_SPELL_PRESERVE);
828 	mob->setIntrinsic(INTRINSIC_SPELL_DIRECTWIND);
829 	mob->setIntrinsic(INTRINSIC_SPELL_STICKYFLAMES);
830 	mob->setIntrinsic(INTRINSIC_SKILL_WEAPON_RIPOSTE);
831 	mob->setIntrinsic(INTRINSIC_SKILL_WEAPON_TRUEAIM);
832 	mob->setIntrinsic(INTRINSIC_SKILL_CHARGE);
833 	mob->setIntrinsic(INTRINSIC_SKILL_LEAPATTACK);
834 	mob->setIntrinsic(INTRINSIC_SKILL_TWOWEAPON);
835 	mob->setIntrinsic(INTRINSIC_SKILL_WEAPON_DISARM);
836 	mob->setIntrinsic(INTRINSIC_SPELL_DISINTEGRATE);
837 	mob->setIntrinsic(INTRINSIC_SPELL_RAISE_UNDEAD);
838 	mob->setIntrinsic(INTRINSIC_SPELL_RECLAIM_SOUL);
839 	mob->setIntrinsic(INTRINSIC_SPELL_DARK_RITUAL);
840 	mob->setIntrinsic(INTRINSIC_SPELL_FORCEWALL);
841 	mob->setIntrinsic(INTRINSIC_SPELL_SUMMON_FAMILIAR);
842 	mob->setIntrinsic(INTRINSIC_SPELL_TRANSFER_KNOWLEDGE);
843 
844 	mob->setIntrinsic(INTRINSIC_REFLECTION);
845 	mob->setIntrinsic(INTRINSIC_JUMP);
846 	mob->myMP = 200;
847 //	mob->myMagicDie = 100;
848 	mob->myHitDie = 8;
849 	mob->myMaxMP = 200;
850         mob->myHP = 100;
851 	mob->myMaxHP = 100;
852     }
853 #endif
854 
855 #if 0
856     for (int i = 0; definition == MOB_AVATAR && i < 90; i++)
857     //for (int i = 0; i < 90; i++)
858     {
859 	// item = ITEM::createRandom();
860 	// We can also create a bunch of items of a given type, useful
861 	// for testing...
862 	item = ITEM::createRandomType(ITEMTYPE_WAND);
863 	mob->acquireItem(item);
864 	item = ITEM::createRandomType(ITEMTYPE_WEAPON);
865 	mob->acquireItem(item);
866     }
867     // mob->myMagicDie = 38;
868     // mob->myHitDie = 38;
869 #endif
870     if (glbStressTest && !glbMapStats && definition == MOB_AVATAR)
871     {
872 	if (rand_chance(80))
873 	{
874 	    mob->buildAscensionKit();
875 	}
876 	else
877 	{
878 	    int		i, n;
879 	    SPELL_NAMES	spell;
880 	    SKILL_NAMES	skill;
881 
882 	    // A much more random character...
883 	    n = 20;
884 	    for (i = 0; i < 20; i++)
885 	    {
886 		item = ITEM::createRandomType(ITEMTYPE_ANY);
887 		mob->acquireItem(item);
888 	    }
889 
890 	    n = rand_range(1, 20);
891 	    for (i = 0; i < n; i++)
892 	    {
893 		spell = (SPELL_NAMES) rand_range(1, NUM_SPELLS-1);
894 		mob->setIntrinsic((INTRINSIC_NAMES)glb_spelldefs[spell].intrinsic);
895 	    }
896 	    n = rand_range(1, 20);
897 	    for (i = 0; i < n; i++)
898 	    {
899 		skill = (SKILL_NAMES) rand_range(1, NUM_SKILLS-1);
900 		mob->setIntrinsic((INTRINSIC_NAMES)glb_skilldefs[skill].intrinsic);
901 	    }
902 	    mob->myMP = rand_range(30, 150);
903 	    mob->myMaxMP = mob->myMP;
904 	    mob->myMagicDie = mob->myMP / 7;
905 	    mob->myHP = rand_range(30, 150);
906 	    mob->myMaxHP = mob->myHP;
907 	    mob->myHitDie = mob->myHP / 7 + 1;
908 	}
909     }
910 
911     // Make everyone do a poly dance.
912     // mob->setIntrinsic(INTRINSIC_POLYMORPH);
913 
914     return mob;
915 }
916 
917 void
changeDefinition(MOB_NAMES definition,bool reset)918 MOB::changeDefinition(MOB_NAMES definition, bool reset)
919 {
920     myDefinition = definition;
921     myOrigDefinition = definition;
922 
923     if (reset)
924     {
925 	myExpLevel = glb_mobdefs[definition].explevel;
926 	myExp = 0;
927 	myHitDie = glb_mobdefs[definition].hitdie * 2;
928 	myHP = rand_dice(glb_mobdefs[definition].hp);
929 	myMaxHP = myHP;
930 	myMagicDie = glb_mobdefs[definition].mpdie * 2;
931 	myMP = rand_dice(glb_mobdefs[definition].mp);
932 	myMaxMP = myMP;
933 	myNoiseLevel = 0;
934 
935 	myIntrinsic.clearAll();
936 	myIntrinsic.loadFromString(glb_mobdefs[definition].intrinsic);
937     }
938 }
939 
940 MOB_NAMES
chooseNPC(int threatlevel)941 MOB::chooseNPC(int threatlevel)
942 {
943     int		percentages[NUM_MOBS];
944     int		total = 0;
945     int		explevel, guess;
946     bool	fuck_the_player = false;
947     MOB_NAMES	def;
948 
949     if (piety_chosengod() == GOD_CULTIST)
950     {
951 	if (rand_choice(100) == 66)
952 	{
953 	    msg_report("><0|V| laughs.");
954 	    fuck_the_player = true;
955 	}
956     }
957     else
958     {
959 	if (rand_choice(5000) == 666)
960 	    fuck_the_player = true;
961     }
962 
963     if (threatlevel < 0)
964 	threatlevel = 0;
965 
966     threatlevel = threatlevel / 2;
967 
968     FOREACH_MOB(def)
969     {
970 	if (def == MOB_NONE)
971 	    percentages[def] = 0;
972 	else if (def == MOB_AVATAR)
973 	    percentages[def] = 0;
974 	else
975 	{
976 	    // The "ExpLevel" is the rough guess of the strength of the mob.
977 	    explevel = glb_mobdefs[def].explevel;
978 
979 	    // We want to create mobs matching our adjusted threatlevel.
980 	    // We do this by biassing the creation.  Anything within two
981 	    // levels gets a value of 65536.  For every level outside
982 	    // that, it is dropped in half, to a minimum of 1.  So
983 	    // you could get all powerful creatures in level 1.
984 	    // This gives a level difference of 18 to mean there isn't
985 	    // a chance in one direction of successful combat.
986 
987 	    // I don't like the above schema very much, a level 1 character
988 	    // has a 1/32 bias against a dragon of any type.  As there
989 	    // is a peak of dragons currnetly, (32 possible monsters, 3
990 	    // are non-green dragons, chance of non-green dragon is
991 	    // one in 341 chance of a dragon.
992 	    // I'd like something 8 levels away to be 1/1000 chance.
993 
994 	    // New system:
995 	    // Things your level have chance 1024.
996 	    // Every level above your level halves chance, for 1 chance
997 	    // at 10 over (7 over on dragons is 1/128, or 8)
998 	    // Below you falls off slower, and halves every two levels.
999 	    // Creatures an odd number below are 75%.
1000 
1001 	    // Screw these schemas.  They are all crap.
1002 	    // The first rule is KEEP IT SIMPLE STUPID.
1003 	    // I obviously am not sufficiently stupid.
1004 	    // The new perfect rule is this:
1005 	    // 1) All mobs less than the threat level have equal chance
1006 	    //    of spawning.
1007 	    // 2) One higher has 50%.  Two higher 10%.
1008 	    // 3) More than two higher?  0%.
1009 	    // If the fuck_the_player flag is set, all mobs are equally likely.
1010 	    if (explevel <= threatlevel)
1011 		percentages[def] = 10;
1012 	    else if (explevel <= threatlevel+1)
1013 		percentages[def] = 5;
1014 	    else if (explevel <= threatlevel+2)
1015 		percentages[def] = 1;
1016 	    else
1017 		percentages[def] = 0;
1018 
1019 	    if (fuck_the_player)
1020 		percentages[def] = 1;
1021 #if 0
1022 	    // You know... I think I have the following set entirely backwards.
1023 	    // That likely explains why it sucked so much :>
1024 	    if (threatlevel == explevel)
1025 		percentages[def] = 1024;
1026 	    else if (threatlevel < explevel)
1027 	    {
1028 		int		odd;
1029 
1030 		diff = explevel - threatlevel - 1;
1031 		if (diff < 0)
1032 		{
1033 		    percentages[def] = 1024;
1034 		}
1035 		else if (diff >= 20)
1036 		{
1037 		    percentages[def] = 1;
1038 		}
1039 		else
1040 		{
1041 		    odd = diff & 1;
1042 		    diff = (diff + 1) >> 1;
1043 		    percentages[def] = 1 << (10 - diff);
1044 		    if (odd)
1045 			percentages[def] += percentages[def] >> 1;
1046 		}
1047 	    }
1048 	    else
1049 	    {
1050 		diff = threatlevel - explevel;
1051 		if (diff >= 10)
1052 		    percentages[def] = 1;
1053 		else
1054 		    percentages[def] = 1 << (10 - diff);
1055 	    }
1056 #endif
1057 #if 0
1058 	    if (abs(threatlevel - explevel) <= 2)
1059 		percentages[def] = 65536;
1060 	    else
1061 	    {
1062 		diff = abs(threatlevel - explevel) - 2;
1063 		if (diff > 16)
1064 		    percentages[def] = 1;
1065 		else
1066 		    percentages[def] = 1 << (16 - diff);
1067 	    }
1068 #endif
1069 
1070 	    // The percentage is then modulated by the rarity.  100 is
1071 	    // common, smaller numbers less common.
1072 	    // This can reduce the percentage to 0.
1073 	    percentages[def] *= glb_mobdefs[def].rarity;
1074 	}
1075 #if 0
1076 	BUF		buf;
1077 	buf.sprintf("%s - %d: %d.  ",
1078 		glb_mobdefs[def].name, def, percentages[def]);
1079 	msg_report(buf);
1080 #endif
1081 
1082 	total += percentages[def];
1083     }
1084 
1085     guess = rand_choice(total);
1086 
1087 #if 0
1088     {
1089 	BUF		buf;
1090 	buf.sprintf("%d of %d, ", guess, total);
1091 	msg_report(buf);
1092     }
1093 #endif
1094 
1095     total = 0;
1096     // NB: We could store the sum in percentages rather than the individual
1097     // value, then do a binary search here.  (or even phonebook search!)
1098     FOREACH_MOB(def)
1099     {
1100 	total += percentages[def];
1101 	if (total > guess)
1102 	    break;
1103     }
1104 #if 0
1105     {
1106 	BUF		buf;
1107 	buf.sprintf("%d.  ", def);
1108 	msg_report(buf);
1109     }
1110 #endif
1111 
1112     if (def == NUM_MOBS)
1113     {
1114 	// Ran through wihtout a match, quite a bad case...
1115 	UT_ASSERT(!"Didn't find a mob!");
1116 	def = (MOB_NAMES) (def-1);
1117     }
1118 
1119     // Note we CANNOT hard code to a single MOB as then
1120     // self-poly prevention triggers an infinite loop!
1121 #if 0
1122     if (rand_choice(2))
1123         def =  MOB_GELATINOUSCUBE;
1124     else
1125 	def =  MOB_GOLDBEETLE;
1126 #endif
1127 
1128     return def;
1129 }
1130 
1131 MOB *
createNPC(int threatlevel)1132 MOB::createNPC(int threatlevel)
1133 {
1134     MOB_NAMES	def;
1135 
1136     def = chooseNPC(threatlevel);
1137 
1138     return create(def);
1139 }
1140 
1141 void
makeUnique()1142 MOB::makeUnique()
1143 {
1144     // Build the unique name.
1145     const char *syllable[] =
1146     {
1147 	"gor",
1148 	"blad",
1149 	"ret",
1150 	"waz",
1151 	"por",
1152 	"ylem",
1153 	"kral",
1154 	"qual",
1155 	"t'rl",
1156 	"zam",
1157 	"jon",
1158 	"eri",
1159 	"opi",
1160 	"hjur",
1161 	0
1162     };
1163     int		i, n;
1164     BUF		buf;
1165 
1166     setIntrinsic(INTRINSIC_UNIQUE);
1167 
1168     for (n = 0; syllable[n]; n++);
1169 
1170     buf.strcpy(syllable[rand_choice(n)]);
1171     if (rand_chance(90))
1172 	buf.strcat(syllable[rand_choice(n)]);
1173 
1174     christen(buf);
1175 
1176     // Buf the mob.
1177     myMaxHP = myMaxHP * 2 + 20;
1178     myHP = myMaxHP;
1179     if (myMP)
1180     {
1181 	myMaxMP = myMaxMP * 2 + 40;
1182 	myMP = myMaxMP;
1183     }
1184 
1185     myHitDie = myHitDie * 2;
1186     myMagicDie = myMagicDie * 2;
1187     myExpLevel = myExpLevel * 2;
1188 
1189     // We get extra bonus loot!
1190     i = 0;
1191 
1192     while (i < 5)
1193     {
1194 	ITEM		*loot;
1195 	loot = ITEM::createRandom();
1196 	acquireItem(loot);
1197 	i++;
1198 	if (rand_chance(30))
1199 	    break;
1200     }
1201 
1202     // We get bonus intrinsics.
1203     INTRINSIC_NAMES		intrinsics[] =
1204     {
1205 	INTRINSIC_QUICK,
1206 	INTRINSIC_UNCHANGING,
1207 	INTRINSIC_RESISTSTONING,
1208 	INTRINSIC_RESISTFIRE,
1209 	INTRINSIC_RESISTCOLD,
1210 	INTRINSIC_RESISTSHOCK,
1211 	INTRINSIC_RESISTACID,
1212 	INTRINSIC_RESISTPOISON,
1213 	INTRINSIC_RESISTPHYSICAL,
1214 	INTRINSIC_TELEPORTCONTROL,
1215 	INTRINSIC_REGENERATION,
1216 	INTRINSIC_REFLECTION,
1217 	INTRINSIC_SEARCH,	// In someways, this is bad :>
1218 	INTRINSIC_INVISIBLE,
1219 	INTRINSIC_SEEINVISIBLE,
1220 	INTRINSIC_TELEPATHY,
1221 	INTRINSIC_NOBREATH,
1222 	INTRINSIC_LIFESAVE,	// This really sucks on a creature.
1223 				// Got axed by a red dragon unique
1224 				// with this.
1225 	INTRINSIC_RESISTSLEEP,
1226 	INTRINSIC_FREEDOM,
1227 	INTRINSIC_WATERWALK,
1228 	INTRINSIC_JUMP,
1229 	INTRINSIC_WARNING,
1230 	INTRINSIC_LICHFORM,	// I look forward to this triggering!
1231 				// This has proven the most fun thing for
1232 				// people to discover!
1233 	INTRINSIC_DIG,		// Likely nerfed as the ai doesn't wander
1234 				// into walls
1235 	INTRINSIC_NONE
1236     };
1237 
1238     for (n = 0; intrinsics[n] != INTRINSIC_NONE; n++);
1239 
1240     setIntrinsic(intrinsics[rand_choice(n)]);
1241 }
1242 
1243 MOVE_NAMES
getMoveType() const1244 MOB::getMoveType() const
1245 {
1246     MOVE_NAMES		movetype;
1247 
1248     movetype = (MOVE_NAMES) glb_mobdefs[myDefinition].movetype;
1249 
1250     // The avatar is a special case: They are allowed to
1251     // jump in water even though they can't swim.
1252     if (isAvatar())
1253     {
1254 	movetype = (MOVE_NAMES) (movetype | MOVE_STD_SWIM);
1255     }
1256     else if (hasIntrinsic(INTRINSIC_WATERWALK))
1257     {
1258 	// If we have water walking and are not the avatar, we can swim
1259 	movetype = (MOVE_NAMES) (movetype | MOVE_STD_SWIM);
1260     }
1261 
1262     return movetype;
1263 }
1264 
1265 bool
canMove(int nx,int ny,bool checkitem,bool allowdoor,bool checksafety) const1266 MOB::canMove(int nx, int ny, bool checkitem, bool allowdoor, bool checksafety) const
1267 {
1268     int		tile;
1269 
1270     if (nx < 0 || ny < 0 || nx >= MAP_WIDTH || ny >= MAP_HEIGHT)
1271 	return false;
1272 
1273     if (getSize() >= SIZE_GARGANTUAN)
1274 	if (nx >= MAP_WIDTH-1 || ny >= MAP_HEIGHT-1)
1275 	    return false;
1276 
1277     // Check for items.
1278     if (checkitem)
1279     {
1280 	ITEM		*item;
1281 
1282 	item = glbCurLevel->getItem(nx, ny);
1283 	if (item && !item->isPassable())
1284 	    return false;
1285     }
1286 
1287     // Get the relevant tile...
1288     tile = glbCurLevel->getTile(nx, ny);
1289     if ( (glb_squaredefs[tile].movetype & getMoveType()) ||
1290 	 (allowdoor && tile == SQUARE_DOOR) ||
1291 	 (allowdoor && isAvatar() && tile == SQUARE_BLOCKEDDOOR))
1292     {
1293 	// Easy as pie...
1294 	if (getSize() >= SIZE_GARGANTUAN)
1295 	{
1296 	    if ( (glb_squaredefs[glbCurLevel->getTile(nx+1,ny)].movetype & getMoveType()) &&
1297 	         (glb_squaredefs[glbCurLevel->getTile(nx+1,ny+1)].movetype & getMoveType()) &&
1298 	         (glb_squaredefs[glbCurLevel->getTile(nx,ny+1)].movetype & getMoveType()) )
1299 	    {
1300 		// fall through
1301 	    }
1302 	    else
1303 		return false;
1304 	}
1305     }
1306     else
1307 	return false;
1308 
1309     if (checksafety)
1310     {
1311 	// Prohibit moving to tiles that will damage us.
1312 	switch (tile)
1313 	{
1314 	    case SQUARE_LAVA:
1315 		if (!hasIntrinsic(INTRINSIC_RESISTFIRE) ||
1316 		     hasIntrinsic(INTRINSIC_VULNFIRE))
1317 		    return false;
1318 		break;
1319 	    case SQUARE_ACID:
1320 		// Flying critters are safe from acid,
1321 		if (!(getMoveType() & MOVE_FLY))
1322 		{
1323 		    // Verify we actually care about acid damage
1324 		    if (!hasIntrinsic(INTRINSIC_RESISTACID) ||
1325 			 hasIntrinsic(INTRINSIC_VULNACID))
1326 		    {
1327 			return false;
1328 		    }
1329 		}
1330 		break;
1331 	    case SQUARE_STARS1:
1332 	    case SQUARE_STARS2:
1333 	    case SQUARE_STARS3:
1334 	    case SQUARE_STARS4:
1335 	    case SQUARE_STARS5:
1336 		if (!hasIntrinsic(INTRINSIC_NOBREATH))
1337 		    return false;
1338 		break;
1339 	}
1340     }
1341     return true;
1342 }
1343 
1344 bool
canMoveDelta(int dx,int dy,bool checkitem,bool allowdoor,bool checksafety) const1345 MOB::canMoveDelta(int dx, int dy, bool checkitem, bool allowdoor, bool checksafety) const
1346 {
1347     if (!dx || !dy)
1348 	return canMove(getX()+dx, getY()+dy, checkitem, allowdoor, checksafety);
1349 
1350     // Verify either ortho directions satisfy regular movement.
1351     if (!canMove(getX()+dx, getY(), false, false, false) &&
1352         !canMove(getX(), getY()+dy, false, false, false))
1353     {
1354 	return false;
1355     }
1356 
1357     // Now go back to regular canMove
1358     return canMove(getX()+dx, getY()+dy, checkitem, allowdoor, checksafety);
1359 }
1360 
1361 bool
canSense(const MOB * mob,bool checklos,bool onlysight) const1362 MOB::canSense(const MOB *mob, bool checklos, bool onlysight) const
1363 {
1364     // We try to do these tests from the fastest to slowest.
1365 
1366     // This fails from ::refresh for no readily apparent reason!
1367     // UT_ASSERT(mob != 0);
1368 
1369     int			dist, distx, disty;
1370 
1371     // A quick sanity test on the mob.
1372     if (!mob)
1373 	return false;
1374 
1375     bool		thisdoublesize = getSize() >= SIZE_GARGANTUAN;
1376     bool		mobdoublesize = mob->getSize() >= SIZE_GARGANTUAN;
1377 
1378     // Obviously, different levels are illegal.  Either falling
1379     // through a trap door, or perhaps -1 because it has not
1380     // yet been created.
1381     if (mob->getDLevel() != getDLevel())
1382 	return false;
1383 
1384     // Mobs start with -1 position, which should always be out of sight.
1385     if (mob->getX() == -1 || mob->getX() == 255)
1386 	return false;
1387 
1388     distx = abs(getX() - mob->getX());
1389     disty = abs(getY() - mob->getY());
1390     dist = distx + disty;
1391 
1392     // See if the target has position revealed, in which case
1393     // everyone gets to see them.
1394     if (!onlysight && mob->hasIntrinsic(INTRINSIC_POSITIONREVEALED))
1395     {
1396 	return true;
1397     }
1398 
1399     // See if we can hear our target.
1400     if (!onlysight && !hasIntrinsic(INTRINSIC_DEAF))
1401     {
1402 	// You hear the monster.
1403 	// You are not allowed to hear yourself!
1404 	// Hearing is a tricky business, you don't always hear things.
1405 	if (dist && dist <= mob->getNoiseLevel(this))
1406 	    return true;
1407     }
1408 
1409     if (!onlysight && mob->isSlave(this))
1410     {
1411 	if (hasIntrinsic(INTRINSIC_TELEPATHY))
1412 	    return true;
1413     }
1414 
1415     if (!onlysight && hasIntrinsic(INTRINSIC_WARNING))
1416     {
1417 	if (dist < 5)
1418 	    return true;
1419     }
1420 
1421     // First, if this is blind, we can see iff we have telepathy and
1422     // the target has a mind...
1423     if (hasIntrinsic(INTRINSIC_BLIND))
1424     {
1425 	if (onlysight)
1426 	    return false;
1427 
1428 	if (mob->getSmarts() != 0 &&
1429 	    hasIntrinsic(INTRINSIC_TELEPATHY))
1430 	{
1431 	    return true;
1432 	}
1433 
1434 	// Being blind doesn't stop you from seeing yourself.
1435 	if (mob == this)
1436 	   return true;
1437 
1438 	return false;
1439     }
1440 
1441     // From here on are only sight checks.
1442 
1443     // Now, if the target is invisible, make sure we can see invisible
1444     // or we can't possibly sense.
1445     if (mob->hasIntrinsic(INTRINSIC_INVISIBLE))
1446     {
1447 	if (!hasIntrinsic(INTRINSIC_SEEINVISIBLE))
1448 	    return false;
1449     }
1450 
1451     // We could potentially see the mob in the usual fashion.
1452     // We now check to see if the mob is lit...
1453     // We can always be aware of mobs beside us even in the dark.
1454     if (mob != this &&
1455 	!mobdoublesize &&
1456 	(MAX(distx, disty) > 1) &&
1457 	!glbCurLevel->isLit(mob->getX(), mob->getY()))
1458 	return false;
1459 
1460     // Giant mobs have looser is lit requirements.
1461     if (mob != this &&
1462 	 mobdoublesize &&
1463 	 (MAX(distx, disty) > 1) &&
1464 	!glbCurLevel->isLit(mob->getX(), mob->getY()) &&
1465 	!glbCurLevel->isLit(mob->getX()+1, mob->getY()) &&
1466 	!glbCurLevel->isLit(mob->getX(), mob->getY()+1) &&
1467 	!glbCurLevel->isLit(mob->getX()+1, mob->getY()+1) )
1468 	return false;
1469 
1470     // If either of the mobs is in a pit, we can only see each other
1471     // if one square away.
1472     if (mob->hasIntrinsic(INTRINSIC_INPIT) || hasIntrinsic(INTRINSIC_INPIT))
1473     {
1474 	if (MAX(distx, disty) > 1)
1475 	{
1476 	    return false;
1477 	}
1478     }
1479 
1480     // If target is in tree, but we are not, only visible at one
1481     // square
1482     if (mob->hasIntrinsic(INTRINSIC_INTREE) && !hasIntrinsic(INTRINSIC_INTREE))
1483     {
1484 	if (MAX(distx, disty) > 1)
1485 	{
1486 	    return false;
1487 	}
1488     }
1489 
1490     // If one of the mobs is submerged and the other is not, there is
1491     // no way to see.
1492     if (mob->hasIntrinsic(INTRINSIC_SUBMERGED) ^ hasIntrinsic(INTRINSIC_SUBMERGED))
1493     {
1494 	return false;
1495     }
1496 
1497     if (!checklos)
1498 	return true;
1499 
1500     // Finally, ensure we have LOS.
1501     UT_ASSERT(mob->getX() >= 0 && mob->getX() < MAP_WIDTH);
1502     UT_ASSERT(mob->getY() >= 0 && mob->getY() < MAP_HEIGHT);
1503     if (glbCurLevel->hasLOS(getX(), getY(), mob->getX(), mob->getY()))
1504     {
1505 	return true;
1506     }
1507     // If we are huge, we gotta check rest of the mob..
1508     if (thisdoublesize &&
1509 	(glbCurLevel->hasLOS(getX()+1, getY(), mob->getX(), mob->getY()) ||
1510 	 glbCurLevel->hasLOS(getX(), getY()+1, mob->getX(), mob->getY()) ||
1511 	 glbCurLevel->hasLOS(getX()+1, getY()+1, mob->getX(), mob->getY())) )
1512 	return true;
1513     // If target is huge, gotta check the rest of the mob.
1514     if (mobdoublesize &&
1515 	(glbCurLevel->hasLOS(getX(), getY(), mob->getX()+1, mob->getY()) ||
1516 	 glbCurLevel->hasLOS(getX(), getY(), mob->getX(), mob->getY()+1) ||
1517 	 glbCurLevel->hasLOS(getX(), getY(), mob->getX()+1, mob->getY()+1)) )
1518 	return true;
1519     // No LOS, no sight...
1520     return false;
1521 }
1522 
1523 SENSE_NAMES
getSenseType(const MOB * mob) const1524 MOB::getSenseType(const MOB *mob) const
1525 {
1526     // This is a bit expensive as we have to address
1527     // the senses from the most informative to the least.
1528 
1529     // This fails for no reason from refresh...
1530     //UT_ASSERT(mob != 0);
1531 
1532     // A quick sanity test on the mob.
1533     if (!mob)
1534 	return SENSE_NONE;
1535 
1536     // Obviously, different levels are illegal.  Either falling
1537     // through a trap door, or perhaps -1 because it has not
1538     // yet been created.
1539     if (mob->getDLevel() != getDLevel())
1540 	return SENSE_NONE;
1541 
1542     // Mobs start with -1 position, which should always be out of sight.
1543     if (mob->getX() == -1 || mob->getX() == 255)
1544 	return SENSE_NONE;
1545 
1546     // Check for boring sensing.
1547     if (canSense(mob, true, true))
1548 	return SENSE_SIGHT;
1549 
1550     // First, if this is blind, we can see iff we have telepathy and
1551     // the target has a mind...
1552     if (hasIntrinsic(INTRINSIC_BLIND))
1553     {
1554 	if (mob->getSmarts() != 0 &&
1555 	    hasIntrinsic(INTRINSIC_TELEPATHY))
1556 	{
1557 	    return SENSE_ESP;
1558 	}
1559 
1560 	// Being blind doesn't stop you from seeing yourself.
1561 	if (mob == this)
1562 	   return SENSE_ESP;
1563     }
1564 
1565     // If the target has position revealed, we get to sense
1566     // the target via ESP.
1567     if (mob->hasIntrinsic(INTRINSIC_POSITIONREVEALED))
1568     {
1569 	return SENSE_ESP;
1570     }
1571 
1572     // If the target is our slave and we have telepathy we get to sense
1573     // the target via ESP.
1574     if (mob->isSlave(this))
1575     {
1576 	if (hasIntrinsic(INTRINSIC_TELEPATHY))
1577 	    return SENSE_ESP;
1578     }
1579 
1580     int			dist;
1581 
1582     dist = abs(getX() - mob->getX()) + abs(getY() - mob->getY());
1583 
1584     // See if we can hear our target.
1585     if (!hasIntrinsic(INTRINSIC_DEAF))
1586     {
1587 	// You hear the monster.
1588 	// You are not allowed to hear yourself!
1589 	// Hearing is a tricky business, you don't always hear things.
1590 	if (dist && dist <= mob->getNoiseLevel(this))
1591 	    return SENSE_HEAR;
1592     }
1593 
1594     // See if warning kicks in.
1595     if (hasIntrinsic(INTRINSIC_WARNING))
1596     {
1597 	if (dist < 5)
1598 	    return SENSE_WARN;
1599     }
1600 
1601     return SENSE_NONE;
1602 }
1603 
1604 void
makeNoise(int noiselevel)1605 MOB::makeNoise(int noiselevel)
1606 {
1607     // You don't always gain the full noiselevel.
1608     // The general theory is that:
1609     // 1) Your noiselevel should be at least the maximum
1610     //    individual noiselevel
1611     // 2) Additional noise sources have some effect.
1612     //
1613     // We want a system where a + a = a + 1.
1614     // The chance of going up is thus 1 in 1 << |a - b|
1615 
1616     pietyMakeNoise(noiselevel);
1617 
1618     // Ensure myNoiseLevel is the maximum of the two.
1619     if (myNoiseLevel < noiselevel)
1620     {
1621 	int		tmp;
1622 	tmp = myNoiseLevel;
1623 	myNoiseLevel = noiselevel;
1624 	noiselevel = tmp;
1625     }
1626 
1627     if (noiselevel)
1628     {
1629 	if (!rand_choice( 1 << (myNoiseLevel - noiselevel) ))
1630 	    myNoiseLevel++;
1631 
1632 	// Clamp the max noise level.
1633 	// No matter how unlikely, overflow errors are embarrassing.
1634 	if (myNoiseLevel > 250)
1635 	    myNoiseLevel = 250;
1636     }
1637 }
1638 
1639 void
makeEquipNoise(ITEMSLOT_NAMES slot)1640 MOB::makeEquipNoise(ITEMSLOT_NAMES slot)
1641 {
1642     ITEM		*item;
1643 
1644     item = getEquippedItem(slot);
1645     if (item)
1646     {
1647 	makeNoise(item->getNoiseLevel());
1648     }
1649 }
1650 
1651 int
getNoiseLevel(const MOB * observer) const1652 MOB::getNoiseLevel(const MOB *observer) const
1653 {
1654     if (!observer)
1655 	return myNoiseLevel;
1656 
1657     if (!myNoiseLevel)
1658 	return 0;
1659 
1660     // Find a hash.
1661     u32			hash;
1662 
1663     MOBREF		thisref, obref;
1664 
1665     thisref.setMob(this);
1666     obref.setMob(observer);
1667 
1668     hash = thisref.getId();
1669     hash = (hash << 16) | (hash >> 16);
1670     hash ^= obref.getId();
1671 
1672     hash ^= speed_gettime() * 2654435761U;	// gold
1673 
1674     // Now use hash to find a random number.  We do this by wang hashing
1675     // it and then modulating by the noise level + 1.
1676     // http://www.concentric.net/~Ttwang/tech/inthash.htm
1677     hash += ~(hash << 15);
1678     hash ^=  (hash >> 10);
1679     hash +=  (hash << 3);
1680     hash ^=  (hash >> 6);
1681     hash += ~(hash << 11);
1682     hash ^=  (hash >> 16);
1683 
1684     // Modulus.
1685     hash %= myNoiseLevel + 1;
1686 
1687     return hash;
1688 }
1689 
1690 int
getTile() const1691 MOB::getTile() const
1692 {
1693     return glb_mobdefs[myDefinition].tile;
1694 }
1695 
1696 int
getTileLL() const1697 MOB::getTileLL() const
1698 {
1699     return glb_mobdefs[myDefinition].tilell;
1700 }
1701 
1702 int
getTileUR() const1703 MOB::getTileUR() const
1704 {
1705     return glb_mobdefs[myDefinition].tileur;
1706 }
1707 
1708 int
getTileLR() const1709 MOB::getTileLR() const
1710 {
1711     return glb_mobdefs[myDefinition].tilelr;
1712 }
1713 
1714 void
incrementMaxHP(int amount)1715 MOB::incrementMaxHP(int amount)
1716 {
1717     MOB		*base;
1718 
1719     // If we our polyed, our base form also gets the bonus/malus.
1720     base = myBaseType.getMob();
1721     if (base)
1722 	base->incrementMaxHP(amount);
1723 
1724     if (amount < 0)
1725     {
1726 	// do not allow max hp to go negative.
1727 	if (myMaxHP + amount < 0)
1728 	    myMaxHP = 1;
1729 	else
1730 	    myMaxHP += amount;
1731 
1732 	// Reduce our HP if necessary.
1733 	if (myHP > myMaxHP)
1734 	    myHP = myMaxHP;
1735     }
1736     else
1737     {
1738 	// Avoid embarrassing overflows.
1739 	if (myMaxHP + amount > 30000)
1740 	{
1741 	    amount = 30000 - myMaxHP;
1742 	}
1743 	// Both our HP and maxHP get boosted.
1744 	myMaxHP += amount;
1745 	myHP += amount;
1746     }
1747 }
1748 
1749 void
incrementMaxMP(int amount)1750 MOB::incrementMaxMP(int amount)
1751 {
1752     MOB		*base;
1753 
1754     // If we our polyed, our base form also gets the bonus/malus.
1755     base = myBaseType.getMob();
1756     if (base)
1757 	base->incrementMaxMP(amount);
1758 
1759     if (amount < 0)
1760     {
1761 	// do not allow max hp to go negative.
1762 	if (myMaxMP - amount < 0)
1763 	    myMaxMP = 1;
1764 	else
1765 	    myMaxMP -= amount;
1766 
1767 	// Reduce our MP if necessary.
1768 	if (myMP > myMaxMP)
1769 	    myMP = myMaxMP;
1770     }
1771     else
1772     {
1773 	// Avoid embarrassing overflows.
1774 	if (myMaxMP + amount > 30000)
1775 	{
1776 	    amount = 30000 - myMaxMP;
1777 	}
1778 	// Both our MP and maxMP get boosted.
1779 	myMaxMP += amount;
1780 	myMP += amount;
1781     }
1782 }
1783 
1784 bool
move(int nx,int ny,bool interlevel)1785 MOB::move(int nx, int ny, bool interlevel)
1786 {
1787     UT_ASSERT(nx >= 0 && nx < MAP_WIDTH);
1788     UT_ASSERT(ny >= 0 && ny < MAP_HEIGHT);
1789     if (interlevel || (myX != nx) || (myY != ny))
1790     {
1791 	// Any successful move will clear off our in pit flag.
1792 	clearIntrinsic(INTRINSIC_INPIT);
1793 
1794 	// If the new square isn't a forest, clear the tree flag
1795 	if (glbCurLevel &&
1796 	    (getDLevel() == glbCurLevel->getDepth()))
1797 	{
1798 	    if (!glb_squaredefs[glbCurLevel->getTileSafe(nx, ny)].isforest)
1799 	    {
1800 		if (hasIntrinsic(INTRINSIC_INTREE))
1801 		{
1802 		    formatAndReport("%U <fall> to the ground.");
1803 		    clearIntrinsic(INTRINSIC_INTREE);
1804 		}
1805 	    }
1806 	}
1807 	else	// Don't track this for other levels.
1808 	    clearIntrinsic(INTRINSIC_INTREE);
1809 
1810 	// Any successful move of the avatar sets the global avatar
1811 	// fresh move flag.
1812 	if (isAvatar())
1813 	    setAvatarOnFreshSquare(true);
1814     }
1815 
1816     if (interlevel)
1817     {
1818 	// And our submerged...
1819 	clearIntrinsic(INTRINSIC_SUBMERGED);
1820     }
1821 
1822     // Update the map to reflect the mob's movement:
1823     if (glbCurLevel &&
1824 	(getDLevel() == glbCurLevel->getDepth()))
1825     {
1826 	glbCurLevel->moveMob(this, myX, myY, nx, ny);
1827     }
1828 
1829     myX = nx;
1830     myY = ny;
1831 
1832     // We now check to see if, as a result of our move,
1833     // we encountered any nasty surprises.
1834     // We only do this if we are not teleporting between levels (as
1835     // then we don't know if glbCurLevel will be the right place
1836     // to find our tile!)
1837     if (!interlevel)
1838     {
1839 	// We always implicitly search the square we stand on.  This
1840 	// will also cause us to do the trap detection, and thus
1841 	// be able to fall into the newly found trap :>
1842 	// This may be weird as less alert people are less likely
1843 	// to trigger traps, but we can retcon it by noting less alert
1844 	// implies less curious, thus less likely to say: "What does
1845 	// this switch do?"
1846 	// However, we balance it by forcing the trap to work if the
1847 	// user found it the hard way :>
1848 	// Note that searchSquare will call dropMobs with forcetrap
1849 	// if a new trap is found.  Thus new traps will have a double
1850 	// jeopordy, though the second case is likely irrelevant as if
1851 	// you survive forcetrap, you are immune.
1852 	// That being said, you _will_ get spam from the second drop.
1853 	// Thus, if the search found something, we don't trigger dropMobs.
1854 	if (searchSquare(nx, ny, false))
1855 	    return true;
1856 
1857 	return glbCurLevel->dropMobs(nx, ny, false, this);
1858     }
1859 
1860     // The mob lived!
1861     return true;
1862 }
1863 
1864 ATTITUDE_NAMES
getAttitude(const MOB * appraisee) const1865 MOB::getAttitude(const MOB *appraisee) const
1866 {
1867     // Everyone likes themselves :>
1868     if (appraisee == this)
1869 	return ATTITUDE_FRIENDLY;
1870 
1871     MOB		*master;
1872     // If the appraisee is possessed by ourselves, it is friendly.
1873     if (appraisee->hasIntrinsic(INTRINSIC_POSSESSED))
1874     {
1875 	master = appraisee->getInflictorOfIntrinsic(INTRINSIC_POSSESSED);
1876 	if (master == this)
1877 	    return ATTITUDE_FRIENDLY;
1878     }
1879 
1880     master = getMaster();
1881     if (master)
1882     {
1883 	// Check for FSM state.
1884 	if (myAIFSM == AI_FSM_ATTACK)
1885 	{
1886 	    if (appraisee == getAITarget())
1887 	    {
1888 		// Check for a common master.  We don't want
1889 		// infighting!
1890 		if (hasCommonMaster(appraisee))
1891 		{
1892 		    // It would make sense to clear the ai target
1893 		    // but that is non const.
1894 		    return ATTITUDE_FRIENDLY;
1895 		}
1896 		else
1897 		    return ATTITUDE_HOSTILE;
1898 	    }
1899 	}
1900 	// Yesh mashter...
1901 	return master->getAttitude(appraisee);
1902     }
1903 
1904     // If I am the avatar, I have the opinion of the other guy.
1905     // This way we are friendly to friendlies, and hate hatreds.
1906     if (isAvatar())
1907 	return appraisee->getAttitude(this);
1908 
1909     // If they are our target to kill, clearly we don't like them.
1910     if (appraisee == getAITarget())
1911 	return ATTITUDE_HOSTILE;
1912 
1913     // Now, base it off our ai...
1914     int		ai = glb_mobdefs[myDefinition].ai;
1915 
1916     // Special case greedy gold beetles.
1917     if (getDefinition() == MOB_GOLDBEETLE)
1918     {
1919 	if (appraisee->isWearing(MATERIAL_GOLD))
1920 	    return ATTITUDE_HOSTILE;
1921     }
1922 
1923     if (getDefinition() == MOB_VAMPIREBAT)
1924     {
1925 	// Not a good source of food?  Ignore.
1926 	if (appraisee->isBloodless())
1927 	{
1928 	    return ATTITUDE_NEUTRAL;
1929 	}
1930     }
1931 
1932     if (glb_aidefs[ai].attackavatar && appraisee->isAvatar())
1933 	return ATTITUDE_HOSTILE;
1934 
1935     if (!glb_aidefs[ai].attacktype)
1936     {
1937 	// Determine if other is of our mobtype.  If so, friendly.
1938 	// If not, go to speciest.
1939 	// NOTE: UNUSUAL mob types do not ally as this is a catch
1940 	// all for one offs!
1941 	if (glb_mobdefs[appraisee->myDefinition].mobtype != MOBTYPE_UNUSUAL)
1942 	{
1943 	    if (glb_mobdefs[appraisee->myDefinition].mobtype ==
1944 		glb_mobdefs[myDefinition].mobtype)
1945 	    {
1946 		return ATTITUDE_FRIENDLY;
1947 	    }
1948 	}
1949     }
1950     if (!glb_aidefs[ai].attackspecies)
1951     {
1952 	// Determine if other is our species.  If so, friendly.
1953 	// Otherwise, neutral.
1954 	if (appraisee->myDefinition == myDefinition)
1955 	    return ATTITUDE_FRIENDLY;
1956     }
1957     // It is an alien, return based on that...
1958     if (!glb_aidefs[ai].attackalien)
1959 	return ATTITUDE_FRIENDLY;
1960 
1961     return ATTITUDE_NEUTRAL;
1962 }
1963 
1964 int
calculateFoesSurrounding() const1965 MOB::calculateFoesSurrounding() const
1966 {
1967     // Check surrounding squares for enemies:
1968     int		numenemy = 0;
1969     int		dx, dy;
1970 
1971     FORALL_4DIR(dx, dy)
1972     {
1973 	MOB		*mob;
1974 
1975 	mob = glbCurLevel->getMob(getX()+dx, getY()+dy);
1976 
1977 	if (mob && mob->getAttitude(this) == ATTITUDE_HOSTILE
1978 		&& !mob->hasIntrinsic(INTRINSIC_ASLEEP)
1979 		&& !mob->hasIntrinsic(INTRINSIC_PARALYSED))
1980 	{
1981 	    numenemy++;
1982 	}
1983     }
1984 
1985     return numenemy;
1986 }
1987 
1988 bool
doHeartbeat()1989 MOB::doHeartbeat()
1990 {
1991     // Remove intrinsics...
1992     bool			 lived = true;
1993     bool			 unchanging;
1994     INTRINSIC_COUNTER		*counter, *next, *last;
1995 #ifdef STRESS_TEST
1996     INTRINSIC_COUNTER		*ctest;
1997 #endif
1998     SQUARE_NAMES		 tile;
1999     ITEM			*item;
2000     POISON_NAMES		 poison;
2001     MOBREF			 myself;
2002 
2003     // Determine if we are in a proper phase.
2004     if (!speed_isheartbeatphase())
2005 	return true;
2006 
2007     // Determine what sort of square we are on
2008     tile = glbCurLevel->getTile(getX(), getY());
2009 
2010     // Manually test if we are about to lose possesed status
2011     // as it is too complicated to do later.
2012     if (hasIntrinsic(INTRINSIC_POSSESSED))
2013     {
2014 	counter = getCounter(INTRINSIC_POSSESSED);
2015 	if (counter && counter->myTurns <= 1)
2016 	{
2017 	    // About to expire, so trigger by hand
2018 	    actionReleasePossession();
2019 	}
2020     }
2021 
2022     // Decay all knowledge of ourselves & clear up any null pointers
2023     aiDecayKnowledgeBase();
2024 
2025     myself.setMob(this);
2026 
2027     // Reset all of our accumulated noise.
2028     myNoiseLevel = 0;
2029     // Get the insta-noise from equipment, etc.
2030     // Footwear, shields, and weapons only make noise on use.
2031     for (item = myInventory; item; item = item->getNext())
2032     {
2033 	if (item->getX() == 0)
2034 	{
2035 	    switch ((ITEMSLOT_NAMES) item->getY())
2036 	    {
2037 		case ITEMSLOT_HEAD:
2038 		case ITEMSLOT_AMULET:
2039 		case ITEMSLOT_LRING:
2040 		case ITEMSLOT_RRING:
2041 		case ITEMSLOT_BODY:
2042 		    makeNoise(item->getNoiseLevel());
2043 		    break;
2044 		case ITEMSLOT_LHAND:
2045 		case ITEMSLOT_RHAND:
2046 		case ITEMSLOT_FEET:
2047 		case NUM_ITEMSLOTS:
2048 		    break;
2049 	    }
2050 	}
2051     }
2052     // Check for noisy intrinsic.
2053     if (hasIntrinsic(INTRINSIC_NOISY))
2054 	makeNoise(3);
2055     // Add our creature base noise.
2056     makeNoise(glb_mobdefs[getDefinition()].noise);
2057 
2058     // If we have a missing finger, we should drop a ring.
2059     if (hasIntrinsic(INTRINSIC_MISSINGFINGER))
2060 	dropOneRing();
2061 
2062     // Check to see if the avatar will wake us.
2063     if (hasIntrinsic(INTRINSIC_ASLEEP))
2064     {
2065 	int		noisethreshold = 10;
2066 	if (hasIntrinsic(INTRINSIC_TIRED))
2067 	{
2068 	    noisethreshold = 20;
2069 	    // A small chance of clearing the tired intrinsic early!
2070 	    if (!rand_choice(20))
2071 	    {
2072 		clearIntrinsic(INTRINSIC_TIRED);
2073 	    }
2074 	}
2075 	if (isAvatar())
2076 	{
2077 	    // If we are the avatar, we check all other mobs to
2078 	    // see if any of them will wake us.
2079 	    // Technically, all mobs should use this code path,
2080 	    // but we do have to worry about efficiency somewhere.
2081 	    MOB		*mob;
2082 	    for (mob = glbCurLevel->getMobHead(); mob; mob = mob->getNext())
2083 	    {
2084 		// Ignore our own noise!
2085 		if (mob == this)
2086 		    continue;
2087 
2088 		int		noise, dist;
2089 
2090 		noise = getNoiseLevel(mob);
2091 		dist = abs(getX() - mob->getX())
2092 		     + abs(getY() - mob->getY());
2093 
2094 		noise -= dist;
2095 		if (noise > 0)
2096 		{
2097 		    if (rand_choice(noisethreshold) < noise)
2098 		    {
2099 			clearIntrinsic(INTRINSIC_ASLEEP);
2100 			// Can stop the search.
2101 			break;
2102 		    }
2103 		}
2104 	    }
2105 	}
2106 	else if (getAvatar())
2107 	{
2108 	    int		noise, dist;
2109 
2110 	    noise = getAvatar()->getNoiseLevel(this);
2111 	    dist = abs(getAvatar()->getX() - getX())
2112 		 + abs(getAvatar()->getY() - getY());
2113 
2114 	    noise -= dist;
2115 	    if (noise > 0)
2116 	    {
2117 		if (rand_choice(noisethreshold) < noise)
2118 		    clearIntrinsic(INTRINSIC_ASLEEP);
2119 	    }
2120 	}
2121     }
2122 
2123     unchanging = hasIntrinsic(INTRINSIC_UNCHANGING);
2124 
2125     // If we have stoning resist, we auto-clear the stoning intrinsic.
2126     if (hasIntrinsic(INTRINSIC_RESISTSTONING))
2127 	clearIntrinsic(INTRINSIC_STONING);
2128 
2129     // If we have sleep resist, we autoclear sleeping.
2130     if (hasIntrinsic(INTRINSIC_RESISTSLEEP))
2131 	clearIntrinsic(INTRINSIC_ASLEEP);
2132 
2133     // We store what the final poison damage is so we apply it *after*
2134     // processing all the counters.  Any damage dealt has the potential
2135     // of killing but life saving, which clearDeathIntrinsics, which thus
2136     // destroys the counter linked list!
2137     // Note this only allows us to apply one final poison in a single
2138     // heartbeat, but our intention is that one only suffers from
2139     // the most powerful poison, so this isn't an error.
2140     ATTACK_NAMES	finalpoisondamage = ATTACK_NONE;
2141     MOBREF		finalpoisoninflictor;
2142     INTRINSIC_NAMES	finalpoisonintrinsic = INTRINSIC_NONE;
2143     bool		finalpoisonreport = false;
2144     bool		losttame = false;
2145 
2146     last = 0;
2147     for (counter = myCounters; counter; counter = next)
2148     {
2149 	next = counter->myNext;
2150 
2151 	// Special case amnesia and tame:  If the source is dead, cap at five
2152 	// turns.
2153 	if (glb_intrinsicdefs[counter->myIntrinsic].losewheninflictordies)
2154 	{
2155 	    if (!counter->myInflictor.getMob() ||
2156 		(counter->myInflictor.getMob()->hasIntrinsic(INTRINSIC_DEAD)))
2157 	    {
2158 		// We are about to dec the counter, hence 6 rather
2159 		// than 5.
2160 		if (counter->myTurns > 6)
2161 		    counter->myTurns = 6;
2162 	    }
2163 	}
2164 
2165 	// Special case tame.
2166 	// Do not decrement if it is over 10k as we want this sort
2167 	// of tameness to be permament.
2168 	// But what if your pet resurrects you?  They are in a five
2169 	// turn count down, so will then go wild shortly thereafter.
2170 	// We thus rephrase this to mean that we lock tame to 10k so
2171 	// long as the master is alive, so in that currently impossible
2172 	// resurrection scenario the pet will immediately go full-tame.
2173 	if (counter->myIntrinsic == INTRINSIC_TAME)
2174 	{
2175 	    if (counter->myInflictor.getMob() &&
2176 		!counter->myInflictor.getMob()->hasIntrinsic(INTRINSIC_DEAD))
2177 	    {
2178 		counter->myTurns = 10000;
2179 	    }
2180 	}
2181 
2182 	counter->myTurns--;
2183 	if (counter->myTurns <= 0)
2184 	{
2185 	    // Intrinsic has expired (and no permament set since, as
2186 	    // that will destroy the counter)
2187 	    if (last)
2188 		last->myNext = next;
2189 	    else
2190 		myCounters = next;
2191 
2192 	    // Check to see if it is a poison, and if so, if there
2193 	    // is a final damage message....
2194 	    // NOTE: We ignore poison resistance here!  This has been
2195 	    // defined, for now, as a feature, not a bug.
2196 	    if (glb_intrinsicdefs[counter->myIntrinsic].ispoison)
2197 	    {
2198 		FOREACH_POISON(poison)
2199 		{
2200 		    if (counter->myIntrinsic == glb_poisondefs[poison].intrinsic)
2201 		    {
2202 			// Apply the relevant damage...
2203 			if (glb_poisondefs[poison].finaldamage != ATTACK_NONE)
2204 			{
2205 			    finalpoisondamage = (ATTACK_NAMES)
2206 					glb_poisondefs[poison].finaldamage;
2207 			    finalpoisoninflictor = counter->myInflictor;
2208 			    finalpoisonintrinsic = counter->myIntrinsic;
2209 			}
2210 			break;
2211 		    }
2212 		}
2213 	    }
2214 
2215 	    // Check to see if it is petrification, in which case
2216 	    // the mob gets stoned.
2217 	    if (counter->myIntrinsic == INTRINSIC_STONING)
2218 	    {
2219 		// We cached the unchanging state prior to starting this
2220 		// loop.  If unchanging expires the same turn as stoning
2221 		// takes effect, we give the player the benefit of the doubt
2222 		// as otherwise juggling Preserve with Stoning is too
2223 		// unpredictable.
2224 		if (unchanging)
2225 		{
2226 		    formatAndReport("%U <feel> momentarily stony.");
2227 		}
2228 		else if (actionPetrify())
2229 		{
2230 		    // Don't do losetxt as it was lost in a most
2231 		    // unfortunate way.
2232 		    myIntrinsic.clearIntrinsic(counter->myIntrinsic);
2233 		    delete counter;
2234 #ifdef STRESS_TEST
2235 		    for (ctest = myCounters; ctest; ctest = ctest->myNext)
2236 		    {
2237 			UT_ASSERT(ctest != counter);
2238 		    }
2239 #endif
2240 		    return false;
2241 		}
2242 	    }
2243 
2244 	    // Check to see if it is summoned, in which case the
2245 	    // mob disappears in a puff of smoke.
2246 	    if (counter->myIntrinsic == INTRINSIC_SUMMONED)
2247 	    {
2248 		// Exception are trees that turn into forest squares
2249 		if (getDefinition() == MOB_LIVINGTREE)
2250 		    formatAndReport("%U <return> to an inanimate state.");
2251 		else
2252 		    formatAndReport("%U <disappear> in a puff of smoke.");
2253 
2254 		// Release any possession
2255 		if (hasIntrinsic(INTRINSIC_POSSESSED))
2256 		    actionReleasePossession(true);
2257 
2258 		if (getDefinition() == MOB_LIVINGTREE)
2259 		{
2260 		    glbCurLevel->growForest(getX(), getY(), this);
2261 		    if (hasIntrinsic(INTRINSIC_AFLAME))
2262 		    {
2263 			// Burning trees turn into burning forests
2264 			if (glbCurLevel->getTile(getX(), getY()) == SQUARE_FOREST)
2265 			    glbCurLevel->setTile(getX(), getY(), SQUARE_FORESTFIRE);
2266 		    }
2267 		}
2268 		else
2269 		    glbCurLevel->setSmoke(getX(), getY(), SMOKE_SMOKE, this);
2270 
2271 		glbCurLevel->unregisterMob(this);
2272 
2273 		// Drop the inventory.
2274 		ITEM		*cur;
2275 
2276 		// Drop our inventory on this square...
2277 		while ((cur = myInventory))
2278 		{
2279 		    // We manually drop the item for efficiency.
2280 		    myInventory = cur->getNext();
2281 		    cur->setNext(0);
2282 		    glbCurLevel->acquireItem(cur, getX(), getY(), 0);
2283 		}
2284 
2285 		// Destroy the counter that was unlinked.
2286 		delete counter;
2287 #ifdef STRESS_TEST
2288 		for (ctest = myCounters; ctest; ctest = ctest->myNext)
2289 		{
2290 		    UT_ASSERT(ctest != counter);
2291 		}
2292 #endif
2293 
2294 		delete this;
2295 		return false;
2296 	    }
2297 
2298 	    // Mark if the creature just went wild
2299 	    if (counter->myIntrinsic == INTRINSIC_TAME)
2300 	    {
2301 		losttame = true;
2302 	    }
2303 
2304 	    if (myIntrinsic.clearIntrinsic(counter->myIntrinsic))
2305 	    {
2306 		if (glb_intrinsicdefs[counter->myIntrinsic].affectappearance)
2307 		    rebuildAppearance();
2308 		// Was previously set...
2309 		if (isAvatar() ||
2310 		    (getAvatar() &&
2311 		     glb_intrinsicdefs[counter->myIntrinsic].visiblechange &&
2312 		     getAvatar()->canSense(this)))
2313 		{
2314 		    if (counter->myIntrinsic == finalpoisonintrinsic)
2315 		    {
2316 			// Don't report right away as this gives
2317 			// you are cured before the final damage
2318 			finalpoisonreport = true;
2319 		    }
2320 		    else
2321 			formatAndReport(
2322 			    glb_intrinsicdefs[counter->myIntrinsic].losetxt);
2323 		}
2324 	    }
2325 
2326 	    delete counter;
2327 #ifdef STRESS_TEST
2328 	    for (ctest = myCounters; ctest; ctest = ctest->myNext)
2329 	    {
2330 		UT_ASSERT(ctest != counter);
2331 	    }
2332 #endif
2333 	}
2334 	else
2335 	{
2336 	    // Intrinsic is still active...
2337 	    last = counter;
2338 	}
2339     }
2340 
2341     // Apply the final poison damage.
2342     if (finalpoisondamage != ATTACK_NONE)
2343     {
2344 	formatAndReport("%U <be> wracked by the last of the poison.");
2345 
2346 	lived = receiveDamage(finalpoisondamage,
2347 				finalpoisoninflictor.getMob(),
2348 				0, 0,
2349 				ATTACKSTYLE_POISON);
2350 
2351 	if (!lived)
2352 	{
2353 	    // We died thanks to the final poison damage
2354 	    return false;
2355 	}
2356 
2357 	// Only if they live do we get cured.
2358 	if (finalpoisonreport)
2359 	    formatAndReport(glb_intrinsicdefs[finalpoisonintrinsic].losetxt);
2360     }
2361 
2362     // If we went wild this turn, we also grow momentarily confused.
2363     if (losttame)
2364 	setTimedIntrinsic(0, INTRINSIC_CONFUSED, 4);
2365 
2366     //
2367     // Consume food.
2368     //
2369     starve(1);
2370 
2371     // Bonus food consumption for specific intrinsics.
2372     // This only takes effect when the intrinsic comes from wearing an
2373     // item.
2374     if (hasIntrinsic(INTRINSIC_INVISIBLE))
2375     {
2376 	if (getSourceOfIntrinsic(INTRINSIC_INVISIBLE))
2377 	    starve(1);
2378     }
2379     if (hasIntrinsic(INTRINSIC_REGENERATION))
2380     {
2381 	if (getSourceOfIntrinsic(INTRINSIC_REGENERATION))
2382 	    starve(1);
2383     }
2384 
2385     HUNGER_NAMES		hunger;
2386 
2387     hunger = getHungerLevel();
2388 
2389     // If you can endure hunger, you suffer no malus for starving.
2390     if (hunger < HUNGER_NORMAL)
2391     {
2392 	if (hasSkill(SKILL_ENDUREHUNGER))
2393 	    hunger = HUNGER_NORMAL;
2394     }
2395 
2396     // Regain health...
2397     bool allowregen = !hasIntrinsic(INTRINSIC_NOREGEN);
2398     if (myHP < getMaxHP() && hunger > HUNGER_STARVING)
2399     {
2400 	if (hasIntrinsic(INTRINSIC_REGENERATION))
2401 	{
2402 	    // If noregen is present, this only has effect if the
2403 	    // regeneration is temporary.  The idea is we want the
2404 	    // regeneration spell to work, but we don't want rings of
2405 	    // regeneration to negate noregen.
2406 	    if (allowregen ||
2407 		(intrinsicFromWhat(INTRINSIC_REGENERATION) & INTRINSIC_FROM_TEMPORARY))
2408 	    {
2409 		// We want to regen every turn if full even if
2410 		// ring of regen is on.  Otherwise a ring becomes less
2411 		// useful when full.
2412 		if (speed_matchheartbeat(2) || (hunger > HUNGER_NORMAL))
2413 		{
2414 		    myHP++;
2415 		    starve(1);
2416 		}
2417 	    }
2418 	}
2419 	else if (allowregen)
2420 	{
2421 	    // Every 8th turn, at random...
2422 	    if (hunger > HUNGER_HUNGRY)
2423 	    {
2424 		if (speed_matchheartbeat(8))
2425 		{
2426 		    starve(1);
2427 		    myHP++;
2428 		}
2429 		else if (hunger > HUNGER_NORMAL)
2430 		{
2431 		    myHP++;
2432 		    starve(1);
2433 		}
2434 	    }
2435 	}
2436 
2437 	if (myHP < getMaxHP()
2438 		&& allowregen
2439 		&& hasIntrinsic(INTRINSIC_DRESSED_FIGHTER))
2440 	{
2441 	    // Fighters get half regen!  Yowzers!
2442 	    if (speed_matchheartbeat(2))
2443 	    {
2444 		myHP++;
2445 		starve(1);
2446 	    }
2447 	}
2448 
2449 	// If we are tame and our owner is dressed like a cleric
2450 	// we get regen...
2451 	// Cleric induced regen is active even for constructs.
2452 	if (myHP < getMaxHP())
2453 	{
2454 	    MOB		*master = getMaster();
2455 
2456 	    if (master)
2457 	    {
2458 		if (master->hasIntrinsic(INTRINSIC_DRESSED_CLERIC))
2459 		{
2460 		    myHP++;
2461 		    starve(1);
2462 		}
2463 	    }
2464 	}
2465     }
2466 
2467     // If we have mind drain intrinsic, we loses magic points.
2468     if (myMP && hasIntrinsic(INTRINSIC_MAGICDRAIN))
2469     {
2470 	myMP--;
2471     }
2472 
2473     // Regain magic...
2474     if (myMP < getMaxMP() && hunger > HUNGER_STARVING)
2475     {
2476 	// Regain every turn.
2477 	if (hunger > HUNGER_HUNGRY)
2478 	{
2479 	    if (speed_matchheartbeat(2))
2480 	    {
2481 		myMP++;
2482 		starve(1);
2483 	    }
2484 	}
2485 	else
2486 	{
2487 	    // Hungry players gain at half rate.
2488 	    if (speed_matchheartbeat(4))
2489 	    {
2490 		myMP++;
2491 		starve(1);
2492 	    }
2493 	}
2494 
2495 	// Wizards get bonus magic!  Yowzers!
2496 	if (myMP < getMaxMP() && hasIntrinsic(INTRINSIC_DRESSED_WIZARD))
2497 	{
2498 	    myMP++;
2499 	    starve(1);
2500 	}
2501     }
2502 
2503     // Check to see if we teleport...
2504     if (hasIntrinsic(INTRINSIC_TELEPORT))
2505     {
2506 	if (!rand_choice(100))
2507 	{
2508 	    // This may kill the player.
2509 	    actionTeleport();
2510 
2511 	    // Check if we died...
2512 	    if (!myself.getMob() ||
2513 		myself.getMob() != this ||
2514 		myself.getMob()->hasIntrinsic(INTRINSIC_DEAD))
2515 	    {
2516 		return false;
2517 	    }
2518 	}
2519     }
2520 
2521     // If we have searching, auto search.
2522     if (hasIntrinsic(INTRINSIC_SEARCH))
2523     {
2524 	actionSearch();
2525 
2526 	// Searching may reveal a pit that kills us.
2527 	// Check if we died...
2528 	if (!myself.getMob() ||
2529 	    myself.getMob() != this ||
2530 	    myself.getMob()->hasIntrinsic(INTRINSIC_DEAD))
2531 	{
2532 	    return false;
2533 	}
2534     }
2535 
2536     // Handle worn items being affected by liquids
2537     if (hasIntrinsic(INTRINSIC_SUBMERGED))
2538     {
2539 	submergeItemEffects(tile);
2540     }
2541 
2542     // Handle breath related attacks.  These are strangling, drowning,
2543     // and smoke inhalation.  You can only suffer one attack, as
2544     // strangling prevents drowning, and drowners can't inhale smoke.
2545     // Vaccuum issues are handled by lava effects.
2546     if (!hasIntrinsic(INTRINSIC_NOBREATH))
2547     {
2548 	if (hasIntrinsic(INTRINSIC_STRANGLE))
2549 	{
2550 	    // Lose 1d10 hits a turn..
2551 	    ITEM		*strangler;
2552 	    MOB			*stranglemob = this;
2553 
2554 	    counter = getCounter(INTRINSIC_STRANGLE);
2555 	    if (counter)
2556 		stranglemob = counter->myInflictor.getMob();
2557 
2558 	    strangler = getSourceOfIntrinsic(INTRINSIC_STRANGLE);
2559 	    if (strangler)
2560 		stranglemob = 0;
2561 
2562 	    lived = receiveDamage(ATTACK_STRANGLED, stranglemob, strangler, 0,
2563 				  ATTACKSTYLE_MISC);
2564 	}
2565 	else if (hasIntrinsic(INTRINSIC_SUBMERGED) ||
2566 		// If we are stuck in a wall?
2567 		// ignore doors as they are thin
2568 		 (!canMove(getX(), getY(), false, true)))
2569 	{
2570 	    // Lose damage to drowning...
2571 	    // We want to use a different message if suffocating
2572 	    // rather than drowning, so check what sort of square
2573 	    // we are submerged in..
2574 	    lived = receiveDamage((ATTACK_NAMES) glb_squaredefs[tile].submergeattack,
2575 				    glbCurLevel->getGuiltyMob(getX(), getY()),
2576 				    0, 0,
2577 				  ATTACKSTYLE_MISC);
2578 	}
2579 	else
2580 	{
2581 	    SMOKE_NAMES		 smoke;
2582 	    MOB			*smoker;
2583 
2584 	    smoke = glbCurLevel->getSmoke(getX(), getY(), &smoker);
2585 	    if (smoke != SMOKE_NONE)
2586 	    {
2587 		// Inflict the smoke damage.
2588 		lived = receiveDamage(
2589 			    (ATTACK_NAMES)glb_smokedefs[smoke].attack,
2590 			    smoker, 0, 0,
2591 			    ATTACKSTYLE_MISC);
2592 	    }
2593 	}
2594     }
2595 
2596     // Deal with bleeding..
2597     if (lived && hasIntrinsic(INTRINSIC_BLEED))
2598     {
2599 	// This likely is impossible, but be careful.
2600 	if (isBloodless())
2601 	    clearIntrinsic(INTRINSIC_BLEED);
2602 	else
2603 	{
2604 	    counter = getCounter(INTRINSIC_BLEED);
2605 	    lived = receiveDamage(ATTACK_BLEED,
2606 		    (counter ? counter->myInflictor.getMob() : 0), 0, 0,
2607 		    ATTACKSTYLE_MISC);
2608 	}
2609     }
2610 
2611     // Now, deal with poison...
2612     if (lived)
2613     {
2614 	bool		 resistpoison = hasIntrinsic(INTRINSIC_RESISTPOISON);
2615 	MOB		*inflictor;
2616 
2617 	FOREACH_POISON(poison)
2618 	{
2619 	    if (!lived)
2620 		break;
2621 
2622 	    if (hasIntrinsic((INTRINSIC_NAMES) glb_poisondefs[poison].intrinsic))
2623 	    {
2624 		// Apply the poison damage if the chance passes...
2625 		if (speed_matchheartbeat(glb_poisondefs[poison].modulus))
2626 		{
2627 		    counter = getCounter((INTRINSIC_NAMES) glb_poisondefs[poison].intrinsic);
2628 		    inflictor = (counter ? counter->myInflictor.getMob() : 0);
2629 
2630 		    // Report poison resist or apply poison damage.
2631 		    if (resistpoison)
2632 		    {
2633 			if (inflictor)
2634 			    inflictor->aiNoteThatTargetHasIntrinsic(this,
2635 					    INTRINSIC_RESISTPOISON);
2636 		    }
2637 		    else
2638 		    {
2639 			lived = receiveDamage(
2640 			    (ATTACK_NAMES)glb_poisondefs[poison].damage,
2641 			    inflictor, 0, 0,
2642 			    ATTACKSTYLE_POISON);
2643 		    }
2644 		}
2645 	    }
2646 	}
2647     }
2648 
2649     // Deal with being blown by the wind.
2650     if (lived && glb_mobdefs[getDefinition()].blownbywind)
2651     {
2652 	int		wdx, wdy;
2653 
2654 	glbCurLevel->getWindDirection(wdx, wdy);
2655 
2656 	if (wdx || wdy)
2657 	{
2658 	    if (canMoveDelta(wdx, wdy, true))
2659 	    {
2660 		// Move!
2661 		actionWalk(wdx, wdy);
2662 
2663 		// It is unlikely because this is a flying creature,
2664 		// but moving can kill us.
2665 		// Note that if the creature changes levels we may get
2666 		// some weird behaviour here, but I think it is safe
2667 		// At least that's what the gin & gingerale I'm drinking
2668 		// is telling me.  :>
2669 		if (!myself.getMob() ||
2670 		    myself.getMob() != this ||
2671 		    myself.getMob()->hasIntrinsic(INTRINSIC_DEAD))
2672 		{
2673 		    lived = false;
2674 		}
2675 	    }
2676 	}
2677     }
2678 
2679     // Disperse smoke.
2680     if (lived && getDefinition() == MOB_AIRELEMENTAL)
2681     {
2682 	glbCurLevel->setSmoke(getX(), getY(), SMOKE_NONE, 0);
2683     }
2684 
2685     // Burn squares due to our own heat.
2686     if (lived && getDefinition() == MOB_FIREELEMENTAL)
2687     {
2688 	glbCurLevel->burnSquare(getX(), getY(), this);
2689 
2690 	// If we are standing on water we will reveal a pit that we
2691 	// fall into.  The resulting pit may kill us.
2692 	if (!myself.getMob() ||
2693 	    myself.getMob() != this ||
2694 	    myself.getMob()->hasIntrinsic(INTRINSIC_DEAD))
2695 	{
2696 	    lived = false;
2697 	}
2698     }
2699 
2700     // Freeze squares due to our own chill.
2701     if (lived && getDefinition() == MOB_LIVINGFROST)
2702     {
2703 	glbCurLevel->freezeSquare(getX(), getY(), this);
2704 
2705 	// I don't think freezing a square can kill you, but
2706 	// I don't want to find out the hard way.
2707 	if (!myself.getMob() ||
2708 	    myself.getMob() != this ||
2709 	    myself.getMob()->hasIntrinsic(INTRINSIC_DEAD))
2710 	{
2711 	    lived = false;
2712 	}
2713     }
2714 
2715     // Deal with standing on/in bad stuff.
2716     if (lived)
2717     {
2718 	switch (tile)
2719 	{
2720 	    case SQUARE_LAVA:
2721 		if (!hasIntrinsic(INTRINSIC_RESISTFIRE) ||
2722 		     hasIntrinsic(INTRINSIC_VULNFIRE))
2723 		{
2724 		    lived = receiveDamage(ATTACK_LAVABURN,
2725 					    0, 0, 0,
2726 					    ATTACKSTYLE_MISC);
2727 		}
2728 		if (lived && !hasIntrinsic(INTRINSIC_AFLAME))
2729 		{
2730 		    int		delay;
2731 
2732 		    delay = rand_dice(1, 4, 2);
2733 		    // Set aflame...
2734 		    setTimedIntrinsic(this, INTRINSIC_AFLAME, delay);
2735 		}
2736 		break;
2737 
2738 	    case SQUARE_FORESTFIRE:
2739 		if (!hasIntrinsic(INTRINSIC_RESISTFIRE) ||
2740 		     hasIntrinsic(INTRINSIC_VULNFIRE))
2741 		{
2742 		    lived = receiveDamage(ATTACK_FORESTFIRE,
2743 					    0, 0, 0,
2744 					    ATTACKSTYLE_MISC);
2745 		}
2746 		break;
2747 
2748 
2749 	    case SQUARE_ACID:
2750 		// We only burn if we are submerged or are not
2751 		// flying
2752 		if (hasIntrinsic(INTRINSIC_SUBMERGED) ||
2753 		    !(getMoveType() & MOVE_FLY))
2754 		{
2755 		    lived = receiveDamage(ATTACK_ACIDPOOLBURN,
2756 					    0, 0, 0,
2757 					    ATTACKSTYLE_MISC);
2758 		}
2759 		break;
2760 
2761 	    case SQUARE_WATER:
2762 		if (hasIntrinsic(INTRINSIC_SUBMERGED) &&
2763 		    hasIntrinsic(INTRINSIC_AFLAME))
2764 		{
2765 		    formatAndReport("The water puts out %R flames.");
2766 		    clearIntrinsic(INTRINSIC_AFLAME);
2767 		}
2768 		break;
2769 	    case SQUARE_STARS1:
2770 	    case SQUARE_STARS2:
2771 	    case SQUARE_STARS3:
2772 	    case SQUARE_STARS4:
2773 	    case SQUARE_STARS5:
2774 		if (!hasIntrinsic(INTRINSIC_NOBREATH) &&
2775 		    !hasIntrinsic(INTRINSIC_STRANGLE))
2776 		{
2777 		    // We have made sure you didn't already pay
2778 		    // for this cost elsewhere.
2779 		    lived = receiveDamage(ATTACK_ASPHYXIATE,
2780 				    0, 0, 0,
2781 				    ATTACKSTYLE_MISC);
2782 		}
2783 		break;
2784             default:
2785                 break;
2786 	}
2787     }
2788 
2789     // Now, deal with being set afire.
2790     if (lived && (!hasIntrinsic(INTRINSIC_RESISTFIRE) || hasIntrinsic(INTRINSIC_VULNFIRE)) )
2791     {
2792 	if (hasIntrinsic(INTRINSIC_AFLAME))
2793 	{
2794 	    counter = getCounter(INTRINSIC_AFLAME);
2795 	    lived = receiveDamage(ATTACK_AFLAME,
2796 				(counter ? counter->myInflictor.getMob() : 0), 0, 0,
2797 				ATTACKSTYLE_MISC);
2798 	}
2799     }
2800 
2801     // Apply silver damage for anything we are wielding
2802     if (lived && hasIntrinsic(INTRINSIC_VULNSILVER))
2803     {
2804 	for (item = myInventory; item; item = item->getNext())
2805 	{
2806 	    if (item->getX() == 0)
2807 	    {
2808 		if (item->getMaterial() == MATERIAL_SILVER)
2809 		{
2810 		    lived = receiveDamage(ATTACK_SILVERITEM, 0,
2811 					    item, 0,
2812 					    ATTACKSTYLE_MISC);
2813 
2814 		    if (!lived)
2815 			break;
2816 		    // Note that even if we live, we may delete
2817 		    // items, ie, items of life saving,
2818 		    // and thus may end up with a corrupt inventory
2819 		    // list.
2820 		    // Our hacky solution is to detect life saving
2821 		    // by looking for us having max hp..
2822 		    if (getHP() == getMaxHP())
2823 			break;
2824 		}
2825 	    }
2826 	}
2827     }
2828 
2829     // Apply gold damage for anything we are wielding
2830     if (lived && hasIntrinsic(INTRINSIC_GOLDALLERGY))
2831     {
2832 	for (item = myInventory; item; item = item->getNext())
2833 	{
2834 	    if (item->getX() == 0)
2835 	    {
2836 		if (item->getMaterial() == MATERIAL_GOLD)
2837 		{
2838 		    lived = receiveDamage(ATTACK_GOLDITEM, 0,
2839 					    item, 0,
2840 					    ATTACKSTYLE_MISC);
2841 
2842 		    if (!lived)
2843 			break;
2844 		    // Note that even if we live, we may delete
2845 		    // items, ie, items of life saving,
2846 		    // and thus may end up with a corrupt inventory
2847 		    // list.
2848 		    // Our hacky solution is to detect life saving
2849 		    // by looking for us having max hp..
2850 		    if (getHP() == getMaxHP())
2851 			break;
2852 		}
2853 	    }
2854 	}
2855     }
2856 
2857     // Run heartbeat on all our items...
2858     if (lived)
2859     {
2860 	ITEM		*item, *next;
2861 
2862 	for (item = myInventory; item; item = next)
2863 	{
2864 	    next = item->getNext();
2865 
2866 	    if (!item->doHeartbeat(glbCurLevel, this))
2867 	    {
2868 		ITEM		*drop;
2869 		drop = dropItem(item->getX(), item->getY());
2870 		UT_ASSERT(drop == item);
2871 
2872 		delete drop;
2873 	    }
2874 	}
2875     }
2876 
2877     // This must be last, as it may change our MOB on us!
2878     if (lived && hasIntrinsic(INTRINSIC_POLYMORPH) && !unchanging)
2879     {
2880 	if (!rand_choice(300))
2881 	{
2882 	    actionPolymorph();
2883 
2884 	    // Note we do not want anyone calling doAI.
2885 	    // We hand wave this by saying it is the shock
2886 	    // of polymorphing.
2887 	    lived = false;
2888 	}
2889     }
2890 
2891     if (lived && isPolymorphed() && !unchanging)
2892     {
2893 	if (!rand_choice(1000))
2894 	{
2895 	    actionUnPolymorph();
2896 
2897 	    // Note we do not want anyone calling doAI.
2898 	    // We hand wave this by saying it is the shock
2899 	    // of polymorphing.
2900 	    lived = false;
2901 	}
2902     }
2903 
2904     // Check for any per-turn piety conditions.
2905     // If piety ever goes beyond avatars, should check for all
2906     // MOBs.  However, some of these tests are expensive, so
2907     // we keep it limitted for now.
2908     if (lived && isAvatar())
2909     {
2910 	if (hasIntrinsic(INTRINSIC_DRESSED_WIZARD))
2911 	    pietyDress(INTRINSIC_DRESSED_WIZARD);
2912 	if (hasIntrinsic(INTRINSIC_DRESSED_RANGER))
2913 	    pietyDress(INTRINSIC_DRESSED_RANGER);
2914 	if (hasIntrinsic(INTRINSIC_DRESSED_FIGHTER))
2915 	    pietyDress(INTRINSIC_DRESSED_FIGHTER);
2916 	if (hasIntrinsic(INTRINSIC_DRESSED_CLERIC))
2917 	    pietyDress(INTRINSIC_DRESSED_CLERIC);
2918 	if (hasIntrinsic(INTRINSIC_DRESSED_BARBARIAN))
2919 	    pietyDress(INTRINSIC_DRESSED_BARBARIAN);
2920 	if (hasIntrinsic(INTRINSIC_DRESSED_NECROMANCER))
2921 	    pietyDress(INTRINSIC_DRESSED_NECROMANCER);
2922 
2923 	// Check surrounding squares for enemies:
2924 	int		numenemy = 0;
2925 
2926 	numenemy = calculateFoesSurrounding();
2927 
2928 	pietySurrounded(numenemy);
2929     }
2930 
2931     return lived;
2932 }
2933 
2934 void
findRandomValidMove(int & dx,int & dy,MOB * owner)2935 MOB::findRandomValidMove(int &dx, int &dy, MOB *owner)
2936 {
2937     int		possible_moves[8];
2938     const static int	sdx[8] = { -1, 1,  0, 0, -1, 1, -1,  1 };
2939     const static int	sdy[8] = {  0, 0, -1, 1,  1, 1, -1, -1 };
2940     int		i, j, max;
2941     bool	allowdoor = false;
2942     bool	onlysafe = true;
2943 
2944     allowdoor = aiWillOpenDoors();
2945     if (isAvatar())
2946     {
2947 	allowdoor = true;
2948 	onlysafe = false;
2949     }
2950 
2951     max = 4;
2952     if (myDefinition == MOB_GRIDBUG)
2953 	max = 8;
2954 
2955     j = 0;
2956     for (i = 0; i < max; i++)
2957     {
2958 	if (canMoveDelta(sdx[i], sdy[i], true, allowdoor/*, onlysafe*/))
2959 	{
2960 	    // Determine if there is a mob there and if it is
2961 	    // our no swap mob.  Displacing our owner is bad form.
2962 	    if (!owner ||
2963 		(glbCurLevel->getMob(getX()+sdx[i], getY()+sdy[i]) != owner))
2964 	    {
2965 		possible_moves[j++] = i;
2966 	    }
2967 	}
2968     }
2969 
2970     if (!j)
2971     {
2972 	dx = dy = 0;
2973 	return;
2974     }
2975     i = possible_moves[rand_choice(j)];
2976     dx = sdx[i];
2977     dy = sdy[i];
2978 }
2979 
2980 void
applyConfusion(int & dx,int & dy)2981 MOB::applyConfusion(int &dx, int &dy)
2982 {
2983     int		dz = 0;
2984 
2985     applyConfusion(dx, dy, dz);
2986 }
2987 
2988 void
applyConfusionNoSelf(int & dx,int & dy)2989 MOB::applyConfusionNoSelf(int &dx, int &dy)
2990 {
2991     int		dz = 0;
2992     bool	notatself = false;
2993 
2994     if (dx || dy)
2995 	notatself = true;
2996 
2997     applyConfusion(dx, dy, dz);
2998 
2999     if (!dx && !dy && notatself)
3000     {
3001 	// Whoops, redirected at self.  Must cancel.
3002 	rand_eightwaydirection(dx, dy);
3003     }
3004 }
3005 
3006 void
applyConfusion(int & dx,int & dy,int & dz)3007 MOB::applyConfusion(int &dx, int &dy, int &dz)
3008 {
3009     // Determine if the critter is confused.
3010     if (!hasIntrinsic(INTRINSIC_CONFUSED))
3011 	return;
3012 
3013     // Random chance we get to do what we wanted to do.
3014     if (rand_chance(80))
3015 	return;
3016 
3017     // If dz is set, randomize it.
3018     if (dz)
3019     {
3020 	dz = rand_sign();
3021     }
3022 
3023     // If dx & dy are within the given range, pick a random direction.
3024     // Note this can allow diagonal movement, for example.
3025     if (dx >= -1 && dx <= 1 && dy >= -1 && dy <= 1)
3026     {
3027 	dx = rand_range(-1, 1);
3028 	dy = rand_range(-1, 1);
3029     }
3030     else
3031     {
3032 	// We are doing an arbitrary ranged command (like teleport)
3033 	// We thus fall somewhere random.
3034 	dx = rand_range(-dx*2, dx*2);
3035 	dy = rand_range(-dy*2, dy*2);
3036     }
3037 }
3038 
3039 bool
validMovePhase(PHASE_NAMES phase)3040 MOB::validMovePhase(PHASE_NAMES phase)
3041 {
3042     bool		skipthismove = false;
3043 
3044     // Check our phase to see if we are allowed to move.
3045     switch (speed_getphase())
3046     {
3047 	case PHASE_FAST:
3048 	    // If we don't have fast, no go.
3049 	    if (!hasIntrinsic(INTRINSIC_FAST))
3050 		skipthismove = true;
3051 	    break;
3052 
3053 	case PHASE_QUICK:
3054 	    // If we don't have quick, no go.
3055 	    if (!hasIntrinsic(INTRINSIC_QUICK))
3056 		skipthismove = true;
3057 	    break;
3058 
3059 	case PHASE_NORMAL:
3060 	    // Always allowed this phase.
3061 	    break;
3062 
3063 	case PHASE_SLOW:
3064 	    // So long as we DON'T have slow, we can move.
3065 	    if (hasIntrinsic(INTRINSIC_SLOW))
3066 		skipthismove = true;
3067 	    break;
3068 
3069 	case NUM_PHASES:
3070 	    UT_ASSERT(!"Invalid phase!");
3071 	    break;
3072     }
3073 
3074     return !skipthismove;
3075 }
3076 
3077 bool
doMovePrequel()3078 MOB::doMovePrequel()
3079 {
3080     if (!validMovePhase(speed_getphase()))
3081 	return false;
3082 
3083     // Check for paralysis:
3084     if (hasIntrinsic(INTRINSIC_PARALYSED))
3085     {
3086 	if (!hasIntrinsic(INTRINSIC_FREEDOM))
3087 	{
3088 	    // Sit here and do nothing...
3089 	    // We don't want to spam the avatar as it is annoying.
3090 	    // IN any case, it is in the status bar!
3091 	    if (!isAvatar())
3092 	    {
3093 		formatAndReport("%U <be> paralysed.");
3094 	    }
3095 	    return false;
3096 	}
3097 	else
3098 	{
3099 	    // People with freedom have paralysis instantly cured.
3100 	    clearIntrinsic(INTRINSIC_PARALYSED);
3101 	}
3102     }
3103 
3104     // Check to see if we are sleeping.
3105     if (hasIntrinsic(INTRINSIC_ASLEEP))
3106     {
3107 	if (!hasIntrinsic(INTRINSIC_RESISTSLEEP))
3108 	{
3109 	    // TODO: Check for any wakeup notifiers!
3110 
3111 	    // Snore.  Do not snore if one doesn't breath.
3112 	    if (!hasIntrinsic(INTRINSIC_NOBREATH) && !rand_choice(10))
3113 	    {
3114 		formatAndReport("%U <snore>.");
3115 	    }
3116 	    return false;
3117 	}
3118 	else
3119 	{
3120 	    // Sleepers with resist sleep instantly awake.
3121 	    clearIntrinsic(INTRINSIC_ASLEEP);
3122 	}
3123     }
3124 
3125     // Check if starving and auto forage 2% of time
3126     if (isAvatar() && getHungerLevel() <= HUNGER_STARVING &&
3127 	!hasSkill(SKILL_ENDUREHUNGER) &&
3128 	rand_chance(2))
3129     {
3130 	const char *msg[] =
3131 	{
3132 	    "%U <scrape> lichen from the wall and <eat> it.",
3133 	    "%U <find> a soft-looking rock to eat.",
3134 	    "%U <find> and <gobble> up an unidentified piece of offal.",
3135 	    "%U <stop> and <listen> to the growling of %r stomach.",
3136 	    "%U <scoop> up a fat grub and <crunch> on it happily.",
3137 	    0
3138 	};
3139 
3140 	formatAndReport(rand_string(msg));
3141 
3142 	// We lose this turn
3143 	return false;
3144     }
3145 
3146     return true;
3147 }
3148 
3149 bool
meltRocksCBStatic(int x,int y,bool final,void * data)3150 MOB::meltRocksCBStatic(int x, int y, bool final, void *data)
3151 {
3152     MOBREF		*mobref = (MOBREF *) data;
3153 
3154     if (final) return false;
3155 
3156     SQUARE_NAMES	old = glbCurLevel->getTile(x, y);
3157 
3158     if (!glb_squaredefs[old].invulnerable)
3159     {
3160 	// If the square is a ladder, we don't want to melt rocks.
3161 	glbCurLevel->setTile(x, y, SQUARE_LAVA);
3162 	glbCurLevel->setFlag(x, y, SQUAREFLAG_LIT);
3163 	glbCurLevel->dropItems(x, y, mobref->getMob());
3164     }
3165     return true;
3166 }
3167 
3168 
3169 bool
areaAttackCBStatic(int x,int y,bool final,void * data)3170 MOB::areaAttackCBStatic(int x, int y, bool final, void *data)
3171 {
3172     MOB			*mob;
3173     MOBREF		*srcmobref;
3174     bool		 hit = false;
3175 
3176     srcmobref = (MOBREF *) data;
3177 
3178     if (final) return false;
3179 
3180     ATTACK_NAMES	 attack = ourEffectAttack;
3181 
3182     mob = glbCurLevel->getMob(x, y);
3183     if (mob)
3184     {
3185 	mob->receiveAttack(attack, srcmobref->getMob(),
3186 			    0, 0, ATTACKSTYLE_SPELL, &hit);
3187     }
3188 
3189     // If it was cold or fire, do the right thing.
3190     if (glb_attackdefs[attack].element == ELEMENT_FIRE)
3191     {
3192 	glbCurLevel->burnSquare(x, y, srcmobref->getMob());
3193     }
3194 
3195     if (glb_attackdefs[attack].element == ELEMENT_COLD)
3196     {
3197 	glbCurLevel->freezeSquare(x, y, srcmobref->getMob());
3198     }
3199 
3200     // Might be tempted to do just shock, but what of less
3201     // powerful shocks, ala spark?  Do I care?
3202     // Answer is to report the amount of damage done so we
3203     // can get a fair estimate.
3204     if (glb_attackdefs[attack].element == ELEMENT_SHOCK)
3205     {
3206 	glbCurLevel->electrifySquare(x, y,
3207 			rand_dice(glb_attackdefs[attack].damage),
3208 			srcmobref->getMob());
3209     }
3210 
3211 
3212     if (hit && glb_attackdefs[attack].stopathit)
3213     {
3214 	return false;
3215     }
3216 
3217     // Continue the breath attack...
3218     return true;
3219 }
3220 
3221 bool
searchSquare(int x,int y,bool isaction)3222 MOB::searchSquare(int x, int y, bool isaction)
3223 {
3224     SQUARE_NAMES	tile;
3225     BUF			buf;
3226     int			findchance;
3227 
3228     tile = glbCurLevel->getTile(x, y);
3229 
3230     findchance = glb_squaredefs[tile].findchance;
3231 
3232     // Half chance to find in the dark.
3233     // Note that searching by walking on the square doesn't get
3234     // the penalty - people walking in the dark are as likely to set
3235     // off a trap door as those walking in the light.  (If anything,
3236     // we may want to reverse this!)
3237     if (isaction && !glbCurLevel->isLit(x, y))
3238     {
3239 	// Decrease find chance.
3240 	findchance = (findchance + 1) / 2;
3241     }
3242 
3243     // First, determine if this was a secret square.  If so,
3244     // the secret is up!
3245     if (glb_squaredefs[tile].hiddensquare != SQUARE_EMPTY &&
3246 	(rand_chance(findchance) ||
3247 	 hasIntrinsic(INTRINSIC_SKILL_SEARCH)))
3248     {
3249 	tile = (SQUARE_NAMES) glb_squaredefs[tile].hiddensquare;
3250 
3251 	// Grant piety.
3252 	pietyFindSecret();
3253 
3254 	buf = formatToString("%U <find> %B1.", this, 0, 0, 0,
3255 			glb_squaredefs[tile].description, 0);
3256 	reportMessage(buf);
3257 	glbCurLevel->setTile(x, y, tile);
3258 	glbCurLevel->dropItems(x, y, this);
3259 
3260 	// Warning: This may kill this!
3261 	glbCurLevel->dropMobs(x, y, true, this, true);
3262 
3263 	return true;		// Found something.
3264     }
3265     return false;		// Nothing to find.
3266 }
3267 
3268 bool
canCastSpell(SPELL_NAMES spell)3269 MOB::canCastSpell(SPELL_NAMES spell)
3270 {
3271     if (!hasIntrinsic((INTRINSIC_NAMES) glb_spelldefs[spell].intrinsic))
3272 	return false;
3273 
3274     if (hasIntrinsic(INTRINSIC_AMNESIA))
3275 	return false;
3276 
3277     if (myMP < glb_spelldefs[spell].mpcost)
3278 	return false;
3279     if (myHP <= glb_spelldefs[spell].hpcost)
3280 	return false;
3281     if (myExp < glb_spelldefs[spell].xpcost)
3282 	return false;
3283 
3284     return true;
3285 }
3286 
3287 static void
testability(int & ability,int & total,int threshold,int value)3288 testability(int &ability, int &total, int threshold, int value)
3289 {
3290     if (threshold)
3291     {
3292 	// Actually test against this requirement.
3293 	total++;
3294 	if (value >= threshold)
3295 	    ability += 256;
3296 	else
3297 	{
3298 	    // Always round down so exactly equal only occurs when
3299 	    // everything is fulfilled.
3300 	    ability += (value*256) / threshold;
3301 	}
3302     }
3303 }
3304 
3305 int
spellCastability(SPELL_NAMES spell)3306 MOB::spellCastability(SPELL_NAMES spell)
3307 {
3308     if (!hasIntrinsic((INTRINSIC_NAMES) glb_spelldefs[spell].intrinsic))
3309 	return 0;
3310 
3311     if (hasIntrinsic(INTRINSIC_AMNESIA))
3312 	return 0;
3313 
3314     int		total = 0;
3315     int		ability = 0;
3316 
3317     testability(ability, total, glb_spelldefs[spell].mpcost, myMP);
3318     testability(ability, total, glb_spelldefs[spell].hpcost, myHP);
3319     testability(ability, total, glb_spelldefs[spell].xpcost, myExp);
3320 
3321     if (total == 0)
3322     {
3323 	// A spell with no requirements is always castable!
3324 	return 256;
3325     }
3326 
3327     return ability / total;
3328 }
3329 
3330 HUNGER_NAMES
getHungerLevel() const3331 MOB::getHungerLevel() const
3332 {
3333     HUNGER_NAMES		hunger;
3334 
3335     FOREACH_HUNGER(hunger)
3336     {
3337 	if (myFoodLevel < glb_hungerdefs[hunger].foodlevel)
3338 	    break;
3339     }
3340     hunger = (HUNGER_NAMES) (hunger-1);
3341 
3342     // If we can't eat anything, we can't starve.
3343     if (!glb_mobdefs[myDefinition].edible ||
3344 	!*glb_mobdefs[myDefinition].edible)
3345 	return HUNGER_NORMAL;
3346 
3347     return hunger;
3348 }
3349 
3350 void
starve(int foodvalue)3351 MOB::starve(int foodvalue)
3352 {
3353     UT_ASSERT(foodvalue >= 0);
3354 
3355     // If we can't eat anything, we can't starve.
3356     if (!glb_mobdefs[myDefinition].edible ||
3357 	!*glb_mobdefs[myDefinition].edible)
3358 	return;
3359 
3360     if (foodvalue < myFoodLevel)
3361 	myFoodLevel -= foodvalue;
3362     else
3363 	myFoodLevel = 0;
3364 }
3365 
3366 void
feed(int foodvalue)3367 MOB::feed(int foodvalue)
3368 {
3369     UT_ASSERT(foodvalue >= 0);
3370     // Maximum food value of 10,000.  As you can't eat
3371     // past full (2500) this should be pretty safe.
3372     if (foodvalue + myFoodLevel > 10000)
3373     {
3374 	myFoodLevel = 10000;
3375     }
3376     else
3377     {
3378 	myFoodLevel += foodvalue;
3379     }
3380 }
3381 
3382 void
systemshock()3383 MOB::systemshock()
3384 {
3385     // Apply system shock: The base types max hit points are damaged
3386     // as a result of the pain.
3387     int		newmax;
3388 
3389     newmax = getMaxHP();
3390     // Lose 10%.
3391     newmax -= (newmax+9) / 10;
3392     if (newmax < 1)
3393 	newmax = 1;
3394 
3395     // C++'s private definitions are meaningless if I can do crap
3396     // like this :>
3397     myMaxHP = newmax;
3398     if (getHP() > getMaxHP())
3399 	setMaximalHP();
3400 
3401     // Let people be warned of the effect!
3402     formatAndReport("%U <feel> a little dead inside.");
3403 }
3404 
3405 int
getWeaponSkillLevel(const ITEM * weapon,ATTACKSTYLE_NAMES attackstyle) const3406 MOB::getWeaponSkillLevel(const ITEM *weapon, ATTACKSTYLE_NAMES attackstyle) const
3407 {
3408     // Non-avatar are exempt from this.
3409     // They get maximum skill.
3410     if (!isAvatar())
3411 	return 2;
3412 
3413     // Determine if we have the attack skill.
3414     SKILL_NAMES		 attackskill;
3415     const ATTACK_DEF	*attack;
3416     int			 skilllevel = 0;
3417     bool		 misused = false;
3418 
3419     if (weapon)
3420     {
3421 	attack = 0;
3422 	if (attackstyle == ATTACKSTYLE_MELEE)
3423 	{
3424 	    attack = weapon->getAttack();
3425 	}
3426 	else if (attackstyle == ATTACKSTYLE_THROWN)
3427 	{
3428 	    attack = weapon->getThrownAttack();
3429 	}
3430 	if (attack == &glb_attackdefs[ATTACK_MISUSED] ||
3431 	    attack == &glb_attackdefs[ATTACK_MISUSED_BUTWEAPON] ||
3432 	    attack == &glb_attackdefs[ATTACK_MISTHROWN])
3433 	    misused = true;
3434 
3435 	attackskill = weapon->getAttackSkill();
3436 	if (misused)
3437 	    attackskill = SKILL_WEAPON_IMPROVISE;
3438 	if (hasSkill(attackskill))
3439 	    skilllevel++;
3440 
3441 	if (!misused && hasSkill(weapon->getSpecialSkill()))
3442 	    skilllevel++;
3443     }
3444 
3445     // Switch on the attack style.
3446     switch (attackstyle)
3447     {
3448 	case ATTACKSTYLE_MELEE:
3449 	    if (weapon)
3450 	    {
3451 		switch (weapon->getSize())
3452 		{
3453 		    case SIZE_TINY:
3454 		    case SIZE_SMALL:
3455 			if (hasSkill(SKILL_WEAPON_SMALL))
3456 			    skilllevel++;
3457 			break;
3458 		    case SIZE_MEDIUM:
3459 			if (hasSkill(SKILL_WEAPON_MEDIUM))
3460 			    skilllevel++;
3461 			break;
3462 		    case SIZE_LARGE:
3463 			if (hasSkill(SKILL_WEAPON_LARGE))
3464 			    skilllevel++;
3465 			break;
3466 		    default:
3467 			// No adjustment, as no skills for these weapons.
3468 			break;
3469 		}
3470 	    }
3471 	    break;
3472 
3473 	case ATTACKSTYLE_THROWN:
3474 	    if (hasSkill(SKILL_WEAPON_RANGED))
3475 		skilllevel++;
3476 	    break;
3477 
3478 	default:
3479 	    // It is assumed we are skilled with magic attacks, etc.
3480 	    skilllevel++;
3481 	    break;
3482     }
3483 
3484     return skilllevel;
3485 }
3486 
3487 int
getArmourSkillLevel(const ITEM * armour) const3488 MOB::getArmourSkillLevel(const ITEM *armour) const
3489 {
3490     // Determine what skill we have.
3491     // Creatures always get maximum skill
3492     if (!isAvatar())
3493 	return 2;
3494 
3495     int			skilllevel = 0;
3496 
3497     if (armour)
3498     {
3499 	// Switch base on type of armour
3500 	if (armour->isHelmet())
3501 	    skilllevel += hasSkill(SKILL_ARMOUR_HELMET);
3502 	if (armour->isJacket())
3503 	    skilllevel += hasSkill(SKILL_ARMOUR_BODY);
3504 	if (armour->isShield())
3505 	    skilllevel += hasSkill(SKILL_ARMOUR_SHIELD);
3506 	if (armour->isBoots())
3507 	    skilllevel += hasSkill(SKILL_ARMOUR_BOOTS);
3508 
3509 	// Switch based on material of armour.
3510 	switch (armour->getMaterial())
3511 	{
3512 	    case MATERIAL_CLOTH:
3513 	    case MATERIAL_PAPER:
3514 		skilllevel += hasSkill(SKILL_ARMOUR_CLOTH);
3515 		break;
3516 
3517 	    case MATERIAL_LEATHER:
3518 	    case MATERIAL_WOOD:
3519 		skilllevel += hasSkill(SKILL_ARMOUR_LEATHER);
3520 		break;
3521 
3522 	    case MATERIAL_IRON:
3523 		skilllevel += hasSkill(SKILL_ARMOUR_IRON);
3524 		break;
3525 
3526 	    case MATERIAL_GOLD:
3527 	    case MATERIAL_GLASS:
3528 	    case MATERIAL_MITHRIL:
3529 	    case MATERIAL_SILVER:
3530 	    case MATERIAL_STONE:
3531 	    case MATERIAL_FLESH:
3532 		skilllevel += hasSkill(SKILL_ARMOUR_EXOTIC);
3533 		break;
3534 
3535 	    case MATERIAL_ETHEREAL:
3536 	    case MATERIAL_WATER:
3537 	    case MATERIAL_NONE:
3538 	    case NUM_MATERIALS:
3539 		// No skills let you use this.
3540 		break;
3541 	}
3542     }
3543 
3544     return skilllevel;
3545 }
3546 
3547 void
noteAttacker(MOB * src)3548 MOB::noteAttacker(MOB *src)
3549 {
3550     if (!src)
3551 	return;
3552 
3553     // Don't hate self.  (This is technically covered by
3554     // hasCommonMaster, but it is better safe than sorry - nothing
3555     // is more embarrasing than suicidal mobs.)
3556     if (src == this)
3557 	return;
3558 
3559     // This is likely friendly fire.
3560     if (hasCommonMaster(src))
3561     {
3562 	// TODO: Weaken our bonds!
3563 	return;
3564     }
3565 
3566     setAITarget(src);
3567 
3568     if (glb_aidefs[getAI()].markattackpos)
3569     {
3570 	myAIState &= ~AI_LAST_HIT_LOC;
3571 	myAIState |= src->getX() + src->getY() * MAP_WIDTH;
3572     }
3573 }
3574 
3575 bool
receiveAttack(ATTACK_NAMES attack,MOB * src,ITEM * weapon,ITEM * launcher,ATTACKSTYLE_NAMES style,bool * didhit,int multiplierbonus)3576 MOB::receiveAttack(ATTACK_NAMES attack, MOB *src, ITEM *weapon,
3577 		    ITEM *launcher,
3578 		    ATTACKSTYLE_NAMES style, bool *didhit,
3579 		    int multiplierbonus)
3580 {
3581     return receiveAttack(&glb_attackdefs[attack],
3582 			src, weapon, launcher, style, didhit,
3583 			multiplierbonus);
3584 }
3585 
3586 bool
receiveAttack(const ATTACK_DEF * attack,MOB * src,ITEM * weapon,ITEM * launcher,ATTACKSTYLE_NAMES style,bool * didhit,int multiplierbonus)3587 MOB::receiveAttack(const ATTACK_DEF *attack, MOB *src, ITEM *weapon,
3588 		    ITEM *launcher,
3589 		    ATTACKSTYLE_NAMES style, bool *didhit,
3590 		    int multiplierbonus)
3591 {
3592     // Determine if it hit...
3593     int		hitroll;		// Raw d20.
3594     int		multiplier = 1 + multiplierbonus;
3595     int		modifier;
3596     int		acroll;
3597     int		attackroll;
3598     bool	lived;
3599     int		damage_reduction = 0;
3600     int		skilllevel;
3601     bool	forcehit = false;
3602     bool	forcemiss = false;
3603     bool	abletomove;
3604 
3605     if (isAvatar())
3606     {
3607 	// An attempt to hit us will also break the autorun even
3608 	// if it fails.
3609 	if (glbAutoRunEnabled)
3610 	    formatAndReport("%U <stop> over concerns for your safety.");
3611 	glbAutoRunEnabled = false;
3612     }
3613 
3614     // Before we get angered, we check with piety.  This way
3615     // we can detect attacks on friendlies.
3616     if (src && src->isAvatar() && src != this)
3617     {
3618 	src->pietyAttack(this, attack, style);
3619     }
3620 
3621     // Someone attacked us!  We will make it our target if it exists!
3622     noteAttacker(src);
3623 
3624     // Assign all the noises.
3625     // Defender always noises their shield.
3626     // Attacker always noises their weapon.
3627     // If no weapon, we use the attacker's base noise level (A bit of
3628     // hack...  Perhaps should use attack's noise level?)
3629     makeEquipNoise(ITEMSLOT_LHAND);
3630     if (src)
3631     {
3632 	if (weapon)
3633 	    src->makeNoise(weapon->getNoiseLevel());
3634 	else
3635 	    src->makeNoise(glb_mobdefs[src->getDefinition()].noise);
3636     }
3637 
3638     //
3639     // Summary of To Hit Calculation
3640     //
3641     // We start with a hitroll = 1..20
3642     //     1 is crit miss, 20 crit threat.
3643     // We have an attack bonus (Phys or Magic level), which is 1..20
3644     // depending on how advanced the character is.
3645     // This modifier is rolled against depending on skilllevel
3646     // Weapon enchantment is added (0..7), this is rolled against
3647     // skilllevel.
3648     // Target Invisible [0,-4]
3649     // Target natural foe [0, 4]
3650     // Ranged attack from trees, [0,4]
3651     // -10 is added to allow AC 0 to represent 50% hit chance.
3652     // Attack Roll is thus:
3653     //  (1..20) + (1..20]) + (0..7) + [0,-4] + [0,4] + [0,4] - 10
3654     // (() marks a random range, [] a fixed range dependent on some factor.)
3655 
3656     // AC is calculated
3657     // AC is the rolled natural ac (0..20)  (Turtleoids go to 40!)
3658     // The worn AC of the specific body part
3659     //   This is the items base AC [0..10]
3660     //   Worn AC is adjusted by enchantment [0..5]
3661     //   And again by skilllevel [0..2]
3662     // AC adds the rolled worn AC (0..17)
3663     // The two are then combined using 1/3rd semantic:
3664     // AC = max(nat, worn) + min(nat, worn) / 3.
3665     // Usually, we have one system or the other, so have
3666     // AC = (0..20)
3667 
3668     // We then compare the two:
3669     // if (attack_roll - 10 >= ac_roll) then hit occurs.
3670     // This is:
3671     // (1..20) + (0..[0..20]) + [0..7] - 10 >= (0..20)
3672 
3673     // First, we determine those cases where the attacker is guaranteed
3674     // to hit.
3675     forcehit = attack->alwayshit;
3676     if (src && weapon && (style == ATTACKSTYLE_THROWN))
3677     {
3678 	// Check for true aim ability.
3679 	if (weapon->defn().specialskill == SKILL_WEAPON_TRUEAIM)
3680 	{
3681 	    if (src->hasSkill(SKILL_WEAPON_TRUEAIM))
3682 		forcehit = true;
3683 	}
3684     }
3685 
3686     // Next, if they are guaranteed to miss.  Missing always takes
3687     // precedence.
3688     if (src && src->hasIntrinsic(INTRINSIC_OFFBALANCE) &&
3689 	    (style == ATTACKSTYLE_THROWN || style == ATTACKSTYLE_MELEE))
3690     {
3691 	// Off balance creatures will always miss
3692 	forcemiss = true;
3693     }
3694 
3695     hitroll = rand_range(1, 20);
3696 
3697     // Check for guaranteed misses or criticals....
3698     if (forcemiss || (hitroll == 1 && !forcehit))
3699     {
3700 	// Critical miss.
3701 	if (src)
3702 	    src->formatAndReport("%U completely <miss>!");
3703 	else
3704 	    reportMessage("It completely misses!");
3705 	return true;
3706     }
3707 
3708     if (src)
3709     {
3710 	modifier = src->getAttackBonus(attack, style);
3711 	// Determine skill level with this weapon and this style.
3712 	skilllevel = src->getWeaponSkillLevel(weapon, style);
3713     }
3714     else
3715     {
3716 	modifier = 0;
3717 	skilllevel = 2;	// Non-sourced match monsters for behaviour.
3718     }
3719 
3720     // The base modifier gets a skilllevel bonus so level 1 people
3721     // get a substantial boost for learning their weapons.
3722     modifier += skilllevel;
3723 
3724     // If there is an attacking weapon, its enchantment becomes
3725     // a modifier.
3726     // We roll these against skill level as well.
3727     if (weapon)
3728 	modifier += weapon->getEnchantment();
3729     if (weapon && launcher)
3730 	modifier += launcher->getEnchantment();
3731 
3732     // We roll against skill level once for total modifier
3733     // rather than once per bonus, this will minimize the effectiveness
3734     // of the bonus - ie, +1 enchant is just as useful as +1 physical
3735     // level.
3736     modifier = rand_roll(modifier, skilllevel - 1);
3737 
3738     // If we are invisible, and the source exists, and it cannot see
3739     // invisible, we get a negative modifier of -4.
3740     if (src)
3741     {
3742 	if (!src->canSense(this))
3743 	{
3744 	    modifier -= 4;
3745 	}
3746 
3747 	// If we are the natural foe of the attacker, +4
3748 	if (src->defn().naturalfoe == defn().mobtype)
3749 	{
3750 	    modifier += 4;
3751 	}
3752 
3753 	// If we are attacked by someone in a tree at range,
3754 	// opposite effect.
3755 	if (style == ATTACKSTYLE_THROWN &&
3756 	    src->hasIntrinsic(INTRINSIC_INTREE) &&
3757 	    !hasIntrinsic(INTRINSIC_INTREE))
3758 	{
3759 	    modifier += 4;
3760 	}
3761     }
3762 
3763     attackroll = hitroll + modifier - 10;
3764 
3765     acroll = rollAC(src);
3766 
3767     if (hitroll == 20)
3768     {
3769 	int		threatroll;
3770 
3771 	// We now have a critical threat.  We check to see if we hit
3772 	// a second time.
3773 	threatroll = rand_range(1, 20);
3774 	attackroll = threatroll + modifier - 10;
3775 	if (threatroll == 20 ||
3776 	    (threatroll != 1 &&
3777 	            (attackroll >= acroll)) )
3778 	{
3779 	    multiplier++;
3780 	}
3781     }
3782     else
3783     {
3784 	if (attackroll < acroll && !forcehit)
3785 	{
3786 	    // A miss...
3787 	    if (src)
3788 	    {
3789 		// Cannot use M:miss trick as then reflexive is handled
3790 		// backwards.  Maybe reflexive should analyse verb
3791 		// to find direction?
3792 		BUF	buf = formatToString("%U <miss> %MU.", src, 0,
3793 						this, 0);
3794 		reportMessage(buf);
3795 	    }
3796 	    else if (weapon)
3797 		formatAndReport("%IU <I:miss> %U.", weapon);
3798 	    else
3799 		formatAndReport("It misses %U.");
3800 	    return true;
3801 	}
3802     }
3803 
3804     // Figure out damage reduction.
3805     // We know, other than crits, that attackroll >= acroll.
3806     // However, a high acroll should reduce end damage somewhat.
3807     // Nethack style damage reduction.  Unlike nethack, this
3808     // primarily helps the monsters :>
3809     if (acroll > 10)
3810 	damage_reduction = acroll - 10;
3811 
3812     // We may have a hit.  Check for other special skills.
3813     // Obviously all these skills require that we can actually move :>
3814     // if we can dodge, we get a flat 10% dodge chance.
3815     abletomove = false;
3816     if (!hasIntrinsic(INTRINSIC_ASLEEP) &&
3817 	(!hasIntrinsic(INTRINSIC_PARALYSED) || hasIntrinsic(INTRINSIC_FREEDOM)))
3818     {
3819 	abletomove = true;
3820 	// Check for dodging: Flat 10%.
3821 	if (!forcehit && hasSkill(SKILL_DODGE) && skillProc(SKILL_DODGE))
3822 	{
3823 	    const char *dodgelist[] =
3824 	    {
3825 		"dodge",
3826 		"sidestep",
3827 		"evade",
3828 		0
3829 	    };
3830 	    BUF		weapname;
3831 	    BUF		buf;
3832 
3833 	    if (weapon)
3834 		weapname = weapon->getName(false);
3835 	    else
3836 		weapname.reference("attack");
3837 
3838 	    if (src)
3839 		buf = formatToString("%U <%B1> %MR %B2.",
3840 			this, 0, src, 0,
3841 			rand_string(dodgelist), weapname.buffer());
3842 	    else
3843 		buf = formatToString("%U <%B1> its %B2.",
3844 			this, 0, 0, 0,
3845 			rand_string(dodgelist), weapname.buffer());
3846 	    reportMessage(buf);
3847 	    return true;
3848 	}
3849 
3850 	// Check for moving target: 20% if moved last turn.
3851 	if (!forcehit &&
3852 	    (style == ATTACKSTYLE_THROWN ||
3853 	     style == ATTACKSTYLE_SPELL ||
3854 	     style == ATTACKSTYLE_WAND) &&
3855 	    hasMovedLastTurn() &&
3856 	    hasSkill(SKILL_MOVINGTARGET) &&
3857 	    skillProc(SKILL_MOVINGTARGET))
3858 	{
3859 	    // Phew!  Wonder if anyone will do this?
3860 	    // Yep, I've seen this in practice :>
3861 	    const char *dodgelist[] =
3862 	    {
3863 		"avoid",
3864 		"duck under",
3865 		"roll clear of",
3866 		0
3867 	    };
3868 	    BUF		weapname;
3869 	    BUF		buf;
3870 
3871 	    if (weapon)
3872 		weapname = weapon->getName(false);
3873 	    else
3874 		weapname.reference("attack");
3875 
3876 	    if (src)
3877 		buf = formatToString("%U <%B1> %MR %B2.",
3878 			this, 0, src, 0,
3879 			rand_string(dodgelist), weapname.buffer());
3880 	    else
3881 		buf = formatToString("%U <%B1> its %B2.",
3882 			this, 0, 0, 0,
3883 			rand_string(dodgelist), weapname.buffer());
3884 	    reportMessage(buf);
3885 	    return true;
3886 	}
3887 
3888 	// Check for parry.
3889 	if (!forcehit && hasSkill(SKILL_WEAPON_PARRY))
3890 	{
3891 	    // Check if we actually have valid weapons.
3892 	    ITEM		*myweapon;
3893 	    int			 slot;
3894 
3895 	    for (slot = ITEMSLOT_RHAND; slot <= ITEMSLOT_LHAND; slot++)
3896 	    {
3897 		myweapon = getEquippedItem((ITEMSLOT_NAMES)slot);
3898 		if (!myweapon)
3899 		    continue;
3900 		if (myweapon->getSpecialSkill() != SKILL_WEAPON_PARRY)
3901 		    continue;
3902 
3903 		// This is a parriable weapon..
3904 		// Check if this is off hand.
3905 		if (slot == ITEMSLOT_LHAND)
3906 		{
3907 		    // One must have two weapon skill and pass
3908 		    // the two weapon test.
3909 		    if (!rand_chance(getSecondWeaponChance()))
3910 			continue;
3911 		}
3912 
3913 		// Flat 10% parry chance.
3914 		if (skillProc(SKILL_WEAPON_PARRY))
3915 		{
3916 		    const char *parrylist[] =
3917 		    {
3918 			"block",
3919 			"parry",
3920 			"deflect",
3921 			0
3922 		    };
3923 		    BUF		weapname, myweapname;
3924 		    BUF		buf;
3925 
3926 		    myweapname = myweapon->getName(false);
3927 
3928 		    if (weapon)
3929 			weapname = weapon->getName(false);
3930 		    else
3931 			weapname.reference("attack");
3932 
3933 		    if (src)
3934 			buf = formatToString("%U <%B1> %MR %B2 with %r %B3.",
3935 				this, 0, src, 0,
3936 				rand_string(parrylist), weapname.buffer(),
3937 				myweapname.buffer());
3938 		    else
3939 			buf = formatToString("%U <%B1> its %B2 with %r %B3.",
3940 				this, 0, 0, 0,
3941 				rand_string(parrylist), weapname.buffer(),
3942 				myweapname.buffer());
3943 		    reportMessage(buf);
3944 		    return true;
3945 		}
3946 	    }
3947 	}
3948     }
3949 
3950     if (didhit)
3951 	*didhit = true;
3952 
3953     lived = receiveDamage(attack, src, weapon, launcher, style, multiplier,
3954 			  damage_reduction);
3955 
3956     if (lived && weapon)
3957     {
3958 	weapon->applyPoison(src, this);
3959     }
3960 
3961     // We only trigger reflex attacks on melee attacks.
3962     if (lived && src && (style == ATTACKSTYLE_MELEE))
3963     {
3964 	ATTACK_NAMES		reflex_attack;
3965 
3966 	reflex_attack = (ATTACK_NAMES) defn().reflex_attack;
3967 	// Successful attack, get the reflex...
3968 	if (reflex_attack != ATTACK_NONE)
3969 	{
3970 	    if (rand_chance(defn().reflex_chance))
3971 	    {
3972 		// We do NOT go to receiveAttack here, as we will
3973 		// get infinite loop.  Further, the damage is not
3974 		// based on to-hit, but rather a flat chance.
3975 		// Thus, we go straight to receive damage.
3976 		src->receiveDamage(reflex_attack, this, 0, 0, ATTACKSTYLE_MELEE);
3977 	    }
3978 	}
3979 	else if (abletomove)
3980 	{
3981 	    // Still a chance that we'll decide to riposte the attack.
3982 	    if (hasSkill(SKILL_WEAPON_RIPOSTE))
3983 	    {
3984 		// Check if we actually have valid weapons.
3985 		ITEM		*myweapon;
3986 		int			 slot;
3987 
3988 		for (slot = ITEMSLOT_RHAND; slot <= ITEMSLOT_LHAND; slot++)
3989 		{
3990 		    myweapon = getEquippedItem((ITEMSLOT_NAMES)slot);
3991 		    if (!myweapon)
3992 			continue;
3993 		    if (myweapon->getSpecialSkill() != SKILL_WEAPON_RIPOSTE)
3994 			continue;
3995 
3996 		    // This is a parriable weapon..
3997 		    // Check if this is off hand.
3998 		    if (slot == ITEMSLOT_LHAND)
3999 		    {
4000 			// One must have two weapon skill and pass
4001 			// the two weapon test.
4002 			if (!rand_chance(getSecondWeaponChance()))
4003 			    continue;
4004 		    }
4005 
4006 		    // Flat 10% riposte chance.  If you succeed at riposte,
4007 		    // you always do damage without triggering the attackers
4008 		    // reflex.  This avoids infinite loops and ensures that
4009 		    // riposte isn't a bad thing.
4010 		    // Well, I guess worshipers of Tlosh may be upset at
4011 		    // losing piety for a melee kill.
4012 		    if (skillProc(SKILL_WEAPON_RIPOSTE))
4013 		    {
4014 			const char *ripostelist[] =
4015 			{
4016 			    "riposte",
4017 			    "counterstrike",
4018 			    "counterattack",
4019 			    0
4020 			};
4021 			BUF		weapname, myweapname;
4022 
4023 			if (weapon)
4024 			    weapname = weapon->getName(false);
4025 			else
4026 			    weapname.reference("attack");
4027 
4028 			myweapname = myweapon->getName(false);
4029 
4030 			BUF		buf;
4031 			if (src)
4032 			    buf = formatToString("%U <%B1> %MR %B2 with %r %B3.",
4033 				    this, 0, src, 0,
4034 				    rand_string(ripostelist), weapname.buffer(),
4035 				    myweapname.buffer());
4036 			else
4037 			    buf = formatToString("%U <%B1> its %B2 with %r %B3.",
4038 				    this, 0, 0, 0,
4039 				    rand_string(ripostelist), weapname.buffer(),
4040 				    myweapname.buffer());
4041 			reportMessage(buf);
4042 
4043 			// Actually apply riposte effect.
4044 			src->receiveDamage(myweapon->getAttack(), this, 0, 0, ATTACKSTYLE_MELEE);
4045 
4046 			return lived;
4047 		    }
4048 		}
4049 	    }
4050 	}
4051     }
4052     return lived;
4053 }
4054 
4055 bool
receiveDamage(ATTACK_NAMES attack,MOB * src,ITEM * weapon,ITEM * launcher,ATTACKSTYLE_NAMES style,int initialmultiplier,int initialdamagereduction)4056 MOB::receiveDamage(ATTACK_NAMES attack, MOB *src, ITEM *weapon,
4057 		    ITEM *launcher,
4058 		    ATTACKSTYLE_NAMES style,
4059 		    int initialmultiplier,
4060 		    int initialdamagereduction)
4061 {
4062     return receiveDamage(&glb_attackdefs[attack], src, weapon, launcher,
4063 			    style, initialmultiplier,
4064 			    initialdamagereduction);
4065 }
4066 
4067 bool
receiveDamage(const ATTACK_DEF * attack,MOB * src,ITEM * weapon,ITEM * launcher,ATTACKSTYLE_NAMES style,int initialmultiplier,int initialdamagereduction)4068 MOB::receiveDamage(const ATTACK_DEF *attack, MOB *src, ITEM *weapon,
4069 		    ITEM *launcher,
4070 		    ATTACKSTYLE_NAMES style,
4071 		    int initialmultiplier,
4072 		    int initialdamagereduction)
4073 {
4074     const char		*verb;
4075     ELEMENT_NAMES	 element;
4076     int			 multiplier = initialmultiplier;
4077     ATTACK_NAMES	 attackname;
4078     int			 damage_reduction = initialdamagereduction;
4079     bool		 silversafe = false;
4080     bool		 goldsafe = false;
4081 
4082     if (isAvatar())
4083     {
4084 	// Being damaged will always break the auto run.
4085 	if (glbAutoRunEnabled)
4086 	    formatAndReport("%U <stop> over concerns for your well-being.");
4087 	glbAutoRunEnabled = false;
4088     }
4089 
4090     // TODO: Add innate damage reduction
4091     // Roll against damage reduction.
4092     // We want a possibility of 0!
4093     if (damage_reduction)
4094 	damage_reduction = rand_roll(damage_reduction + 1, -1) - 1;
4095 
4096     // Someone attacked us!  We will make it our target if it exists!
4097     // This covers cases where people damage is "less" intentionally,
4098     // ie: zapping wands, reading scrolls, etc.
4099     noteAttacker(src);
4100 
4101     verb = attack->verb;
4102 
4103     // We may have a critical already...
4104     if (multiplier > 1)
4105 	verb = "crush";
4106 
4107     element = (ELEMENT_NAMES) attack->element;
4108 
4109     // Check elemental resistance or vulnerability.
4110     switch (element)
4111     {
4112 	case ELEMENT_FIRE:
4113 	    if (hasIntrinsic(INTRINSIC_RESISTFIRE))
4114 		multiplier = 0;
4115 	    if (hasIntrinsic(INTRINSIC_VULNFIRE))
4116 		multiplier++;
4117 	    break;
4118 	case ELEMENT_COLD:
4119 	    if (hasIntrinsic(INTRINSIC_RESISTCOLD))
4120 		multiplier = 0;
4121 	    if (hasIntrinsic(INTRINSIC_VULNCOLD))
4122 		multiplier++;
4123 	    break;
4124 	case ELEMENT_ACID:
4125 	    if (hasIntrinsic(INTRINSIC_RESISTACID))
4126 		multiplier = 0;
4127 	    if (hasIntrinsic(INTRINSIC_VULNACID))
4128 		multiplier++;
4129 
4130 	    // In any case, we lose our stoning when we take this
4131 	    // sort of damage.
4132 	    clearIntrinsic(INTRINSIC_STONING);
4133 	    break;
4134 	case ELEMENT_SHOCK:
4135 	    if (hasIntrinsic(INTRINSIC_RESISTSHOCK))
4136 		multiplier = 0;
4137 	    if (hasIntrinsic(INTRINSIC_VULNSHOCK))
4138 		multiplier++;
4139 	    break;
4140 	case ELEMENT_LIGHT:
4141 	    // Blind people obviously don't care about light...
4142 	    if (hasIntrinsic(INTRINSIC_BLIND))
4143 		multiplier = 0;
4144 	    // Sleeping people are blind.
4145 	    if (hasIntrinsic(INTRINSIC_ASLEEP))
4146 		multiplier = 0;
4147 	    break;
4148 	case ELEMENT_PHYSICAL:
4149 	    if (hasIntrinsic(INTRINSIC_RESISTPHYSICAL))
4150 		multiplier = 0;
4151 	    if (hasIntrinsic(INTRINSIC_VULNPHYSICAL))
4152 		multiplier++;
4153 	    break;
4154 	case ELEMENT_DEATH:
4155 	    // The undead get free resistance
4156 	    // Or should this be death resistance?
4157 	    if (glb_mobdefs[myDefinition].mobtype == MOBTYPE_UNDEAD ||
4158 		glb_mobdefs[myDefinition].mobtype == MOBTYPE_DAEMON)
4159 		multiplier = 0;
4160 	    if (hasIntrinsic(INTRINSIC_DRESSED_NECROMANCER))
4161 		multiplier = 0;		// You look too cool to die.
4162 	    break;
4163 	case ELEMENT_POISON:
4164 	case ELEMENT_REFLECTIVITY:
4165 	case NUM_ELEMENTS:
4166 	    // Unknown element..
4167 	    UT_ASSERT(!"Unhandled ELEMENT!");
4168 	    break;
4169     }
4170 
4171     // Check if it has a vulnerability to the material...
4172     if (hasIntrinsic(INTRINSIC_VULNSILVER))
4173     {
4174 	if (weapon && weapon->getMaterial() == MATERIAL_SILVER)
4175 	{
4176 	    multiplier++;
4177 	    verb = "sear";
4178 	    silversafe = true;
4179 	}
4180 	// Creatures made of silver that engage in melee do silver
4181 	// damage.
4182 	if (!weapon && src && (style == ATTACKSTYLE_MELEE) &&
4183 	    (src->getMaterial() == MATERIAL_SILVER))
4184 	{
4185 	    multiplier++;
4186 	    verb = "sear";
4187 	}
4188     }
4189     if (hasIntrinsic(INTRINSIC_GOLDALLERGY))
4190     {
4191 	if (weapon && weapon->getMaterial() == MATERIAL_GOLD)
4192 	{
4193 	    multiplier++;
4194 	    verb = "sear";
4195 	    goldsafe = true;
4196 	}
4197 	// Creatures made of gold that engage in melee do gold
4198 	// damage.
4199 	if (!weapon && src && (style == ATTACKSTYLE_MELEE) &&
4200 	    (src->getMaterial() == MATERIAL_GOLD))
4201 	{
4202 	    multiplier++;
4203 	    verb = "sear";
4204 	}
4205     }
4206 
4207     // Check if we are to damage silver weapons
4208     if (!silversafe && weapon && weapon->getMaterial() == MATERIAL_SILVER
4209 	    && (style == ATTACKSTYLE_MELEE || style == ATTACKSTYLE_THROWN)
4210 	    && !weapon->isArtifact())
4211     {
4212 	if (rand_chance(1))
4213 	{
4214 	    if (src)
4215 		src->formatAndReport("%R %Iu <I:dull>.", weapon);
4216 	    else
4217 		formatAndReport("%Iu <I:dull>.", weapon);
4218 	    weapon->enchant(-1);
4219 	}
4220     }
4221 
4222     // Check if we are to damage gold weapons
4223     if (!goldsafe && weapon && weapon->getMaterial() == MATERIAL_GOLD
4224 	    && (style == ATTACKSTYLE_MELEE || style == ATTACKSTYLE_THROWN)
4225 	    && !weapon->isArtifact())
4226     {
4227 	// Gold is a lot more malleable
4228 	if (rand_chance(10))
4229 	{
4230 	    if (src)
4231 		src->formatAndReport("%R %Iu <I:dull>.", weapon);
4232 	    else
4233 		formatAndReport("%Iu <I:dull>.", weapon);
4234 	    weapon->enchant(-1);
4235 	}
4236     }
4237 
4238     // Earth elementals take extra damage from anyone who can
4239     // dig
4240     // Provided, of course, it is a melee attack.
4241     if (getDefinition() == MOB_EARTHELEMENTAL &&
4242 	((weapon && weapon->hasIntrinsic(INTRINSIC_DIG)) ||
4243 	 (!weapon && src && src->hasIntrinsic(INTRINSIC_DIG) && (style == ATTACKSTYLE_MELEE))))
4244     {
4245 	multiplier++;
4246 	verb = "disintegrate";
4247     }
4248 
4249     // Check to see if damage was resisted.  If so, we want the
4250     // attacker to make a note of it.  Notice that vuln to silver may
4251     // cause one to think that someone can't resist fire - this is intended.
4252     // (Silver wands of fire are great against he who cannot be spelled
4253     // properly)
4254     if (!multiplier && src)
4255     {
4256 	src->aiNoteThatTargetCanResist(this, element);
4257     }
4258 
4259     // Apply damage...
4260     int		damage;
4261 
4262     damage = rand_dice(attack->damage, multiplier);
4263 
4264     // If it came from a weapon, the weapon's enchantment is added
4265     // in.  Note this DOES include the multiplier!
4266     // We only add the bonus to the base attack to avoid double-dipping
4267     // with flaming swords and what not.
4268     // We also roll against the total enchantment to effectively
4269     // cap barbarian bonuses.
4270     int		enchantbonus = 0;
4271 
4272     if (attack->sameattack == ATTACK_NONE)
4273     {
4274 	if (weapon)
4275 	    enchantbonus += weapon->getEnchantment() * multiplier;
4276 	if (weapon && launcher)
4277 	    enchantbonus += launcher->getEnchantment() * multiplier;
4278 
4279 	// If there is a multiplier, we boost the re-rolls.
4280 	enchantbonus = rand_roll(enchantbonus, multiplier-1);
4281     }
4282     damage += enchantbonus;
4283 
4284     // Apply damage reduction.  This is not allowed to
4285     // set the damage to 0.  (Or should it?)
4286     if (damage > 0)
4287     {
4288 	damage -= damage_reduction;
4289 	if (damage <= 0)
4290 	    damage = 1;
4291     }
4292     else
4293     {
4294 	// Damage is not greater than zero.  It could, however,
4295 	// be less than zero.  We don't want damage attacks to
4296 	// heal people!
4297 	damage = 0;
4298     }
4299 
4300     if (damage == 0)
4301     {
4302 	BUF		buf;
4303 
4304 	// The attack did no damage.  Likely resistance, should
4305 	// give feedback so loser doesn't keep acting.
4306 	if (src)
4307 	    formatAndReport("%U <ignore> %MR attack.", src);
4308 	else if (weapon)
4309 	    formatAndReport("%U <ignore> %IR attack.", weapon);
4310 	else
4311 	    formatAndReport("%U <ignore> its attack.");
4312     }
4313     else
4314     {
4315 	BUF	buf;
4316 
4317 
4318 	if (attack->eat > 0 && src)
4319 	{
4320 	    if (!isBloodless())
4321 	    {
4322 		src->feed(attack->eat);
4323 	    }
4324 	}
4325 
4326 	// Regular attack.
4327 	if (src)
4328 	    buf = formatToString("%U <%B1> %MU",
4329 			src, 0, this, 0,
4330 			verb, 0);
4331 	else if (weapon)
4332 	    buf = formatToString("%U <%B1> %MU",
4333 			0, weapon, this, 0,
4334 			verb, 0);
4335 	else
4336 	{
4337 	    buf = formatToString("%p <%B1> %MU",
4338 			0, 0, this, 0,
4339 			verb, 0);
4340 	}
4341 	if (damage > 10)
4342 	    buf.append('!');
4343 	else
4344 	    buf.append('.');
4345 	reportMessage(buf);
4346     }
4347 
4348     // Test for explosion...
4349     if (attack->explode_chance &&
4350 	rand_chance(attack->explode_chance))
4351     {
4352 	// TODO: This is improper conjugation!
4353 	formatAndReport("%B1 explodes!", attack->explode_name);
4354 
4355 	// Create the relevant smoke.
4356 	if (attack->explode_smoke != SMOKE_NONE)
4357 	{
4358 	    int		dx, dy;
4359 	    SMOKE_NAMES smoke = (SMOKE_NAMES)
4360 				    attack->explode_smoke;
4361 
4362 	    for (dy = -1; dy <= 1; dy++)
4363 		for (dx = -1; dx <= 1; dx++)
4364 		    if (glbCurLevel->canMove(getX()+dx, getY()+dy,
4365 					    MOVE_STD_FLY))
4366 			glbCurLevel->setSmoke(getX()+dx, getY()+dy,
4367 					    smoke, src);
4368 	}
4369 
4370 	// Inflict the proper attacks...
4371 	// Do not reply apply to this, or we may kill ourselves.
4372 	if (src && attack->explode_attack != ATTACK_NONE)
4373 	{
4374 	    ATTACK_NAMES		oldeffect;
4375 	    MOBREF			myself;
4376 	    MOBREF			srcref;
4377 
4378 	    myself.setMob(this);
4379 	    srcref.setMob(src);
4380 
4381 	    oldeffect = ourEffectAttack;
4382 	    ourEffectAttack = (ATTACK_NAMES)
4383 				    attack->explode_attack;
4384 
4385 	    glbCurLevel->fireBall(getX(), getY(), 1, true,
4386 				    areaAttackCBStatic, &srcref);
4387 
4388 	    ourEffectAttack = oldeffect;
4389 
4390 	    // Check to see if we are still alive!  If not, we want
4391 	    // to return early.
4392 	    if (!myself.getMob() ||
4393 		myself.getMob() != this ||
4394 		myself.getMob()->hasIntrinsic(INTRINSIC_DEAD))
4395 	    {
4396 		return false;
4397 	    }
4398 	}
4399     }
4400 
4401     // TODO: Should we do this even if no damage?
4402     if (damage)
4403     {
4404 	// Chance to wake up as a result of being hit.  Check before
4405 	// inflictable in case this is a sleep attack.
4406 	// Only get woken up 1 in 10 for 1 damage, additional 10%
4407 	// for each more damage, to guarantee wake up with 10 damage.
4408 	if (hasIntrinsic(INTRINSIC_ASLEEP))
4409 	{
4410 	    int noisethreshold = 10;
4411 	    if (hasIntrinsic(INTRINSIC_TIRED))
4412 		noisethreshold = 20;
4413 	    if (rand_choice(noisethreshold) < damage)
4414 	    {
4415 		// Notification done automatically.
4416 		clearIntrinsic(INTRINSIC_ASLEEP);
4417 	    }
4418 	}
4419 
4420 	// Check for inflictable intrinsic...
4421 	if (attack->inflict_intrinsic != INTRINSIC_NONE)
4422 	{
4423 	    if (rand_chance(attack->inflict_chance))
4424 	    {
4425 		if (!hasIntrinsic((INTRINSIC_NAMES) attack->inflict_resistance)
4426 		    && (!src || !src->hasIntrinsic((INTRINSIC_NAMES) attack->inflict_negate)))
4427 		{
4428 		    setTimedIntrinsic(src, (INTRINSIC_NAMES) attack->inflict_intrinsic,
4429 				      rand_dice(attack->inflict_duration));
4430 		}
4431 	    }
4432 	}
4433 
4434 	// If the attack uses the light element, it will cause
4435 	// blindness.
4436 	if (element == ELEMENT_LIGHT)
4437 	{
4438 	    // We cap this at 6 to prevent stupidly long blindness
4439 	    // (and also the nerfing of light weapons)
4440 	    setTimedIntrinsic(src, INTRINSIC_BLIND, (damage < 6) ? damage : 6);
4441 	}
4442 
4443 	// Decrement the weapon's charges to reflect the hit.
4444 	if (weapon && src)
4445 	{
4446 	    // Only use charges in melee combat, not in ranged combat
4447 	    // such as by zapping the rapier.
4448 	    if (weapon->getDefinition() == ITEM_LIGHTNINGRAPIER &&
4449 		style == ATTACKSTYLE_MELEE)
4450 	    {
4451 		weapon->useCharge();
4452 		if (!weapon->getCharges())
4453 		{
4454 		    src->formatAndReport("%R %Iu <I:power> down.", weapon);
4455 		    weapon->setDefinition(ITEM_RAPIER);
4456 		    src->rebuildAppearance();
4457 		}
4458 		else
4459 		{
4460 		    // Quicken!
4461 		    src->setTimedIntrinsic(src, INTRINSIC_QUICK, 2);
4462 		}
4463 	    }
4464 	}
4465     }
4466 
4467     myHP -= damage;
4468     if (myHP <= 0)
4469     {
4470 	bool		madecorpse = false;
4471 
4472 	// Determine if the creature actually dies.  They may have life
4473 	// saving...
4474 	if (attemptLifeSaving("%U <die>!"))
4475 	{
4476 	    // Proceed to the next attack.  (Which will hopefuly
4477 	    // finish the job in a suitably ironic manner :>)
4478 	    goto nextattack;
4479 	}
4480 
4481 	// Determine if they can recover through polymorph...
4482 	if (myBaseType.getMob())
4483 	{
4484 	    // However, if they are unchanging, the unpolymorph
4485 	    // will fail, so they should die anyways.
4486 	    if (hasIntrinsic(INTRINSIC_UNCHANGING))
4487 	    {
4488 		formatAndReport("%U <convulse> violently.");
4489 	    }
4490 	    else
4491 	    {
4492 		// Unpolymorph with system shock.
4493 		actionUnPolymorph(false, true);
4494 
4495 		// this is now deleted, so return false!
4496 		return false;
4497 	    }
4498 	}
4499 
4500 	// Getting here implies that the creature will truly die.
4501 
4502 	const char *deathlist;
4503 	const char *deathmsg;
4504 
4505 	deathlist = glb_deathmsgdefs[
4506 			glb_mobdefs[getDefinition()].deathmsg].msglist;
4507 
4508 	deathmsg = glb_deathmsg_msgdefs[ (u8)
4509 			deathlist[rand_choice(strlen(deathlist))]].msg;
4510 
4511 	formatAndReport(deathmsg);
4512 
4513 	// If a possessed creature dies, release the possession
4514 	// with system shock
4515 	if (hasIntrinsic(INTRINSIC_POSSESSED))
4516 	{
4517 	    actionReleasePossession(true);
4518 	}
4519 
4520 	// Create an item from this dude and dump on the level...
4521 	madecorpse = rand_chance(defn().corpsechance);
4522 
4523 	// If the dead creature is a familiar we always leave a corpse
4524 	// to give you the chance to resurrect your pet.
4525 	// We only do this if there is a non-zero corpse chance
4526 	// to avoid corpse generation of impossible monsters
4527 	if (hasIntrinsic(INTRINSIC_FAMILIAR) && defn().corpsechance)
4528 	{
4529 	    // Writing this while travelling 890km/h in a westerly
4530 	    // direction.  Thankfully the new 777s have a normal power
4531 	    // output so I should not have to run out of power on
4532 	    // this 13 hour flight.  The p1610 has good battery power,
4533 	    // but not that good!
4534 	    madecorpse = true;
4535 	}
4536 
4537 	// See if the attacker has the clean kill ability and this was
4538 	// a melee kill.  In that case, we increase the odds of a corpse.
4539 	if (!madecorpse && defn().corpsechance &&
4540 	    style == ATTACKSTYLE_MELEE && src && src->hasSkill(SKILL_CLEANKILL))
4541 	{
4542 	    // Re-roll our chance.
4543 	    madecorpse = rand_chance(defn().corpsechance);
4544 	}
4545 
4546 	// Summoned critters never leave corpses.
4547 	// Summoned creatures that become familiars likewise leave not
4548 	// a corpse.
4549 	if (hasIntrinsic(INTRINSIC_SUMMONED))
4550 	    madecorpse = false;
4551 
4552 	// Tell the world we were slain.
4553 	triggerAsDeath(attack, src, weapon, madecorpse);
4554 
4555 	// Find the worst poison that we have so we can apply it
4556 	// to the corpse.
4557 	POISON_NAMES		worstpoison = POISON_NONE;
4558 
4559 	{
4560 	    int		i;
4561 
4562 	    for (i = POISON_NONE; i < NUM_POISONS; i++)
4563 	    {
4564 		if (glb_poisondefs[i].intrinsic != INTRINSIC_NONE)
4565 		{
4566 		    if (hasIntrinsic((INTRINSIC_NAMES) glb_poisondefs[i].intrinsic))
4567 		    {
4568 			worstpoison = (POISON_NAMES) i;
4569 		    }
4570 		}
4571 	    }
4572 	}
4573 
4574 	// Clear their death intrinsics so if they are ressed, they
4575 	// don't keep burning...
4576 	// We want to do this AFTER the victory screen, or the
4577 	// death intrinsics won't be listed.
4578 	clearDeathIntrinsics(true);
4579 
4580 	glbCurLevel->unregisterMob(this);
4581 
4582 	ITEM		*cur;
4583 	bool		 isbelowgrade;
4584 
4585 	// We wish to track if the resulting items should be put
4586 	// underground.
4587 	isbelowgrade = hasIntrinsic(INTRINSIC_INPIT) ||
4588 		       hasIntrinsic(INTRINSIC_SUBMERGED);
4589 
4590 	// Drop our inventory on this square...
4591 	while ((cur = myInventory))
4592 	{
4593 	    // We manually drop the item for efficiency.
4594 	    myInventory = cur->getNext();
4595 	    cur->setNext(0);
4596 
4597 	    cur->markBelowGrade(isbelowgrade);
4598 
4599 	    // Because we want the grade of the item to be used
4600 	    // we set ignoregrade.  This seems entirely backwards
4601 	    // but I'm sure I had a good reason for calling it that.
4602 	    glbCurLevel->acquireItem(cur, getX(), getY(), 0, 0, true);
4603 	}
4604 
4605 	// Grant experience to the slayer.
4606 	// ExpLevel is considered differently from hit die as it reflects
4607 	// the full challenge of the critter.
4608 	// DO NOT reward experience for suiciding!
4609 	// (I once gained a level by dying :>)
4610 	// We do not get experience for offing allies
4611 	if (src && src != this)
4612 	{
4613 	    if (!hasCommonMaster(src))
4614 		src->receiveDeathExp(myExpLevel);
4615 
4616 	    // Grant piety for killing:
4617 	    src->pietyKill(this, style);
4618 	}
4619 
4620 	// If we created a corpse, the corpse tracks us and thus
4621 	// will delete this.  Otherwise, we better clean up.
4622 	if (madecorpse)
4623 	{
4624 	    // We also want to set the dead intrinsic so those
4625 	    // following old mobrefs will correctly determine our state.
4626 	    setIntrinsic(INTRINSIC_DEAD);
4627 
4628 	    // Note that acquiring a corpse on a square may delete the
4629 	    // mob, ie: if it falls into a pool of acid.
4630 	    // That is why we had to set the dead intrinsic first.
4631 	    cur = ITEM::createCorpse(this);
4632 	    cur->markBelowGrade(isbelowgrade);
4633 
4634 	    // Make the object poisoned.
4635 	    if (worstpoison != POISON_NONE)
4636 		cur->makePoisoned(worstpoison, 1);
4637 
4638 	    // Mark ignore grade so we end up where we died.
4639 	    glbCurLevel->acquireItem(cur, getX(), getY(), 0, 0, true);
4640 	}
4641 	else
4642 	{
4643 	    delete this;
4644 	}
4645 
4646 	// In any case, the mob is removed from the map list so
4647 	// is deleted in the eyes of the caller.
4648 	return false;
4649     }
4650 
4651 nextattack:
4652     attackname = (ATTACK_NAMES) attack->sameattack;
4653     if (attackname != ATTACK_NONE)
4654 	return receiveDamage(attackname, src, weapon, launcher,
4655 			     style,
4656 			     initialmultiplier, initialdamagereduction);
4657 
4658     return true;
4659 }
4660 
4661 void
clearDeathIntrinsics(bool silent)4662 MOB::clearDeathIntrinsics(bool silent)
4663 {
4664     // Remove all intrinsics that have clearondeath set...
4665     INTRINSIC_NAMES		intrinsic;
4666 
4667     for (intrinsic = INTRINSIC_NONE; intrinsic < NUM_INTRINSICS;
4668 	 intrinsic = (INTRINSIC_NAMES) (intrinsic+1))
4669     {
4670 	if (glb_intrinsicdefs[intrinsic].clearondeath)
4671 	{
4672 	    clearIntrinsic(intrinsic, silent);
4673 	}
4674     }
4675 }
4676 
4677 void
receiveDeathExp(int explevel)4678 MOB::receiveDeathExp(int explevel)
4679 {
4680     float		basic;
4681     float		newbiebonus;
4682 
4683     // The rough attempt at balance is this:
4684     // 1) We can kill people of our own exp level easily
4685     // 2) You must kill 10 + myExpLevel to get a new level
4686     // 3) You get a new level every 1000 exp.
4687 
4688     basic = 1000.0f / (10 + myExpLevel);
4689     while (explevel > myExpLevel)
4690     {
4691 	basic *= 1.10f;
4692 	explevel--;
4693     }
4694     while (explevel < myExpLevel)
4695     {
4696 	basic *= 0.8f;
4697 	explevel++;
4698     }
4699 
4700     newbiebonus = 1.0f;
4701 #if 0
4702     // This doubles the rate of experience at early levels.
4703     // It has been removed as monster generation is now safer.
4704     if (myExpLevel < 10)
4705     {
4706 	newbiebonus = 2.0f - (myExpLevel / 10.0f);
4707     }
4708 #endif
4709 
4710     basic *= newbiebonus;
4711 
4712     // Round up...
4713     receiveExp((int)basic + 1);
4714 }
4715 
4716 SPELL_NAMES
findSpell(GOD_NAMES god,int * pnumspell) const4717 MOB::findSpell(GOD_NAMES god, int *pnumspell) const
4718 {
4719     // Pick a random skill
4720     SPELL_NAMES		spells[NUM_SPELLS];
4721     SPELL_NAMES		spell;
4722     int			numspell = 0;
4723 
4724     FOREACH_SPELL(spell)
4725     {
4726 	if (spell == SPELL_NONE)
4727 	    continue;
4728 
4729 	if (hasSpell(spell))
4730 	    continue;
4731 
4732 	if (god == GOD_AGNOSTIC)
4733 	    continue;
4734 
4735 	// See if this is a valid god.
4736 	if (!strchr(glb_spelldefs[spell].god, (char) god))
4737 	    continue;
4738 
4739 	// Make sure we have prereq.
4740 	if (!canLearnSpell(spell))
4741 	    continue;
4742 
4743 	// Valid addition.
4744 	spells[numspell++] = spell;
4745     }
4746 
4747     if (numspell)
4748 	spell = spells[rand_choice(numspell)];
4749     else
4750 	spell = SPELL_NONE;
4751 
4752     if (pnumspell)
4753 	*pnumspell = numspell;
4754 
4755     return spell;
4756 }
4757 
4758 
4759 SKILL_NAMES
findSkill(GOD_NAMES god,int * pnumskill) const4760 MOB::findSkill(GOD_NAMES god, int *pnumskill) const
4761 {
4762     // Pick a random skill
4763     SKILL_NAMES		skills[NUM_SKILLS];
4764     SKILL_NAMES		skill;
4765     int			numskill = 0;
4766 
4767     FOREACH_SKILL(skill)
4768     {
4769 	if (skill == SKILL_NONE)
4770 	    continue;
4771 
4772 	if (hasSkill(skill))
4773 	    continue;
4774 
4775 	if (god == GOD_AGNOSTIC)
4776 	    continue;
4777 
4778 	// See if this is a valid god.
4779 	if (!strchr(glb_skilldefs[skill].god, (char) god))
4780 	    continue;
4781 
4782 	// Make sure we have prereq.
4783 	if (!canLearnSkill(skill))
4784 	    continue;
4785 
4786 	// Valid addition.
4787 	skills[numskill++] = skill;
4788     }
4789 
4790     if (numskill)
4791 	skill = skills[rand_choice(numskill)];
4792     else
4793 	skill = SKILL_NONE;
4794 
4795     if (pnumskill)
4796 	*pnumskill = numskill;
4797 
4798     return skill;
4799 }
4800 
4801 void
learnSpell(SPELL_NAMES spell,int duration)4802 MOB::learnSpell(SPELL_NAMES spell, int duration)
4803 {
4804     int		i;
4805 
4806     // Check for bad spell..
4807     if (spell == (SPELL_NAMES) -1)
4808 	return;
4809 
4810     if (spell == SPELL_NONE)
4811 	return;
4812 
4813     // Find the right intrinsic...
4814 
4815     i = glb_spelldefs[spell].intrinsic;
4816 
4817     UT_ASSERT(i != NUM_INTRINSICS);
4818     if (i == NUM_INTRINSICS)
4819     {
4820 	return;
4821     }
4822 
4823     if (duration < 0)
4824     {
4825 	setIntrinsic((INTRINSIC_NAMES) i);
4826     }
4827     else
4828     {
4829 	setTimedIntrinsic(this, (INTRINSIC_NAMES) i, duration);
4830     }
4831 
4832     // Apply to base self, if present.
4833     MOB		*base;
4834 
4835     base = myBaseType.getMob();
4836     if (base)
4837 	base->learnSpell(spell, duration);
4838 }
4839 
4840 void
learnSkill(SKILL_NAMES skill,int duration)4841 MOB::learnSkill(SKILL_NAMES skill, int duration)
4842 {
4843     int		i;
4844 
4845     // Check for bad skill..
4846     if (skill == SKILL_NONE)
4847 	return;
4848 
4849     // Find the right intrinsic...
4850 
4851     i = glb_skilldefs[skill].intrinsic;
4852 
4853     UT_ASSERT(i != NUM_INTRINSICS);
4854     if (i == NUM_INTRINSICS)
4855     {
4856 	return;
4857     }
4858 
4859     if (duration < 0)
4860     {
4861 	setIntrinsic((INTRINSIC_NAMES) i);
4862     }
4863     else
4864     {
4865 	setTimedIntrinsic(this, (INTRINSIC_NAMES) i, duration);
4866     }
4867 
4868     // Apply to base self, if present.
4869     MOB		*base;
4870 
4871     base = myBaseType.getMob();
4872     if (base)
4873 	base->learnSkill(skill, duration);
4874 }
4875 void
gainLevel()4876 MOB::gainLevel()
4877 {
4878     // If this is a creature, gain a physical level.  If it causes
4879     // us to evolve, evolve.
4880     if (glbStressTest || !isAvatar())
4881     {
4882 	MOB		*base;
4883 	BUF		 buf;
4884 
4885 	base = myBaseType.getMob();
4886 
4887 	int		 newhp, newmp = 0;
4888 	int		 newhd, newmd = 0;
4889 
4890 	newhd = 2;
4891 	newhp = rand_dice(newhd, 4, newhd);
4892 
4893 	// If we have magic, grant us another half magic level.
4894 	if (myMaxMP)
4895 	{
4896 	    newmd = 1;
4897 	    newmp = rand_dice(newmd, 4, newmd);
4898 	}
4899 
4900 	myHitDie += newhd;
4901 	myHP += newhp;
4902 	myMaxHP += newhp;
4903 
4904 	myMagicDie += newmd;
4905 	myMP += newmp;
4906 	myMaxMP += newmp;
4907 
4908 	myExpLevel++;
4909 
4910 	if (base)
4911 	{
4912 	    base->myHitDie += newhd;
4913 	    base->myHP += newhp;
4914 	    base->myMaxHP += newhp;
4915 
4916 	    base->myMagicDie += newmd;
4917 	    base->myMP += newmp;
4918 	    base->myMaxMP += newmp;
4919 
4920 	    base->myExpLevel++;
4921 	}
4922 
4923 	formatAndReport("%U <be> stronger!");
4924 
4925 	// Only do evolution if we are in our base form.
4926 	if (!base)
4927 	{
4928 	    // Check to see if we are now greater than our evolve target.
4929 	    if (defn().evolvetarget != MOB_NONE)
4930 	    {
4931 		if (myExpLevel >= defn().evolvelevel)
4932 		{
4933 		    buf.sprintf("%%U <grow> into %s%s!",
4934 			    gram_getarticle(glb_mobdefs[defn().evolvetarget].name),
4935 			    glb_mobdefs[defn().evolvetarget].name);
4936 		    formatAndReport(buf.buffer());
4937 
4938 		    // Transform our base type.
4939 		    myDefinition = defn().evolvetarget;
4940 
4941 		    // Roll new mp & hp for ourselves with the new type
4942 		    // and adjust us upwards.
4943 		    // This way we ensure we are at least as powerful as
4944 		    // we should be regardless of the actual level gap.
4945 		    newhp = rand_dice(defn().hp);
4946 		    newmp = rand_dice(defn().mp);
4947 		    if (newhp > getMaxHP())
4948 			incrementMaxHP(newhp - getMaxHP());
4949 		    if (newmp > getMaxMP())
4950 			incrementMaxMP(newmp - getMaxMP());
4951 
4952 		    // Acquire all the new nifty intrinsics.
4953 		    mergeIntrinsicsFromString(defn().intrinsic);
4954 		}
4955 	    }
4956 
4957 	    // If we are a familiar in our base form, we can
4958 	    // learn, for now, any new spell.  Most will be a waste
4959 	    // of XP, but, admittedly, may be needed as a prereq.
4960 	    if (hasIntrinsic(INTRINSIC_FAMILIAR))
4961 	    {
4962 		SPELL_NAMES		spell;
4963 
4964 		spell = findSpell(GOD_WIZARD);
4965 		learnSpell(spell);
4966 		if (spell != SPELL_NONE)
4967 		{
4968 		    buf.sprintf("%%U <learn> %s!",
4969 			    glb_spelldefs[spell].name);
4970 		    formatAndReport(buf.buffer());
4971 		}
4972 	    }
4973 	}
4974 
4975 	return;
4976     }
4977 
4978     // Coming here means that this is the avatar.
4979     // We wish the user to select any one of the legal professions:
4980     // This is all handled by the piety code, thus the jump to
4981     // pietyGainLevel().
4982     pietyGainLevel();
4983 }
4984 
4985 void
receiveExp(int exp)4986 MOB::receiveExp(int exp)
4987 {
4988     int		 newexp;
4989     MOB		*base;
4990     MOB		*master;
4991 
4992     // The owner of this mob gets his cut.
4993     master = getMaster();
4994     if (master)
4995     {
4996 	// Just in case we have an infinite loop here, we verify
4997 	// we have non-zero exp to propagate.
4998 	if (exp > 2)
4999 	    master->receiveExp(exp / 2 + 1);
5000     }
5001 
5002     // We do this bit of craziness to allow us to guarantee
5003     // that a u16 will fit the experience.
5004     newexp = myExp;
5005     newexp += exp;
5006     while (newexp >= 1000)
5007     {
5008 	newexp -= 1000;
5009 	gainLevel();
5010     }
5011     myExp = newexp;
5012 
5013     base = myBaseType.getMob();
5014     if (base)
5015 	base->myExp = myExp;
5016 }
5017 
5018 void
wipeExp()5019 MOB::wipeExp()
5020 {
5021     MOB		*base;
5022 
5023     myExp = 0;
5024     base = myBaseType.getMob();
5025     if (base)
5026 	base->myExp = myExp;
5027 }
5028 
5029 void
receiveArmourEnchant(ITEMSLOT_NAMES slot,int bonus,bool shudder)5030 MOB::receiveArmourEnchant(ITEMSLOT_NAMES slot, int bonus, bool shudder)
5031 {
5032     int		 	 numvalid;
5033     ITEM		*armour, *drop;
5034     ITEMSLOT_NAMES	 slots[4];
5035     const char		*flavour;
5036     BUF			 buf;
5037 
5038     // First, determine an armour slot to enchant...
5039     if (slot == (ITEMSLOT_NAMES) -1)
5040     {
5041 	numvalid = 0;
5042 	if (getEquippedItem(ITEMSLOT_HEAD))
5043 	    slots[numvalid++] = ITEMSLOT_HEAD;
5044 
5045 	// Only enchange shields in left hand.
5046 	armour = getEquippedItem(ITEMSLOT_LHAND);
5047 	if (armour && !armour->isShield())
5048 	    armour = 0;
5049 	if (armour)
5050 	    slots[numvalid++] = ITEMSLOT_LHAND;
5051 
5052 	if (getEquippedItem(ITEMSLOT_BODY))
5053 	    slots[numvalid++] = ITEMSLOT_BODY;
5054 	if (getEquippedItem(ITEMSLOT_FEET))
5055 	    slots[numvalid++] = ITEMSLOT_FEET;
5056 
5057 	if (numvalid)
5058 	    slot = slots[rand_choice(numvalid)];
5059 	else
5060 	    slot = ITEMSLOT_BODY;
5061     }
5062 
5063     armour = getEquippedItem(slot);
5064     if (armour)
5065     {
5066 	switch (bonus)
5067 	{
5068 	    case -1:
5069 		flavour = "dull brown";
5070 		break;
5071 	    case 0:
5072 		// Likely a problem.
5073 		flavour = "not at all";
5074 		break;
5075 	    case 1:
5076 		flavour = "silver";
5077 		break;
5078 	    case 2:
5079 		flavour = "bright silver";
5080 		break;
5081 	    default:
5082 		// One of the many nods to Crawl.
5083 		flavour = "aubergine";
5084 		break;
5085 	}
5086 
5087 	formatAndReport("%R %Iu <I:glow> %B1.", armour, flavour);
5088 	// If this is you, you now know the enchantment:
5089 	if (isAvatar())
5090 	    armour->markEnchantKnown();
5091 	armour->enchant(bonus);
5092 	// Warn if enchant is now greater than 3.
5093 	if (shudder && (armour->getEnchantment() > 3))
5094 	{
5095 	    buf = formatToString("%U <shudder>!", 0, armour, 0, 0);
5096 	    reportMessage(buf);
5097 	}
5098 	if (shudder && (armour->getEnchantment() > 5))
5099 	{
5100 	    // Determine if it is destroyed!
5101 	    // NOTE: We had better hope the armour is not enchanting itself :>
5102 	    if (rand_choice(2))
5103 	    {
5104 		buf = formatToString("%U <dissolve> into dust.", 0, armour, 0, 0);
5105 		reportMessage(buf);
5106 
5107 		drop = dropItem(armour->getX(), armour->getY());
5108 		UT_ASSERT(drop == armour);
5109 		delete drop;
5110 
5111 		rebuildAppearance();
5112 	    }
5113 	}
5114     }
5115     else
5116     {
5117 	formatAndReport("%R skin hardens.");
5118     }
5119 }
5120 
5121 void
receiveWeaponEnchant(int bonus,bool shudder)5122 MOB::receiveWeaponEnchant(int bonus, bool shudder)
5123 {
5124     ITEM	*weapon, *drop;
5125     const char	*flavour;
5126     BUF		 buf;
5127 
5128     weapon = getEquippedItem(ITEMSLOT_RHAND);
5129 
5130     if (!weapon)
5131     {
5132 	// TODO: You may not have hands!
5133 	if (bonus > 0)
5134 	    formatAndReport("%R hands move quicker.");
5135 	else if (bonus == 0)
5136 	    formatAndReport("%R hands move the same speed.");
5137 	else
5138 	    formatAndReport("%R hands move slower.");
5139     }
5140     else
5141     {
5142 	if (!weapon->isWeapon())
5143 	{
5144 	    // Not a real weapon...
5145 	    formatAndReport("%R %Iu <I:glow> briefly.", weapon);
5146 	}
5147 	else
5148 	{
5149 	    switch (bonus)
5150 	    {
5151 		case -1:
5152 		    flavour = "dull brown";
5153 		    break;
5154 		case 0:
5155 		    // Likely a problem.
5156 		    flavour = "not at all";
5157 		    break;
5158 		case 1:
5159 		    flavour = "silver";
5160 		    break;
5161 		case 2:
5162 		    flavour = "bright silver";
5163 		    break;
5164 		default:
5165 		    // One of the many nods to Crawl.
5166 		    flavour = "aubergine";
5167 		    break;
5168 	    }
5169 
5170 	    formatAndReport("%R %Iu <I:glow> %B1.", weapon, flavour);
5171 	    weapon->enchant(bonus);
5172 	    // If this is you, you now know the enchantment:
5173 	    if (isAvatar())
5174 		weapon->markEnchantKnown();
5175 
5176 	    // Warn if +5 or higher...
5177 	    if (shudder && (weapon->getEnchantment() > 5))
5178 	    {
5179 		buf = formatToString("%U <shudder>!", 0, weapon, 0, 0);
5180 		reportMessage(buf);
5181 	    }
5182 	    // Check if it dissolves!
5183 	    if (shudder && (weapon->getEnchantment() > 7))
5184 	    {
5185 		// NOTE: We had better hope the weapon is not
5186 		// enchanting itself!
5187 		if (rand_choice(2))
5188 		{
5189 		    buf = formatToString("%U <dissolve> into dust!", 0, weapon, 0, 0);
5190 		    reportMessage(buf);
5191 
5192 		    drop = dropItem(weapon->getX(), weapon->getY());
5193 		    UT_ASSERT(drop == weapon);
5194 		    delete drop;
5195 
5196 		    rebuildAppearance();
5197 		}
5198 	    }
5199 	}
5200     }
5201 }
5202 
5203 void
receiveCurse(bool onlyequip,bool forceany)5204 MOB::receiveCurse(bool onlyequip, bool forceany)
5205 {
5206     ITEM		*curselist[MOBINV_WIDTH * MOBINV_HEIGHT];
5207     int			 numcurse = 0;
5208     ITEM		*item;
5209     BUF			 buf;
5210 
5211     for (item = myInventory; item; item = item->getNext())
5212     {
5213 	if (!item->isCursed())
5214 	{
5215 	    if (item->getX() == 0)
5216 	    {
5217 		// This is an equipped item.
5218 		if (!onlyequip && !forceany)
5219 		    numcurse = 0;
5220 		onlyequip = true;
5221 		curselist[numcurse++] = item;
5222 	    }
5223 	    else if (!onlyequip || forceany)
5224 	    {
5225 		curselist[numcurse++] = item;
5226 	    }
5227 	}
5228     }
5229 
5230     // If you have no items that are uncursed, we get a black cloud
5231     // on a body part.
5232     if (!numcurse)
5233     {
5234 	ITEMSLOT_NAMES	 slot;
5235 	slot = (ITEMSLOT_NAMES) rand_choice(NUM_ITEMSLOTS);
5236 
5237 	// It could be that the current creature
5238 	// doesn't have that body part!  Then
5239 	// we give a different message.
5240 	const char *bodypart = getSlotName(slot);
5241 
5242 	if (!bodypart)
5243 	{
5244 	    formatAndReport("A black cloud forms nowhere near %U.");
5245 	}
5246 	else
5247 	{
5248 	    // Nothing equipped there.
5249 	    buf = formatToString("A black cloud surrounds %r %B1.",
5250 		    this, 0, 0, 0,
5251 		    bodypart);
5252 	    reportMessage(buf);
5253 	}
5254 
5255 	return;
5256     }
5257 
5258     // Choose an item to curse at random.
5259     item = curselist[rand_choice(numcurse)];
5260 
5261     buf = formatToString("%U <glow> black.",
5262 		0, item, 0, 0);
5263     reportMessage(buf);
5264     item->makeCursed();
5265 
5266     // The black glow is a bit of a give away
5267     if (isAvatar())
5268 	item->markCursedKnown();
5269 }
5270 
5271 void
receiveUncurse(bool doall)5272 MOB::receiveUncurse(bool doall)
5273 {
5274     bool		 onlyequip = false;
5275     ITEM		*uncurselist[MOBINV_WIDTH * MOBINV_HEIGHT];
5276     int			 numuncurse = 0, i;
5277     ITEM		*item;
5278     BUF			 buf;
5279 
5280     for (item = myInventory; item; item = item->getNext())
5281     {
5282 	if (item->isCursed())
5283 	{
5284 	    if (item->getX() == 0)
5285 	    {
5286 		// This is an equipped item.
5287 		if (!onlyequip && !doall)
5288 		    numuncurse = 0;
5289 		onlyequip = true;
5290 		uncurselist[numuncurse++] = item;
5291 	    }
5292 	    else if (!onlyequip || doall)
5293 	    {
5294 		uncurselist[numuncurse++] = item;
5295 	    }
5296 	}
5297     }
5298 
5299     // If you have nothing cursed, get helping message.
5300     if (!numuncurse)
5301     {
5302 	formatAndReport("%U <feel> as if someone were helping %A.");
5303 	return;
5304     }
5305 
5306     // Choose an item to uncurse at random, unless doall is set
5307     // in which case we uncurse it all.
5308     for (i = 0; i < numuncurse; i++)
5309     {
5310 	if (doall)
5311 	    item = uncurselist[i];
5312 	else
5313 	    item = uncurselist[rand_choice(numuncurse)];
5314 
5315 	formatAndReport("%IU <I:glow> blue.", item);
5316 	item->makeBlessed();
5317 
5318 	// The blue glow is a bit of a give away
5319 	if (isAvatar())
5320 	    item->markCursedKnown();
5321 
5322 	if (!doall)
5323 	    break;
5324     }
5325 }
5326 
5327 bool
receiveHeal(int hp,MOB * src,bool vampiric)5328 MOB::receiveHeal(int hp, MOB *src, bool vampiric)
5329 {
5330     bool	didanything = false;
5331     if (myHP < getMaxHP())
5332     {
5333 	didanything = true;
5334 
5335 	// Only top up our health.
5336 	if (myHP + hp > getMaxHP())
5337 	    hp = getMaxHP() - myHP;
5338 
5339 	if (src)
5340 	    src->pietyHeal(hp, src == this,
5341 			   getAttitude(src) == ATTITUDE_HOSTILE,
5342 			   vampiric);
5343 
5344 	myHP += hp;
5345     }
5346     // Cure bleeding.  This is done even if you were at max hp.
5347     if (hasIntrinsic(INTRINSIC_BLEED))
5348     {
5349 	clearIntrinsic(INTRINSIC_BLEED);
5350 	didanything = true;
5351     }
5352     return didanything;
5353 }
5354 
5355 bool
receiveMana(int mp,MOB * src)5356 MOB::receiveMana(int mp, MOB *src)
5357 {
5358     bool	didanything = false;
5359     if (myMP < getMaxMP())
5360     {
5361 	didanything = true;
5362 
5363 	// Only top up our health.
5364 	if (myMP + mp > getMaxMP())
5365 	    mp = getMaxMP() - myMP;
5366 
5367 	myMP += mp;
5368     }
5369     // Cure magic drain.  This is done even if you were at max mp.
5370     if (hasIntrinsic(INTRINSIC_MAGICDRAIN))
5371     {
5372 	clearIntrinsic(INTRINSIC_MAGICDRAIN);
5373 
5374 	// it could be we got the intrinsic from our armour.  Thus,
5375 	// we only count as it doing something if there was a net change.
5376 	if (!hasIntrinsic(INTRINSIC_MAGICDRAIN))
5377 	    didanything = true;
5378     }
5379     return didanything;
5380 }
5381 
5382 bool
receiveCure()5383 MOB::receiveCure()
5384 {
5385     int		i;
5386     bool	didanything = false;
5387 
5388     for (i = 0; i < NUM_POISONS; i++)
5389     {
5390 	if (hasIntrinsic((INTRINSIC_NAMES) glb_poisondefs[i].intrinsic))
5391 	{
5392 	    clearIntrinsic((INTRINSIC_NAMES) glb_poisondefs[i].intrinsic);
5393 	    didanything = true;
5394 	}
5395     }
5396 
5397     return didanything;
5398 }
5399 
5400 bool
receiveSlowPoison()5401 MOB::receiveSlowPoison()
5402 {
5403     // Half all our poison counters...
5404     INTRINSIC_COUNTER		*counter;
5405     int				 i;
5406     bool			 didanything = false;
5407 
5408     for (i = 0; i < NUM_POISONS; i++)
5409     {
5410 	counter = getCounter((INTRINSIC_NAMES) glb_poisondefs[i].intrinsic);
5411 	if (counter)
5412 	{
5413 	    counter->myTurns = (counter->myTurns + 1) / 2;
5414 	    didanything = true;
5415 	}
5416     }
5417 
5418     return didanything;
5419 }
5420 
5421 void
reportMessage(const char * str) const5422 MOB::reportMessage(const char *str) const
5423 {
5424     // We sometimes have certain things null, like gaintxt.
5425     if (!str)
5426 	return;
5427 
5428     // Check if we can see the source of the message.  If not, we
5429     // can't very well report it!
5430 #if 1
5431     if (isAvatar())
5432     {
5433 	UT_ASSERT(getX() >= 0 && getX() < MAP_WIDTH);
5434 	UT_ASSERT(getY() >= 0 && getY() < MAP_HEIGHT);
5435     }
5436     if (MOB::getAvatar() &&
5437 	!glbCurLevel->hasLOS(MOB::getAvatar()->getX(), MOB::getAvatar()->getY(),
5438 		      getX(), getY()))
5439     {
5440 	// Eat the message.
5441 	return;
5442     }
5443 #endif
5444 
5445     msg_report(gram_capitalize(str));
5446     // We always trail with some spaces, this will not cause line wrap,
5447     // but will ensure successive messages are all froody.
5448     msg_append("  ");
5449 }
5450 
5451 void
formatAndReport(const char * str)5452 MOB::formatAndReport(const char *str)
5453 {
5454     formatAndReportExplicit(str, 0, 0, 0);
5455 }
5456 
5457 void
formatAndReport(const char * str,const char * b1,const char * b2,const char * b3)5458 MOB::formatAndReport(const char *str, const char *b1, const char *b2, const char *b3)
5459 {
5460     formatAndReportExplicit(str, 0, 0, b1, b2, b3);
5461 }
5462 
5463 void
formatAndReport(const char * str,const MOB * mob,const char * b1,const char * b2,const char * b3)5464 MOB::formatAndReport(const char *str, const MOB *mob, const char *b1, const char *b2, const char *b3)
5465 {
5466     formatAndReportExplicit(str, mob, 0, b1, b2, b3);
5467 }
5468 
5469 void
formatAndReport(const char * str,const ITEM * item,const char * b1,const char * b2,const char * b3)5470 MOB::formatAndReport(const char *str, const ITEM *item, const char *b1, const char *b2, const char *b3)
5471 {
5472     formatAndReportExplicit(str, 0, item, b1, b2, b3);
5473 }
5474 
5475 void
formatAndReportExplicit(const char * str,const MOB * mob,const ITEM * item,const char * b1,const char * b2,const char * b3)5476 MOB::formatAndReportExplicit(const char *str, const MOB *mob, const ITEM *item, const char *b1, const char *b2, const char *b3)
5477 {
5478     BUF			buf;
5479 
5480     // It is valid to call with no string - for example the gaintxt
5481     // message of intrinsics.
5482     if (!str)
5483 	return;
5484 
5485     buf = MOB::formatToString(str, this, 0, mob, item, b1, b2, b3);
5486     reportMessage(buf);
5487 }
5488 
5489 BUF
formatToString(const char * str,const MOB * self,const ITEM * selfitem,const MOB * mob,const ITEM * item,const char * b1,const char * b2,const char * b3)5490 MOB::formatToString(const char *str, const MOB *self, const ITEM *selfitem, const MOB *mob, const ITEM *item, const char *b1, const char *b2, const char *b3)
5491 {
5492     BUF		buf1, buf2, buf3;
5493     buf1.reference(b1);
5494     buf2.reference(b2);
5495     buf3.reference(b3);
5496     return formatToString(str, self, selfitem, mob, item, buf1, buf2, buf3);
5497 }
5498 
5499 BUF
formatToString(const char * str,const MOB * self,const ITEM * selfitem,const MOB * mob,const ITEM * item,BUF b1,BUF b2,BUF b3)5500 MOB::formatToString(const char *str, const MOB *self, const ITEM *selfitem, const MOB *mob, const ITEM *item, BUF b1, BUF b2, BUF b3)
5501 {
5502     BUF			 verbbuf;
5503     int			 targettype;
5504     BUF			 buf;
5505     BUF			*buflist[3];
5506     bool		 forcesingle = false;
5507 
5508     buflist[0] = &b1;
5509     buflist[1] = &b2;
5510     buflist[2] = &b3;
5511 
5512     buf.reference("");
5513 
5514     // It is acceptable to have a null string.  For example, when
5515     // being called with gaintxt.
5516     if (!str)
5517     {
5518 	return buf;
5519     }
5520 
5521     // Only one of self and selfitem should be true!
5522     // HOwever, both can be false in which case we are "something".
5523     UT_ASSERT(!self || !selfitem);
5524 
5525     buf.allocate(100);
5526 
5527     while (*str)
5528     {
5529 	targettype = 0;		// yourself
5530 	if (*str == '%')
5531 	{
5532 	    str++;
5533 	    if (*str == 'M')
5534 	    {
5535 		// The mob
5536 		targettype = 1;
5537 		UT_ASSERT(mob != 0);
5538 		if (!mob)
5539 		    targettype = 0;
5540 		str++;
5541 	    }
5542 	    else if (*str == 'I')
5543 	    {
5544 		// the item
5545 		targettype = 2;
5546 		str++;
5547 		if (*str == '1')
5548 		{
5549 		    str++;
5550 		    forcesingle = true;
5551 		}
5552 
5553 		UT_ASSERT(item != 0);
5554 		if (!item)
5555 		    targettype = 0;
5556 	    }
5557 	    if (*str == 'U')
5558 	    {
5559 		str++;
5560 		if (targettype == 0)
5561 		{
5562 		    if (self)
5563 			buf.strcat(self->getName(true, true, false));
5564 		    else if (selfitem)
5565 			buf.strcat(selfitem->getName(true, false, false, false, forcesingle));
5566 		    else
5567 			buf.strcat("something");
5568 		}
5569 		else if (targettype == 1)
5570 		{
5571 		    // If you target yourself, use "yourself".
5572 		    if (mob == self)
5573 			buf.strcat(mob->getReflexive());
5574 		    else
5575 			buf.strcat(mob->getName(true, true, false));
5576 		}
5577 		else if (targettype == 2)
5578 		    buf.strcat(item->getName(true, false, false, false, forcesingle));
5579 	    }
5580 	    else if (*str == 'B')
5581 	    {
5582 		// Get the buffer number.
5583 		int		bufnum = str[1] - '1';
5584 		if (bufnum < 0)
5585 		    bufnum = 0;
5586 		if (bufnum > 2)
5587 		    bufnum = 2;
5588 
5589 		buf.strcat(*buflist[bufnum]);
5590 		str+=2;
5591 	    }
5592 	    else if (*str == 'i')
5593 	    {
5594 		str++;
5595 		if (targettype == 0)
5596 		{
5597 		    if (self)
5598 			buf.strcat(self->getName(true, true, false));
5599 		    else if (selfitem)
5600 			buf.strcat(selfitem->getName(true, false, false, false, forcesingle));
5601 		    else
5602 			buf.strcat("something");
5603 		}
5604 		else if (targettype == 1)
5605 		{
5606 		    // If you target yourself, use "yourself".
5607 		    if (mob == self)
5608 			buf.strcat(mob->getReflexive());
5609 		    else
5610 			buf.strcat(mob->getName(true, true, false));
5611 		}
5612 		else if (targettype == 2)
5613 		    buf.strcat(item->getName(true, false, false, true, forcesingle));
5614 	    }
5615 	    else if (*str == 'u')
5616 	    {
5617 		str++;
5618 		if (targettype == 0)
5619 		{
5620 		    if (self)
5621 			buf.strcat(self->getName(false, false, false));
5622 		    else if (selfitem)
5623 			buf.strcat(selfitem->getName(false, false, false, false, forcesingle));
5624 		    else
5625 			buf.strcat("something");
5626 		}
5627 		else if (targettype == 1)
5628 		{
5629 		    // If you target yourself, use "yourself".
5630 		    if (mob == self)
5631 			buf.strcat(mob->getReflexive());
5632 		    else
5633 			buf.strcat(mob->getName(false, false, false));
5634 		}
5635 		else if (targettype == 2)
5636 		    buf.strcat(item->getName(false, false, false, false, forcesingle));
5637 	    }
5638 	    else if (*str == 'R')
5639 	    {
5640 		str++;
5641 		if (targettype == 0)
5642 		{
5643 		    if (self)
5644 			buf.strcat(gram_makepossessive(self->getName(true, true, false)));
5645 		    else if (selfitem)
5646 			buf.strcat(gram_makepossessive(selfitem->getName(true, false, false, false, forcesingle)));
5647 		    else
5648 			buf.strcat("something's");
5649 		}
5650 		else if (targettype == 1)
5651 		    buf.strcat(gram_makepossessive(mob->getName(true, true, false)));
5652 		else if (targettype == 2)
5653 		    buf.strcat(gram_makepossessive(item->getName(true, false, false, false, forcesingle)));
5654 	    }
5655 	    else if (*str == 'r')
5656 	    {
5657 		str++;
5658 		if (targettype == 0)
5659 		{
5660 		    if (self)
5661 			buf.strcat(self->getPossessive());
5662 		    else if (selfitem)
5663 			buf.strcat(selfitem->getPossessive());
5664 		    else
5665 			buf.strcat("its");
5666 		}
5667 		else if (targettype == 1)
5668 		    buf.strcat(mob->getPossessive());
5669 		else if (targettype == 2)
5670 		    buf.strcat(item->getPossessive());
5671 	    }
5672 	    else if (*str == 'A')
5673 	    {
5674 		str++;
5675 		if (targettype == 0)
5676 		{
5677 		    if (self)
5678 			buf.strcat(self->getAccusative());
5679 		    else if (selfitem)
5680 			buf.strcat(selfitem->getAccusative());
5681 		    else
5682 			buf.strcat("something");
5683 		}
5684 		else if (targettype == 1)
5685 		    buf.strcat(mob->getAccusative());
5686 		else if (targettype == 2)
5687 		    buf.strcat(item->getAccusative());
5688 	    }
5689 	    else if (*str == 'p')
5690 	    {
5691 		str++;
5692 		if (targettype == 0)
5693 		{
5694 		    if (self)
5695 			buf.strcat(self->getPronoun());
5696 		    else if (selfitem)
5697 			buf.strcat(selfitem->getPronoun());
5698 		    else
5699 			buf.strcat("it");
5700 		}
5701 		else if (targettype == 1)
5702 		    buf.strcat(mob->getPronoun());
5703 		else if (targettype == 2)
5704 		    buf.strcat(item->getPronoun());
5705 	    }
5706 	    else if (*str)
5707 	    {
5708 		buf.append(*str++);
5709 	    }
5710 	}
5711 	else if (*str == '<')
5712 	{
5713 	    // Double angle means a normal angle.
5714 	    if (str[1] == '<')
5715 	    {
5716 		buf.append(*str++);
5717 		str++;
5718 		continue;
5719 	    }
5720 
5721 	    targettype = 0;
5722 
5723 	    // Check for an item or mob specifier.
5724 	    if (str[1] != '\0' && str[1] != '>' && str[2] == ':')
5725 	    {
5726 		if (str[1] == 'M')
5727 		{
5728 		    str += 2;
5729 		    targettype = 1;
5730 		    UT_ASSERT(mob != 0);
5731 		    if (!mob)
5732 			targettype = 0;
5733 		}
5734 		else if (str[1] == 'I')
5735 		{
5736 		    str += 2;
5737 		    targettype = 2;
5738 		    UT_ASSERT(item != 0);
5739 		    if (!item)
5740 			targettype = 0;
5741 		}
5742 	    }
5743 
5744 	    // Search for back angle.
5745 	    verbbuf.clear();
5746 	    str++;
5747 	    if (*str == '%' && str[1] == 'B')
5748 	    {
5749 		// Read in the specified buffer number
5750 		int	bufnum = str[2] - '1';
5751 		if (bufnum < 0) bufnum = 0;
5752 		if (bufnum > 2) bufnum = 2;
5753 
5754 		verbbuf = *buflist[bufnum];
5755 
5756 		// Eat up to next close
5757 		while (*str && *str != '>')
5758 		    str++;
5759 	    }
5760 	    else
5761 	    {
5762 		while (*str && *str != '>')
5763 		{
5764 		    verbbuf.append(*str++);
5765 		}
5766 	    }
5767 	    if (*str)
5768 		str++;
5769 	    // Conjugate & write out the verb.
5770 	    switch (targettype)
5771 	    {
5772 		case 0:
5773 		    if (self)
5774 			buf.strcat(self->conjugate(verbbuf.buffer()));
5775 		    else if (selfitem)
5776 			buf.strcat(selfitem->conjugate(verbbuf.buffer()));
5777 		    else
5778 			buf.strcat(gram_conjugate(verbbuf, VERB_IT));
5779 		    break;
5780 		case 1:
5781 		    buf.strcat(mob->conjugate(verbbuf.buffer()));
5782 		    break;
5783 		case 2:
5784 		    buf.strcat(item->conjugate(verbbuf.buffer()));
5785 		    break;
5786 	    }
5787 	}
5788 	else
5789 	{
5790 	    buf.append(*str++);
5791 	}
5792     }
5793 
5794     return buf;
5795 }
5796 
5797 // Grammar style stuff...
5798 BUF
conjugate(const char * verb,bool past) const5799 MOB::conjugate(const char *verb, bool past) const
5800 {
5801     return gram_conjugate(verb, getPerson(), past);
5802 }
5803 
5804 VERB_PERSON
getPerson() const5805 MOB::getPerson() const
5806 {
5807     if (isAvatar())
5808 	return VERB_YOU;
5809 
5810     return (VERB_PERSON) myGender;
5811 }
5812 
5813 VERB_PERSON
getGender() const5814 MOB::getGender() const
5815 {
5816     // We can't just go to getPerson as that returns VERB_YOU which
5817     // isn't a gender.
5818     return (VERB_PERSON) myGender;
5819 }
5820 
5821 const char *
getPronoun() const5822 MOB::getPronoun() const
5823 {
5824     return gram_getpronoun(getPerson());
5825 }
5826 
5827 const char *
getPossessive() const5828 MOB::getPossessive() const
5829 {
5830     return gram_getpossessive(getPerson());
5831 }
5832 
5833 const char *
getAccusative() const5834 MOB::getAccusative() const
5835 {
5836     return gram_getaccusative(getPerson());
5837 }
5838 
5839 const char *
getOwnership() const5840 MOB::getOwnership() const
5841 {
5842     return gram_getownership(getPerson());
5843 }
5844 
5845 const char *
getReflexive() const5846 MOB::getReflexive() const
5847 {
5848     return gram_getreflexive(getPerson());
5849 }
5850 
5851 BUF
getName(bool usearticle,bool usedefinite,bool forcevisible,bool neverpronoun) const5852 MOB::getName(bool usearticle, bool usedefinite,
5853 	     bool forcevisible, bool neverpronoun) const
5854 {
5855     BUF		 basename;
5856     BUF		 buf;
5857 
5858     // The avatar, regardless of his current incarnation, is you.
5859     if (isAvatar())
5860     {
5861 	buf.reference("you");
5862 	return buf;
5863     }
5864 
5865     basename.reference(glb_mobdefs[myDefinition].name);
5866 
5867     // If we are a zombie or skeleton, include our basetype
5868     if (myDefinition != myOrigDefinition)
5869     {
5870 	// Check that our new definition is the sort of thing that
5871 	// gets double counted.  Ie, a cave troll turning into a normal
5872 	// troll should not be double printed, while one becoming a ghast
5873 	// should.
5874 	if (defn().usenameasprefix)
5875 	    basename.sprintf("%s %s", glb_mobdefs[myOrigDefinition].name, glb_mobdefs[myDefinition].name);
5876     }
5877 
5878     // Determine if we can see this critter.  If we can't,
5879     // it is an it.  We can always see ourselves in this sense.
5880     if (!forcevisible && !isAvatar() && getAvatar())
5881     {
5882 	SENSE_NAMES	 sense;
5883 	sense = getAvatar()->getSenseType(this);
5884 
5885 	switch (sense)
5886 	{
5887 	    case NUM_SENSES:
5888 		UT_ASSERT(0);
5889 		// FALL THROUGH
5890 	    case SENSE_SIGHT:
5891 		// This is the traditional control path.
5892 		break;
5893 	    case SENSE_NONE:
5894 		// We cannot sense the creature at all.
5895 		// Makes you wonder why we are requesting the name :>
5896 		buf.reference("something");
5897 		return buf;
5898 	    case SENSE_HEAR:
5899 		// Normal control path.  It is assumed the sounds
5900 		// are sufficient to disambiguate.
5901 		break;
5902 	    case SENSE_ESP:
5903 		// Again, normal control path.
5904 		break;
5905 	    case SENSE_WARN:
5906 	    {
5907 		// We only have a sense of the relative power.
5908 		int		leveldiff;
5909 
5910 		leveldiff = getExpLevel() - getAvatar()->getExpLevel();
5911 		if (leveldiff < -2)
5912 		    basename.reference("wimpy creature");
5913 		else if (leveldiff <= -1)
5914 		    basename.reference("weak creature");
5915 		else if (leveldiff == 0)
5916 		    basename.reference("creature");
5917 		else if (leveldiff <= 2)
5918 		    basename.reference("strong creature");
5919 		else if (leveldiff <= 5)
5920 		    basename.reference("scary creature");
5921 		else
5922 		    basename.reference("very scary creature");
5923 
5924 		// Note: We proceed to apply the normal article
5925 		// rules to this entry!
5926 		break;
5927 	    }
5928 	}
5929     }
5930 
5931     // Specifically named critters always have the internal
5932     // pronoun, and no external one.
5933     // Unseen creatures do not get this path!
5934     if (myName.getName())
5935     {
5936 	buf.sprintf("%s the %s",
5937 		     myName.getName(), basename.buffer());
5938 	return buf;
5939     }
5940 
5941     // Default to just the plain basename
5942     buf = basename;
5943     if (!usearticle)
5944     {
5945 	return buf;
5946     }
5947 
5948     // Some people never use articles.  Specifically those which
5949     // are pronouns.
5950     // This path should not be reached.
5951     if (gram_ispronoun(basename))
5952 	return buf;
5953 
5954     // Or those that are proper names, ie, start with upper case.
5955     if (isupper(basename.buffer()[0]))
5956 	return buf;
5957 
5958     if (usedefinite)
5959     {
5960         buf.sprintf("the %s", basename.buffer());
5961     }
5962     else
5963     {
5964 	buf.sprintf("%s%s", gram_getarticle(basename), basename.buffer());
5965     }
5966 
5967     return buf;
5968 }
5969 
5970 BUF
getDescription() const5971 MOB::getDescription() const
5972 {
5973     const char		*health;
5974     const char		*con;
5975     BUF			 buf;
5976     SENSE_NAMES		 sense;
5977 
5978     // Determine how the avatar can sense it.
5979     sense = SENSE_SIGHT;
5980     if (getAvatar())
5981     {
5982 	sense = getAvatar()->getSenseType(this);
5983     }
5984 
5985     if (getHP() > getMaxHP())
5986     {
5987 	health = "in superior condition";
5988     }
5989     else if (getHP() == getMaxHP())
5990     {
5991 	health = "in perfect shape";
5992     }
5993     else if (getHP() > getMaxHP() * 0.6)
5994     {
5995 	health = "in good shape";
5996     }
5997     else if (getHP() > getMaxHP() * 0.3)
5998     {
5999 	health = "in poor shape";
6000     }
6001     else if (getHP() > 1)
6002     {
6003 	health = "near death";
6004     }
6005     else
6006 	health = "at death's gate";
6007 
6008     if (getAvatar())
6009     {
6010 	int leveldiff;
6011 
6012 	leveldiff = getExpLevel() - getAvatar()->getExpLevel();
6013 	if (leveldiff < -2)
6014 	    con = "a windshield kill";
6015 	else if (leveldiff <= -1)
6016 	    con = "an easy fight";
6017 	else if (leveldiff == 0)
6018 	    con = "an even fight";
6019 	else if (leveldiff <= 2)
6020 	    con = "a hard fight";
6021 	else if (leveldiff <= 5)
6022 	    con = "an impossible fight";
6023 	else
6024 	    con = "your funeral";
6025 
6026 	// We always get the verbose description of ourselves.
6027 	if (isAvatar())
6028 	{
6029 	    sense = SENSE_SIGHT;
6030 	}
6031 
6032 	BUF		name;
6033 	switch (sense)
6034 	{
6035 	    case SENSE_SIGHT:
6036 		buf = formatToString("%U <be> %B1.  %p <look> like %B2.  ",
6037 				this, 0, 0, 0,
6038 				health, con);
6039 		buf = gram_capitalize(buf);
6040 		break;
6041 	    case SENSE_HEAR:
6042 		name = getName(true, false);
6043 		buf.sprintf("Sounds like %s.  ",
6044 			    name.buffer());
6045 		break;
6046 	    case SENSE_ESP:
6047 		name = getName(true, false);
6048 		buf.sprintf("Thinks like %s.  ",
6049 			    name.buffer());
6050 		break;
6051 	    case SENSE_WARN:
6052 		buf.sprintf("It feels like %s.  ",
6053 			    con);
6054 		break;
6055 	    case NUM_SENSES:
6056 	    case SENSE_NONE:
6057 		buf.sprintf("Seems suspicious....  ");
6058 		break;
6059 	}
6060     }
6061     else
6062     {
6063 	buf = formatToString("%U <be> %B1.  ",
6064 		this, 0, 0, 0,
6065 		health);
6066     }
6067 
6068     return buf;
6069 }
6070 
6071 void
viewDescription() const6072 MOB::viewDescription() const
6073 {
6074     BUF		buf;
6075 
6076     gfx_pager_addtext(getDescription());
6077     gfx_pager_newline();
6078 
6079     // If we are unique, add the proper unique description.
6080     if (hasIntrinsic(INTRINSIC_UNIQUE))
6081     {
6082 	// TODO: Randomly generate something that hints at
6083 	// the given abilities.
6084 	buf = formatToString("The rampages of %U have become so well known as to earn %A a name!  ", this, 0, 0, 0);
6085 	gfx_pager_addtext(buf);
6086 	gfx_pager_newline();
6087     }
6088 
6089     // Add specific intrinsics that we can see at a glance.
6090     // These are any intrinsics with the visiblechange flag.
6091     INTRINSIC_NAMES		intrinsic;
6092     bool			hasany = false;
6093 
6094     for (intrinsic = INTRINSIC_NONE; intrinsic < NUM_INTRINSICS;
6095 	    intrinsic = (INTRINSIC_NAMES) (intrinsic + 1))
6096     {
6097 	// Skip spell and skill as they get listed separately.
6098 	if (glb_intrinsicdefs[intrinsic].isspell ||
6099 	    glb_intrinsicdefs[intrinsic].isskill)
6100 	    continue;
6101 
6102 	// We know if we are amnesiac.
6103 	if (((intrinsic == INTRINSIC_AMNESIA && isAvatar()) ||
6104 		glb_intrinsicdefs[intrinsic].visiblechange) &&
6105 	    hasIntrinsic(intrinsic))
6106 	{
6107 	    if (!hasany)
6108 	    {
6109 		buf = formatToString("%p <be> ",
6110 			this, 0, 0, 0);
6111 		buf = gram_capitalize(buf);
6112 		gfx_pager_addtext(buf);
6113 		hasany = true;
6114 	    }
6115 	    else
6116 		gfx_pager_addtext(", ");
6117 
6118 	    if (intrinsic == INTRINSIC_TAME)
6119 	    {
6120 		// It is confusing to call enemy creatures "tame" when
6121 		// they are in the employ of a foe.  Thus we special
6122 		// case them
6123 		if (isSlave(getAvatar()))
6124 		    gfx_pager_addtext(glb_intrinsicdefs[intrinsic].name);
6125 		else
6126 		    gfx_pager_addtext("a follower");
6127 	    }
6128 	    else
6129 	    {
6130 		gfx_pager_addtext(glb_intrinsicdefs[intrinsic].name);
6131 	    }
6132 	}
6133     }
6134 
6135     // Reveal the general noise level as we see it...
6136     if (getAvatar())
6137     {
6138 	int		noiselevel;
6139 
6140 	noiselevel = getNoiseLevel(getAvatar());
6141 
6142 	const char *noisetext;
6143 	switch (noiselevel)
6144 	{
6145 	    case 0:
6146 		noisetext = "silent";
6147 		break;
6148 	    case 1:
6149 		noisetext = "quiet";
6150 		break;
6151 	    case 2:
6152 		noisetext = "noisy";
6153 		break;
6154 	    case 3:
6155 		noisetext = "loud";
6156 		break;
6157 	    default:
6158 	    case 4:
6159 		noisetext = "very loud";
6160 		break;
6161 	}
6162 	if (!hasany)
6163 	{
6164 	    buf = formatToString("%p <be> ",
6165 		    this, 0, 0, 0);
6166 	    buf = gram_capitalize(buf);
6167 	    gfx_pager_addtext(buf);
6168 	    hasany = true;
6169 	}
6170 	else
6171 	    gfx_pager_addtext(", ");
6172 
6173 	gfx_pager_addtext(noisetext);
6174     }
6175 
6176     // Reveal the friendliness level.  We don't do this if we
6177     // are tame or neutral.
6178     if (!isAvatar() && getAvatar())
6179     {
6180 	const char		*iff = 0;
6181 
6182 	switch (getAttitude(getAvatar()))
6183 	{
6184 	    case ATTITUDE_HOSTILE:
6185 		iff = "hostile";
6186 		break;
6187 
6188 	    case ATTITUDE_FRIENDLY:
6189 		iff = "friendly";
6190 		break;
6191 
6192 	    case NUM_ATTITUDES:
6193 	    case ATTITUDE_NEUTRAL:
6194 		// Report nothing in these cases.
6195 		break;
6196 	}
6197 
6198 	if (iff)
6199 	{
6200 	    if (!hasany)
6201 	    {
6202 		buf = formatToString("%p <be> ",
6203 			this, 0, 0, 0);
6204 		buf = gram_capitalize(buf);
6205 		gfx_pager_addtext(buf);
6206 		hasany = true;
6207 	    }
6208 	    else
6209 		gfx_pager_addtext(", ");
6210 
6211 	    gfx_pager_addtext(iff);
6212 	}
6213     }
6214 
6215     if (hasany)
6216     {
6217 	gfx_pager_addtext(".  ");
6218 	gfx_pager_newline();
6219     }
6220 
6221 
6222     // If this is the avatar, list their spells and skills.
6223     if (isAvatar())
6224     {
6225 	hasany = false;
6226 	SPELL_NAMES		spell;
6227 	FOREACH_SPELL(spell)
6228 	{
6229 	    if (hasSpell(spell, true, true))
6230 	    {
6231 		if (!hasany)
6232 		{
6233 		    gfx_pager_newline();
6234 		    gfx_pager_addtext("Known spells: ");
6235 		    hasany = true;
6236 		}
6237 		else
6238 		    gfx_pager_addtext(", ");
6239 
6240 		gfx_pager_addtext(glb_spelldefs[spell].name);
6241 	    }
6242 	}
6243 	if (hasany)
6244 	{
6245 	    gfx_pager_addtext(".  ");
6246 	    gfx_pager_newline();
6247 	}
6248 
6249 	hasany = false;
6250 	SKILL_NAMES		skill;
6251 	FOREACH_SKILL(skill)
6252 	{
6253 	    if (hasSkill(skill, true, true))
6254 	    {
6255 		if (!hasany)
6256 		{
6257 		    gfx_pager_newline();
6258 		    gfx_pager_addtext("Learned skills: ");
6259 		    hasany = true;
6260 		}
6261 		else
6262 		    gfx_pager_addtext(", ");
6263 
6264 		gfx_pager_addtext(glb_skilldefs[skill].name);
6265 	    }
6266 	}
6267 	if (hasany)
6268 	{
6269 	    gfx_pager_addtext(".  ");
6270 	    gfx_pager_newline();
6271 	}
6272     }
6273 
6274     // Describe what we can see of its equipment.
6275     ITEMSLOT_NAMES	slot;
6276     bool		hasitems = false;
6277 
6278     FOREACH_ITEMSLOT(slot)
6279     {
6280 	ITEM		*item;
6281 
6282 	item = getEquippedItem(slot);
6283 
6284 	if (item)
6285 	{
6286 	    // You can't have anything equipped in empty slots.
6287 	    UT_ASSERT(getSlotName(slot) != 0);
6288 	    if (getSlotName(slot))
6289 	    {
6290 		if (!hasitems)
6291 		{
6292 		    gfx_pager_separator();
6293 		    gfx_pager_addtext("Equipped Items:");
6294 		    gfx_pager_newline();
6295 		    hasitems = true;
6296 		}
6297 
6298 		BUF		itemname = item->getName(false, false, true);
6299 
6300 		buf.sprintf("- %s %s %s.",
6301 			itemname.buffer(),
6302 			glb_itemslotdefs[slot].preposition,
6303 			getSlotName(slot));
6304 		buf = gram_capitalize(buf);
6305 
6306 		gfx_pager_addtext(buf);
6307 		gfx_pager_newline();
6308 	    }
6309 	}
6310     }
6311 
6312     // Add encyclopedia entry.
6313     if (encyc_hasentry("MOB", getDefinition()))
6314     {
6315 	gfx_pager_separator();
6316 	encyc_pageentry("MOB", getDefinition());
6317     }
6318 
6319     gfx_pager_display();
6320 }
6321 
6322 void
pageCharacterDump(bool ondeath,bool didwin,int truetime)6323 MOB::pageCharacterDump(bool ondeath, bool didwin, int truetime)
6324 {
6325     BUF		buf;
6326 
6327     if (truetime == -1)
6328 	truetime = gfx_getframecount();
6329 
6330     if (isAvatar())
6331     {
6332 	buf.sprintf("POWDER %03d", hiscore_getversion());
6333 	gfx_pager_addtext(buf);
6334 	gfx_pager_newline();
6335 
6336 	// Output our current platform.  No real easy way to
6337 	// do this yet :>
6338 #ifdef USING_SDL
6339 #ifdef WIN32
6340 	gfx_pager_addtext("Windows Version");
6341 #elif defined(MACOSX)
6342 	gfx_pager_addtext("Mac OSX Version");
6343 #elif defined(SYS_PSP)
6344 	gfx_pager_addtext("PSP Version");
6345 #elif defined(iPOWDER)
6346 	gfx_pager_addtext("iPOWDER Version");
6347 #elif defined(ANDROID)
6348 	gfx_pager_addtext("Android Version");
6349 #elif defined(LINUX)
6350 	gfx_pager_addtext("Linux Version");
6351 #else
6352 	gfx_pager_addtext("Unknown SDL Version");
6353 #endif
6354 #else
6355 #ifdef USING_DS
6356 	gfx_pager_addtext("NDS Version");
6357 #else
6358 	gfx_pager_addtext("GBA Version");
6359 #endif
6360 #endif
6361 	gfx_pager_newline();
6362 	if (!hiscore_isnewgame() && hiscore_savecount() > 2)
6363 	{
6364 	    buf.sprintf("You have save scummed %d times.",
6365 			    hiscore_savecount()-2);
6366 	    gfx_pager_addtext(buf);
6367 	    gfx_pager_newline();
6368 	}
6369     }
6370 
6371     // Name...
6372     if (glbWizard)
6373     {
6374 	// Don't reveal wizard code.
6375 	buf.sprintf("Wizard mode is activated.");
6376 	gfx_pager_addtext(buf);
6377     }
6378     else
6379     {
6380 	gfx_pager_addtext("Name: ");
6381 	if (isAvatar())
6382 	    gfx_pager_addtext(glbAvatarName);
6383 	else
6384 	    gfx_pager_addtext(getName(false));
6385     }
6386     gfx_pager_newline();
6387 
6388     gfx_pager_separator();
6389 
6390     buf.sprintf("Physical: %d/%d (max %d)",
6391 	    getHP(), getHitDie(), getMaxHP());
6392     gfx_pager_addtext(buf); gfx_pager_newline();
6393 
6394     buf.sprintf("Mental: %d/%d (max %d)",
6395 	    getMP(), getMagicDie(), getMaxMP());
6396     gfx_pager_addtext(buf); gfx_pager_newline();
6397 
6398     buf.sprintf("AC: %d", getAC());
6399     gfx_pager_addtext(buf); gfx_pager_newline();
6400 
6401     buf.sprintf("X: %d", getExp());
6402     gfx_pager_addtext(buf); gfx_pager_newline();
6403 
6404     if (isAvatar())
6405     {
6406 	// We take the global level as authoratative.  If we are stoned
6407 	// we are removed from the level map so have a dlevel of -1.
6408 	if (glbCurLevel)
6409 	    buf.sprintf("Depth: %d", glbCurLevel->getDepth());
6410 	else
6411 	    buf.sprintf("Depth: %d", getDLevel());
6412 	gfx_pager_addtext(buf); gfx_pager_newline();
6413 
6414 	buf.sprintf("%d moves over ", speed_gettime());
6415 	gfx_pager_addtext(buf);
6416 
6417 	// Report wall clock time taken.
6418 	buf = victory_formattime(truetime);
6419 	gfx_pager_addtext(buf); gfx_pager_newline();
6420 
6421 	if (ondeath)
6422 	{
6423 	    // Give the final score.
6424 	    buf.sprintf("Score: %d", calcScore(didwin));
6425 	    gfx_pager_addtext(buf); gfx_pager_newline();
6426 	}
6427     }
6428 
6429     // Add all intrinsics.
6430     // These are any intrinsics with the visiblechange flag.
6431     INTRINSIC_NAMES		intrinsic;
6432     bool			hasany = false;
6433     char 			dash[3] = { '-', ' ', 0 };
6434 
6435     FOREACH_INTRINSIC(intrinsic)
6436     {
6437 	// Don't show unnecessary stuff.
6438 	if (!ondeath && !glb_intrinsicdefs[intrinsic].visiblechange)
6439 	    continue;
6440 
6441 	// Skip spell and skill intrinsics as they'd be duplicate
6442 	if (glb_intrinsicdefs[intrinsic].isspell ||
6443 	    glb_intrinsicdefs[intrinsic].isskill)
6444 	    continue;
6445 
6446 	if (hasIntrinsic(intrinsic))
6447 	{
6448 	    if (!hasany)
6449 	    {
6450 		hasany = true;
6451 		gfx_pager_separator();
6452 		gfx_pager_addtext("Intrinsics:");
6453 		gfx_pager_newline();
6454 	    }
6455 	    dash[0] = *intrinsicFromWhatLetter(intrinsic);
6456 	    gfx_pager_addtext(dash);
6457 	    gfx_pager_addtext(gram_capitalize(glb_intrinsicdefs[intrinsic].name));
6458 	    gfx_pager_newline();
6459 	}
6460     }
6461 
6462     hasany = false;
6463     SPELL_NAMES		spell;
6464     FOREACH_SPELL(spell)
6465     {
6466 	intrinsic = (INTRINSIC_NAMES) glb_spelldefs[spell].intrinsic;
6467 	if (hasSpell(spell, true, true))
6468 	{
6469 	    if (!hasany)
6470 	    {
6471 		gfx_pager_separator();
6472 		gfx_pager_addtext("Spells:");
6473 		gfx_pager_newline();
6474 		hasany = true;
6475 	    }
6476 
6477 	    dash[0] = *intrinsicFromWhatLetter(intrinsic);
6478 	    gfx_pager_addtext(dash);
6479 	    gfx_pager_addtext(glb_spelldefs[spell].name);
6480 	    gfx_pager_newline();
6481 	}
6482     }
6483 
6484     hasany = false;
6485     SKILL_NAMES skill;
6486     FOREACH_SKILL(skill)
6487     {
6488 	intrinsic = (INTRINSIC_NAMES) glb_skilldefs[skill].intrinsic;
6489 	if (hasSkill(skill, true, true))
6490 	{
6491 	    if (!hasany)
6492 	    {
6493 		gfx_pager_separator();
6494 		gfx_pager_addtext("Skills:");
6495 		gfx_pager_newline();
6496 		hasany = true;
6497 	    }
6498 
6499 	    dash[0] = *intrinsicFromWhatLetter(intrinsic);
6500 	    gfx_pager_addtext(dash);
6501 	    gfx_pager_addtext(glb_skilldefs[skill].name);
6502 	    gfx_pager_newline();
6503 	}
6504     }
6505 
6506     // Describe what we can see of its equipment.
6507     int			slot;
6508     bool		hasitems = false;
6509     ITEM		*item;
6510 
6511     for (slot = 0; slot < NUM_ITEMSLOTS; slot++)
6512     {
6513 	item = getEquippedItem((ITEMSLOT_NAMES) slot);
6514 
6515 	if (item)
6516 	{
6517 	    // You can't have anything equipped in empty slots.
6518 	    UT_ASSERT(getSlotName((ITEMSLOT_NAMES) slot) != 0);
6519 	    if (getSlotName((ITEMSLOT_NAMES) slot))
6520 	    {
6521 		if (!hasitems)
6522 		{
6523 		    gfx_pager_separator();
6524 		    gfx_pager_addtext("Equipped Items:");
6525 		    gfx_pager_newline();
6526 		    hasitems = true;
6527 		}
6528 		if (ondeath)
6529 		    item->markIdentified(true);
6530 
6531 		BUF		itemname = item->getName(false, false, true);
6532 		buf.sprintf("- %s %s %s.",
6533 			itemname.buffer(),
6534 			glb_itemslotdefs[slot].preposition,
6535 			getSlotName((ITEMSLOT_NAMES) slot));
6536 		buf = gram_capitalize(buf);
6537 
6538 		gfx_pager_addtext(buf);
6539 		gfx_pager_newline();
6540 	    }
6541 	}
6542     }
6543 
6544     sortInventoryBySlot();
6545 
6546     // List all artifacts
6547     hasitems = false;
6548     for (item = myInventory; item; item = item->getNext())
6549     {
6550 	if (!item->isArtifact())
6551 	    continue;
6552 
6553 	if (!hasitems)
6554 	{
6555 	    gfx_pager_separator();
6556 	    gfx_pager_addtext("Artifacts:");
6557 	    gfx_pager_newline();
6558 	    hasitems = true;
6559 	}
6560 	else
6561 	    gfx_pager_newline();
6562 	if (ondeath)
6563 	    item->markIdentified(true);
6564 
6565 	item->pageDescription(true);
6566     }
6567 
6568     // List all items
6569     hasitems = false;
6570     for (item = myInventory; item; item = item->getNext())
6571     {
6572 	if (!hasitems)
6573 	{
6574 	    gfx_pager_separator();
6575 	    gfx_pager_addtext("Inventory Items:");
6576 	    gfx_pager_newline();
6577 	    hasitems = true;
6578 	}
6579 	if (ondeath)
6580 	    item->markIdentified(true);
6581 
6582 	buf.strcpy("- ");
6583 	buf.strcat(item->getName(false, false, true));
6584 	buf.strcat(".");
6585 	buf = gram_capitalize(buf);
6586 
6587 	gfx_pager_addtext(buf);
6588 	gfx_pager_newline();
6589     }
6590 }
6591 
6592 void
characterDump(bool ondeath,bool didwin,bool savefile,int truetime)6593 MOB::characterDump(bool ondeath, bool didwin, bool savefile, int truetime)
6594 {
6595     BUF			buf;
6596 
6597     if (savefile)
6598     {
6599 	gfx_pager_setwidth(72);		// Nice USENET width.
6600 
6601 	pageCharacterDump(ondeath, didwin, truetime);
6602 
6603 	// Save the character dump
6604 	buf.sprintf("%s.txt", glbAvatarName);
6605 	buf.makeFileSafe();
6606 
6607 	gfx_pager_savetofile(buf);
6608 
6609 	gfx_pager_setwidth(30);		// Back to GBA mode
6610     }
6611 
6612     pageCharacterDump(ondeath, didwin, truetime);
6613 
6614     gfx_pager_display();
6615 }
6616 
6617 bool
hasMovedLastTurn() const6618 MOB::hasMovedLastTurn() const
6619 {
6620     if (!isAvatar())
6621 	return false;
6622 
6623     if (ourAvatarDx || ourAvatarDy)
6624 	return true;
6625 
6626     return false;
6627 }
6628 
6629 void
getLastMoveDirection(int & dx,int & dy) const6630 MOB::getLastMoveDirection(int &dx, int &dy) const
6631 {
6632     if (!isAvatar())
6633     {
6634 	dx = 0;
6635 	dy = 0;
6636 	return;
6637     }
6638     dx = ourAvatarDx;
6639     dy = ourAvatarDy;
6640 }
6641 
6642 void
resetLastMoveDirection() const6643 MOB::resetLastMoveDirection() const
6644 {
6645     if (!isAvatar())
6646     {
6647 	return;
6648     }
6649     if (ourAvatarMoveOld)
6650     {
6651 	ourAvatarDx = 0;
6652 	ourAvatarDy = 0;
6653     }
6654 }
6655 
6656 void
flagLastMoveAsOld() const6657 MOB::flagLastMoveAsOld() const
6658 {
6659     ourAvatarMoveOld = true;
6660 }
6661 
6662 void
christen(const char * name)6663 MOB::christen(const char *name)
6664 {
6665     if (name)
6666 	myName.setName(gram_capitalize(name));
6667     else
6668 	myName.setName(0);
6669 }
6670 
6671 int
smartsCheck(int smarts)6672 MOB::smartsCheck(int smarts)
6673 {
6674     int		test;
6675 
6676     // The general theory is that people can adjust their actual
6677     // level by +/- 2 whenever they do a test.
6678     // Success is greater or equal.
6679     if (getSmarts() < smarts-2)
6680 	return -1;		// Force failure.
6681     if (getSmarts() >= smarts+2)
6682 	return 2;		// Force success.
6683 
6684     // Probability curve:
6685     // -2 : 1/9
6686     // -1 : 2/9
6687     //  0 : 3/9
6688     //  1 : 2/9
6689     //  2 : 1/9
6690     test = getSmarts() + rand_range(-1, 1) + rand_range(-1, 1);
6691 
6692     if (test >= smarts)
6693 	return 1;
6694 
6695     return 0;
6696 }
6697 
6698 int
strengthCheck(int str)6699 MOB::strengthCheck(int str)
6700 {
6701     int		test;
6702 
6703     // The general theory is that people can adjust their actual
6704     // level by +/- 2 whenever they do a test.
6705     // Success is greater or equal.
6706     if (getStrength() < str-2)
6707 	return -1;		// Force failure.
6708     if (getStrength() >= str+2)
6709 	return 2;		// Force success.
6710 
6711     test = getStrength() + rand_range(-1, 1) + rand_range(-1, 1);
6712 
6713     if (test >= str)
6714 	return 1;
6715     return 0;
6716 }
6717 
6718 int
getAC() const6719 MOB::getAC() const
6720 {
6721     int		 natural_ac, worn_ac;
6722     int  	 slot;
6723     ITEM	*item;
6724 
6725     natural_ac = glb_mobdefs[myDefinition].baseac;
6726 
6727     worn_ac = 0;
6728 
6729     // Each slot has a rough multiplier weight we use.
6730 
6731     for (slot = 0; slot < NUM_ITEMSLOTS; slot++)
6732     {
6733 	item = getEquippedItem((ITEMSLOT_NAMES)slot);
6734 	if (item)
6735 	{
6736 	    int		skilllevel, itemac;
6737 
6738 	    // If the slot is the left hand, we only count it if it
6739 	    // has the shield flag.  You can't put platemail in your
6740 	    // left hand and get a super shield :>
6741 	    // We do want to support artifacts that get a armour boost,
6742 	    // however.
6743 	    if (slot == ITEMSLOT_LHAND)
6744 	    {
6745 		if (!item->isShield() && item->isArmor())
6746 		    continue;
6747 	    }
6748 	    // We want to give an effective estimate of the ac,
6749 	    // so this should match the expected value of the
6750 	    // rollAC call.
6751 	    skilllevel = getArmourSkillLevel(item);
6752 	    itemac = item->getAC();
6753 	    itemac += skilllevel;
6754 
6755 	    // Shields use skillchance for modifier, everything else
6756 	    // can use the official table
6757 	    if (slot == ITEMSLOT_LHAND)
6758 	    {
6759 		int		parrychance;
6760 		if (skilllevel <= 0)
6761 		    parrychance = 25;
6762 		else if (skilllevel == 1)
6763 		    parrychance = 50;
6764 		else
6765 		    parrychance = 75;
6766 
6767 		worn_ac += itemac * parrychance;
6768 	    }
6769 	    else
6770 	    {
6771 		// Apply rolling effect.
6772 		itemac = rand_rollMean(itemac, skilllevel-1, 256);
6773 		itemac *= glb_itemslotdefs[slot].areaweight;
6774 		itemac += 128;
6775 		itemac >>= 8;
6776 
6777 		worn_ac += itemac;
6778 	    }
6779 	}
6780     }
6781 
6782     // Normalize the worn_ac.  We do so against 50, not 100, because
6783     // this grants torso armour its apparent full AC, leading to
6784     // less user confusion (we can hope :>)
6785     // We have to normalize again now that we properly reflect
6786     // the fact we roll skill chance into the equation
6787     worn_ac = (worn_ac + 24) / 25;
6788 
6789     // The worn_ac that corresponds to natural_ac only applies for
6790     // 33%.
6791     // This is easiest calculated by the minimum of the two by three
6792     // and adding it up.
6793     if (worn_ac < natural_ac)
6794     {
6795 	worn_ac = (worn_ac + 2) / 3;
6796     }
6797     else
6798     {
6799 	natural_ac = (natural_ac + 2) / 3;
6800     }
6801 
6802     return natural_ac + worn_ac;
6803 }
6804 
6805 int
calcScore(bool haswon) const6806 MOB::calcScore(bool haswon) const
6807 {
6808     int		score;
6809     bool	hasheart;
6810     const MOB	*mob = this;
6811 
6812     // We don't want to use score of polyed version.
6813     if (mob->getBaseType())
6814 	mob = mob->getBaseType();
6815 
6816     // First experience level gives no points.
6817     score = (mob->getExpLevel() - 1) * 1000;
6818     score += mob->getExp();
6819 
6820     // If we have the heart, get a bonus
6821     hasheart = mob->hasItem(ITEM_BLACKHEART);
6822 
6823     if (hasheart)
6824     {
6825 	score += 5000;
6826     }
6827 
6828     // Get a bonus for every level explored.
6829     MAP		*map, *lastmap;
6830     int		 maxdepth, depth;
6831 
6832     depth = glbCurLevel->getDepth();
6833     lastmap = glbCurLevel;
6834     for (map = glbCurLevel; map; map = map->getMapDown(false))
6835     {
6836 	lastmap = map;
6837     }
6838 
6839     maxdepth = lastmap->getDepth();
6840 
6841     // Give no points for the first level.
6842     score += (maxdepth - 1) * 500;
6843 
6844     if (hasheart && depth < 25)
6845     {
6846 	// Gain points for every level you got back up.
6847 	score += (25 - depth) * 500;
6848     }
6849 
6850     if (haswon)
6851     {
6852 	// We penalize the longer you have been in the dungeon.
6853 	// Your goal is to save the world, you shouldn't be rewarded
6854 	// for dilly-dallying killing kobolds on your way back up.
6855 	// A tiered system is used providing progressively greater
6856 	// per-turn punishments.  This is balanced by the XP score
6857 	// reward, so the break even point is wherever you can't
6858 	// make as many XP per turn as your current reward level.
6859 	score += 100000;
6860 
6861 	int		turns = speed_gettime();
6862 	score -= turns;
6863 	if (turns > 20000)
6864 	    score -= (turns-20000);
6865 	if (turns > 60000)
6866 	    score -= (turns-60000);
6867 	if (turns > 120000)
6868 	    score -= (turns-120000);
6869 	if (turns > 240000)
6870 	    score -= 9*(turns-240000);	// Please turn off your bot.  kthx.
6871 
6872 	// Have pity on the poor fool.
6873 	if (score < 5)
6874 	    score = 5;
6875     }
6876 
6877     // Total score at this point:
6878     // Examplar wins:
6879     // Level:                        38  39  47
6880     // Turns:                        54k 20k 33k
6881     // All scores in ks.
6882     // 500 * 25 * 2 for exploration: 25  25  25
6883     // 1000 * XP:                    38  39  47
6884     // Heart bonus:                   5   5   5
6885     // Win bonus:                    10  10  10
6886     // 100 - turn                    46  80  67
6887     //                              114 148 144
6888     // 100 - turn exponential       -74  70  28
6889     //                               -6 137 105
6890     // 100 - two tier turn 	     12  80  54
6891     //				     80 147 131
6892     // 10k flat                      78  77  87
6893     //
6894 
6895     // Finally, normalize the score.
6896     score += 5;
6897     score /= 10;
6898 
6899     if (score > 32678)
6900     {
6901 	int		bonus;
6902 
6903 	bonus = score - 32768;
6904 	bonus /= 100;
6905 	score = 32768 + bonus;
6906 
6907 	if (score > 65535)
6908 	    score = 65535;
6909     }
6910 
6911     return score;
6912 }
6913 
6914 int
rollAC(MOB * attacker) const6915 MOB::rollAC(MOB *attacker) const
6916 {
6917     int		 	 natural_ac;
6918     int		 	 worn_ac;
6919     ITEM		*item;
6920     ITEM		*shield;
6921     int		 	 skilllevel = 0;
6922     ITEMSLOT_NAMES	 slot;
6923 
6924     // The natural ac applies to every part of the creature.
6925     natural_ac = glb_mobdefs[myDefinition].baseac;
6926 
6927     // Determine which slot is hit.
6928     int			*attackprob;
6929     int			 attackloc_roll;
6930     // Probability tables.  These are for Head, Neck, Body, and Feet, scaled
6931     // to 100.
6932     // Our old AC contribution of boots was max 3, vs max 7 for body
6933     // armour.  This suggests a ratio of 1:3:1., or 20:60:20
6934     int			 attack_by_small[4] = {  5,  1, 55, 39 };
6935     int			 attack_by_same[4]  = { 20,  5, 55, 20 };
6936     int			 attack_by_large[4] = { 35,  5, 55,  5 };
6937 
6938     if (!attacker)
6939 	attackprob = attack_by_same;
6940     else
6941     {
6942 	if (attacker->getSize() > getSize())
6943 	    attackprob = attack_by_large;
6944 	else if (attacker->getSize() == getSize())
6945 	    attackprob = attack_by_same;
6946 	else
6947 	    attackprob = attack_by_small;
6948     }
6949 
6950     // Find the slot
6951     attackloc_roll = rand_choice(100);
6952     if (attackloc_roll < attackprob[0])
6953 	slot = ITEMSLOT_HEAD;
6954     else if (attackloc_roll < (attackprob[0] + attackprob[1]))
6955 	slot = ITEMSLOT_AMULET;
6956     else if (attackloc_roll < (attackprob[0] + attackprob[1] + attackprob[2]))
6957 	slot = ITEMSLOT_BODY;
6958     else
6959 	slot = ITEMSLOT_FEET;
6960 
6961     // If we don't have the slot, we obviously won't be hit there
6962     // We then assign the body.
6963     // I certainly hope we have a body :>
6964     if (!hasSlot(slot))
6965 	slot = ITEMSLOT_BODY;
6966 
6967     // Get the armour for the body.
6968     item = getEquippedItem(slot);
6969 
6970     if (item)
6971     {
6972 	worn_ac = item->getAC();
6973 	skilllevel = getArmourSkillLevel(item);
6974     }
6975     else
6976     {
6977 	worn_ac = 0;
6978 	skilllevel = 0;
6979     }
6980 
6981     // Calculate our effective worn_ac.
6982     // We get a bonus based on our skilllevel.  This allows
6983     // weak armours (which don't benefit from re-rolling) to still
6984     // benefit from skills.
6985     worn_ac += skilllevel;
6986 
6987     // Reroll depending on skill level.  -1 on reroll means to
6988     // pick lowest.
6989     worn_ac = rand_roll(worn_ac, skilllevel - 1);
6990 
6991     // Add in the shield.
6992     shield = getEquippedItem(ITEMSLOT_LHAND);
6993     if (shield && (shield->isShield() || !shield->isArmor()))
6994     {
6995 	int		parrychance;
6996 
6997 	skilllevel = getArmourSkillLevel(shield);
6998 
6999 	if (skilllevel <= 0)
7000 	    parrychance = 25;
7001 	else if (skilllevel == 1)
7002 	    parrychance = 50;
7003 	else
7004 	    parrychance = 75;
7005 
7006 	if (rand_chance(parrychance))
7007 	{
7008 	    worn_ac += rand_roll(shield->getAC() + skilllevel, skilllevel-1);
7009 	}
7010     }
7011 
7012     // Roll the natural_ac.  No longer any skills for this.
7013     natural_ac = rand_roll(natural_ac);
7014 
7015     // Combine the two.
7016     // The minimum is divided by 3 and then they are added together.
7017     if (natural_ac > worn_ac)
7018     {
7019 	worn_ac = (worn_ac + 2) / 3;
7020 	worn_ac += natural_ac;
7021     }
7022     else
7023     {
7024 	natural_ac = (natural_ac + 2) / 3;
7025 	worn_ac += natural_ac;
7026     }
7027 
7028     // Actual AC for the purposes of this computation.
7029     return worn_ac;
7030 }
7031 
7032 int
getAttackBonus(const ATTACK_DEF * attack,ATTACKSTYLE_NAMES style)7033 MOB::getAttackBonus(const ATTACK_DEF *attack, ATTACKSTYLE_NAMES style)
7034 {
7035     int		bonus;
7036 
7037     // Your exact attack bonus depends on the style.
7038     bonus = 0;
7039 
7040     switch (style)
7041     {
7042 	case ATTACKSTYLE_MELEE:
7043 	case ATTACKSTYLE_THROWN:
7044 	    bonus += getHitDie();
7045 	    break;
7046 	case ATTACKSTYLE_SPELL:
7047 	    bonus += getMagicDie();
7048 	    break;
7049 	case ATTACKSTYLE_WAND:
7050 	    bonus += getHitDie() + getMagicDie();
7051 	    break;
7052 	case ATTACKSTYLE_MISC:
7053 	    bonus = 0;
7054 	    break;
7055 	case ATTACKSTYLE_POISON:
7056 	    bonus = 0;
7057 	    break;
7058 	case NUM_ATTACKSTYLES:
7059 	case ATTACKSTYLE_MINION:
7060 	    break;
7061     }
7062 
7063     bonus += attack->bonustohit;
7064 
7065     return bonus;
7066 }
7067 
7068 int
getSecondWeaponChance() const7069 MOB::getSecondWeaponChance() const
7070 {
7071     ITEM	*primary, *secondary;
7072     int		 totalskill;
7073     int		 chance;
7074     const ATTACK_DEF	*attack;
7075 
7076     // Try for trivial reject.
7077     if (!hasSkill(SKILL_TWOWEAPON))
7078 	return 0;
7079 
7080     primary = getEquippedItem(ITEMSLOT_RHAND);
7081     secondary = getEquippedItem(ITEMSLOT_LHAND);
7082 
7083     // You need a second weapon to proc.
7084     if (!secondary)
7085 	return 0;
7086 
7087     // Find how skilled we are with this pair.
7088     totalskill = getWeaponSkillLevel(primary, ATTACKSTYLE_MELEE);
7089     totalskill += getWeaponSkillLevel(secondary, ATTACKSTYLE_MELEE);
7090 
7091     // If secondary is bigger than myself, not possible.
7092     if (getSize() < secondary->getSize())
7093 	return 0;
7094 
7095     // If we don't have a proper weapon attack, bail.
7096     // We don't check the ATTACK_NAME as we want to proc on weird
7097     // artifacts.
7098     attack = secondary->getAttack();
7099     if (attack == &glb_attackdefs[ATTACK_MISUSED] ||
7100 	attack == &glb_attackdefs[ATTACK_MISUSED_BUTWEAPON] ||
7101 	attack == &glb_attackdefs[ATTACK_MISTHROWN])
7102     {
7103 	return 0;
7104     }
7105 
7106     // Add one star of effectiveness if ambidextrous
7107     if (hasSkill(SKILL_AMBIDEXTROUS))
7108 	totalskill++;
7109 
7110     // Build the chance table...
7111     chance = 10;
7112     if (totalskill < 3)
7113 	chance += totalskill * 8;
7114     else
7115 	chance += 22 + (totalskill-3)*6;
7116 
7117     // If the secondary is not smaller than the primary, only have 2/3
7118     // chance...
7119     // Somewhat ironicly, I type this with only my left hand as S---
7120     // uses the right to go to sleep.
7121     // Ambidextrous cancels this.
7122     if (primary && (primary->getSize() <= secondary->getSize())
7123 	&& !hasSkill(SKILL_AMBIDEXTROUS))
7124     {
7125 	chance = (2*chance + 2) / 3;
7126     }
7127 
7128     return chance;
7129 }
7130 
7131 bool
acquireItem(ITEM * item,int sx,int sy,ITEM ** newitem)7132 MOB::acquireItem(ITEM *item, int sx, int sy, ITEM **newitem)
7133 {
7134     ITEM		*merge;
7135 
7136     if (newitem)
7137 	*newitem = item;
7138 
7139     // Check if slot is mergeable.
7140     merge = getItem(sx, sy);
7141 
7142     if (merge)
7143     {
7144 	if (merge->canMerge(item))
7145 	{
7146 	    // Do a merge.
7147 	    merge->mergeStack(item);
7148 	    delete item;
7149 	    if (newitem)
7150 		*newitem = merge;
7151 	    return true;
7152 	}
7153 	else
7154 	{
7155 	    // Failure to acquire.  Tried to load into a slot
7156 	    // already occupied with a non-mergeable item.
7157 	    return false;
7158 	}
7159     }
7160 
7161     // Check to see if we have that slot!
7162     if (!sx)
7163     {
7164 	if (!hasSlot((ITEMSLOT_NAMES) sy))
7165 	    return false;
7166     }
7167 
7168     // Add the item to our list..  It should alread be removed
7169     // from its own list.
7170     UT_ASSERT(!item->getNext());
7171     item->setNext(myInventory);
7172     myInventory = item;
7173     item->setPos(sx, sy);
7174 
7175     item->setInventory(true);
7176 
7177     // If this was acquired in our equipment slot, rebuild our intrinsics.
7178     if (!sx || item->isCarryIntrinsic())
7179     {
7180 	rebuildWornIntrinsic();
7181 	rebuildAppearance();
7182     }
7183 
7184     // Mark that we will need to reinspect our inventory
7185     // to see if we should do anything.
7186     myAIState |= AI_DIRTY_INVENTORY;
7187     if (canDigest(item))
7188 	myAIState |= AI_HAS_EDIBLE;
7189 
7190     return true;
7191 }
7192 
7193 bool
acquireItem(ITEM * item,ITEM ** newitem)7194 MOB::acquireItem(ITEM *item, ITEM **newitem)
7195 {
7196     int		 sx, sy;
7197     bool	 foundmerge = false;
7198     ITEM	*merge;
7199 
7200     // Test to see if we can merge anywhere.
7201     for (merge = myInventory; merge; merge = merge->getNext())
7202     {
7203 	// Do *not* merge with stuff already equipped.  Otherwise
7204 	// you can easily wear 6 rings.  (It should be tough for
7205 	// someone to do that :>)
7206 	if (!merge->getX())
7207 	    continue;
7208 
7209 	if (merge->canMerge(item))
7210 	{
7211 	    sx = merge->getX();
7212 	    sy = merge->getY();
7213 	    foundmerge = true;
7214 	}
7215     }
7216 
7217     if (!foundmerge)
7218     {
7219 	if (!findItemSlot(sx, sy))
7220 	    return false;
7221     }
7222 
7223     acquireItem(item, sx, sy, newitem);
7224     return true;
7225 }
7226 
7227 bool
findItemSlot(int & sx,int & sy) const7228 MOB::findItemSlot(int &sx, int &sy) const
7229 {
7230     bool		seenempty = false;
7231     bool		seenfull = false;
7232     int			x, y;
7233 
7234     // Default values to ensure someone who forgets the return code
7235     // won't cause *too* many problems.
7236     sx = 1;
7237     sy = 0;
7238 
7239     // Rather than finding the first available slot, we find the slot
7240     // after the last item slot.  New items are thus added to the end
7241     // even if there are holes in the inventory.
7242     // This is a REALLY slow method of doing this!
7243     for (x = MOBINV_WIDTH-1; x > 0; x--)
7244     {
7245 	for (y = MOBINV_HEIGHT-1; y >= 0; y--)
7246 	{
7247 	    if (!getItem(x, y))
7248 	    {
7249 		// Empty slot, a valid location.
7250 		seenempty = true;
7251 		sx = x;
7252 		sy = y;
7253 		if (seenfull)
7254 		{
7255 		    // Already seen a full slot so this is the last
7256 		    // available hole so we use it.
7257 		    return true;
7258 		}
7259 	    }
7260 	    else
7261 	    {
7262 		seenfull = true;
7263 		// If we have already seen an empty slot, sx and sy
7264 		// will be initialized to it so we can return accordingly.
7265 		if (seenempty)
7266 		    return true;
7267 		// Otherwise, keep looking for a free slot.
7268 	    }
7269 	}
7270     }
7271 
7272     // If our inventory is entirely empty, we will get here but
7273     // be able to succeed
7274     return seenempty;
7275 }
7276 
7277 ITEM *
getItem(int sx,int sy) const7278 MOB::getItem(int sx, int sy) const
7279 {
7280     ITEM		*cur;
7281 
7282     for (cur = myInventory; cur; cur = cur->getNext())
7283     {
7284 	if (cur->getX() == sx && cur->getY() == sy)
7285 	    return cur;
7286     }
7287 
7288     return 0;
7289 }
7290 
7291 ITEM *
randomItem() const7292 MOB::randomItem() const
7293 {
7294     ITEM		*cur, *result = 0;
7295     int			 numitems = 0;
7296 
7297     for (cur = myInventory; cur; cur = cur->getNext())
7298     {
7299 	if (!rand_choice(++numitems))
7300 	    result = cur;
7301     }
7302     return result;
7303 }
7304 
7305 ITEM *
getSourceOfIntrinsic(INTRINSIC_NAMES intrinsic) const7306 MOB::getSourceOfIntrinsic(INTRINSIC_NAMES intrinsic) const
7307 {
7308     ITEM	*item;
7309 
7310     for (item = myInventory; item; item = item->getNext())
7311     {
7312 	// This should match rebuildWornIntrinsic!
7313 	if (item->getX() == 0 || item->isCarryIntrinsic())
7314 	{
7315 	    if (item->hasIntrinsic(intrinsic))
7316 		return item;
7317 	}
7318     }
7319     return 0;
7320 }
7321 
7322 MOB *
getInflictorOfIntrinsic(INTRINSIC_NAMES intrinsic) const7323 MOB::getInflictorOfIntrinsic(INTRINSIC_NAMES intrinsic) const
7324 {
7325     INTRINSIC_COUNTER		*counter;
7326 
7327     counter = getCounter(intrinsic);
7328 
7329     if (!counter)
7330 	return 0;
7331 
7332     return counter->myInflictor.getMob();
7333 }
7334 
7335 ITEM *
getEquippedItem(ITEMSLOT_NAMES slot) const7336 MOB::getEquippedItem(ITEMSLOT_NAMES slot) const
7337 {
7338     return getItem(0, slot);
7339 }
7340 
7341 bool
hasAnyEquippedItems() const7342 MOB::hasAnyEquippedItems() const
7343 {
7344     ITEM	*item;
7345 
7346     for (item = myInventory; item; item = item->getNext())
7347     {
7348 	if (item->getX() == 0)
7349 	    return true;
7350     }
7351     return false;
7352 }
7353 
7354 bool
hasSlot(ITEMSLOT_NAMES slot) const7355 MOB::hasSlot(ITEMSLOT_NAMES slot) const
7356 {
7357     if (getSlotName(slot))
7358 	return true;
7359     else
7360 	return false;
7361 }
7362 
7363 const char *
getSlotName(ITEMSLOT_NAMES slot) const7364 MOB::getSlotName(ITEMSLOT_NAMES slot) const
7365 {
7366     ITEMSLOTSET_NAMES	slotset;
7367     bool		lefthanded = false;
7368 
7369     slotset = (ITEMSLOTSET_NAMES) glb_mobdefs[myDefinition].slotset;
7370 
7371     // Only care about handedness for a few things.
7372     if (slot == ITEMSLOT_LHAND || slot == ITEMSLOT_RHAND)
7373     {
7374 	// If the creature only has one "hand", such as a slug,
7375 	// we consider it right handed.
7376 	if (glb_itemslotsetdefs[slotset].lhand_name &&
7377 	    glb_itemslotsetdefs[slotset].rhand_name)
7378 	{
7379 	    lefthanded = hasIntrinsic(INTRINSIC_LEFTHANDED);
7380 	}
7381 	if (lefthanded)
7382 	{
7383 	    // Swap slots...
7384 	    if (slot == ITEMSLOT_LHAND)
7385 		slot = ITEMSLOT_RHAND;
7386 	    else
7387 		slot = ITEMSLOT_LHAND;
7388 	}
7389     }
7390 
7391     switch (slot)
7392     {
7393 	case ITEMSLOT_HEAD:
7394 	    return glb_itemslotsetdefs[slotset].head_name;
7395 	case ITEMSLOT_AMULET:
7396 	    return glb_itemslotsetdefs[slotset].neck_name;
7397 	case ITEMSLOT_RHAND:
7398 	    return glb_itemslotsetdefs[slotset].rhand_name;
7399 	case ITEMSLOT_LHAND:
7400 	    return glb_itemslotsetdefs[slotset].lhand_name;
7401 	case ITEMSLOT_BODY:
7402 	    return glb_itemslotsetdefs[slotset].body_name;
7403 	case ITEMSLOT_RRING:
7404 	    return glb_itemslotsetdefs[slotset].rring_name;
7405 	case ITEMSLOT_LRING:
7406 	    return glb_itemslotsetdefs[slotset].lring_name;
7407 	case ITEMSLOT_FEET:
7408 	    return glb_itemslotsetdefs[slotset].feet_name;
7409 	case NUM_ITEMSLOTS:
7410 	    UT_ASSERT(!"Invalid slot name!");
7411 	    return 0;
7412 	    break;
7413     }
7414 
7415     return 0;
7416 }
7417 
7418 ITEM *
dropItem(int sx,int sy)7419 MOB::dropItem(int sx, int sy)
7420 {
7421     ITEM		*cur, *prev = 0;
7422 
7423     for (cur = myInventory; cur; cur = cur->getNext())
7424     {
7425 	if (cur->getX() == sx && cur->getY() == sy)
7426 	{
7427 	    // Take this guy out of our list.
7428 	    if (prev)
7429 		prev->setNext(cur->getNext());
7430 	    else
7431 		myInventory = cur->getNext();
7432 
7433 	    cur->setNext(0);
7434 	    cur->setInventory(false);
7435 
7436 	    // If this was droppped from our equipment slot,
7437 	    // rebuild our intrinsics.
7438 	    if (!cur->getX() || cur->isCarryIntrinsic())
7439 	    {
7440 		rebuildWornIntrinsic();
7441 		rebuildAppearance();
7442 	    }
7443 
7444 	    return cur;
7445 	}
7446 
7447 	prev = cur;
7448     }
7449 
7450     return 0;
7451 }
7452 
7453 void
destroyInventory()7454 MOB::destroyInventory()
7455 {
7456     ITEM		*cur, *next;
7457 
7458     // TODO: This is very dangerous!  We may have a quest
7459     // item or similar?
7460     for (cur = myInventory; cur; cur = next)
7461     {
7462 	next = cur->getNext();
7463 
7464 	cur->setNext(0);
7465 	delete cur;
7466     }
7467 
7468     myInventory = 0;
7469     rebuildWornIntrinsic();
7470 }
7471 
7472 void
reverseInventory()7473 MOB::reverseInventory()
7474 {
7475     ITEM	*newlist, *cur, *next;
7476 
7477     newlist = 0;
7478     for (cur = myInventory; cur; cur = next)
7479     {
7480 	next = cur->getNext();
7481 	cur->setNext(newlist);
7482 	newlist = cur;
7483     }
7484     myInventory = newlist;
7485 }
7486 
7487 void
dropOneRing()7488 MOB::dropOneRing()
7489 {
7490     if (getEquippedItem(ITEMSLOT_RRING) &&
7491 	getEquippedItem(ITEMSLOT_LRING))
7492     {
7493 	// Random finger falls off...
7494 	ITEM		*drop, *olditem;
7495 	BUF		 buf;
7496 	const char	*slotname;
7497 	ITEMSLOT_NAMES	 slot;
7498 
7499 	slot = rand_choice(2) ? ITEMSLOT_LRING : ITEMSLOT_RRING;
7500 
7501 	slotname = getSlotName(slot);
7502 	// Since you have something equipped there, it better have a name!
7503 	UT_ASSERT(slotname != 0);
7504 	if (!slotname)
7505 	    slotname = "finger";
7506 
7507 	olditem = getEquippedItem(slot);
7508 
7509 	drop = dropItem(olditem->getX(), olditem->getY());
7510 	UT_ASSERT(drop == olditem);
7511 
7512 	glbCurLevel->acquireItem(drop, getX(), getY(), this);
7513 
7514 	buf.sprintf("%%IU <I:fall> off %%r %s.", slotname);
7515 	formatAndReport(buf.buffer(), drop);
7516 
7517 	// We dropped a ring, so must rebuild.
7518 	rebuildAppearance();
7519 	rebuildWornIntrinsic();
7520     }
7521 }
7522 
7523 bool
hasItem(ITEM_NAMES def) const7524 MOB::hasItem(ITEM_NAMES def) const
7525 {
7526     ITEM		*item;
7527 
7528     for (item = myInventory; item; item = item->getNext())
7529     {
7530 	if (item->getDefinition() == def)
7531 	    return true;
7532     }
7533     return false;
7534 }
7535 
7536 bool
isLight() const7537 MOB::isLight() const
7538 {
7539     int		 i;
7540     ITEM	*item;
7541 
7542     if (glb_mobdefs[myDefinition].lightradius)
7543 	return true;
7544 
7545     if (hasIntrinsic(INTRINSIC_AFLAME))
7546 	return true;
7547 
7548     for (i = 0; i < NUM_ITEMSLOTS; i++)
7549     {
7550 	item = getEquippedItem((ITEMSLOT_NAMES) i);
7551 	if (item && item->isLight())
7552 	    return true;
7553     }
7554 
7555     return false;
7556 }
7557 
7558 bool
isBoneless() const7559 MOB::isBoneless() const
7560 {
7561     return glb_mobdefs[myDefinition].isboneless;
7562 }
7563 
7564 bool
isBloodless() const7565 MOB::isBloodless() const
7566 {
7567     return defn().isbloodless;
7568 }
7569 
7570 bool
canResurrectFromCorpse() const7571 MOB::canResurrectFromCorpse() const
7572 {
7573     return defn().canresurrectfromcorpse;
7574 }
7575 
7576 MOB *
getMaster() const7577 MOB::getMaster() const
7578 {
7579     if (!hasIntrinsic(INTRINSIC_TAME))
7580 	return 0;
7581 
7582     return getInflictorOfIntrinsic(INTRINSIC_TAME);
7583 }
7584 
7585 bool
isSlave(const MOB * master) const7586 MOB::isSlave(const MOB *master) const
7587 {
7588     const MOB	*slave, *slavesmaster;
7589 
7590     if (!master)
7591 	return false;
7592 
7593     slave = this;
7594     while (slave->hasIntrinsic(INTRINSIC_TAME))
7595     {
7596 	slavesmaster = slave->getInflictorOfIntrinsic(INTRINSIC_TAME);
7597 
7598 	if (!slavesmaster)
7599 	{
7600 	    // We are no longer tame as our master has died.
7601 	    // However, this is const...
7602 	    // slave->clearIntrinsic(INTRINSIC_TAME);
7603 	    return false;
7604 	}
7605 
7606 	slave = slavesmaster;
7607 
7608 	if (slave == master)
7609 	    return true;
7610     }
7611     return false;
7612 }
7613 
7614 void
makeSlaveOf(const MOB * master)7615 MOB::makeSlaveOf(const MOB *master)
7616 {
7617     if (master == this)
7618     {
7619 	// I am already my own master :>
7620 	return;
7621     }
7622     setTimedIntrinsic((MOB *) master, INTRINSIC_TAME, 10000);
7623     // Reset the ai target in case it is a master's friend.
7624     clearAITarget();
7625 }
7626 
7627 bool
hasCommonMaster(const MOB * other) const7628 MOB::hasCommonMaster(const MOB *other) const
7629 {
7630     const MOB	*master1, *master2;
7631 
7632     if (!other)
7633 	return false;
7634 
7635     master1 = this;
7636     while (master1 && master1->hasIntrinsic(INTRINSIC_TAME))
7637     {
7638 	master1 = master1->getInflictorOfIntrinsic(INTRINSIC_TAME);
7639     }
7640     master2 = other;
7641     while (master2 && master2->hasIntrinsic(INTRINSIC_TAME))
7642     {
7643 	master2 = master2->getInflictorOfIntrinsic(INTRINSIC_TAME);
7644     }
7645 
7646     if (master1 && (master1 == master2))
7647 	return true;
7648     return false;
7649 }
7650 
7651 int
getLightRadius() const7652 MOB::getLightRadius() const
7653 {
7654     int		 radius = 0;
7655     int		 i;
7656     ITEM	*item;
7657 
7658     radius = glb_mobdefs[myDefinition].lightradius;
7659 
7660     if (hasIntrinsic(INTRINSIC_AFLAME))
7661     {
7662 	if (radius < 2)
7663 	    radius = 2;
7664     }
7665 
7666     for (i = 0; i < NUM_ITEMSLOTS; i++)
7667     {
7668 	item = getEquippedItem((ITEMSLOT_NAMES) i);
7669 	if (item && item->isLight() && item->getLightRadius() > radius)
7670 	    radius = item->getLightRadius();
7671     }
7672 
7673     return radius;
7674 }
7675 
7676 bool
canMoveDiabolically() const7677 MOB::canMoveDiabolically() const
7678 {
7679     if (getDefinition() == MOB_GRIDBUG)
7680 	return true;
7681 
7682     if (hasIntrinsic(INTRINSIC_DRESSED_BARBARIAN))
7683 	return true;
7684 
7685     return false;
7686 }
7687 
7688 bool
canDigest(ITEM * item) const7689 MOB::canDigest(ITEM *item) const
7690 {
7691     // If item is acid resistant, no one can digest.
7692     if (item->hasIntrinsic(INTRINSIC_RESISTACID))
7693     {
7694 	if (!item->hasIntrinsic(INTRINSIC_VULNACID))
7695 	    return false;
7696     }
7697     else if (item->hasIntrinsic(INTRINSIC_VULNACID))
7698     {
7699 	return true;
7700     }
7701 
7702     MATERIAL_NAMES		 mat;
7703     const char			*edible;
7704 
7705     mat = item->getMaterial();
7706     edible = glb_mobdefs[myDefinition].edible;
7707     if (!edible)
7708 	return false;
7709     while (*edible)
7710     {
7711 	if (*edible == mat)
7712 	    return true;
7713 	edible++;
7714     }
7715 
7716     return false;
7717 }
7718 
7719 bool
safeToDigest(ITEM * item) const7720 MOB::safeToDigest(ITEM *item) const
7721 {
7722     if (!item)
7723 	return false;
7724 
7725     bool	ignorepoison;
7726     bool	ignorestone;
7727     bool	ignoregold;
7728     bool	ignoresilver;
7729 
7730     ignorepoison = hasIntrinsic(INTRINSIC_RESISTPOISON) || hasSkill(SKILL_BUTCHERY);
7731     ignorestone = hasIntrinsic(INTRINSIC_UNCHANGING) || hasIntrinsic(INTRINSIC_RESISTSTONING);
7732     ignoregold = !isWearing(MATERIAL_GOLD);
7733     ignoresilver = !isWearing(MATERIAL_SILVER);
7734 
7735     // Don't eat intrinsically poisoned stuff
7736     // Stuff that got poisoned we may still foolishly eat,
7737     // unless we are a familiar.  We want to be able to poison others
7738     // but we don't want familiars that learn poison bolt to suicide.
7739 
7740     // We do let familiars eat poly corpses.  This is considered amusing
7741     // and rare enough for users to avoid if they dislike.
7742 
7743     // We do let everyone eat acidic, yes it is damaging, but so is
7744     // being hungry and I don't want to risk the destoning code path
7745     // failing because someone didn't want to take a hit.
7746 
7747     if (ignorepoison)
7748     {
7749 	if (hasIntrinsic(INTRINSIC_FAMILIAR))
7750 	{
7751 	    if (item->isPoisoned())
7752 		return false;
7753 	}
7754     }
7755 
7756     MOB		*corpse;
7757     corpse = item->getCorpseMob();
7758     if (corpse)
7759     {
7760 	INTRINSIC_NAMES		intrinsic;
7761 
7762 	FOREACH_INTRINSIC(intrinsic)
7763 	{
7764 	    if (INTRINSIC::hasIntrinsic(
7765 			corpse->defn().eatgrant,
7766 			intrinsic))
7767 	    {
7768 		// See how bad this intrinsic is
7769 		if (glb_intrinsicdefs[intrinsic].ispoison)
7770 		{
7771 		    if (!ignorepoison)
7772 			return false;
7773 		}
7774 
7775 		if (intrinsic == INTRINSIC_STONING)
7776 		{
7777 		    if (!ignorestone)
7778 			return false;
7779 		}
7780 
7781 		if (intrinsic == INTRINSIC_VULNSILVER)
7782 		{
7783 		    if (!ignoresilver)
7784 			return false;
7785 		}
7786 
7787 		if (intrinsic == INTRINSIC_GOLDALLERGY)
7788 		{
7789 		    if (!ignoregold)
7790 			return false;
7791 		}
7792 	    }
7793 	}
7794     }
7795 
7796     return true;
7797 }
7798 
7799 bool
canFly() const7800 MOB::canFly() const
7801 {
7802     if (getMoveType() & MOVE_FLY)
7803 	return true;
7804 
7805     return false;
7806 }
7807 
7808 bool
isWearing(MATERIAL_NAMES material) const7809 MOB::isWearing(MATERIAL_NAMES material) const
7810 {
7811     ITEM	*item;
7812 
7813     for (item = myInventory; item; item = item->getNext())
7814     {
7815 	// Ignore inventory items
7816 	if (item->getX())
7817 	    continue;
7818 
7819 	if (item->getMaterial() == material)
7820 	    return true;
7821     }
7822     return false;
7823 }
7824 
7825 static int
glb_itemsortcomparebyslot(const void * v1,const void * v2)7826 glb_itemsortcomparebyslot(const void *v1, const void *v2)
7827 {
7828     const ITEM	*i1, *i2;
7829 
7830     i1 = *((const ITEM **) v1);
7831     i2 = *((const ITEM **) v2);
7832 
7833     // First sort by column
7834     if (i1->getX() < i2->getX())
7835 	return -1;
7836     if (i1->getX() > i2->getX())
7837 	return 1;
7838 
7839     // Then by row
7840     if (i1->getY() < i2->getY())
7841 	return -1;
7842     if (i1->getY() > i2->getY())
7843 	return 1;
7844 
7845     // Really should never happen.
7846     return 0;
7847 }
7848 
7849 void
sortInventoryBySlot()7850 MOB::sortInventoryBySlot()
7851 {
7852     // Sort all the items...
7853     ITEM		*item;
7854     int			 numitems, i;
7855 
7856     // Count how many items we have so we can make a flat array.
7857     numitems = 0;
7858     for (item = myInventory; item; item = item->getNext())
7859 	numitems++;
7860 
7861     // This is a trivially true path.
7862     if (!numitems)
7863 	return;
7864 
7865     ITEM		**flatarray;
7866 
7867     // Create a flat array of all the items.
7868     flatarray = new ITEM *[numitems];
7869 
7870     i = 0;
7871     for (item = myInventory; item; item = item->getNext())
7872 	flatarray[i++] = item;
7873 
7874     // Now, we sort the item pointers according to our rules:
7875     qsort(flatarray, numitems, sizeof(ITEM *), glb_itemsortcomparebyslot);
7876 
7877     // Now create our item list to match the flat array...
7878     myInventory = 0;
7879     while (i--)
7880     {
7881 	flatarray[i]->setNext(myInventory);
7882 	myInventory = flatarray[i];
7883     }
7884 
7885     delete [] flatarray;
7886 }
7887 
7888 void
alertMobGone(MOB * mob)7889 MOB::alertMobGone(MOB *mob)
7890 {
7891     // A no-op as our internal pointers are all marshalled.
7892     // We could use this to determine when someone has left the leve.
7893     // Perhaps we should do stair follow if we can see it?  Dive down
7894     // the hole?  Etc?
7895 }
7896 
7897 void
clearReferences()7898 MOB::clearReferences()
7899 {
7900     // This is used to reset mobs when they change levels.
7901     // As all references should be marshalled with MOBREF, I don't know
7902     // what this should do.
7903 }
7904 
7905 void
rebuildAppearance()7906 MOB::rebuildAppearance()
7907 {
7908     ITEM		*item;
7909     bool		 lefthanded;
7910     bool		 ismale;
7911 
7912     // Ignore non avatars as we may then equip before we initialize.
7913     if (!isAvatar())
7914 	return;
7915 
7916     // Don't rebuild if our tile isn't the avatar tile.  This
7917     // can happen if we possess someone else - we don't want the
7918     // old avatar to update!
7919     if (getDefinition() != MOB_AVATAR)
7920 	return;
7921 
7922     lefthanded = hasIntrinsic(INTRINSIC_LEFTHANDED);
7923 
7924     // Default to female, "He"s become male.  This leaves "it"s with the
7925     // feminine tilest util we make a neuter tileset :>
7926     // Interesting bug: getGender was returning "You", so avatars were
7927     // always female ;>
7928     ismale = false;
7929     if (getGender() == VERB_HE)
7930 	ismale = true;
7931 
7932     // We have a very specific comp order...
7933     if (hasIntrinsic(INTRINSIC_STONING))
7934 	gfx_copytiledata(TILE_AVATAR, MINI_NAKEDSTONE, ismale);
7935     else if (aiIsPoisoned())
7936 	gfx_copytiledata(TILE_AVATAR, MINI_NAKEDPOISON, ismale);
7937     else
7938 	gfx_copytiledata(TILE_AVATAR, MINI_NAKED, ismale);
7939     item = getEquippedItem(ITEMSLOT_HEAD);
7940     if (item && item->getMiniTile() != MINI_NONE)
7941 	gfx_compositetile(ITEMSLOT_HEAD, item->getMiniTile(), ismale);
7942     item = getEquippedItem(ITEMSLOT_FEET);
7943     if (item && item->getMiniTile() != MINI_NONE)
7944 	gfx_compositetile(ITEMSLOT_FEET, item->getMiniTile(), ismale);
7945     item = getEquippedItem(ITEMSLOT_BODY);
7946     if (item && item->getMiniTile() != MINI_NONE)
7947 	gfx_compositetile(ITEMSLOT_BODY, item->getMiniTile(), ismale);
7948     item = getEquippedItem(ITEMSLOT_RHAND);
7949     if (item && item->getMiniTile() != MINI_NONE)
7950     {
7951 	// This seems backwards.  That is just because I drew them
7952 	// all backwards :>
7953 	if (lefthanded)
7954 	    gfx_compositetile(ITEMSLOT_RHAND, item->getMiniTile(), ismale);
7955 	else
7956 	    gfx_compositetile(ITEMSLOT_LHAND, item->getMiniTile(), ismale);
7957     }
7958     item = getEquippedItem(ITEMSLOT_LHAND);
7959     if (item && item->getMiniTile() != MINI_NONE)
7960     {
7961 	if (lefthanded)
7962 	    gfx_compositetile(ITEMSLOT_LHAND, item->getMiniTile(), ismale);
7963 	else
7964 	    gfx_compositetile(ITEMSLOT_RHAND, item->getMiniTile(), ismale);
7965     }
7966     item = getEquippedItem(ITEMSLOT_RRING);
7967     if (item && item->getMiniTile() != MINI_NONE)
7968 	gfx_compositetile(ITEMSLOT_RRING, item->getMiniTile(), ismale);
7969     item = getEquippedItem(ITEMSLOT_LRING);
7970     if (item && item->getMiniTile() != MINI_NONE)
7971 	gfx_compositetile(ITEMSLOT_LRING, item->getMiniTile(), ismale);
7972     item = getEquippedItem(ITEMSLOT_AMULET);
7973     if (item && item->getMiniTile() != MINI_NONE)
7974 	gfx_compositetile(ITEMSLOT_AMULET, item->getMiniTile(), ismale);
7975 }
7976 
7977 void
rebuildWornIntrinsic()7978 MOB::rebuildWornIntrinsic()
7979 {
7980     ITEM		*item;
7981 
7982     if (myWornIntrinsic)
7983 	myWornIntrinsic->clearAll();
7984 
7985     for (item = myInventory; item; item = item->getNext())
7986     {
7987 	// Check to see if it is equipped...
7988 	if (item->getX() == 0 || item->isCarryIntrinsic())
7989 	{
7990 	    // Equipped, build the table:
7991 	    item->buildIntrinsicTable(myWornIntrinsic);
7992 	}
7993     }
7994 
7995     determineClassiness();
7996 }
7997 
7998 void
determineClassiness(int * srcdressiness)7999 MOB::determineClassiness(int *srcdressiness)
8000 {
8001     // Determine if the player is well dressed.
8002     bool 	wizard = true, fighter = true, ranger = true;
8003     bool	cleric = true, barb = true, necro = true;
8004     ITEM_NAMES	itemname;
8005     ITEM	*item;
8006     int		lcl_dress[NUM_GODS];
8007     int		*dressiness;
8008 
8009     if (srcdressiness)
8010 	dressiness = srcdressiness;
8011     else
8012 	dressiness = lcl_dress;
8013 
8014     memset(dressiness, 0, sizeof(int)*NUM_GODS);
8015 
8016     item = getEquippedItem(ITEMSLOT_HEAD);
8017     if (item)
8018     {
8019 	itemname = item->getDefinition();
8020 	if (itemname != ITEM_HAT)
8021 	    wizard = false;
8022 	else
8023 	    dressiness[GOD_WIZARD]++;
8024 
8025 	if (itemname != ITEM_LEATHERCAP)
8026 	    ranger = false;
8027 	else
8028 	    dressiness[GOD_ROGUE]++;
8029 
8030 	if (itemname != ITEM_HELM)
8031 	    fighter = false;
8032 	else
8033 	    dressiness[GOD_FIGHTER]++;
8034 
8035 	if (itemname != ITEM_FEATHERHELM)
8036 	    barb = false;
8037 	else
8038 	    dressiness[GOD_BARB]++;
8039 
8040 	if (item->getMaterial() != MATERIAL_GOLD)
8041 	    necro = false;
8042 	else
8043 	    dressiness[GOD_NECRO]++;
8044     }
8045     else
8046     {
8047 	wizard = false;
8048 	fighter = false;
8049 	ranger = false;
8050 	necro = false;
8051 	barb = false;
8052     }
8053 
8054     // Check for Tower shield for fighters.
8055     if (fighter || srcdressiness)
8056     {
8057 	item = getEquippedItem(ITEMSLOT_LHAND);
8058 	if (item)
8059 	{
8060 	    itemname = item->getDefinition();
8061 	    if (itemname != ITEM_TOWERSHIELD)
8062 		fighter = false;
8063 	    else
8064 		dressiness[GOD_FIGHTER]++;
8065 	}
8066 	else
8067 	{
8068 	    fighter = false;
8069 	}
8070     }
8071 
8072     // Check for spears for barbarians.
8073     if (barb || srcdressiness)
8074     {
8075 	item = getEquippedItem(ITEMSLOT_RHAND);
8076 	if (item)
8077 	{
8078 	    itemname = item->getDefinition();
8079 	    if (itemname != ITEM_SPEAR &&
8080 		itemname != ITEM_SILVERSPEAR)
8081 	    {
8082 		barb = false;
8083 	    }
8084 	    else
8085 		dressiness[GOD_BARB]++;
8086 	}
8087 	else
8088 	    barb = false;
8089     }
8090 
8091     // Check for either the bow or torch for rangers.
8092     if (ranger || srcdressiness)
8093     {
8094 	bool		rangergood = false;
8095 
8096 	item = getEquippedItem(ITEMSLOT_RHAND);
8097 	if (item && (item->getDefinition() == ITEM_RAPIER))
8098 	{
8099 	    ITEM		*lhand;
8100 	    // Check for swashbuckling!
8101 	    dressiness[GOD_ROGUE]++;
8102 	    lhand = getEquippedItem(ITEMSLOT_LHAND);
8103 	    if (lhand && (lhand->getDefinition() == ITEM_BUCKLER))
8104 	    {
8105 		rangergood = true;
8106 		dressiness[GOD_ROGUE]++;
8107 	    }
8108 	}
8109 	if (!rangergood && (!item || (item->getDefinition() != ITEM_BOW)))
8110 	{
8111 	    // Check for torch.  Aragorn, after all, wielded
8112 	    // his torch in battle :>
8113 	    item = getEquippedItem(ITEMSLOT_LHAND);
8114 	    if (!item ||
8115 		((item->getDefinition() != ITEM_TORCH) &&
8116 		 (item->getDefinition() != ITEM_BOW)
8117 		))
8118 		ranger = false;
8119 	    else
8120 		dressiness[GOD_ROGUE]++;
8121 	}
8122 	else
8123 	{
8124 	    if (!rangergood && (item->getDefinition() != ITEM_BOW))
8125 		dressiness[GOD_ROGUE]++;
8126 	}
8127     }
8128 
8129     // Check for a blunt weapon for clerics
8130     // I don't think this should be necessary...  Spellbooks suffice, IMO.
8131 #if 0
8132     if (cleric)
8133     {
8134 	item = getEquippedItem(ITEMSLOT_RHAND);
8135 	if (!item || item->getAttackSkill() != SKILL_WEAPON_BLUNT)
8136 	    cleric = false;
8137     }
8138 #endif
8139 
8140     // Check for book wield for clerics.
8141     if (cleric || srcdressiness)
8142     {
8143 	item = getEquippedItem(ITEMSLOT_LHAND);
8144 	if (!item || item->getItemType() != ITEMTYPE_SPELLBOOK)
8145 	    cleric = false;
8146 	else
8147 	    dressiness[GOD_CLERIC]++;
8148     }
8149 
8150     // Check for wand or staff for necromancers
8151     if (necro || srcdressiness)
8152     {
8153 	item = getEquippedItem(ITEMSLOT_RHAND);
8154 	if (!item ||
8155 	    (item->getItemType() != ITEMTYPE_STAFF &&
8156 	     item->getItemType() != ITEMTYPE_WAND))
8157 	{
8158 	    item = getEquippedItem(ITEMSLOT_LHAND);
8159 	    if (!item ||
8160 		(item->getItemType() != ITEMTYPE_STAFF &&
8161 		 item->getItemType() != ITEMTYPE_WAND))
8162 	    {
8163 		necro = false;
8164 	    }
8165 	    else
8166 		dressiness[GOD_NECRO]++;
8167 	}
8168 	else
8169 	    dressiness[GOD_NECRO]++;
8170     }
8171 
8172     if (wizard || ranger || fighter || cleric || necro || barb || srcdressiness)
8173     {
8174 	item = getEquippedItem(ITEMSLOT_BODY);
8175 	if (item)
8176 	{
8177 	    itemname = item->getDefinition();
8178 	    if (itemname != ITEM_ROBE)
8179 		wizard = false;
8180 	    else
8181 		dressiness[GOD_WIZARD]++;
8182 	    if (itemname != ITEM_STUDDEDLEATHER &&
8183 		itemname != ITEM_LEATHERTUNIC)
8184 		ranger = false;
8185 	    else
8186 		dressiness[GOD_ROGUE]++;
8187 	    if (itemname != ITEM_PLATEMAIL)
8188 		fighter = false;
8189 	    else
8190 		dressiness[GOD_FIGHTER]++;
8191 	    if (itemname != ITEM_CRYSTALPLATE)
8192 		necro = false;
8193 	    else
8194 		dressiness[GOD_NECRO]++;
8195 	    if (itemname != ITEM_CHAINMAIL &&
8196 		itemname != ITEM_MITHRILMAIL)
8197 		cleric = false;
8198 	    else
8199 		dressiness[GOD_CLERIC]++;
8200 	    if (itemname != ITEM_STUDDEDLEATHER &&
8201 		itemname != ITEM_LEATHERTUNIC &&
8202 		itemname != ITEM_CHAINMAIL &&
8203 		itemname != ITEM_MITHRILMAIL)
8204 		barb = false;
8205 	    else
8206 		dressiness[GOD_BARB]++;
8207 	}
8208 	else
8209 	{
8210 	    wizard = false;
8211 	    ranger = false;
8212 	    fighter = false;
8213 	    cleric = false;
8214 	    necro = false;
8215 	    barb = false;
8216 	}
8217     }
8218 
8219     if (wizard || ranger || fighter || cleric || barb || srcdressiness)
8220     {
8221 	item = getEquippedItem(ITEMSLOT_FEET);
8222 	if (item)
8223 	{
8224 	    itemname = item->getDefinition();
8225 	    if (itemname != ITEM_POINTEDSLIPPERS)
8226 		wizard = false;
8227 	    else
8228 		dressiness[GOD_WIZARD]++;
8229 	    if (itemname != ITEM_HIKINGBOOTS)
8230 		ranger = false;
8231 	    else
8232 		dressiness[GOD_ROGUE]++;
8233 	    if (itemname != ITEM_IRONSHOES)
8234 		fighter = false;
8235 	    else
8236 		dressiness[GOD_FIGHTER]++;
8237 	    if (itemname != ITEM_SANDALS)
8238 		cleric = false;
8239 	    else
8240 		dressiness[GOD_CLERIC]++;
8241 	    if (itemname != ITEM_RIDINGBOOTS)
8242 		barb = false;
8243 	    else
8244 		dressiness[GOD_BARB]++;
8245 	}
8246 	else
8247 	{
8248 	    wizard = false;
8249 	    ranger = false;
8250 	    fighter = false;
8251 	    cleric = false;
8252 	    barb = false;
8253 	}
8254     }
8255 
8256     // Wizards require both ring slots to be full.
8257     // Necros require it full with gold rings.
8258     if (wizard || necro || srcdressiness)
8259     {
8260 	item = getEquippedItem(ITEMSLOT_LRING);
8261 	if (!item)
8262 	{
8263 	    wizard = false;
8264 	    necro = false;
8265 	}
8266 	else if (item->getMaterial() != MATERIAL_GOLD)
8267 	{
8268 	    necro = false;
8269 	    dressiness[GOD_WIZARD]++;
8270 	}
8271 	else
8272 	{
8273 	    dressiness[GOD_WIZARD]++;
8274 	    dressiness[GOD_NECRO]++;
8275 	}
8276 
8277 	item = getEquippedItem(ITEMSLOT_RRING);
8278 	if (!item)
8279 	{
8280 	    wizard = false;
8281 	    necro = false;
8282 	}
8283 	else if (item->getMaterial() != MATERIAL_GOLD)
8284 	{
8285 	    necro = false;
8286 	    dressiness[GOD_WIZARD]++;
8287 	}
8288 	else
8289 	{
8290 	    dressiness[GOD_WIZARD]++;
8291 	    dressiness[GOD_NECRO]++;
8292 	}
8293     }
8294 
8295     // Clerics require the proper amulet to be held.
8296     // THis should be made a specific amulet as soon as I have one
8297     // that looks more like a holy symbol.
8298     // Necros are similar, but have specific amulets in mind.
8299     // Barbarians just want something protecting their neck.
8300     if (cleric || necro || barb || srcdressiness)
8301     {
8302 	item = getEquippedItem(ITEMSLOT_AMULET);
8303 	if (!item)
8304 	{
8305 	    cleric = false;
8306 	    necro = false;
8307 	    barb = false;
8308 	}
8309 	else if (item->getMaterial() != MATERIAL_GOLD)
8310 	{
8311 	    necro = false;
8312 	    dressiness[GOD_BARB]++;
8313 	    dressiness[GOD_CLERIC]++;
8314 	}
8315 	else
8316 	{
8317 	    dressiness[GOD_BARB]++;
8318 	    dressiness[GOD_CLERIC]++;
8319 	    dressiness[GOD_NECRO]++;
8320 	}
8321     }
8322 
8323     if (wizard)
8324 	setIntrinsic(INTRINSIC_DRESSED_WIZARD);
8325     else
8326 	clearIntrinsic(INTRINSIC_DRESSED_WIZARD);
8327 
8328     if (ranger)
8329 	setIntrinsic(INTRINSIC_DRESSED_RANGER);
8330     else
8331 	clearIntrinsic(INTRINSIC_DRESSED_RANGER);
8332 
8333     if (fighter)
8334 	setIntrinsic(INTRINSIC_DRESSED_FIGHTER);
8335     else
8336 	clearIntrinsic(INTRINSIC_DRESSED_FIGHTER);
8337 
8338     if (cleric)
8339 	setIntrinsic(INTRINSIC_DRESSED_CLERIC);
8340     else
8341 	clearIntrinsic(INTRINSIC_DRESSED_CLERIC);
8342 
8343     if (necro)
8344 	setIntrinsic(INTRINSIC_DRESSED_NECROMANCER);
8345     else
8346 	clearIntrinsic(INTRINSIC_DRESSED_NECROMANCER);
8347 
8348     if (barb)
8349 	setIntrinsic(INTRINSIC_DRESSED_BARBARIAN);
8350     else
8351 	clearIntrinsic(INTRINSIC_DRESSED_BARBARIAN);
8352 }
8353 
8354 bool
hasIntrinsic(INTRINSIC_NAMES intrinsic,bool allowitem) const8355 MOB::hasIntrinsic(INTRINSIC_NAMES intrinsic, bool allowitem) const
8356 {
8357     if (myIntrinsic.hasIntrinsic(intrinsic))
8358 	return true;
8359 
8360     if (allowitem &&
8361 	myWornIntrinsic && myWornIntrinsic->hasIntrinsic(intrinsic))
8362 	return true;
8363 
8364     return false;
8365 }
8366 
8367 int
intrinsicFromWhat(INTRINSIC_NAMES intrinsic) const8368 MOB::intrinsicFromWhat(INTRINSIC_NAMES intrinsic) const
8369 {
8370     int		src = 0;
8371 
8372     if (myWornIntrinsic && myWornIntrinsic->hasIntrinsic(intrinsic))
8373 	src |= INTRINSIC_FROM_ITEM;
8374 
8375     if (myIntrinsic.hasIntrinsic(intrinsic))
8376     {
8377 	// Determine if permament or not.
8378 	if (getCounter(intrinsic))
8379 	    src |= INTRINSIC_FROM_TEMPORARY;
8380 	else
8381 	    src |= INTRINSIC_FROM_PERMAMENT;
8382     }
8383 
8384     return src;
8385 }
8386 
8387 const char *
intrinsicFromWhatLetter(INTRINSIC_NAMES intrinsic) const8388 MOB::intrinsicFromWhatLetter(INTRINSIC_NAMES intrinsic) const
8389 {
8390     int		src = intrinsicFromWhat(intrinsic);
8391 
8392     // This is the sort of exciting thing I get to write while
8393     // waiting for the GO train.  Again, it is 20 minutes late!
8394     if (src == 0)
8395 	return "";
8396     if (src == INTRINSIC_FROM_ITEM)
8397 	return "x";
8398     if (src & INTRINSIC_FROM_ITEM)
8399 	return "*";
8400     // Built in only.
8401     return "+";
8402 }
8403 
8404 bool
hasSkill(SKILL_NAMES skill,bool allowitem,bool ignoreamnesia) const8405 MOB::hasSkill(SKILL_NAMES skill, bool allowitem, bool ignoreamnesia) const
8406 {
8407     if (skill == SKILL_NONE)
8408 	return false;
8409 
8410     if (!ignoreamnesia && hasIntrinsic(INTRINSIC_AMNESIA))
8411 	return false;
8412 
8413     return hasIntrinsic((INTRINSIC_NAMES) glb_skilldefs[skill].intrinsic, allowitem);
8414 }
8415 
8416 bool
skillProc(SKILL_NAMES skill) const8417 MOB::skillProc(SKILL_NAMES skill) const
8418 {
8419     if (skill == SKILL_NONE)
8420 	return false;
8421 
8422     return rand_chance(glb_skilldefs[skill].proc);
8423 }
8424 
8425 int
getUsedSkillSlots() const8426 MOB::getUsedSkillSlots() const
8427 {
8428     bool	testedskill[NUM_INTRINSICS];
8429     int		i, numslots;
8430 
8431     memset(testedskill, 0, sizeof(bool) * NUM_INTRINSICS);
8432 
8433     numslots = 0;
8434     for (i = 0; i < NUM_SKILLS; i++)
8435     {
8436 	if (testedskill[glb_skilldefs[i].intrinsic])
8437 	    continue;
8438 	if (hasSkill((SKILL_NAMES) i))
8439 	{
8440 	    // If this was granted by an item, ignore it.
8441 	    if (getSourceOfIntrinsic((INTRINSIC_NAMES) glb_skilldefs[i].intrinsic))
8442 		continue;
8443 	    numslots++;
8444 	    testedskill[glb_skilldefs[i].intrinsic] = true;
8445 	}
8446     }
8447 
8448     return numslots;
8449 }
8450 
8451 int
getFreeSkillSlots() const8452 MOB::getFreeSkillSlots() const
8453 {
8454     int		freeslots;
8455 
8456     freeslots = getHitDie() - getUsedSkillSlots();
8457 
8458     // We've artificially lifted our skill slots by the given hit die,
8459     // subtract out our raw form to get how many we have earned.
8460     freeslots += defn().baseskills - defn().hitdie;
8461 
8462     return freeslots;
8463 }
8464 
8465 bool
canLearnSkill(SKILL_NAMES skill,bool explain) const8466 MOB::canLearnSkill(SKILL_NAMES skill, bool explain) const
8467 {
8468     bool		canlearn = true;
8469     BUF			buf;
8470 
8471     if (skill == SKILL_NONE)
8472     {
8473 	if (explain)
8474 	    reportMessage("That is not a skill!");
8475 	return false;
8476     }
8477 
8478     if (hasIntrinsic(INTRINSIC_AMNESIA))
8479     {
8480 	if (explain)
8481 	    reportMessage("Your condition seems to be anterograde as well as retrograde.");
8482 	return false;
8483     }
8484 
8485     if (hasSkill(skill))
8486     {
8487 	if (explain)
8488 	{
8489 	    buf.sprintf("You already know %s.",
8490 			    glb_skilldefs[skill].name);
8491 	    reportMessage(buf);
8492 	}
8493 	return false;
8494     }
8495 
8496     // Determine if we have any free skills...
8497     int		freeslots;
8498 
8499     freeslots = getFreeSkillSlots();
8500     if (freeslots <= 0)
8501     {
8502 	if (explain)
8503 	{
8504 	    reportMessage("You have no free skill slots.");
8505 	}
8506 	return false;
8507     }
8508 
8509     const char 		*prereq;
8510     prereq = glb_skilldefs[skill].prereq;
8511     while (*prereq)
8512     {
8513 	if (!hasSkill((SKILL_NAMES) *prereq))
8514 	{
8515 	    canlearn = false;
8516 	    if (explain)
8517 	    {
8518 		buf.sprintf("You must first learn %s.",
8519 			    glb_skilldefs[(u8) *prereq].name);
8520 		reportMessage(buf);
8521 	    }
8522 	}
8523 	prereq++;
8524     }
8525 
8526     return canlearn;
8527 }
8528 
8529 bool
hasSpell(SPELL_NAMES spell,bool allowitem,bool ignoreamnesia) const8530 MOB::hasSpell(SPELL_NAMES spell, bool allowitem, bool ignoreamnesia) const
8531 {
8532     if (!ignoreamnesia && hasIntrinsic(INTRINSIC_AMNESIA))
8533 	return false;
8534 
8535     return hasIntrinsic((INTRINSIC_NAMES) glb_spelldefs[spell].intrinsic, allowitem);
8536 }
8537 
8538 int
getUsedSpellSlots() const8539 MOB::getUsedSpellSlots() const
8540 {
8541     bool	testedspell[NUM_INTRINSICS];
8542     int		i, numslots;
8543 
8544     memset(testedspell, 0, sizeof(bool) * NUM_INTRINSICS);
8545 
8546     numslots = 0;
8547     for (i = 0; i < NUM_SPELLS; i++)
8548     {
8549 	if (testedspell[glb_spelldefs[i].intrinsic])
8550 	    continue;
8551 	if (hasSpell((SPELL_NAMES) i))
8552 	{
8553 	    // If this was granted by an item, ignore it.
8554 	    // (Ie, staff granted spells)
8555 	    if (getSourceOfIntrinsic((INTRINSIC_NAMES) glb_spelldefs[i].intrinsic))
8556 		continue;
8557 	    numslots++;
8558 	    testedspell[glb_spelldefs[i].intrinsic] = true;
8559 	}
8560     }
8561 
8562     return numslots;
8563 }
8564 
8565 int
getFreeSpellSlots() const8566 MOB::getFreeSpellSlots() const
8567 {
8568     int		freeslots;
8569 
8570     freeslots = getMagicDie() - getUsedSpellSlots();
8571 
8572     freeslots += defn().basespells - defn().mpdie;
8573 
8574     return freeslots;
8575 }
8576 
8577 bool
canLearnSpell(SPELL_NAMES spell,bool explain) const8578 MOB::canLearnSpell(SPELL_NAMES spell, bool explain) const
8579 {
8580     bool		canlearn = true;
8581     BUF			buf;
8582 
8583     if (spell == SPELL_NONE)
8584     {
8585 	if (explain)
8586 	    reportMessage("That is not a spell!");
8587 	return false;
8588     }
8589 
8590     if (hasIntrinsic(INTRINSIC_AMNESIA))
8591     {
8592 	if (explain)
8593 	    reportMessage("Your condition seems to be anterograde as well as retrograde.");
8594 	return false;
8595     }
8596 
8597     if (hasSpell(spell))
8598     {
8599 	if (explain)
8600 	{
8601 	    buf.sprintf("You already know %s.",
8602 			    glb_spelldefs[spell].name);
8603 	    reportMessage(buf);
8604 	}
8605 	return false;
8606     }
8607 
8608     // Determine if we have any free skills...
8609     int		freeslots;
8610 
8611     // Get a bonus magic spell slot cause we count from zero.
8612     freeslots = getFreeSpellSlots();
8613     if (freeslots <= 0)
8614     {
8615 	if (explain)
8616 	{
8617 	    reportMessage("You have no free spell slots.");
8618 	}
8619 	return false;
8620     }
8621 
8622     const char 		*prereq;
8623     prereq = glb_spelldefs[spell].prereq;
8624     while (*prereq)
8625     {
8626 	if (!hasSpell((SPELL_NAMES) *prereq))
8627 	{
8628 	    canlearn = false;
8629 	    if (explain)
8630 	    {
8631 		buf.sprintf("You must first learn %s.",
8632 			    glb_spelldefs[(u8) *prereq].name);
8633 		reportMessage(buf);
8634 	    }
8635 	}
8636 	prereq++;
8637     }
8638 
8639     return canlearn;
8640 }
8641 
8642 void
setIntrinsic(INTRINSIC_NAMES intrinsic)8643 MOB::setIntrinsic(INTRINSIC_NAMES intrinsic)
8644 {
8645     // If we have a counter, we get rid of it.  Otherwise this
8646     // intrinsic will vanish when the counter is up :>
8647     removeCounter(intrinsic);
8648 
8649     // Set our intrinsic list...
8650     if (!myIntrinsic.setIntrinsic(intrinsic))
8651     {
8652 	if (glb_intrinsicdefs[intrinsic].affectappearance)
8653 	    rebuildAppearance();
8654 	// Verify it is not masked be worn clothing
8655 	if (glb_intrinsicdefs[intrinsic].maskchangeifclothed &&
8656 	    myWornIntrinsic &&
8657 	    myWornIntrinsic->hasIntrinsic(intrinsic))
8658 	{
8659 	    return;
8660 	}
8661 
8662 	// A net new intrinsic.  We broad cast it, possible to the world,
8663 	// depending on visiblechange.
8664 	if (isAvatar() ||
8665 	    (getAvatar() && glb_intrinsicdefs[intrinsic].visiblechange &&
8666 	     getAvatar()->canSense(this)))
8667 	{
8668 	    formatAndReport(glb_intrinsicdefs[intrinsic].gaintxt);
8669 	}
8670     }
8671 }
8672 
8673 void
mergeIntrinsicsFromString(const char * rawstr)8674 MOB::mergeIntrinsicsFromString(const char *rawstr)
8675 {
8676     if (!rawstr)
8677 	return;
8678 
8679     while (*rawstr)
8680     {
8681 	setIntrinsic( (INTRINSIC_NAMES) ((u8)*rawstr) );
8682 	rawstr++;
8683     }
8684 }
8685 
8686 void
clearIntrinsic(INTRINSIC_NAMES intrinsic,bool silent)8687 MOB::clearIntrinsic(INTRINSIC_NAMES intrinsic, bool silent)
8688 {
8689     // Remove the counter so future counter sets don't get
8690     // confused.
8691     removeCounter(intrinsic);
8692 
8693     if (myIntrinsic.clearIntrinsic(intrinsic))
8694     {
8695 	if (glb_intrinsicdefs[intrinsic].affectappearance)
8696 	    rebuildAppearance();
8697 
8698 	if (silent)
8699 	    return;
8700 
8701 	// Verify it is not masked be worn clothing
8702 	if (glb_intrinsicdefs[intrinsic].maskchangeifclothed &&
8703 	    myWornIntrinsic &&
8704 	    myWornIntrinsic->hasIntrinsic(intrinsic))
8705 	{
8706 	    return;
8707 	}
8708 
8709 	// Was previously set...
8710 	if (isAvatar() ||
8711 	    (getAvatar() && glb_intrinsicdefs[intrinsic].visiblechange &&
8712 	     getAvatar()->canSense(this)))
8713 	{
8714 	    formatAndReport(glb_intrinsicdefs[intrinsic].losetxt);
8715 	}
8716     }
8717 }
8718 
8719 POISON_NAMES
getPoisonFromIntrinsic(INTRINSIC_NAMES poison)8720 getPoisonFromIntrinsic(INTRINSIC_NAMES poison)
8721 {
8722     if (!glb_intrinsicdefs[poison].ispoison)
8723 	return POISON_NONE;
8724 
8725     // Look up current poison.
8726     POISON_NAMES	pname;
8727 
8728     FOREACH_POISON(pname)
8729     {
8730 	if (glb_poisondefs[pname].intrinsic == poison)
8731 	    return pname;
8732     }
8733 
8734     UT_ASSERT(!"Poison intrinsic with no poison.");
8735     return POISON_NONE;
8736 }
8737 
8738 INTRINSIC_NAMES
getNextMorePowerfulPoisonIntrinsic(INTRINSIC_NAMES poison)8739 getNextMorePowerfulPoisonIntrinsic(INTRINSIC_NAMES poison)
8740 {
8741     if (!glb_intrinsicdefs[poison].ispoison)
8742 	return INTRINSIC_NONE;
8743 
8744     // Look up current poison.
8745     POISON_NAMES	pname;
8746 
8747     FOREACH_POISON(pname)
8748     {
8749 	if (glb_poisondefs[pname].intrinsic == poison)
8750 	    break;
8751     }
8752 
8753     pname = (POISON_NAMES) (pname + 1);
8754     if (pname >= NUM_POISONS)
8755 	return INTRINSIC_NONE;
8756 
8757     return (INTRINSIC_NAMES) glb_poisondefs[pname].intrinsic;
8758 }
8759 
8760 void
setTimedIntrinsic(MOB * inflictor,INTRINSIC_NAMES intrinsic,int turns,bool quiet)8761 MOB::setTimedIntrinsic(MOB *inflictor, INTRINSIC_NAMES intrinsic, int turns, bool quiet)
8762 {
8763     INTRINSIC_COUNTER	*counter;
8764 
8765     bool		 ispoison;
8766     ispoison = glb_intrinsicdefs[intrinsic].ispoison;
8767 
8768     // Check for any intrinisc resistance.  These will prevent us
8769     // from ever getting the intrinsic in the first place.
8770     if (glb_intrinsicdefs[intrinsic].resistance != INTRINSIC_NONE)
8771     {
8772 	if (hasIntrinsic((INTRINSIC_NAMES)glb_intrinsicdefs[intrinsic].resistance))
8773 	{
8774 	    // If there is an inflictor, we can tell them that
8775 	    // we resisted.
8776 	    if (inflictor)
8777 	    {
8778 		inflictor->aiNoteThatTargetHasIntrinsic(this,
8779 		    (INTRINSIC_NAMES)glb_intrinsicdefs[intrinsic].resistance);
8780 	    }
8781 	    return;
8782 	}
8783     }
8784 
8785     counter = getCounter(intrinsic);
8786 
8787     if (counter)
8788     {
8789 	if (ispoison)
8790 	{
8791 	    // When you stack poisons, you always get the next more
8792 	    // powerful poison.
8793 	    INTRINSIC_COUNTER		*nextcounter;
8794 	    INTRINSIC_NAMES		 nextpoison;
8795 
8796 	    nextpoison = getNextMorePowerfulPoisonIntrinsic(intrinsic);
8797 	    if (nextpoison == INTRINSIC_NONE)
8798 	    {
8799 		// Already most powerful poison.
8800 		counter->myTurns += turns;
8801 		counter->myInflictor.setMob(inflictor);
8802 	    }
8803 	    else
8804 	    {
8805 		// See if we already are poisoned with it (we should not be)
8806 		nextcounter = getCounter(nextpoison);
8807 		if (nextcounter)
8808 		{
8809 		    // We are masked by the more powerful poison, so remove
8810 		    // ourselves.
8811 		    // This should never happen.
8812 		    clearIntrinsic(intrinsic, true);
8813 		    return;
8814 		}
8815 
8816 		// No next more powerful poison exists.
8817 		// Upgrade and create it.
8818 		int	newcount;
8819 		newcount = (counter->myTurns + turns + 1) / 2;
8820 		clearIntrinsic(intrinsic, true);
8821 		setTimedIntrinsic(inflictor, nextpoison, newcount);
8822 		return;
8823 	    }
8824 	}
8825 	else
8826 	{
8827 	    // Increment our counter.
8828 	    counter->myTurns += turns;
8829 	    counter->myInflictor.setMob(inflictor);
8830 	}
8831     }
8832     else
8833     {
8834 	// If we are a poison, we want to make sure we are not
8835 	// masked by a more powerful poison.
8836 	if (ispoison)
8837 	{
8838 	    INTRINSIC_NAMES		 nextpoison;
8839 
8840 	    for (nextpoison = getNextMorePowerfulPoisonIntrinsic(intrinsic);
8841 		 nextpoison != INTRINSIC_NONE;
8842 		 nextpoison = getNextMorePowerfulPoisonIntrinsic(nextpoison))
8843 	    {
8844 		if (hasIntrinsic(nextpoison))
8845 		{
8846 		    return;
8847 		}
8848 	    }
8849 
8850 	    // Check to see if there is a weaker poison that we will
8851 	    // mask.  If there is, we want to clear it.
8852 	    POISON_NAMES	newpoison = getPoisonFromIntrinsic(intrinsic);
8853 	    POISON_NAMES	lesserpoison;
8854 	    INTRINSIC_NAMES	lesserintrinsic;
8855 	    INTRINSIC_COUNTER	*lessercounter;
8856 
8857 	    FOREACH_POISON(lesserpoison)
8858 	    {
8859 		if (lesserpoison == newpoison)
8860 		    break;
8861 		if (lesserpoison != POISON_NONE)
8862 		{
8863 		    lesserintrinsic = (INTRINSIC_NAMES) glb_poisondefs[lesserpoison].intrinsic;
8864 		    lessercounter = getCounter(lesserintrinsic);
8865 		    if (lessercounter)
8866 			turns += (lessercounter->myTurns + 1) >> (int)(newpoison - lesserpoison);
8867 		    clearIntrinsic(lesserintrinsic, true);
8868 		}
8869 	    }
8870 
8871 	}
8872 
8873 	// Determine if we already have the intrinsic
8874 	// permamently.  If we do, we don't want to set
8875 	// the counter as we will then LOSE it when it runs
8876 	// out!
8877 	if (!myIntrinsic.hasIntrinsic(intrinsic))
8878 	    counter = addCounter(inflictor, intrinsic, turns);
8879 	else
8880 	    counter = 0;
8881     }
8882 
8883     if (counter && glb_intrinsicdefs[intrinsic].maxcount)
8884     {
8885 	if (counter->myTurns > glb_intrinsicdefs[intrinsic].maxcount)
8886 	    counter->myTurns = glb_intrinsicdefs[intrinsic].maxcount;
8887     }
8888 
8889     // Always set our main block..
8890     if (!myIntrinsic.setIntrinsic(intrinsic))
8891     {
8892 	if (glb_intrinsicdefs[intrinsic].affectappearance)
8893 	    rebuildAppearance();
8894 	// Was previously unset...
8895 	if (isAvatar() ||
8896 	    (getAvatar() && glb_intrinsicdefs[intrinsic].visiblechange &&
8897 	     getAvatar()->canSense(this)))
8898 	{
8899 	    if (!quiet)
8900 		formatAndReport(glb_intrinsicdefs[intrinsic].gaintxt);
8901 	}
8902     }
8903 }
8904 
8905 INTRINSIC_COUNTER *
getCounter(INTRINSIC_NAMES intrinsic) const8906 MOB::getCounter(INTRINSIC_NAMES intrinsic) const
8907 {
8908     INTRINSIC_COUNTER	*counter;
8909 
8910     for (counter = myCounters; counter; counter = counter->myNext)
8911     {
8912 	if (counter->myIntrinsic == intrinsic)
8913 	    return counter;
8914     }
8915     // None found...
8916     return 0;
8917 }
8918 
8919 void
removeCounter(INTRINSIC_NAMES intrinsic)8920 MOB::removeCounter(INTRINSIC_NAMES intrinsic)
8921 {
8922     INTRINSIC_COUNTER	*counter, *last;
8923 
8924     last = 0;
8925     for (counter = myCounters; counter; counter = counter->myNext)
8926     {
8927 	if (counter->myIntrinsic == intrinsic)
8928 	    break;
8929 	last = counter;
8930     }
8931 
8932     if (!counter)
8933 	return;		// Trivial remove
8934     if (last)
8935 	last->myNext = counter->myNext;
8936     else
8937 	myCounters = counter->myNext;
8938     delete counter;
8939 
8940     // Debug code to verify the counter was unlinked.
8941 #ifdef STRESS_TEST
8942     for (last = myCounters; last; last = last->myNext)
8943     {
8944 	UT_ASSERT(last != counter);
8945     }
8946 #endif
8947 }
8948 
8949 INTRINSIC_COUNTER *
addCounter(MOB * inflictor,INTRINSIC_NAMES intrinsic,int turns)8950 MOB::addCounter(MOB *inflictor, INTRINSIC_NAMES intrinsic, int turns)
8951 {
8952     INTRINSIC_COUNTER	*counter;
8953 
8954     counter = new INTRINSIC_COUNTER;
8955     counter->myNext = myCounters;
8956     counter->myIntrinsic = intrinsic;
8957     counter->myTurns = turns;
8958     counter->myInflictor.setMob(inflictor);
8959     myCounters = counter;
8960 
8961     return counter;
8962 }
8963 
8964 void
loadGlobal(SRAMSTREAM & is)8965 MOB::loadGlobal(SRAMSTREAM &is)
8966 {
8967     int		mob;
8968     int		val;
8969 
8970     for (mob = 0; mob < NUM_MOBS; mob++)
8971     {
8972 	is.uread(val, 16);
8973 	glbKillCount[mob] = val;
8974     }
8975 
8976     is.read(val, 8);
8977     ourAvatarDx = val;
8978     is.read(val, 8);
8979     ourAvatarDy = val;
8980 
8981     ai_load(is);
8982 }
8983 
8984 void
saveGlobal(SRAMSTREAM & os)8985 MOB::saveGlobal(SRAMSTREAM &os)
8986 {
8987     int		mob;
8988     int		val;
8989 
8990     for (mob = 0; mob < NUM_MOBS; mob++)
8991     {
8992 	val = glbKillCount[mob];
8993 	os.write(val, 16);
8994     }
8995 
8996     os.write(ourAvatarDx, 8);
8997     os.write(ourAvatarDy, 8);
8998 
8999     ai_save(os);
9000 }
9001 
9002 MOB *
load(SRAMSTREAM & is)9003 MOB::load(SRAMSTREAM &is)
9004 {
9005     int		 val;
9006     MOB		*me;
9007 
9008     me = new MOB();
9009 
9010     is.uread(val, 8);
9011     me->myDefinition = val;
9012 
9013     is.uread(val, 8);
9014     me->myOrigDefinition = val;
9015 
9016     // Reset this to zero so uninitialized memory doesn't make
9017     // everyone loud.
9018     me->myNoiseLevel = 0;
9019 
9020     // Load our self-reference.
9021     me->myOwnRef.load(is);
9022     if (!me->myOwnRef.transferMOB(me))
9023     {
9024 	UT_ASSERT(!"Transfer fail in Load");
9025     }
9026 
9027     me->myName.load(is);
9028 
9029     is.uread(val, 8);
9030     me->myX = val;
9031     is.uread(val, 8);
9032     me->myY = val;
9033     is.uread(val, 8);
9034     val ^= glb_mobdefs[me->myDefinition].explevel;
9035     me->myExpLevel = val;
9036     is.uread(val, 8);
9037     me->myDLevel = val;
9038     is.uread(val, 8);
9039     me->myAIFSM = val;
9040     is.uread(val, 16);
9041     me->myAIState = val;
9042     is.read(val, 16);
9043     me->myHP = val;
9044     is.read(val, 8);
9045     val ^= (glb_mobdefs[me->myDefinition].hitdie * 2);
9046     me->myHitDie = val;
9047     is.read(val, 16);
9048     me->myMaxHP = val;
9049     me->myHP ^= me->myMaxHP;
9050     is.read(val, 16);
9051     me->myMP = val;
9052     is.read(val, 8);
9053     val ^= (glb_mobdefs[me->myDefinition].mpdie * 2);
9054     me->myMagicDie = val;
9055     is.read(val, 16);
9056     me->myMaxMP = val;
9057     me->myMP ^= me->getMaxMP();
9058     is.read(val, 16);
9059     me->myExp = val;
9060     is.uread(val, 16);
9061     me->myFoodLevel = val;
9062     is.uread(val, 8);
9063     me->myGender = val;
9064 
9065     me->myAITarget.load(is);
9066 
9067     // Read in intrinsics...
9068     me->myIntrinsic.load(is);
9069 
9070     // Load counters...
9071     while (1)
9072     {
9073 	int			 i;
9074 	INTRINSIC_COUNTER	*counter;
9075 
9076 	is.uread(val, 8);
9077 	if (val)
9078 	    break;
9079 
9080 	is.uread(i, 8);
9081 	is.uread(val, 32);
9082 	// As it was set in our main block which loaded silently,
9083 	// we shouldn't get the spurious Intrinsic messages here.
9084 
9085 	// We do not want to call setTimedIntrinsic as that checks
9086 	// against the main block.  Instead, as we know we have the
9087 	// intrinsic already set in the main block, and we must add
9088 	// the counter, we just call add.
9089 
9090 	counter = me->addCounter(0, (INTRINSIC_NAMES) i, val);
9091 	UT_ASSERT(counter != 0);
9092 	if (counter)
9093 	    counter->myInflictor.load(is);
9094     }
9095 
9096     // Read in items.
9097     ITEM		*item;
9098     while (1)
9099     {
9100 	is.uread(val, 8);
9101 	if (val)
9102 	    break;
9103 	item = ITEM::load(is);
9104 
9105 	me->acquireItem(item, item->getX(), item->getY());
9106     }
9107 
9108     me->reverseInventory();
9109 
9110     // Load our poly base, if present:
9111     me->myBaseType.load(is);
9112 
9113     if (!me->myBaseType.isNull())
9114     {
9115 	MOB		*mob;
9116 
9117 	mob = MOB::load(is);
9118 	// myBaseType will already point to mob, but might as well
9119 	// assert.
9120 	UT_ASSERT(mob == me->myBaseType.getMob());
9121     }
9122 
9123     return me;
9124 }
9125 
9126 void
save(SRAMSTREAM & os)9127 MOB::save(SRAMSTREAM &os)
9128 {
9129     os.write(myDefinition, 8);
9130     os.write(myOrigDefinition, 8);
9131     myOwnRef.save(os);
9132     myName.save(os);
9133     os.write(myX, 8);
9134     os.write(myY, 8);
9135     os.write(myExpLevel ^ glb_mobdefs[myDefinition].explevel, 8);
9136     os.write(myDLevel, 8);
9137     os.write(myAIFSM, 8);
9138     os.write(myAIState, 16);
9139     os.write(myHP ^ myMaxHP, 16);
9140     os.write(myHitDie ^ (glb_mobdefs[myDefinition].hitdie * 2), 8);
9141     os.write(myMaxHP, 16);
9142     os.write(myMP ^ (getMaxMP()), 16);
9143     os.write(myMagicDie ^ (glb_mobdefs[myDefinition].mpdie * 2), 8);
9144     os.write(myMaxMP, 16);
9145     os.write(myExp, 16);
9146     os.write(myFoodLevel, 16);
9147     os.write(myGender, 8);
9148 
9149     myAITarget.save(os);
9150 
9151     // Intrinsics:
9152     myIntrinsic.save(os);
9153 
9154     // Counters...
9155     INTRINSIC_COUNTER		*counter;
9156 
9157     for (counter = myCounters; counter; counter = counter->myNext)
9158     {
9159 	// Flag for a counter...
9160 	os.write(0, 8);
9161 	os.write(counter->myIntrinsic, 8);
9162 	os.write(counter->myTurns, 32);
9163 	counter->myInflictor.save(os);
9164     }
9165     // End of intrinsic counters...
9166     os.write(1, 8);
9167 
9168     // Inventory:
9169     ITEM		*item;
9170     for (item = myInventory; item; item = item->getNext())
9171     {
9172 	os.write(0, 8);
9173 	item->save(os);
9174     }
9175     os.write(1, 8);
9176 
9177     // Save our poly base, if present:
9178     MOB		*mob;
9179 
9180     mob = myBaseType.getMob();
9181     myBaseType.save(os);
9182     if (mob)
9183 	mob->save(os);
9184 }
9185 
9186 bool
verifyMob() const9187 MOB::verifyMob() const
9188 {
9189     bool		valid = true;
9190     ITEM		*item;
9191 
9192     MOB			*self;
9193 
9194     self = myOwnRef.getMob();
9195     if (!self)
9196     {
9197 	// Null self references...
9198 	msg_report("0");
9199 	valid = false;
9200     }
9201     else if (self != this)
9202     {
9203 	// Invalid link.
9204 	msg_report("!");
9205 	valid = false;
9206     }
9207 
9208     // Verify all of our inventory.
9209     for (item = myInventory; item; item = item->getNext())
9210     {
9211 	valid &= item->verifyMob();
9212     }
9213 
9214     if (getBaseType())
9215     {
9216 	valid &= getBaseType()->verifyMob();
9217     }
9218 
9219     return valid;
9220 }
9221 
9222 bool
verifyCounterGone(INTRINSIC_COUNTER * counter) const9223 MOB::verifyCounterGone(INTRINSIC_COUNTER *counter) const
9224 {
9225     bool		valid = true;
9226     ITEM		*item;
9227     INTRINSIC_COUNTER	*test;
9228 
9229     for (test = myCounters; test; test = test->myNext)
9230     {
9231 	if (test == counter)
9232 	{
9233 	    UT_ASSERT(!"Counter still exists!");
9234 	    valid = false;
9235 	}
9236     }
9237 
9238     // Verify all of our inventory.
9239     for (item = myInventory; item; item = item->getNext())
9240     {
9241 	valid &= item->verifyCounterGone(counter);
9242     }
9243 
9244     if (getBaseType())
9245     {
9246 	valid &= getBaseType()->verifyCounterGone(counter);
9247     }
9248     return valid;
9249 }
9250 
9251 bool
hasBreathAttack() const9252 MOB::hasBreathAttack() const
9253 {
9254     if (getBreathAttack() != ATTACK_NONE)
9255 	return true;
9256 
9257     return false;
9258 }
9259 
9260 ATTACK_NAMES
getBreathAttack() const9261 MOB::getBreathAttack() const
9262 {
9263     return (ATTACK_NAMES) glb_mobdefs[myDefinition].breathattack;
9264 }
9265 
9266 int
getBreathRange() const9267 MOB::getBreathRange() const
9268 {
9269     ATTACK_NAMES		attack;
9270 
9271     attack = getBreathAttack();
9272 
9273     return (glb_attackdefs[attack].range);
9274 }
9275 
9276 bool
isBreathAttackCharged() const9277 MOB::isBreathAttackCharged() const
9278 {
9279     if (hasIntrinsic(INTRINSIC_TIRED))
9280 	return false;
9281 
9282     return true;
9283 }
9284 
9285 const char *
getBreathSubstance() const9286 MOB::getBreathSubstance() const
9287 {
9288     return glb_mobdefs[myDefinition].breathsubstance;
9289 }
9290 
9291 bool
isTalkative() const9292 MOB::isTalkative() const
9293 {
9294     if (getDefinition() == MOB_ELDER)
9295 	return true;
9296     return false;
9297 }
9298 
9299 void
submergeItemEffects(SQUARE_NAMES tile)9300 MOB::submergeItemEffects(SQUARE_NAMES tile)
9301 {
9302     ITEMSLOT_NAMES	 slot;
9303     ITEM		*item;
9304     bool		 destroy = false;
9305 
9306     slot = (ITEMSLOT_NAMES) rand_choice(NUM_ITEMSLOTS);
9307     item = getEquippedItem(slot);
9308     if (item)
9309     {
9310 	// Drown the item according to the liquid..
9311 	glbCurLevel->fillBottle(item, getX(), getY());
9312 
9313 	// Again find myself relazing on the shinkansen.  Truly
9314 	// a more relaxed way to travel.  Spent most of the trip
9315 	// reviewing for an upcoming meeting, so did not get my
9316 	// usual POWDER development.
9317 	// The new trains seem to have power outlets at every seat, however,
9318 	// so no need to restrict yourself to micro laptops like I am.
9319 	// But, cables?  Who wants to deal with them?
9320 	switch (tile)
9321 	{
9322 	    case SQUARE_WATER:
9323 		destroy = item->douse();
9324 		break;
9325 	    case SQUARE_LAVA:
9326 		destroy = item->ignite();
9327 		break;
9328 	    case SQUARE_ACID:
9329 		destroy = item->dissolve();
9330 		break;
9331 	    default:
9332 		break;
9333 	}
9334 	if (destroy)
9335 	{
9336 	    item = dropItem(item->getX(), item->getY());
9337 	    delete item;
9338 	}
9339 
9340 	// We may have just messed with our status..
9341 	rebuildAppearance();
9342 	rebuildWornIntrinsic();
9343     }
9344 }
9345 
9346 bool
zapCallbackStatic(int x,int y,bool final,void * data)9347 MOB::zapCallbackStatic(int x, int y, bool final, void *data)
9348 {
9349     MOB			*mob;
9350     MOB			*thismob;
9351     MOBREF		*mobref;
9352     bool		 keepgoing = true, lived = true;
9353     bool		 actuallyhit = false;
9354     bool		 forcehit = false;
9355     bool		 allowfinal = false;
9356 
9357     ATTACK_NAMES	 attack;
9358 
9359     mobref = (MOBREF *) data;
9360     thismob = mobref->getMob();
9361 
9362     // We need this to determine attack, Ie: digging eath elementals!
9363     mob = glbCurLevel->getMob(x, y);
9364 
9365     attack = ATTACK_NONE;
9366     switch (ourZapSpell)
9367     {
9368 	case SPELL_DIG:
9369 	    if (mob && mob->getDefinition() == MOB_EARTHELEMENTAL)
9370 	    {
9371 		attack = ATTACK_DIGEARTHELEMENTAL;
9372 	    }
9373 	    break;
9374 	case SPELL_FORCEBOLT:
9375 	    attack = ATTACK_SPELL_FORCEBOLT;
9376 	    break;
9377 
9378 	case SPELL_FORCEWALL:
9379 	    attack = ATTACK_SPELL_FORCEWALL;
9380 	    break;
9381 
9382 	case SPELL_FROSTBOLT:
9383 	    attack = ATTACK_SPELL_FROSTBOLT;
9384 	    break;
9385 
9386 	case SPELL_LIGHTNINGBOLT:
9387 	    attack = ATTACK_SPELL_LIGHTNING;
9388 	    break;
9389 
9390 	case SPELL_MAGICMISSILE:
9391 	    attack = ATTACK_SPELL_MAGICMISSILE;
9392 	    break;
9393 
9394 	case SPELL_FIREBALL:
9395 	    attack = ATTACK_SPELL_FIREBALL;
9396 	    allowfinal = true;
9397 	    break;
9398 
9399 	case SPELL_CHAINLIGHTNING:
9400 	    attack = ATTACK_SPELL_LIGHTNING;
9401 	    break;
9402 
9403 	case SPELL_POISONBOLT:
9404 	    attack = ATTACK_SPELL_POISONBOLT;
9405 	    break;
9406 
9407 	case SPELL_CLOUDKILL:
9408 	    attack = ATTACK_SPELL_CLOUDKILL;
9409 	    allowfinal = true;
9410 	    break;
9411 
9412 	case SPELL_FINGEROFDEATH:
9413 	    attack = ATTACK_SPELL_FINGEROFDEATH;
9414 	    if (thismob && thismob->hasIntrinsic(INTRINSIC_DRESSED_NECROMANCER))
9415 	    {
9416 		forcehit = true;
9417 	    }
9418 	    break;
9419 
9420 	default:
9421 	    attack = ATTACK_NONE;
9422 	    break;
9423     }
9424 
9425     // Do nothing on final attacks for these modes.
9426     if (!allowfinal && final)
9427 	return false;
9428 
9429     // Final attakcs always trigger this path.
9430     // Beware, mob may be null!
9431     if (mob || final)
9432     {
9433 	if (attack != ATTACK_NONE)
9434 	{
9435 	    // Check to see if we die...
9436 	    lived = true;
9437 	    actuallyhit = true;
9438 	    if (forcehit && mob && !final)
9439 	    {
9440 		lived = mob->receiveDamage(attack, thismob,
9441 					0, 0, ATTACKSTYLE_SPELL);
9442 		actuallyhit = true;
9443 	    }
9444 	    else if (mob && !final)
9445 	    {
9446 		lived = mob->receiveAttack(attack, thismob,
9447 					0, 0, ATTACKSTYLE_SPELL,
9448 					&actuallyhit);
9449 	    }
9450 
9451 	    if (!lived && mob == thismob)
9452 		return false;
9453 
9454 	    if (actuallyhit)
9455 	    {
9456 		// Magic missile can hit one foe per fireball count.
9457 		if (ourZapSpell == SPELL_MAGICMISSILE)
9458 		{
9459 		    if (ourFireBallCount > 0)
9460 			ourFireBallCount--;
9461 		    else
9462 			keepgoing = false;
9463 		}
9464 
9465 		if (ourZapSpell == SPELL_POISONBOLT)
9466 		    keepgoing = false;
9467 
9468 		if (ourZapSpell == SPELL_CLOUDKILL)
9469 		{
9470 		    if (final)
9471 		    {
9472 			// Generate our smoke
9473 			// TODO: This is improper conjugation!
9474 			thismob->formatAndReport("%B1 explodes!", glb_attackdefs[attack].explode_name);
9475 
9476 			// Create the relevant smoke.
9477 			if (glb_attackdefs[attack].explode_smoke != SMOKE_NONE)
9478 			{
9479 			    int		dx, dy;
9480 			    SMOKE_NAMES smoke = (SMOKE_NAMES)
9481 						    glb_attackdefs[attack].explode_smoke;
9482 
9483 			    for (dy = -1; dy <= 1; dy++)
9484 				for (dx = -1; dx <= 1; dx++)
9485 				    if (glbCurLevel->canMove(x+dx, y+dy,
9486 							    MOVE_STD_FLY))
9487 					glbCurLevel->setSmoke(x+dx, y+dy,
9488 							    smoke, thismob);
9489 			}
9490 		    }
9491 		    keepgoing = false;
9492 		}
9493 
9494 		if (ourZapSpell == SPELL_FIREBALL &&
9495 		    (rand_choice(ourFireBallCount) < 2))
9496 		{
9497 		    keepgoing = false;
9498 
9499 		    ourFireBallCount++;
9500 
9501 		    // Time for an inferno!
9502 		    {
9503 			BUF		buf;
9504 			buf.sprintf("%s fireball explodes!",
9505 			    (thismob ? thismob->getPossessive() : "the"));
9506 			glbCurLevel->reportMessage(buf, x, y);
9507 		    }
9508 
9509 		    ourEffectAttack = ATTACK_SPELL_FIREBALLBALL;
9510 		    glbCurLevel->fireBall(x, y, 1, true,
9511 					    areaAttackCBStatic,
9512 					    mobref);
9513 		}
9514 
9515 		// Check for chain lightning.  Note that chain lightning
9516 		// will keep going.
9517 		if (ourZapSpell == SPELL_CHAINLIGHTNING &&
9518 		    (rand_choice(ourFireBallCount) < 5))
9519 		{
9520 		    ourFireBallCount++;
9521 
9522 		    {
9523 			BUF		buf;
9524 			buf.sprintf("%s lightning forks!",
9525 				(thismob ? thismob->getPossessive() : "the"));
9526 			glbCurLevel->reportMessage(buf, x, y);
9527 		    }
9528 
9529 		    int		dx, dy;
9530 
9531 		    rand_direction(dx, dy);
9532 
9533 		    glbCurLevel->fireRay(x, y,
9534 					 dx, dy,
9535 					 6,
9536 					 MOVE_STD_FLY, true,
9537 					 zapCallbackStatic,
9538 					 mobref);
9539 		}
9540 	    }
9541 	}
9542     }
9543 
9544     // Local effects:
9545     if (ourZapSpell == SPELL_FROSTBOLT)
9546     {
9547 	if (glbCurLevel->freezeSquare(x, y, thismob))
9548 	    return false;
9549     }
9550 
9551     if (ourZapSpell == SPELL_FIREBALL)
9552     {
9553 	glbCurLevel->burnSquare(x, y, thismob);
9554 	// Yes, continue even if melted!
9555     }
9556 
9557     if (ourZapSpell == SPELL_DIG)
9558     {
9559 	if (!glbCurLevel->digSquare(x, y, thismob))
9560 	    return false;
9561     }
9562 
9563     // This dubious decision to omit the spark spell from this effect
9564     // was made whilst flying in economy over the pacific.  Much
9565     // thanks to my p1610 which is more than up to the task of working
9566     // in such confined areas, and thanks to AC for outfitting their 777s
9567     // with seatback power so I have no fear of even this eleven
9568     // hour flight depleting my battery.
9569     // I still think I prefer first class, however.
9570     // The dubious decision, it turns out, was of no merit as spark doesn't
9571     // use this code path and was added when I provided support for
9572     // blue dragons charging rapiers.  Please thus ignore that above
9573     // comment which no longer applies.  Well, the part about first
9574     // class certainly applies.
9575     if (ourZapSpell == SPELL_LIGHTNINGBOLT || ourZapSpell == SPELL_CHAINLIGHTNING)
9576     {
9577 	// Charge anything on this square...
9578 	glbCurLevel->electrifySquare(x, y,
9579 			     rand_dice(glb_attackdefs[attack].damage),
9580 			     thismob);
9581     }
9582 
9583     if (ourZapSpell == SPELL_FORCEBOLT || ourZapSpell == SPELL_FORCEWALL)
9584     {
9585 	// Force bolts will:
9586 	// Shatter doors
9587 	// Shatter secret doors
9588 	// Shatter boulders.
9589 	// Shatters trees.
9590 	// Shatter viewports
9591 	SQUARE_NAMES		tile;
9592 	BUF			buf;
9593 
9594 	tile = glbCurLevel->getTile(x, y);
9595 	switch (tile)
9596 	{
9597 	    case SQUARE_DOOR:
9598 	    case SQUARE_BLOCKEDDOOR:
9599 	    case SQUARE_SECRETDOOR:
9600 	    {
9601 		buf.sprintf("The force bolt shatters the %s.",
9602 			    (tile == SQUARE_SECRETDOOR ? "secret door" : "door"));
9603 		glbCurLevel->reportMessage(buf, x, y);
9604 
9605 		glbCurLevel->setTile(x, y, SQUARE_CORRIDOR);
9606 
9607 		// Ends the bolt
9608 		return false;
9609 	    }
9610 	    case SQUARE_VIEWPORT:
9611 	    {
9612 		glbCurLevel->reportMessage("The force bolt shatters the glass wall.", x, y);
9613 
9614 		glbCurLevel->setTile(x, y, SQUARE_BROKENVIEWPORT);
9615 
9616 		// Ends the bolt
9617 		return false;
9618 	    }
9619 
9620 	    case SQUARE_FOREST:
9621 	    case SQUARE_FORESTFIRE:
9622 	    {
9623 		buf.sprintf("The force bolt shatters the %s.",
9624 			    (tile == SQUARE_FOREST ? "tree" : "burning tree"));
9625 		glbCurLevel->reportMessage(buf, x, y);
9626 		glbCurLevel->setTile(x, y, SQUARE_CORRIDOR);
9627 		// People should fall out of trees.
9628 		glbCurLevel->dropMobs(x, y, true, mobref->getMob(), true);
9629 
9630 		int		dx, dy;
9631 		FORALL_8DIR(dx, dy)
9632 		{
9633 		    ITEM	*arrow;
9634 
9635 		    if (tile == SQUARE_FORESTFIRE)
9636 			arrow = ITEM::create(ITEM_FIREARROW);
9637 		    else
9638 			arrow = ITEM::create(ITEM_ARROW);
9639 		    // We only want one arrow at a time.
9640 		    arrow->setStackCount(1);
9641 		    // Throw the arrow...
9642 		    glbCurLevel->throwItem(x, y, dx, dy,
9643 				arrow, 0, 0, mobref->getMob(),
9644 				arrow->getThrownAttack(),
9645 				false);
9646 		}
9647 
9648 		// Consumes the bolt.
9649 		return false;
9650 	    }
9651 
9652 	    default:
9653 		break;
9654 	}
9655 
9656 	ITEM		*item;
9657 
9658 	item = glbCurLevel->getItem(x, y);
9659 	if (item && item->getDefinition() == ITEM_BOULDER)
9660 	{
9661 	    ITEM	*rocks;
9662 
9663 	    item->formatAndReport("The force bolt shatters %U.");
9664 	    // Destroy the item.
9665 	    glbCurLevel->dropItem(item);
9666 	    delete item;
9667 
9668 	    // Create some rocks.
9669 	    rocks = ITEM::create(ITEM_ROCK, false, false);
9670 	    rocks->setStackCount(rand_range(3,8));
9671 	    glbCurLevel->acquireItem(rocks, x, y, thismob);
9672 
9673 	    return false;
9674 	}
9675 
9676 	if (item && item->getDefinition() == ITEM_STATUE)
9677 	{
9678 	    MOB		*victim;
9679 	    ITEM	*rocks;
9680 
9681 	    item->formatAndReport("The force bolt shatters %U.");
9682 
9683 	    // Create some rocks.
9684 	    rocks = ITEM::create(ITEM_ROCK, false, false);
9685 	    rocks->setStackCount(rand_range(2,4));
9686 	    glbCurLevel->acquireItem(rocks, x, y, thismob);
9687 
9688 	    victim = item->getStatueMob();
9689 
9690 	    if (victim)
9691 	    {
9692 		// The victim finally dies.
9693 		victim->triggerAsDeath(ATTACK_NONE, thismob, 0, false);
9694 
9695 		// You get the experience for killing this dude!
9696 		if (!thismob->hasCommonMaster(victim))
9697 		    thismob->receiveDeathExp(victim->getExpLevel());
9698 
9699 		// Good old piety.
9700 		thismob->pietyKill(victim, ATTACKSTYLE_SPELL);
9701 
9702 		// Drop the victim's stuff on the ground.
9703 		ITEM		*vit, *next, *drop;
9704 
9705 		for (vit = victim->myInventory; vit; vit = next)
9706 		{
9707 		    next = vit->getNext();
9708 
9709 		    drop = victim->dropItem(vit->getX(), vit->getY());
9710 		    UT_ASSERT(drop == vit);
9711 
9712 		    glbCurLevel->acquireItem(vit, x, y, thismob);
9713 		}
9714 
9715 		// Note victim will be deleted when the item is.
9716 	    }
9717 
9718 	    // Delete the item (and any mob inside of it)
9719 	    glbCurLevel->dropItem(item);
9720 	    delete item;
9721 	}
9722     }
9723 
9724     // Continue the zap...
9725     return keepgoing;
9726 }
9727 
9728 bool
attemptLifeSaving(const char * deathstring)9729 MOB::attemptLifeSaving(const char *deathstring)
9730 {
9731     if (hasIntrinsic(INTRINSIC_LIFESAVE))
9732     {
9733 	if (getMobType() == MOBTYPE_UNDEAD)
9734 	{
9735 	    // Life saving doesn't work on the dead.  They have
9736 	    // no life to save...
9737 	    formatAndReport("%U <realize> too late that being undead means having no life to save.");
9738 	    return false;
9739 	}
9740 
9741 	// This is either a built in intrinsic (in which case
9742 	// we now lose it) or its from an item (in which case
9743 	// it will glow and disappear)
9744 	if (myIntrinsic.hasIntrinsic(INTRINSIC_LIFESAVE))
9745 	{
9746 	    // It is a built in intrinsic.
9747 	    formatAndReport(deathstring);
9748 	    clearIntrinsic(INTRINSIC_LIFESAVE);
9749 	    formatAndReport("But %U <return> to life!");
9750 	    myHP = myMaxHP;
9751 
9752 	    clearDeathIntrinsics(false);
9753 
9754 	    return true;
9755 	}
9756 
9757 	ITEM		*lifesaver, *drop;
9758 
9759 	lifesaver = getSourceOfIntrinsic(INTRINSIC_LIFESAVE);
9760 
9761 	if (lifesaver)
9762 	{
9763 	    // The life saver is what saved our life!
9764 	    formatAndReport(deathstring);
9765 
9766 	    formatAndReport("%R %Iu <I:glow> brightly and crumbles to dust!",
9767 			    lifesaver);
9768 
9769 	    // If the heart grants life save it could get rather
9770 	    // embarassing.  Since we can't destroy the heart, we need
9771 	    // to not rescue the carrier.
9772 	    if (lifesaver->defn().isquest)
9773 	    {
9774 		formatAndReport("The dust reassembles itself into %Iu!",
9775 				lifesaver);
9776 		formatAndReport("%U <do> not return to life.");
9777 		return false;
9778 	    }
9779 	    formatAndReport("%U <return> to life!");
9780 
9781 	    // If it was our amulet, or we saw the creature regenerate,
9782 	    // we will get the id of the item.
9783 	    if (isAvatar() ||
9784 		(MOB::getAvatar() && MOB::getAvatar()->canSense(this)))
9785 	    {
9786 		// We only id type, not the particulars!
9787 		lifesaver->markClassKnown();
9788 	    }
9789 
9790 	    drop = dropItem(lifesaver->getX(), lifesaver->getY());
9791 	    UT_ASSERT(drop == lifesaver);
9792 	    delete lifesaver;
9793 	    setMaximalHP();
9794 	    clearDeathIntrinsics(false);
9795 
9796 	    return true;
9797 	}
9798 	// This might occur if rebuildWornIntrinsic
9799 	// doesn't match getSourceOfIntrinsic
9800 	UT_ASSERT(!"UNKNOWN SOURCE OF LIFE SAVING");
9801 	// Cute message for this impossible condition.
9802 	formatAndReport("%U <attempt> to cheat death - but <fail>!");
9803     }
9804     else if (hasIntrinsic(INTRINSIC_LICHFORM))
9805     {
9806 	if (getMobType() == MOBTYPE_UNDEAD)
9807 	{
9808 	    // The undead can't make use of the imbued evil.
9809 	    formatAndReport("%U <feel> %r concentrated evil dissipate uselessly.");
9810 	    return false;
9811 	}
9812 
9813 	// Transformation forbidden if unchanging.
9814 	if (hasIntrinsic(INTRINSIC_UNCHANGING))
9815 	{
9816 	    formatAndReport("%U <shudder> as the evil dissipates.");
9817 	    return false;
9818 	}
9819 
9820 	// This is either a built in intrinsic (in which case
9821 	// we now lose it) or its from an item (in which case
9822 	// it will glow and disappear)
9823 	if (myIntrinsic.hasIntrinsic(INTRINSIC_LICHFORM))
9824 	{
9825 	    // It is a built in intrinsic.
9826 	    formatAndReport(deathstring);
9827 	    clearIntrinsic(INTRINSIC_LICHFORM);
9828 	    // "The r in intervene is in memory of Zamkral the giant spider,
9829 	    //  who reincarnated himself as a lich when he was killed on
9830 	    //  floor 7 of the dungeon." - Oohara Yuuma
9831 	    formatAndReport("But evil forces intervene!  %U <transform> into a lich!");
9832 
9833 	    // Set your id to Lich.
9834 	    myDefinition = MOB_LICH;
9835 
9836 	    // Reacquire full HP
9837 	    setMaximalHP();
9838 
9839 	    // Gain all of the Lich intrinsics...
9840 	    mergeIntrinsicsFromString(glb_mobdefs[MOB_LICH].intrinsic);
9841 
9842 	    clearDeathIntrinsics(false);
9843 
9844 	    // We become more powerful!
9845 	    gainLevel();
9846 
9847 	    return true;
9848 	}
9849 
9850 	// For now, we disallow items that grant lichform.  This is supposed
9851 	// to require a proper suicide, after all.
9852 
9853 	UT_ASSERT(!"UNKNOWN SOURCE OF LICH FORM");
9854 	// Cute message for this impossible condition.
9855 	formatAndReport("%U <attempt> to transform into a lich - but <fail>!");
9856     }
9857 
9858     return false;
9859 }
9860 
9861 void
triggerAsDeath(ATTACK_NAMES attack,MOB * src,ITEM * weapon,bool becameitem)9862 MOB::triggerAsDeath(ATTACK_NAMES attack, MOB *src, ITEM *weapon, bool becameitem)
9863 {
9864     triggerAsDeath(&glb_attackdefs[attack], src, weapon, becameitem);
9865 }
9866 
9867 void
triggerAsDeath(const ATTACK_DEF * attack,MOB * src,ITEM * weapon,bool becameitem)9868 MOB::triggerAsDeath(const ATTACK_DEF *attack, MOB *src, ITEM *weapon,
9869 		    bool becameitem)
9870 {
9871     // If a familiar dies, the master is punished.
9872     // This only occurs when the creature fully decays, so if it is
9873     // just transformed into an item. skip.
9874     if (hasIntrinsic(INTRINSIC_FAMILIAR) &&
9875 	!becameitem)
9876     {
9877 	MOB		*master;
9878 	master = getMaster();
9879 	if (master)
9880 	{
9881 	    master->systemshock();
9882 
9883 	    master->formatAndReport("%U <feel> a great loss.");
9884 	}
9885     }
9886 
9887     // Increment the killcount.
9888     // We do not want to do this if a it was a turn to stone.
9889     // Except....  We do could kill/res loops, so we should count
9890     // stone/unstone loops here.
9891     // No credit to you for that!
9892     // Also no credit for killing yourself.
9893     if (!isAvatar())
9894 	if (glbKillCount[getDefinition()] < 65530)
9895 	    glbKillCount[getDefinition()]++;
9896 
9897     if (isAvatar())
9898     {
9899 	// Losing screen.
9900 	if (!glbStressTest)
9901 	{
9902 	    glbVictoryScreen(false, attack, src, weapon);
9903 	}
9904 
9905 	// Mark avatar dead.
9906 	setAvatar(0);
9907     }
9908 
9909     // Check if it is Baezlebub!  Hurrah, good on you, warrior of
9910     // light!
9911     if (myDefinition == MOB_BAEZLBUB)
9912     {
9913 	msg_report("Baezl'bub has died!  Bring his black heart to "
9914 		    "the surface world!  ");
9915 	// Now you do not get victory until retiring to the surface
9916 	// world.
9917 	//glbVictoryScreen(true, attack, src, weapon);
9918     }
9919 }
9920