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 void stack::BeKicked (character
*Kicker
, int KickDamage
, int Direction
) {
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);
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
);
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
) {
207 if (GetArea()->IsValidPos(GetPos()+v2(0, 1))) return GetNearLSquare(GetPos() + v2(0, 1));
210 if (GetArea()->IsValidPos(GetPos()+v2(-1, 0))) return GetNearLSquare(GetPos() + v2(-1, 0));
213 if (GetArea()->IsValidPos(GetPos()+v2(0, -1))) return GetNearLSquare(GetPos() + v2(0, -1));
216 if (GetArea()->IsValidPos(GetPos()+v2(1, 0))) return GetNearLSquare(GetPos() + v2(1, 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 {
252 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
, ++c
) if (c
== I
) return *i
;
257 /* Don't use; this function is only for gum solutions */
258 int stack::SearchItem (item
*ToBeSearched
) const {
260 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
, ++c
) if (*i
== ToBeSearched
) return c
;
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 };
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
) {
341 if (Flags
& NONE_AS_CHOICE
) {
342 if (!Selected
) return 0;
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;
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;
374 for (uInt p
= 0; p
< PileVector
.size(); ++p
) {
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);
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);
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());
411 int Amount
= PileVector
[p
].size();
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());
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;
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
;
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
);
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
);
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;
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
);
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
;
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
;
537 item
*stack::GetBottomVisibleItem (ccharacter
*Viewer
) const {
538 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
)
539 if ((Flags
& HIDDEN
) || i
->CanBeSeenBy(Viewer
)) return *i
;
544 void stack::SignalVolumeAndWeightChange () {
545 if (!(Flags
& FREEZED
)) {
546 CalculateVolumeAndWeight();
547 if (MotherEntity
) MotherEntity
->SignalVolumeAndWeightChange();
552 void stack::CalculateVolumeAndWeight () {
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
);
567 if (!(Flags
& FREEZED
) && game::CompareLights(EmitationUpdate
, Emitation
) > 0) {
568 Emitation
= game::CombineConstLights(Emitation
, EmitationUpdate
);
570 if (MotherEntity
->AllowContentEmitation()) MotherEntity
->SignalEmitationIncrease(EmitationUpdate
);
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
);
584 if (!(Flags
& FREEZED
) && Emitation
&& game::CompareLights(EmitationUpdate
, Emitation
) >= 0) {
585 col24 Backup
= Emitation
;
586 CalculateEmitation();
587 if (Backup
!= Emitation
) {
589 if (MotherEntity
->AllowContentEmitation()) MotherEntity
->SignalEmitationDecrease(EmitationUpdate
);
591 GetLSquareUnder()->SignalEmitationDecrease(EmitationUpdate
);
598 void stack::CalculateEmitation () {
599 Emitation
= GetSideEmitation(CENTER
);
603 col24
stack::GetSideEmitation (int RequiredSquarePosition
) {
605 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
)
606 if (i
->GetSquarePosition() == RequiredSquarePosition
) game::CombineLights(Emitation
, i
->GetEmitation());
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;
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
);
632 for (uInt c
= 0; c
< ItemVector
.size(); ++c
)
633 if (ItemVector
[c
]->Exists() && ItemVector
[c
]->DuplicateToStack(this, Flags
) && ++p
== Max
) break;
638 /* Adds the item without any external update requests */
639 void stack::AddElement (item
*Item
, truth
) {
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
) {
649 (Slot
->Last
? Slot
->Last
->Next
: Bottom
) = Slot
->Next
;
650 (Slot
->Next
? Slot
->Next
->Last
: Top
) = Slot
->Last
;
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
;
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
688 void stack::Pile (itemvectorvector
&PileVector
, ccharacter
*Viewer
,
689 int RequiredSquarePosition
, sorter SorterFunction
) const
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
)))
698 for (std::list
<item
*>::iterator i
= List
.begin(); i
!= List
.end(); ++i
) {
699 PileVector
.resize(PileVector
.size()+1);
700 itemvector
& Pile
= PileVector
.back();
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
)) {
707 std::list
<item
*>::iterator Dirt
= j
++;
715 std::stable_sort(PileVector
.begin(), PileVector
.end(), CategorySorter
);
719 /* Total price of the stack */
720 sLong
stack::GetTruePrice () const {
722 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) Price
+= i
->GetTruePrice();
727 /* GUI used for instance by chests and bookcases.
728 Returns whether anything was done. */
729 truth
stack::TakeSomethingFrom (character
*Opener
, cfestring
&ContainerName
) {
731 ADD_MESSAGE("There is nothing in %s.", ContainerName
.CStr());
734 truth Success
= false;
735 room
*Room
= GetLSquareUnder()->GetRoom();
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());
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());
759 truth Success
= false;
760 room
*Room
= GetLSquareUnder()->GetRoom();
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());
771 uInt Amount
= Min
<uInt
>((StorageVolume
-GetVolume())/ToPut
[0]->GetVolume(), ToPut
.size());
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());
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());
789 truth
stack::IsOnGround () const {
790 return !MotherEntity
|| MotherEntity
->IsOnGround();
794 int stack::GetSpoiledItems () const {
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
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;
823 void stack::PreProcessForBone () {
825 itemvector ItemVector
;
826 FillItemVector(ItemVector
);
827 for (uInt c
= 0; c
< ItemVector
.size(); ++c
) ItemVector
[c
]->PreProcessForBone();
832 void stack::PostProcessForBone () {
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
) {
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
);
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;
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
;
922 sLong
stack::GetWeight (ccharacter
*Viewer
, int SquarePosition
) const {
924 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
)
925 if (i
->GetSquarePosition() == SquarePosition
&& ((Flags
& HIDDEN
) || i
->CanBeSeenBy(Viewer
)))
926 Weight
+= i
->GetWeight();
931 truth
stack::DetectMaterial (cmaterial
*Material
) const {
932 for (stackiterator i
= GetBottom(); i
.HasItem(); ++i
) if (i
->DetectMaterial(Material
)) return true;
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;
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();