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