3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
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 */
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 {
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
);
42 BlitData
.CustomData
&= ~SQUARE_INDEX_MASK
;
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();
52 if (PlusSymbol
) igraph::GetSymbolGraphic()->LuminanceMaskedBlit(BlitData
);
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();
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
) {
101 stackslot
*Slot
= Bottom
;
104 Volume
= Weight
= Items
= 0;
105 SignalVolumeAndWeightChange();
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
;
117 if (!LastClean
) SignalEmitationDecrease(Item
->GetSquarePosition(), Item
->GetEmitation());
122 void stack::Save (outputfile
&SaveFile
) const {
124 SaveFile
<< uShort(0);
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
);
142 Volume
+= (*Top
)->GetVolume();
143 Weight
+= (*Top
)->GetWeight();
144 if ((*Top
)->GetSquarePosition() == CENTER
) Emitation
= game::CombineConstLights(Emitation
, (*Top
)->GetEmitation());
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 {
156 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) {
157 if ((SorterFunction
== 0 || ((*i
)->*SorterFunction
)(Viewer
)) && ((Flags
& HIDDEN
) || i
->CanBeSeenBy(Viewer
))) return true;
164 int stack::SortedItemsCount (ccharacter
*Viewer
, sorter SorterFunction
) const {
167 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) {
168 if ((SorterFunction
== 0 || ((*i
)->*SorterFunction
)(Viewer
)) && ((Flags
& HIDDEN
) || i
->CanBeSeenBy(Viewer
))) ++res
;
175 // 0, 1 or most recent pickup time
176 feuLong
stack::SortedItemsRecentTime (ccharacter
*Viewer
, sorter SorterFunction
) const {
177 feuLong highestTime
= 0;
179 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) {
180 if ((SorterFunction
== 0 || ((*i
)->*SorterFunction
)(Viewer
)) && ((Flags
& HIDDEN
) || i
->CanBeSeenBy(Viewer
))) {
181 feuLong pt
= (*i
)->pickupTime
;
183 if (pt
> highestTime
) highestTime
= pt
;
191 void stack::BeKicked (character
*Kicker
, int KickDamage
, int Direction
) {
193 ReceiveDamage(Kicker
, KickDamage
, PHYSICAL_DAMAGE
, Direction
);
194 if (GetItems() && GetLSquareUnder()->IsFlyable()) {///&& SquarePosition == CENTER)
195 item
*Item1
= *GetTop();
196 item
*Item2
= RAND() & 1 && GetItems() > 1 ? *--GetTop() : 0;
197 Item1
->Fly(Kicker
, Direction
, KickDamage
*3);
199 /*k8: if(!Item2->Exists() || Item2->GetPos() != GetPos()) int esko = esko = 2; */
200 if (!(!Item2
->Exists() || Item2
->GetPos() != GetPos())) Item2
->Fly(Kicker
, Direction
, KickDamage
* 3);
203 } else if (Kicker
->IsPlayer() && GetNativeVisibleItems(Kicker
)) {
204 ADD_MESSAGE("Your weak kick has no effect.");
209 void stack::Polymorph (character
*Polymorpher
) {
210 itemvector ItemVector
;
211 FillItemVector(ItemVector
);
213 for (uInt c
= 0; c
< ItemVector
.size(); ++c
) {
214 if (ItemVector
[c
]->Exists() && ItemVector
[c
]->Polymorph(Polymorpher
, this) && ++p
== 5) break;
219 void stack::CheckForStepOnEffect (character
*Stepper
) {
220 itemvector ItemVector
;
221 FillItemVector(ItemVector
);
222 for (uInt c
= 0; c
< ItemVector
.size(); ++c
) {
223 if (ItemVector
[c
]->Exists()) {
224 ItemVector
[c
]->StepOnEffect(Stepper
);
225 if (!Stepper
->IsEnabled()) return;
231 lsquare
*stack::GetLSquareTrulyUnder (int SquarePosition
) const {
232 if (SquarePosition
== DOWN
) {
233 if (GetArea()->IsValidPos(GetPos()+v2(0, 1))) return GetNearLSquare(GetPos() + v2(0, 1));
236 if (SquarePosition
== LEFT
) {
237 if (GetArea()->IsValidPos(GetPos()+v2(-1, 0))) return GetNearLSquare(GetPos() + v2(-1, 0));
240 if (SquarePosition
== UP
) {
241 if (GetArea()->IsValidPos(GetPos()+v2(0, -1))) return GetNearLSquare(GetPos() + v2(0, -1));
244 if (SquarePosition
== RIGHT
) {
245 if (GetArea()->IsValidPos(GetPos()+v2(1, 0))) return GetNearLSquare(GetPos() + v2(1, 0));
248 return GetLSquareUnder();
252 void stack::ReceiveDamage (character
*Damager
, int Damage
, int Type
, int Direction
) {
253 itemvector ItemVector
;
254 FillItemVector(ItemVector
);
255 for (uInt c
= 0; c
< ItemVector
.size(); ++c
)
256 if (ItemVector
[c
]->Exists() && AllowDamage(Direction
, ItemVector
[c
]->GetSquarePosition()))
257 ItemVector
[c
]->ReceiveDamage(Damager
, Damage
, Type
);
261 void stack::TeleportRandomly (uInt Amount
) {
262 itemvector ItemVector
;
263 FillItemVector(ItemVector
);
264 for (uInt c
= 0; c
< ItemVector
.size() && c
< Amount
; ++c
)
265 if (ItemVector
[c
]->Exists()) {
266 if (ItemVector
[c
]->CanBeSeenByPlayer()) ADD_MESSAGE("%s disappears!", ItemVector
[c
]->GetExtendedDescription().CStr());
267 ItemVector
[c
]->TeleportRandomly();
272 /* ItemVector receives all items in the stack */
273 void stack::FillItemVector (itemvector
&ItemVector
) const {
274 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) ItemVector
.push_back(*i
);
278 /* Don't use; this function is only for gum solutions */
279 item
*stack::GetItem (int I
) const {
281 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
, ++c
) if (c
== I
) return *i
;
286 /* Don't use; this function is only for gum solutions */
287 int stack::SearchItem (item
*ToBeSearched
) const {
289 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
, ++c
) if (*i
== ToBeSearched
) return c
;
294 /* Flags for all DrawContents functions can be found in ivandef.h.
295 Those returning int return 0 on success and a felist error
296 otherwise (see felibdef.h) */
297 item
*stack::DrawContents (ccharacter
*Viewer
, cfestring
&Topic
, int Flags
, sorter SorterFunction
, item
*hiitem
) const {
298 itemvector ReturnVector
;
299 DrawContents(ReturnVector
, 0, Viewer
, Topic
, CONST_S(""), CONST_S(""), CONST_S(""), 0, Flags
|NO_MULTI_SELECT
, SorterFunction
, hiitem
);
300 return ReturnVector
.empty() ? 0 : ReturnVector
[0];
304 int stack::DrawContents (itemvector
&ReturnVector
, ccharacter
*Viewer
, cfestring
&Topic
, int Flags
, sorter SorterFunction
, item
*hiitem
) const {
305 return DrawContents(ReturnVector
, 0, Viewer
, Topic
, CONST_S(""), CONST_S(""), CONST_S(""), 0, Flags
, SorterFunction
, hiitem
);
309 /* MergeStack is used for showing two stacks together. Like when eating when
310 there are items on the ground and in the character's stack */
311 int stack::DrawContents (itemvector
&ReturnVector
, stack
*MergeStack
,
312 ccharacter
*Viewer
, cfestring
&Topic
, cfestring
&ThisDesc
, cfestring
&ThatDesc
,
313 cfestring
&SpecialDesc
, col16 SpecialDescColor
, int Flags
, sorter SorterFunction
, item
*hiitem
) const
315 felist
Contents(Topic
);
316 lsquare
*Square
= GetLSquareUnder();
317 stack
*AdjacentStack
[4] = { 0, 0, 0, 0 };
319 if ((this->Flags
&HIDDEN
) == 0) for (int c
= 0; c
< 4; ++c
) AdjacentStack
[c
] = Square
->GetStackOfAdjacentSquare(c
);
321 if (!SpecialDesc
.IsEmpty()) {
322 Contents
.AddDescription(CONST_S(""));
323 Contents
.AddDescription(SpecialDesc
.CapitalizeCopy(), SpecialDescColor
);
327 if (!(Flags & NO_SPECIAL_INFO)) {
328 Contents.AddDescription(CONST_S(""));
329 sLong Weight = GetWeight(Viewer, CENTER);
330 if (MergeStack) Weight += MergeStack->GetWeight(Viewer, CENTER);
331 for (c = 0; c < 4; ++c)
332 if (AdjacentStack[c])
333 Weight += AdjacentStack[c]->GetWeight(Viewer, 3 - c);
334 Contents.AddDescription(CONST_S("Overall weight: ") + Weight + " grams");
338 if (Flags
&NONE_AS_CHOICE
) {
339 int ImageKey
= game::AddToItemDrawVector(itemvector());
340 Contents
.AddEntry(CONST_S("none"), LIGHT_GRAY
, 0, ImageKey
);
343 if (MergeStack
) MergeStack
->AddContentsToList(Contents
, Viewer
, ThatDesc
, Flags
, CENTER
, SorterFunction
, hiitem
);
344 AddContentsToList(Contents
, Viewer
, ThisDesc
, Flags
, CENTER
, SorterFunction
, hiitem
);
346 static cchar
*WallDescription
[] = { "western", "southern", "nothern", "eastern" };
347 for (int c
= 0; c
< 4; ++c
) {
348 if (AdjacentStack
[c
]) AdjacentStack
[c
]->AddContentsToList(Contents
, Viewer
, CONST_S("Items on the ")+WallDescription
[c
] + " wall:", Flags
, 3-c
, SorterFunction
, hiitem
);
351 game::SetStandardListAttributes(Contents
);
352 Contents
.SetPageLength(12);
353 Contents
.RemoveFlags(BLIT_AFTERWARDS
);
354 Contents
.SetEntryDrawer(game::ItemEntryDrawer
);
356 if ((Flags
&NO_SELECT
) == 0) Contents
.AddFlags(SELECTABLE
);
358 // `Contents.Draw()` will fix invalid selections
360 if (Flags
&REMEMBER_SELECTED
) {
361 if ((Flags
&NONE_AS_CHOICE
) && (Flags
&SKIP_FIRST_IF_NO_OLD
) && !hiitem
&& GetSelected() == 0) {
365 if ((Flags
&NONE_AS_CHOICE
) && (Flags
&SKIP_FIRST_IF_NO_OLD
) && !hiitem
) {
370 if ((Flags
&NO_SELECT
) == 0 && (Flags
&SELECT_MOST_RECENT
)) {
373 for (uInt c
= 0; c
< Contents
.GetLength(); ++c
) {
374 if (!Contents
.IsEntrySelectable(c
)) continue;
376 if (cursel
< selected
) continue;
377 feuLong pt
= Contents
.GetEntryUData(c
);
378 if (pt
<= 1 || game::GetTick()-pt
> 15+4) continue;
379 if (pt
< maxpt
) continue;
385 if (selected
<= 0) selected
= GetSelected();
386 Contents
.SetSelected(selected
);
388 game::DrawEverythingNoBlit(); //doesn't prevent mirage puppies
389 int Chosen
= Contents
.Draw();
390 game::ClearItemDrawVector();
392 if (Chosen
&FELIST_ERROR_BIT
) {
400 if (Flags
&NONE_AS_CHOICE
) {
401 if (!Selected
) return 0;
406 Pos
= MergeStack
->SearchChosen(ReturnVector
, Viewer
, Pos
, Selected
, Flags
, CENTER
, SorterFunction
);
407 if (!ReturnVector
.empty()) return 0;
410 Pos
= SearchChosen(ReturnVector
, Viewer
, Pos
, Selected
, Flags
, CENTER
, SorterFunction
);
412 if (!ReturnVector
.empty()) return 0;
414 for (int c
= 0; c
< 4; ++c
) {
415 if (AdjacentStack
[c
]) {
416 AdjacentStack
[c
]->SearchChosen(ReturnVector
, Viewer
, Pos
, Selected
, Flags
, 3-c
, SorterFunction
);
417 if (!ReturnVector
.empty()) break;
425 /* Internal function to fill Contents list */
426 void stack::AddContentsToList (felist
&Contents
, ccharacter
*Viewer
, cfestring
&Desc
, int Flags
,
427 int RequiredSquarePosition
, sorter SorterFunction
, item
*hiitem
) const
429 itemvectorvector PileVector
;
430 Pile(PileVector
, Viewer
, RequiredSquarePosition
, SorterFunction
);
431 truth DrawDesc
= Desc
.GetSize();
432 sLong LastCategory
= 0;
434 for (uInt p
= 0; p
< PileVector
.size(); ++p
) {
436 if (!Contents
.IsEmpty()) Contents
.AddEntry(CONST_S(""), WHITE
, 0, NO_IMAGE
, false);
437 Contents
.AddEntry(Desc
, WHITE
, 0, NO_IMAGE
, false);
438 Contents
.AddEntry(CONST_S(""), WHITE
, 0, NO_IMAGE
, false);
441 item
*Item
= PileVector
[p
].back();
442 if (Item
->GetCategory() != LastCategory
) {
443 LastCategory
= Item
->GetCategory();
444 Contents
.AddEntry(item::GetItemCategoryName(LastCategory
), LIGHT_GRAY
, 0, NO_IMAGE
, false);
447 Item
->AddInventoryEntry(Viewer
, Entry
, PileVector
[p
].size(), !(Flags
& NO_SPECIAL_INFO
));
448 int ImageKey
= game::AddToItemDrawVector(PileVector
[p
]);
449 Contents
.AddEntry(Entry
, (Item
== hiitem
? WHITE
: LIGHT_GRAY
), 0, ImageKey
, true, Item
->pickupTime
);
454 /* Internal function which fills ReturnVector according to Chosen,
455 which is given by felist::Draw, and possibly the user's additional
456 input about item amount. */
457 int stack::SearchChosen (itemvector
&ReturnVector
, ccharacter
*Viewer
, int Pos
, int Chosen
, int Flags
,
458 int RequiredSquarePosition
, sorter SorterFunction
) const
460 /* Not really efficient... :( */
461 itemvectorvector PileVector
;
462 Pile(PileVector
, Viewer
, RequiredSquarePosition
, SorterFunction
);
463 for (uInt p
= 0; p
< PileVector
.size(); ++p
) {
464 if (Pos
++ == Chosen
) {
465 if (Flags
& NO_MULTI_SELECT
) {
466 int Amount
= (Flags
& SELECT_PAIR
&& PileVector
[p
][0]->HandleInPairs() && PileVector
[p
].size() >= 2 ? 2 : 1);
467 ReturnVector
.assign(PileVector
[p
].end() - Amount
, PileVector
[p
].end());
470 int Amount
= PileVector
[p
].size();
472 Amount
= game::ScrollBarQuestion(CONST_S("How many ")+PileVector
[p
][0]->GetName(PLURAL
)+'?',
473 Amount
, 1, 0, Amount
, 0, WHITE
,
474 LIGHT_GRAY
, DARK_GRAY
);
476 ReturnVector
.assign(PileVector
[p
].end() - Amount
, PileVector
[p
].end());
485 truth
stack::RaiseTheDead (character
*Summoner
) {
486 itemvector ItemVector
;
487 FillItemVector(ItemVector
);
488 for (uInt c
= 0; c
< ItemVector
.size(); ++c
) if (ItemVector
[c
]->RaiseTheDead(Summoner
)) return true;
493 /* Returns false if the Applier didn't try to use the key */
494 truth
stack::TryKey (item
*Key
, character
*Applier
) {
495 if (!Applier
->IsPlayer()) return false;
496 item
*ToBeOpened
= DrawContents(Applier
, CONST_S("Where do you wish to use the key?"), 0, &item::HasLock
);
497 if (!ToBeOpened
) return false;
498 return ToBeOpened
->TryKey(Key
, Applier
);
502 /* Returns false if the Applier didn't try to open anything */
503 truth
stack::Open (character
*Opener
) {
504 if (!Opener
->IsPlayer()) return false;
505 item
*ToBeOpened
= DrawContents(Opener
, CONST_S("What do you wish to open?"), 0, &item::IsOpenable
);
506 return ToBeOpened
? ToBeOpened
->Open(Opener
) : false;
510 int stack::GetSideItems (int RequiredSquarePosition
) const {
511 int VisibleItems
= 0;
512 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
)
513 if (i
->GetSquarePosition() == RequiredSquarePosition
) ++VisibleItems
;
518 int stack::GetVisibleItems (ccharacter
*Viewer
) const {
519 int VisibleItems
= 0;
520 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
)
521 if (i
->GetSquarePosition() == CENTER
&& i
->CanBeSeenBy(Viewer
)) ++VisibleItems
;
522 lsquare
*Square
= GetLSquareUnder();
523 for (int c
= 0; c
< 4; ++c
) {
524 stack
*Stack
= Square
->GetStackOfAdjacentSquare(c
);
525 if (Stack
) VisibleItems
+= Stack
->GetVisibleSideItems(Viewer
, 3-c
);
531 void stack::GetVisibleItemsV (ccharacter
*Viewer
, std::vector
<item
*> &vi
) {
532 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) {
533 if (i
->GetSquarePosition() == CENTER
&& i
->CanBeSeenBy(Viewer
)) vi
.push_back(*i
);
535 lsquare
*Square
= GetLSquareUnder();
536 for (int c
= 0; c
< 4; ++c
) {
537 stack
*Stack
= Square
->GetStackOfAdjacentSquare(c
);
539 //VisibleItems += Stack->GetVisibleSideItems(Viewer, 3-c);
540 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) {
541 if (i
->GetSquarePosition() == (3-c
) && i
->CanBeSeenBy(Viewer
)) vi
.push_back(*i
);
548 truth
stack::HasSomethingFunny (ccharacter
*Viewer
, truth seeCorpses
, truth seeUnstepped
) {
549 std::vector
<item
*> vi
;
551 GetVisibleItemsV(Viewer
, vi
);
552 for (unsigned int f
= 0; f
< vi
.size(); f
++) {
553 if (!vi
[f
]->CanBePickedUp()) continue;
554 //fprintf(stderr, "::%s %s\n", vi[f]->IsSteppedOn()?"T":"o", seeUnstepped?"T":"o");
555 if (!seeUnstepped
&& vi
[f
]->IsSteppedOn()) continue;
556 if (seeCorpses
|| (!vi
[f
]->IsCorpse() && !vi
[f
]->IsBodyPart())) return true;
562 void stack::SetSteppedOn (truth v
) {
563 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) {
564 if (i
->GetSquarePosition() == CENTER
) i
->SetSteppedOn(v
);
566 lsquare
*Square
= GetLSquareUnder();
567 for (int c
= 0; c
< 4; ++c
) {
568 stack
*Stack
= Square
->GetStackOfAdjacentSquare(c
);
570 //VisibleItems += Stack->GetVisibleSideItems(Viewer, 3-c);
571 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) {
572 if (i
->GetSquarePosition() == (3-c
)) i
->SetSteppedOn(v
);
576 //fprintf(stderr, "STON!\n");
580 int stack::GetNativeVisibleItems (ccharacter
*Viewer
) const {
581 if (Flags
& HIDDEN
) return Items
;
582 int VisibleItems
= 0;
583 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) if (i
->CanBeSeenBy(Viewer
)) ++VisibleItems
;
588 int stack::GetVisibleSideItems (ccharacter
*Viewer
, int RequiredSquarePosition
) const {
589 int VisibleItems
= 0;
590 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
)
591 if (i
->GetSquarePosition() == RequiredSquarePosition
&& i
->CanBeSeenBy(Viewer
)) ++VisibleItems
;
596 item
*stack::GetBottomVisibleItem (ccharacter
*Viewer
) const {
597 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
)
598 if ((Flags
& HIDDEN
) || i
->CanBeSeenBy(Viewer
)) return *i
;
603 void stack::SignalVolumeAndWeightChange () {
604 if (!(Flags
& FREEZED
)) {
605 CalculateVolumeAndWeight();
606 if (MotherEntity
) MotherEntity
->SignalVolumeAndWeightChange();
611 void stack::CalculateVolumeAndWeight () {
613 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) {
614 Volume
+= i
->GetVolume();
615 Weight
+= i
->GetWeight();
620 void stack::SignalEmitationIncrease (int ItemSquarePosition
, col24 EmitationUpdate
) {
621 if (ItemSquarePosition
< CENTER
) {
622 stack
*Stack
= GetLSquareUnder()->GetStackOfAdjacentSquare(ItemSquarePosition
);
623 if (Stack
) Stack
->SignalEmitationIncrease(CENTER
, EmitationUpdate
);
626 if (!(Flags
& FREEZED
) && game::CompareLights(EmitationUpdate
, Emitation
) > 0) {
627 Emitation
= game::CombineConstLights(Emitation
, EmitationUpdate
);
629 if (MotherEntity
->AllowContentEmitation()) MotherEntity
->SignalEmitationIncrease(EmitationUpdate
);
631 GetLSquareUnder()->SignalEmitationIncrease(EmitationUpdate
);
637 void stack::SignalEmitationDecrease (int ItemSquarePosition
, col24 EmitationUpdate
) {
638 if (ItemSquarePosition
< CENTER
) {
639 stack
*Stack
= GetLSquareUnder()->GetStackOfAdjacentSquare(ItemSquarePosition
);
640 if (Stack
) Stack
->SignalEmitationDecrease(CENTER
, EmitationUpdate
);
643 if (!(Flags
& FREEZED
) && Emitation
&& game::CompareLights(EmitationUpdate
, Emitation
) >= 0) {
644 col24 Backup
= Emitation
;
645 CalculateEmitation();
646 if (Backup
!= Emitation
) {
648 if (MotherEntity
->AllowContentEmitation()) MotherEntity
->SignalEmitationDecrease(EmitationUpdate
);
650 GetLSquareUnder()->SignalEmitationDecrease(EmitationUpdate
);
657 void stack::CalculateEmitation () {
658 Emitation
= GetSideEmitation(CENTER
);
662 col24
stack::GetSideEmitation (int RequiredSquarePosition
) {
664 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
)
665 if (i
->GetSquarePosition() == RequiredSquarePosition
) game::CombineLights(Emitation
, i
->GetEmitation());
670 truth
stack::CanBeSeenBy (ccharacter
*Viewer
, int SquarePosition
) const {
671 if (MotherEntity
) return MotherEntity
->ContentsCanBeSeenBy(Viewer
);
672 lsquare
*Square
= GetLSquareTrulyUnder(SquarePosition
);
673 return Viewer
->IsOver(Square
->GetPos()) || Square
->CanBeSeenBy(Viewer
);
677 truth
stack::IsDangerous (ccharacter
*Stepper
) const {
678 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
)
679 if (i
->IsDangerous(Stepper
) && i
->CanBeSeenBy(Stepper
)) return true;
684 /* Returns true if something was duplicated.
685 Max is the cap of items to be affected */
686 truth
stack::Duplicate (int Max
, feuLong Flags
) {
687 if (!GetItems()) return false;
688 itemvector ItemVector
;
689 FillItemVector(ItemVector
);
691 for (uInt c
= 0; c
< ItemVector
.size(); ++c
)
692 if (ItemVector
[c
]->Exists() && ItemVector
[c
]->DuplicateToStack(this, Flags
) && ++p
== Max
) break;
697 /* Adds the item without any external update requests */
698 void stack::AddElement (item
*Item
, truth
) {
700 /* "I love writing illegible code." - Guy who wrote this */
701 (Top
= (Bottom
? Top
->Next
: Bottom
) = new stackslot(this, Top
))->PutInItem(Item
);
705 /* Removes the slot without any external update requests */
706 void stack::RemoveElement (stackslot
*Slot
) {
708 (Slot
->Last
? Slot
->Last
->Next
: Bottom
) = Slot
->Next
;
709 (Slot
->Next
? Slot
->Next
->Last
: Top
) = Slot
->Last
;
714 void stack::MoveItemsTo (stack
*Stack
) {
715 while (Items
) GetBottom()->MoveTo(Stack
);
719 void stack::MoveItemsTo (slot
*Slot
) {
720 while (Items
) Slot
->AddFriendItem(*GetBottom());
724 item
*stack::GetBottomItem (ccharacter
*Char
, truth ForceIgnoreVisibility
) const {
725 if ((Flags
& HIDDEN
) || ForceIgnoreVisibility
) return Bottom
? **Bottom
: 0;
726 return GetBottomVisibleItem(Char
);
730 item
*stack::GetBottomSideItem (ccharacter
*Char
, int RequiredSquarePosition
, truth ForceIgnoreVisibility
) const {
731 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) {
732 if ((i
->GetSquarePosition() == RequiredSquarePosition
&& (Flags
& HIDDEN
)) ||
733 ForceIgnoreVisibility
|| i
->CanBeSeenBy(Char
)) return *i
;
739 truth
CategorySorter (const itemvector
&V1
, const itemvector
&V2
) {
740 return (*V1
.begin())->GetCategory() < (*V2
.begin())->GetCategory();
744 /* Slow function which sorts the stack's contents to a vector of piles
745 (itemvectors) of which elements are similiar to each other, for instance
747 void stack::Pile (itemvectorvector
&PileVector
, ccharacter
*Viewer
,
748 int RequiredSquarePosition
, sorter SorterFunction
) const
751 std::list
<item
*> List
;
752 for (stackiterator s
= GetBottom(); s
.HasItem(); ++s
)
753 if (s
->GetSquarePosition() == RequiredSquarePosition
&&
754 (SorterFunction
== 0 || ((*s
)->*SorterFunction
)(Viewer
)) &&
755 ((Flags
& HIDDEN
) || s
->CanBeSeenBy(Viewer
)))
757 for (std::list
<item
*>::iterator i
= List
.begin(); i
!= List
.end(); ++i
) {
758 PileVector
.resize(PileVector
.size()+1);
759 itemvector
& Pile
= PileVector
.back();
761 if ((*i
)->CanBePiled()) {
762 std::list
<item
*>::iterator j
= i
;
763 for (++j
; j
!= List
.end(); ) {
764 if ((*j
)->CanBePiled() && (*i
)->CanBePiledWith(*j
, Viewer
)) {
766 std::list
<item
*>::iterator Dirt
= j
++;
774 std::stable_sort(PileVector
.begin(), PileVector
.end(), CategorySorter
);
778 /* Total price of the stack */
779 sLong
stack::GetTruePrice () const {
781 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) Price
+= i
->GetTruePrice();
786 /* GUI used for instance by chests and bookcases.
787 Returns whether anything was done. */
788 truth
stack::TakeSomethingFrom (character
*Opener
, cfestring
&ContainerName
) {
790 ADD_MESSAGE("There is nothing in %s.", ContainerName
.CStr());
793 truth Success
= false;
794 room
*Room
= GetLSquareUnder()->GetRoom();
798 game::DrawEverythingNoBlit();
799 DrawContents(ToTake
, Opener
, CONST_S("What do you want to take from ")+ContainerName
+'?', REMEMBER_SELECTED
);
800 if (ToTake
.empty()) break;
801 if (!IsOnGround() || !Room
|| Room
->PickupItem(Opener
, ToTake
[0], ToTake
.size())) {
802 for (uInt c
= 0; c
< ToTake
.size(); ++c
) ToTake
[c
]->MoveTo(Opener
->GetStack());
803 ADD_MESSAGE("You take %s from %s.", ToTake
[0]->GetName(DEFINITE
, ToTake
.size()).CStr(), ContainerName
.CStr());
811 /* GUI used for instance by chests and bookcases (use ContainerID == 0 if
812 the container isn't an item). Returns whether anything was done. */
813 truth
stack::PutSomethingIn (character
*Opener
, cfestring
&ContainerName
, sLong StorageVolume
, feuLong ContainerID
) {
814 if (!Opener
->GetStack()->GetItems()) {
815 ADD_MESSAGE("You have nothing to put in %s.", ContainerName
.CStr());
818 truth Success
= false;
819 room
*Room
= GetLSquareUnder()->GetRoom();
823 game::DrawEverythingNoBlit();
824 Opener
->GetStack()->DrawContents(ToPut
, Opener
, CONST_S("What do you want to put in ")+ContainerName
+'?', REMEMBER_SELECTED
|SELECT_MOST_RECENT
);
825 if (ToPut
.empty()) break;
826 if (ToPut
[0]->GetID() == ContainerID
) {
827 ADD_MESSAGE("You can't put %s inside itself!", ContainerName
.CStr());
830 uInt Amount
= Min
<uInt
>((StorageVolume
-GetVolume())/ToPut
[0]->GetVolume(), ToPut
.size());
832 if (ToPut
.size() == 1) ADD_MESSAGE("%s doesn't fit in %s.", ToPut
[0]->CHAR_NAME(DEFINITE
), ContainerName
.CStr());
833 else ADD_MESSAGE("None of the %d %s fit in %s.", int(ToPut
.size()), ToPut
[0]->CHAR_NAME(PLURAL
), ContainerName
.CStr());
836 if (Amount
!= ToPut
.size())
837 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());
838 if (!IsOnGround() || !Room
|| Room
->DropItem(Opener
, ToPut
[0], Amount
)) {
839 for (uInt c
= 0; c
< Amount
; ++c
) ToPut
[c
]->MoveTo(this);
840 ADD_MESSAGE("You put %s in %s.", ToPut
[0]->GetName(DEFINITE
, Amount
).CStr(), ContainerName
.CStr());
848 truth
stack::IsOnGround () const {
849 return !MotherEntity
|| MotherEntity
->IsOnGround();
853 int stack::GetSpoiledItems () const {
855 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
)
856 Counter
+= (i
->GetSpoilLevel() > 0); // even though this is pretty unclear, it isn't mine but Hex's
861 /* Adds all items and recursively their contents
862 which satisfy the sorter to ItemVector */
863 void stack::SortAllItems (const sortdata
&SortData
) const {
864 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) i
->SortAllItems(SortData
);
868 /* Search for traps and other secret items */
869 void stack::Search (ccharacter
*Char
, int Perception
) {
870 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) i
->Search(Char
, Perception
);
874 /* Used to determine whether the danger symbol should be shown */
875 truth
stack::NeedDangerSymbol (ccharacter
*Viewer
) const {
876 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
)
877 if (i
->NeedDangerSymbol() && i
->CanBeSeenBy(Viewer
)) return true;
882 void stack::PreProcessForBone () {
884 itemvector ItemVector
;
885 FillItemVector(ItemVector
);
886 for (uInt c
= 0; c
< ItemVector
.size(); ++c
) ItemVector
[c
]->PreProcessForBone();
891 void stack::PostProcessForBone () {
893 itemvector ItemVector
;
894 FillItemVector(ItemVector
);
895 for (uInt c
= 0; c
< ItemVector
.size(); ++c
) ItemVector
[c
]->PostProcessForBone();
900 void stack::FinalProcessForBone () {
901 /* Items can't be removed during the final processing stage */
902 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) i
->FinalProcessForBone();
906 /* VolumeModifier increases the spilled liquid's volume.
907 Note that the original liquid isn't placed anywhere nor deleted,
908 but its volume is decreased (possibly to zero). */
909 void stack::SpillFluid (character
*Spiller
, liquid
*Liquid
, sLong VolumeModifier
) {
911 double ChanceMultiplier
= 1.0/(300+sqrt(Volume
));
912 itemvector ItemVector
;
913 FillItemVector(ItemVector
);
914 for (int c
= ItemVector
.size()-1; c
>= 0; --c
) {
915 if (ItemVector
[c
]->Exists() && ItemVector
[c
]->AllowFluids()) {
916 sLong ItemVolume
= ItemVector
[c
]->GetVolume();
917 double Root
= sqrt(ItemVolume
);
918 if (Root
> RAND() % 200 || Root
> RAND() % 200) {
919 sLong SpillVolume
= sLong(VolumeModifier
*Root
*ChanceMultiplier
);
921 Liquid
->EditVolume(-Max(SpillVolume
, Liquid
->GetVolume()));
922 ItemVector
[c
]->SpillFluid(Spiller
, Liquid
->SpawnMoreLiquid(SpillVolume
), ItemVector
[c
]->GetSquareIndex(GetPos()));
923 if (!Liquid
->GetVolume()) return;
931 void stack::AddItems (const itemvector
&ItemVector
) {
932 for (uInt c
= 0; c
< ItemVector
.size(); ++c
) AddItem(ItemVector
[c
]);
936 void stack::MoveItemsTo (itemvector
&ToVector
, int RequiredSquarePosition
) {
937 itemvector ItemVector
;
938 FillItemVector(ItemVector
);
939 for (uInt c
= 0; c
< ItemVector
.size(); ++c
) {
940 if (ItemVector
[c
]->GetSquarePosition() == RequiredSquarePosition
) {
941 ItemVector
[c
]->RemoveFromSlot();
942 ToVector
.push_back(ItemVector
[c
]);
948 void stack::DropSideItems () {
949 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) {
950 int SquarePosition
= i
->GetSquarePosition();
951 if (SquarePosition
!= CENTER
) {
952 if (i
->IsAnimated()) {
953 lsquare
*Square
= GetLSquareTrulyUnder(SquarePosition
);
954 if (Square
) Square
->DecStaticAnimatedEntities();
955 GetLSquareUnder()->IncStaticAnimatedEntities();
957 i
->SignalSquarePositionChange(CENTER
);
958 SignalEmitationDecrease(SquarePosition
, i
->GetEmitation());
959 SignalEmitationIncrease(CENTER
, i
->GetEmitation());
965 truth
stack::AllowDamage (int Direction
, int SquarePosition
) {
966 if (SquarePosition
== CENTER
) return true;
968 case 0: return SquarePosition
== DOWN
|| SquarePosition
== RIGHT
;
969 case 1: return SquarePosition
== DOWN
;
970 case 2: return SquarePosition
== DOWN
|| SquarePosition
== LEFT
;
971 case 3: return SquarePosition
== RIGHT
;
972 case 4: return SquarePosition
== LEFT
;
973 case 5: return SquarePosition
== UP
|| SquarePosition
== RIGHT
;
974 case 6: return SquarePosition
== UP
;
975 case 7: return SquarePosition
== UP
|| SquarePosition
== LEFT
;
981 sLong
stack::GetWeight (ccharacter
*Viewer
, int SquarePosition
) const {
983 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
)
984 if (i
->GetSquarePosition() == SquarePosition
&& ((Flags
& HIDDEN
) || i
->CanBeSeenBy(Viewer
)))
985 Weight
+= i
->GetWeight();
990 truth
stack::DetectMaterial (cmaterial
*Material
) const {
991 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) if (i
->DetectMaterial(Material
)) return true;
996 void stack::SetLifeExpectancy (int Base
, int RandPlus
) {
997 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) i
->SetLifeExpectancy(Base
, RandPlus
);
1001 truth
stack::Necromancy (character
*Necromancer
) {
1002 itemvector ItemVector
;
1003 FillItemVector(ItemVector
);
1004 for (uInt c
= 0; c
< ItemVector
.size(); ++c
) if (ItemVector
[c
]->Necromancy(Necromancer
)) return true;
1009 void stack::CalculateEnchantments () {
1010 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) i
->CalculateEnchantment();
1014 ccharacter
*stack::FindCarrier () const {
1015 return MotherEntity
? MotherEntity
->FindCarrier() : 0;
1019 void stack::Haste () {
1020 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) i
->Haste();
1024 void stack::Slow () {
1025 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) i
->Slow();