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: item.cpp ( POWDER Library, C++ )
11 *
12 * COMMENTS:
13 * Implementation of the item manipulation routines
14 */
15
16 #include <mygba.h>
17 #include <ctype.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include "assert.h"
21 #include "artifact.h"
22 #include "item.h"
23 #include "itemstack.h"
24 #include "creature.h"
25 #include "gfxengine.h"
26 #include "msg.h"
27 #include "map.h"
28 #include "sramstream.h"
29 #include "mobref.h"
30 #include "victory.h"
31 #include "piety.h"
32 #include "encyc_support.h"
33
34 //
35 // ITEM defintions
36 //
37
38 // Global data:
39
40 // This is a list of what true magic item each mundane item will correspond
41 // to. What type it refers to varies depending on the item's type, ie:
42 // potions will refer to POTION_.
43 u8 glb_magicitem[NUM_ITEMS];
44 // This tracks if each given item type has been ided. This is different from
45 // a specic item has been ided. A generically ided item will properly
46 // index its glb_magicitem class to give its type (acid potion rather than
47 // purple potion), a specific id will do this and its own state (bless vs curse)
48 bool glb_itemid[NUM_ITEMS];
49
50 // This is the overloaded name someone has given this dude.
51 // It has been strduped so should be freed.
52 char *glb_itemnames[NUM_ITEMS];
53
54 int glbItemCount = 0;
55
56 // This is for the callback to track who did the zapping.
57 MOBREF ITEM::ourZapper;
58
59 void
item_init()60 item_init()
61 {
62 memset(glb_itemnames, 0, sizeof(char *) * NUM_ITEMS);
63 }
64
ITEM()65 ITEM::ITEM()
66 {
67 myNext = 0;
68 myName.setName(0);
69 glbItemCount++;
70 }
71
~ITEM()72 ITEM::~ITEM()
73 {
74 MOB *mob;
75
76 mob = myCorpseMob.getMob();
77
78 if (mob)
79 {
80 // This MUST be safe, as we own it!
81 // We want to punish the owner if we are a familiar.
82 // This will catch the case of corpses decaying...
83 // (Or being eaten!)
84 // One problem is I think double jeapordy for smashing a statue
85 // of your familiar. But such evil scum that do that deserve
86 // to be punished.
87 if (mob->hasIntrinsic(INTRINSIC_FAMILIAR))
88 {
89 MOB *master;
90 master = mob->getMaster();
91 if (master)
92 {
93 master->systemshock();
94
95 master->formatAndReport("%U <feel> a great loss.");
96 }
97 }
98
99 delete mob;
100 }
101
102 glbItemCount--;
103 }
104
105 void
item_randomizetype(MAGICTYPE_NAMES magictype,int num)106 item_randomizetype(MAGICTYPE_NAMES magictype, int num)
107 {
108 int i, j;
109 u8 *lut;
110
111 lut = new u8[num];
112 for (i = 0; i < num; i++)
113 {
114 lut[i] = i;
115 }
116 // Now, mix randomly...
117 rand_shuffle(lut, num);
118
119 // Now, run through our items and assign the relevant type for each
120 // potion, asserting we have enough!
121 j = 0;
122 for (i = 0; i < NUM_ITEMS; i++)
123 {
124 if (glb_itemdefs[i].magictype == magictype)
125 {
126 glb_magicitem[i] = lut[j++];
127 UT_ASSERT(j <= num);
128 }
129 }
130 delete [] lut;
131
132 // Verify we gave away each item exactly once.
133 UT_ASSERT(j == num);
134 }
135
136 void
init()137 ITEM::init()
138 {
139 int i;
140
141 // Clear the item names.
142 for (i = 0; i < NUM_ITEMS; i++)
143 {
144 if (glb_itemnames[i])
145 free(glb_itemnames[i]);
146 glb_itemnames[i] = 0;
147 }
148
149 // Build all the magic lookup tables...
150 memset(glb_magicitem, 0, NUM_ITEMS);
151
152 // Clear out the id flags.
153 memset(glb_itemid, 0, NUM_ITEMS);
154
155 // Now, for each of the special types, enumerate them...
156 item_randomizetype(MAGICTYPE_POTION, NUM_POTIONS);
157 item_randomizetype(MAGICTYPE_SCROLL, NUM_SCROLLS);
158 item_randomizetype(MAGICTYPE_RING, NUM_RINGS);
159 item_randomizetype(MAGICTYPE_HELM, NUM_HELMS);
160 item_randomizetype(MAGICTYPE_BOOTS, NUM_BOOTSS);
161 item_randomizetype(MAGICTYPE_AMULET, NUM_AMULETS);
162 item_randomizetype(MAGICTYPE_WAND, NUM_WANDS);
163 item_randomizetype(MAGICTYPE_SPELLBOOK, NUM_SPELLBOOKS);
164 item_randomizetype(MAGICTYPE_STAFF, NUM_STAFFS);
165 }
166
167 ITEM *
createClone() const168 ITEM::createClone() const
169 {
170 ITEM *clone;
171
172 clone = ITEM::create((ITEM_NAMES) myDefinition);
173
174 clone->myStackCount = myStackCount;
175 clone->myX = myX;
176 clone->myY = myY;
177 clone->myName = myName;
178 clone->myEnchantment = myEnchantment;
179 clone->myCharges = myCharges;
180 clone->myPoison = myPoison;
181 clone->myPoisonCharges = myPoisonCharges;
182 clone->myFlag1 = myFlag1;
183 clone->myCorpseMob.setMob(0);
184
185 return clone;
186 }
187
188 bool
petrify(MAP * map,MOB * petrifier)189 ITEM::petrify(MAP *map, MOB *petrifier)
190 {
191 // Mounds of flesh turn into boulders.
192 // Things made of flesh become stones.
193
194 if (myDefinition == ITEM_MOUNDFLESH)
195 {
196 myDefinition = ITEM_BOULDER;
197 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 | ITEMFLAG1_ISBLOCKING);
198 formatAndReport("The mound of flesh hardens into a boulder.");
199 return true;
200 }
201
202 if (getMaterial() == MATERIAL_FLESH)
203 {
204
205 if (defn().isquest)
206 {
207 // No petrification of quest items!
208 formatAndReport("%U <resist> the petrification!");
209 return false;
210 }
211
212 // This is made of flesh. Should turn into a rock.
213 formatAndReport("%U <harden> into a rock.");
214
215 myDefinition = ITEM_ROCK;
216 return true;
217 }
218
219 return false;
220 }
221
222 bool
unpetrify(MAP * map,MOB * unpetrifier)223 ITEM::unpetrify(MAP *map, MOB *unpetrifier)
224 {
225 if (myDefinition == ITEM_STATUE)
226 {
227 // This is at least possible
228 MOB *mob;
229
230 mob = myCorpseMob.getMob();
231
232 if (!mob)
233 {
234 // Mobless statue.
235 formatAndReport("%U, being but a work of art, <collapse> into a meatball.");
236 myDefinition = ITEM_MEATBALL;
237 return true;
238 }
239
240 // Bring back to life
241 mob->clearIntrinsic(INTRINSIC_DEAD);
242 // Register on the map.
243 mob->move(getX(), getY(), true);
244 map->registerMob(mob);
245
246 // We may have somehow been looted in the meantime.
247 mob->rebuildAppearance();
248 mob->rebuildWornIntrinsic();
249
250 mob->formatAndReport("The statue of %U comes to life!");
251
252 // Important so we don't delete ourselves :>
253 myCorpseMob.setMob(0);
254 map->dropItem(this);
255 delete this;
256
257 return true;
258 }
259
260 if (myDefinition == ITEM_BOULDER)
261 {
262 // Becomes a mound of flesh.
263 formatAndReport("%U <collapse> into a mound of flesh.");
264 myDefinition = ITEM_MOUNDFLESH;
265 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 & ~ITEMFLAG1_ISBLOCKING);
266
267 return true;
268 }
269
270 if (getMaterial() == MATERIAL_STONE)
271 {
272 if (defn().isquest)
273 {
274 // No petrification of quest items!
275 formatAndReport("%U <remain> stony!");
276 return false;
277 }
278
279 // Transforms into a meatball.
280 formatAndReport("%U <collapse>...");
281 myDefinition = ITEM_MEATBALL;
282 formatAndReport("into %U.");
283 return true;
284 }
285
286 return false;
287 }
288
289 ITEM *
polymorph(MOB * polyer)290 ITEM::polymorph(MOB *polyer)
291 {
292 // Flavour text...
293 formatAndReport(polyer, "A shimmering cloud engulfs %U.");
294
295 // Check for poly-immune items.
296 if (defn().isquest || hasIntrinsic(INTRINSIC_UNCHANGING) || isArtifact())
297 {
298 formatAndReport("%U <shudder>.");
299 return 0;
300 }
301
302 // Create a new item of the same type.
303 ITEMTYPE_NAMES itemtype;
304
305 itemtype = getItemType();
306
307 // Note artifacts can never arise from polying.
308 ITEM *newitem;
309
310 newitem = ITEM::createRandomType(itemtype, false);
311
312 newitem->formatAndReport(polyer, "The cloud dissipates revealing %U.");
313
314 return newitem;
315 }
316
317 bool
resurrect(MAP * map,MOB * resser,int x,int y)318 ITEM::resurrect(MAP *map, MOB *resser, int x, int y)
319 {
320 if (myDefinition == ITEM_BONES || myDefinition == ITEM_CORPSE)
321 {
322 // This is at least possible
323 MOB *mob;
324
325 mob = myCorpseMob.getMob();
326
327 if (!mob)
328 {
329 // This likely should be an assert: Why does a corpse/bones
330 // lack a mob?
331 resser->reportMessage("The spirit has travelled too far.");
332
333 return false;
334 }
335
336 // See if we can find room in which to resurrect the mob.
337 // The closest valid square is used.
338 // We or in STD_SWIM here as we want creatures brought back
339 // inside water to stay in the water.
340 if (!map->findCloseTile(x, y, (MOVE_NAMES) (mob->getMoveType() | MOVE_STD_SWIM)))
341 {
342 formatAndReport("%U <twitch> feebily.");
343 return false;
344 }
345
346 // Bring back to the verge of death.
347 mob->setMinimalHP();
348 if (mob->canResurrectFromCorpse())
349 {
350 // Trolls get full hit points.
351 mob->setMaximalHP();
352 }
353 mob->clearIntrinsic(INTRINSIC_DEAD);
354
355 // Register on the map.
356 mob->move(x, y, true);
357 map->registerMob(mob);
358
359 // The mob may have been looted on death. Thus, we rebuild
360 // it's worn intrinisics.
361 mob->rebuildWornIntrinsic();
362 mob->rebuildAppearance();
363
364 mob->formatAndReport("%U <return> from the dead!");
365
366 // After being resurrected, one is, of course, thankful.
367 mob->makeSlaveOf(resser);
368
369 // Important so we don't delete ourselves :>
370 myCorpseMob.setMob(0);
371
372 // Drop back into whatever liquid we were in. This is a bit
373 // mean as pets that die in pits will insta die on resurrecting
374 // as they will fall back into the pit. However, you can easily
375 // fix this by carrying the corpse out.
376 map->dropMobs(mob->getX(), mob->getY(), true, resser);
377
378 return true;
379 }
380 return false;
381 }
382
383 bool
raiseZombie(MAP * map,MOB * raiser)384 ITEM::raiseZombie(MAP *map, MOB *raiser)
385 {
386 if (myDefinition == ITEM_CORPSE)
387 {
388 // This is at least possible
389 MOB *mob, *zombie;
390
391 // It doesn't matter if the mob is there, it just allows
392 // us to get extra hits.
393 mob = myCorpseMob.getMob();
394
395 // Note we don't clear out myCorpseMob which means that
396 // if the mob is a familiar we will be punished by zombifying
397 // it.
398 // On reflection, I think this is appropriate.
399
400 zombie = MOB::create(MOB_ZOMBIE);
401 zombie->destroyInventory();
402 if (mob)
403 {
404 zombie->forceHP(mob->getMaxHP());
405 zombie->setOrigDefinition(mob->getDefinition());
406 }
407
408 // Register on the map.
409 zombie->move(getX(), getY(), true);
410 map->registerMob(zombie);
411
412 zombie->formatAndReport("%U <rise> from the dead!");
413
414 // The zombie is marked as tame...
415 zombie->makeSlaveOf(raiser);
416
417 return true;
418 }
419 return false;
420 }
421
422 bool
raiseSkeleton(MAP * map,MOB * raiser)423 ITEM::raiseSkeleton(MAP *map, MOB *raiser)
424 {
425 if (myDefinition == ITEM_BONES)
426 {
427 // This is at least possible
428 MOB *mob, *skeleton;
429
430 // It doesn't matter if the mob is there, it just allows
431 // us to get extra hits.
432 mob = myCorpseMob.getMob();
433
434 skeleton = MOB::create(MOB_SKELETON);
435 skeleton->destroyInventory();
436 if (mob)
437 {
438 skeleton->forceHP(mob->getMaxHP());
439 skeleton->setOrigDefinition(mob->getDefinition());
440 }
441
442 // Register on the map.
443 skeleton->move(getX(), getY(), true);
444 map->registerMob(skeleton);
445
446 skeleton->formatAndReport("%U <rise> from the dead!");
447
448 // The skeleton is marked as tame...
449 skeleton->makeSlaveOf(raiser);
450
451 return true;
452 }
453 return false;
454 }
455
456 bool
doBreak(int dc,MOB * breaker,MOB * owner,MAP * map,int x,int y)457 ITEM::doBreak(int dc, MOB *breaker, MOB *owner, MAP *map, int x, int y)
458 {
459 bool shouldbreak = false;
460 bool deleteself = false;
461
462 // Determine if this DC is enough to break the item...
463
464 // For now, potions break...
465 if (getItemType() == ITEMTYPE_POTION)
466 {
467 shouldbreak = true;
468 }
469
470 if (getDefinition() == ITEM_BOTTLE)
471 shouldbreak = true;
472
473 if (getDefinition() == ITEM_MINDACIDHAND)
474 {
475 deleteself = true;
476 shouldbreak = true;
477 }
478
479 // TODO: Build break chance into item properties,
480 // can have Glass Swords then.
481 if (getDefinition() == ITEM_ARROW || getDefinition() == ITEM_FIREARROW)
482 {
483 // Blessed arrows much less likely to break than cursed.
484 int chance;
485
486 chance = 10;
487 if (isCursed())
488 chance = 50;
489 if (isBlessed())
490 chance = 1;
491
492 // A bit more likely to break...
493 if (getDefinition() == ITEM_FIREARROW)
494 chance *= 2;
495
496 // If the breaker is a ranger, we don't break!
497 if (breaker && breaker->hasIntrinsic(INTRINSIC_DRESSED_RANGER))
498 chance = 0;
499
500 if (rand_chance(chance))
501 {
502 deleteself = true;
503 shouldbreak = true;
504 }
505 }
506
507 // Exit early if there is no breakage.
508 if (!shouldbreak)
509 return false;
510
511 // Stash this before piety as piety may kill us.
512 ourZapper.setMob(breaker);
513
514 // Provide the breaker with piety...
515 if (breaker)
516 breaker->pietyBreak(this);
517
518 // Switch on type & hence effect.
519 switch (getItemType())
520 {
521 case ITEMTYPE_MISC:
522 {
523 if (getDefinition() == ITEM_BOTTLE)
524 {
525 // Empty bottles grenade...
526 // FALL THROUGH
527 }
528 else
529 break;
530 }
531
532 case ITEMTYPE_POTION:
533 {
534 // Potions are always fully destroyed
535 deleteself = true;
536
537 // Give the appropriate dialog...
538 formatAndReport("%U <shatter>.");
539
540 if (map)
541 {
542 map->fireBall(x, y, 1 + isBlessed() - isCursed(), true,
543 grenadeCallbackStatic,
544 this);
545 }
546
547 break;
548 }
549
550 case ITEMTYPE_AMULET:
551 case ITEMTYPE_WEAPON:
552 case ITEMTYPE_ARMOUR:
553 case ITEMTYPE_RING:
554 case ITEMTYPE_SCROLL:
555 case ITEMTYPE_SPELLBOOK:
556 case ITEMTYPE_WAND:
557 case ITEMTYPE_STAFF:
558 case NUM_ITEMTYPES:
559 case ITEMTYPE_NONE:
560 case ITEMTYPE_ANY:
561 case ITEMTYPE_ARTIFACT:
562 break;
563 }
564
565
566 // Destroy our self if required.
567 if (deleteself)
568 {
569 // If we are stacked, we can escape actually breaking...
570 if (getStackCount() > 1)
571 {
572 ITEM *top;
573
574 // This is very round about. The theory goes we may
575 // do something zany in splitStack or delete...
576 top = splitStack();
577 delete top;
578
579 // Well, it broke, but the ITEM * is still valid...
580 return false;
581 }
582
583 if (owner)
584 {
585 owner->dropItem(getX(), getY());
586 owner->rebuildAppearance();
587 owner->rebuildWornIntrinsic();
588 }
589 delete this;
590 }
591
592 // Breakage occurred!
593 return true;
594 }
595
596 void
renameType(const char * newname)597 ITEM::renameType(const char *newname)
598 {
599 if (glb_itemnames[myDefinition])
600 free(glb_itemnames[myDefinition]);
601 if (newname && newname[0])
602 {
603 glb_itemnames[myDefinition] = strdup(newname);
604 }
605 else
606 {
607 glb_itemnames[myDefinition] = 0;
608 }
609 }
610
611 void
rename(const char * newname)612 ITEM::rename(const char *newname)
613 {
614 myName.setName(newname);
615 }
616
617 void
rename(BUF buf)618 ITEM::rename(BUF buf)
619 {
620 rename(buf.buffer());
621 }
622
623 const char *
getPersonalName() const624 ITEM::getPersonalName() const
625 {
626 return myName.getName();
627 }
628
629 ITEM *
create(ITEM_NAMES definition,bool allowartifact,bool nocurse)630 ITEM::create(ITEM_NAMES definition, bool allowartifact, bool nocurse)
631 {
632 ITEM *item;
633
634 item = new ITEM();
635
636 item->myDefinition = definition;
637 item->myStackCount = rand_dice(glb_itemdefs[item->myDefinition].stacksize);
638 item->myX = item->myY = 0;
639 item->myFlag1 = (ITEMFLAG1_NAMES)glb_itemdefs[item->myDefinition].flag1;
640 item->myNext = 0;
641 item->myEnchantment = 0;
642 item->myCharges = 0;
643
644 item->myPoison = POISON_NONE;
645 item->myPoisonCharges = 0;
646
647 // Determine cursed state...
648 CURSECHANCE_NAMES cursechance;
649
650 cursechance = (CURSECHANCE_NAMES)
651 glb_itemdefs[item->myDefinition].cursechance;
652 // Do indirection on special magic types...
653 switch ((MAGICTYPE_NAMES) glb_itemdefs[item->myDefinition].magictype)
654 {
655 case MAGICTYPE_POTION:
656 cursechance = (CURSECHANCE_NAMES) glb_potiondefs[glb_magicitem[item->myDefinition]].cursechance;
657 break;
658 case MAGICTYPE_SCROLL:
659 cursechance = (CURSECHANCE_NAMES) glb_scrolldefs[glb_magicitem[item->myDefinition]].cursechance;
660 break;
661 case MAGICTYPE_RING:
662 cursechance = (CURSECHANCE_NAMES) glb_ringdefs[glb_magicitem[item->myDefinition]].cursechance;
663 break;
664 case MAGICTYPE_HELM:
665 cursechance = (CURSECHANCE_NAMES) glb_helmdefs[glb_magicitem[item->myDefinition]].cursechance;
666 break;
667 case MAGICTYPE_BOOTS:
668 cursechance = (CURSECHANCE_NAMES) glb_bootsdefs[glb_magicitem[item->myDefinition]].cursechance;
669 break;
670 case MAGICTYPE_AMULET:
671 cursechance = (CURSECHANCE_NAMES) glb_amuletdefs[glb_magicitem[item->myDefinition]].cursechance;
672 break;
673 case MAGICTYPE_WAND:
674 cursechance = (CURSECHANCE_NAMES) glb_wanddefs[glb_magicitem[item->myDefinition]].cursechance;
675 break;
676 case MAGICTYPE_SPELLBOOK:
677 cursechance = (CURSECHANCE_NAMES) glb_spellbookdefs[glb_magicitem[item->myDefinition]].cursechance;
678 break;
679 case MAGICTYPE_STAFF:
680 cursechance = (CURSECHANCE_NAMES) glb_staffdefs[glb_magicitem[item->myDefinition]].cursechance;
681 break;
682 case MAGICTYPE_NONE:
683 break;
684
685 case NUM_MAGICTYPES:
686 UT_ASSERT(!"Unknown magic type!");
687 break;
688 }
689
690 // Probabilities of each.
691 int curse, normal, bless;
692
693 curse = glb_cursechancedefs[cursechance].curse;
694 normal = glb_cursechancedefs[cursechance].normal;
695 bless = glb_cursechancedefs[cursechance].bless;
696
697 UT_ASSERT(curse + normal + bless == 100);
698 int percent;
699 percent = rand_choice(100);
700 if (!nocurse)
701 {
702 if (percent < curse)
703 item->myFlag1 = (ITEMFLAG1_NAMES) (item->myFlag1 | ITEMFLAG1_ISCURSED);
704 else if (percent < curse + bless)
705 item->myFlag1 = (ITEMFLAG1_NAMES) (item->myFlag1 | ITEMFLAG1_ISBLESSED);
706 }
707
708 // Determine enchantment. Only certain types of items will
709 // have a default enchantment.
710 // You can enchant anything, however.
711 if (item->isBoots() || item->isHelmet() || item->isShield() ||
712 item->isJacket() || item->isWeapon())
713 {
714 // Armour enchant or weapon
715 // We have a 10% chance of +/-, 50-50 for each.
716 // This gives 1% for +2 or -2, 18% chance for +1 and 18% chance for -1.
717 // Bah, not very good. This makes 40% of items enchanted...
718 // Thus, we hard code:
719 // 1% +2
720 // 9% +1
721 // 9% -1
722 // 1% -2
723 // This is also silly, as we get limitted to +/-2.
724 // Instead, just use a power distribution and have 10% less chance
725 // for every bonus.
726 // The major flaw with this is that we go from a 20% enchanted
727 // to a 10% enchanted. Thus, we encompsass the first test
728 // with a 20% check.
729
730 // Randomly determine if we will increase or decrease the enchantment.
731 int enchantdir;
732 if (rand_choice(2))
733 enchantdir = 1;
734 else
735 enchantdir = -1;
736
737 item->myEnchantment = 0;
738
739 if (rand_chance(20))
740 {
741 item->enchant(enchantdir);
742
743 // Each additional level of enchantment has 10% chance of going
744 // through.
745 // This loop will terminate. I hope.
746 // Call me paranoid...
747 int maxenchant = 50;
748 while (rand_chance(10))
749 {
750 item->enchant(enchantdir);
751 maxenchant--;
752 if (!maxenchant)
753 {
754 UT_ASSERT(!"Excessive enchantment!");
755 break;
756 }
757 }
758 }
759
760 // If the enchantment was negative, we get an extra 30% chance
761 // of it being (more) cursed.
762 if (item->myEnchantment < 0 && rand_chance(30))
763 item->makeCursed();
764 // Otherwise, if it is positive, there is a chance for blessed.
765 if (item->myEnchantment > 0 && rand_chance(30))
766 item->makeBlessed();
767 }
768
769 // Add in charges...
770 if (item->getMagicType() == MAGICTYPE_WAND)
771 {
772 item->myCharges = rand_dice(glb_wanddefs[item->getMagicClass()].charges);
773 }
774
775 if (item->getMagicType() == MAGICTYPE_SPELLBOOK)
776 {
777 item->myCharges = rand_dice(glb_spellbookdefs[item->getMagicClass()].charges);
778 }
779
780 // One in a hundred mundanes is a real artifact! Woot!
781 if (allowartifact && rand_chance(1))
782 item->makeArtifact();
783
784 return item;
785 }
786
787 ITEM_NAMES
lookupMagicItem(MAGICTYPE_NAMES magictype,int magicclass)788 ITEM::lookupMagicItem(MAGICTYPE_NAMES magictype, int magicclass)
789 {
790 int itemtype;
791
792 for (itemtype = 0; itemtype < NUM_ITEMS; itemtype++)
793 {
794 if (glb_itemdefs[itemtype].magictype == magictype)
795 {
796 if (glb_magicitem[itemtype] == magicclass)
797 {
798 return (ITEM_NAMES) itemtype;
799 }
800 }
801 }
802
803 return ITEM_NONE;
804 }
805
806 ITEM *
createMagicItem(MAGICTYPE_NAMES magictype,int magicclass,bool allowartifact,bool nocurse)807 ITEM::createMagicItem(MAGICTYPE_NAMES magictype, int magicclass,
808 bool allowartifact,
809 bool nocurse)
810 {
811 ITEM_NAMES item;
812
813 item = lookupMagicItem(magictype, magicclass);
814 if (item != ITEM_NONE)
815 return ITEM::create(item, allowartifact, nocurse);
816
817 return 0;
818 }
819
820 ITEM *
createRandom(bool allowartifact)821 ITEM::createRandom(bool allowartifact)
822 {
823 int table[NUM_ITEMS];
824 int i, max;
825 int prob;
826
827 max = 0;
828 for (i = 0; i < NUM_ITEMS; i++)
829 {
830 if (glb_itemdefs[i].magictype == MAGICTYPE_SCROLL)
831 prob = glb_scrolldefs[glb_magicitem[i]].rarity;
832 else
833 prob = glb_itemdefs[i].rarity;
834
835 prob *= ((int)glb_itemtypedefs[glb_itemdefs[i].itemtype].rarity);
836 table[i] = prob;
837 max += prob;
838 }
839
840 return createRandomFromTable(table, max, allowartifact);
841 }
842
843 ITEM *
createRandomType(ITEMTYPE_NAMES type,bool allowartifact)844 ITEM::createRandomType(ITEMTYPE_NAMES type, bool allowartifact)
845 {
846 int table[NUM_ITEMS];
847 int i, max;
848 int prob;
849
850 // Special case item types.
851 if (type == ITEMTYPE_ANY)
852 return ITEM::createRandom(allowartifact);
853
854 if (type == ITEMTYPE_ARTIFACT)
855 {
856 ITEM *item;
857
858 item = ITEM::createRandom(false);
859 item->makeArtifact();
860 return item;
861 }
862
863 max = 0;
864 for (i = 0; i < NUM_ITEMS; i++)
865 {
866 if (glb_itemdefs[i].itemtype == type)
867 {
868 if (glb_itemdefs[i].magictype == MAGICTYPE_SCROLL)
869 prob = glb_scrolldefs[glb_magicitem[i]].rarity;
870 else
871 prob = glb_itemdefs[i].rarity;
872 }
873 else
874 prob = 0;
875
876 table[i] = prob;
877 max += prob;
878 }
879
880 return createRandomFromTable(table, max, allowartifact);
881 }
882
883 ITEM *
createRandomFromTable(int * table,int max_prob,bool allowartifact)884 ITEM::createRandomFromTable(int *table, int max_prob, bool allowartifact)
885 {
886 int item, i;
887
888 if (!max_prob)
889 {
890 UT_ASSERT(!"No such items.");
891 return 0;
892 }
893
894 item = rand_choice(max_prob);
895 for (i = 0; i < NUM_ITEMS; i++)
896 {
897 item -= table[i];
898 if (item < 0)
899 return create((ITEM_NAMES) i, allowartifact);
900 }
901
902 UT_ASSERT(!"Could not find item!");
903 return 0;
904 }
905
906 ITEM *
createCorpse(MOB * mob)907 ITEM::createCorpse(MOB *mob)
908 {
909 ITEM *item;
910
911 item = ITEM::create(ITEM_CORPSE, false);
912 item->myCorpseMob.setMob(mob);
913
914 item->myCharges = 20;
915
916 // Boneless critters get extra time as corpses as they have
917 // no skeleton state.
918 if (mob->isBoneless())
919 item->myCharges += 20;
920
921 return item;
922 }
923
924 ITEM *
createStatue(MOB * mob)925 ITEM::createStatue(MOB *mob)
926 {
927 ITEM *item;
928
929 item = ITEM::create(ITEM_STATUE, false);
930 item->myCorpseMob.setMob(mob);
931
932 return item;
933 }
934
935 void
setPos(int x,int y)936 ITEM::setPos(int x, int y)
937 {
938 myX = x;
939 myY = y;
940 }
941
942 void
enchant(int delta)943 ITEM::enchant(int delta)
944 {
945 int newval;
946
947 newval = myEnchantment + delta;
948
949 if (newval < -99)
950 newval = -99;
951 if (newval > 99)
952 newval = 99;
953
954 myEnchantment = newval;
955 }
956
957 void
makeArtifact(const char * name)958 ITEM::makeArtifact(const char *name)
959 {
960 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 | ITEMFLAG1_ISARTIFACT);
961
962 if (!name)
963 rename(artifact_buildname(getDefinition()));
964 else
965 rename(name);
966 }
967
968 void
makeVanilla()969 ITEM::makeVanilla()
970 {
971 // Ensure it isn't artifact.
972 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 & ~ITEMFLAG1_ISARTIFACT);
973 rename(0);
974 makePoisoned(POISON_NONE, 0);
975 myEnchantment = 0;
976 myCharges = 0;
977
978 // Ensure neither bless nor cursed.
979 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 & ~ITEMFLAG1_ISBLESSED);
980 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 & ~ITEMFLAG1_ISCURSED);
981 }
982
983 bool
doHeartbeat(MAP * map,MOB * owner)984 ITEM::doHeartbeat(MAP *map, MOB *owner)
985 {
986 // Determine if we are in a proper phase.
987 if (!speed_isheartbeatphase())
988 return true;
989
990 // Run mind acid decay...
991 if (myDefinition == ITEM_MINDACIDHAND)
992 {
993 if (myCharges)
994 myCharges--;
995 else
996 {
997 // Has fully decayed...
998 // The owner is confused.
999 if (owner)
1000 {
1001 owner->formatAndReport("Mind-acid soaks into %R skin.");
1002
1003 owner->setTimedIntrinsic(owner, INTRINSIC_CONFUSED,
1004 rand_dice(3, 5, 10));
1005 }
1006
1007 // Request deletion.
1008 return false;
1009 }
1010 }
1011
1012 // Run decay...
1013 if (myDefinition == ITEM_CORPSE ||
1014 myDefinition == ITEM_BONES)
1015 {
1016 // Check for the "preserved" bit...
1017 if (myCharges == 255)
1018 return true;
1019
1020 if (myCharges)
1021 myCharges--;
1022 else
1023 {
1024 // Has fully decayed...
1025 if (myDefinition == ITEM_CORPSE)
1026 {
1027 // We may want to resurrect ourselves.
1028 MOB *mob;
1029
1030 mob = myCorpseMob.getMob();
1031 if (mob)
1032 {
1033 if (mob->canResurrectFromCorpse())
1034 {
1035 // Trigger resurrection on self.
1036 if (owner)
1037 {
1038 if (resurrect(glbCurLevel, mob,
1039 owner->getX(), owner->getY()))
1040 {
1041 return false;
1042 }
1043 }
1044 else
1045 {
1046 if (resurrect(glbCurLevel, mob,
1047 getX(), getY()))
1048 {
1049 return false;
1050 }
1051 }
1052 }
1053
1054 if (mob->isBoneless())
1055 {
1056 // Don't create bones...
1057 return false;
1058 }
1059 }
1060
1061 myDefinition = ITEM_BONES;
1062 myCharges = 20;
1063 }
1064 else
1065 {
1066 // Erase.
1067 return false;
1068 }
1069 }
1070 }
1071
1072 return true;
1073 }
1074
1075 MATERIAL_NAMES
getMaterial() const1076 ITEM::getMaterial() const
1077 {
1078 // If we are a corpse, inherit from our base type.
1079 MOB *corpse;
1080
1081 corpse = getCorpseMob();
1082 if (corpse)
1083 {
1084 return corpse->getMaterial();
1085 }
1086 return (MATERIAL_NAMES) glb_itemdefs[myDefinition].material;
1087 }
1088
1089 ATTACK_NAMES
getAttackName() const1090 ITEM::getAttackName() const
1091 {
1092 return (ATTACK_NAMES) glb_itemdefs[myDefinition].attack;
1093 }
1094
1095 ATTACK_NAMES
getThrownAttackName() const1096 ITEM::getThrownAttackName() const
1097 {
1098 return (ATTACK_NAMES) glb_itemdefs[myDefinition].thrownattack;
1099 }
1100
1101 SKILL_NAMES
getAttackSkill() const1102 ITEM::getAttackSkill() const
1103 {
1104 return (SKILL_NAMES) glb_itemdefs[myDefinition].attackskill;
1105 }
1106
1107 SKILL_NAMES
getSpecialSkill() const1108 ITEM::getSpecialSkill() const
1109 {
1110 return (SKILL_NAMES) glb_itemdefs[myDefinition].specialskill;
1111 }
1112
1113 const ATTACK_DEF *
getAttack() const1114 ITEM::getAttack() const
1115 {
1116 const ARTIFACT *art;
1117
1118 art = getArtifact();
1119
1120 if (art && art->hasattack)
1121 {
1122 return &art->attack;
1123 }
1124
1125 return &glb_attackdefs[glb_itemdefs[myDefinition].attack];
1126 }
1127
1128 const ATTACK_DEF *
getThrownAttack() const1129 ITEM::getThrownAttack() const
1130 {
1131 const ARTIFACT *art;
1132
1133 art = getArtifact();
1134
1135 if (art && art->hasthrownattack)
1136 {
1137 return &art->thrownattack;
1138 }
1139
1140 return &glb_attackdefs[glb_itemdefs[myDefinition].thrownattack];
1141 }
1142
1143 bool
requiresLauncher() const1144 ITEM::requiresLauncher() const
1145 {
1146 if (glb_itemdefs[myDefinition].launcher == ITEM_NONE)
1147 return false;
1148
1149 return true;
1150 }
1151
1152 bool
canLaunchThis(ITEM * launcher) const1153 ITEM::canLaunchThis(ITEM *launcher) const
1154 {
1155 if (!launcher)
1156 return false;
1157
1158 if (launcher->getDefinition() == glb_itemdefs[myDefinition].launcher)
1159 return true;
1160
1161 return false;
1162 }
1163
1164 SIZE_NAMES
getSize() const1165 ITEM::getSize() const
1166 {
1167 return (SIZE_NAMES) glb_itemdefs[myDefinition].size;
1168 }
1169
1170 int
getNoiseLevel() const1171 ITEM::getNoiseLevel() const
1172 {
1173 return glb_itemdefs[myDefinition].noise;
1174 }
1175
1176 MOB *
getCorpseMob() const1177 ITEM::getCorpseMob() const
1178 {
1179 if (myDefinition != ITEM_CORPSE &&
1180 myDefinition != ITEM_BONES)
1181 return 0;
1182 return myCorpseMob.getMob();
1183 }
1184
1185 MOB *
getStatueMob() const1186 ITEM::getStatueMob() const
1187 {
1188 if (myDefinition != ITEM_STATUE)
1189 return 0;
1190
1191 return myCorpseMob.getMob();
1192 }
1193
1194 int
calcAverageDamage() const1195 ITEM::calcAverageDamage() const
1196 {
1197 ATTACK_NAMES same;
1198 int dam = 0;
1199 const ATTACK_DEF *attack, *sameattack;
1200
1201 attack = getAttack();
1202 while (attack)
1203 {
1204 sameattack = attack;
1205
1206 while (sameattack)
1207 {
1208 dam += (sameattack->damage.myNumdie *
1209 (1 + sameattack->damage.mySides)) / 2;
1210 dam += sameattack->damage.myBonus;
1211 dam += getEnchantment();
1212
1213 // Non physical attacks are always cooler.
1214 if (sameattack->element != ELEMENT_PHYSICAL)
1215 dam += 2;
1216
1217 same = (ATTACK_NAMES)sameattack->sameattack;
1218 if (same == ATTACK_NONE)
1219 sameattack = 0;
1220 else
1221 sameattack = &glb_attackdefs[same];
1222 }
1223 if (attack->nextattack == ATTACK_NONE)
1224 attack = 0;
1225 else
1226 attack = &glb_attackdefs[attack->nextattack];
1227 }
1228
1229 // Extra stuff: bonus for poisoned.
1230 if (isPoisoned())
1231 {
1232 dam += 4;
1233 }
1234
1235 // Being made of silver is just cool.
1236 if (getMaterial() == MATERIAL_SILVER)
1237 {
1238 dam += 4;
1239 }
1240 return dam;
1241 }
1242
1243 ITEMTYPE_NAMES
getItemType() const1244 ITEM::getItemType() const
1245 {
1246 return (ITEMTYPE_NAMES) glb_itemdefs[getDefinition()].itemtype;
1247 }
1248
1249 bool
isCarryIntrinsic() const1250 ITEM::isCarryIntrinsic() const
1251 {
1252 const ARTIFACT *art;
1253
1254 // Artifacts may have carry intrinsics...
1255 art = getArtifact();
1256 if (art && art->iscarryintrinsic)
1257 return true;
1258
1259 return glb_itemdefs[myDefinition].iscarryintrinsic;
1260 }
1261
1262 bool
isFullyIdentified() const1263 ITEM::isFullyIdentified() const
1264 {
1265 return isIdentified() && isKnownCursed() &&
1266 isKnownEnchant() && isKnownCharges() &&
1267 isKnownPoison();
1268 }
1269
1270 bool
isIdentified() const1271 ITEM::isIdentified() const
1272 {
1273 return glb_itemid[myDefinition];
1274 }
1275
1276 bool
isKnownCursed() const1277 ITEM::isKnownCursed() const
1278 {
1279 return (myFlag1 & ITEMFLAG1_ISKNOWCURSE) ? true : false;
1280 }
1281
1282 bool
isKnownNotCursed() const1283 ITEM::isKnownNotCursed() const
1284 {
1285 return (myFlag1 & ITEMFLAG1_ISKNOWNOTCURSE) ? true : false;
1286 }
1287
1288 bool
isKnownEnchant() const1289 ITEM::isKnownEnchant() const
1290 {
1291 return (myFlag1 & ITEMFLAG1_ISKNOWENCHANT) ? true : false;
1292 }
1293
1294 bool
isKnownCharges() const1295 ITEM::isKnownCharges() const
1296 {
1297 return (myFlag1 & ITEMFLAG1_ISKNOWCHARGES) ? true : false;
1298 }
1299
1300 bool
isKnownPoison() const1301 ITEM::isKnownPoison() const
1302 {
1303 return (myFlag1 & ITEMFLAG1_ISKNOWPOISON) ? true : false;
1304 }
1305
1306 bool
isKnownClass() const1307 ITEM::isKnownClass() const
1308 {
1309 if (glb_itemid[myDefinition])
1310 return true;
1311 return false;
1312 }
1313
1314 bool
isInventory() const1315 ITEM::isInventory() const
1316 {
1317 return (myFlag1 & ITEMFLAG1_ISINVENTORY) ? true : false;
1318 }
1319
1320 bool
isArtifact() const1321 ITEM::isArtifact() const
1322 {
1323 return (myFlag1 & ITEMFLAG1_ISARTIFACT) ? true : false;
1324 }
1325
1326 const ARTIFACT *
getArtifact() const1327 ITEM::getArtifact() const
1328 {
1329 if (!isArtifact())
1330 return 0;
1331
1332 return artifact_buildartifact(myName.getName(), getDefinition());
1333 }
1334
1335 void
setInventory(bool isinventory)1336 ITEM::setInventory(bool isinventory)
1337 {
1338 if (isinventory)
1339 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 | ITEMFLAG1_ISINVENTORY);
1340 else
1341 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 & ~ITEMFLAG1_ISINVENTORY);
1342 }
1343
1344 void
markIdentified(bool silent)1345 ITEM::markIdentified(bool silent)
1346 {
1347 markEnchantKnown();
1348 markCursedKnown();
1349 markChargesKnown();
1350 markPoisonKnown();
1351
1352 markClassKnown(silent);
1353 }
1354
1355 void
markClassKnown(bool silent)1356 ITEM::markClassKnown(bool silent)
1357 {
1358 // Avatar gets piety for this.
1359 if (!glb_itemid[myDefinition])
1360 {
1361 MOB *avatar;
1362
1363 avatar = MOB::getAvatar();
1364 if (avatar)
1365 {
1366 // Only spam for interesting class changes.
1367 if (!silent && getMagicType() != MAGICTYPE_NONE)
1368 avatar->formatAndReport("%U <learn> that %IU <I:be> %Ii.", this);
1369 avatar->pietyIdentify((ITEM_NAMES) myDefinition);
1370 }
1371 }
1372
1373 glb_itemid[myDefinition] = 1;
1374 }
1375
1376 void
markEnchantKnown()1377 ITEM::markEnchantKnown()
1378 {
1379 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 | ITEMFLAG1_ISKNOWENCHANT);
1380 }
1381
1382 void
markCursedKnown()1383 ITEM::markCursedKnown()
1384 {
1385 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 | ITEMFLAG1_ISKNOWCURSE);
1386 }
1387
1388 void
markNotCursedKnown(bool newknown)1389 ITEM::markNotCursedKnown(bool newknown)
1390 {
1391 if (newknown)
1392 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 | ITEMFLAG1_ISKNOWNOTCURSE);
1393 else
1394 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 & ~ITEMFLAG1_ISKNOWNOTCURSE);
1395 }
1396
1397 void
markChargesKnown()1398 ITEM::markChargesKnown()
1399 {
1400 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 | ITEMFLAG1_ISKNOWCHARGES);
1401 }
1402
1403 void
clearChargesKnown()1404 ITEM::clearChargesKnown()
1405 {
1406 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 & (~ITEMFLAG1_ISKNOWCHARGES));
1407 }
1408
1409 void
markPoisonKnown()1410 ITEM::markPoisonKnown()
1411 {
1412 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 | ITEMFLAG1_ISKNOWPOISON);
1413 }
1414
1415 void
makeCursed()1416 ITEM::makeCursed()
1417 {
1418 if (myFlag1 & ITEMFLAG1_ISBLESSED)
1419 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 & ~ITEMFLAG1_ISBLESSED);
1420 else
1421 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 | ITEMFLAG1_ISCURSED);
1422
1423 // Regardless, anyone's theories about this are now wrong.
1424 // One could argue we should not clear this flag as if they
1425 // cursing was obvious we usually do a markCursedKnown, so if
1426 // that is not done it is truly the avatar that doesn't know
1427 // the change, and hence the player shouldn't as well.
1428 // For now, I consider that logic too mean.
1429 markNotCursedKnown(false);
1430 }
1431
1432 void
makeBlessed()1433 ITEM::makeBlessed()
1434 {
1435 if (myFlag1 & ITEMFLAG1_ISCURSED)
1436 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 & ~ITEMFLAG1_ISCURSED);
1437 else
1438 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 | ITEMFLAG1_ISBLESSED);
1439 }
1440
1441 void
makePoisoned(POISON_NAMES poison,int charges)1442 ITEM::makePoisoned(POISON_NAMES poison, int charges)
1443 {
1444 myPoison = poison;
1445 myPoisonCharges = charges;
1446 }
1447
1448 bool
isPoisoned() const1449 ITEM::isPoisoned() const
1450 {
1451 if (myPoison != POISON_NONE && myPoisonCharges)
1452 return true;
1453 return false;
1454 }
1455
1456 void
applyPoison(MOB * src,MOB * target,bool force)1457 ITEM::applyPoison(MOB *src, MOB *target, bool force)
1458 {
1459 if (!force && rand_choice(glb_poisondefs[myPoison].modulus))
1460 return;
1461
1462 if (!isPoisoned())
1463 return;
1464
1465 // Is this a sensible time?
1466 target->setTimedIntrinsic( src, (INTRINSIC_NAMES)
1467 glb_poisondefs[myPoison].intrinsic,
1468 rand_dice(3, 3, 0) );
1469
1470 myPoisonCharges--;
1471 }
1472
1473 bool
isCursed() const1474 ITEM::isCursed() const
1475 {
1476 return (myFlag1 & ITEMFLAG1_ISCURSED) ? true : false;
1477 }
1478
1479 bool
isBlessed() const1480 ITEM::isBlessed() const
1481 {
1482 return (myFlag1 & ITEMFLAG1_ISBLESSED) ? true : false;
1483 }
1484
1485 bool
isQuivered() const1486 ITEM::isQuivered() const
1487 {
1488 return (myFlag1 & ITEMFLAG1_ISQUIVERED) ? true : false;
1489 }
1490
1491 void
markQuivered(bool newquiver)1492 ITEM::markQuivered(bool newquiver)
1493 {
1494 if (newquiver)
1495 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 | ITEMFLAG1_ISQUIVERED);
1496 else
1497 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 & ~ITEMFLAG1_ISQUIVERED);
1498
1499 }
1500
1501 bool
isFavorite() const1502 ITEM::isFavorite() const
1503 {
1504 return (myFlag1 & ITEMFLAG1_ISFAVORITE) ? true : false;
1505 }
1506
1507 void
markFavorite(bool newfavorite)1508 ITEM::markFavorite(bool newfavorite)
1509 {
1510 if (newfavorite)
1511 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 | ITEMFLAG1_ISFAVORITE);
1512 else
1513 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 & ~ITEMFLAG1_ISFAVORITE);
1514
1515 }
1516
1517 bool
isMapped() const1518 ITEM::isMapped() const
1519 {
1520 return (myFlag1 & ITEMFLAG1_ISMAPPED) ? true : false;
1521 }
1522
1523 void
markMapped(bool val)1524 ITEM::markMapped(bool val)
1525 {
1526 if (val)
1527 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 | ITEMFLAG1_ISMAPPED);
1528 else
1529 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 & ~ITEMFLAG1_ISMAPPED);
1530 }
1531
1532 bool
isBelowGrade() const1533 ITEM::isBelowGrade() const
1534 {
1535 return (myFlag1 & ITEMFLAG1_ISBELOWGRADE) ? true : false;
1536 }
1537
1538 void
markBelowGrade(bool val)1539 ITEM::markBelowGrade(bool val)
1540 {
1541 if (val)
1542 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 | ITEMFLAG1_ISBELOWGRADE);
1543 else
1544 myFlag1 = (ITEMFLAG1_NAMES) (myFlag1 & ~ITEMFLAG1_ISBELOWGRADE);
1545 }
1546
1547 bool
isWeapon() const1548 ITEM::isWeapon() const
1549 {
1550 // Not the simplest of computation as we can equip anything...
1551 if (getAttackName() != ATTACK_MISUSED)
1552 return true;
1553 return false;
1554 }
1555
1556 bool
isArmor() const1557 ITEM::isArmor() const
1558 {
1559 return (myFlag1 & ITEMFLAG1_ARMORSLOTMASK) ? true : false;
1560 }
1561
1562 bool
isBoots() const1563 ITEM::isBoots() const
1564 {
1565 return (myFlag1 & ITEMFLAG1_ARMORSLOTMASK) == ITEMFLAG1_ISBOOTS;
1566 }
1567
1568 bool
isHelmet() const1569 ITEM::isHelmet() const
1570 {
1571 return (myFlag1 & ITEMFLAG1_ARMORSLOTMASK) == ITEMFLAG1_ISHELMET;
1572 }
1573
1574 bool
isJacket() const1575 ITEM::isJacket() const
1576 {
1577 return (myFlag1 & ITEMFLAG1_ARMORSLOTMASK) == ITEMFLAG1_ISJACKET;
1578 }
1579
1580 bool
isShield() const1581 ITEM::isShield() const
1582 {
1583 return (myFlag1 & ITEMFLAG1_ARMORSLOTMASK) == ITEMFLAG1_ISSHIELD;
1584 }
1585
1586 bool
isRing() const1587 ITEM::isRing() const
1588 {
1589 return (myFlag1 & ITEMFLAG1_ARMORSLOTMASK) == ITEMFLAG1_ISRING;
1590 }
1591
1592 bool
isAmulet() const1593 ITEM::isAmulet() const
1594 {
1595 return (myFlag1 & ITEMFLAG1_ARMORSLOTMASK) == ITEMFLAG1_ISAMULET;
1596 }
1597
1598 bool
isPassable() const1599 ITEM::isPassable() const
1600 {
1601 return !(myFlag1 & ITEMFLAG1_ISBLOCKING);
1602 }
1603
1604 int
getStackOrder() const1605 ITEM::getStackOrder() const
1606 {
1607 // This is used to force the sorting of items. We want users
1608 // to quickly see if there are interesting drops so put corpses
1609 // on the bottom. Likewise, impassable objects need to be known
1610 // or people won't know why they can't move there - this currently
1611 // only means boulders.
1612 // Finally, artifacts are put on the top to draw attention to them
1613 // as they are cool.
1614 if (myDefinition == ITEM_CORPSE ||
1615 myDefinition == ITEM_BONES)
1616 {
1617 return 0;
1618 }
1619
1620 if (!isPassable())
1621 return 3;
1622
1623 if (isArtifact())
1624 return 2;
1625
1626 // Default level.
1627 return 1;
1628 }
1629
1630 const char *
getItemName(ITEM_NAMES item,bool forceid)1631 ITEM::getItemName(ITEM_NAMES item, bool forceid)
1632 {
1633 const char *rawname = 0;
1634
1635 if (forceid || glb_itemid[item])
1636 {
1637 switch ((MAGICTYPE_NAMES) glb_itemdefs[item].magictype)
1638 {
1639 case NUM_MAGICTYPES:
1640 case MAGICTYPE_NONE:
1641 rawname = glb_itemdefs[item].name;
1642 break;
1643
1644 case MAGICTYPE_POTION:
1645 rawname = glb_potiondefs[glb_magicitem[item]].name;
1646 break;
1647
1648 case MAGICTYPE_SCROLL:
1649 rawname = glb_scrolldefs[glb_magicitem[item]].name;
1650 break;
1651
1652 case MAGICTYPE_RING:
1653 rawname = glb_ringdefs[glb_magicitem[item]].name;
1654 break;
1655
1656 case MAGICTYPE_HELM:
1657 rawname = glb_helmdefs[glb_magicitem[item]].name;
1658 break;
1659
1660 case MAGICTYPE_BOOTS:
1661 rawname = glb_bootsdefs[glb_magicitem[item]].name;
1662 break;
1663
1664 case MAGICTYPE_AMULET:
1665 rawname = glb_amuletdefs[glb_magicitem[item]].name;
1666 break;
1667
1668 case MAGICTYPE_WAND:
1669 rawname = glb_wanddefs[glb_magicitem[item]].name;
1670 break;
1671
1672 case MAGICTYPE_SPELLBOOK:
1673 rawname = glb_spellbookdefs[glb_magicitem[item]].name;
1674 break;
1675
1676 case MAGICTYPE_STAFF:
1677 rawname = glb_staffdefs[glb_magicitem[item]].name;
1678 break;
1679 }
1680 }
1681 else
1682 {
1683 rawname = glb_itemdefs[item].name;
1684 }
1685
1686 return rawname;
1687 }
1688
1689 const char *
getRawName(bool idclass) const1690 ITEM::getRawName(bool idclass) const
1691 {
1692 const char *rawname = 0;
1693
1694 rawname = ITEM::getItemName(getDefinition(), idclass);
1695
1696 return rawname;
1697 }
1698
1699 BUF
getName(bool article,bool shortname,bool neverpronoun,bool idclass,bool forcesingle) const1700 ITEM::getName(bool article, bool shortname, bool neverpronoun, bool idclass, bool forcesingle) const
1701 {
1702 BUF rawname;
1703 bool ignorecurse = false;
1704 BUF result;
1705
1706 rawname.reference(getRawName(idclass));
1707
1708 // In some cases, the actual name comes from what mob it actually is.
1709 if (myDefinition == ITEM_BONES || myDefinition == ITEM_CORPSE ||
1710 myDefinition == ITEM_STATUE)
1711 {
1712 MOB *mob;
1713
1714 mob = myCorpseMob.getMob();
1715 if (mob)
1716 {
1717 BUF buf, mobname;
1718
1719 mobname = mob->getName(false, false, true);
1720
1721 buf.sprintf("%s%s %s",
1722 (myCharges == 255 ? "preserved " : ""),
1723 mobname.buffer(), rawname.buffer());
1724 rawname = buf;
1725 }
1726 }
1727
1728 // We have a special case off the front here. We don't want blessed
1729 // bottles of water to be "a holy bottle of water", but rather be
1730 // "a bottle of holy water". This is because it is the water that
1731 // is affected, not the bottle.
1732 if (myDefinition == ITEM_WATER)
1733 {
1734 if (isKnownCursed())
1735 {
1736 if (isCursed())
1737 rawname.reference("bottle of unholy water");
1738 if (isBlessed())
1739 rawname.reference("bottle of holy water");
1740
1741 ignorecurse = true;
1742 }
1743 }
1744
1745 // NB: This must handle every case which causes further tmp buffers
1746 // to be needed.
1747 if (!article &&
1748 (forcesingle || (getStackCount() == 1)) &&
1749 (shortname ||
1750 ((!isKnownCursed() || ignorecurse) &&
1751 !isKnownEnchant() && !isKnownCharges() &&
1752 !isKnownPoison() && !glb_itemnames[myDefinition] &&
1753 !myName.getName())))
1754 {
1755 return rawname;
1756 }
1757
1758 BUF buf;
1759
1760 if (shortname ||
1761 ((!isKnownCursed() || ignorecurse) && !isKnownEnchant() && !isKnownCharges() && !isKnownPoison() && !glb_itemnames[myDefinition] && !myName.getName()))
1762 {
1763 // We require an article, but that is it.
1764 return gram_createcount(rawname, forcesingle ? 1 : getStackCount(), article);
1765 }
1766 else
1767 {
1768 // We first build the name without the article, as the prefix
1769 // may alter what the proper article is. Eg: an evil sword.
1770 // The problem with this are cases where there should
1771 // be no article - in these cases, the presence of the prefix
1772 // may trick us into adding an unnecessary article, Eg:
1773 // an evil Baezl'bub's black heart.
1774 BUF nonarticle;
1775
1776 nonarticle.clear();
1777
1778 if (isKnownCursed() && !ignorecurse)
1779 {
1780 if (isCursed())
1781 nonarticle.strcat("evil ");
1782 if (isBlessed())
1783 nonarticle.strcat("holy ");
1784 }
1785
1786 if (isKnownPoison() && isPoisoned())
1787 {
1788 nonarticle.strcat("poisoned ");
1789 }
1790
1791 if (isKnownEnchant())
1792 {
1793 // Don't waste space on +0 unless something where that
1794 // counts.
1795 if (getEnchantment() != 0 ||
1796 isBoots() || isHelmet() || isShield() ||
1797 isJacket() || isWeapon())
1798 {
1799 BUF tmp;
1800
1801 tmp.sprintf("%+d ", getEnchantment());
1802 nonarticle.strcat(tmp);
1803 }
1804 }
1805
1806 nonarticle.strcat(rawname);
1807
1808 if (article && isArtifact() && (forcesingle || (getStackCount() == 1)))
1809 {
1810 buf.strcpy("the ");
1811 buf.strcat(nonarticle);
1812 }
1813 else
1814 {
1815 buf = gram_createcount(nonarticle, forcesingle ? 1 : getStackCount(), article);
1816 }
1817
1818 // Now, we append the charge count if we know it, and the magictype
1819 // is something with charges. Currently, that means wands or
1820 // spellbooks.
1821 if (isKnownCharges() &&
1822 (getMagicType() == MAGICTYPE_WAND ||
1823 getMagicType() == MAGICTYPE_SPELLBOOK ||
1824 getDefinition() == ITEM_LIGHTNINGRAPIER))
1825 {
1826 BUF tmp;
1827
1828 tmp.sprintf(" (%d)", myCharges);
1829 buf.strcat(tmp);
1830 }
1831
1832 if (myName.getName())
1833 {
1834 // User named items are called. Artifact items
1835 // just *are* that name. Ie: the long sword foobar.
1836 if (isArtifact())
1837 buf.strcat(" ");
1838 else
1839 buf.strcat(" called ");
1840 buf.strcat(myName.getName());
1841 }
1842 else if (glb_itemnames[myDefinition])
1843 {
1844 buf.strcat(" named ");
1845 buf.strcat(glb_itemnames[myDefinition]);
1846 }
1847 }
1848
1849 return buf;
1850 }
1851
1852 void
formatAndReport(const char * str)1853 ITEM::formatAndReport(const char *str)
1854 {
1855 formatAndReport(0, str);
1856 }
1857
1858 void
formatAndReport(MOB * owner,const char * str)1859 ITEM::formatAndReport(MOB *owner, const char *str)
1860 {
1861 BUF buf;
1862
1863 if (!str)
1864 return;
1865
1866 // Ignore if we are loading so we don't spam about re-wetting
1867 // sunk treasure.
1868 if (MAP::isLoading())
1869 return;
1870
1871 buf = MOB::formatToString(str, 0, this, 0, 0);
1872
1873 if (!owner && !isInventory())
1874 {
1875 // Spam our report to the map
1876 glbCurLevel->reportMessage(buf, getX(), getY());
1877 }
1878 else
1879 {
1880 if (owner)
1881 owner->reportMessage(buf);
1882 else if (MOB::getAvatar())
1883 MOB::getAvatar()->reportMessage(buf);
1884 }
1885 }
1886
1887 void
viewDiscoveries()1888 ITEM::viewDiscoveries()
1889 {
1890 ITEM_NAMES item;
1891 int num = 0;
1892
1893 // Written while waiting for C967 to show up in the line
1894 // at the passport office.
1895 gfx_pager_addtext("Your discoveries:");
1896 gfx_pager_newline();
1897 gfx_pager_separator();
1898
1899 FOREACH_ITEM(item)
1900 {
1901 // Check to see if this is a meaningful id
1902 // Unlike nethack, I don't think iding longswords is interesting.
1903 const char *basename, *idname;
1904
1905 idname = ITEM::getItemName(item);
1906 basename = glb_itemdefs[item].name;
1907 // No need for strcmp here since we are using the
1908 // prebuilt tables.
1909 if (idname != basename)
1910 {
1911 BUF buf;
1912
1913 buf.sprintf("%s:", basename);
1914 gfx_pager_addtext(gram_capitalize(buf));
1915 gfx_pager_newline();
1916 buf.sprintf(" %s", idname);
1917 gfx_pager_addtext(gram_capitalize(buf));
1918 gfx_pager_newline();
1919 num++;
1920 }
1921 }
1922
1923 if (!num)
1924 {
1925 gfx_pager_addtext("You have learned nothing.");
1926 gfx_pager_newline();
1927 }
1928
1929 gfx_pager_display();
1930 }
1931
1932 void
viewDescription() const1933 ITEM::viewDescription() const
1934 {
1935 pageDescription(false);
1936
1937 gfx_pager_display();
1938 }
1939
1940 BUF
buildAttackName(const ATTACK_DEF * attack)1941 ITEM::buildAttackName(const ATTACK_DEF *attack)
1942 {
1943 BUF buf;
1944
1945 buf.strcpy("");
1946 while (attack)
1947 {
1948 BUF section;
1949 bool skipbonus = false;
1950
1951 if (attack->damage.myNumdie)
1952 {
1953 if (attack->damage.mySides < 2)
1954 {
1955 // Constant damage. Fold in the bonus damage
1956 // and report just this.
1957 section.sprintf("%d",
1958 attack->damage.myNumdie + attack->damage.myBonus);
1959 skipbonus = true;
1960 buf.strcat(section);
1961 }
1962 else
1963 {
1964 section.sprintf("%d",
1965 attack->damage.myNumdie);
1966 buf.strcat(section);
1967 section.sprintf("d%d",
1968 attack->damage.mySides);
1969 buf.strcat(section);
1970 }
1971 }
1972 if (!skipbonus && attack->damage.myBonus)
1973 {
1974 section.sprintf("%+d",
1975 attack->damage.myBonus);
1976 buf.strcat(section);
1977 }
1978 if (attack->bonustohit)
1979 {
1980 section.sprintf("[%+d]", attack->bonustohit);
1981 buf.strcat(section);
1982 }
1983 section.sprintf(" (%s)", glb_elementdefs[attack->element].name);
1984 buf.strcat(section);
1985
1986 if (attack->nextattack != ATTACK_NONE)
1987 {
1988 buf.strcat(", {");
1989
1990 section = buildAttackName(&glb_attackdefs[attack->nextattack]);
1991 buf.strcat(section);
1992
1993 buf.strcat("}");
1994 }
1995
1996 if (attack->sameattack != ATTACK_NONE)
1997 {
1998 buf.strcat(", ");
1999 attack = &glb_attackdefs[attack->sameattack];
2000 }
2001 else
2002 attack = 0;
2003 }
2004
2005 return buf;
2006 }
2007
2008 void
pageDescription(bool brief) const2009 ITEM::pageDescription(bool brief) const
2010 {
2011 BUF buf;
2012 int skilllevel;
2013
2014 gfx_pager_addtext(gram_capitalize(getName()));
2015 gfx_pager_newline();
2016
2017 // Add the identify info.
2018 const ARTIFACT *art = getArtifact();
2019 if ((isIdentified() && !art) ||
2020 (isFullyIdentified() && art))
2021 {
2022 bool hasany = false;
2023 INTRINSIC_NAMES intrinsic;
2024 const char *prequel;
2025
2026 if (!brief)
2027 {
2028 gfx_pager_separator();
2029 }
2030
2031 if (art)
2032 prequel = "This artifact grants ";
2033 else
2034 prequel = "This item grants ";
2035
2036 for (intrinsic = INTRINSIC_NONE; intrinsic < NUM_INTRINSICS;
2037 intrinsic = (INTRINSIC_NAMES) (intrinsic + 1))
2038 {
2039 if (hasIntrinsic(intrinsic))
2040 {
2041 if (!hasany)
2042 {
2043 gfx_pager_addtext(prequel);
2044 hasany = true;
2045 }
2046 else
2047 gfx_pager_addtext(", ");
2048
2049 gfx_pager_addtext(glb_intrinsicdefs[intrinsic].name);
2050 }
2051 }
2052
2053 // Light radius:
2054 if (isLight())
2055 {
2056 if (!hasany)
2057 {
2058 gfx_pager_addtext(prequel);
2059 hasany = true;
2060 }
2061 else
2062 gfx_pager_addtext(", ");
2063
2064 buf.sprintf("light radius %d", getLightRadius());
2065 gfx_pager_addtext(buf);
2066 }
2067
2068 // Amour class
2069 if (getAC())
2070 {
2071 if (!hasany)
2072 {
2073 gfx_pager_addtext(prequel);
2074 hasany = true;
2075 }
2076 else
2077 gfx_pager_addtext(", ");
2078
2079 buf.sprintf("armour %d", getAC());
2080 gfx_pager_addtext(buf);
2081 }
2082
2083 // Finish off the special stuff list, and go to mandatory list.
2084 if (hasany)
2085 {
2086 gfx_pager_addtext(". ");
2087 gfx_pager_newline();
2088 }
2089
2090 if (isCarryIntrinsic())
2091 {
2092 gfx_pager_addtext("Intrinsics are granted by carrying this item.");
2093 gfx_pager_newline();
2094 }
2095
2096 const ATTACK_DEF *attack;
2097
2098 gfx_pager_addtext("Melee: ");
2099 buf = buildAttackName(getAttack());
2100 gfx_pager_addtext(buf);
2101 gfx_pager_newline();
2102
2103 gfx_pager_addtext("Thrown ");
2104 attack = getThrownAttack();
2105
2106 buf.sprintf("%d: ", attack->range);
2107 gfx_pager_addtext(buf);
2108
2109 buf = buildAttackName(attack);
2110 gfx_pager_addtext(buf);
2111
2112 gfx_pager_newline();
2113 }
2114
2115 // Skip out here if brief.
2116 if (brief)
2117 return;
2118
2119 // Add the boring stats.
2120 gfx_pager_separator();
2121
2122 buf.strcpy("Size: ");
2123 buf.strcat(gram_capitalize(glb_sizedefs[getSize()].name));
2124 gfx_pager_addtext(buf);
2125 gfx_pager_newline();
2126
2127 buf.strcpy("Material: ");
2128 buf.strcat(gram_capitalize(glb_materialdefs[getMaterial()].name));
2129 gfx_pager_addtext(buf);
2130 gfx_pager_newline();
2131
2132 buf.sprintf("Weight: %d",
2133 glb_itemdefs[getDefinition()].weight);
2134 gfx_pager_addtext(buf);
2135 gfx_pager_newline();
2136
2137 const char *noisetext;
2138 switch (getNoiseLevel())
2139 {
2140 case 0:
2141 noisetext = "Silent";
2142 break;
2143 case 1:
2144 noisetext = "Quiet";
2145 break;
2146 case 2:
2147 noisetext = "Average";
2148 break;
2149 case 3:
2150 noisetext = "Loud";
2151 break;
2152 default:
2153 case 4:
2154 noisetext = "Very Loud";
2155 break;
2156 }
2157
2158 buf.sprintf("Noise: %s", noisetext);
2159 gfx_pager_addtext(buf);
2160 gfx_pager_newline();
2161
2162 gfx_pager_addtext("Attack Style: ");
2163 buf.strcpy(glb_skilldefs[getAttackSkill()].name);
2164 // Remove first space.
2165 {
2166 char *c;
2167 for (c = buf.evildata(); *c && !isspace(*c); c++);
2168 *c = '\0';
2169 }
2170 gfx_pager_addtext(buf);
2171 gfx_pager_newline();
2172
2173 // Special skill, if any.
2174 if (getSpecialSkill() != SKILL_NONE)
2175 {
2176 gfx_pager_addtext("Special Skill: ");
2177 gfx_pager_addtext(glb_skilldefs[getSpecialSkill()].name);
2178 gfx_pager_newline();
2179 }
2180
2181 // Calculate your attack ability.
2182 skilllevel = MOB::getAvatar()->getWeaponSkillLevel(this, ATTACKSTYLE_MELEE);
2183 if (skilllevel)
2184 {
2185 gfx_pager_addtext("Melee Skill: ");
2186 while (skilllevel--)
2187 {
2188 gfx_pager_addtext("*");
2189 }
2190 gfx_pager_newline();
2191 }
2192
2193 skilllevel = MOB::getAvatar()->getWeaponSkillLevel(this, ATTACKSTYLE_THROWN);
2194 if (skilllevel)
2195 {
2196 gfx_pager_addtext("Thrown Skill: ");
2197 while (skilllevel--)
2198 {
2199 gfx_pager_addtext("*");
2200 }
2201 gfx_pager_newline();
2202 }
2203
2204 skilllevel = MOB::getAvatar()->getArmourSkillLevel(this);
2205 if (skilllevel)
2206 {
2207 gfx_pager_addtext("Armour Skill: ");
2208 while (skilllevel--)
2209 {
2210 gfx_pager_addtext("*");
2211 }
2212 gfx_pager_newline();
2213 }
2214
2215 // Add encyclopedia description.
2216 if (encyc_hasentry("ITEM", getDefinition()))
2217 {
2218 gfx_pager_separator();
2219 encyc_pageentry("ITEM", getDefinition());
2220 }
2221
2222 // Add the magical encylopedia entry
2223 if (isIdentified() && getMagicType() != MAGICTYPE_NONE)
2224 {
2225 if (encyc_hasentry(glb_magictypedefs[getMagicType()].cycname, getMagicClass()))
2226 {
2227 gfx_pager_separator();
2228 encyc_pageentry(glb_magictypedefs[getMagicType()].cycname, getMagicClass());
2229 }
2230 }
2231 }
2232
2233 const char *
getPronoun() const2234 ITEM::getPronoun() const
2235 {
2236 return gram_getpronoun(getPerson());
2237 }
2238
2239 const char *
getPossessive() const2240 ITEM::getPossessive() const
2241 {
2242 return gram_getpossessive(getPerson());
2243 }
2244
2245 const char *
getReflexive() const2246 ITEM::getReflexive() const
2247 {
2248 return gram_getreflexive(getPerson());
2249 }
2250
2251 const char *
getAccusative() const2252 ITEM::getAccusative() const
2253 {
2254 return gram_getaccusative(getPerson());
2255 }
2256
2257 BUF
conjugate(const char * infinitive,bool past) const2258 ITEM::conjugate(const char *infinitive, bool past) const
2259 {
2260 return gram_conjugate(infinitive, getPerson(), past);
2261 }
2262
2263 VERB_PERSON
getPerson() const2264 ITEM::getPerson() const
2265 {
2266 // Determine our plurality...
2267 if (myStackCount > 1)
2268 {
2269 // We are stacked, we are plural no matter what...
2270 return VERB_THEY;
2271 }
2272
2273 const char *name;
2274
2275 // *cries*
2276 // Guess who called getPronoun inside of getName?
2277 name = getRawName();
2278
2279 if (gram_isnameplural(name))
2280 {
2281 return VERB_THEY;
2282 }
2283 return VERB_IT;
2284 }
2285
2286 bool
isLight() const2287 ITEM::isLight() const
2288 {
2289 if (glb_itemdefs[myDefinition].lightradius)
2290 return true;
2291
2292 // Check for ring of light...
2293 if (getMagicType() == MAGICTYPE_RING &&
2294 getMagicClass() == RING_LIGHT)
2295 {
2296 return true;
2297 }
2298
2299 const ARTIFACT *art;
2300 art = getArtifact();
2301
2302 if (art && art->lightradius)
2303 return true;
2304
2305 return false;
2306 }
2307
2308 int
getLightRadius() const2309 ITEM::getLightRadius() const
2310 {
2311 int radius;
2312
2313 radius = glb_itemdefs[myDefinition].lightradius;
2314
2315 // Check for ring of light...
2316 if (getMagicType() == MAGICTYPE_RING &&
2317 getMagicClass() == RING_LIGHT)
2318 {
2319 if (radius < 3)
2320 radius = 3;
2321 }
2322
2323 const ARTIFACT *art;
2324
2325 art = getArtifact();
2326 if (art)
2327 {
2328 if (radius < art->lightradius)
2329 radius = art->lightradius;
2330 }
2331
2332 return radius;
2333 }
2334
2335 TILE_NAMES
getTile() const2336 ITEM::getTile() const
2337 {
2338 return (TILE_NAMES) glb_itemdefs[myDefinition].tile;
2339 }
2340
2341 MINI_NAMES
getMiniTile() const2342 ITEM::getMiniTile() const
2343 {
2344 return (MINI_NAMES) glb_itemdefs[myDefinition].minitile;
2345 }
2346
2347 //
2348 // Stack Routines
2349 //
2350
2351 bool
canMerge(ITEM * item) const2352 ITEM::canMerge(ITEM *item) const
2353 {
2354 // Verify we match on all particulars.
2355 if (item->myDefinition != myDefinition)
2356 return false;
2357
2358 // If either of our items have a name, we can't merge.
2359 if (item->myName.getName() || myName.getName())
2360 {
2361 // However, if both have a name
2362 if (item->myName.getName() && myName.getName())
2363 {
2364 // And the names match
2365 if (!strcmp(item->myName.getName(), myName.getName()))
2366 {
2367 // We can merge!
2368 }
2369 else
2370 return false;
2371 }
2372 else
2373 return false;
2374 }
2375
2376 // Prohibit merging of artifacts unless both are artifacts.
2377 // Otherwise, naming an item the same as an artifact would allow
2378 // the creation of artifacts.
2379 if (item->isArtifact() != isArtifact())
2380 return false;
2381
2382 // Prohibit merging of corpses!
2383 // This is required as we keep the mob * underneath them.
2384 // We also don't want to merge statues for the same reason.
2385 // Thus, if either item has a non-zero corpsemob, it is illegal.
2386 if (!item->myCorpseMob.isNull() || !myCorpseMob.isNull())
2387 return false;
2388
2389 if (item->myEnchantment != myEnchantment)
2390 return false;
2391
2392 if (item->myCharges != myCharges)
2393 return false;
2394
2395 if (item->myPoison != myPoison)
2396 return false;
2397
2398 if (item->myPoisonCharges != myPoisonCharges)
2399 return false;
2400
2401 // Flags are a bit trickier, as we only care about some of them.
2402
2403 if (item->isBlessed() != isBlessed())
2404 return false;
2405 if (item->isCursed() != isCursed())
2406 return false;
2407
2408 // We don't want to reveal curse states by merging, so known items
2409 // won't merge with unknown.
2410 if (item->isKnownCursed() != isKnownCursed())
2411 return false;
2412 // We only care about enchantment amounts where it is armour or
2413 // weapon.
2414 if (getItemType() == ITEMTYPE_ARMOUR ||
2415 getItemType() == ITEMTYPE_WEAPON)
2416 {
2417 if (item->isKnownEnchant() != isKnownEnchant())
2418 return false;
2419 }
2420 // We only care about charges with books and wands...
2421 if (getItemType() == ITEMTYPE_SPELLBOOK ||
2422 getItemType() == ITEMTYPE_WAND)
2423 {
2424 if (item->isKnownCharges() != isKnownCharges())
2425 return false;
2426 }
2427 // We only care about poison if there is poison. This does
2428 // let people id non-poisoned things but this is less of an
2429 // issue than the pain of detect curse not letting stuff stack.
2430 if (myPoisonCharges)
2431 if (item->isKnownPoison() != isKnownPoison())
2432 return false;
2433
2434 // Make sure a merge would not overflow our stack count.
2435 if (myStackCount + item->myStackCount > 99)
2436 return false;
2437
2438 // These items are indistinguishable, merge them!
2439 return true;
2440 }
2441
2442 void
mergeStack(ITEM * item)2443 ITEM::mergeStack(ITEM *item)
2444 {
2445 UT_ASSERT(canMerge(item));
2446 UT_ASSERT(myStackCount + item->myStackCount <= 99);
2447
2448 myStackCount += item->myStackCount;
2449
2450 // Make sure we have the union of knowledge.
2451 // We don't want to lose any knowledge as a result of a merge,
2452 // even if that knowledge is deemed useless.
2453 if (item->isKnownCharges())
2454 markChargesKnown();
2455 if (item->isKnownEnchant())
2456 markEnchantKnown();
2457 if (item->isKnownPoison())
2458 markPoisonKnown();
2459 }
2460
2461 ITEM *
splitStack(int count)2462 ITEM::splitStack(int count)
2463 {
2464 UT_ASSERT(count > 0);
2465 UT_ASSERT(count < myStackCount);
2466
2467 ITEM *item;
2468
2469 item = createClone();
2470 item->myStackCount = count;
2471 myStackCount -= count;
2472
2473 return item;
2474 }
2475
2476 void
splitAndDeleteStack(int count)2477 ITEM::splitAndDeleteStack(int count)
2478 {
2479 ITEM *split;
2480
2481 split = splitStack(count);
2482 delete split;
2483 }
2484
2485 int
getAC() const2486 ITEM::getAC() const
2487 {
2488 int ac;
2489
2490 ac = glb_itemdefs[myDefinition].ac;
2491
2492 // Enchantments... Only counts if it is armour
2493 if (isBoots() || isHelmet() || isJacket() || isShield() || isAmulet())
2494 {
2495 ac += getEnchantment();
2496 }
2497
2498 // Add in the artifact bonus.
2499 const ARTIFACT *art;
2500
2501 art = getArtifact();
2502 if (art)
2503 ac += art->acbonus;
2504
2505 return ac;
2506 }
2507
2508 int
getCoolness() const2509 ITEM::getCoolness() const
2510 {
2511 int cool = 0;
2512
2513 // Find coolness from magic type.
2514 switch (getMagicType())
2515 {
2516 case MAGICTYPE_RING:
2517 cool += glb_ringdefs[getMagicClass()].coolness;
2518 break;
2519 case MAGICTYPE_AMULET:
2520 cool += glb_amuletdefs[getMagicClass()].coolness;
2521 break;
2522 default:
2523 break;
2524 }
2525
2526 return cool;
2527 }
2528
2529 int
getWeight() const2530 ITEM::getWeight() const
2531 {
2532 int weight;
2533
2534 weight = glb_itemdefs[myDefinition].weight;
2535
2536 return weight;
2537 }
2538
2539 int
getCharges() const2540 ITEM::getCharges() const
2541 {
2542 int charges;
2543
2544 charges = myCharges;
2545 return charges;
2546 }
2547
2548 void
addCharges(int add)2549 ITEM::addCharges(int add)
2550 {
2551 int charges;
2552
2553 // Divide the charges among the number of items.
2554 add += getStackCount() / 2;
2555 add /= getStackCount();
2556 // Because I am nice, always restore one charge
2557 if (add < 1)
2558 add = 1;
2559
2560 charges = myCharges;
2561 charges += add;
2562
2563 if (charges < 0)
2564 charges = 0;
2565 if (charges > 99)
2566 charges = 99;
2567
2568 myCharges = charges;
2569 }
2570
2571 void
useCharge()2572 ITEM::useCharge()
2573 {
2574 if (myCharges)
2575 myCharges--;
2576 }
2577
2578 void
setAsPreserved()2579 ITEM::setAsPreserved()
2580 {
2581 myCharges = 255;
2582 }
2583
2584 MAGICTYPE_NAMES
getMagicType() const2585 ITEM::getMagicType() const
2586 {
2587 return (MAGICTYPE_NAMES) glb_itemdefs[myDefinition].magictype;
2588 }
2589
2590 int
getMagicClass() const2591 ITEM::getMagicClass() const
2592 {
2593 return glb_magicitem[myDefinition];
2594 }
2595
2596 void
buildIntrinsicTable(INTRINSIC * & intrinsic) const2597 ITEM::buildIntrinsicTable(INTRINSIC *&intrinsic) const
2598 {
2599 if (glb_itemdefs[myDefinition].intrinsic[0])
2600 {
2601 if (!intrinsic)
2602 intrinsic = new INTRINSIC;
2603 intrinsic->mergeFromString(glb_itemdefs[myDefinition].intrinsic);
2604 }
2605
2606 // Try the artifact class.
2607 const ARTIFACT *art;
2608
2609 art = getArtifact();
2610 if (art && art->intrinsics)
2611 {
2612 if (!intrinsic)
2613 intrinsic = new INTRINSIC;
2614 intrinsic->mergeFromString(art->intrinsics);
2615 }
2616
2617 // Try the magic item type...
2618 switch ((MAGICTYPE_NAMES) getMagicType())
2619 {
2620 case MAGICTYPE_RING:
2621 if (glb_ringdefs[getMagicClass()].intrinsic[0])
2622 {
2623 if (!intrinsic)
2624 intrinsic = new INTRINSIC;
2625 intrinsic->mergeFromString(
2626 glb_ringdefs[getMagicClass()].intrinsic);
2627 }
2628 break;
2629 case MAGICTYPE_HELM:
2630 if (glb_helmdefs[getMagicClass()].intrinsic[0])
2631 {
2632 if (!intrinsic)
2633 intrinsic = new INTRINSIC;
2634 intrinsic->mergeFromString(
2635 glb_helmdefs[getMagicClass()].intrinsic);
2636 }
2637 break;
2638 case MAGICTYPE_BOOTS:
2639 if (glb_bootsdefs[getMagicClass()].intrinsic[0])
2640 {
2641 if (!intrinsic)
2642 intrinsic = new INTRINSIC;
2643 intrinsic->mergeFromString(
2644 glb_bootsdefs[getMagicClass()].intrinsic);
2645 }
2646 break;
2647 case MAGICTYPE_AMULET:
2648 if (glb_amuletdefs[getMagicClass()].intrinsic[0])
2649 {
2650 if (!intrinsic)
2651 intrinsic = new INTRINSIC;
2652 intrinsic->mergeFromString(
2653 glb_amuletdefs[getMagicClass()].intrinsic);
2654 }
2655 break;
2656 case MAGICTYPE_STAFF:
2657 if (glb_staffdefs[getMagicClass()].intrinsic[0])
2658 {
2659 if (!intrinsic)
2660 intrinsic = new INTRINSIC;
2661 intrinsic->mergeFromString(
2662 glb_staffdefs[getMagicClass()].intrinsic);
2663 }
2664 break;
2665 case MAGICTYPE_NONE:
2666 case MAGICTYPE_POTION:
2667 case MAGICTYPE_SPELLBOOK:
2668 case MAGICTYPE_SCROLL:
2669 case MAGICTYPE_WAND:
2670 break;
2671
2672 case NUM_MAGICTYPES:
2673 UT_ASSERT(!"Unhandled MAGICTYPE");
2674 break;
2675 }
2676 }
2677
2678 bool
hasIntrinsic(INTRINSIC_NAMES intrinsic) const2679 ITEM::hasIntrinsic(INTRINSIC_NAMES intrinsic) const
2680 {
2681 // Check base item type...
2682 if (INTRINSIC::hasIntrinsic(glb_itemdefs[myDefinition].intrinsic,
2683 intrinsic))
2684 return true;
2685
2686 // Try the artifact class.
2687 const ARTIFACT *art;
2688
2689 art = getArtifact();
2690 if (art)
2691 {
2692 if (INTRINSIC::hasIntrinsic(art->intrinsics, intrinsic))
2693 return true;
2694 }
2695
2696 // Try the magic item type...
2697 switch ((MAGICTYPE_NAMES) getMagicType())
2698 {
2699 case MAGICTYPE_RING:
2700 {
2701 if (INTRINSIC::hasIntrinsic(glb_ringdefs[getMagicClass()].intrinsic,
2702 intrinsic) )
2703 return true;
2704 break;
2705 }
2706 case MAGICTYPE_HELM:
2707 {
2708 if (INTRINSIC::hasIntrinsic(glb_helmdefs[getMagicClass()].intrinsic,
2709 intrinsic) )
2710 return true;
2711 break;
2712 }
2713 case MAGICTYPE_BOOTS:
2714 {
2715 if (INTRINSIC::hasIntrinsic(glb_bootsdefs[getMagicClass()].intrinsic,
2716 intrinsic) )
2717 return true;
2718 break;
2719 }
2720 case MAGICTYPE_AMULET:
2721 {
2722 if (INTRINSIC::hasIntrinsic(glb_amuletdefs[getMagicClass()].intrinsic,
2723 intrinsic) )
2724 return true;
2725 break;
2726 }
2727 case MAGICTYPE_STAFF:
2728 {
2729 if (INTRINSIC::hasIntrinsic(glb_staffdefs[getMagicClass()].intrinsic,
2730 intrinsic) )
2731 return true;
2732 break;
2733 }
2734 case MAGICTYPE_NONE:
2735 case MAGICTYPE_POTION:
2736 case MAGICTYPE_SPELLBOOK:
2737 case MAGICTYPE_SCROLL:
2738 case MAGICTYPE_WAND:
2739 {
2740 // These guys all have no built in intrinsics
2741 return false;
2742 }
2743
2744 case NUM_MAGICTYPES:
2745 {
2746 UT_ASSERT(!"Unhandled MAGICTYPE");
2747 return false;
2748 }
2749 }
2750 return false;
2751 }
2752
2753 bool
dissolve(MOB * dissolver,bool * interesting)2754 ITEM::dissolve(MOB *dissolver, bool *interesting)
2755 {
2756 if (interesting)
2757 *interesting = true;
2758 if (canDissolve())
2759 {
2760 formatAndReport(dissolver, "%U <dissolve> in the acid!");
2761 return true;
2762 }
2763 if (interesting)
2764 *interesting = false;
2765
2766 return false;
2767 }
2768
2769 bool
canDissolve() const2770 ITEM::canDissolve() const
2771 {
2772 bool dodissolve = false;
2773 MATERIAL_NAMES material;
2774
2775 material = getMaterial();
2776 dodissolve = glb_materialdefs[material].soluble;
2777
2778 // If we have resistance to acid, we never dissolve
2779 // If we have vulnerability, we always dissolve.
2780 // If we have both, it is as normal.
2781 if (hasIntrinsic(INTRINSIC_RESISTACID))
2782 {
2783 if (!hasIntrinsic(INTRINSIC_VULNACID))
2784 {
2785 dodissolve = false;
2786 }
2787 }
2788 else if (hasIntrinsic(INTRINSIC_VULNACID))
2789 {
2790 dodissolve = true;
2791 }
2792
2793 // Check for quest flag. We don't want to dissolve those!
2794 if (glb_itemdefs[getDefinition()].isquest)
2795 dodissolve = false;
2796
2797 return dodissolve;
2798 }
2799
2800 bool
ignite(MOB * igniter,bool * interesting)2801 ITEM::ignite(MOB *igniter, bool *interesting)
2802 {
2803 if (interesting)
2804 *interesting = true;
2805
2806 // If we dip a sword, it becomes a flaming sword.
2807 if (getDefinition() == ITEM_LONGSWORD)
2808 {
2809 formatAndReport(igniter, "%U <catch> on fire!");
2810
2811 myDefinition = ITEM_FLAMESWORD;
2812 return false;
2813 }
2814
2815 if (getDefinition() == ITEM_ICEMACE)
2816 {
2817 formatAndReport(igniter, "%U <return> to room temperature.");
2818 myDefinition = ITEM_MACE;
2819 return false;
2820 }
2821
2822 if (getDefinition() == ITEM_ARROW)
2823 {
2824 formatAndReport(igniter, "%U <catch> on fire!");
2825 myDefinition = ITEM_FIREARROW;
2826 return false;
2827 }
2828
2829 // If we dip a club, it becomes a torch.
2830 if (getDefinition() == ITEM_CLUB)
2831 {
2832 formatAndReport(igniter, "%U <catch> on fire!");
2833
2834 myDefinition = ITEM_TORCH;
2835 // Set our shield flag.
2836 myFlag1 = (ITEMFLAG1_NAMES)
2837 (myFlag1 | glb_itemdefs[ITEM_TORCH].flag1);
2838 return false;
2839 }
2840
2841 // Check for burning up
2842 if (canBurn())
2843 {
2844 formatAndReport(igniter, "%U <burn> up.");
2845 return true;
2846 }
2847
2848 // Got here, nothing interesting happened.
2849 if (interesting)
2850 *interesting = false;
2851 return false;
2852 }
2853
2854 bool
electrify(int points,MOB * shocker,bool * interesting)2855 ITEM::electrify(int points, MOB *shocker, bool *interesting)
2856 {
2857 if (interesting)
2858 *interesting = true;
2859
2860 // Energize rapiers.
2861 if (getDefinition() == ITEM_RAPIER)
2862 {
2863 formatAndReport(shocker, "%U <be> filled with electricity!");
2864
2865 myDefinition = ITEM_LIGHTNINGRAPIER;
2866 addCharges(points);
2867 clearChargesKnown();
2868 return false;
2869 }
2870
2871 if (getDefinition() == ITEM_LIGHTNINGRAPIER)
2872 {
2873 formatAndReport(shocker, "%U <drink> deeply from the electric current!");
2874
2875 addCharges(points*2);
2876 clearChargesKnown();
2877 return false;
2878 }
2879
2880 // Got here, nothing interesting happened.
2881 if (interesting)
2882 *interesting = false;
2883 return false;
2884 }
2885
2886 bool
canBurn() const2887 ITEM::canBurn() const
2888 {
2889 bool doburn = false;
2890 MATERIAL_NAMES material;
2891
2892 material = getMaterial();
2893 doburn = glb_materialdefs[material].burnable;
2894
2895 // If we have resistance to fire, we never burn
2896 // If we have vulnerability, we always burn.
2897 // If we have both, it is as normal.
2898 if (hasIntrinsic(INTRINSIC_RESISTFIRE))
2899 {
2900 if (!hasIntrinsic(INTRINSIC_VULNFIRE))
2901 {
2902 doburn = false;
2903 }
2904 }
2905 else if (hasIntrinsic(INTRINSIC_VULNFIRE))
2906 {
2907 doburn = true;
2908 }
2909
2910 // Check for quest flag. We don't want to burn those!
2911 if (glb_itemdefs[getDefinition()].isquest)
2912 doburn = false;
2913
2914 return doburn;
2915 }
2916
2917 // This was factored out in White Rock BC.
2918 bool
douse(MOB * douser,bool * interesting)2919 ITEM::douse(MOB *douser, bool *interesting)
2920 {
2921 if (interesting)
2922 *interesting = true;
2923
2924 // If the dipped item is a flaming sword, it goes out.
2925 if (getDefinition() == ITEM_FLAMESWORD)
2926 {
2927 formatAndReport(douser, "%U <be> extinguished.");
2928 myDefinition = ITEM_LONGSWORD;
2929 }
2930 else if (getDefinition() == ITEM_FIREARROW)
2931 {
2932 formatAndReport(douser, "%U <be> extinguished.");
2933 myDefinition = ITEM_ARROW;
2934 }
2935 // As do torches.
2936 else if (getDefinition() == ITEM_TORCH)
2937 {
2938 formatAndReport(douser, "%U <be> extinguished.");
2939 myDefinition = ITEM_CLUB;
2940 // Clear out the isshield flag.
2941 myFlag1 = (ITEMFLAG1_NAMES)
2942 (myFlag1 & ~glb_itemdefs[ITEM_TORCH].flag1);
2943 }
2944 // Empty bottles fill. This may seem odd as smashing one bottle may
2945 // fill ten, but the same happens with dipping.
2946 else if (getDefinition() == ITEM_BOTTLE)
2947 {
2948 formatAndReport(douser, "%U <be> filled.");
2949 myDefinition = ITEM_WATER;
2950 }
2951 else
2952 {
2953 formatAndReport(douser, "%U <get> wet.");
2954 if (interesting)
2955 *interesting = false;
2956 }
2957
2958 // Water destroys nothing right now.
2959 return false;
2960 }
2961
2962 bool
actionDip(MOB * dipper,ITEM * dippee,ITEM * & newpotion,ITEM * & newitem)2963 ITEM::actionDip(MOB *dipper, ITEM *dippee,
2964 ITEM *&newpotion, ITEM *&newitem)
2965 {
2966 bool consumed = false;
2967 bool possible = false;
2968 BUF buf;
2969
2970 UT_ASSERT(dippee != this);
2971 // This should already be unlinked.
2972 UT_ASSERT(!getNext());
2973 UT_ASSERT(!dippee->getNext());
2974
2975 newpotion = this;
2976 newitem = dippee;
2977
2978 // Check for magic type potion...
2979 switch ((MAGICTYPE_NAMES) getMagicType())
2980 {
2981 case MAGICTYPE_POTION:
2982 {
2983 switch ((POTION_NAMES) getMagicClass())
2984 {
2985 case POTION_GREEKFIRE:
2986 {
2987 bool interesting = false;
2988
2989 possible = true;
2990 consumed = true;
2991
2992 buf = MOB::formatToString("%MU <M:dip> %IU into %U.",
2993 0, this, dipper, dippee);
2994 dipper->reportMessage(buf);
2995 if (dippee->ignite(dipper, &interesting))
2996 {
2997 // Burned up...
2998 delete dippee;
2999 newitem = 0;
3000 newpotion = ITEM::create(ITEM_BOTTLE, false, true);
3001 // Id it...
3002 markIdentified();
3003 // Delete self
3004 delete this;
3005
3006 break;
3007 }
3008 else if (interesting)
3009 {
3010 // Something happened.
3011 newpotion = ITEM::create(ITEM_BOTTLE, false, true);
3012 // Id it...
3013 markIdentified();
3014 // Delete self
3015 delete this;
3016
3017 break;
3018 }
3019 else
3020 {
3021 dipper->formatAndReport("Nothing happens.");
3022 }
3023 break;
3024 }
3025
3026 case POTION_HEAL:
3027 case POTION_BLIND:
3028 case POTION_SMOKE:
3029 buf = MOB::formatToString("%MU <M:dip> %IU into %U. Nothing happens.",
3030 0, this, dipper, dippee);
3031 dipper->reportMessage(buf);
3032 possible = true;
3033 consumed = true;
3034 break;
3035
3036 case POTION_MANA:
3037 buf = MOB::formatToString("%MU <M:dip> %IU into %U.",
3038 0, this, dipper, dippee);
3039 dipper->reportMessage(buf);
3040
3041 if (dippee->getMagicType() == MAGICTYPE_WAND)
3042 {
3043 // Dipping a wand into a mana potion recharges it.
3044 dipper->formatAndReport("%IU hungrily <I:drink> the liquid!", dippee);
3045
3046 newpotion = ITEM::create(ITEM_BOTTLE, false, true);
3047
3048 int netnewcharges;
3049
3050 netnewcharges = rand_dice(1,
3051 6 - isCursed()*3,
3052 isBlessed()*3);
3053
3054 // Charge the wand.
3055 dippee->addCharges(netnewcharges);
3056 // New number of charges is not known.
3057 dippee->clearChargesKnown();
3058
3059 // Id it...
3060 markIdentified();
3061 // Delete self
3062 delete this;
3063
3064 possible = true;
3065 consumed = true;
3066 }
3067 else if (dippee->getMagicType() == MAGICTYPE_SPELLBOOK)
3068 {
3069 // Dipping a spellbook into a mana potion recharges it.
3070 dipper->formatAndReport("The pages of %IU swiftly absorb the liquid!", dippee);
3071
3072 newpotion = ITEM::create(ITEM_BOTTLE, false, true);
3073
3074 int netnewcharges;
3075
3076 netnewcharges = rand_dice(1,
3077 3 - isCursed()*2,
3078 isBlessed());
3079
3080 // Charge the spellbook.
3081 dippee->addCharges(netnewcharges);
3082 // New number of charges is not known.
3083 dippee->clearChargesKnown();
3084
3085 // Id it...
3086 markIdentified();
3087 // Delete self
3088 delete this;
3089
3090 possible = true;
3091 consumed = true;
3092 }
3093 else
3094 {
3095 // Fail the dip.
3096 dipper->reportMessage("Nothing happens.");
3097 possible = true;
3098 consumed = true;
3099 }
3100 break;
3101
3102 case POTION_CURE:
3103 if (dippee->isPoisoned())
3104 {
3105 dippee->makePoisoned(POISON_NONE, 0);
3106
3107 // If the user knew it was poisoned, they get
3108 // a message. Otherwise, no visible effect.
3109 // Well, the potion is removed that is a bit
3110 // of a give away...
3111 if (dippee->isKnownPoison())
3112 {
3113 buf = MOB::formatToString("%MU <M:dip> %IU into %U.",
3114 0, this, dipper, dippee);
3115 dipper->reportMessage(buf);
3116
3117 buf = MOB::formatToString("The poison on %U is neutralized by %IU.",
3118 0, dippee, 0, this);
3119 dipper->reportMessage(buf);
3120 }
3121 else
3122 {
3123 buf = MOB::formatToString("%MU <M:dip> %IU into %U. Nothing happens.",
3124 0, this, dipper, dippee);
3125 dipper->reportMessage(buf);
3126 }
3127
3128 newpotion = ITEM::create(ITEM_BOTTLE, false, true);
3129
3130 // Id it...
3131 markIdentified();
3132 // Delete self
3133 delete this;
3134
3135 possible = true;
3136 consumed = true;
3137 }
3138 else
3139 {
3140 // Fail the dip.
3141 buf = MOB::formatToString("%MU <M:dip> %IU into %U. Nothing happens.",
3142 0, this, dipper, dippee);
3143 dipper->reportMessage(buf);
3144 possible = true;
3145 consumed = true;
3146 }
3147 break;
3148
3149 case POTION_ENLIGHTENMENT:
3150 if (dippee->isFullyIdentified())
3151 {
3152 // No id, no effect.
3153 buf = MOB::formatToString("%MU <M:dip> %IU into %U. Nothing happens.",
3154 0, this, dipper, dippee);
3155 dipper->reportMessage(buf);
3156 possible = true;
3157 consumed = true;
3158 }
3159 else
3160 {
3161 buf = MOB::formatToString("%MU <M:dip> %IU into %U.",
3162 0, this, dipper, dippee);
3163 dipper->reportMessage(buf);
3164
3165 // Identify the dippee.
3166 if (dipper->isAvatar())
3167 dippee->markIdentified();
3168 dipper->formatAndReport("%U <gain> insight into the nature of %IU.", dippee);
3169
3170 newpotion = ITEM::create(ITEM_BOTTLE, false, true);
3171
3172 // Id it...
3173 markIdentified();
3174 // Delete self
3175 delete this;
3176
3177 possible = true;
3178 consumed = true;
3179 }
3180 break;
3181
3182 case POTION_POISON:
3183 {
3184 POISON_NAMES poison;
3185
3186 buf = MOB::formatToString("%MU <M:dip> %IU into %U.",
3187 0, this, dipper, dippee);
3188 dipper->reportMessage(buf);
3189
3190
3191 if (dippee->hasIntrinsic(INTRINSIC_RESISTPOISON))
3192 {
3193 // Fail the dip.
3194 dipper->reportMessage("Nothing happens.");
3195 possible = true;
3196 consumed = true;
3197 break;
3198 }
3199
3200 buf.sprintf("A thick slime coats %s.",
3201 dippee->getAccusative());
3202 dipper->reportMessage(buf);
3203
3204 if (isCursed())
3205 poison = POISON_MILD;
3206 else if (isBlessed())
3207 poison = POISON_STRONG;
3208 else
3209 poison = POISON_NORMAL;
3210
3211 dippee->makePoisoned(poison, rand_dice(1, 3, 0));
3212 if (dipper == MOB::getAvatar())
3213 dippee->markPoisonKnown();
3214
3215 newpotion = ITEM::create(ITEM_BOTTLE, false, true);
3216 // Id it...
3217 markIdentified();
3218 // Delete self
3219 delete this;
3220
3221 possible = true;
3222 consumed = true;
3223 break;
3224 }
3225
3226 case POTION_ACID:
3227 {
3228 buf = MOB::formatToString("%MU <M:dip> %IU into %U.",
3229 0, this, dipper, dippee);
3230 dipper->reportMessage(buf);
3231
3232 if (dippee->dissolve(dipper))
3233 {
3234 newitem = 0;
3235 newpotion = ITEM::create(ITEM_BOTTLE, false, true);
3236 // Id it...
3237 markIdentified();
3238 delete dippee;
3239 // Delete self
3240 delete this;
3241 }
3242 else
3243 {
3244 dipper->reportMessage("Nothing happens.");
3245 }
3246
3247 possible = true;
3248 consumed = true;
3249 break;
3250 }
3251
3252 case NUM_POTIONS:
3253 {
3254 UT_ASSERT(0);
3255 buf.sprintf("Unknown potion type %d", getMagicClass());
3256 dipper->reportMessage(buf);
3257 break;
3258 }
3259 }
3260 break;
3261 }
3262
3263 // Other magic types do nothing:
3264 case MAGICTYPE_NONE:
3265 case MAGICTYPE_HELM:
3266 case MAGICTYPE_BOOTS:
3267 case MAGICTYPE_WAND:
3268 case MAGICTYPE_SCROLL:
3269 case MAGICTYPE_AMULET:
3270 case MAGICTYPE_RING:
3271 case MAGICTYPE_SPELLBOOK:
3272 case MAGICTYPE_STAFF:
3273 case NUM_MAGICTYPES:
3274 break;
3275 }
3276
3277 // Check for specific items...
3278 // Only do this if previous checks failed.
3279 if (!consumed)
3280 {
3281 switch ((ITEM_NAMES) myDefinition)
3282 {
3283 case ITEM_WATER:
3284 {
3285 // If the potion is holy, make new guy holy.
3286 // If potion evil, make new guy evil.
3287 // If potion plain, just wet new guy.
3288 if (isCursed())
3289 {
3290 dipper->formatAndReport("%IU <I:glow> black.", dippee);
3291 dippee->makeCursed();
3292 if (dipper->isAvatar())
3293 dippee->markCursedKnown();
3294 possible = true;
3295 consumed = true;
3296 newpotion = ITEM::create(ITEM_BOTTLE, false, true);
3297 delete this;
3298 }
3299 else if (isBlessed())
3300 {
3301 dipper->formatAndReport("%IU <I:glow> blue.", dippee);
3302 dippee->makeBlessed();
3303 if (dipper->isAvatar())
3304 dippee->markCursedKnown();
3305 possible = true;
3306 consumed = true;
3307 newpotion = ITEM::create(ITEM_BOTTLE, false, true);
3308 delete this;
3309 }
3310 else
3311 {
3312 bool interesting;
3313 if (dippee->douse(dipper, &interesting))
3314 {
3315 newitem = 0;
3316 delete dippee;
3317 newpotion = ITEM::create(ITEM_BOTTLE, false, true);
3318 delete this;
3319 }
3320 else if (interesting)
3321 {
3322 newpotion = ITEM::create(ITEM_BOTTLE, false, true);
3323 delete this;
3324 }
3325
3326 possible = true;
3327 consumed = true;
3328 }
3329 }
3330 default:
3331 break;
3332 }
3333 }
3334
3335 if (!possible)
3336 {
3337 // No one managed to do anything...
3338 buf = MOB::formatToString("%MU cannot figure out how to dip %IU into %U.",
3339 0, this, dipper, dippee);
3340 dipper->reportMessage(buf);
3341 }
3342
3343 return consumed;
3344 }
3345
3346 bool
actionZap(MOB * zapper,int dx,int dy,int dz)3347 ITEM::actionZap(MOB *zapper, int dx, int dy, int dz)
3348 {
3349 bool consumed = false;
3350 bool possible = false;
3351 bool targetself = false;
3352 const char *directionflavour = 0;
3353 bool destroythis = false;
3354 bool doid = false;
3355 bool forceid = false;
3356 BUF buf;
3357 // Zapper position.
3358 int zx, zy;
3359
3360 zx = zapper->getX();
3361 zy = zapper->getY();
3362 if (!dx && !dy && !dz)
3363 {
3364 targetself = true;
3365 directionflavour = zapper->getReflexive();
3366 }
3367 if (dz)
3368 {
3369 if (dz < 0)
3370 directionflavour = "the floor";
3371 else
3372 directionflavour = "the ceiling";
3373 }
3374
3375 // Try the magic type.
3376 switch ((MAGICTYPE_NAMES)getMagicType())
3377 {
3378 case MAGICTYPE_WAND:
3379 {
3380 // Print prequel message..
3381 zapper->formatAndReport("%U <zap> %IU%B1%B2.",
3382 this,
3383 directionflavour ? " at " : "",
3384 directionflavour ? directionflavour : "");
3385
3386 // Determine if we have enough charges...
3387 if (myCharges == 0)
3388 {
3389 // Check for wrest-last-charge
3390 // 1 in 20 to be nice as it is much harder UI wise
3391 // than in Nethack.
3392 // I have since reduced this even farther as the goal
3393 // is to make it not a tactical decision.
3394 if (!rand_choice(7))
3395 {
3396 zapper->formatAndReport("%U <wrest> one last charge.");
3397 destroythis = true;
3398 }
3399 else
3400 {
3401 zapper->reportMessage("Nothing happens.");
3402 possible = true;
3403 consumed = true;
3404 // To be nice, we record the wand is out of charges.
3405 markChargesKnown();
3406 // EARLY EXIT:
3407 break;
3408 }
3409 }
3410
3411
3412 // Getting here implies we successfully zapped.
3413 if (myCharges)
3414 myCharges--;
3415
3416 if (!destroythis && !isKnownCharges())
3417 {
3418 if (zapper->isAvatar())
3419 {
3420 if (!rand_choice(10))
3421 {
3422 markChargesKnown();
3423 buf = MOB::formatToString("%U <know> %r %Iu better. ",
3424 zapper, 0, 0, this);
3425 // This is important enough to be broadcast.
3426 msg_announce(gram_capitalize(buf));
3427 }
3428 }
3429 }
3430
3431 // Check to see if it is cursed and fizzled on the user.
3432 if (isCursed() && rand_chance(10))
3433 {
3434 doid = false;
3435 // This is a pretty simple wand to do...
3436 zapper->formatAndReport("%IU <I:fizzle>.", this);
3437 if (zapper->isAvatar())
3438 {
3439 // We now know it is cursed
3440 markCursedKnown();
3441 }
3442 possible = true;
3443 consumed = true;
3444 break;
3445 }
3446
3447 // Apply any of our artifact based powers.
3448 if (isArtifact())
3449 {
3450 const ARTIFACT *art;
3451 MOB *target;
3452
3453 target = glbCurLevel->getMob(zx + dx,
3454 zy + dy);
3455 if (target)
3456 {
3457 art = getArtifact();
3458
3459 const char *intrinsics;
3460
3461 intrinsics = art->intrinsics;
3462 while (intrinsics && *intrinsics)
3463 {
3464 target->setTimedIntrinsic(zapper,
3465 (INTRINSIC_NAMES) *intrinsics,
3466 rand_dice(3+isBlessed()-isCursed(), 50, 100));
3467 intrinsics++;
3468 }
3469 }
3470 }
3471
3472 switch ((WAND_NAMES) getMagicClass())
3473 {
3474 case WAND_NOTHING:
3475 {
3476 doid = false;
3477 // This is a pretty simple wand to do...
3478 zapper->reportMessage("Nothing happens.");
3479 possible = true;
3480 consumed = true;
3481 break;
3482 }
3483 case WAND_CREATEMONSTER:
3484 {
3485 doid = false;
3486 possible = true;
3487 consumed = true;
3488
3489 if (glbCurLevel->getMob(zx + dx,
3490 zy + dy))
3491 {
3492 zapper->reportMessage("Nothing happens.");
3493 break;
3494 }
3495
3496 MOB *mob;
3497
3498 mob = MOB::createNPC(100);
3499
3500 if (!mob->canMove(zx + dx,
3501 zy + dy,
3502 true))
3503 {
3504 zapper->reportMessage("Nothing happens.");
3505 delete mob;
3506 break;
3507 }
3508
3509 mob->move(zx + dx,
3510 zy + dy,
3511 true);
3512 glbCurLevel->registerMob(mob);
3513
3514 mob->formatAndReport("%U <appear> out of thin air!");
3515
3516 // We have a chance of getting a tamed creature
3517 // based on the BUC of the wand.
3518 int tamechance = 5;
3519 int hostilechance = 0;
3520 if (isBlessed())
3521 tamechance += 15;
3522 if (isCursed())
3523 {
3524 tamechance = 1;
3525 hostilechance = 20;
3526 }
3527
3528 if (rand_chance(tamechance))
3529 {
3530 mob->makeSlaveOf(zapper);
3531 }
3532 else if (rand_chance(hostilechance))
3533 {
3534 // The creature turns on the zapper!
3535 mob->setAITarget(zapper);
3536 }
3537
3538 // Imbue the created creature with our wands
3539 // abilities.
3540 if (isArtifact())
3541 {
3542 const ARTIFACT *art;
3543 art = getArtifact();
3544
3545 const char *intrinsics;
3546
3547 intrinsics = art->intrinsics;
3548 while (intrinsics && *intrinsics)
3549 {
3550 mob->setTimedIntrinsic(zapper,
3551 (INTRINSIC_NAMES) *intrinsics,
3552 rand_dice(3+isBlessed()-isCursed(), 50, 100));
3553 intrinsics++;
3554 }
3555 }
3556
3557 zapper->pietyZapWand(this);
3558
3559 doid = true;
3560 break;
3561 }
3562
3563 case WAND_CREATETRAP:
3564 {
3565 possible = true;
3566 consumed = true;
3567 doid = glbCurLevel->createTrap(zx + dx,
3568 zy + dy,
3569 zapper);
3570 if (doid)
3571 {
3572 zapper->formatAndReport("%U <create> a hidden surprise.");
3573 zapper->pietyZapWand(this);
3574 }
3575 else
3576 zapper->reportMessage("Nothing happens.");
3577 break;
3578 }
3579 case WAND_LIGHT:
3580 {
3581 MOB *target;
3582 doid = true;
3583 possible = true;
3584 consumed = true;
3585 ourZapper.setMob(zapper);
3586 zapper->formatAndReport("A lit field surrounds %U.");
3587 glbCurLevel->applyFlag(SQUAREFLAG_LIT,
3588 zx + dx,
3589 zy + dy,
3590 5, false, true);
3591 zapper->pietyZapWand(this);
3592
3593 // Blind the target, if any.
3594 if (!dz)
3595 {
3596 zapper = ourZapper.getMob();
3597
3598 target = glbCurLevel->getMob(zx+dx, zy+dy);
3599 if (target)
3600 {
3601 // Wands always apply damage...
3602 target->receiveDamage(ATTACK_LIGHTWAND,
3603 zapper,
3604 this, 0,
3605 ATTACKSTYLE_WAND);
3606 }
3607 }
3608
3609 zapper = ourZapper.getMob();
3610 break;
3611 }
3612
3613 case WAND_TELEPORT:
3614 {
3615 MOB *tele = 0;
3616
3617 possible = true;
3618 consumed = true;
3619
3620 // If it went vertical, nothing happens..
3621 if (!dz)
3622 {
3623 tele = glbCurLevel->getMob(zx + dx,
3624 zy + dy);
3625 }
3626 if (tele)
3627 {
3628 // Teleport the monster
3629 ourZapper.setMob(zapper);
3630 tele->actionTeleport();
3631 zapper = ourZapper.getMob();
3632 if (zapper)
3633 zapper->pietyZapWand(this);
3634 doid = true;
3635 }
3636 // Tracked down a bug here.
3637 // Bug was: "TODO: Teleport items"
3638 // :>
3639 // Still in the airport lounge enjoying free beer.
3640 // Fear this code!
3641 // No doubt as a result of aforementioned free beer,
3642 // I foolishly dereferenced zapper->getX() to get the
3643 // tile location. Zapper might be tele in which case
3644 // it might very well be dead. In any case, this
3645 // would end up teleporting items at your destination
3646 // square rather than source square!
3647 ITEMSTACK stack;
3648 glbCurLevel->getItemStack(stack, zx + dx,
3649 zy + dy);
3650 if (stack.entries())
3651 {
3652 ITEM *item;
3653
3654 doid = true;
3655 // We don't want to double count this zap.
3656 if (zapper && !tele)
3657 zapper->pietyZapWand(this);
3658
3659 for (int i = 0; i < stack.entries(); i++)
3660 {
3661 item = stack(i);
3662 item->randomTeleport(glbCurLevel, true, zapper);
3663 }
3664 }
3665
3666 if (!doid && zapper)
3667 {
3668 zapper->reportMessage("Nothing happens.");
3669 }
3670 break;
3671 }
3672
3673 case WAND_POLYMORPH:
3674 {
3675 MOB *poly = 0;
3676 ITEM *item = 0;
3677
3678 possible = true;
3679 consumed = true;
3680
3681 // If it went vertical, nothing happens..
3682 if (!dz)
3683 {
3684 poly = glbCurLevel->getMob(zx + dx,
3685 zy + dy);
3686 }
3687 if (poly)
3688 {
3689 MOBREF zapmobref;
3690
3691 zapmobref.setMob(zapper);
3692
3693 // Polymorph the monster
3694 // If the monster was us, zapper may now point
3695 // to the base object rather than the top object.
3696 // Thus we track the ref.
3697 poly->actionPolymorph();
3698 zapper = zapmobref.getMob();
3699 doid = true;
3700 }
3701 // Zapping the ground means polying on the ground.
3702 // (doid here means did something)
3703 if (dz <= 0 && !doid)
3704 {
3705 item = glbCurLevel->getItem(zx + dx,
3706 zy + dy);
3707 }
3708 if (item)
3709 {
3710 ITEM *newitem;
3711
3712 newitem = item->polymorph(zapper);
3713
3714 if (newitem)
3715 {
3716 if (item->getStackCount() > 1)
3717 item = item->splitStack(1);
3718 else
3719 glbCurLevel->dropItem(item);
3720
3721 glbCurLevel->acquireItem(newitem,
3722 zx + dx,
3723 zy + dy,
3724 zapper);
3725 delete item;
3726 }
3727
3728 doid = true;
3729 }
3730 if (!doid && zapper)
3731 {
3732 zapper->reportMessage("Nothing happens.");
3733 }
3734 else if (zapper)
3735 {
3736 zapper->pietyZapWand(this);
3737 }
3738 break;
3739 }
3740
3741 case WAND_INVISIBLE:
3742 {
3743 MOB *invis = 0;
3744 bool getpiety = false;
3745
3746 possible = true;
3747 consumed = true;
3748
3749
3750 // If it went vertical, nothing happens..
3751 if (!dz)
3752 {
3753 invis = glbCurLevel->getMob(zx + dx,
3754 zy + dy);
3755 }
3756 if (invis)
3757 {
3758 // Invisible the monster
3759 // We notice something if the monster
3760 // is not already invisible.
3761 bool quietchange = false;
3762 if (!invis->hasIntrinsic(INTRINSIC_INVISIBLE))
3763 {
3764 if (invis != MOB::getAvatar())
3765 {
3766 invis->formatAndReport("%U <disappear> from sight.");
3767 }
3768 doid = true;
3769
3770 // We must determine if we can see the monster
3771 // *before* we make it invisible. If we wait
3772 // for normal doid check, the monster may
3773 // be invisible which prevents us from
3774 // iding the wand.
3775 if (zapper == MOB::getAvatar() ||
3776 (MOB::getAvatar() &&
3777 MOB::getAvatar()->canSense(zapper)))
3778 {
3779 forceid = true;
3780 }
3781 }
3782 else
3783 {
3784 quietchange = true;
3785 }
3786 invis->setTimedIntrinsic(zapper,
3787 INTRINSIC_INVISIBLE,
3788 rand_dice(3+isBlessed()-isCursed(), 50, 100),
3789 quietchange);
3790 getpiety = true;
3791 }
3792 SQUARE_NAMES tile;
3793
3794 tile = glbCurLevel->getTileSafe(zx+dx, zy+dy);
3795
3796 // Check to see if we can make a door invisible
3797 switch (tile)
3798 {
3799 // Open doors need to be closed first to avoid
3800 // this nerfing the knock spell.
3801 case SQUARE_DOOR:
3802 case SQUARE_BLOCKEDDOOR:
3803 doid = true;
3804 if (zapper == MOB::getAvatar() ||
3805 glbCurLevel->hasFOV(zx+dx, zy+dy))
3806 {
3807 forceid = true;
3808 }
3809 glbCurLevel->reportMessage("The door fades into the wall.", zx+dx, zy+dy);
3810 glbCurLevel->setTile(zx+dx, zy+dy, SQUARE_SECRETDOOR);
3811 getpiety = true;
3812 break;
3813 case SQUARE_FLOORSMOKEVENT:
3814 case SQUARE_PATHSMOKEVENT:
3815 case SQUARE_FLOORPOISONVENT:
3816 case SQUARE_PATHPOISONVENT:
3817 case SQUARE_FLOORTELEPORTER:
3818 case SQUARE_PATHTELEPORTER:
3819 case SQUARE_FLOORHOLE:
3820 case SQUARE_PATHHOLE:
3821 case SQUARE_FLOORPIT:
3822 case SQUARE_PATHPIT:
3823 case SQUARE_FLOORSPIKEDPIT:
3824 case SQUARE_PATHSPIKEDPIT:
3825 {
3826 BUF buf;
3827
3828 doid = true;
3829 if (zapper == MOB::getAvatar() ||
3830 glbCurLevel->hasFOV(zx+dx, zy+dy))
3831 {
3832 forceid = true;
3833 }
3834
3835 buf.sprintf("%s fades into the ground.",
3836 glb_squaredefs[tile].description);
3837 glbCurLevel->reportMessage(buf, zx+dx, zy+dy);
3838 glbCurLevel->setTile(zx+dx, zy+dy, (SQUARE_NAMES) glb_squaredefs[tile].invissquare);
3839 getpiety = true;
3840 break;
3841 }
3842
3843 default:
3844 // Do nothing
3845 break;
3846 }
3847 if (!doid)
3848 {
3849 zapper->reportMessage("Nothing happens.");
3850 }
3851 if (getpiety)
3852 zapper->pietyZapWand(this);
3853 break;
3854 }
3855
3856 case WAND_SPEED:
3857 {
3858 MOB *speed = 0;
3859
3860 possible = true;
3861 consumed = true;
3862
3863 // If it went vertical, nothing happens..
3864 if (!dz)
3865 {
3866 speed = glbCurLevel->getMob(zx + dx,
3867 zy + dy);
3868 }
3869 if (speed)
3870 {
3871 // Quicken the monster
3872 // We notice something if the monster
3873 // is not already quick.
3874 if (!speed->hasIntrinsic(INTRINSIC_QUICK))
3875 {
3876 doid = true;
3877 }
3878 speed->setTimedIntrinsic(zapper,
3879 INTRINSIC_QUICK,
3880 rand_dice(3+isBlessed()-isCursed(), 50, 100));
3881 zapper->pietyZapWand(this);
3882 }
3883 if (!doid)
3884 {
3885 zapper->reportMessage("Nothing happens.");
3886 }
3887 break;
3888 }
3889
3890 case WAND_SLOW:
3891 {
3892 MOB *slow = 0;
3893
3894 possible = true;
3895 consumed = true;
3896
3897 // If it went vertical, nothing happens..
3898 if (!dz)
3899 {
3900 slow = glbCurLevel->getMob(zx + dx,
3901 zy + dy);
3902 }
3903 if (slow)
3904 {
3905 // Quicken the monster
3906 // We notice something if the monster
3907 // is not already quick.
3908 if (!slow->hasIntrinsic(INTRINSIC_SLOW))
3909 {
3910 doid = true;
3911 }
3912 slow->setTimedIntrinsic(zapper,
3913 INTRINSIC_SLOW,
3914 rand_dice(3+isBlessed()-isCursed(), 50, 100));
3915 zapper->pietyZapWand(this);
3916 }
3917 if (!doid)
3918 {
3919 zapper->reportMessage("Nothing happens.");
3920 }
3921 break;
3922 }
3923
3924 case WAND_FIRE:
3925 case WAND_ICE:
3926 {
3927 possible = true;
3928 consumed = true;
3929 doid = true; // This is pretty obvious.
3930
3931 ourZapper.setMob(zapper);
3932 zapper->pietyZapWand(this);
3933 if (targetself || dz)
3934 {
3935 bool dosquareeffect = false;
3936
3937 // While underwater, always works.
3938 if (zapper->hasIntrinsic(INTRINSIC_SUBMERGED))
3939 dosquareeffect = true;
3940
3941 if (dz && !dosquareeffect)
3942 {
3943 // Shot downwards? affect square
3944 if (dz < 0)
3945 dosquareeffect = true;
3946 zapper->reportMessage("The ray bounces.");
3947 }
3948 // Affect the square.
3949 if (dosquareeffect &&
3950 (WAND_NAMES) getMagicClass() == WAND_ICE)
3951 {
3952 glbCurLevel->freezeSquare(zx, zy, zapper);
3953 }
3954 if (dosquareeffect &&
3955 (WAND_NAMES) getMagicClass() == WAND_FIRE)
3956 {
3957 glbCurLevel->burnSquare(zx, zy, zapper);
3958 }
3959 // Apply to self..
3960 zapCallback(zx, zy);
3961 }
3962 else
3963 {
3964 glbCurLevel->fireRay(zx, zy,
3965 dx, dy,
3966 6,
3967 MOVE_STD_FLY, true,
3968 zapCallbackStatic,
3969 this);
3970
3971 }
3972 zapper = ourZapper.getMob();
3973 break;
3974 }
3975
3976 case WAND_SLEEP:
3977 {
3978 possible = true;
3979 consumed = true;
3980 doid = true; // This is pretty obvious.
3981
3982 ourZapper.setMob(zapper);
3983 zapper->pietyZapWand(this);
3984 if (targetself || dz)
3985 {
3986 if (dz)
3987 {
3988 zapper->reportMessage("The ray bounces.");
3989 }
3990
3991 // Apply to self..
3992 zapCallback(zx, zy);
3993 }
3994 else
3995 {
3996 glbCurLevel->fireRay(zx, zy,
3997 dx, dy,
3998 6,
3999 MOVE_STD_FLY, true,
4000 zapCallbackStatic,
4001 this);
4002
4003 }
4004 zapper = ourZapper.getMob();
4005 break;
4006 }
4007
4008 case WAND_DIGGING:
4009 {
4010 possible = true;
4011 consumed = true;
4012 doid = true; // Wands of digging autoid.
4013
4014 ourZapper.setMob(zapper);
4015 zapper->pietyZapWand(this);
4016 zapper->actionDig(dx, dy, dz, 6, true);
4017 zapper = ourZapper.getMob();
4018
4019 break;
4020 }
4021
4022 case NUM_WANDS:
4023 {
4024 UT_ASSERT(!"Unhandled wand type!");
4025 break;
4026 }
4027 }
4028 }
4029
4030 // Other MAGICTYPES that, of course, do nothing...
4031 case MAGICTYPE_NONE:
4032 case MAGICTYPE_POTION:
4033 case MAGICTYPE_SPELLBOOK:
4034 case MAGICTYPE_SCROLL:
4035 case MAGICTYPE_RING:
4036 case MAGICTYPE_HELM:
4037 case MAGICTYPE_BOOTS:
4038 case MAGICTYPE_AMULET:
4039 case MAGICTYPE_STAFF:
4040 case NUM_MAGICTYPES:
4041 break;
4042 }
4043
4044 // Try the definition.
4045 if (!possible)
4046 {
4047 switch ((ITEM_NAMES) myDefinition)
4048 {
4049 case ITEM_LIGHTNINGRAPIER:
4050 {
4051 possible = true;
4052 consumed = true;
4053 doid = false; // Already ided.
4054
4055 ourZapper.setMob(zapper);
4056 // Rapiers still count as wands in terms of piety.
4057 zapper->pietyZapWand(this);
4058
4059 zapper = ourZapper.getMob();
4060
4061 // Electrocution by lighting rapiers
4062 // only occurs if fired while submerged,
4063 // not if merely fired at the ground.
4064 if (zapper && zapper->hasIntrinsic(INTRINSIC_SUBMERGED))
4065 {
4066 // Drain our charges..
4067 zapper->formatAndReport("%R %Iu violently <I:discharge>.", this);
4068 myCharges = 0;
4069 setDefinition(ITEM_RAPIER);
4070 zapper->rebuildAppearance();
4071 // Affect the square.
4072 glbCurLevel->electrocuteSquare(zx, zy, zapper);
4073
4074 // All done, your targetting has no effect
4075 break;
4076 }
4077 zapper = ourZapper.getMob();
4078
4079 // Getting here implies we successfully zapped.
4080 if (myCharges > 10)
4081 myCharges -= 10;
4082 else
4083 myCharges = 0;
4084 if (!myCharges)
4085 {
4086 if (zapper)
4087 zapper->formatAndReport("%R %Iu <I:power> down.", this);
4088 setDefinition(ITEM_RAPIER);
4089 if (zapper)
4090 zapper->rebuildAppearance();
4091 }
4092
4093 if (myCharges && !isKnownCharges())
4094 {
4095 if (zapper && zapper->isAvatar())
4096 {
4097 if (!rand_choice(10))
4098 {
4099 markChargesKnown();
4100 buf = MOB::formatToString("%U <know> %r %Iu better. ",
4101 zapper, 0, 0, this);
4102 // This is important enough to be broadcast.
4103 msg_announce(gram_capitalize(buf));
4104 }
4105 }
4106 }
4107 if (targetself || dz)
4108 {
4109 if (dz && zapper)
4110 {
4111 zapper->reportMessage("The ray bounces.");
4112 }
4113 // Apply to self..
4114 zapCallback(zx, zy);
4115 }
4116 else
4117 {
4118 glbCurLevel->fireRay(zx, zy,
4119 dx, dy,
4120 6,
4121 MOVE_STD_FLY, true,
4122 zapCallbackStatic,
4123 this);
4124
4125 }
4126 zapper = ourZapper.getMob();
4127 break;
4128 }
4129 default:
4130 // Nothing...
4131 break;
4132 }
4133 }
4134
4135 // Check if zapper is dead.
4136 if (zapper)
4137 {
4138 if (zapper->hasIntrinsic(INTRINSIC_DEAD))
4139 {
4140 zapper = 0;
4141 }
4142 }
4143
4144 if (!possible && zapper)
4145 {
4146 // Noone figured out how to zap it.
4147 zapper->formatAndReport("%U cannot figure out how to zap %IU.",
4148 this);
4149 }
4150
4151 // Check if we should id this...
4152 // Ideally you could id a wand by a creature killing itself with it,
4153 // but that makes the canSense call rather difficult.
4154 if (doid && zapper)
4155 {
4156 if (forceid ||
4157 zapper == MOB::getAvatar() ||
4158 (MOB::getAvatar() && MOB::getAvatar()->canSense(zapper)))
4159 {
4160 // We only id type, not the particulars!
4161 markClassKnown();
4162 }
4163 }
4164
4165 // If we are to destroy this, do so...
4166 // Wands that crumble to dust with the same charge that kills their
4167 // owner get a saving throw.
4168 // (By saving throw, I mean that they don't get destroyed :>)
4169 if (destroythis && zapper)
4170 {
4171 ITEM *drop;
4172
4173 zapper->formatAndReport("%IU <I:crumble> to dust.", this);
4174
4175 drop = zapper->dropItem(getX(), getY());
4176 UT_ASSERT(drop == this);
4177 delete this;
4178 }
4179
4180 return consumed;
4181 }
4182
4183 bool
grenadeCallbackStatic(int x,int y,bool final,void * data)4184 ITEM::grenadeCallbackStatic(int x, int y, bool final, void *data)
4185 {
4186 return ((ITEM *)data)->grenadeCallback(x, y);
4187 }
4188
4189 bool
grenadeCallback(int x,int y)4190 ITEM::grenadeCallback(int x, int y)
4191 {
4192 MOB *victim;
4193 BUF buf;
4194
4195 // Get the mob at this location.
4196 victim = glbCurLevel->getMob(x, y);
4197
4198 // Magic potions..
4199 if (getMagicType() == MAGICTYPE_POTION)
4200 {
4201 POTION_NAMES potion;
4202
4203 potion = (POTION_NAMES) getMagicClass();
4204
4205 switch (potion)
4206 {
4207 case POTION_HEAL:
4208 {
4209 if (!victim)
4210 break;
4211
4212 int heal;
4213
4214 if (isBlessed())
4215 heal = rand_dice(1, 10, 10);
4216 else if (isCursed())
4217 heal = rand_dice(1, 10, 0);
4218 else
4219 heal = rand_dice(1, 20, 0);
4220
4221 if (victim->receiveHeal(heal, ourZapper.getMob()))
4222 {
4223 victim->formatAndReport("%U <look> healthier.");
4224
4225 if (victim->isAvatar() ||
4226 (MOB::getAvatar() &&
4227 MOB::getAvatar()->canSense(victim)))
4228 markIdentified();
4229 }
4230 break;
4231 }
4232
4233 case POTION_MANA:
4234 {
4235 if (!victim)
4236 break;
4237
4238 int magic;
4239
4240 if (isBlessed())
4241 magic = rand_dice(1, 20, 20);
4242 else if (isCursed())
4243 magic = rand_dice(1, 20, 0);
4244 else
4245 magic = rand_dice(1, 40, 0);
4246
4247 if (victim->receiveMana(magic, ourZapper.getMob()))
4248 {
4249 victim->formatAndReport("%U <look> recharged.");
4250
4251 if (victim->isAvatar() ||
4252 (MOB::getAvatar() &&
4253 MOB::getAvatar()->canSense(victim)))
4254 markIdentified();
4255 }
4256 break;
4257 }
4258
4259 case POTION_ACID:
4260 {
4261 // Check to see if we upconvert smoke into acid.
4262 if (glbCurLevel->getSmoke(x, y) == SMOKE_SMOKE)
4263 {
4264 glbCurLevel->reportMessage("The smoke is acidified.", x, y);
4265 if (MOB::getAvatar() &&
4266 glbCurLevel->hasLOS(MOB::getAvatar()->getX(),
4267 MOB::getAvatar()->getY(),
4268 x, y))
4269 {
4270 markIdentified();
4271 }
4272 glbCurLevel->setSmoke(x, y, SMOKE_ACID, ourZapper.getMob());
4273 }
4274 if (!victim)
4275 break;
4276
4277 // Do identify if the avatar can sense this square.
4278 if (glbCurLevel->hasFOV(x, y))
4279 markIdentified();
4280
4281 victim->receiveDamage(ATTACK_ACIDPOTION, ourZapper.getMob(), this, 0,
4282 ATTACKSTYLE_MISC);
4283 break;
4284 }
4285
4286 case POTION_GREEKFIRE:
4287 {
4288 // Check for burning squares. We identify if the
4289 // avatar witnesses this.
4290 if (glbCurLevel->burnSquare(x, y, ourZapper.getMob()))
4291 {
4292 if (glbCurLevel->hasFOV(x, y))
4293 markIdentified();
4294 }
4295
4296 // Burning the square may kill our victim,
4297 // so we refetch.
4298 victim = glbCurLevel->getMob(x, y);
4299 if (!victim)
4300 break;
4301
4302 // Do identify if the avatar can sense this square.
4303 if (glbCurLevel->hasFOV(x, y))
4304 markIdentified();
4305
4306 victim->receiveDamage(ATTACK_GREEKFIREPOTION,
4307 ourZapper.getMob(), this, 0,
4308 ATTACKSTYLE_MISC);
4309 break;
4310 }
4311
4312 case POTION_SMOKE:
4313 {
4314 // We can only put smoke where things can fly.
4315 if (!(glb_squaredefs[glbCurLevel->getTile(x, y)].movetype &
4316 MOVE_STD_FLY))
4317 break;
4318
4319 // Do identify if the avatar can sense this.
4320 if (glbCurLevel->hasFOV(x, y))
4321 markIdentified();
4322
4323 // Make this square smokey.
4324 // If the smoke potion is poisoned, act accordinly
4325 if (isPoisoned())
4326 glbCurLevel->setSmoke(x, y, SMOKE_POISON, ourZapper.getMob());
4327 else
4328 glbCurLevel->setSmoke(x, y, SMOKE_SMOKE, ourZapper.getMob());
4329 break;
4330 }
4331
4332 case POTION_BLIND:
4333 {
4334 if (!victim)
4335 break;
4336
4337 int turns;
4338
4339 // You get 3d20 turns of blindness.
4340 if (isBlessed())
4341 turns = rand_dice(3, 10, 30);
4342 else if (isCursed())
4343 turns = rand_dice(3, 10, 0);
4344 else
4345 turns = rand_dice(3, 20, 0);
4346
4347 // Determine if we notice anything...
4348 if (victim->isAvatar() ||
4349 (MOB::getAvatar() &&
4350 MOB::getAvatar()->canSense(victim)))
4351 {
4352 markIdentified();
4353 }
4354
4355 victim->setTimedIntrinsic(ourZapper.getMob(),
4356 INTRINSIC_BLIND, turns);
4357 break;
4358 }
4359
4360 case POTION_POISON:
4361 {
4362 // Check to see if we upconvert smoke into poison.
4363 if (glbCurLevel->getSmoke(x, y) == SMOKE_SMOKE)
4364 {
4365 glbCurLevel->reportMessage("The smoke is poisoned.", x, y);
4366 if (MOB::getAvatar() &&
4367 glbCurLevel->hasLOS(MOB::getAvatar()->getX(),
4368 MOB::getAvatar()->getY(),
4369 x, y))
4370 {
4371 markIdentified();
4372 }
4373 glbCurLevel->setSmoke(x, y, SMOKE_POISON, ourZapper.getMob());
4374 }
4375
4376 if (!victim)
4377 break;
4378
4379 int turns;
4380 POISON_NAMES poison;
4381
4382 turns = rand_dice(4, 5, 0);
4383 if (isBlessed())
4384 poison = POISON_STRONG;
4385 else if (isCursed())
4386 poison = POISON_MILD;
4387 else
4388 poison = POISON_NORMAL;
4389
4390 victim->setTimedIntrinsic(ourZapper.getMob(), (INTRINSIC_NAMES)
4391 glb_poisondefs[poison].intrinsic,
4392 turns);
4393 // The avatar is exempt as the permament intrinsic
4394 // will duplicate this.
4395 if (!victim->isAvatar())
4396 {
4397 victim->formatAndReport("%U <be> poisoned.");
4398 }
4399
4400 // Determine if we notice anything...
4401 if (victim->isAvatar() ||
4402 (MOB::getAvatar() &&
4403 MOB::getAvatar()->canSense(victim)))
4404 {
4405 markIdentified();
4406 }
4407
4408 break;
4409 }
4410
4411 case POTION_CURE:
4412 {
4413 // Check to see if we downconvert poison smoke into smoke.
4414 if (glbCurLevel->getSmoke(x, y) == SMOKE_POISON)
4415 {
4416 glbCurLevel->reportMessage("The poisoned smoke is neutralized.", x, y);
4417 if (MOB::getAvatar() &&
4418 glbCurLevel->hasLOS(MOB::getAvatar()->getX(),
4419 MOB::getAvatar()->getY(),
4420 x, y))
4421 {
4422 markIdentified();
4423 }
4424
4425 glbCurLevel->setSmoke(x, y, SMOKE_SMOKE, ourZapper.getMob());
4426 }
4427
4428 if (!victim)
4429 break;
4430
4431 if (victim->receiveCure())
4432 {
4433 victim->formatAndReport(
4434 "The poison is expunged from %R veins.");
4435
4436 if (victim->isAvatar() ||
4437 (MOB::getAvatar() &&
4438 MOB::getAvatar()->canSense(victim)))
4439 markIdentified();
4440 }
4441 break;
4442 }
4443
4444 case POTION_ENLIGHTENMENT:
4445 {
4446 if (!victim)
4447 break;
4448
4449 glbShowIntrinsic(victim);
4450 markIdentified();
4451 break;
4452 }
4453
4454 case NUM_POTIONS:
4455 UT_ASSERT(!"Unknown potion class!");
4456 break;
4457 }
4458 }
4459 else
4460 {
4461 // Regular stuff...
4462 switch (getDefinition())
4463 {
4464 case ITEM_WATER:
4465 {
4466 glbCurLevel->douseSquare(x, y, isBlessed(), ourZapper.getMob());
4467
4468 break;
4469 }
4470 case ITEM_BOTTLE:
4471 {
4472 if (!victim)
4473 break;
4474
4475 // Do normal piercing damage
4476 victim->receiveDamage(ATTACK_GLASSFRAGMENTS, ourZapper.getMob(), this,
4477 0, ATTACKSTYLE_THROWN);
4478 break;
4479 }
4480
4481 default:
4482 // Do nothing!
4483 // Not handled currently.
4484 UT_ASSERT(!"Unknown grenade!");
4485 break;
4486 }
4487 }
4488
4489 return true;
4490 }
4491
4492 bool
zapCallbackStatic(int x,int y,bool final,void * data)4493 ITEM::zapCallbackStatic(int x, int y, bool final, void *data)
4494 {
4495 if (final) return false;
4496 return ((ITEM *)data)->zapCallback(x, y);
4497 }
4498
4499 bool
zapCallback(int x,int y)4500 ITEM::zapCallback(int x, int y)
4501 {
4502 MOB *mob;
4503 ATTACK_NAMES attack = ATTACK_NONE;
4504
4505 mob = glbCurLevel->getMob(x, y);
4506 if (mob)
4507 {
4508 if (getMagicType() == MAGICTYPE_WAND)
4509 {
4510 switch ((WAND_NAMES) getMagicClass())
4511 {
4512 case WAND_FIRE:
4513 attack = ATTACK_FIREWAND;
4514 break;
4515 case WAND_ICE:
4516 attack = ATTACK_ICEWAND;
4517 break;
4518 case WAND_DIGGING:
4519 if (mob->getDefinition() == MOB_EARTHELEMENTAL)
4520 {
4521 attack = ATTACK_DIGEARTHELEMENTAL;
4522 }
4523 break;
4524 case WAND_SLEEP:
4525 attack = ATTACK_SLEEPWAND;
4526 break;
4527 default:
4528 break;
4529 }
4530 }
4531 switch (getDefinition())
4532 {
4533 case ITEM_LIGHTNINGRAPIER:
4534 // note it is very important that this can't charge lightning
4535 // rapiers!
4536 attack = ATTACK_ZAPLIGHTNINGRAPIER;
4537 break;
4538 default:
4539 // No, I am not going to list all the items here to take
4540 // advantage of enumeration warnings.
4541 break;
4542 }
4543 if (attack != ATTACK_NONE)
4544 {
4545 mob->receiveDamage(attack, ourZapper.getMob(), this,
4546 0, ATTACKSTYLE_WAND);
4547 }
4548 }
4549
4550 // Special square related stuff...
4551 if (getMagicType() == MAGICTYPE_WAND && getMagicClass() == WAND_DIGGING)
4552 {
4553 SQUARE_NAMES tile;
4554
4555 tile = (SQUARE_NAMES) glbCurLevel->getTile(x, y);
4556 switch (tile)
4557 {
4558 case SQUARE_EMPTY:
4559 case SQUARE_WALL:
4560 {
4561 glbCurLevel->setTile(x, y, SQUARE_CORRIDOR);
4562 break;
4563 }
4564
4565 case SQUARE_DOOR:
4566 case SQUARE_BLOCKEDDOOR:
4567 case SQUARE_SECRETDOOR:
4568 {
4569 // Can't tunnel through these!
4570 return false;
4571 }
4572
4573 default:
4574 break;
4575 }
4576 }
4577
4578 if (getMagicType() == MAGICTYPE_WAND && getMagicClass() == WAND_ICE)
4579 {
4580 // On successful freeze, fizzle out wand.
4581 if (glbCurLevel->freezeSquare(x, y, ourZapper.getMob()))
4582 return false;
4583 }
4584
4585 if (getMagicType() == MAGICTYPE_WAND && getMagicClass() == WAND_FIRE)
4586 {
4587 // Fire wands keep burning :>
4588 glbCurLevel->burnSquare(x, y, ourZapper.getMob());
4589 }
4590
4591 return true;
4592 }
4593
4594 bool
randomTeleport(MAP * map,bool mapownsitem,MOB * teleporter)4595 ITEM::randomTeleport(MAP *map, bool mapownsitem, MOB *teleporter)
4596 {
4597 // Check for tele fixed items.
4598 if (hasIntrinsic(INTRINSIC_TELEFIXED))
4599 {
4600 formatAndReport("%U <shudder>.");
4601 return false;
4602 }
4603 bool teleported = false;
4604 int x, y;
4605 if (map->findRandomLoc(x, y, MOVE_WALK,
4606 true,
4607 false, false,
4608 false, false,
4609 false))
4610 {
4611 // Teleport the item
4612 formatAndReport("%U <teleport>.");
4613 // We lose our mapping information.
4614 markMapped(false);
4615 if (mapownsitem)
4616 map->dropItem(this);
4617 // This ensures our item maps are updated
4618 // and it drops in lava, etc.
4619 map->acquireItem(this, x, y, teleporter);
4620 teleported = true;
4621 }
4622 return teleported;
4623 }
4624
4625 bool
fallInHole(MAP * curLevel,bool mapownsitem,MOB * dropper)4626 ITEM::fallInHole(MAP *curLevel, bool mapownsitem, MOB *dropper)
4627 {
4628 MAP *nextmap;
4629 int x, y;
4630
4631 nextmap = curLevel->getMapDown();
4632 if (!nextmap)
4633 {
4634 formatAndReport("%R fall <be> stopped by an invisible barrier.");
4635 return false;
4636 }
4637
4638 if (!nextmap->findRandomLoc(x, y, MOVE_WALK,
4639 true,
4640 false, false,
4641 false, false,
4642 false))
4643 {
4644 formatAndReport("%U <land> on the edge of a hole.");
4645 return false;
4646 }
4647
4648 formatAndReport("%U <fall> into a hole.");
4649
4650 // We no longer know where it is
4651 markMapped(false);
4652
4653 if (mapownsitem)
4654 curLevel->dropItem(this);
4655 nextmap->acquireItem(this, x, y, dropper);
4656
4657 return true;
4658 }
4659
4660 ITEM *
load(SRAMSTREAM & is)4661 ITEM::load(SRAMSTREAM &is)
4662 {
4663 ITEM *me;
4664 int val;
4665
4666 me = new ITEM();
4667
4668 is.uread(val, 8);
4669 me->myDefinition = val;
4670 me->myName.load(is);
4671 is.uread(val, 8);
4672 val ^= 1;
4673 me->myStackCount = val;
4674 is.uread(val, 8);
4675 me->myX = val;
4676 is.uread(val, 8);
4677 me->myY = val;
4678 is.read(val, 8);
4679 me->myEnchantment = val;
4680 is.uread(val, 8);
4681 me->myCharges = val;
4682 is.uread(val, 8);
4683 me->myPoison = val;
4684 is.uread(val, 8);
4685 me->myPoisonCharges = val;
4686 is.uread(val, 32);
4687 val ^= glb_itemdefs[me->myDefinition].flag1;
4688 me->myFlag1 = (ITEMFLAG1_NAMES) val;
4689
4690 // Read in the corpse mob.
4691 me->myCorpseMob.load(is);
4692 if (!me->myCorpseMob.isNull())
4693 {
4694 MOB *mob;
4695
4696 mob = MOB::load(is);
4697 // myCorpseMob will already point to mob, but might as well
4698 // assert.
4699 UT_ASSERT(mob == me->myCorpseMob.getMob());
4700 }
4701
4702 return me;
4703 }
4704
4705 void
save(SRAMSTREAM & os)4706 ITEM::save(SRAMSTREAM &os)
4707 {
4708 os.write(myDefinition, 8);
4709 myName.save(os);
4710 os.write(myStackCount ^ 1, 8);
4711 os.write(myX, 8);
4712 os.write(myY, 8);
4713 os.write(myEnchantment, 8);
4714 os.write(myCharges, 8);
4715 os.write(myPoison, 8);
4716 os.write(myPoisonCharges, 8);
4717
4718 os.write(myFlag1 ^ glb_itemdefs[myDefinition].flag1, 32);
4719
4720 // Save our corpse mob.
4721 // If it is non-zero, we also will save the mob itself.
4722 MOB *mob;
4723 mob = myCorpseMob.getMob();
4724
4725 myCorpseMob.save(os);
4726 if (mob)
4727 mob->save(os);
4728
4729 // TODO: Save the name???
4730 }
4731
4732 bool
verifyMob() const4733 ITEM::verifyMob() const
4734 {
4735 MOB *mob;
4736 mob = myCorpseMob.getMob();
4737 if (mob)
4738 return mob->verifyMob();
4739 return true;
4740 }
4741
4742 bool
verifyCounterGone(INTRINSIC_COUNTER * counter) const4743 ITEM::verifyCounterGone(INTRINSIC_COUNTER *counter) const
4744 {
4745 MOB *mob;
4746 mob = myCorpseMob.getMob();
4747 if (mob)
4748 return mob->verifyCounterGone(counter);
4749 return true;
4750 }
4751
4752 void
loadGlobal(SRAMSTREAM & is)4753 ITEM::loadGlobal(SRAMSTREAM &is)
4754 {
4755 int i, val;
4756
4757 for (i = 0; i < NUM_ITEMS; i++)
4758 {
4759 is.uread(val, 8);
4760 glb_magicitem[i] = val;
4761 }
4762
4763 for (i = 0; i < NUM_ITEMS; i++)
4764 {
4765 is.uread(val, 8);
4766 glb_itemid[i] = val ? true : false;
4767 }
4768
4769 for (i = 0; i < NUM_ITEMS; i++)
4770 {
4771 char buf[100];
4772 is.readString(buf, 100);
4773 if (glb_itemnames[i])
4774 free(glb_itemnames[i]);
4775 if (buf[0])
4776 glb_itemnames[i] = strdup(buf);
4777 else
4778 glb_itemnames[i] = 0;
4779 }
4780 }
4781
4782 void
saveGlobal(SRAMSTREAM & is)4783 ITEM::saveGlobal(SRAMSTREAM &is)
4784 {
4785 int i, val;
4786
4787 for (i = 0; i < NUM_ITEMS; i++)
4788 {
4789 val = glb_magicitem[i];
4790 is.write(val, 8);
4791 }
4792
4793 for (i = 0; i < NUM_ITEMS; i++)
4794 {
4795 val = glb_itemid[i];
4796 is.write(val, 8);
4797 }
4798 for (i = 0; i < NUM_ITEMS; i++)
4799 {
4800 is.writeString(glb_itemnames[i], 100);
4801 }
4802 }
4803