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