1 /*
2 *
3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
6 * Public License
7 *
8 * See LICENSING which should be included
9 * along with this file for more details
10 *
11 */
12
13 /* Compiled through itemset.cpp */
14
15 #include "dbgmsgproj.h"
16
17 cchar* ToHitValueDescription[] =
18 {
19 "unbelievably inaccurate",
20 "extremely inaccurate",
21 "inaccurate",
22 "decently accurate",
23 "accurate",
24 "highly accurate",
25 "extremely accurate",
26 "unbelievably accurate"
27 };
28 cchar* StrengthValueDescription[] =
29 {
30 "fragile",
31 "rather sturdy",
32 "sturdy",
33 "strong",
34 "very strong",
35 "extremely strong",
36 "almost unbreakable"
37 };
38
itemprototype(const itemprototype * Base,itemspawner Spawner,itemcloner Cloner,cchar * ClassID)39 itemprototype::itemprototype(const itemprototype* Base, itemspawner Spawner, itemcloner Cloner, cchar* ClassID)
40 : Base(Base), Spawner(Spawner), Cloner(Cloner), ClassID(ClassID) { Index = protocontainer<item>::Add(this); }
41
AllowRandomInstantiation() const42 truth itemdatabase::AllowRandomInstantiation() const { return !(Config & S_LOCK_ID); }
43
item()44 item::item() : Slot(0), CloneMotherID(0), Fluid(0), LifeExpectancy(0), ItemFlags(0), iRotateFlyingThrownStep(0) { }
IsOnGround() const45 truth item::IsOnGround() const { return Slot[0]->IsOnGround(); }
IsSimilarTo(item * Item) const46 truth item::IsSimilarTo(item* Item) const { return Item->GetType() == GetType() && Item->GetConfig() == GetConfig(); }
GetBaseDamage() const47 double item::GetBaseDamage() const { return Max(0., sqrt(5e-5 * GetWeaponStrength()) + GetDamageBonus()); }
GetBaseMinDamage() const48 int item::GetBaseMinDamage() const { return int(GetBaseDamage() * 0.75); }
GetBaseMaxDamage() const49 int item::GetBaseMaxDamage() const { return int(GetBaseDamage() * 1.25) + 1; }
GetBaseToHitValue() const50 int item::GetBaseToHitValue() const { return int(10000. / (1000 + GetWeight()) + GetTHVBonus()); }
GetBaseBlockValue() const51 int item::GetBaseBlockValue() const { return int((10000. / (1000 + GetWeight()) + GetTHVBonus()) * GetBlockModifier() / 10000.); }
IsInCorrectSlot(int I) const52 truth item::IsInCorrectSlot(int I) const { return I == RIGHT_WIELDED_INDEX || I == LEFT_WIELDED_INDEX; }
IsInCorrectSlot() const53 truth item::IsInCorrectSlot() const { return IsInCorrectSlot(static_cast<gearslot*>(*Slot)->GetEquipmentIndex()); }
GetEquipmentIndex() const54 int item::GetEquipmentIndex() const { return static_cast<gearslot*>(*Slot)->GetEquipmentIndex(); }
GetGraphicsContainerIndex() const55 int item::GetGraphicsContainerIndex() const { return GR_ITEM; }
IsBroken() const56 truth item::IsBroken() const { return GetConfig() & BROKEN; }
IsFood() const57 truth item::IsFood() const { return DataBase->Category & FOOD; }
GetBreakVerb() const58 cchar* item::GetBreakVerb() const { return "breaks"; }
GetSquareUnderEntity(int I) const59 square* item::GetSquareUnderEntity(int I) const { return GetSquareUnder(I); }
GetSquareUnder(int I) const60 square* item::GetSquareUnder(int I) const { return Slot[I] ? Slot[I]->GetSquareUnder() : 0; }
GetLSquareUnder(int I) const61 lsquare* item::GetLSquareUnder(int I) const { return static_cast<lsquare*>(Slot[I]->GetSquareUnder()); }
SignalStackAdd(stackslot * StackSlot,void (stack::*)(item *,truth))62 void item::SignalStackAdd(stackslot* StackSlot, void (stack::*)(item*, truth)) { Slot[0] = StackSlot; }
IsAnimated() const63 truth item::IsAnimated() const { return GraphicData.AnimationFrames > 1 || (Fluid && ShowFluids()) || (IsBurning()); }
IsRusted() const64 truth item::IsRusted() const { return MainMaterial->GetRustLevel() != NOT_RUSTED; }
IsBurnt() const65 truth item::IsBurnt() const { return MainMaterial->GetBurnLevel() != NOT_BURNT; }
IsEatable(ccharacter * Eater) const66 truth item::IsEatable(ccharacter* Eater) const { return GetConsumeMaterial(Eater, &material::IsSolid) && IsConsumable() && !IsBurning(); }
IsDrinkable(ccharacter * Eater) const67 truth item::IsDrinkable(ccharacter* Eater) const { return GetConsumeMaterial(Eater, &material::IsLiquid) && IsConsumable() && !IsBurning(); }
IsValidRecipeIngredient(ccharacter *) const68 truth item::IsValidRecipeIngredient(ccharacter*) const { return ValidRecipeIngredient; }
GetFluidPixelAllowedPredicate() const69 pixelpredicate item::GetFluidPixelAllowedPredicate() const { return &rawbitmap::IsTransparent; }
Cannibalize()70 void item::Cannibalize() { Flags |= CANNIBALIZED; }
SetMainMaterial(material * NewMaterial,int SpecialFlags)71 material* item::SetMainMaterial(material* NewMaterial, int SpecialFlags)
72 { return SetMaterial(MainMaterial, NewMaterial, GetDefaultMainVolume(), SpecialFlags); }
InitMaterials(const materialscript * M,const materialscript *,truth CUP)73 void item::InitMaterials(const materialscript* M, const materialscript*, truth CUP)
74 { InitMaterials(M->Instantiate(), CUP); }
GetMainMaterialRustLevel() const75 int item::GetMainMaterialRustLevel() const { return MainMaterial->GetRustLevel(); }
76
item(citem & Item)77 item::item(citem& Item)
78 : object(Item), Slot(0), Size(Item.Size), DataBase(Item.DataBase), Volume(Item.Volume), Weight(Item.Weight),
79 iRotateFlyingThrownStep(Item.iRotateFlyingThrownStep),
80 Fluid(0), SquaresUnder(Item.SquaresUnder), LifeExpectancy(Item.LifeExpectancy), ItemFlags(Item.ItemFlags)
81 {
82 Flags &= ENTITY_FLAGS|SQUARE_POSITION_BITS;
83 ID = game::CreateNewItemID(this);
84
85 CloneMotherID = new idholder(Item.ID);
86 idholder* TI = CloneMotherID;
87 for(idholder* II = Item.CloneMotherID; II; II = II->Next)
88 TI = TI->Next = new idholder(II->ID);
89 TI->Next = 0;
90
91 Slot = new slot*[SquaresUnder];
92 for(int c = 0; c < SquaresUnder; ++c)
93 Slot[c] = 0;
94 }
95
~item()96 item::~item()
97 {
98 delete [] Slot;
99 game::RemoveItemID(ID);
100
101 fluid** FP = Fluid;
102
103 if(FP)
104 {
105 for(int c = 0; c < SquaresUnder; ++c)
106 for(fluid* F = FP[c]; F;)
107 {
108 fluid* ToDel = F;
109 F = F->Next;
110 delete ToDel;
111 }
112
113 delete [] FP;
114 }
115
116 for(idholder* I = CloneMotherID; I;)
117 {
118 idholder* ToDel = I;
119 I = I->Next;
120 delete ToDel;
121 }
122 }
123
Fly(character * Thrower,int Direction,int Force,bool bTryStartThrownRotation)124 void item::Fly(character* Thrower, int Direction, int Force, bool bTryStartThrownRotation)
125 {
126 if(ivanconfig::GetRotateTimesPerSquare() > 0)
127 {
128 iRotateFlyingThrownStep=0; //simple granted reset
129 }
130 lsquare* LandingSquare=NULL;
131
132 int Range = Force * 25 / Max(long(sqrt(GetWeight())), 1L);
133
134 lsquare* LSquareUnder = GetLSquareUnder();
135 RemoveFromSlot();
136 LSquareUnder->GetStack()->AddItem(this, false);
137 LandingSquare=LSquareUnder;
138
139 if(!Range || GetSquaresUnder() != 1)
140 {
141 if(GetLSquareUnder()->GetRoom())
142 GetLSquareUnder()->GetRoom()->AddItemEffect(this);
143
144 return;
145 }
146
147 if(Direction == RANDOM_DIR)
148 Direction = RAND() & 7;
149
150 v2 StartingPos = GetPos();
151 v2 Pos = StartingPos;
152 v2 DirVector = game::GetMoveVector(Direction);
153 truth Breaks = false;
154 double BaseDamage, BaseToHitValue;
155
156 /*** check ***/
157 int iRotateTimes = ivanconfig::GetRotateTimesPerSquare();
158
159 if(Thrower)
160 {
161 int Bonus = Thrower->IsHumanoid() ? Thrower->GetCWeaponSkill(GetWeaponCategory())->GetBonus() : 1000;
162 BaseDamage = sqrt(5e-12 * GetWeaponStrength() * Force / Range) * Bonus;
163 BaseToHitValue = 10 * Bonus * Thrower->GetMoveEase()
164 / (500 + GetWeight()) * Thrower->GetAttribute(DEXTERITY)
165 * sqrt(2.5e-8 * Thrower->GetAttribute(PERCEPTION)) / Range;
166
167 if(bTryStartThrownRotation && ivanconfig::GetRotateTimesPerSquare()>0)
168 iRotateFlyingThrownStep = (clock()%2)==0 ? 1 : -1; //init rotation
169
170 if(!Thrower->IsPlayer() && iRotateTimes>1)
171 iRotateTimes=1; //disable "dramatic" rotations from NPCs but still keep minimum if enabled
172 }
173 else
174 {
175 BaseDamage = sqrt(5e-6 * GetWeaponStrength() * Force / Range);
176 BaseToHitValue = 10 * 100 / (500 + GetWeight()) / Range;
177 iRotateTimes=0;
178 }
179
180 int RangeLeft;
181
182 truth Draw=false;
183 float fFlyDelay = 0.03;
184 bool bLowerRotationsPerSqr = iRotateTimes==5;
185 for(RangeLeft = Range; RangeLeft; --RangeLeft)
186 {
187 if(!GetLevel()->IsValidPos(Pos + DirVector))
188 break;
189
190 lsquare* JustHit = GetNearLSquare(Pos + DirVector);
191
192 if(!JustHit->IsFlyable())
193 {
194 Breaks = true;
195 JustHit->GetOLTerrain()->HasBeenHitByItem(Thrower, this, int(BaseDamage * sqrt(RangeLeft)));
196 break;
197 }
198 else
199 {
200 clock_t StartTime = clock();
201 Pos += DirVector;
202 RemoveFromSlot();
203 JustHit->GetStack()->AddItem(this, false);
204 LandingSquare=JustHit;
205 Draw = game::OnScreen(JustHit->GetPos()) && JustHit->CanBeSeenByPlayer();
206
207 if(Draw)
208 game::DrawEverything();
209
210 if(JustHit->GetCharacter())
211 {
212 int Damage = int(BaseDamage * sqrt(RangeLeft));
213 double ToHitValue = BaseToHitValue * RangeLeft;
214 int Returned = HitCharacter(Thrower, JustHit->GetCharacter(), Damage, ToHitValue, Direction);
215
216 if(Returned == HIT)
217 Breaks = true;
218
219 if(Returned != MISSED)
220 break;
221 }
222
223 if(Draw)
224 while(clock() - StartTime < fFlyDelay * CLOCKS_PER_SEC);
225
226 if(ivanconfig::GetRotateTimesPerSquare()>0 && iRotateFlyingThrownStep!=0){
227 if(iRotateTimes==1){
228 iRotateFlyingThrownStep += iRotateFlyingThrownStep>0 ? 1 : -1; //next rotation step on next square
229 }else{ //if rotation steps is >= 2 rotate at least one more time on the same square
230 for(int i=0;i<(iRotateTimes-1);i++){
231 iRotateFlyingThrownStep += iRotateFlyingThrownStep>0 ? 1 : -1;
232 if(Draw){
233 StartTime = clock();
234 RemoveFromSlot();JustHit->GetStack()->AddItem(this, false); //TODO find a better way then remove and re-add to same square to redraw...
235 game::DrawEverything();
236 if(bLowerRotationsPerSqr){
237 iRotateTimes--;
238 if(iRotateTimes<1)iRotateTimes=1;
239 }
240 //while(clock() - StartTime < fFlyDelay * CLOCKS_PER_SEC);
241 }
242 }
243 }
244 }
245
246 }
247
248 }
249
250 if(ivanconfig::GetRotateTimesPerSquare()>0 && iRotateFlyingThrownStep!=0){ //must be disabled before exiting Fly()
251 iRotateFlyingThrownStep=4; //default rotation is w/o the rotation flags at the switch(){}
252 //force redraw at default rotation to avoid another spin when player moves TODO how to let it stay in the last rotation?
253 RemoveFromSlot();LandingSquare->GetStack()->AddItem(this, false); //TODO find a better way then remove and re-add to same square...
254 game::DrawEverything();
255 iRotateFlyingThrownStep=0; //disables rotation
256 }
257
258 if(Breaks)
259 ReceiveDamage(Thrower, int(sqrt(GetWeight() * RangeLeft) / 10), THROW|PHYSICAL_DAMAGE, Direction);
260
261 if(Exists() && GetLSquareUnder()->GetRoom())
262 GetLSquareUnder()->GetRoom()->AddItemEffect(this);
263 }
264
HitCharacter(character * Thrower,character * Dude,int Damage,double ToHitValue,int Direction)265 int item::HitCharacter(character* Thrower, character* Dude, int Damage, double ToHitValue, int Direction)
266 {
267 if(Dude->Catches(this))
268 return CATCHED;
269
270 if(Thrower && !EffectIsGood())
271 Thrower->Hostility(Dude);
272
273 if(Dude->DodgesFlyingItem(this, ToHitValue))
274 {
275 if(Dude->IsPlayer())
276 ADD_MESSAGE("%s misses you.", CHAR_NAME(DEFINITE));
277 else if(Dude->CanBeSeenByPlayer())
278 ADD_MESSAGE("%s misses %s.", CHAR_NAME(DEFINITE), Dude->CHAR_NAME(DEFINITE));
279
280 return MISSED;
281 }
282
283 Dude->HasBeenHitByItem(Thrower, this, Damage, ToHitValue, Direction);
284 return HIT;
285 }
286
GetWeaponStrength() const287 double item::GetWeaponStrength() const
288 {
289 return GetFormModifier() * GetMainMaterial()->GetStrengthValue() * sqrt(GetMainMaterial()->GetWeight());
290 }
291
GetStrengthRequirement() const292 int item::GetStrengthRequirement() const
293 {
294 double WeightTimesSize = GetWeight() * GetSize();
295 return int(1.25e-10 * WeightTimesSize * WeightTimesSize);
296 }
297
Apply(character * Applier)298 truth item::Apply(character* Applier)
299 {
300 if(Applier->IsPlayer())
301 ADD_MESSAGE("You can't apply this!");
302
303 return false;
304 }
305
306 /* Returns truth that tells whether the Polymorph really happened */
307
Polymorph(character * Polymorpher,stack * CurrentStack)308 truth item::Polymorph(character* Polymorpher, stack* CurrentStack)
309 {
310 if(!IsPolymorphable())
311 return false;
312 else
313 {
314 if(Polymorpher && IsOnGround())
315 {
316 room* Room = GetRoom();
317
318 if(Room)
319 Room->HostileAction(Polymorpher);
320 }
321
322 if(GetSquarePosition() != CENTER)
323 {
324 stack* Stack = CurrentStack->GetLSquareUnder()->GetStackOfAdjacentSquare(GetSquarePosition());
325
326 if(Stack)
327 CurrentStack = Stack;
328 }
329
330 CurrentStack->AddItem(protosystem::BalancedCreateItem(0, MAX_PRICE, ANY_CATEGORY, 0, 0, 0, true));
331 RemoveFromSlot();
332 SendToHell();
333 return true;
334 }
335 }
336
Polymorph(character * Polymorpher,character * Wielder)337 truth item::Polymorph(character* Polymorpher, character* Wielder)
338 {
339 if(!IsPolymorphable())
340 return false;
341 else if(!Wielder->Equips(this))
342 return false;
343 else
344 {
345 if(Polymorpher && Wielder)
346 {
347 Polymorpher->Hostility(Wielder);
348 }
349
350 item* NewItem = protosystem::BalancedCreateItem(0, MAX_PRICE, ANY_CATEGORY, 0, 0, 0, true);
351 int EquipSlot = GetEquipmentIndex();
352
353 if(Wielder->IsPlayer())
354 ADD_MESSAGE("Your %s polymorphs into %s.", CHAR_NAME(UNARTICLED), NewItem->CHAR_NAME(INDEFINITE));
355 else if(CanBeSeenByPlayer())
356 ADD_MESSAGE("%s's %s polymorphs into %s.", Wielder->CHAR_NAME(DEFINITE), CHAR_NAME(UNARTICLED), NewItem->CHAR_NAME(INDEFINITE));
357
358 RemoveFromSlot();
359 SendToHell();
360
361 switch (EquipSlot)
362 {
363 /*
364 case HELMET_INDEX: Wielder->SetHelmet(NewItem); break;
365 case AMULET_INDEX: Wielder->SetAmulet(NewItem); break;
366 case CLOAK_INDEX: Wielder->SetCloak(NewItem); break;
367 case BODY_ARMOR_INDEX: Wielder->SetBodyArmor(NewItem); break;
368 case BELT_INDEX: Wielder->SetBelt(NewItem); break;
369 */
370 case RIGHT_WIELDED_INDEX: Wielder->SetRightWielded(NewItem); break;
371 case LEFT_WIELDED_INDEX: Wielder->SetLeftWielded(NewItem); break;
372 /*
373 case RIGHT_RING_INDEX: Wielder->SetRightRing(NewItem); break;
374 case LEFT_RING_INDEX: Wielder->SetLeftRing(NewItem); break;
375 case RIGHT_GAUNTLET_INDEX: Wielder->SetRightGauntlet(NewItem); break;
376 case LEFT_GAUNTLET_INDEX: Wielder->SetLeftGauntlet(NewItem); break;
377 case RIGHT_BOOT_INDEX: Wielder->SetRightBoot(NewItem); break;
378 case LEFT_BOOT_INDEX: Wielder->SetLeftBoot(NewItem); break;
379 */
380 default: Wielder->ReceiveItemAsPresent(NewItem); break;
381 }
382
383 if(Wielder->IsPlayer())
384 game::AskForKeyPress(CONST_S("Equipment polymorphed! [press any key to continue]"));
385 return true;
386 }
387 }
388
389 /* Returns truth that tells whether the alchemical conversion really happened. */
390
Alchemize(character * Midas,stack * CurrentStack)391 truth item::Alchemize(character* Midas, stack* CurrentStack)
392 {
393 if(IsQuestItem())
394 return false;
395 else
396 {
397 if(Midas && IsOnGround())
398 {
399 room* Room = GetRoom();
400
401 if(Room)
402 Room->HostileAction(Midas);
403 }
404
405 long Price = GetTruePrice();
406
407 if(Price)
408 {
409 Price /= 4; /* slightly lower than with 10 Cha */
410 ADD_MESSAGE("Gold pieces clatter on the floor.");
411 Midas->SetMoney(Midas->GetMoney() + Price);
412 }
413
414 RemoveFromSlot();
415 SendToHell();
416 return true;
417 }
418 }
419
SoftenMaterial()420 truth item::SoftenMaterial()
421 {
422 if(!IsMaterialChangeable() || !CanBeSoftened())
423 {
424 return false;
425 }
426
427 int Config = GetMainMaterial()->GetSoftenedMaterial(this);
428
429 if(!Config)
430 {
431 /* Should not be possible. */
432 return false;
433 }
434
435 msgsystem::EnterBigMessageMode();
436
437 if(CanBeSeenByPlayer())
438 ADD_MESSAGE("Suddenly %s starts glowing dull yellow.", CHAR_NAME(INDEFINITE));
439
440 material* TempMaterial = MAKE_MATERIAL(Config);
441 material* MainMaterial = GetMainMaterial();
442 material* SecondaryMaterial = GetSecondaryMaterial();
443
444 if(SecondaryMaterial && SecondaryMaterial->IsSameAs(MainMaterial))
445 delete SetSecondaryMaterial(TempMaterial->SpawnMore());
446
447 delete SetMainMaterial(TempMaterial);
448
449 if(CanBeSeenByPlayer())
450 ADD_MESSAGE("It softens into %s!", GetMainMaterial()->GetName(false, false).CStr());
451
452 msgsystem::LeaveBigMessageMode();
453 return true;
454 }
455
456 /* Returns whether the Eater must stop eating the item */
457
Consume(character * Eater,long Amount)458 truth item::Consume(character* Eater, long Amount)
459 {
460 material* ConsumeMaterial = GetConsumeMaterial(Eater);
461
462 if(!ConsumeMaterial)
463 return true;
464
465 if(Eater->IsPlayer() && !(Flags & CANNIBALIZED) && Eater->CheckCannibalism(ConsumeMaterial))
466 {
467 game::DoEvilDeed(25);
468 ADD_MESSAGE("You feel that this was an evil deed.");
469 Cannibalize();
470 }
471
472 ulong ID = GetID();
473 material* Garbage = ConsumeMaterial->EatEffect(Eater, Amount);
474 item* NewConsuming = GetID() ? this : game::SearchItem(ID);
475 material* NewConsumeMaterial = NewConsuming->GetConsumeMaterial(Eater);
476
477 if(!NewConsuming->Exists()
478 || !NewConsumeMaterial
479 || !NewConsumeMaterial->IsSameAs(ConsumeMaterial))
480 ConsumeMaterial->FinishConsuming(Eater);
481
482 delete Garbage;
483 return !NewConsuming->Exists() || !NewConsumeMaterial;
484 }
485
CanBeEatenByAI(ccharacter * Eater) const486 truth item::CanBeEatenByAI(ccharacter* Eater) const
487 {
488 material* ConsumeMaterial = GetConsumeMaterial(Eater);
489
490 return (!Eater->IsPet()
491 || !(Eater->GetCommandFlags() & DONT_CONSUME_ANYTHING_VALUABLE)
492 || !IsValuable())
493 && IsConsumable()
494 && ConsumeMaterial && ConsumeMaterial->CanBeEatenByAI(Eater);
495 }
496
HasTag(char tag)497 bool item::HasTag(char tag)
498 {
499 static char Tag[3]={'#',0,0};
500 Tag[1]=tag;
501 return label.Find(Tag,0) != festring::NPos;
502 }
503
504 /**
505 * look for all usages to avoid tag clashes
506 */
SetTag(char tag)507 void item::SetTag(char tag)
508 {
509 if(!HasTag(tag))
510 label<<"#"<<tag;
511 }
512
ClearTag(char tag)513 void item::ClearTag(char tag)
514 {
515 static char Tag[3]={'#',0,0};
516 Tag[1]=tag;
517 int pos = label.Find(Tag,0);
518 if(pos != festring::NPos)
519 label.Erase(pos,2);
520 }
521
SetLabel(cfestring & What)522 void item::SetLabel(cfestring& What)
523 {
524 label.Empty();
525 if(!What.IsEmpty())
526 label << What;
527 }
528
AddName(festring & Name,int Case) const529 void item::AddName(festring& Name, int Case) const
530 {
531 object::AddName(Name,Case);
532
533 if(label.GetSize())
534 Name << " inscribed " << label;
535 }
536
Save(outputfile & SaveFile) const537 void item::Save(outputfile& SaveFile) const
538 {
539 SaveFile << static_cast<ushort>(GetType());
540 object::Save(SaveFile);
541 SaveFile << static_cast<ushort>(GetConfig());
542 SaveFile << static_cast<ushort>(Flags);
543 SaveFile << Size << ID << LifeExpectancy << ItemFlags;
544 SaveFile << label;
545 SaveLinkedList(SaveFile, CloneMotherID);
546
547 if(Fluid)
548 {
549 SaveFile.Put(true);
550
551 for(int c = 0; c < SquaresUnder; ++c)
552 SaveLinkedList(SaveFile, Fluid[c]);
553 }
554 else
555 SaveFile.Put(false);
556 }
557
Load(inputfile & SaveFile)558 void item::Load(inputfile& SaveFile)
559 {
560 object::Load(SaveFile);
561 databasecreator<item>::InstallDataBase(this, ReadType<ushort>(SaveFile));
562 Flags |= ReadType<ushort>(SaveFile) & ~ENTITY_FLAGS;
563 SaveFile >> Size >> ID >> LifeExpectancy >> ItemFlags;
564 if(game::GetCurrentSavefileVersion()>=132)
565 SaveFile >> label;
566 LoadLinkedList(SaveFile, CloneMotherID);
567
568 if(LifeExpectancy)
569 Enable();
570
571 game::AddItemID(this, ID);
572
573 if(SaveFile.Get())
574 {
575 Fluid = new fluid*[SquaresUnder];
576
577 for(int c = 0; c < SquaresUnder; ++c)
578 {
579 LoadLinkedList(SaveFile, Fluid[c]);
580
581 for(fluid* F = Fluid[c]; F; F = F->Next)
582 F->SetMotherItem(this);
583 }
584 }
585 }
586
TeleportRandomly()587 void item::TeleportRandomly()
588 {
589 if(GetSquaresUnder() == 1) // gum solution
590 {
591 lsquare* Square = GetNearLSquare(GetLevel()->GetRandomSquare());
592 MoveTo(Square->GetStack());
593
594 if(Square->CanBeSeenByPlayer())
595 ADD_MESSAGE("Suddenly %s appears!", CHAR_NAME(INDEFINITE));
596 }
597 }
598
GetStrengthValue() const599 int item::GetStrengthValue() const
600 {
601 return long(GetStrengthModifier()) * GetMainMaterial()->GetStrengthValue() / 2000;
602 }
603
RemoveFromSlot()604 void item::RemoveFromSlot()
605 {
606 for(int c = 0; c < SquaresUnder; ++c)
607 if(Slot[c])
608 {
609 try
610 {
611 Slot[c]->Empty();
612 }
613 catch(quitrequest)
614 {
615 SendToHell();
616 throw;
617 }
618
619 Slot[c] = 0;
620 }
621 }
622
MoveTo(stack * Stack)623 void item::MoveTo(stack* Stack)
624 {
625 RemoveFromSlot();
626 Stack->AddItem(this);
627 }
628
GetItemCategoryName(long Category)629 cchar* item::GetItemCategoryName(long Category) // convert to array
630 {
631 switch(Category)
632 {
633 case HELMET: return "Helmets";
634 case AMULET: return "Amulets";
635 case CLOAK: return "Cloaks";
636 case BODY_ARMOR: return "Body armors";
637 case WEAPON: return "Weapons";
638 case SHIELD: return "Shields";
639 case RING: return "Rings";
640 case GAUNTLET: return "Gauntlets";
641 case BELT: return "Belts";
642 case BOOT: return "Boots";
643 case FOOD: return "Food";
644 case POTION: return "Potions";
645 case SCROLL: return "Scrolls";
646 case BOOK: return "Books";
647 case WAND: return "Wands";
648 case TOOL: return "Tools";
649 case VALUABLE: return "Valuables";
650 case MISC: return "Miscellaneous items";
651 }
652
653 return "Warezzz";
654 }
655
GetResistance(int Type) const656 int item::GetResistance(int Type) const
657 {
658 switch(Type&0xFFF)
659 {
660 case PHYSICAL_DAMAGE: return GetStrengthValue();
661 case ENERGY: return GetEnergyResistance();
662 case DRAIN:
663 case PSI:
664 case MUSTARD_GAS_DAMAGE:
665 return 0;
666 case FIRE: return GetFireResistance();
667 case POISON: return GetPoisonResistance();
668 case ELECTRICITY: return GetElectricityResistance();
669 case ACID: return GetAcidResistance();
670 case SOUND: return GetSoundResistance();
671 }
672
673 ABORT("Resistance lack detected!");
674 return 0;
675 }
676
Open(character * Char)677 truth item::Open(character* Char)
678 {
679 if(Char->IsPlayer())
680 ADD_MESSAGE("You can't open %s.", CHAR_NAME(DEFINITE));
681
682 return false;
683 }
684
SpawnAndLoad(inputfile & SaveFile) const685 item* itemprototype::SpawnAndLoad(inputfile& SaveFile) const
686 {
687 item* Item = Spawner(0, LOAD);
688 Item->Load(SaveFile);
689 Item->CalculateAll();
690 return Item;
691 }
692
LoadDataBaseStats()693 void item::LoadDataBaseStats()
694 {
695 SetSize(GetDefaultSize());
696 }
697
Initialize(int NewConfig,int SpecialFlags)698 void item::Initialize(int NewConfig, int SpecialFlags)
699 {
700 CalculateSquaresUnder();
701 Slot = new slot*[SquaresUnder];
702
703 for(int c = 0; c < SquaresUnder; ++c)
704 Slot[c] = 0;
705
706 if(!(SpecialFlags & LOAD))
707 {
708 ID = game::CreateNewItemID(this);
709 databasecreator<item>::InstallDataBase(this, NewConfig);
710 LoadDataBaseStats();
711 RandomizeVisualEffects();
712 Flags |= CENTER << SQUARE_POSITION_SHIFT;
713
714 if(!(SpecialFlags & NO_MATERIALS))
715 GenerateMaterials();
716 }
717
718 if(!(SpecialFlags & LOAD))
719 PostConstruct();
720
721 if(!(SpecialFlags & (LOAD|NO_MATERIALS)))
722 {
723 CalculateAll();
724
725 if(!(SpecialFlags & NO_PIC_UPDATE))
726 UpdatePictures();
727 }
728 }
729
ShowMaterial() const730 truth item::ShowMaterial() const
731 {
732 if(GetMainMaterialConfig().Size == 1)
733 return GetMainMaterial()->GetConfig() != GetMainMaterialConfig()[0];
734 else
735 return true;
736 }
737
GetBlockModifier() const738 long item::GetBlockModifier() const
739 {
740 if(!IsShield(0))
741 return GetSize() * GetRoundness() << 1;
742 else
743 return GetSize() * GetRoundness() << 2;
744 }
745
CanBeSeenByPlayer() const746 truth item::CanBeSeenByPlayer() const
747 {
748 return CanBeSeenBy(PLAYER);
749 }
750
CanBeSeenBy(ccharacter * Who) const751 truth item::CanBeSeenBy(ccharacter* Who) const
752 {
753 for(int c = 0; c < SquaresUnder; ++c)
754 if(Slot[c] && Slot[c]->CanBeSeenBy(Who))
755 return true;
756
757 return Who->IsPlayer() && game::GetSeeWholeMapCheatMode();
758 }
759
GetDescription(int Case) const760 festring item::GetDescription(int Case) const
761 {
762 if(CanBeSeenByPlayer())
763 return GetName(Case);
764 else
765 return CONST_S("something");
766 }
767
SignalVolumeAndWeightChange()768 void item::SignalVolumeAndWeightChange()
769 {
770 CalculateVolumeAndWeight();
771
772 for(int c = 0; c < SquaresUnder; ++c)
773 if(Slot[c])
774 Slot[c]->SignalVolumeAndWeightChange();
775 }
776
CalculateVolumeAndWeight()777 void item::CalculateVolumeAndWeight()
778 {
779 Volume = Weight = 0;
780
781 for(int c = 0; c < GetMaterials(); ++c)
782 {
783 cmaterial* Material = GetMaterial(c);
784
785 if(Material)
786 {
787 Volume += Material->GetVolume();
788 Weight += Material->GetWeight();
789 }
790 }
791 }
792
SignalEmitationIncrease(col24 EmitationUpdate)793 void item::SignalEmitationIncrease(col24 EmitationUpdate)
794 {
795 if(game::CompareLights(EmitationUpdate, Emitation) > 0)
796 {
797 game::CombineLights(Emitation, EmitationUpdate);
798
799 for(int c = 0; c < SquaresUnder; ++c)
800 if(Slot[c])
801 Slot[c]->SignalEmitationIncrease(EmitationUpdate);
802 }
803 }
804
SignalEmitationDecrease(col24 EmitationUpdate)805 void item::SignalEmitationDecrease(col24 EmitationUpdate)
806 {
807 if(game::CompareLights(EmitationUpdate, Emitation) >= 0 && Emitation)
808 {
809 col24 Backup = Emitation;
810 CalculateEmitation();
811
812 if(Backup != Emitation)
813 for(int c = 0; c < SquaresUnder; ++c)
814 if(Slot[c])
815 Slot[c]->SignalEmitationDecrease(EmitationUpdate);
816 }
817 }
818
CalculateAll()819 void item::CalculateAll()
820 {
821 CalculateVolumeAndWeight();
822 CalculateEmitation();
823 }
824
825 /* Temporary and buggy. */
826
WeaponSkillHit(int Hits)827 void item::WeaponSkillHit(int Hits)
828 {
829 if(Slot[0] && Slot[0]->IsGearSlot())
830 static_cast<arm*>(static_cast<gearslot*>(*Slot)->GetBodyPart())->WieldedSkillHit(Hits);
831 }
832
833 /* Returns 0 if item cannot be cloned */
834
Duplicate(ulong Flags)835 item* item::Duplicate(ulong Flags)
836 {
837 if(!(Flags & IGNORE_PROHIBITIONS)
838 && ((!(Flags & MIRROR_IMAGE) && !CanBeCloned())
839 || (Flags & MIRROR_IMAGE && (!CanBeMirrored()
840 || (MainMaterial
841 && !(MainMaterial->GetCommonFlags() & CAN_BE_MIRRORED))
842 || (GetSecondaryMaterial()
843 && !(GetSecondaryMaterial()->GetCommonFlags() & CAN_BE_MIRRORED))))))
844 return 0;
845
846 item* Clone = GetProtoType()->Clone(this);
847
848 if(Flags & MIRROR_IMAGE)
849 Clone->SetLifeExpectancy(Flags >> LE_BASE_SHIFT & LE_BASE_RANGE,
850 Flags >> LE_RAND_SHIFT & LE_RAND_RANGE);
851
852 Clone->UpdatePictures();
853 return Clone;
854 }
855
AddInventoryEntry(ccharacter *,festring & Entry,int Amount,truth ShowSpecialInfo) const856 void item::AddInventoryEntry(ccharacter*, festring& Entry, int Amount, truth ShowSpecialInfo) const
857 {
858 if(Amount == 1)
859 AddName(Entry, INDEFINITE);
860 else
861 {
862 Entry << Amount << ' ';
863 AddName(Entry, PLURAL);
864 }
865
866 if(ShowSpecialInfo){
867 Entry << " [" << GetWeight() * Amount << "g"; //TODO if the 1st and 2nd of 3 items have 100g and the last has 2000g, the weight shown would be 300g ... now that lumps, stones and sticks are useful, this may not be that good...
868 if(ivanconfig::IsShowVolume()){
869 Entry << " " << GetVolume() * Amount << "cm3"; //the item can be seen therefore it's volume guessed already
870 if(GetSecondaryMaterial()==NULL){ //simple items like ingots sticks etc
871 static char density[20];
872 sprintf(density, "%.1f", GetWeight()/(float)GetVolume());
873 Entry << " " << density << "g/cm3"; //the item can be seen and weighted already so this just helps avoiding having to mentally calc density for every item
874 if(game::WizardModeIsActive()) //TODO || Char-> possess item <materialmanual*>
875 Entry << " " << GetStrengthValue() << "str"; //this is special info tho.
876 }
877 }
878 Entry << "]";
879 }
880 }
881
ChooseBaseForConfig(itemdatabase ** TempConfig,int Configs,int ConfigNumber)882 const itemdatabase* itemprototype::ChooseBaseForConfig(itemdatabase** TempConfig, int Configs, int ConfigNumber)
883 {
884 if(!(ConfigNumber & BROKEN))
885 return *TempConfig;
886 else
887 {
888 ConfigNumber ^= BROKEN;
889
890 for(int c = 0; c < Configs; ++c)
891 if(TempConfig[c]->Config == ConfigNumber)
892 return TempConfig[c];
893
894 return *TempConfig;
895 }
896 }
897
ReceiveDamage(character * Damager,int Damage,int Type,int Dir)898 truth item::ReceiveDamage(character* Damager, int Damage, int Type, int Dir)
899 {
900 if(MainMaterial && IsDestroyable(Damager))
901 {
902 if(CanBeBurned() && (MainMaterial->GetInteractionFlags() & CAN_BURN) && !IsBurning() && Type & FIRE)
903 {
904 TestActivationEnergy(Damage);
905 }
906 else if(IsBurning() && Type & FIRE)
907 GetMainMaterial()->AddToThermalEnergy(Damage);
908 }
909
910 if(CanBeBroken() && !IsBroken() && Type & (PHYSICAL_DAMAGE|SOUND|ENERGY|ACID))
911 {
912 int StrengthValue = GetStrengthValue();
913
914 if(!StrengthValue)
915 StrengthValue = 1;
916
917 if(Damage > StrengthValue << 2 && RAND() & 3 && RAND() % (25 * Damage / StrengthValue) >= 100)
918 {
919 Break(Damager, Dir);
920 return true;
921 }
922 }
923
924 if(Type & ACID && IsBroken() && IsDestroyable(Damager))
925 {
926 int StrengthValue = GetStrengthValue();
927
928 if(!StrengthValue)
929 StrengthValue = 1;
930
931 if(Damage > StrengthValue << 4 && !(RAND() & 3) && RAND() % (100 * Damage / StrengthValue) >= 100)
932 {
933 Destroy(Damager, Dir);
934 return true;
935 }
936 }
937
938 return false;
939 }
940
InitDefaults(const itemprototype * NewProtoType,int NewConfig)941 void itemdatabase::InitDefaults(const itemprototype* NewProtoType, int NewConfig)
942 {
943 IsAbstract = false;
944 ProtoType = NewProtoType;
945 Config = NewConfig;
946
947 if(NewConfig & BROKEN)
948 {
949 if(Adjective.GetSize())
950 Adjective.Insert(0, "broken ");
951 else
952 Adjective = CONST_S("broken");
953
954 DefaultSize >>= 1;
955 FormModifier >>= 2;
956 StrengthModifier >>= 1;
957 }
958 }
959
GetNutritionValue() const960 long item::GetNutritionValue() const
961 {
962 long NV = 0;
963
964 for(int c = 0; c < GetMaterials(); ++c)
965 if(GetMaterial(c))
966 NV += GetMaterial(c)->GetTotalNutritionValue();
967
968 return NV;
969 }
970
SignalSpoil(material *)971 void item::SignalSpoil(material*)
972 {
973 if(!Exists())
974 return;
975
976 if(CanBeSeenByPlayer())
977 ADD_MESSAGE("%s spoils completely.", GetExtendedDescription().CStr());
978
979 truth Equipped = PLAYER->Equips(this);
980 Disappear();
981
982 if(Equipped)
983 game::AskForKeyPress(CONST_S("Equipment destroyed! [press any key to continue]"));
984 }
985
SignalBurn(material * Material)986 void item::SignalBurn(material* Material)
987 {
988 if(!Exists())
989 return;
990
991 if(CanBeSeenByPlayer())
992 ADD_MESSAGE("%s burns away completely.", GetExtendedDescription().CStr());
993
994 truth Equipped = PLAYER->Equips(this);
995 Disappear();
996
997 if(Equipped)
998 game::AskForKeyPress(CONST_S("Equipment destroyed! [press any key to continue]"));
999 }
1000
DuplicateToStack(stack * CurrentStack,ulong Flags)1001 item* item::DuplicateToStack(stack* CurrentStack, ulong Flags)
1002 {
1003 item* Duplicated = Duplicate(Flags);
1004
1005 if(!Duplicated)
1006 return 0;
1007
1008 CurrentStack->AddItem(Duplicated);
1009 return Duplicated;
1010 }
1011
CanBePiledWith(citem * Item,ccharacter * Viewer) const1012 truth item::CanBePiledWith(citem* Item, ccharacter* Viewer) const
1013 {
1014 return (GetType() == Item->GetType()
1015 && GetConfig() == Item->GetConfig()
1016 && ItemFlags == Item->ItemFlags
1017 && ((!ivanconfig::IsAllWeightIsRelevant() && WeightIsIrrelevant()) || Weight == Item->Weight)
1018 && MainMaterial->IsSameAs(Item->MainMaterial)
1019 && MainMaterial->GetSpoilLevel() == Item->MainMaterial->GetSpoilLevel()
1020 && MainMaterial->GetRustLevel() == Item->MainMaterial->GetRustLevel()
1021 && MainMaterial->GetBurnLevel() == Item->MainMaterial->GetBurnLevel()
1022 && Viewer->GetCWeaponSkillLevel(this) == Viewer->GetCWeaponSkillLevel(Item)
1023 && Viewer->GetSWeaponSkillLevel(this) == Viewer->GetSWeaponSkillLevel(Item)
1024 && !Fluid && !Item->Fluid
1025 && !LifeExpectancy == !Item->LifeExpectancy
1026 && !IsBurning() == !Item->IsBurning()
1027 && label==Item->label );
1028 }
1029
Break(character * Breaker,int)1030 void item::Break(character* Breaker, int)
1031 {
1032 if(!CanBeBroken() || IsBroken())
1033 return;
1034
1035 if(CanBeSeenByPlayer())
1036 ADD_MESSAGE("%s %s.", GetExtendedDescription().CStr(), GetBreakVerb());
1037
1038 if(Breaker && IsOnGround())
1039 {
1040 room* Room = GetRoom();
1041
1042 if(Room)
1043 Room->HostileAction(Breaker);
1044 }
1045
1046 item* Broken = GetProtoType()->Clone(this);
1047 Broken->SetConfig(GetConfig() | BROKEN);
1048 Broken->SetSize(Broken->GetSize() >> 1);
1049 DonateFluidsTo(Broken);
1050 DonateIDTo(Broken);
1051 DonateSlotTo(Broken);
1052 SendToHell();
1053
1054 if(PLAYER->Equips(Broken))
1055 game::AskForKeyPress(CONST_S("Equipment broken! [press any key to continue]"));
1056 }
1057
Be()1058 void item::Be()
1059 {
1060 MainMaterial->Be(ItemFlags);
1061
1062 if(Exists() && LifeExpectancy)
1063 {
1064 if(LifeExpectancy == 1)
1065 {
1066 if(CanBeSeenByPlayer())
1067 ADD_MESSAGE("%s disappears.", GetExtendedDescription().CStr());
1068
1069 truth Equipped = PLAYER->Equips(this);
1070 Disappear();
1071
1072 if(Equipped)
1073 game::AskForKeyPress(CONST_S("Equipment destroyed! [press any key to continue]"));
1074 }
1075 else
1076 {
1077 --LifeExpectancy;
1078 //TODO fluids emitation on weapons require updating in case it lowers with time, this didnt work: if(Fluid)CalculateEmitation();
1079 }
1080 }
1081 }
1082
GetOfferValue(int Receiver) const1083 int item::GetOfferValue(int Receiver) const
1084 {
1085 /* Temporary */
1086
1087 int OfferValue = int(sqrt(GetTruePrice()));
1088
1089 if(Receiver == GetAttachedGod())
1090 OfferValue <<= 1;
1091 else
1092 OfferValue >>= 1;
1093
1094 return OfferValue;
1095 }
1096
SignalEnchantmentChange()1097 void item::SignalEnchantmentChange()
1098 {
1099 for(int c = 0; c < SquaresUnder; ++c)
1100 if(Slot[c])
1101 Slot[c]->SignalEnchantmentChange();
1102 }
1103
GetEnchantedPrice(int Enchantment) const1104 long item::GetEnchantedPrice(int Enchantment) const
1105 {
1106 if(!PriceIsProportionalToEnchantment())
1107 return item::GetPrice();
1108 else
1109 return Max<int>(item::GetPrice() * Enchantment * Enchantment, 0);
1110 }
1111
Fix()1112 item* item::Fix()
1113 {
1114 item* Fixed = this;
1115
1116 if(IsBroken())
1117 {
1118 Fixed = GetProtoType()->Clone(this);
1119 Fixed->SetConfig(GetConfig() ^ BROKEN);
1120 Fixed->SetSize(Fixed->GetSize() << 1);
1121 DonateFluidsTo(Fixed);
1122 DonateIDTo(Fixed);
1123 DonateSlotTo(Fixed);
1124 SendToHell();
1125 }
1126
1127 return Fixed;
1128 }
1129
DonateSlotTo(item * Item)1130 void item::DonateSlotTo(item* Item)
1131 {
1132 if(Slot[0])
1133 {
1134 Slot[0]->DonateTo(Item);
1135 Slot[0] = 0;
1136
1137 for(int c = 1; c < SquaresUnder; ++c)
1138 if(Slot[c])
1139 {
1140 Slot[c]->Empty();
1141 Slot[c] = 0;
1142 }
1143 }
1144 }
1145
GetSpoilLevel() const1146 int item::GetSpoilLevel() const
1147 {
1148 return MainMaterial->GetSpoilLevel();
1149 }
1150
GetBurnLevel() const1151 int item::GetBurnLevel() const
1152 {
1153 return MainMaterial->GetBurnLevel();
1154 }
1155
SignalSpoilLevelChange(material *)1156 void item::SignalSpoilLevelChange(material*)
1157 {
1158 if(!IsAnimated() && GetSpoilLevel() && Slot[0] && Slot[0]->IsVisible())
1159 for(int c = 0; c < SquaresUnder; ++c)
1160 GetSquareUnder(c)->IncStaticAnimatedEntities();
1161
1162 SignalVolumeAndWeightChange(); // gum
1163 UpdatePictures();
1164 }
1165
AllowSpoil() const1166 truth item::AllowSpoil() const
1167 {
1168 DBG5(GetName(DEFINITE).CStr(),GetID(),GetSquareUnder(),GetWearer(),GetSlot());
1169 DBGEXEC( //this wont even compile if DBGMSG is not enabled
1170 /** crash helper
1171 * THE CAUSE SEEMS TO BE from crafting a new item, it being a chest, and cancelling. The code was not sending the canceled spawned chest to hell and it was not placed anywhere. Fixed that, this may not happen again.
1172 * TODO remove this debug code and the workaround below after sure wont need anymore. despite the debug code will only compile if using DBGMSG
1173 * seems to be about a buggy organic spawned "on floor" that has no square under...
1174 * happens below at: if(IsOnGround())
1175 * item.cpp:1048:AllowSpoil:{GetName(2).CStr()}="the loaf of dark bread";{GetID()}="92980";{GetSquareUnder()}="0";{GetWearer()}="0";{GetSlot()}="0x8272630";
1176 2018/07/17-15:36:08(1122986) item.cpp:1049:AllowSpoil:{GetName(2).CStr()}="the loaf of dark bread";{GetID()}="780622";{GetSquareUnder()}="0";{GetWearer()}="0";{GetSlot()}="0x38be0e0";
1177 2018/07/17-15:36:08(1122987) item.cpp:1091:AllowSpoil:{itSS}="0x3293700";{ss->GetSquareUnder()}="0";
1178 2018/07/17-15:36:08(1122988) item.cpp:1091:AllowSpoil:{itSS->GetID()}="780622";{itSS->GetNameSingular().CStr()}="loaf";
1179 2018/07/17-15:36:08(1122989) item.cpp:1091:AllowSpoil:{ss->GetMotherStack()->GetItems()}="3";{ent}="0x62a17d0";{ss->GetMotherStack()->GetSquareUnder()}="0";
1180 2018/07/17-15:36:08(1122990) item.cpp:1091:AllowSpoil:{ent->GetSquareUnderEntity()}="0";
1181 2018/07/17-15:36:08(1122991) item.cpp:1091:AllowSpoil:{itM->GetID()}="780621";{itM->GetNameSingular().CStr()}="chest";
1182 * ./bin//ivan(_ZN6dbgmsg20getCurrentStackTraceEbRi+0xaa) [0x92f43e]
1183 * ./bin//ivan(_ZN6dbgmsg22getCurrentStackTraceSSB5cxx11Ebb+0x54) [0x92f53c]
1184 * ./bin//ivan(_ZN6dbgmsg8SigHndlrEi+0x1ae) [0x92fcdc]
1185 * /lib/x86_64-linux-gnu/libc.so.6(+0x354b0) [0x7f6b145e14b0]
1186 * ./bin//ivan(_ZNK4item10IsOnGroundEv+0x17) [0x8963c7]
1187 * ./bin//ivan(_ZNK5stack10IsOnGroundEv+0x38) [0x8f12a8]
1188 * ./bin//ivan(_ZNK9stackslot10IsOnGroundEv+0x20) [0x8ebb96]
1189 * ./bin//ivan(_ZNK4item10IsOnGroundEv+0x31) [0x8963e1]
1190 * ./bin//ivan(_ZNK4item10AllowSpoilEv+0x27f) [0x89b0ab]
1191 * ./bin//ivan(_ZN7organic2BeEm+0x4f) [0x839d0d]
1192 * ./bin//ivan(_ZN4item2BeEv+0x49) [0x89a819]
1193 * ./bin//ivan(_ZN4pool2BeEv+0x3b) [0x69593b]
1194 * ./bin//ivan(_ZN4game3RunEv+0x3da) [0x77ad5a]
1195 * ./bin//ivan(main+0x4d7) [0x70d462]
1196 * /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f6b145cc830]
1197 * ./bin//ivan(_start+0x29) [0x68f999]
1198 */
1199 if(dynamic_cast<stackslot*>(GetSlot())!=NULL){
1200 stackslot* ss = (stackslot*)GetSlot();
1201 item* itSS = ss->GetItem();
1202 DBG2(itSS,ss->GetSquareUnder());
1203 if(itSS!=NULL)DBG2(itSS->GetID(),itSS->GetNameSingular().CStr());
1204 stack* stkM=ss->GetMotherStack();
1205 if(stkM!=NULL){
1206 entity* ent = stkM->GetMotherEntity();
1207 DBG3(ss->GetMotherStack()->GetItems(), ent, ss->GetMotherStack()->GetSquareUnder());
1208 if(ent!=NULL){
1209 DBG1(ent->GetSquareUnderEntity());
1210 // if(dynamic_cast<id*>(ent))DBG1(((id*)ent)->GetID());
1211 if(dynamic_cast<item*>(ent)){
1212 item* itM = (item*)ent;
1213 DBG2(itM->GetID(),itM->GetNameSingular().CStr());
1214 }
1215 }
1216 }
1217 }
1218 );
1219 //TODO remove this. The workaround is NOT good as SquareUnder is essential everywhere!!!!
1220 // bool bIsOnGround=false;
1221 // lsquare* Square = GetLSquareUnder(); //TODO what could cause Square to be NULL ?????
1222 // static bool bAllowSpoilBugWorkaround=true;
1223 // if(bAllowSpoilBugWorkaround){ //See the above bug track code, the origin of the problem is still NOT solved/understood/discovered!!!!!!
1224 // if(Square!=NULL){
1225 // bIsOnGround=IsOnGround();
1226 // DBG1("WorkaroundForItemPlacedNoWhere");
1227 // }
1228 // /**
1229 // * what this means?
1230 // *
1231 // * not knowing where the item is, if on a room, the item will ALWAYS spoil,
1232 // * so if the buggy item "is" on a shop, it will spoil.
1233 // *
1234 // * and, on this stack/flow that game wont crash, but still may on other code flows!!!
1235 // */
1236 // }else{
1237 // bIsOnGround=IsOnGround();
1238 // }
1239
1240 if(IsOnGround())
1241 {
1242 lsquare* Square = GetLSquareUnder();
1243 int RoomNumber = Square->GetRoomIndex();
1244 return !RoomNumber || Square->GetLevel()->GetRoom(RoomNumber)->AllowSpoil(this);
1245 }
1246 else
1247 return true;
1248 }
1249
ResetSpoiling()1250 void item::ResetSpoiling()
1251 {
1252 for(int c = 0; c < GetMaterials(); ++c)
1253 if(GetMaterial(c))
1254 GetMaterial(c)->ResetSpoiling();
1255 }
1256
ResetBurning()1257 void item::ResetBurning()
1258 {
1259 for(int c = 0; c < GetMaterials(); ++c)
1260 if(GetMaterial(c))
1261 GetMaterial(c)->ResetBurning();
1262 SignalEmitationDecrease(MakeRGB24(150, 120, 90));
1263 }
1264
1265 /* be careful calling this function, it will also cause flames to extinguish */
ResetThermalEnergies()1266 void item::ResetThermalEnergies()
1267 {
1268 for(int c = 0; c < GetMaterials(); ++c)
1269 if(GetMaterial(c))
1270 GetMaterial(c)->ResetThermalEnergies();
1271 }
1272
GetBaseToHitValueDescription() const1273 cchar* item::GetBaseToHitValueDescription() const
1274 {
1275 if(GetBaseToHitValue() < 10)
1276 return ToHitValueDescription[Min(GetBaseToHitValue(), 6)];
1277 else
1278 return ToHitValueDescription[7];
1279 }
1280
GetBaseBlockValueDescription() const1281 cchar* item::GetBaseBlockValueDescription() const
1282 {
1283 if(GetBaseBlockValue() < 20)
1284 return ToHitValueDescription[Min(GetBaseBlockValue() >> 1, 6)];
1285 else
1286 return ToHitValueDescription[7];
1287 }
1288
GetStrengthValueDescription() const1289 cchar* item::GetStrengthValueDescription() const
1290 {
1291 int SV = GetStrengthValue();
1292
1293 if(SV < 3)
1294 return StrengthValueDescription[0];
1295 else if(SV < 5)
1296 return StrengthValueDescription[1];
1297 else if(SV < 8)
1298 return StrengthValueDescription[2];
1299 else if(SV < 11)
1300 return StrengthValueDescription[3];
1301 else if(SV < 16)
1302 return StrengthValueDescription[4];
1303 else if(SV < 20)
1304 return StrengthValueDescription[5];
1305 else
1306 return StrengthValueDescription[6];
1307 }
1308
SpecialGenerationHandler()1309 void item::SpecialGenerationHandler()
1310 {
1311 if(HandleInPairs())
1312 Slot[0]->AddFriendItem(Duplicate());
1313 }
1314
SortAllItems(const sortdata & SortData) const1315 void item::SortAllItems(const sortdata& SortData) const
1316 {
1317 if(SortData.Sorter == 0 || (this->*SortData.Sorter)(SortData.Character))
1318 SortData.AllItems.push_back(const_cast<item*>(this));
1319 }
1320
GetAttachedGod() const1321 int item::GetAttachedGod() const
1322 {
1323 return DataBase->AttachedGod ? DataBase->AttachedGod : MainMaterial->GetAttachedGod();
1324 }
1325
GetMaterialPrice() const1326 long item::GetMaterialPrice() const
1327 {
1328 return MainMaterial->GetRawPrice();
1329 }
1330
GetTruePrice() const1331 long item::GetTruePrice() const
1332 {
1333 if(LifeExpectancy)
1334 return 0;
1335
1336 long Price = Max(GetPrice(), GetMaterialPrice());
1337
1338 if(Spoils())
1339 Price = Price * (100 - GetMaxSpoilPercentage()) / 500;
1340
1341 return Price;
1342 }
1343
1344 #ifdef WIZARD
1345
AddAttackInfo(felist & List) const1346 void item::AddAttackInfo(felist& List) const
1347 {
1348 festring Entry(40, ' ');
1349 Entry << int(GetWeight());
1350 Entry.Resize(50);
1351 Entry << GetSize();
1352 Entry.Resize(60);
1353 Entry << GetStrengthRequirement();
1354 Entry.Resize(70);
1355 Entry << GetBaseMinDamage() << '-' << GetBaseMaxDamage();
1356 List.AddEntry(Entry, LIGHT_GRAY);
1357 }
1358
AddMiscellaneousInfo(felist & List) const1359 void item::AddMiscellaneousInfo(felist& List) const
1360 {
1361 festring Entry(40, ' ');
1362 Entry << int(GetTruePrice());
1363 Entry.Resize(55);
1364 Entry << GetOfferValue(0);
1365 Entry.Resize(70);
1366 Entry << int(GetNutritionValue());
1367 List.AddEntry(Entry, LIGHT_GRAY);
1368 }
1369
1370 #endif
1371
PreProcessForBone()1372 void item::PreProcessForBone()
1373 {
1374 if(IsQuestItem())
1375 {
1376 RemoveFromSlot();
1377 SendToHell();
1378 }
1379 else
1380 {
1381 game::RemoveItemID(ID);
1382 ID = -ID;
1383 game::AddItemID(this, ID);
1384 }
1385 }
1386
_BugWorkaround_ItemDup(ulong key)1387 void item::_BugWorkaround_ItemDup(ulong key){ID=key;} //keep it simple!
1388
PostProcessForBone()1389 void item::PostProcessForBone()
1390 {
1391 boneidmap::iterator BI = game::GetBoneItemIDMap().find(-ID);
1392 game::RemoveItemID(ID);
1393
1394 if(BI == game::GetBoneItemIDMap().end())
1395 {
1396 ulong NewID = game::CreateNewItemID(this);
1397 game::GetBoneItemIDMap().insert(std::make_pair(-ID, NewID));
1398 ID = NewID;
1399 }
1400 else
1401 {
1402 ID = BI->second;
1403 game::AddItemID(this, ID);
1404 }
1405
1406 for(idholder* I = CloneMotherID; I; I = I->Next)
1407 {
1408 BI = game::GetBoneItemIDMap().find(I->ID);
1409
1410 if(BI == game::GetBoneItemIDMap().end())
1411 {
1412 ulong NewCloneMotherID = game::CreateNewItemID(0);
1413 game::GetBoneItemIDMap().insert(std::make_pair(I->ID, NewCloneMotherID));
1414 I->ID = NewCloneMotherID;
1415 }
1416 else
1417 I->ID = BI->second;
1418 }
1419 }
1420
SetConfig(int NewConfig,int SpecialFlags)1421 void item::SetConfig(int NewConfig, int SpecialFlags)
1422 {
1423 databasecreator<item>::InstallDataBase(this, NewConfig);
1424 CalculateAll();
1425
1426 if(!(SpecialFlags & NO_PIC_UPDATE))
1427 UpdatePictures();
1428 }
1429
SetConfigIfPossible(int NewConfig,int SpecialFlags)1430 truth item::SetConfigIfPossible(int NewConfig, int SpecialFlags)
1431 {
1432 if(databasecreator<item>::InstallDataBaseIfPossible(this, NewConfig, GetConfig())){
1433 CalculateAll();
1434
1435 if(!(SpecialFlags & NO_PIC_UPDATE))
1436 UpdatePictures();
1437
1438 return true;
1439 }
1440
1441 return false;
1442 }
1443
GetMasterGod() const1444 god* item::GetMasterGod() const
1445 {
1446 return game::GetGod(GetConfig());
1447 }
1448
CreateSpecialConfigurations(itemdatabase ** TempConfig,int Configs,int Level)1449 int itemprototype::CreateSpecialConfigurations(itemdatabase** TempConfig, int Configs, int Level)
1450 {
1451 if(Level)
1452 return Configs;
1453
1454 if(TempConfig[0]->CreateDivineConfigurations)
1455 Configs = databasecreator<item>::CreateDivineConfigurations(this, TempConfig, Configs);
1456
1457 /* Gum solution */
1458
1459 if(TempConfig[0]->CreateLockConfigurations)
1460 {
1461 const item::database*const* KeyConfigData = key::ProtoType.GetConfigData();
1462 int KeyConfigSize = key::ProtoType.GetConfigSize();
1463 int OldConfigs = Configs;
1464
1465 for(int c1 = 0; c1 < OldConfigs; ++c1)
1466 if(!TempConfig[c1]->IsAbstract)
1467 {
1468 int BaseConfig = TempConfig[c1]->Config;
1469 int NewConfig = BaseConfig | BROKEN_LOCK;
1470 itemdatabase* ConfigDataBase = new itemdatabase(*TempConfig[c1]);
1471 ConfigDataBase->InitDefaults(this, NewConfig);
1472 ConfigDataBase->PostFix << "with a broken lock";
1473 ConfigDataBase->Possibility = 0;
1474 TempConfig[Configs++] = ConfigDataBase;
1475
1476 for(int c2 = 0; c2 < KeyConfigSize; ++c2)
1477 {
1478 NewConfig = BaseConfig | KeyConfigData[c2]->Config;
1479 ConfigDataBase = new itemdatabase(*TempConfig[c1]);
1480 ConfigDataBase->InitDefaults(this, NewConfig);
1481 ConfigDataBase->PostFix << "with ";
1482
1483 if(KeyConfigData[c2]->UsesLongAdjectiveArticle)
1484 ConfigDataBase->PostFix << "an ";
1485 else
1486 ConfigDataBase->PostFix << "a ";
1487
1488 ConfigDataBase->PostFix << KeyConfigData[c2]->Adjective << " lock";
1489 ConfigDataBase->Possibility = 0;
1490 TempConfig[Configs++] = ConfigDataBase;
1491 }
1492 }
1493 }
1494
1495 return Configs;
1496 }
1497
Draw(blitdata & BlitData) const1498 void item::Draw(blitdata& BlitData) const
1499 {
1500 cint AF = GraphicData.AnimationFrames;
1501 cint F = !(BlitData.CustomData & ALLOW_ANIMATE) || AF == 1 ? 0 : GET_TICK() & (AF - 1);
1502
1503 bitmap* bmp = GraphicData.Picture[F];
1504 if(ivanconfig::GetRotateTimesPerSquare()>0 && iRotateFlyingThrownStep!=0){ // tests made using a single bladed (unbalanced) thrown axe where 0 degrees was: blade at topRight poiting downwards
1505 static blitdata B = [](){B=DEFAULT_BLITDATA; //to reuse tmp bitmap memory
1506 B.Bitmap = new bitmap(TILE_V2); //bmp->GetSize());
1507 B.Border = TILE_V2; return B; }();
1508
1509 bitmap::ConfigureBlitdataRotation(B,iRotateFlyingThrownStep);
1510
1511 bmp->NormalBlit(B); //or NormalMaskedBlit() only way to rotate...
1512 bmp = B.Bitmap;
1513 }
1514 cbitmap* P = bmp;
1515
1516 if(BlitData.CustomData & ALLOW_ALPHA)
1517 P->AlphaLuminanceBlit(BlitData);
1518 else
1519 P->LuminanceMaskedBlit(BlitData);
1520
1521 if(Fluid && ShowFluids())
1522 DrawFluids(BlitData);
1523 }
1524
GetLargeBitmapPos(v2 BasePos,int I) const1525 v2 item::GetLargeBitmapPos(v2 BasePos, int I) const
1526 {
1527 cint SquareIndex = I ? I / (GraphicData.AnimationFrames >> 2) : 0;
1528 return v2(SquareIndex & 1 ? BasePos.X + 16 : BasePos.X, SquareIndex & 2 ? BasePos.Y + 16 : BasePos.Y);
1529 }
1530
LargeDraw(blitdata & BlitData) const1531 void item::LargeDraw(blitdata& BlitData) const
1532 {
1533 cint TrueAF = GraphicData.AnimationFrames >> 2;
1534 cint SquareIndex = BlitData.CustomData & SQUARE_INDEX_MASK;
1535 cint F = !(BlitData.CustomData & ALLOW_ANIMATE) ? SquareIndex * TrueAF : SquareIndex * TrueAF + (GET_TICK() & (TrueAF - 1));
1536 cbitmap* P = GraphicData.Picture[F];
1537
1538 if(BlitData.CustomData & ALLOW_ALPHA)
1539 P->AlphaLuminanceBlit(BlitData);
1540 else
1541 P->LuminanceMaskedBlit(BlitData);
1542 }
1543
DonateIDTo(item * Item)1544 void item::DonateIDTo(item* Item)
1545 {
1546 game::RemoveItemID(Item->ID);
1547 game::UpdateItemID(Item, ID);
1548 Item->ID = ID;
1549 ID = 0;
1550 }
1551
SignalRustLevelChange()1552 void item::SignalRustLevelChange()
1553 {
1554 SignalVolumeAndWeightChange();
1555 UpdatePictures();
1556 SendNewDrawAndMemorizedUpdateRequest();
1557 }
1558
SignalBurnLevelTransitionMessage()1559 void item::SignalBurnLevelTransitionMessage()
1560 {
1561 if(CanBeSeenByPlayer())
1562 {
1563 if(MainMaterial->GetBurnLevel() == NOT_BURNT)
1564 ADD_MESSAGE("%s burns.", GetExtendedDescription().CStr());
1565 else
1566 ADD_MESSAGE("%s burns more.", GetExtendedDescription().CStr());
1567 }
1568 }
1569
SignalBurnLevelChange()1570 void item::SignalBurnLevelChange()
1571 {
1572 if(!IsAnimated() && GetBurnLevel() && Slot[0] && Slot[0]->IsVisible())
1573 for(int c = 0; c < SquaresUnder; ++c)
1574 GetSquareUnder(c)->IncStaticAnimatedEntities();
1575
1576 SignalEmitationDecrease(MakeRGB24(150, 120, 90)); // completely remove previously applied emitation increases
1577 SignalEmitationIncrease(GetEmitationDueToBurnLevel()); // apply an emitation increase according to the current burn level
1578
1579 SignalVolumeAndWeightChange();
1580 UpdatePictures();
1581 SendNewDrawAndMemorizedUpdateRequest();
1582 }
1583
1584 /* emitation slowly ramps down with increasing item BurnLevel, in the beginning the
1585 light is bright, with light decreasing in intensity as the item gets more burnt */
GetEmitationDueToBurnLevel()1586 col24 item::GetEmitationDueToBurnLevel()
1587 {
1588 if(MainMaterial)
1589 {
1590 int CurrentBurnLevel = GetBurnLevel();
1591
1592 int Red = 150 - 10 * CurrentBurnLevel;
1593 int Green = 120 - 8 * CurrentBurnLevel;
1594 int Blue = 90 - 6 * CurrentBurnLevel;
1595 //ADD_MESSAGE("Emitation due to BurnLevel: R%d G%d B%d", Red, Green, Blue); // por debug
1596 return MakeRGB24(Red, Green, Blue);
1597 }
1598 else
1599 return MakeRGB24(0, 0, 0);
1600 }
1601
GetRawPicture() const1602 const rawbitmap* item::GetRawPicture() const
1603 {
1604 return igraph::GetRawGraphic(GetGraphicsContainerIndex());
1605 }
1606
RemoveFluid(fluid * ToBeRemoved)1607 void item::RemoveFluid(fluid* ToBeRemoved)
1608 {
1609 truth WasAnimated = IsAnimated();
1610 truth HasFluids = false;
1611
1612 for(int c = 0; c < SquaresUnder; ++c)
1613 {
1614 fluid* F = Fluid[c];
1615
1616 if(F == ToBeRemoved)
1617 Fluid[c] = F->Next;
1618 else if(F)
1619 {
1620 fluid* LF = F;
1621
1622 for(F = F->Next; F; LF = F, F = F->Next)
1623 if(F == ToBeRemoved)
1624 {
1625 LF->Next = F->Next;
1626 break;
1627 }
1628 }
1629
1630 if(Fluid[c])
1631 HasFluids = true;
1632 }
1633
1634 UpdatePictures();
1635
1636 if(!HasFluids)
1637 {
1638 delete [] Fluid;
1639 Fluid = 0;
1640
1641 if(!IsAnimated() != !WasAnimated && Slot[0]->IsVisible())
1642 GetSquareUnder()->DecStaticAnimatedEntities();
1643 }
1644
1645 SignalEmitationDecrease(ToBeRemoved->GetEmitation());
1646 SignalVolumeAndWeightChange();
1647 }
1648
AddFluid(liquid * ToBeAdded,festring LocationName,int SquareIndex,truth IsInside)1649 void item::AddFluid(liquid* ToBeAdded, festring LocationName, int SquareIndex, truth IsInside)
1650 {
1651 truth WasAnimated = IsAnimated();
1652
1653 if(Fluid)
1654 {
1655 fluid* F = Fluid[SquareIndex];
1656
1657 if(!F)
1658 Fluid[SquareIndex] = new fluid(ToBeAdded, this, LocationName, IsInside);
1659 else
1660 {
1661 fluid* LF;
1662
1663 do
1664 {
1665 if(ToBeAdded->IsSameAs(F->GetLiquid()))
1666 {
1667 F->AddLiquidAndVolume(ToBeAdded->GetVolume());
1668 delete ToBeAdded;
1669 return;
1670 }
1671
1672 LF = F;
1673 F = F->Next;
1674 }
1675 while(F);
1676
1677 LF->Next = new fluid(ToBeAdded, this, LocationName, IsInside);
1678 }
1679 }
1680 else
1681 {
1682 Fluid = new fluid*[SquaresUnder];
1683 memset(Fluid, 0, SquaresUnder * sizeof(fluid*));
1684 Fluid[SquareIndex] = new fluid(ToBeAdded, this, LocationName, IsInside);
1685 }
1686
1687 UpdatePictures();
1688 SignalVolumeAndWeightChange();
1689 SignalEmitationIncrease(ToBeAdded->GetEmitation());
1690
1691 if(Slot[0])
1692 {
1693 if(!IsAnimated() != !WasAnimated && Slot[0]->IsVisible())
1694 GetSquareUnder()->IncStaticAnimatedEntities();
1695
1696 SendNewDrawAndMemorizedUpdateRequest();
1697 }
1698 }
1699
SendNewDrawAndMemorizedUpdateRequest() const1700 void item::SendNewDrawAndMemorizedUpdateRequest() const
1701 {
1702 if(!game::IsInWilderness())
1703 for(int c = 0; c < SquaresUnder; ++c)
1704 if(Slot[c])
1705 {
1706 lsquare* Square = GetLSquareUnder(c);
1707 Square->SendNewDrawRequest();
1708 Square->SendMemorizedUpdateRequest();
1709 }
1710 }
1711
CalculateEmitation()1712 void item::CalculateEmitation()
1713 {
1714 object::CalculateEmitation();
1715
1716 if(Fluid){
1717 for(int c = 0; c < SquaresUnder; ++c){
1718 for(const fluid* F = Fluid[c]; F; F = F->Next){
1719 DBG2(F->GetEmitation(),F->GetLiquid()->GetVolume());
1720 game::CombineLights(Emitation,
1721 CalcEmitationBasedOnVolume( 0, F->GetEmitation(), F->GetLiquid()->GetVolume() ) );
1722 }
1723 }
1724 }
1725 }
1726
FillFluidVector(fluidvector & Vector,int SquareIndex) const1727 void item::FillFluidVector(fluidvector& Vector, int SquareIndex) const
1728 {
1729 if(Fluid)
1730 for(fluid* F = Fluid[SquareIndex]; F; F = F->Next)
1731 Vector.push_back(F);
1732 }
1733
SpillFluid(character *,liquid * Liquid,int SquareIndex)1734 void item::SpillFluid(character*, liquid* Liquid, int SquareIndex)
1735 {
1736 if(AllowFluids() && Liquid->GetVolume())
1737 AddFluid(Liquid, "", SquareIndex, false);
1738 else
1739 delete Liquid;
1740 }
1741
TryToRust(long LiquidModifier)1742 void item::TryToRust(long LiquidModifier)
1743 {
1744 if(MainMaterial->TryToRust(LiquidModifier))
1745 {
1746 if(CanBeSeenByPlayer())
1747 {
1748 if(MainMaterial->GetRustLevel() == NOT_RUSTED)
1749 ADD_MESSAGE("%s rusts.", CHAR_NAME(DEFINITE));
1750 else
1751 ADD_MESSAGE("%s rusts more.", CHAR_NAME(DEFINITE));
1752 }
1753
1754 MainMaterial->SetRustLevel(MainMaterial->GetRustLevel() + 1);
1755 }
1756 }
1757
TestActivationEnergy(int Damage)1758 truth item::TestActivationEnergy(int Damage)
1759 {
1760 if(Damage <= 0)
1761 return false;
1762
1763 truth Success = false;
1764 // if(MainMaterial)
1765 // {
1766 // int molamola = ((GetMainMaterial()->GetStrengthValue() >> 1) + 5 * MainMaterial->GetFireResistance() + GetResistance(FIRE));
1767 // ADD_MESSAGE("%s is being tested (Damage is %d, AE is %d)", CHAR_NAME(DEFINITE), Damage, molamola);
1768 // }
1769
1770 if(MainMaterial)
1771 {
1772 int TestDamage = Damage + MainMaterial->GetTransientThermalEnergy();
1773 GetMainMaterial()->AddToTransientThermalEnergy(Damage);
1774 if(CanBeBurned() && GetMainMaterial()->GetInteractionFlags() & CAN_BURN
1775 && TestDamage >= ((GetMainMaterial()->GetStrengthValue() >> 1)
1776 + 5 * MainMaterial->GetFireResistance() + GetResistance(FIRE)))
1777 {
1778 if(CanBeSeenByPlayer())
1779 {
1780 ADD_MESSAGE("%s catches fire!", CHAR_NAME(DEFINITE));
1781 //ADD_MESSAGE("%s catches fire! (TestDamage was %d)", CHAR_NAME(DEFINITE), TestDamage);
1782 }
1783 Ignite();
1784 GetMainMaterial()->AddToSteadyStateThermalEnergy(Damage);
1785 Success = true;
1786 }
1787 }
1788 return Success;
1789 }
1790
Ignite()1791 void item::Ignite(/*character* Arsonist*/)
1792 {
1793 truth WasAnimated = IsAnimated();
1794
1795 MainMaterial->SetIsBurning(true);
1796 SignalEmitationIncrease(GetEmitationDueToBurnLevel()); // kick this off by applying an emitation increase proportional to the burn level of the item
1797 UpdatePictures();
1798 //ADD_MESSAGE("The %s now burns brightly.", CHAR_NAME(DEFINITE));
1799
1800 if(Slot[0])
1801 {
1802 if(!IsAnimated() != !WasAnimated && Slot[0]->IsVisible())
1803 GetSquareUnder()->IncStaticAnimatedEntities();
1804
1805 SendNewDrawAndMemorizedUpdateRequest();
1806 }
1807 }
1808
1809 /* This causes the main material to stop burning, resets the thermal energies
1810 and does a picture update on the level, as well as wielded pictures */
Extinguish(truth SendMessages)1811 void item::Extinguish(/*character* FireFighter, */truth SendMessages)
1812 {
1813 truth WasAnimated = IsAnimated();
1814 truth WasSeen = CanBeSeenByPlayer();
1815 truth WasBurning = IsBurning();
1816
1817 MainMaterial->SetIsBurning(false);
1818 MainMaterial->ResetThermalEnergies();
1819 SignalEmitationDecrease(MakeRGB24(150, 120, 90));
1820
1821 if(Slot[0])
1822 {
1823 if(!IsAnimated() != !WasAnimated && Slot[0]->IsVisible())
1824 GetSquareUnder()->DecStaticAnimatedEntities();
1825 }
1826
1827 if(WasBurning && WasSeen && SendMessages) // by now it is dark...
1828 AddExtinguishMessage();
1829
1830 SignalVolumeAndWeightChange();
1831 UpdatePictures();
1832 SendNewDrawAndMemorizedUpdateRequest();
1833 }
1834
AddExtinguishMessage()1835 void item::AddExtinguishMessage()
1836 {
1837 ADD_MESSAGE("The flames on %s die away.", GetExtendedDescription().CStr());
1838 }
1839
1840 // This is for anything made from phoenix feather
AddSpecialExtinguishMessageForPF()1841 void item::AddSpecialExtinguishMessageForPF()
1842 {
1843 if(CanBeSeenByPlayer())
1844 ADD_MESSAGE("%s burns even more! But lo', even as it does so, the ashes "
1845 "peel away from it and it is made new by some innate virtue.", GetExtendedDescription().CStr());
1846 }
1847
CheckFluidGearPictures(v2 ShadowPos,int SpecialFlags,truth BodyArmor)1848 void item::CheckFluidGearPictures(v2 ShadowPos, int SpecialFlags, truth BodyArmor)
1849 {
1850 if(Fluid)
1851 for(fluid* F = Fluid[0]; F; F = F->Next)
1852 F->CheckGearPicture(ShadowPos, SpecialFlags, BodyArmor);
1853 }
1854
DrawFluidGearPictures(blitdata & BlitData,int SpecialFlags) const1855 void item::DrawFluidGearPictures(blitdata& BlitData, int SpecialFlags) const
1856 {
1857 if(Fluid)
1858 for(const fluid* F = Fluid[0]; F; F = F->Next)
1859 F->DrawGearPicture(BlitData, SpecialFlags);
1860 }
1861
DrawFluidBodyArmorPictures(blitdata & BlitData,int SpecialFlags) const1862 void item::DrawFluidBodyArmorPictures(blitdata& BlitData, int SpecialFlags) const
1863 {
1864 if(Fluid)
1865 for(const fluid* F = Fluid[0]; F; F = F->Next)
1866 F->DrawBodyArmorPicture(BlitData, SpecialFlags);
1867 }
1868
DrawFluids(blitdata & BlitData) const1869 void item::DrawFluids(blitdata& BlitData) const
1870 {
1871 cint SquareIndex = BlitData.CustomData & SQUARE_INDEX_MASK;
1872
1873 for(const fluid* F = Fluid[SquareIndex]; F; F = F->Next)
1874 F->Draw(BlitData);
1875 }
1876
ReceiveAcid(material *,cfestring &,long Modifier)1877 void item::ReceiveAcid(material*, cfestring&, long Modifier)
1878 {
1879 if(GetMainMaterial()->GetInteractionFlags() & CAN_DISSOLVE)
1880 {
1881 int Damage = Modifier / 1000;
1882
1883 if(Damage)
1884 {
1885 Damage += RAND() % Damage;
1886 ReceiveDamage(0, Damage, ACID);
1887 }
1888 }
1889 }
1890
ReceiveHeat(material *,cfestring &,long Modifier)1891 void item::ReceiveHeat(material*, cfestring&, long Modifier)
1892 {
1893 if(GetMainMaterial()->GetInteractionFlags() & CAN_BURN)
1894 {
1895 int Damage = Modifier / 1000;
1896
1897 if(Damage)
1898 {
1899 Damage += RAND() % Damage;
1900 ReceiveDamage(0, Damage, FIRE);
1901 }
1902 }
1903 }
1904
FightFire(material *,cfestring &,long Volume)1905 void item::FightFire(material*, cfestring&, long Volume)
1906 {
1907 int Amount = sqrt(Volume);
1908 GetMainMaterial()->RemoveFromThermalEnergy(Amount);
1909 }
1910
DonateFluidsTo(item * Item)1911 void item::DonateFluidsTo(item* Item)
1912 {
1913 if(Fluid)
1914 for(int c = 0; c < GetSquaresUnder(); ++c)
1915 for(fluid* F = Fluid[c]; F; F = F->Next)
1916 {
1917 liquid* Liquid = F->GetLiquid();
1918 Item->AddFluid(Liquid->SpawnMoreLiquid(Liquid->GetVolume()), F->GetLocationName(), c, F->IsInside());
1919 }
1920 }
1921
Destroy(character * Destroyer,int)1922 void item::Destroy(character* Destroyer, int)
1923 {
1924 if(CanBeSeenByPlayer())
1925 ADD_MESSAGE("%s is destroyed.", GetExtendedDescription().CStr());
1926
1927 if(Destroyer && IsOnGround())
1928 {
1929 room* Room = GetRoom();
1930
1931 if(Room)
1932 Room->HostileAction(Destroyer);
1933 }
1934
1935 truth Equipped = PLAYER->Equips(this);
1936 RemoveFromSlot();
1937 SendToHell();
1938
1939 if(Equipped)
1940 game::AskForKeyPress(CONST_S("Equipment destroyed! [press any key to continue]"));
1941 }
1942
RemoveRust()1943 void item::RemoveRust()
1944 {
1945 for(int c = 0; c < GetMaterials(); ++c)
1946 if(GetMaterial(c))
1947 GetMaterial(c)->SetRustLevel(NOT_RUSTED);
1948 }
1949
RemoveBurns()1950 void item::RemoveBurns()
1951 {
1952 for(int c = 0; c < GetMaterials(); ++c)
1953 if(GetMaterial(c))
1954 GetMaterial(c)->SetBurnLevel(NOT_BURNT, true);
1955 }
1956
SetSpoilPercentage(int Value)1957 void item::SetSpoilPercentage(int Value)
1958 {
1959 for(int c = 0; c < GetMaterials(); ++c)
1960 {
1961 material* Material = GetMaterial(c);
1962
1963 if(Material && Material->CanSpoil())
1964 Material->SetSpoilCounter(Material->GetSpoilModifier() * Value / 100);
1965 }
1966 }
1967
RedistributeFluids()1968 void item::RedistributeFluids()
1969 {
1970 if(Fluid)
1971 for(int c = 0; c < GetSquaresUnder(); ++c)
1972 for(fluid* F = Fluid[c]; F; F = F->Next)
1973 F->Redistribute();
1974 }
1975
GetConsumeMaterial(ccharacter * Consumer,materialpredicate Predicate) const1976 material* item::GetConsumeMaterial(ccharacter* Consumer, materialpredicate Predicate) const
1977 {
1978 return (MainMaterial->*Predicate)() && Consumer->CanConsume(MainMaterial) ? MainMaterial : 0;
1979 }
1980
1981 /* The parameter can only be MainMaterial */
1982
RemoveMaterial(material *)1983 material* item::RemoveMaterial(material*)
1984 {
1985 RemoveFromSlot();
1986 SendToHell();
1987 return 0;
1988 }
1989
InitMaterials(material * FirstMaterial,truth CallUpdatePictures)1990 void item::InitMaterials(material* FirstMaterial, truth CallUpdatePictures)
1991 {
1992 InitMaterial(MainMaterial, FirstMaterial, GetDefaultMainVolume());
1993 SignalVolumeAndWeightChange();
1994
1995 if(CallUpdatePictures)
1996 UpdatePictures();
1997 }
1998
GenerateMaterials()1999 void item::GenerateMaterials()
2000 {
2001 int Chosen = RandomizeMaterialConfiguration();
2002 const fearray<long>& MMC = GetMainMaterialConfig();
2003 InitMaterial(MainMaterial,
2004 MAKE_MATERIAL(MMC.Data[MMC.Size == 1 ? 0 : Chosen]),
2005 GetDefaultMainVolume());
2006 }
2007
SignalSquarePositionChange(int Position)2008 void item::SignalSquarePositionChange(int Position)
2009 {
2010 Flags &= ~SQUARE_POSITION_BITS;
2011 Flags |= Position << SQUARE_POSITION_SHIFT;
2012 }
2013
Read(character * Reader)2014 truth item::Read(character* Reader)
2015 {
2016 Reader->StartReading(this, GetReadDifficulty());
2017 return true;
2018 }
2019
CanBeHardened(ccharacter *) const2020 truth item::CanBeHardened(ccharacter*) const
2021 {
2022 return MainMaterial->GetHardenedMaterial(this) != NONE;
2023 }
2024
CanBeSoftened() const2025 truth item::CanBeSoftened() const
2026 {
2027 return MainMaterial->GetSoftenedMaterial(this) != NONE;
2028 }
2029
SetLifeExpectancy(int Base,int RandPlus)2030 void item::SetLifeExpectancy(int Base, int RandPlus)
2031 {
2032 LifeExpectancy = RandPlus > 1 ? Base + RAND_N(RandPlus) : Base;
2033 Enable();
2034 }
2035
IsVeryCloseToSpoiling() const2036 truth item::IsVeryCloseToSpoiling() const
2037 {
2038 for(int c = 0; c < GetMaterials(); ++c)
2039 if(GetMaterial(c) && !GetMaterial(c)->IsVeryCloseToSpoiling())
2040 return false;
2041
2042 return true;
2043 }
2044
IsVeryCloseToBurning() const2045 truth item::IsVeryCloseToBurning() const
2046 {
2047 for(int c = 0; c < GetMaterials(); ++c)
2048 if(GetMaterial(c) && !GetMaterial(c)->IsVeryCloseToBurning())
2049 return false;
2050
2051 return true;
2052 }
2053
IsValuable() const2054 truth item::IsValuable() const
2055 {
2056 if(DataBase->IsValuable)
2057 return true;
2058
2059 for(int c = 0; c < GetMaterials(); ++c)
2060 {
2061 material* M = GetMaterial(c);
2062
2063 if(M && M->GetCommonFlags() & IS_VALUABLE)
2064 return true;
2065 }
2066
2067 return false;
2068 }
2069
GetHinderVisibilityBonus(ccharacter * Char) const2070 int item::GetHinderVisibilityBonus(ccharacter* Char) const
2071 {
2072 int Bonus = 0;
2073
2074 if(GetGearStates() & INFRA_VISION
2075 && !Char->TemporaryStateIsActivated(INFRA_VISION))
2076 Bonus += 20000;
2077
2078 if(GetGearStates() & ESP
2079 && !Char->TemporaryStateIsActivated(ESP))
2080 Bonus += 20000;
2081
2082 if(!game::IsDark(GetEmitation()))
2083 Bonus += 5000;
2084
2085 return Bonus;
2086 }
2087
GetFixPrice() const2088 long item::GetFixPrice() const
2089 {
2090 item* Clone = GetProtoType()->Clone(this);
2091 Clone = Clone->Fix();
2092 Clone->RemoveRust();
2093 Clone->RemoveBurns();
2094 long FixPrice = Clone->GetTruePrice();
2095 Clone->SendToHell();
2096 return Max(long(3.5 * sqrt(FixPrice)), 10L);
2097 }
2098
AddTrapName(festring & String,int Amount) const2099 void item::AddTrapName(festring& String, int Amount) const
2100 {
2101 if(Amount == 1)
2102 AddName(String, DEFINITE);
2103 else
2104 {
2105 String << Amount << ' ';
2106 AddName(String, PLURAL);
2107 }
2108 }
2109
Spoils() const2110 truth item::Spoils() const
2111 {
2112 for(int c = 0; c < GetMaterials(); ++c)
2113 {
2114 cmaterial* Material = GetMaterial(c);
2115
2116 if(Material && Material->Spoils())
2117 return true;
2118 }
2119
2120 return false;
2121 }
2122
GetMaxSpoilPercentage() const2123 int item::GetMaxSpoilPercentage() const
2124 {
2125 int MaxPercentage = 0;
2126
2127 for(int c = 0; c < GetMaterials(); ++c)
2128 {
2129 cmaterial* Material = GetMaterial(c);
2130
2131 if(Material)
2132 MaxPercentage = Max(MaxPercentage, Material->GetSpoilPercentage());
2133 }
2134
2135 return MaxPercentage;
2136 }
2137
HasPrice() const2138 truth item::HasPrice() const
2139 {
2140 return GetPrice() || GetMaterialPrice();
2141 }
2142
Disappear()2143 void item::Disappear()
2144 {
2145 RemoveFromSlot();
2146 SendToHell();
2147 }
2148
operator <<(outputfile & SaveFile,const idholder * IdHolder)2149 outputfile& operator<<(outputfile& SaveFile, const idholder* IdHolder)
2150 {
2151 SaveFile << IdHolder->ID;
2152 return SaveFile;
2153 }
2154
operator >>(inputfile & SaveFile,idholder * & IdHolder)2155 inputfile& operator>>(inputfile& SaveFile, idholder*& IdHolder)
2156 {
2157 IdHolder = new idholder(ReadType<ulong>(SaveFile));
2158 return SaveFile;
2159 }
2160
GetExtendedDescription() const2161 festring item::GetExtendedDescription() const
2162 {
2163 if(!CanBeSeenByPlayer())
2164 return CONST_S("something");
2165
2166 festring Desc;
2167 ccharacter* Carrier = FindCarrier();
2168
2169 if(Carrier)
2170 {
2171 if(Carrier->IsPlayer())
2172 {
2173 Desc << "your ";
2174 AddName(Desc, UNARTICLED);
2175 return Desc;
2176 }
2177 else if(Carrier->CanBeSeenByPlayer())
2178 {
2179 Carrier->AddName(Desc, DEFINITE);
2180 Desc << "'s ";
2181 AddName(Desc, UNARTICLED);
2182 return Desc;
2183 }
2184 }
2185
2186 AddName(Desc, DEFINITE);
2187
2188 if(IsOnGround())
2189 GetLSquareUnder()->AddLocationDescription(Desc);
2190
2191 return Desc;
2192 }
2193
FindCarrier() const2194 ccharacter* item::FindCarrier() const
2195 {
2196 return Slot[0]->FindCarrier();
2197 }
2198
2199 /* returns 0 if not worn or wielded else the wearer */
2200
GetWearer() const2201 const character* item::GetWearer() const
2202 {
2203 if(!GetSlot()->IsGearSlot())
2204 return 0;
2205
2206 return FindCarrier();
2207 }
2208
PostConstruct()2209 void itemlock::PostConstruct()
2210 {
2211 /* Terrible gum solution! */
2212
2213 if(!(GetVirtualConfig() & LOCK_BITS))
2214 {
2215 int NormalLockTypes = 0;
2216 const itemdatabase*const* ConfigData = GetVirtualProtoType()->GetConfigData();
2217 int c, ConfigSize = GetVirtualProtoType()->GetConfigSize();
2218
2219 for(c = 0; c < ConfigSize; ++c)
2220 if(ConfigData[c]->Config & LOCK_BITS
2221 && (ConfigData[c]->Config & ~LOCK_BITS) == GetVirtualConfig()
2222 && !(ConfigData[c]->Config & S_LOCK_ID))
2223 ++NormalLockTypes;
2224
2225 int ChosenLock = RAND() % NormalLockTypes;
2226
2227 for(c = 0; c < ConfigSize; ++c)
2228 if(ConfigData[c]->Config & LOCK_BITS
2229 && (ConfigData[c]->Config & ~LOCK_BITS) == GetVirtualConfig()
2230 && !(ConfigData[c]->Config & S_LOCK_ID)
2231 && !ChosenLock--)
2232 {
2233 SetVirtualConfig(ConfigData[c]->Config, NO_PIC_UPDATE);
2234 break;
2235 }
2236 }
2237 }
2238
TryKey(item * Key,character * Applier)2239 truth itemlock::TryKey(item* Key, character* Applier)
2240 {
2241 if(GetVirtualConfig() & BROKEN_LOCK)
2242 {
2243 ADD_MESSAGE("The lock is broken.");
2244 return true;
2245 }
2246
2247 if(Key->CanOpenLockType(GetVirtualConfig()&LOCK_BITS))
2248 {
2249 if(Locked)
2250 {
2251 if(Applier->IsPlayer())
2252 ADD_MESSAGE("You unlock %s.", GetVirtualDescription(DEFINITE).CStr());
2253 else if(Applier->CanBeSeenByPlayer())
2254 ADD_MESSAGE("%s unlocks %s.", Applier->CHAR_NAME(DEFINITE), GetVirtualDescription(DEFINITE).CStr());
2255 }
2256 else
2257 {
2258 if(Applier->IsPlayer())
2259 ADD_MESSAGE("You lock %s.", GetVirtualDescription(DEFINITE).CStr());
2260 else if(Applier->CanBeSeenByPlayer())
2261 ADD_MESSAGE("%s locks %s.", Applier->CHAR_NAME(DEFINITE), GetVirtualDescription(DEFINITE).CStr());
2262 }
2263
2264 // Add a tiny chance that any key you use breaks, so that there is some value in having
2265 // multiples of a key, and in keys of better materials.
2266 if(!RAND_N(Key->GetMainMaterial()->GetStrengthValue()))
2267 {
2268 Key->Break(Applier);
2269 }
2270
2271 Locked = !Locked;
2272 }
2273 else
2274 {
2275 if(Applier->IsPlayer())
2276 ADD_MESSAGE("%s doesn't fit in the lock.", Key->CHAR_NAME(DEFINITE));
2277 else if(Applier->CanBeSeenByPlayer())
2278 ADD_MESSAGE("%s tries to fit %s in the lock, but fails.",
2279 Applier->CHAR_NAME(DEFINITE), Key->CHAR_NAME(DEFINITE));
2280 }
2281
2282 return true;
2283 }
2284
Save(outputfile & SaveFile) const2285 void itemlock::Save(outputfile& SaveFile) const
2286 {
2287 SaveFile << Locked;
2288 }
2289
Load(inputfile & SaveFile)2290 void itemlock::Load(inputfile& SaveFile)
2291 {
2292 SaveFile >> Locked;
2293 }
2294
IsBeverage(ccharacter *) const2295 truth item::IsBeverage(ccharacter*) const
2296 {
2297 for(int c = 0; c < GetMaterials(); ++c)
2298 {
2299 cmaterial* Material = GetMaterial(c);
2300
2301 if(Material && (Material->GetCategoryFlags() & IS_BEVERAGE))
2302 return true;
2303 }
2304
2305 return false;
2306 }
2307
Haste()2308 void item::Haste()
2309 {
2310 ItemFlags |= HASTE;
2311 ItemFlags &= ~SLOW;
2312 SendMemorizedUpdateRequest();
2313 }
2314
Slow()2315 void item::Slow()
2316 {
2317 ItemFlags |= SLOW;
2318 ItemFlags &= ~HASTE;
2319 SendMemorizedUpdateRequest();
2320 }
2321
SendMemorizedUpdateRequest() const2322 void item::SendMemorizedUpdateRequest() const
2323 {
2324 if(!game::IsInWilderness())
2325 for(int c = 0; c < SquaresUnder; ++c)
2326 if(Slot[c])
2327 {
2328 lsquare* Square = GetLSquareUnder(c);
2329 Square->SendMemorizedUpdateRequest();
2330 }
2331 }
2332
AddContainerPostFix(festring & String) const2333 void item::AddContainerPostFix(festring& String) const
2334 {
2335 if(GetSecondaryMaterial()){
2336 float fRatio = GetSecondaryMaterial()->GetVolume()/(float)GetDefaultSecondaryVolume();
2337 const char* c="full of";
2338 if (fRatio<=0.10)c="with just a little bit of";
2339 else if(fRatio<=0.25)c="with a bit of";
2340 else if(fRatio<=0.45)c="with some";
2341 else if(fRatio<=0.55)c="half full of";
2342 else if(fRatio<=0.75)c="well filled with"; //TODO any better phrasing for this?
2343 else if(fRatio<=0.93)c="almost full of"; //nice arguable arbitrary percent :)
2344 GetSecondaryMaterial()->AddName(String << " "<< c << " ", false, false);
2345 }
2346 }
2347
AddStateDescription(festring & Name,truth Articled) const2348 truth item::AddStateDescription(festring& Name, truth Articled) const
2349 {
2350 if(!Spoils() || !(ItemFlags & (HASTE|SLOW)))
2351 return false;
2352
2353 if(Articled)
2354 Name << "a ";
2355
2356 if(ItemFlags & HASTE)
2357 Name << "hasted ";
2358
2359 if(ItemFlags & SLOW)
2360 Name << "slowed ";
2361
2362 return true;
2363 }
2364