cosmetix
[k8-i-v-a-n.git] / src / game / stack.cpp
blob8b7f7d7ed8cf4ef00fe0023b5e5a3b8d636c63e4
1 /*
3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
6 * Public License
8 * See LICENSING which should be included
9 * along with this file for more details
12 /* Compiled through slotset.cpp */
15 /* If REMEMBER_SELECTED flag is used, DrawContents() will use this to determine the initial selected item */
17 int stack::Selected;
20 stack::stack (square *MotherSquare, entity* MotherEntity, feuLong Flags) :
21 Bottom(0), Top(0), MotherSquare(MotherSquare), MotherEntity(MotherEntity),
22 Volume(0), Weight(0), Emitation(0), Flags(Flags), Items(0)
27 stack::~stack () { Clean(true); }
30 square *stack::GetSquareUnder () const { return !MotherEntity ? MotherSquare : MotherEntity->GetSquareUnderEntity(); }
33 /* Modifies the square index bits of BlitData.CustomData */
34 void stack::Draw (ccharacter *Viewer, blitdata &BlitData, int RequiredSquarePosition) const {
35 if (!Items) return;
36 int VisibleItems = 0;
37 v2 StackPos = GetPos();
38 for (stackiterator i = GetBottom(); i.HasItem(); ++i) {
39 if (i->GetSquarePosition() == RequiredSquarePosition && (i->CanBeSeenBy(Viewer) || game::GetSeeWholeMapCheatMode())) {
40 BlitData.CustomData |= i->GetSquareIndex(StackPos);
41 i->Draw(BlitData);
42 BlitData.CustomData &= ~SQUARE_INDEX_MASK;
43 ++VisibleItems;
46 if (RequiredSquarePosition == CENTER) {
47 truth PlusSymbol = VisibleItems > 1, Dangerous = NeedDangerSymbol(Viewer);
48 if (PlusSymbol || Dangerous) {
49 col24 L = BlitData.Luminance;
50 BlitData.Luminance = ivanconfig::GetContrastLuminance();
51 BlitData.Src.Y = 16;
52 if (PlusSymbol) igraph::GetSymbolGraphic()->LuminanceMaskedBlit(BlitData);
53 if (Dangerous) {
54 BlitData.Src.X = 160;
55 igraph::GetSymbolGraphic()->LuminanceMaskedBlit(BlitData);
57 BlitData.Src.X = BlitData.Src.Y = 0; /// check
58 BlitData.Luminance = L;
64 void stack::AddItem (item *ToBeAdded, truth RunRoomEffects) {
65 if (!ToBeAdded) return;
66 AddElement(ToBeAdded);
67 if (Flags & HIDDEN) return;
68 lsquare *SquareUnder = GetLSquareTrulyUnder(ToBeAdded->GetSquarePosition());
69 if (!SquareUnder) return;
70 if (ToBeAdded->IsAnimated()) SquareUnder->IncStaticAnimatedEntities();
71 if (!game::IsGenerating()) {
72 if (RunRoomEffects && GetLSquareUnder()->GetRoom()) GetLSquareUnder()->GetRoom()->AddItemEffect(ToBeAdded);
73 SquareUnder->SendNewDrawRequest();
74 SquareUnder->SendMemorizedUpdateRequest();
79 void stack::RemoveItem (stackslot *Slot) {
80 item *Item = Slot->GetItem();
81 truth WasAnimated = Item->IsAnimated();
82 col24 Emit = Item->GetEmitation();
83 RemoveElement(Slot);
84 SignalVolumeAndWeightChange();
85 SignalEmitationDecrease(Item->GetSquarePosition(), Emit);
86 if (Flags & HIDDEN) return;
87 lsquare *SquareUnder = GetLSquareTrulyUnder(Item->GetSquarePosition());
88 if (Item->GetSquarePosition() != CENTER) Item->SignalSquarePositionChange(CENTER);
89 if (!SquareUnder) return;
90 if (WasAnimated) SquareUnder->DecStaticAnimatedEntities();
91 if (!game::IsGenerating()) {
92 SquareUnder->SendNewDrawRequest();
93 SquareUnder->SendMemorizedUpdateRequest();
98 /* Removes all items. LastClean should be true only if the stack is being deleted (the default is false) */
99 void stack::Clean (truth LastClean) {
100 if (!Items) return;
101 stackslot *Slot = Bottom;
102 if (!LastClean) {
103 Bottom = Top = 0;
104 Volume = Weight = Items = 0;
105 SignalVolumeAndWeightChange();
107 while (Slot) {
108 item *Item = Slot->GetItem();
109 if (!(Flags & HIDDEN) && Item->IsAnimated() && !LastClean) {
110 lsquare *Square = GetLSquareTrulyUnder(Item->GetSquarePosition());
111 if (Square) Square->DecStaticAnimatedEntities();
113 if (LastClean && Item->GetSquaresUnder() == 1) delete Item; else Item->SendToHell();
114 stackslot *Rubbish = Slot;
115 Slot = Slot->Next;
116 delete Rubbish;
117 if (!LastClean) SignalEmitationDecrease(Item->GetSquarePosition(), Item->GetEmitation());
122 void stack::Save (outputfile &SaveFile) const {
123 if (!Items) {
124 SaveFile << uShort(0);
125 return;
127 uShort SavedItems = 0;
128 for (stackiterator i1 = GetBottom(); i1.HasItem(); ++i1) if (i1->IsMainSlot(&i1.GetSlot())) ++SavedItems;
129 SaveFile << SavedItems;
130 /* Save multitiled items only to one stack */
131 for (stackiterator i2 = GetBottom(); i2.HasItem(); ++i2) if (i2->IsMainSlot(&i2.GetSlot())) SaveFile << i2.GetSlot();
135 void stack::Load (inputfile &SaveFile) {
136 uShort SavedItems = 0;
137 SaveFile >> SavedItems;
138 for (int c = 0; c < SavedItems; ++c) {
139 if (!c && !Items) Bottom = Top = new stackslot(this, 0);
140 else Top = Top->Next = new stackslot(this, Top);
141 SaveFile >> *Top;
142 Volume += (*Top)->GetVolume();
143 Weight += (*Top)->GetWeight();
144 if ((*Top)->GetSquarePosition() == CENTER) Emitation = game::CombineConstLights(Emitation, (*Top)->GetEmitation());
146 Items += SavedItems;
150 v2 stack::GetPos () const { return GetSquareUnder()->GetPos(); }
153 /* Returns whether there are any items satisfying the sorter or any visible items if it is zero */
154 truth stack::SortedItems (ccharacter *Viewer, sorter SorterFunction) const {
155 if (Items) {
156 for (stackiterator i = GetBottom(); i.HasItem(); ++i) {
157 if ((SorterFunction == 0 || ((*i)->*SorterFunction)(Viewer)) && ((Flags & HIDDEN) || i->CanBeSeenBy(Viewer))) return true;
160 return false;
164 void stack::BeKicked (character *Kicker, int KickDamage, int Direction) {
165 if (KickDamage) {
166 ReceiveDamage(Kicker, KickDamage, PHYSICAL_DAMAGE, Direction);
167 if (GetItems() && GetLSquareUnder()->IsFlyable()) {///&& SquarePosition == CENTER)
168 item *Item1 = *GetTop();
169 item *Item2 = RAND() & 1 && GetItems() > 1 ? *--GetTop() : 0;
170 Item1->Fly(Kicker, Direction, KickDamage*3);
171 if (Item2) {
172 /*k8: if(!Item2->Exists() || Item2->GetPos() != GetPos()) int esko = esko = 2; */
173 if (!(!Item2->Exists() || Item2->GetPos() != GetPos())) Item2->Fly(Kicker, Direction, KickDamage * 3);
176 } else if (Kicker->IsPlayer() && GetNativeVisibleItems(Kicker)) {
177 ADD_MESSAGE("Your weak kick has no effect.");
182 void stack::Polymorph (character *Polymorpher) {
183 itemvector ItemVector;
184 FillItemVector(ItemVector);
185 int p = 0;
186 for (uInt c = 0; c < ItemVector.size(); ++c) {
187 if (ItemVector[c]->Exists() && ItemVector[c]->Polymorph(Polymorpher, this) && ++p == 5) break;
192 void stack::CheckForStepOnEffect (character *Stepper) {
193 itemvector ItemVector;
194 FillItemVector(ItemVector);
195 for (uInt c = 0; c < ItemVector.size(); ++c) {
196 if (ItemVector[c]->Exists()) {
197 ItemVector[c]->StepOnEffect(Stepper);
198 if (!Stepper->IsEnabled()) return;
204 lsquare *stack::GetLSquareTrulyUnder (int SquarePosition) const {
205 switch (SquarePosition) {
206 case DOWN:
207 if (GetArea()->IsValidPos(GetPos()+v2(0, 1))) return GetNearLSquare(GetPos() + v2(0, 1));
208 return 0;
209 case LEFT:
210 if (GetArea()->IsValidPos(GetPos()+v2(-1, 0))) return GetNearLSquare(GetPos() + v2(-1, 0));
211 return 0;
212 case UP:
213 if (GetArea()->IsValidPos(GetPos()+v2(0, -1))) return GetNearLSquare(GetPos() + v2(0, -1));
214 return 0;
215 case RIGHT:
216 if (GetArea()->IsValidPos(GetPos()+v2(1, 0))) return GetNearLSquare(GetPos() + v2(1, 0));
217 return 0;
219 return GetLSquareUnder();
223 void stack::ReceiveDamage (character *Damager, int Damage, int Type, int Direction) {
224 itemvector ItemVector;
225 FillItemVector(ItemVector);
226 for (uInt c = 0; c < ItemVector.size(); ++c)
227 if (ItemVector[c]->Exists() && AllowDamage(Direction, ItemVector[c]->GetSquarePosition()))
228 ItemVector[c]->ReceiveDamage(Damager, Damage, Type);
232 void stack::TeleportRandomly (uInt Amount) {
233 itemvector ItemVector;
234 FillItemVector(ItemVector);
235 for (uInt c = 0; c < ItemVector.size() && c < Amount; ++c)
236 if (ItemVector[c]->Exists()) {
237 if (ItemVector[c]->CanBeSeenByPlayer()) ADD_MESSAGE("%s disappears!", ItemVector[c]->GetExtendedDescription().CStr());
238 ItemVector[c]->TeleportRandomly();
243 /* ItemVector receives all items in the stack */
244 void stack::FillItemVector (itemvector &ItemVector) const {
245 for (stackiterator i = GetBottom(); i.HasItem(); ++i) ItemVector.push_back(*i);
249 /* Don't use; this function is only for gum solutions */
250 item *stack::GetItem (int I) const {
251 int c = 0;
252 for (stackiterator i = GetBottom(); i.HasItem(); ++i, ++c) if (c == I) return *i;
253 return 0;
257 /* Don't use; this function is only for gum solutions */
258 int stack::SearchItem (item *ToBeSearched) const {
259 int c = 0;
260 for (stackiterator i = GetBottom(); i.HasItem(); ++i, ++c) if (*i == ToBeSearched) return c;
261 return -1;
265 /* Flags for all DrawContents functions can be found in ivandef.h.
266 Those returning int return 0 on success and a felist error
267 otherwise (see felibdef.h) */
268 item *stack::DrawContents (ccharacter *Viewer, cfestring &Topic, int Flags, sorter SorterFunction) const {
269 itemvector ReturnVector;
270 DrawContents(ReturnVector, 0, Viewer, Topic, CONST_S(""), CONST_S(""), CONST_S(""), 0, Flags|NO_MULTI_SELECT, SorterFunction);
271 return ReturnVector.empty() ? 0 : ReturnVector[0];
275 int stack::DrawContents (itemvector &ReturnVector, ccharacter *Viewer, cfestring &Topic, int Flags, sorter SorterFunction) const {
276 return DrawContents(ReturnVector, 0, Viewer, Topic, CONST_S(""), CONST_S(""), CONST_S(""), 0, Flags, SorterFunction);
280 /* MergeStack is used for showing two stacks together. Like when eating when
281 there are items on the ground and in the character's stack */
282 int stack::DrawContents (itemvector &ReturnVector, stack *MergeStack,
283 ccharacter *Viewer, cfestring &Topic, cfestring &ThisDesc, cfestring &ThatDesc,
284 cfestring &SpecialDesc, col16 SpecialDescColor, int Flags, sorter SorterFunction) const
286 felist Contents(Topic);
287 lsquare *Square = GetLSquareUnder();
288 stack *AdjacentStack[4] = { 0, 0, 0, 0 };
289 int c;
290 if (!(this->Flags & HIDDEN))
291 for (c = 0; c < 4; ++c)
292 AdjacentStack[c] = Square->GetStackOfAdjacentSquare(c);
293 if (!SpecialDesc.IsEmpty()) {
294 Contents.AddDescription(CONST_S(""));
295 Contents.AddDescription(SpecialDesc.CapitalizeCopy(), SpecialDescColor);
298 if (!(Flags & NO_SPECIAL_INFO)) {
299 Contents.AddDescription(CONST_S(""));
300 sLong Weight = GetWeight(Viewer, CENTER);
301 if (MergeStack) Weight += MergeStack->GetWeight(Viewer, CENTER);
302 for (c = 0; c < 4; ++c)
303 if (AdjacentStack[c])
304 Weight += AdjacentStack[c]->GetWeight(Viewer, 3 - c);
305 Contents.AddDescription(CONST_S("Overall weight: ") + Weight + " grams");
308 if (Flags & NONE_AS_CHOICE) {
309 int ImageKey = game::AddToItemDrawVector(itemvector());
310 Contents.AddEntry(CONST_S("none"), LIGHT_GRAY, 0, ImageKey);
312 if (MergeStack) MergeStack->AddContentsToList(Contents, Viewer, ThatDesc, Flags, CENTER, SorterFunction);
313 AddContentsToList(Contents, Viewer, ThisDesc, Flags, CENTER, SorterFunction);
314 static cchar *WallDescription[] = { "western", "southern", "nothern", "eastern" };
315 for (c = 0; c < 4; ++c)
316 if (AdjacentStack[c])
317 AdjacentStack[c]->AddContentsToList(Contents, Viewer, CONST_S("Items on the ")+WallDescription[c] + " wall:", Flags, 3-c, SorterFunction);
319 game::SetStandardListAttributes(Contents);
320 Contents.SetPageLength(12);
321 Contents.RemoveFlags(BLIT_AFTERWARDS);
322 Contents.SetEntryDrawer(game::ItemEntryDrawer);
324 if (!(Flags & NO_SELECT)) Contents.AddFlags(SELECTABLE);
326 if (Flags & REMEMBER_SELECTED) Contents.SetSelected(GetSelected());
328 game::DrawEverythingNoBlit(); //doesn't prevent mirage puppies
329 int Chosen = Contents.Draw();
330 game::ClearItemDrawVector();
332 if (Chosen & FELIST_ERROR_BIT) {
333 Selected = 0;
334 return Chosen;
335 } else {
336 Selected = Chosen;
339 int Pos = 0;
341 if (Flags & NONE_AS_CHOICE) {
342 if (!Selected) return 0;
343 ++Pos;
346 if (MergeStack) {
347 Pos = MergeStack->SearchChosen(ReturnVector, Viewer, Pos, Selected, Flags, CENTER, SorterFunction);
348 if (!ReturnVector.empty()) return 0;
351 Pos = SearchChosen(ReturnVector, Viewer, Pos, Selected, Flags, CENTER, SorterFunction);
353 if (!ReturnVector.empty()) return 0;
355 for (c = 0; c < 4; ++c)
356 if (AdjacentStack[c]) {
357 AdjacentStack[c]->SearchChosen(ReturnVector, Viewer, Pos, Selected, Flags, 3-c, SorterFunction);
358 if (!ReturnVector.empty()) break;
361 return 0;
365 /* Internal function to fill Contents list */
366 void stack::AddContentsToList (felist &Contents, ccharacter *Viewer, cfestring &Desc, int Flags,
367 int RequiredSquarePosition, sorter SorterFunction) const
369 itemvectorvector PileVector;
370 Pile(PileVector, Viewer, RequiredSquarePosition, SorterFunction);
371 truth DrawDesc = Desc.GetSize();
372 sLong LastCategory = 0;
373 festring Entry;
374 for (uInt p = 0; p < PileVector.size(); ++p) {
375 if (DrawDesc) {
376 if (!Contents.IsEmpty()) Contents.AddEntry(CONST_S(""), WHITE, 0, NO_IMAGE, false);
377 Contents.AddEntry(Desc, WHITE, 0, NO_IMAGE, false);
378 Contents.AddEntry(CONST_S(""), WHITE, 0, NO_IMAGE, false);
379 DrawDesc = false;
381 item *Item = PileVector[p].back();
382 if (Item->GetCategory() != LastCategory) {
383 LastCategory = Item->GetCategory();
384 Contents.AddEntry(item::GetItemCategoryName(LastCategory),
385 LIGHT_GRAY, 0, NO_IMAGE, false);
387 Entry.Empty();
388 Item->AddInventoryEntry(Viewer, Entry, PileVector[p].size(), !(Flags & NO_SPECIAL_INFO));
389 int ImageKey = game::AddToItemDrawVector(PileVector[p]);
390 Contents.AddEntry(Entry, LIGHT_GRAY, 0, ImageKey);
395 /* Internal function which fills ReturnVector according to Chosen,
396 which is given by felist::Draw, and possibly the user's additional
397 input about item amount. */
398 int stack::SearchChosen (itemvector &ReturnVector, ccharacter *Viewer, int Pos, int Chosen, int Flags,
399 int RequiredSquarePosition, sorter SorterFunction) const
401 /* Not really efficient... :( */
402 itemvectorvector PileVector;
403 Pile(PileVector, Viewer, RequiredSquarePosition, SorterFunction);
404 for (uInt p = 0; p < PileVector.size(); ++p) {
405 if (Pos++ == Chosen) {
406 if (Flags & NO_MULTI_SELECT) {
407 int Amount = (Flags & SELECT_PAIR && PileVector[p][0]->HandleInPairs() && PileVector[p].size() >= 2 ? 2 : 1);
408 ReturnVector.assign(PileVector[p].end() - Amount, PileVector[p].end());
409 return -1;
410 } else {
411 int Amount = PileVector[p].size();
412 if (Amount > 1) {
413 Amount = game::ScrollBarQuestion(CONST_S("How many ")+PileVector[p][0]->GetName(PLURAL)+'?',
414 Amount, 1, 0, Amount, 0, WHITE,
415 LIGHT_GRAY, DARK_GRAY);
417 ReturnVector.assign(PileVector[p].end() - Amount, PileVector[p].end());
418 return -1;
422 return Pos;
426 truth stack::RaiseTheDead (character *Summoner) {
427 itemvector ItemVector;
428 FillItemVector(ItemVector);
429 for (uInt c = 0; c < ItemVector.size(); ++c) if (ItemVector[c]->RaiseTheDead(Summoner)) return true;
430 return false;
434 /* Returns false if the Applier didn't try to use the key */
435 truth stack::TryKey (item *Key, character *Applier) {
436 if (!Applier->IsPlayer()) return false;
437 item *ToBeOpened = DrawContents(Applier, CONST_S("Where do you wish to use the key?"), 0, &item::HasLock);
438 if (!ToBeOpened) return false;
439 return ToBeOpened->TryKey(Key, Applier);
443 /* Returns false if the Applier didn't try to open anything */
444 truth stack::Open (character *Opener) {
445 if (!Opener->IsPlayer()) return false;
446 item *ToBeOpened = DrawContents(Opener, CONST_S("What do you wish to open?"), 0, &item::IsOpenable);
447 return ToBeOpened ? ToBeOpened->Open(Opener) : false;
451 int stack::GetSideItems (int RequiredSquarePosition) const {
452 int VisibleItems = 0;
453 for (stackiterator i = GetBottom(); i.HasItem(); ++i)
454 if (i->GetSquarePosition() == RequiredSquarePosition) ++VisibleItems;
455 return VisibleItems;
459 int stack::GetVisibleItems (ccharacter *Viewer) const {
460 int VisibleItems = 0;
461 for (stackiterator i = GetBottom(); i.HasItem(); ++i)
462 if (i->GetSquarePosition() == CENTER && i->CanBeSeenBy(Viewer)) ++VisibleItems;
463 lsquare *Square = GetLSquareUnder();
464 for (int c = 0; c < 4; ++c) {
465 stack *Stack = Square->GetStackOfAdjacentSquare(c);
466 if (Stack) VisibleItems += Stack->GetVisibleSideItems(Viewer, 3-c);
468 return VisibleItems;
472 void stack::GetVisibleItemsV (ccharacter *Viewer, std::vector<item *> &vi) {
473 for (stackiterator i = GetBottom(); i.HasItem(); ++i) {
474 if (i->GetSquarePosition() == CENTER && i->CanBeSeenBy(Viewer)) vi.push_back(*i);
476 lsquare *Square = GetLSquareUnder();
477 for (int c = 0; c < 4; ++c) {
478 stack *Stack = Square->GetStackOfAdjacentSquare(c);
479 if (Stack) {
480 //VisibleItems += Stack->GetVisibleSideItems(Viewer, 3-c);
481 for (stackiterator i = GetBottom(); i.HasItem(); ++i) {
482 if (i->GetSquarePosition() == (3-c) && i->CanBeSeenBy(Viewer)) vi.push_back(*i);
489 truth stack::HasSomethingFunny (ccharacter *Viewer, truth seeCorpses, truth seeUnstepped) {
490 std::vector<item *> vi;
492 GetVisibleItemsV(Viewer, vi);
493 for (unsigned int f = 0; f < vi.size(); f++) {
494 if (!vi[f]->CanBePickedUp()) continue;
495 //fprintf(stderr, "::%s %s\n", vi[f]->IsSteppedOn()?"T":"o", seeUnstepped?"T":"o");
496 if (!seeUnstepped && vi[f]->IsSteppedOn()) continue;
497 if (seeCorpses || (!vi[f]->IsCorpse() && !vi[f]->IsBodyPart())) return true;
499 return false;
503 void stack::SetSteppedOn (truth v) {
504 for (stackiterator i = GetBottom(); i.HasItem(); ++i) {
505 if (i->GetSquarePosition() == CENTER) i->SetSteppedOn(v);
507 lsquare *Square = GetLSquareUnder();
508 for (int c = 0; c < 4; ++c) {
509 stack *Stack = Square->GetStackOfAdjacentSquare(c);
510 if (Stack) {
511 //VisibleItems += Stack->GetVisibleSideItems(Viewer, 3-c);
512 for (stackiterator i = GetBottom(); i.HasItem(); ++i) {
513 if (i->GetSquarePosition() == (3-c)) i->SetSteppedOn(v);
517 //fprintf(stderr, "STON!\n");
521 int stack::GetNativeVisibleItems (ccharacter *Viewer) const {
522 if (Flags & HIDDEN) return Items;
523 int VisibleItems = 0;
524 for (stackiterator i = GetBottom(); i.HasItem(); ++i) if (i->CanBeSeenBy(Viewer)) ++VisibleItems;
525 return VisibleItems;
529 int stack::GetVisibleSideItems (ccharacter *Viewer, int RequiredSquarePosition) const {
530 int VisibleItems = 0;
531 for (stackiterator i = GetBottom(); i.HasItem(); ++i)
532 if (i->GetSquarePosition() == RequiredSquarePosition && i->CanBeSeenBy(Viewer)) ++VisibleItems;
533 return VisibleItems;
537 item *stack::GetBottomVisibleItem (ccharacter *Viewer) const {
538 for (stackiterator i = GetBottom(); i.HasItem(); ++i)
539 if ((Flags & HIDDEN) || i->CanBeSeenBy(Viewer)) return *i;
540 return 0;
544 void stack::SignalVolumeAndWeightChange () {
545 if (!(Flags & FREEZED)) {
546 CalculateVolumeAndWeight();
547 if (MotherEntity) MotherEntity->SignalVolumeAndWeightChange();
552 void stack::CalculateVolumeAndWeight () {
553 Volume = Weight = 0;
554 for (stackiterator i = GetBottom(); i.HasItem(); ++i) {
555 Volume += i->GetVolume();
556 Weight += i->GetWeight();
561 void stack::SignalEmitationIncrease (int ItemSquarePosition, col24 EmitationUpdate) {
562 if (ItemSquarePosition < CENTER) {
563 stack *Stack = GetLSquareUnder()->GetStackOfAdjacentSquare(ItemSquarePosition);
564 if (Stack) Stack->SignalEmitationIncrease(CENTER, EmitationUpdate);
565 return;
567 if (!(Flags & FREEZED) && game::CompareLights(EmitationUpdate, Emitation) > 0) {
568 Emitation = game::CombineConstLights(Emitation, EmitationUpdate);
569 if (MotherEntity) {
570 if (MotherEntity->AllowContentEmitation()) MotherEntity->SignalEmitationIncrease(EmitationUpdate);
571 } else {
572 GetLSquareUnder()->SignalEmitationIncrease(EmitationUpdate);
578 void stack::SignalEmitationDecrease (int ItemSquarePosition, col24 EmitationUpdate) {
579 if (ItemSquarePosition < CENTER) {
580 stack *Stack = GetLSquareUnder()->GetStackOfAdjacentSquare(ItemSquarePosition);
581 if (Stack) Stack->SignalEmitationDecrease(CENTER, EmitationUpdate);
582 return;
584 if (!(Flags & FREEZED) && Emitation && game::CompareLights(EmitationUpdate, Emitation) >= 0) {
585 col24 Backup = Emitation;
586 CalculateEmitation();
587 if (Backup != Emitation) {
588 if (MotherEntity) {
589 if (MotherEntity->AllowContentEmitation()) MotherEntity->SignalEmitationDecrease(EmitationUpdate);
590 } else {
591 GetLSquareUnder()->SignalEmitationDecrease(EmitationUpdate);
598 void stack::CalculateEmitation () {
599 Emitation = GetSideEmitation(CENTER);
603 col24 stack::GetSideEmitation (int RequiredSquarePosition) {
604 col24 Emitation = 0;
605 for (stackiterator i = GetBottom(); i.HasItem(); ++i)
606 if (i->GetSquarePosition() == RequiredSquarePosition) game::CombineLights(Emitation, i->GetEmitation());
607 return Emitation;
611 truth stack::CanBeSeenBy (ccharacter *Viewer, int SquarePosition) const {
612 if (MotherEntity) return MotherEntity->ContentsCanBeSeenBy(Viewer);
613 lsquare *Square = GetLSquareTrulyUnder(SquarePosition);
614 return Viewer->IsOver(Square->GetPos()) || Square->CanBeSeenBy(Viewer);
618 truth stack::IsDangerous (ccharacter *Stepper) const {
619 for (stackiterator i = GetBottom(); i.HasItem(); ++i)
620 if (i->IsDangerous(Stepper) && i->CanBeSeenBy(Stepper)) return true;
621 return false;
625 /* Returns true if something was duplicated.
626 Max is the cap of items to be affected */
627 truth stack::Duplicate (int Max, feuLong Flags) {
628 if (!GetItems()) return false;
629 itemvector ItemVector;
630 FillItemVector(ItemVector);
631 int p = 0;
632 for (uInt c = 0; c < ItemVector.size(); ++c)
633 if (ItemVector[c]->Exists() && ItemVector[c]->DuplicateToStack(this, Flags) && ++p == Max) break;
634 return p > 0;
638 /* Adds the item without any external update requests */
639 void stack::AddElement (item *Item, truth) {
640 ++Items;
641 /* "I love writing illegible code." - Guy who wrote this */
642 (Top = (Bottom ? Top->Next : Bottom) = new stackslot(this, Top))->PutInItem(Item);
646 /* Removes the slot without any external update requests */
647 void stack::RemoveElement (stackslot *Slot) {
648 --Items;
649 (Slot->Last ? Slot->Last->Next : Bottom) = Slot->Next;
650 (Slot->Next ? Slot->Next->Last : Top) = Slot->Last;
651 delete Slot;
655 void stack::MoveItemsTo (stack *Stack) {
656 while (Items) GetBottom()->MoveTo(Stack);
660 void stack::MoveItemsTo (slot *Slot) {
661 while (Items) Slot->AddFriendItem(*GetBottom());
665 item *stack::GetBottomItem (ccharacter *Char, truth ForceIgnoreVisibility) const {
666 if ((Flags & HIDDEN) || ForceIgnoreVisibility) return Bottom ? **Bottom : 0;
667 return GetBottomVisibleItem(Char);
671 item *stack::GetBottomSideItem (ccharacter *Char, int RequiredSquarePosition, truth ForceIgnoreVisibility) const {
672 for (stackiterator i = GetBottom(); i.HasItem(); ++i) {
673 if ((i->GetSquarePosition() == RequiredSquarePosition && (Flags & HIDDEN)) ||
674 ForceIgnoreVisibility || i->CanBeSeenBy(Char)) return *i;
676 return 0;
680 truth CategorySorter (const itemvector &V1, const itemvector &V2) {
681 return (*V1.begin())->GetCategory() < (*V2.begin())->GetCategory();
685 /* Slow function which sorts the stack's contents to a vector of piles
686 (itemvectors) of which elements are similiar to each other, for instance
687 4 bananas */
688 void stack::Pile (itemvectorvector &PileVector, ccharacter *Viewer,
689 int RequiredSquarePosition, sorter SorterFunction) const
691 if (!Items) return;
692 std::list<item *> List;
693 for (stackiterator s = GetBottom(); s.HasItem(); ++s)
694 if (s->GetSquarePosition() == RequiredSquarePosition &&
695 (SorterFunction == 0 || ((*s)->*SorterFunction)(Viewer)) &&
696 ((Flags & HIDDEN) || s->CanBeSeenBy(Viewer)))
697 List.push_back(*s);
698 for (std::list<item*>::iterator i = List.begin(); i != List.end(); ++i) {
699 PileVector.resize(PileVector.size()+1);
700 itemvector& Pile = PileVector.back();
701 Pile.push_back(*i);
702 if ((*i)->CanBePiled()) {
703 std::list<item*>::iterator j = i;
704 for (++j; j != List.end(); ) {
705 if ((*j)->CanBePiled() && (*i)->CanBePiledWith(*j, Viewer)) {
706 Pile.push_back(*j);
707 std::list<item *>::iterator Dirt = j++;
708 List.erase(Dirt);
709 } else {
710 ++j;
715 std::stable_sort(PileVector.begin(), PileVector.end(), CategorySorter);
719 /* Total price of the stack */
720 sLong stack::GetTruePrice () const {
721 sLong Price = 0;
722 for (stackiterator i = GetBottom(); i.HasItem(); ++i) Price += i->GetTruePrice();
723 return Price;
727 /* GUI used for instance by chests and bookcases.
728 Returns whether anything was done. */
729 truth stack::TakeSomethingFrom (character *Opener, cfestring &ContainerName) {
730 if (!GetItems()) {
731 ADD_MESSAGE("There is nothing in %s.", ContainerName.CStr());
732 return false;
734 truth Success = false;
735 room *Room = GetLSquareUnder()->GetRoom();
736 SetSelected(0);
737 for (;;) {
738 itemvector ToTake;
739 game::DrawEverythingNoBlit();
740 DrawContents(ToTake, Opener, CONST_S("What do you want to take from ")+ContainerName+'?', REMEMBER_SELECTED);
741 if (ToTake.empty()) break;
742 if (!IsOnGround() || !Room || Room->PickupItem(Opener, ToTake[0], ToTake.size())) {
743 for (uInt c = 0; c < ToTake.size(); ++c) ToTake[c]->MoveTo(Opener->GetStack());
744 ADD_MESSAGE("You take %s from %s.", ToTake[0]->GetName(DEFINITE, ToTake.size()).CStr(), ContainerName.CStr());
745 Success = true;
748 return Success;
752 /* GUI used for instance by chests and bookcases (use ContainerID == 0 if
753 the container isn't an item). Returns whether anything was done. */
754 truth stack::PutSomethingIn (character *Opener, cfestring &ContainerName, sLong StorageVolume, feuLong ContainerID) {
755 if (!Opener->GetStack()->GetItems()) {
756 ADD_MESSAGE("You have nothing to put in %s.", ContainerName.CStr());
757 return false;
759 truth Success = false;
760 room *Room = GetLSquareUnder()->GetRoom();
761 SetSelected(0);
762 for (;;) {
763 itemvector ToPut;
764 game::DrawEverythingNoBlit();
765 Opener->GetStack()->DrawContents(ToPut, Opener, CONST_S("What do you want to put in ")+ContainerName+'?', REMEMBER_SELECTED);
766 if (ToPut.empty()) break;
767 if (ToPut[0]->GetID() == ContainerID) {
768 ADD_MESSAGE("You can't put %s inside itself!", ContainerName.CStr());
769 continue;
771 uInt Amount = Min<uInt>((StorageVolume-GetVolume())/ToPut[0]->GetVolume(), ToPut.size());
772 if (!Amount) {
773 if (ToPut.size() == 1) ADD_MESSAGE("%s doesn't fit in %s.", ToPut[0]->CHAR_NAME(DEFINITE), ContainerName.CStr());
774 else ADD_MESSAGE("None of the %d %s fit in %s.", int(ToPut.size()), ToPut[0]->CHAR_NAME(PLURAL), ContainerName.CStr());
775 continue;
777 if (Amount != ToPut.size())
778 ADD_MESSAGE("Only %d of the %d %s fit%s in %s.", Amount, int(ToPut.size()), ToPut[0]->CHAR_NAME(PLURAL), Amount == 1 ? "s" : "", ContainerName.CStr());
779 if (!IsOnGround() || !Room || Room->DropItem(Opener, ToPut[0], Amount)) {
780 for (uInt c = 0; c < Amount; ++c) ToPut[c]->MoveTo(this);
781 ADD_MESSAGE("You put %s in %s.", ToPut[0]->GetName(DEFINITE, Amount).CStr(), ContainerName.CStr());
782 Success = true;
785 return Success;
789 truth stack::IsOnGround () const {
790 return !MotherEntity || MotherEntity->IsOnGround();
794 int stack::GetSpoiledItems () const {
795 int Counter = 0;
796 for (stackiterator i = GetBottom(); i.HasItem(); ++i)
797 Counter += (i->GetSpoilLevel() > 0); // even though this is pretty unclear, it isn't mine but Hex's
798 return Counter;
802 /* Adds all items and recursively their contents
803 which satisfy the sorter to ItemVector */
804 void stack::SortAllItems (const sortdata &SortData) const {
805 for (stackiterator i = GetBottom(); i.HasItem(); ++i) i->SortAllItems(SortData);
809 /* Search for traps and other secret items */
810 void stack::Search (ccharacter *Char, int Perception) {
811 for (stackiterator i = GetBottom(); i.HasItem(); ++i) i->Search(Char, Perception);
815 /* Used to determine whether the danger symbol should be shown */
816 truth stack::NeedDangerSymbol (ccharacter *Viewer) const {
817 for (stackiterator i = GetBottom(); i.HasItem(); ++i)
818 if (i->NeedDangerSymbol() && i->CanBeSeenBy(Viewer)) return true;
819 return false;
823 void stack::PreProcessForBone () {
824 if (Items) {
825 itemvector ItemVector;
826 FillItemVector(ItemVector);
827 for (uInt c = 0; c < ItemVector.size(); ++c) ItemVector[c]->PreProcessForBone();
832 void stack::PostProcessForBone () {
833 if (Items) {
834 itemvector ItemVector;
835 FillItemVector(ItemVector);
836 for (uInt c = 0; c < ItemVector.size(); ++c) ItemVector[c]->PostProcessForBone();
841 void stack::FinalProcessForBone () {
842 /* Items can't be removed during the final processing stage */
843 for (stackiterator i = GetBottom(); i.HasItem(); ++i) i->FinalProcessForBone();
847 /* VolumeModifier increases the spilled liquid's volume.
848 Note that the original liquid isn't placed anywhere nor deleted,
849 but its volume is decreased (possibly to zero). */
850 void stack::SpillFluid (character *Spiller, liquid *Liquid, sLong VolumeModifier) {
851 if (!Items) return;
852 double ChanceMultiplier = 1.0/(300+sqrt(Volume));
853 itemvector ItemVector;
854 FillItemVector(ItemVector);
855 for (int c = ItemVector.size()-1; c >= 0; --c) {
856 if (ItemVector[c]->Exists() && ItemVector[c]->AllowFluids()) {
857 sLong ItemVolume = ItemVector[c]->GetVolume();
858 double Root = sqrt(ItemVolume);
859 if (Root > RAND() % 200 || Root > RAND() % 200) {
860 sLong SpillVolume = sLong(VolumeModifier*Root*ChanceMultiplier);
861 if (SpillVolume) {
862 Liquid->EditVolume(-Max(SpillVolume, Liquid->GetVolume()));
863 ItemVector[c]->SpillFluid(Spiller, Liquid->SpawnMoreLiquid(SpillVolume), ItemVector[c]->GetSquareIndex(GetPos()));
864 if (!Liquid->GetVolume()) return;
872 void stack::AddItems (const itemvector &ItemVector) {
873 for (uInt c = 0; c < ItemVector.size(); ++c) AddItem(ItemVector[c]);
877 void stack::MoveItemsTo (itemvector &ToVector, int RequiredSquarePosition) {
878 itemvector ItemVector;
879 FillItemVector(ItemVector);
880 for (uInt c = 0; c < ItemVector.size(); ++c) {
881 if (ItemVector[c]->GetSquarePosition() == RequiredSquarePosition) {
882 ItemVector[c]->RemoveFromSlot();
883 ToVector.push_back(ItemVector[c]);
889 void stack::DropSideItems () {
890 for (stackiterator i = GetBottom(); i.HasItem(); ++i) {
891 int SquarePosition = i->GetSquarePosition();
892 if (SquarePosition != CENTER) {
893 if (i->IsAnimated()) {
894 lsquare *Square = GetLSquareTrulyUnder(SquarePosition);
895 if (Square) Square->DecStaticAnimatedEntities();
896 GetLSquareUnder()->IncStaticAnimatedEntities();
898 i->SignalSquarePositionChange(CENTER);
899 SignalEmitationDecrease(SquarePosition, i->GetEmitation());
900 SignalEmitationIncrease(CENTER, i->GetEmitation());
906 truth stack::AllowDamage (int Direction, int SquarePosition) {
907 if (SquarePosition == CENTER) return true;
908 switch (Direction) {
909 case 0: return SquarePosition == DOWN || SquarePosition == RIGHT;
910 case 1: return SquarePosition == DOWN;
911 case 2: return SquarePosition == DOWN || SquarePosition == LEFT;
912 case 3: return SquarePosition == RIGHT;
913 case 4: return SquarePosition == LEFT;
914 case 5: return SquarePosition == UP || SquarePosition == RIGHT;
915 case 6: return SquarePosition == UP;
916 case 7: return SquarePosition == UP || SquarePosition == LEFT;
918 return true;
922 sLong stack::GetWeight (ccharacter *Viewer, int SquarePosition) const {
923 sLong Weight = 0;
924 for (stackiterator i = GetBottom(); i.HasItem(); ++i)
925 if (i->GetSquarePosition() == SquarePosition && ((Flags & HIDDEN) || i->CanBeSeenBy(Viewer)))
926 Weight += i->GetWeight();
927 return Weight;
931 truth stack::DetectMaterial (cmaterial *Material) const {
932 for (stackiterator i = GetBottom(); i.HasItem(); ++i) if (i->DetectMaterial(Material)) return true;
933 return false;
937 void stack::SetLifeExpectancy (int Base, int RandPlus) {
938 for (stackiterator i = GetBottom(); i.HasItem(); ++i) i->SetLifeExpectancy(Base, RandPlus);
942 truth stack::Necromancy (character *Necromancer) {
943 itemvector ItemVector;
944 FillItemVector(ItemVector);
945 for (uInt c = 0; c < ItemVector.size(); ++c) if (ItemVector[c]->Necromancy(Necromancer)) return true;
946 return false;
950 void stack::CalculateEnchantments () {
951 for (stackiterator i = GetBottom(); i.HasItem(); ++i) i->CalculateEnchantment();
955 ccharacter *stack::FindCarrier () const {
956 return MotherEntity ? MotherEntity->FindCarrier() : 0;
960 void stack::Haste () {
961 for (stackiterator i = GetBottom(); i.HasItem(); ++i) i->Haste();
965 void stack::Slow () {
966 for (stackiterator i = GetBottom(); i.HasItem(); ++i) i->Slow();