1 /*
2  *
3  *  Iter Vehemens ad Necem (IVAN)
4  *  Copyright (C) Timo Kiviluoto
5  *  Released under the GNU General
6  *  Public License
7  *
8  *  See LICENSING which should be included
9  *  along with this file for more details
10  *
11  */
12 
13 /* Compiled through slotset.cpp */
14 
15 /* If REMEMBER_SELECTED flag is used, DrawContents() will use this to determine
16    the initial selected item */
17 
18 int stack::Selected;
19 uint stack::StandardPageLength = stack::GetDefaultPageLength();
20 
stack(square * MotherSquare,entity * MotherEntity,ulong Flags)21 stack::stack(square* MotherSquare, entity* MotherEntity, ulong Flags)
22 : Bottom(0), Top(0), MotherSquare(MotherSquare), MotherEntity(MotherEntity),
23   Volume(0), Weight(0), Emitation(0), Flags(Flags), Items(0) { }
~stack()24 stack::~stack() { Clean(true); }
GetSquareUnder() const25 square* stack::GetSquareUnder() const
26 { return !MotherEntity ? MotherSquare : MotherEntity->GetSquareUnderEntity(); }
27 
28 /* Modifies the square index bits of BlitData.CustomData */
29 
Draw(ccharacter * Viewer,blitdata & BlitData,int RequiredSquarePosition) const30 void stack::Draw(ccharacter* Viewer, blitdata& BlitData,
31                  int RequiredSquarePosition) const
32 {
33   if(!Items)
34     return;
35 
36   int VisibleItems = 0;
37   v2 StackPos = GetPos();
38 
39   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
40     if(i->GetSquarePosition() == RequiredSquarePosition
41        && (i->CanBeSeenBy(Viewer) || game::GetSeeWholeMapCheatMode()))
42     {
43       BlitData.CustomData |= i->GetSquareIndex(StackPos);
44       i->Draw(BlitData);
45       BlitData.CustomData &= ~SQUARE_INDEX_MASK;
46       ++VisibleItems;
47     }
48 
49   if(RequiredSquarePosition == CENTER)
50   {
51     truth PlusSymbol = VisibleItems > 1, Dangerous = NeedDangerSymbol(Viewer);
52 
53     if(PlusSymbol || Dangerous)
54     {
55       col24 L = BlitData.Luminance;
56       BlitData.Luminance = ivanconfig::GetContrastLuminance();
57       BlitData.Src.Y = 16;
58 
59       if(PlusSymbol)
60         igraph::GetSymbolGraphic()->LuminanceMaskedBlit(BlitData);
61 
62       if(Dangerous)
63       {
64         BlitData.Src.X = 160;
65         igraph::GetSymbolGraphic()->LuminanceMaskedBlit(BlitData);
66       }
67 
68       BlitData.Src.X = BlitData.Src.Y = 0; /// check
69       BlitData.Luminance = L;
70     }
71   }
72 }
73 
AddItem(item * ToBeAdded,truth RunRoomEffects)74 void stack::AddItem(item* ToBeAdded, truth RunRoomEffects)
75 {
76   if(!ToBeAdded)
77     return;
78 
79   AddElement(ToBeAdded);
80 
81   if(Flags & HIDDEN)
82     return;
83 
84   lsquare* SquareUnder = GetLSquareTrulyUnder(ToBeAdded->GetSquarePosition());
85 
86   if(!SquareUnder)
87     return;
88 
89   if(ToBeAdded->IsAnimated())
90     SquareUnder->IncStaticAnimatedEntities();
91 
92   if(!game::IsGenerating())
93   {
94     if(RunRoomEffects && GetLSquareUnder()->GetRoom())
95       GetLSquareUnder()->GetRoom()->AddItemEffect(ToBeAdded);
96 
97     SquareUnder->SendNewDrawRequest();
98     SquareUnder->SendMemorizedUpdateRequest();
99   }
100 }
101 
RemoveItem(stackslot * Slot)102 void stack::RemoveItem(stackslot* Slot)
103 {
104   item* Item = Slot->GetItem();
105   truth WasAnimated = Item->IsAnimated();
106   col24 Emit = Item->GetEmitation();
107   RemoveElement(Slot);
108   SignalVolumeAndWeightChange();
109   SignalEmitationDecrease(Item->GetSquarePosition(), Emit);
110 
111   if(Flags & HIDDEN)
112     return;
113 
114   lsquare* SquareUnder = GetLSquareTrulyUnder(Item->GetSquarePosition());
115 
116   if(Item->GetSquarePosition() != CENTER)
117     Item->SignalSquarePositionChange(CENTER);
118 
119   if(!SquareUnder)
120     return;
121 
122   if(WasAnimated)
123     SquareUnder->DecStaticAnimatedEntities();
124 
125   if(!game::IsGenerating())
126   {
127     SquareUnder->SendNewDrawRequest();
128     SquareUnder->SendMemorizedUpdateRequest();
129   }
130 }
131 
132 /* Removes all items. LastClean should be true only if the stack is being
133    deleted (the default is false) */
134 
Clean(truth LastClean)135 void stack::Clean(truth LastClean)
136 {
137   if(!Items)
138     return;
139 
140   stackslot* Slot = Bottom;
141 
142   if(!LastClean)
143   {
144     Bottom = Top = 0;
145     Volume = Weight = Items = 0;
146     SignalVolumeAndWeightChange();
147   }
148 
149   while(Slot)
150   {
151     item* Item = Slot->GetItem();
152 
153     if(!(Flags & HIDDEN) && Item->IsAnimated() && !LastClean)
154     {
155       lsquare* Square = GetLSquareTrulyUnder(Item->GetSquarePosition());
156 
157       if(Square)
158         Square->DecStaticAnimatedEntities();
159     }
160 
161     if(LastClean && Item->GetSquaresUnder() == 1)
162       delete Item;
163     else
164       Item->SendToHell();
165 
166     stackslot* Rubbish = Slot;
167     Slot = Slot->Next;
168     delete Rubbish;
169 
170     if(!LastClean)
171       SignalEmitationDecrease(Item->GetSquarePosition(), Item->GetEmitation());
172   }
173 }
174 
Save(outputfile & SaveFile) const175 void stack::Save(outputfile& SaveFile) const
176 {
177   if(!Items)
178   {
179     SaveFile << ushort(0);
180     return;
181   }
182 
183   int SavedItems = 0;
184 
185   for(stackiterator i1 = GetBottom(); i1.HasItem(); ++i1)
186     if(i1->IsMainSlot(&i1.GetSlot()))
187       ++SavedItems;
188 
189   SaveFile << static_cast<ushort>(SavedItems);
190 
191   /* Save multitiled items only to one stack */
192 
193   for(stackiterator i2 = GetBottom(); i2.HasItem(); ++i2)
194     if(i2->IsMainSlot(&i2.GetSlot()))
195       SaveFile << i2.GetSlot();
196 }
197 
Load(inputfile & SaveFile)198 void stack::Load(inputfile& SaveFile)
199 {
200   int SavedItems = 0;
201   SaveFile >> reinterpret_cast<ushort&>(SavedItems);
202 
203   for(int c = 0; c < SavedItems; ++c)
204   {
205     if(!c && !Items)
206       Bottom = Top = new stackslot(this, 0);
207     else
208       Top = Top->Next = new stackslot(this, Top);
209 
210     SaveFile >> *Top;
211     Volume += (*Top)->GetVolume();
212     Weight += (*Top)->GetWeight();
213 
214     if((*Top)->GetSquarePosition() == CENTER)
215       Emitation = game::CombineConstLights(Emitation, (*Top)->GetEmitation());
216   }
217 
218   Items += SavedItems;
219 }
220 
GetPos() const221 v2 stack::GetPos() const
222 {
223   return GetSquareUnder()->GetPos();
224 }
225 
226 /* Returns whether there are any items satisfying the sorter or any visible
227    items if it is zero */
228 
SortedItems(ccharacter * Viewer,sorter SorterFunction) const229 truth stack::SortedItems(ccharacter* Viewer, sorter SorterFunction) const
230 {
231   itemvector SortedItems;
232   FillItemVectorSorted(SortedItems, Viewer, SorterFunction, 1);
233   return !SortedItems.empty();
234 }
235 
BeKicked(character * Kicker,int KickDamage,int Direction)236 void stack::BeKicked(character* Kicker, int KickDamage, int Direction)
237 {
238   if(KickDamage)
239   {
240     ReceiveDamage(Kicker, KickDamage, PHYSICAL_DAMAGE, Direction);
241 
242     if(GetItems() && GetLSquareUnder()->IsFlyable())///&& SquarePosition == CENTER)
243     {
244       item* Item1 = *GetTop();
245       item* Item2 = RAND() & 1 && GetItems() > 1 ? *--GetTop() : 0;
246       Item1->Fly(Kicker, Direction, KickDamage * 3);
247 
248       if(Item2)
249         Item2->Fly(Kicker, Direction, KickDamage * 3);
250     }
251   }
252   else if(Kicker->IsPlayer() && GetNativeVisibleItems(Kicker))
253     ADD_MESSAGE("Your weak kick has no effect.");
254 }
255 
Polymorph(character * Polymorpher)256 void stack::Polymorph(character* Polymorpher)
257 {
258   itemvector ItemVector;
259   FillItemVector(ItemVector);
260   int p = 0;
261 
262   for(uint c = 0; c < ItemVector.size(); ++c)
263     if(ItemVector[c]->Exists()
264        && ItemVector[c]->Polymorph(Polymorpher, this)
265        && ++p == 5)
266       break;
267 }
268 
Alchemize(character * Midas)269 void stack::Alchemize(character* Midas)
270 {
271   itemvector ItemVector;
272   FillItemVector(ItemVector);
273   int p = 0;
274 
275   for(uint c = 0; c < ItemVector.size(); ++c)
276     if(ItemVector[c]->Exists()
277        && ItemVector[c]->Alchemize(Midas, this)
278        && ++p == 5)
279       break;
280 }
281 
SoftenMaterial(character * Jerk)282 void stack::SoftenMaterial(character* Jerk)
283 {
284   itemvector ItemVector;
285   FillItemVector(ItemVector);
286   int p = 0;
287 
288   for(uint c = 0; c < ItemVector.size(); ++c)
289     if(ItemVector[c]->Exists()
290        && ItemVector[c]->SoftenMaterial()
291        && ++p == 5)
292       break;
293 }
294 
CheckForStepOnEffect(character * Stepper)295 void stack::CheckForStepOnEffect(character* Stepper)
296 {
297   itemvector ItemVector;
298   FillItemVector(ItemVector);
299 
300   for(uint c = 0; c < ItemVector.size(); ++c)
301     if(ItemVector[c]->Exists())
302     {
303       ItemVector[c]->StepOnEffect(Stepper);
304 
305       if(!Stepper->IsEnabled())
306         return;
307     }
308 }
309 
GetLSquareTrulyUnder(int SquarePosition) const310 lsquare* stack::GetLSquareTrulyUnder(int SquarePosition) const
311 {
312   switch(SquarePosition)
313   {
314    case DOWN:
315     if(GetArea()->IsValidPos(GetPos() + v2(0, 1)))
316       return GetNearLSquare(GetPos() + v2(0, 1));
317     else
318       return 0;
319    case LEFT:
320     if(GetArea()->IsValidPos(GetPos() + v2(-1, 0)))
321       return GetNearLSquare(GetPos() + v2(-1, 0));
322     else
323       return 0;
324    case UP:
325     if(GetArea()->IsValidPos(GetPos() + v2(0, -1)))
326       return GetNearLSquare(GetPos() + v2(0, -1));
327     else
328       return 0;
329    case RIGHT:
330     if(GetArea()->IsValidPos(GetPos() + v2(1, 0)))
331       return GetNearLSquare(GetPos() + v2(1, 0));
332     else
333       return 0;
334   }
335 
336   return GetLSquareUnder();
337 }
338 
ReceiveDamage(character * Damager,int Damage,int Type,int Direction)339 void stack::ReceiveDamage(character* Damager, int Damage,
340                           int Type, int Direction)
341 {
342   itemvector ItemVector;
343   FillItemVector(ItemVector);
344 
345   for(uint c = 0; c < ItemVector.size(); ++c)
346     if(ItemVector[c]->Exists()
347        && AllowDamage(Direction, ItemVector[c]->GetSquarePosition()))
348       ItemVector[c]->ReceiveDamage(Damager, Damage, Type);
349 }
350 
TeleportRandomly(uint Amount)351 void stack::TeleportRandomly(uint Amount)
352 {
353   itemvector ItemVector;
354   FillItemVector(ItemVector);
355 
356   for(uint c = 0; c < ItemVector.size() && c < Amount; ++c)
357     if(ItemVector[c]->Exists())
358     {
359       if(ItemVector[c]->CanBeSeenByPlayer())
360         ADD_MESSAGE("%s disappears!",
361                     ItemVector[c]->GetExtendedDescription().CStr());
362 
363       ItemVector[c]->TeleportRandomly();
364     }
365 }
366 
367 /* ItemVector receives all items in the stack */
368 
FillItemVector(itemvector & ItemVector) const369 void stack::FillItemVector(itemvector& ItemVector) const
370 {
371   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
372     ItemVector.push_back(*i);
373 }
374 
375 /* ItemVector receives all items satisfying the sorter or all visible items
376    if the sorter is zero. MaxItemsToAdd < 1 implies no maximum. */
377 
FillItemVectorSorted(itemvector & ItemVector,ccharacter * Viewer,sorter SorterFunction,int MaxItemsToAdd) const378 void stack::FillItemVectorSorted(itemvector& ItemVector, ccharacter* Viewer,
379                                  sorter SorterFunction, int MaxItemsToAdd) const
380 {
381   int ItemsAdded = 0;
382 
383   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
384     if((SorterFunction == 0 || ((*i)->*SorterFunction)(Viewer))
385        && ((Flags & HIDDEN) || i->CanBeSeenBy(Viewer)))
386     {
387       ItemVector.push_back(*i);
388 
389       if(++ItemsAdded == MaxItemsToAdd)
390         return;
391     }
392 }
393 
394 /* Don't use; this function is only for gum solutions */
395 
GetItem(int I) const396 item* stack::GetItem(int I) const
397 {
398   int c = 0;
399 
400   for(stackiterator i = GetBottom(); i.HasItem(); ++i, ++c)
401     if(c == I)
402       return *i;
403 
404   return 0;
405 }
406 
407 /* Don't use; this function is only for gum solutions */
408 
SearchItem(item * ToBeSearched) const409 int stack::SearchItem(item* ToBeSearched) const
410 {
411   int c = 0;
412 
413   for(stackiterator i = GetBottom(); i.HasItem(); ++i, ++c)
414     if(*i == ToBeSearched)
415       return c;
416 
417   return -1;
418 }
419 
420 /* Flags for all DrawContents functions can be found in ivandef.h.
421    Those returning int return 0 on success and a felist error
422    otherwise (see felibdef.h) */
423 
DrawContents(ccharacter * Viewer,cfestring & Topic,int Flags,sorter SorterFunction) const424 item* stack::DrawContents(ccharacter* Viewer, cfestring& Topic,
425                           int Flags, sorter SorterFunction) const
426 {
427   itemvector ReturnVector;
428   DrawContents(ReturnVector, 0, Viewer, Topic, CONST_S(""), CONST_S(""),
429                CONST_S(""), 0, Flags|NO_MULTI_SELECT, SorterFunction);
430   return ReturnVector.empty() ? 0 : ReturnVector[0];
431 }
432 
DrawContents(itemvector & ReturnVector,ccharacter * Viewer,cfestring & Topic,int Flags,sorter SorterFunction) const433 int stack::DrawContents(itemvector& ReturnVector,
434                         ccharacter* Viewer,
435                         cfestring& Topic, int Flags,
436                         sorter SorterFunction) const
437 {
438   return DrawContents(ReturnVector, 0, Viewer, Topic, CONST_S(""),
439                       CONST_S(""), CONST_S(""), 0, Flags, SorterFunction);
440 }
441 
442 /* MergeStack is used for showing two stacks together. Like when eating when
443    there are items on the ground and in the character's stack */
444 
DrawContents(itemvector & ReturnVector,stack * MergeStack,ccharacter * Viewer,cfestring & Topic,cfestring & ThisDesc,cfestring & ThatDesc,cfestring & SpecialDesc,col16 SpecialDescColor,int Flags,sorter SorterFunction) const445 int stack::DrawContents(itemvector& ReturnVector, stack* MergeStack,
446                         ccharacter* Viewer, cfestring& Topic,
447                         cfestring& ThisDesc, cfestring& ThatDesc,
448                         cfestring& SpecialDesc, col16 SpecialDescColor,
449                         int Flags, sorter SorterFunction) const
450 {
451   felist Contents(Topic);
452   lsquare* Square = GetLSquareUnder();
453   stack* AdjacentStack[4] = { 0, 0, 0, 0 };
454   int c;
455 
456   if(!(this->Flags & HIDDEN))
457     for(c = 0; c < 4; ++c)
458       AdjacentStack[c] = Square->GetStackOfAdjacentSquare(c);
459 
460   if(!SpecialDesc.IsEmpty())
461   {
462     Contents.AddDescription(CONST_S(""));
463     Contents.AddDescription(SpecialDesc.CapitalizeCopy(), SpecialDescColor);
464   }
465 
466   /*if(!(Flags & NO_SPECIAL_INFO))
467     {
468     Contents.AddDescription(CONST_S(""));
469     long Weight = GetWeight(Viewer, CENTER);
470 
471     if(MergeStack)
472     Weight += MergeStack->GetWeight(Viewer, CENTER);
473 
474     for(c = 0; c < 4; ++c)
475     if(AdjacentStack[c])
476     Weight += AdjacentStack[c]->GetWeight(Viewer, 3 - c);
477 
478     Contents.AddDescription(CONST_S("Overall weight: ") + Weight + " grams");
479     }*/
480 
481   if(Flags & NONE_AS_CHOICE)
482   {
483     int ImageKey = game::AddToItemDrawVector(itemvector());
484     Contents.AddEntry(CONST_S("none"), LIGHT_GRAY, 0, ImageKey);
485   }
486 
487   if(MergeStack)
488     MergeStack->AddContentsToList(Contents, Viewer, ThatDesc,
489                                   Flags, CENTER, SorterFunction);
490 
491   AddContentsToList(Contents, Viewer, ThisDesc, Flags, CENTER, SorterFunction);
492   static cchar* WallDescription[] = { "western", "southern",
493                                            "northern", "eastern" };
494 
495   for(c = 0; c < 4; ++c)
496     if(AdjacentStack[c])
497       AdjacentStack[c]->AddContentsToList(Contents, Viewer,
498                                           CONST_S("Items on the ")
499                                           + WallDescription[c] + " wall:",
500                                           Flags, 3 - c, SorterFunction);
501 
502   game::SetStandardListAttributes(Contents);
503   Contents.SetPageLength(stack::GetStandardPageLength());
504   Contents.RemoveFlags(BLIT_AFTERWARDS);
505   Contents.SetEntryDrawer(game::ItemEntryDrawer);
506 
507   if(!(Flags & NO_SELECT))
508     Contents.AddFlags(SELECTABLE);
509 
510   if(Flags & REMEMBER_SELECTED)
511     Contents.SetSelected(GetSelected());
512 
513   game::DrawEverythingNoBlit(); // doesn't prevent mirage puppies
514   int Chosen = Contents.Draw();
515   game::ClearItemDrawVector();
516 
517   if(Chosen & FELIST_ERROR_BIT)
518   {
519     Selected = 0;
520     return Chosen;
521   }
522   else
523     Selected = Chosen;
524 
525   int Pos = 0;
526 
527   if(Flags & NONE_AS_CHOICE)
528   {
529     if(!Selected)
530       return 0;
531     else
532       ++Pos;
533   }
534 
535   if(MergeStack)
536   {
537     Pos = MergeStack->SearchChosen(ReturnVector, Viewer, Pos, Selected,
538                                    Flags, CENTER, SorterFunction);
539 
540     if(!ReturnVector.empty())
541       return 0;
542   }
543 
544   Pos = SearchChosen(ReturnVector, Viewer, Pos, Selected,
545                      Flags, CENTER, SorterFunction);
546 
547   if(!ReturnVector.empty())
548     return 0;
549 
550   for(c = 0; c < 4; ++c)
551     if(AdjacentStack[c])
552     {
553       AdjacentStack[c]->SearchChosen(ReturnVector, Viewer, Pos, Selected,
554                                      Flags, 3 - c, SorterFunction);
555 
556       if(!ReturnVector.empty())
557         break;
558     }
559 
560   return 0;
561 }
562 
563 /* Internal function to fill Contents list */
564 
AddContentsToList(felist & Contents,ccharacter * Viewer,cfestring & Desc,int Flags,int RequiredSquarePosition,sorter SorterFunction) const565 void stack::AddContentsToList(felist& Contents, ccharacter* Viewer,
566                               cfestring& Desc, int Flags,
567                               int RequiredSquarePosition,
568                               sorter SorterFunction) const
569 {
570   itemvectorvector PileVector;
571   Pile(PileVector, Viewer, RequiredSquarePosition, SorterFunction);
572 
573   truth DrawDesc = Desc.GetSize();
574   long LastCategory = 0;
575   festring Entry;
576 
577   for(uint p = 0; p < PileVector.size(); ++p)
578   {
579     if(DrawDesc)
580     {
581       if(!Contents.IsEmpty())
582         Contents.AddEntry(CONST_S(""), WHITE, 0, NO_IMAGE, false);
583 
584       Contents.AddEntry(Desc, WHITE, 0, NO_IMAGE, false);
585       Contents.AddEntry(CONST_S(""), WHITE, 0, NO_IMAGE, false);
586       DrawDesc = false;
587     }
588 
589     item* Item = PileVector[p].back();
590 
591     if(Item->GetCategory() != LastCategory)
592     {
593       LastCategory = Item->GetCategory();
594       Contents.AddEntry(item::GetItemCategoryName(LastCategory),
595                         LIGHT_GRAY, 0, NO_IMAGE, false);
596     }
597 
598     Entry.Empty();
599     Item->AddInventoryEntry(Viewer, Entry, PileVector[p].size(),
600                             !(Flags & NO_SPECIAL_INFO));
601     int ImageKey = game::AddToItemDrawVector(PileVector[p]);
602     Contents.AddEntry(Entry, LIGHT_GRAY, 0, ImageKey);
603     if(!Item->GetDescriptiveInfo().IsEmpty())
604       Contents.SetLastEntryHelp(festring()<<Entry<<"\n\n"<<Item->GetDescriptiveInfo());
605   }
606 }
607 
608 /* Internal function which fills ReturnVector according to Chosen,
609    which is given by felist::Draw, and possibly the user's additional
610    input about item amount. */
611 
SearchChosen(itemvector & ReturnVector,ccharacter * Viewer,int Pos,int Chosen,int Flags,int RequiredSquarePosition,sorter SorterFunction) const612 int stack::SearchChosen(itemvector& ReturnVector,
613                         ccharacter* Viewer,
614                         int Pos, int Chosen, int Flags,
615                         int RequiredSquarePosition,
616                         sorter SorterFunction) const
617 {
618   /* Not really efficient... :( */
619 
620   itemvectorvector PileVector;
621   Pile(PileVector, Viewer, RequiredSquarePosition, SorterFunction);
622 
623   for(uint p = 0; p < PileVector.size(); ++p)
624     if(Pos++ == Chosen)
625     {
626       if(Flags & NO_MULTI_SELECT)
627       {
628         int Amount = (Flags & SELECT_PAIR
629                       && PileVector[p][0]->HandleInPairs()
630                       && PileVector[p].size() >= 2
631                       ? 2 : 1);
632         ReturnVector.assign(PileVector[p].end() - Amount, PileVector[p].end());
633         return -1;
634       }
635       else
636       {
637         int Amount = PileVector[p].size();
638 
639         if(Amount > 1)
640           Amount = game::ScrollBarQuestion(CONST_S("How many ")
641                                            + PileVector[p][0]->GetName(PLURAL)
642                                            + '?',
643                                            Amount, 1, 0, Amount, 0, WHITE,
644                                            LIGHT_GRAY, DARK_GRAY);
645 
646         ReturnVector.assign(PileVector[p].end() - Amount, PileVector[p].end());
647         return -1;
648       }
649     }
650 
651   return Pos;
652 }
653 
RaiseTheDead(character * Summoner)654 truth stack::RaiseTheDead(character* Summoner)
655 {
656   itemvector ItemVector;
657   FillItemVector(ItemVector);
658 
659   for(uint c = 0; c < ItemVector.size(); ++c)
660     if(ItemVector[c]->RaiseTheDead(Summoner))
661       return true;
662 
663   return false;
664 }
665 
666 /* Returns false if the Applier didn't try to use the key */
667 
TryKey(item * Key,character * Applier)668 truth stack::TryKey(item* Key, character* Applier)
669 {
670   if(!Applier->IsPlayer())
671     return false;
672 
673   itemvector ItemsWithLock;
674   FillItemVectorSorted(ItemsWithLock, Applier, &item::HasLock, 2);
675 
676   if(ItemsWithLock.empty())
677     return false;
678   else if(ItemsWithLock.size() == 1)
679     return ItemsWithLock[0]->TryKey(Key, Applier);
680   else
681   {
682     item* ToBeOpened = DrawContents(Applier,
683                                     CONST_S("Where do you wish to use the key?"),
684                                     0, &item::HasLock);
685     return ToBeOpened ? ToBeOpened->TryKey(Key, Applier) : 0;
686   }
687 }
688 
689 /* Returns false if the Applier didn't try to open anything */
690 
Open(character * Opener)691 truth stack::Open(character* Opener)
692 {
693   if(!Opener->IsPlayer())
694     return false;
695 
696   itemvector OpenableItems;
697   FillItemVectorSorted(OpenableItems, Opener, &item::IsOpenable, 2);
698 
699   if(OpenableItems.empty())
700     return false;
701   else if(OpenableItems.size() == 1)
702     return OpenableItems[0]->Open(Opener);
703   else
704   {
705     item* ToBeOpened = DrawContents(Opener, CONST_S("What do you wish to open?"),
706                                     0, &item::IsOpenable);
707     return ToBeOpened ? ToBeOpened->Open(Opener) : 0;
708   }
709 }
710 
GetSideItems(int RequiredSquarePosition) const711 int stack::GetSideItems(int RequiredSquarePosition) const
712 {
713   int VisibleItems = 0;
714 
715   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
716     if(i->GetSquarePosition() == RequiredSquarePosition)
717       ++VisibleItems;
718 
719   return VisibleItems;
720 }
721 
GetVisibleItems(ccharacter * Viewer) const722 int stack::GetVisibleItems(ccharacter* Viewer) const
723 {
724   int VisibleItems = 0;
725 
726   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
727     if(i->GetSquarePosition() == CENTER && i->CanBeSeenBy(Viewer))
728       ++VisibleItems;
729 
730   lsquare* Square = GetLSquareUnder();
731 
732   for(int c = 0; c < 4; ++c)
733   {
734     stack* Stack = Square->GetStackOfAdjacentSquare(c);
735 
736     if(Stack)
737       VisibleItems += Stack->GetVisibleSideItems(Viewer, 3 - c);
738   }
739 
740   return VisibleItems;
741 }
742 
GetNativeVisibleItems(ccharacter * Viewer) const743 int stack::GetNativeVisibleItems(ccharacter* Viewer) const
744 {
745   if(Flags & HIDDEN)
746     return Items;
747 
748   int VisibleItems = 0;
749 
750   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
751     if(i->CanBeSeenBy(Viewer))
752       ++VisibleItems;
753 
754   return VisibleItems;
755 }
756 
GetVisibleSideItems(ccharacter * Viewer,int RequiredSquarePosition) const757 int stack::GetVisibleSideItems(ccharacter* Viewer,
758                                int RequiredSquarePosition) const
759 {
760   int VisibleItems = 0;
761 
762   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
763     if(i->GetSquarePosition() == RequiredSquarePosition
764        && i->CanBeSeenBy(Viewer))
765       ++VisibleItems;
766 
767   return VisibleItems;
768 }
769 
GetBottomVisibleItem(ccharacter * Viewer) const770 item* stack::GetBottomVisibleItem(ccharacter* Viewer) const
771 {
772   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
773     if((Flags & HIDDEN) || i->CanBeSeenBy(Viewer))
774       return *i;
775 
776   return 0;
777 }
778 
SignalVolumeAndWeightChange()779 void stack::SignalVolumeAndWeightChange()
780 {
781   if(!(Flags & FREEZED))
782   {
783     CalculateVolumeAndWeight();
784 
785     if(MotherEntity)
786       MotherEntity->SignalVolumeAndWeightChange();
787   }
788 }
789 
CalculateVolumeAndWeight()790 void stack::CalculateVolumeAndWeight()
791 {
792   Volume = Weight = 0;
793 
794   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
795   {
796     Volume += i->GetVolume();
797     Weight += i->GetWeight();
798   }
799 }
800 
SignalEmitationIncrease(int ItemSquarePosition,col24 EmitationUpdate)801 void stack::SignalEmitationIncrease(int ItemSquarePosition,
802                                     col24 EmitationUpdate)
803 {
804   if(ItemSquarePosition < CENTER)
805   {
806     stack* Stack = GetLSquareUnder()
807                    ->GetStackOfAdjacentSquare(ItemSquarePosition);
808 
809     if(Stack)
810       Stack->SignalEmitationIncrease(CENTER, EmitationUpdate);
811 
812     return;
813   }
814 
815   if(!(Flags & FREEZED) && game::CompareLights(EmitationUpdate, Emitation) > 0)
816   {
817     Emitation = game::CombineConstLights(Emitation, EmitationUpdate);
818 
819     if(MotherEntity)
820     {
821       if(MotherEntity->AllowContentEmitation())
822         MotherEntity->SignalEmitationIncrease(EmitationUpdate);
823     }
824     else
825       GetLSquareUnder()->SignalEmitationIncrease(EmitationUpdate);
826   }
827 }
828 
SignalEmitationDecrease(int ItemSquarePosition,col24 EmitationUpdate)829 void stack::SignalEmitationDecrease(int ItemSquarePosition,
830                                     col24 EmitationUpdate)
831 {
832   if(ItemSquarePosition < CENTER)
833   {
834     stack* Stack = GetLSquareUnder()
835                    ->GetStackOfAdjacentSquare(ItemSquarePosition);
836 
837     if(Stack)
838       Stack->SignalEmitationDecrease(CENTER, EmitationUpdate);
839 
840     return;
841   }
842 
843   if(!(Flags & FREEZED) && Emitation
844      && game::CompareLights(EmitationUpdate, Emitation) >= 0)
845   {
846     col24 Backup = Emitation;
847     CalculateEmitation();
848 
849     if(Backup != Emitation)
850     {
851       if(MotherEntity)
852       {
853         if(MotherEntity->AllowContentEmitation())
854           MotherEntity->SignalEmitationDecrease(EmitationUpdate);
855       }
856       else
857         GetLSquareUnder()->SignalEmitationDecrease(EmitationUpdate);
858     }
859   }
860 }
861 
CalculateEmitation()862 void stack::CalculateEmitation()
863 {
864   Emitation = GetSideEmitation(CENTER);
865 }
866 
GetSideEmitation(int RequiredSquarePosition)867 col24 stack::GetSideEmitation(int RequiredSquarePosition)
868 {
869   col24 Emitation = 0;
870 
871   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
872     if(i->GetSquarePosition() == RequiredSquarePosition)
873       game::CombineLights(Emitation, i->GetEmitation());
874 
875   return Emitation;
876 }
877 
CanBeSeenBy(ccharacter * Viewer,int SquarePosition) const878 truth stack::CanBeSeenBy(ccharacter* Viewer, int SquarePosition) const
879 {
880   if(MotherEntity)
881     return MotherEntity->ContentsCanBeSeenBy(Viewer);
882   else
883   {
884     lsquare* Square = GetLSquareTrulyUnder(SquarePosition);
885     return Viewer->IsOver(Square->GetPos()) || Square->CanBeSeenBy(Viewer);
886   }
887 }
888 
IsDangerous(ccharacter * Stepper) const889 truth stack::IsDangerous(ccharacter* Stepper) const
890 {
891   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
892     if(i->IsDangerous(Stepper) && i->CanBeSeenBy(Stepper))
893       return true;
894 
895   return false;
896 }
897 
898 /* Returns true if something was duplicated.
899    Max is the cap of items to be affected */
900 
Duplicate(int Max,ulong Flags)901 truth stack::Duplicate(int Max, ulong Flags)
902 {
903   if(!GetItems())
904     return false;
905 
906   itemvector ItemVector;
907   FillItemVector(ItemVector);
908   int p = 0;
909 
910   for(uint c = 0; c < ItemVector.size(); ++c)
911     if(ItemVector[c]->Exists()
912        && ItemVector[c]->DuplicateToStack(this, Flags)
913        && ++p == Max)
914       break;
915 
916   return p > 0;
917 }
918 
919 /* Adds the item without any external update requests */
920 
AddElement(item * Item,truth)921 void stack::AddElement(item* Item, truth)
922 {
923   ++Items;
924 
925   /* "I love writing illegible code." - Guy who wrote this */
926 
927   (Top = (Bottom ? Top->Next : Bottom) = new stackslot(this, Top))->PutInItem(Item);
928 }
929 
930 /* Removes the slot without any external update requests */
931 
RemoveElement(stackslot * Slot)932 void stack::RemoveElement(stackslot* Slot)
933 {
934   --Items;
935   (Slot->Last ? Slot->Last->Next : Bottom) = Slot->Next;
936   (Slot->Next ? Slot->Next->Last : Top) = Slot->Last;
937   delete Slot;
938 }
939 
MoveItemsTo(stack * Stack)940 void stack::MoveItemsTo(stack* Stack)
941 {
942   while(Items)
943     GetBottom()->MoveTo(Stack);
944 }
945 
MoveItemsTo(slot * Slot)946 void stack::MoveItemsTo(slot* Slot)
947 {
948   while(Items)
949     Slot->AddFriendItem(*GetBottom());
950 }
951 
GetBottomItem(ccharacter * Char,truth ForceIgnoreVisibility) const952 item* stack::GetBottomItem(ccharacter* Char,
953                            truth ForceIgnoreVisibility) const
954 {
955   if((Flags & HIDDEN) || ForceIgnoreVisibility)
956     return Bottom ? **Bottom : 0;
957   else
958     return GetBottomVisibleItem(Char);
959 }
960 
GetBottomSideItem(ccharacter * Char,int RequiredSquarePosition,truth ForceIgnoreVisibility) const961 item* stack::GetBottomSideItem(ccharacter* Char,
962                                int RequiredSquarePosition,
963                                truth ForceIgnoreVisibility) const
964 {
965   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
966     if((i->GetSquarePosition() == RequiredSquarePosition
967        && (Flags & HIDDEN)) || ForceIgnoreVisibility || i->CanBeSeenBy(Char))
968       return *i;
969 
970   return 0;
971 }
972 
CategorySorter(const itemvector & V1,const itemvector & V2)973 truth CategorySorter(const itemvector& V1, const itemvector& V2)
974 {
975   return (*V1.begin())->GetCategory() < (*V2.begin())->GetCategory();
976 }
977 
978 /* Slow function which sorts the stack's contents to a vector of piles
979    (itemvectors) of which elements are similar to each other, for instance
980    4 bananas */
981 
Pile(itemvectorvector & PileVector,ccharacter * Viewer,int RequiredSquarePosition,sorter SorterFunction) const982 void stack::Pile(itemvectorvector& PileVector, ccharacter* Viewer,
983                  int RequiredSquarePosition, sorter SorterFunction) const
984 {
985   if(!Items)
986     return;
987 
988   std::list<item*> List;
989 
990   for(stackiterator s = GetBottom(); s.HasItem(); ++s)
991     if(s->GetSquarePosition() == RequiredSquarePosition
992        && (SorterFunction == 0 || ((*s)->*SorterFunction)(Viewer))
993        && ((Flags & HIDDEN) || s->CanBeSeenBy(Viewer)))
994       List.push_back(*s);
995 
996   for(std::list<item*>::iterator i = List.begin(); i != List.end(); ++i)
997   {
998     PileVector.resize(PileVector.size() + 1);
999     itemvector& Pile = PileVector.back();
1000     Pile.push_back(*i);
1001 
1002     if((*i)->CanBePiled())
1003     {
1004       std::list<item*>::iterator j = i;
1005 
1006       for(++j; j != List.end();)
1007         if((*j)->CanBePiled() && (*i)->CanBePiledWith(*j, Viewer))
1008         {
1009           Pile.push_back(*j);
1010           std::list<item*>::iterator Dirt = j++;
1011           List.erase(Dirt);
1012         }
1013         else
1014           ++j;
1015     }
1016   }
1017 
1018   std::stable_sort(PileVector.begin(), PileVector.end(), CategorySorter);
1019 }
1020 
1021 /* Total price of the stack */
1022 
GetTruePrice() const1023 long stack::GetTruePrice() const
1024 {
1025   long Price = 0;
1026 
1027   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
1028     Price += i->GetTruePrice();
1029 
1030   return Price;
1031 }
1032 
1033 /* GUI used for instance by chests and bookcases.
1034    Returns whether anything was done. */
1035 
TakeSomethingFrom(character * Opener,cfestring & ContainerName)1036 truth stack::TakeSomethingFrom(character* Opener,
1037                                cfestring& ContainerName)
1038 {
1039   if(!GetItems())
1040   {
1041     ADD_MESSAGE("There is nothing in %s.", ContainerName.CStr());
1042     return false;
1043   }
1044 
1045   truth Success = false;
1046   room* Room = GetLSquareUnder()->GetRoom();
1047   SetSelected(0);
1048 
1049   for(;;)
1050   {
1051     itemvector ToTake;
1052     game::DrawEverythingNoBlit();
1053     DrawContents(ToTake, Opener,
1054                  CONST_S("What do you want to take from ")
1055                  + ContainerName + '?',
1056                  REMEMBER_SELECTED);
1057 
1058     if(ToTake.empty())
1059       break;
1060 
1061     if(!IsOnGround() || !Room
1062        || Room->PickupItem(Opener, ToTake[0], ToTake.size()))
1063     {
1064       for(uint c = 0; c < ToTake.size(); ++c)
1065         ToTake[c]->MoveTo(Opener->GetStack());
1066 
1067       ADD_MESSAGE("You take %s from %s.",
1068                   ToTake[0]->GetName(DEFINITE, ToTake.size()).CStr(),
1069                   ContainerName.CStr());
1070       Success = true;
1071     }
1072   }
1073 
1074   return Success;
1075 }
1076 
1077 /* GUI used for instance by chests and bookcases (use ContainerID == 0 if
1078    the container isn't an item). Returns whether anything was done. */
1079 
PutSomethingIn(character * Opener,cfestring & ContainerName,long StorageVolume,ulong ContainerID)1080 truth stack::PutSomethingIn(character* Opener, cfestring& ContainerName,
1081                             long StorageVolume, ulong ContainerID)
1082 {
1083   if(!Opener->GetStack()->GetItems())
1084   {
1085     ADD_MESSAGE("You have nothing to put in %s.", ContainerName.CStr());
1086     return false;
1087   }
1088 
1089   truth Success = false;
1090   room* Room = GetLSquareUnder()->GetRoom();
1091   SetSelected(0);
1092 
1093   for(;;)
1094   {
1095     itemvector ToPut;
1096     game::DrawEverythingNoBlit();
1097     Opener->GetStack()->DrawContents(ToPut, Opener,
1098                                      CONST_S("What do you want to put in ")
1099                                      + ContainerName + '?',
1100                                      REMEMBER_SELECTED);
1101 
1102     if(ToPut.empty())
1103       break;
1104 
1105     if(ToPut[0]->GetID() == ContainerID)
1106     {
1107       ADD_MESSAGE("You can't put %s inside itself!", ContainerName.CStr());
1108       continue;
1109     }
1110 
1111     uint Amount = Min<uint>((StorageVolume - GetVolume())
1112                             / ToPut[0]->GetVolume(),
1113                             ToPut.size());
1114 
1115     if(!Amount)
1116     {
1117       if(ToPut.size() == 1)
1118         ADD_MESSAGE("%s doesn't fit in %s.",
1119                     ToPut[0]->CHAR_NAME(DEFINITE),
1120                     ContainerName.CStr());
1121       else
1122         ADD_MESSAGE("None of the %d %s fit in %s.", int(ToPut.size()),
1123                     ToPut[0]->CHAR_NAME(PLURAL), ContainerName.CStr());
1124 
1125       continue;
1126     }
1127 
1128     if(Amount != ToPut.size())
1129       ADD_MESSAGE("Only %d of the %d %s fit%s in %s.", Amount,
1130                   int(ToPut.size()), ToPut[0]->CHAR_NAME(PLURAL),
1131                   Amount == 1 ? "s" : "", ContainerName.CStr());
1132 
1133     if(!IsOnGround() || !Room || Room->DropItem(Opener, ToPut[0], Amount))
1134     {
1135       for(uint c = 0; c < Amount; ++c)
1136         ToPut[c]->MoveTo(this);
1137 
1138       ADD_MESSAGE("You put %s in %s.",
1139                   ToPut[0]->GetName(DEFINITE, Amount).CStr(),
1140                   ContainerName.CStr());
1141       Success = true;
1142     }
1143   }
1144 
1145   return Success;
1146 }
1147 
IsOnGround() const1148 truth stack::IsOnGround() const
1149 {
1150   return !MotherEntity || MotherEntity->IsOnGround();
1151 }
1152 
GetSpoiledItems() const1153 int stack::GetSpoiledItems() const
1154 {
1155   int Counter = 0;
1156 
1157   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
1158     Counter += (i->GetSpoilLevel() > 0); // even though this is pretty unclear, it isn't mine but Hex's
1159 
1160   return Counter;
1161 }
1162 
1163 /* Adds all items and recursively their contents
1164    which satisfy the sorter to ItemVector */
1165 
SortAllItems(const sortdata & SortData) const1166 void stack::SortAllItems(const sortdata& SortData) const
1167 {
1168   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
1169     i->SortAllItems(SortData);
1170 }
1171 
1172 /* Search for traps and other secret items */
1173 
Search(ccharacter * Char,int Perception)1174 void stack::Search(ccharacter* Char, int Perception)
1175 {
1176   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
1177     i->Search(Char, Perception);
1178 }
1179 
1180 /* Used to determine whether the danger symbol should be shown */
1181 
NeedDangerSymbol(ccharacter * Viewer) const1182 truth stack::NeedDangerSymbol(ccharacter* Viewer) const
1183 {
1184   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
1185     if(i->NeedDangerSymbol() && i->CanBeSeenBy(Viewer))
1186       return true;
1187 
1188   return false;
1189 }
1190 
PreProcessForBone()1191 void stack::PreProcessForBone()
1192 {
1193   if(Items)
1194   {
1195     itemvector ItemVector;
1196     FillItemVector(ItemVector);
1197 
1198     for(uint c = 0; c < ItemVector.size(); ++c)
1199       ItemVector[c]->PreProcessForBone();
1200   }
1201 }
1202 
PostProcessForBone()1203 void stack::PostProcessForBone()
1204 {
1205   if(Items)
1206   {
1207     itemvector ItemVector;
1208     FillItemVector(ItemVector);
1209 
1210     for(uint c = 0; c < ItemVector.size(); ++c)
1211       ItemVector[c]->PostProcessForBone();
1212   }
1213 }
1214 
FinalProcessForBone()1215 void stack::FinalProcessForBone()
1216 {
1217   /* Items can't be removed during the final processing stage */
1218 
1219   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
1220     i->FinalProcessForBone();
1221 }
1222 
1223 /* VolumeModifier increases the spilled liquid's volume.
1224    Note that the original liquid isn't placed anywhere nor deleted,
1225    but its volume is decreased (possibly to zero). */
1226 
SpillFluid(character * Spiller,liquid * Liquid,long VolumeModifier)1227 void stack::SpillFluid(character* Spiller, liquid* Liquid, long VolumeModifier)
1228 {
1229   if(!Items)
1230     return;
1231 
1232   double ChanceMultiplier = 1. / (300 + sqrt(Volume));
1233   itemvector ItemVector;
1234   FillItemVector(ItemVector);
1235 
1236   for(int c = ItemVector.size() - 1; c >= 0; --c)
1237     if(ItemVector[c]->Exists() && ItemVector[c]->AllowFluids())
1238     {
1239       long ItemVolume = ItemVector[c]->GetVolume();
1240       double Root = sqrt(ItemVolume);
1241 
1242       // in this loop, we need to increase the odds with which something on fire can receive a spilled fluid
1243 
1244       if(ItemVector[c]->IsBurning() || Root > RAND() % 200 || Root > RAND() % 200)
1245       {
1246         long SpillVolume = long(VolumeModifier * Root * ChanceMultiplier);
1247 
1248         if(SpillVolume)
1249         {
1250           Liquid->EditVolume(-Max(SpillVolume, Liquid->GetVolume()));
1251           ItemVector[c]->SpillFluid(Spiller,
1252                                     Liquid->SpawnMoreLiquid(SpillVolume),
1253                                     ItemVector[c]->GetSquareIndex(GetPos()));
1254 
1255           if(!Liquid->GetVolume())
1256             return;
1257         }
1258       }
1259     }
1260 }
1261 
AddItems(const itemvector & ItemVector)1262 void stack::AddItems(const itemvector& ItemVector)
1263 {
1264   for(uint c = 0; c < ItemVector.size(); ++c)
1265     AddItem(ItemVector[c]);
1266 }
1267 
MoveItemsTo(itemvector & ToVector,int RequiredSquarePosition)1268 void stack::MoveItemsTo(itemvector& ToVector, int RequiredSquarePosition)
1269 {
1270   itemvector ItemVector;
1271   FillItemVector(ItemVector);
1272 
1273   for(uint c = 0; c < ItemVector.size(); ++c)
1274     if(ItemVector[c]->GetSquarePosition() == RequiredSquarePosition)
1275     {
1276       ItemVector[c]->RemoveFromSlot();
1277       ToVector.push_back(ItemVector[c]);
1278     }
1279 }
1280 
DropSideItems()1281 void stack::DropSideItems()
1282 {
1283   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
1284   {
1285     int SquarePosition = i->GetSquarePosition();
1286 
1287     if(SquarePosition != CENTER)
1288     {
1289       if(i->IsAnimated())
1290       {
1291         lsquare* Square = GetLSquareTrulyUnder(SquarePosition);
1292 
1293         if(Square)
1294           Square->DecStaticAnimatedEntities();
1295 
1296         GetLSquareUnder()->IncStaticAnimatedEntities();
1297       }
1298 
1299       i->SignalSquarePositionChange(CENTER);
1300       SignalEmitationDecrease(SquarePosition, i->GetEmitation());
1301       SignalEmitationIncrease(CENTER, i->GetEmitation());
1302     }
1303   }
1304 }
1305 
AllowDamage(int Direction,int SquarePosition)1306 truth stack::AllowDamage(int Direction, int SquarePosition)
1307 {
1308   if(SquarePosition == CENTER)
1309     return true;
1310 
1311   switch(Direction)
1312   {
1313    case NORTHWEST: return SquarePosition == DOWN || SquarePosition == RIGHT;
1314    case NORTH: return SquarePosition == DOWN;
1315    case NORTHEAST: return SquarePosition == DOWN || SquarePosition == LEFT;
1316    case WEST: return SquarePosition == RIGHT;
1317    case EAST: return SquarePosition == LEFT;
1318    case SOUTHWEST: return SquarePosition == UP || SquarePosition == RIGHT;
1319    case SOUTH: return SquarePosition == UP;
1320    case SOUTHEAST: return SquarePosition == UP || SquarePosition == LEFT;
1321   }
1322 
1323   return true;
1324 }
1325 
GetWeight(ccharacter * Viewer,int SquarePosition) const1326 long stack::GetWeight(ccharacter* Viewer, int SquarePosition) const
1327 {
1328   long Weight = 0;
1329 
1330   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
1331     if(i->GetSquarePosition() == SquarePosition
1332        && ((Flags & HIDDEN) || i->CanBeSeenBy(Viewer)))
1333       Weight += i->GetWeight();
1334 
1335   return Weight;
1336 }
1337 
DetectMaterial(cmaterial * Material) const1338 truth stack::DetectMaterial(cmaterial* Material) const
1339 {
1340   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
1341     if(i->DetectMaterial(Material))
1342       return true;
1343 
1344   return false;
1345 }
1346 
SetLifeExpectancy(int Base,int RandPlus)1347 void stack::SetLifeExpectancy(int Base, int RandPlus)
1348 {
1349   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
1350     i->SetLifeExpectancy(Base, RandPlus);
1351 }
1352 
Necromancy(character * Necromancer)1353 truth stack::Necromancy(character* Necromancer)
1354 {
1355   itemvector ItemVector;
1356   FillItemVector(ItemVector);
1357 
1358   for(uint c = 0; c < ItemVector.size(); ++c)
1359     if(ItemVector[c]->Necromancy(Necromancer))
1360       return true;
1361 
1362   return false;
1363 }
1364 
CalculateEnchantments()1365 void stack::CalculateEnchantments()
1366 {
1367   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
1368     i->CalculateEnchantment();
1369 }
1370 
FindCarrier() const1371 ccharacter* stack::FindCarrier() const
1372 {
1373   return MotherEntity ? MotherEntity->FindCarrier() : 0;
1374 }
1375 
Haste()1376 void stack::Haste()
1377 {
1378   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
1379     i->Haste();
1380 }
1381 
Slow()1382 void stack::Slow()
1383 {
1384   for(stackiterator i = GetBottom(); i.HasItem(); ++i)
1385     i->Slow();
1386 }
1387