moved almost all hardcoded constants to "define.dat"
[k8-i-v-a-n.git] / src / game / item.cpp
blobc5bb93d279c2c6718de84a78286a0ecb1ea2cebf
1 /*
3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
6 * Public License
8 * See LICENSING which should be included
9 * along with this file for more details
12 /* Compiled through itemset.cpp */
14 cchar *ToHitValueDescription[] = {
15 "unbelievably inaccurate",
16 "extremely inaccurate",
17 "inaccurate",
18 "decently accurate",
19 "accurate",
20 "highly accurate",
21 "extremely accurate",
22 "unbelievably accurate"
25 cchar *StrengthValueDescription[] = {
26 "fragile",
27 "rather sturdy",
28 "sturdy",
29 "strong",
30 "very strong",
31 "extremely strong",
32 "almost unbreakable"
36 itemprototype::itemprototype (const itemprototype *Base, itemspawner Spawner, itemcloner Cloner, cchar *ClassID) :
37 Base(Base),
38 Spawner(Spawner),
39 Cloner(Cloner),
40 ClassID(ClassID)
42 Index = protocontainer<item>::Add(this);
46 truth itemdatabase::AllowRandomInstantiation () const { return !(Config & S_LOCK_ID); }
48 item::item () :
49 object(),
50 Slot(0),
51 CloneMotherID(0),
52 Fluid(0),
53 LifeExpectancy(0),
54 ItemFlags(0),
55 mIsStepedOn(false),
56 pickupTime(0)
61 truth item::IsOnGround () const { return Slot[0]->IsOnGround(); }
62 truth item::IsSimiliarTo (item *Item) const { return Item->GetType() == GetType() && Item->GetConfig() == GetConfig(); }
63 double item::GetBaseDamage () const { return Max(0.0, sqrt(5e-5*GetWeaponStrength())+GetDamageBonus()); }
64 int item::GetBaseMinDamage () const { return int(GetBaseDamage()*0.75); }
65 int item::GetBaseMaxDamage () const { return int(GetBaseDamage()*1.25)+1; }
66 int item::GetBaseToHitValue () const { return int(10000.0/(1000+GetWeight())+GetTHVBonus()); }
67 int item::GetBaseBlockValue () const { return int((10000.0/(1000+GetWeight())+GetTHVBonus())*GetBlockModifier()/10000.0); }
68 truth item::IsInCorrectSlot (int I) const { return I == RIGHT_WIELDED_INDEX || I == LEFT_WIELDED_INDEX; }
69 truth item::IsInCorrectSlot () const { return IsInCorrectSlot(static_cast<gearslot *>(*Slot)->GetEquipmentIndex()); }
70 int item::GetEquipmentIndex () const { return static_cast<gearslot *>(*Slot)->GetEquipmentIndex(); }
71 int item::GetGraphicsContainerIndex () const { return GR_ITEM; }
72 truth item::IsBroken () const { return GetConfig () & BROKEN; }
73 truth item::IsFood() const { return DataBase->Category & FOOD; }
74 cchar *item::GetBreakVerb () const { return "breaks"; }
75 square *item::GetSquareUnderEntity (int I) const { return GetSquareUnder(I); }
76 square *item::GetSquareUnder (int I) const { return Slot[I] ? Slot[I]->GetSquareUnder () : 0; }
77 lsquare *item::GetLSquareUnder (int I) const { return static_cast<lsquare *>(Slot[I]->GetSquareUnder ()); }
78 void item::SignalStackAdd (stackslot *StackSlot, void (stack::*)(item*, truth)) { Slot[0] = StackSlot; }
79 truth item::IsAnimated () const { return GraphicData.AnimationFrames > 1 || (Fluid && ShowFluids ()); }
80 truth item::IsRusted () const { return MainMaterial->GetRustLevel () != NOT_RUSTED; }
81 truth item::IsEatable (ccharacter *Eater) const { return GetConsumeMaterial(Eater, &material::IsSolid) && IsConsumable (); }
82 truth item::IsDrinkable (ccharacter *Eater) const { return GetConsumeMaterial(Eater, &material::IsLiquid) && IsConsumable (); }
83 pixelpredicate item::GetFluidPixelAllowedPredicate () const { return &rawbitmap::IsTransparent; }
84 void item::Cannibalize () { Flags |= CANNIBALIZED; }
85 void item::SetMainMaterial (material *NewMaterial, int SpecialFlags) { SetMaterial(MainMaterial, NewMaterial, GetDefaultMainVolume (), SpecialFlags); }
86 void item::ChangeMainMaterial (material *NewMaterial, int SpecialFlags) { ChangeMaterial(MainMaterial, NewMaterial, GetDefaultMainVolume (), SpecialFlags); }
87 void item::InitMaterials (const materialscript *M, const materialscript *, truth CUP) { InitMaterials(M->Instantiate (), CUP); }
88 int item::GetMainMaterialRustLevel () const { return MainMaterial->GetRustLevel(); }
91 item::item (citem &Item) :
92 object(Item),
93 Slot(0),
94 Size(Item.Size),
95 DataBase(Item.DataBase),
96 Volume(Item.Volume),
97 Weight(Item.Weight),
98 Fluid(0),
99 FluidCount(0),
100 SquaresUnder(Item.SquaresUnder),
101 LifeExpectancy(Item.LifeExpectancy),
102 ItemFlags(Item.ItemFlags)
104 Flags &= ENTITY_FLAGS|SQUARE_POSITION_BITS;
105 ID = game::CreateNewItemID(this);
106 CloneMotherID = new idholder(Item.ID);
107 idholder* TI = CloneMotherID;
109 for (idholder* II = Item.CloneMotherID; II; II = II->Next) TI = TI->Next = new idholder(II->ID);
110 TI->Next = 0;
111 Slot = new slot*[SquaresUnder];
112 for (int c = 0; c < SquaresUnder; ++c) Slot[c] = 0;
116 item::~item () {
117 delete [] Slot;
118 game::RemoveItemID(ID);
119 fluid **FP = Fluid;
120 if (FP) {
121 for (int c = 0; c < /*SquaresUnder*/FluidCount; ++c) deleteList(FP[c]);
122 delete [] FP;
124 deleteList(CloneMotherID);
128 void item::Fly (character *Thrower, int Direction, int Force) {
129 int Range = Force*25/Max(sLong(sqrt(GetWeight())), 1);
130 lsquare *LSquareUnder = GetLSquareUnder();
131 RemoveFromSlot();
132 LSquareUnder->GetStack()->AddItem(this, false);
133 if (!Range || GetSquaresUnder() != 1) {
134 if (GetLSquareUnder()->GetRoom()) GetLSquareUnder()->GetRoom()->AddItemEffect(this);
135 return;
137 if (Direction == RANDOM_DIR) Direction = RAND()&7;
138 v2 StartingPos = GetPos();
139 v2 Pos = StartingPos;
140 v2 DirVector = game::GetMoveVector(Direction);
141 truth Breaks = false;
142 double BaseDamage, BaseToHitValue;
143 /*** check ***/
144 if (Thrower) {
145 int Bonus = Thrower->IsHumanoid() ? Thrower->GetCWeaponSkill(GetWeaponCategory())->GetBonus() : 1000;
146 BaseDamage = sqrt(5e-12 * GetWeaponStrength() * Force / Range) * Bonus;
147 BaseToHitValue = 10 * Bonus * Thrower->GetMoveEase() / (500 + GetWeight()) * Thrower->GetAttribute(DEXTERITY) * sqrt(2.5e-8 * Thrower->GetAttribute(PERCEPTION)) / Range;
148 } else {
149 BaseDamage = sqrt(5e-6 * GetWeaponStrength() * Force / Range);
150 BaseToHitValue = 10 * 100 / (500 + GetWeight()) / Range;
152 int RangeLeft;
153 for (RangeLeft = Range; RangeLeft; --RangeLeft) {
154 if (!GetLevel()->IsValidPos(Pos+DirVector)) break;
155 lsquare *JustHit = GetNearLSquare(Pos+DirVector);
156 if (!JustHit->IsFlyable()) {
157 Breaks = true;
158 JustHit->GetOLTerrain()->HasBeenHitByItem(Thrower, this, int(BaseDamage*sqrt(RangeLeft)));
159 break;
160 } else {
161 clock_t StartTime = clock();
162 Pos += DirVector;
163 RemoveFromSlot();
164 JustHit->GetStack()->AddItem(this, false);
165 truth Draw = game::OnScreen(JustHit->GetPos()) && JustHit->CanBeSeenByPlayer();
166 if (Draw) game::DrawEverything();
167 if (JustHit->GetCharacter()) {
168 int Damage = int(BaseDamage * sqrt(RangeLeft));
169 double ToHitValue = BaseToHitValue*RangeLeft;
170 int Returned = HitCharacter(Thrower, JustHit->GetCharacter(), Damage, ToHitValue, Direction);
171 if (Returned == HIT) Breaks = true;
172 if (Returned != MISSED) break;
174 if (Draw) { while (clock()-StartTime < 0.03 * CLOCKS_PER_SEC); } //FIXME
177 if (Breaks) ReceiveDamage(Thrower, int(sqrt(GetWeight()*RangeLeft) / 10), THROW|PHYSICAL_DAMAGE, Direction);
178 if (Exists() && GetLSquareUnder()->GetRoom()) GetLSquareUnder()->GetRoom()->AddItemEffect(this);
182 int item::HitCharacter (character *Thrower, character *Dude, int Damage, double ToHitValue, int Direction) {
183 if (Dude->Catches(this)) return CATCHED;
184 if (Thrower && !EffectIsGood()) Thrower->Hostility(Dude);
185 if (Dude->DodgesFlyingItem(this, ToHitValue)) {
186 if (Dude->IsPlayer()) ADD_MESSAGE("%s misses you.", CHAR_NAME(DEFINITE));
187 else if (Dude->CanBeSeenByPlayer()) ADD_MESSAGE("%s misses %s.", CHAR_NAME(DEFINITE), Dude->CHAR_NAME(DEFINITE));
188 return MISSED;
190 Dude->HasBeenHitByItem(Thrower, this, Damage, ToHitValue, Direction);
191 return HIT;
195 double item::GetWeaponStrength () const {
196 return GetFormModifier() * GetMainMaterial()->GetStrengthValue() * sqrt(GetMainMaterial()->GetWeight());
200 int item::GetStrengthRequirement () const {
201 double WeightTimesSize = GetWeight() * GetSize();
202 return int(1.25e-10 * WeightTimesSize * WeightTimesSize);
206 truth item::Apply (character *Applier) {
207 if (Applier->IsPlayer()) ADD_MESSAGE("You can't apply this!");
208 return false;
212 /* Returns truth that tells whether the Polymorph really happened */
213 truth item::Polymorph (character *Polymorpher, stack *CurrentStack) {
214 if (!IsPolymorphable()) return false;
215 if (Polymorpher && IsOnGround()) {
216 room *Room = GetRoom();
217 if (Room) Room->HostileAction(Polymorpher);
219 if (GetSquarePosition() != CENTER) {
220 stack *Stack = CurrentStack->GetLSquareUnder()->GetStackOfAdjacentSquare(GetSquarePosition());
221 if (Stack) CurrentStack = Stack;
223 CurrentStack->AddItem(protosystem::BalancedCreateItem(0, 0, MAX_PRICE, ANY_CATEGORY, 0, 0, 0, true));
224 RemoveFromSlot();
225 SendToHell();
226 return true;
230 /* Returns whether the Eater must stop eating the item */
231 truth item::Consume (character *Eater, sLong Amount) {
232 material *ConsumeMaterial = GetConsumeMaterial(Eater);
233 if (!ConsumeMaterial) return true;
234 if (Eater->IsPlayer() && !(Flags & CANNIBALIZED) && Eater->CheckCannibalism(ConsumeMaterial)) {
235 game::DoEvilDeed(25);
236 ADD_MESSAGE("You feel that this was an evil deed.");
237 Cannibalize();
239 feuLong ID = GetID();
240 material *Garbage = ConsumeMaterial->EatEffect(Eater, Amount);
241 item *NewConsuming = GetID() ? this : game::SearchItem(ID);
242 material *NewConsumeMaterial = NewConsuming->GetConsumeMaterial(Eater);
243 if (!NewConsuming->Exists() || !NewConsumeMaterial || !NewConsumeMaterial->IsSameAs(ConsumeMaterial))
244 ConsumeMaterial->FinishConsuming(Eater);
245 delete Garbage;
246 return !NewConsuming->Exists() || !NewConsumeMaterial;
250 truth item::CanBeEatenByAI (ccharacter *Eater) const {
251 material *ConsumeMaterial = GetConsumeMaterial(Eater);
252 return (!Eater->IsPet()
253 || !(Eater->GetCommandFlags() & DONT_CONSUME_ANYTHING_VALUABLE)
254 || !IsValuable())
255 && IsConsumable()
256 && ConsumeMaterial && ConsumeMaterial->CanBeEatenByAI(Eater);
260 void item::Save (outputfile &SaveFile) const {
261 SaveFile << (uShort)GetType();
262 object::Save(SaveFile);
263 SaveFile << (uShort)0;
264 SaveFile << mIsStepedOn;
265 SaveFile << (uShort)GetConfig();
266 SaveFile << (uShort)Flags;
267 SaveFile << Size << ID << LifeExpectancy << ItemFlags;
268 SaveFile << pickupTime;
269 SaveLinkedList(SaveFile, CloneMotherID);
270 if (Fluid) {
271 SaveFile.Put(true);
272 SaveFile << FluidCount;
273 for (int c = 0; c < /*SquaresUnder*/FluidCount; ++c) SaveLinkedList(SaveFile, Fluid[c]);
274 } else {
275 SaveFile.Put(false);
280 void item::Load (inputfile &SaveFile) {
281 object::Load(SaveFile);
282 int ver = ReadType(uShort, SaveFile);
283 if (ver != 0) ABORT("invalid item version in savefile: %d", ver);
284 SaveFile >> mIsStepedOn;
285 databasecreator<item>::InstallDataBase(this, ReadType(uShort, SaveFile));
286 Flags |= ReadType(uShort, SaveFile) & ~ENTITY_FLAGS;
287 SaveFile >> Size >> ID >> LifeExpectancy >> ItemFlags;
288 SaveFile >> pickupTime;
289 LoadLinkedList(SaveFile, CloneMotherID);
290 if (LifeExpectancy) Enable();
291 game::AddItemID(this, ID);
292 if (SaveFile.Get()) {
293 SaveFile >> FluidCount;
294 Fluid = new fluid*[/*SquaresUnder*/FluidCount];
295 for (int c = 0; c < /*SquaresUnder*/FluidCount; ++c) {
296 LoadLinkedList(SaveFile, Fluid[c]);
297 for (fluid *F = Fluid[c]; F; F = F->Next) F->SetMotherItem(this);
299 } else {
300 if (Fluid) Fluid = 0; //FIXME: MEMORY LEAK
301 FluidCount = 0;
304 const fearray<festring> &lt = GetLevelTags();
305 if (lt.Size > 1) {
306 fprintf(stderr, "====\n");
307 for (uInt f = 0; f < lt.Size; ++f) fprintf(stderr, " %u: [%s]\n", f, lt[f].CStr());
313 void item::TeleportRandomly () {
314 if (GetSquaresUnder() == 1) {
315 // gum solution
316 lsquare *Square = GetNearLSquare(GetLevel()->GetRandomSquare());
317 MoveTo(Square->GetStack());
318 if (Square->CanBeSeenByPlayer()) ADD_MESSAGE("Suddenly %s appears!", CHAR_NAME(INDEFINITE));
323 int item::GetStrengthValue () const {
324 return sLong(GetStrengthModifier()) * GetMainMaterial()->GetStrengthValue() / 2000;
328 void item::RemoveFromSlot () {
329 for (int c = 0; c < SquaresUnder; ++c) {
330 if (Slot[c]) {
331 try {
332 Slot[c]->Empty();
333 } catch (quitrequest) {
334 SendToHell();
335 throw;
337 Slot[c] = 0;
343 void item::MoveTo (stack *Stack, truth setPickupTime) {
344 RemoveFromSlot();
345 Stack->AddItem(this);
346 if (setPickupTime) {
347 pickupTime = game::GetTick();
348 //fprintf(stderr, "item '%s'; pickuptime set to %u\n", GetNameSingular().CStr(), pickupTime);
353 cchar *item::GetItemCategoryName (sLong Category) {
354 // convert to array
355 if (Category == HELMET) return "Helmets";
356 if (Category == AMULET) return "Amulets";
357 if (Category == CLOAK) return "Cloaks";
358 if (Category == BODY_ARMOR) return "Body armors";
359 if (Category == WEAPON) return "Weapons";
360 if (Category == SHIELD) return "Shields";
361 if (Category == RING) return "Rings";
362 if (Category == GAUNTLET) return "Gauntlets";
363 if (Category == BELT) return "Belts";
364 if (Category == BOOT) return "Boots";
365 if (Category == FOOD) return "Food";
366 if (Category == POTION) return "Potions";
367 if (Category == SCROLL) return "Scrolls";
368 if (Category == BOOK) return "Books";
369 if (Category == WAND) return "Wands";
370 if (Category == TOOL) return "Tools";
371 if (Category == VALUABLE) return "Valuables";
372 if (Category == MISC) return "Miscellaneous items";
373 return "Warezzz";
377 int item::GetResistance (int Type) const {
378 if ((Type&0xFFF) == PHYSICAL_DAMAGE) return GetStrengthValue();
379 if ((Type&0xFFF) == ENERGY) return GetEnergyResistance();
380 if ((Type&0xFFF) == DRAIN || (Type&0xFFF) == MUSTARD_GAS_DAMAGE) return 0;
381 if ((Type&0xFFF) == FIRE) return GetFireResistance();
382 if ((Type&0xFFF) == POISON) return GetPoisonResistance();
383 if ((Type&0xFFF) == ELECTRICITY) return GetElectricityResistance();
384 if ((Type&0xFFF) == ACID) return GetAcidResistance();
385 if ((Type&0xFFF) == SOUND) return GetSoundResistance();
386 ABORT("Resistance lack detected!");
387 return 0;
391 truth item::Open (character *Char) {
392 if (Char->IsPlayer()) ADD_MESSAGE("You can't open %s.", CHAR_NAME(DEFINITE));
393 return false;
397 item *itemprototype::SpawnAndLoad (inputfile &SaveFile) const {
398 item *Item = Spawner(0, LOAD);
399 Item->Load(SaveFile);
400 Item->CalculateAll();
401 return Item;
405 void item::LoadDataBaseStats () {
406 SetSize(GetDefaultSize());
410 void item::Initialize (int NewConfig, int SpecialFlags) {
411 CalculateSquaresUnder();
412 Slot = new slot*[SquaresUnder];
413 for (int c = 0; c < SquaresUnder; ++c) Slot[c] = 0;
414 if (!(SpecialFlags & LOAD)) {
415 ID = game::CreateNewItemID(this);
416 databasecreator<item>::InstallDataBase(this, NewConfig);
417 LoadDataBaseStats();
418 RandomizeVisualEffects();
419 Flags |= CENTER << SQUARE_POSITION_SHIFT;
420 if (!(SpecialFlags & NO_MATERIALS)) GenerateMaterials();
422 if (!(SpecialFlags & LOAD)) PostConstruct();
423 if (!(SpecialFlags & (LOAD|NO_MATERIALS))) {
424 CalculateAll();
425 if (!(SpecialFlags & NO_PIC_UPDATE)) UpdatePictures();
430 truth item::ShowMaterial () const {
431 if (GetMainMaterialConfig().Size == 1) return GetMainMaterial()->GetConfig() != GetMainMaterialConfig()[0];
432 //FIXME: gum solution
433 if (GetNameSingular() == "bone") {
434 // never show the material for 'bone bone'
435 if (GetMainMaterial()->GetConfig() == BONE) return false;
437 return true;
441 sLong item::GetBlockModifier() const
443 if(!IsShield(0))
444 return GetSize() * GetRoundness() << 1;
445 else
446 return GetSize() * GetRoundness() << 2;
449 truth item::CanBeSeenByPlayer() const
451 return CanBeSeenBy(PLAYER);
454 truth item::CanBeSeenBy(ccharacter* Who) const
456 for(int c = 0; c < SquaresUnder; ++c)
457 if(Slot[c] && Slot[c]->CanBeSeenBy(Who))
458 return true;
460 return Who->IsPlayer() && game::GetSeeWholeMapCheatMode();
463 festring item::GetDescription(int Case) const
465 if(CanBeSeenByPlayer())
466 return GetName(Case);
467 else
468 return CONST_S("something");
471 void item::SignalVolumeAndWeightChange()
473 CalculateVolumeAndWeight();
475 for(int c = 0; c < SquaresUnder; ++c)
476 if(Slot[c])
477 Slot[c]->SignalVolumeAndWeightChange();
480 void item::CalculateVolumeAndWeight()
482 Volume = Weight = 0;
484 for(int c = 0; c < GetMaterials(); ++c)
486 cmaterial* Material = GetMaterial(c);
488 if(Material)
490 Volume += Material->GetVolume();
491 Weight += Material->GetWeight();
496 void item::SignalEmitationIncrease(col24 EmitationUpdate)
498 if(game::CompareLights(EmitationUpdate, Emitation) > 0)
500 game::CombineLights(Emitation, EmitationUpdate);
502 for(int c = 0; c < SquaresUnder; ++c)
503 if(Slot[c])
504 Slot[c]->SignalEmitationIncrease(EmitationUpdate);
508 void item::SignalEmitationDecrease(col24 EmitationUpdate)
510 if(game::CompareLights(EmitationUpdate, Emitation) >= 0 && Emitation)
512 col24 Backup = Emitation;
513 CalculateEmitation();
515 if(Backup != Emitation)
516 for(int c = 0; c < SquaresUnder; ++c)
517 if(Slot[c])
518 Slot[c]->SignalEmitationDecrease(EmitationUpdate);
522 void item::CalculateAll()
524 CalculateVolumeAndWeight();
525 CalculateEmitation();
528 /* Temporary and buggy. */
530 void item::WeaponSkillHit(int Hits)
532 if(Slot[0] && Slot[0]->IsGearSlot())
533 static_cast<arm*>(static_cast<gearslot*>(*Slot)->GetBodyPart())->WieldedSkillHit(Hits);
536 /* Returns 0 if item cannot be cloned */
538 item* item::Duplicate(feuLong Flags)
540 if(!(Flags & IGNORE_PROHIBITIONS)
541 && ((!(Flags & MIRROR_IMAGE) && !CanBeCloned())
542 || (Flags & MIRROR_IMAGE && (!CanBeMirrored()
543 || (MainMaterial
544 && !(MainMaterial->GetCommonFlags() & CAN_BE_MIRRORED))
545 || (GetSecondaryMaterial()
546 && !(GetSecondaryMaterial()->GetCommonFlags() & CAN_BE_MIRRORED))))))
547 return 0;
549 item* Clone = GetProtoType()->Clone(this);
551 if(Flags & MIRROR_IMAGE)
552 Clone->SetLifeExpectancy(Flags >> LE_BASE_SHIFT & LE_BASE_RANGE,
553 Flags >> LE_RAND_SHIFT & LE_RAND_RANGE);
555 idholder* I = new idholder(ID);
556 I->Next = CloneMotherID;
557 CloneMotherID = I;
558 game::RemoveItemID(ID);
559 ID = game::CreateNewItemID(this);
560 Clone->UpdatePictures();
561 return Clone;
564 void item::AddInventoryEntry(ccharacter*, festring& Entry, int Amount, truth ShowSpecialInfo) const
566 if(Amount == 1)
567 AddName(Entry, INDEFINITE);
568 else
570 Entry << Amount << ' ';
571 AddName(Entry, PLURAL);
574 if(ShowSpecialInfo)
575 Entry << " [\1C" << GetWeight() * Amount << "g\2]";
578 const itemdatabase* itemprototype::ChooseBaseForConfig(itemdatabase** TempConfig, int Configs, int ConfigNumber)
580 if(!(ConfigNumber & BROKEN))
581 return *TempConfig;
582 else
584 ConfigNumber ^= BROKEN;
586 for(int c = 0; c < Configs; ++c)
587 if(TempConfig[c]->Config == ConfigNumber)
588 return TempConfig[c];
590 return *TempConfig;
594 truth item::ReceiveDamage(character* Damager, int Damage, int Type, int Dir)
596 if(CanBeBroken() && !IsBroken() && Type & (PHYSICAL_DAMAGE|SOUND|ENERGY|ACID))
598 int StrengthValue = GetStrengthValue();
600 if(!StrengthValue)
601 StrengthValue = 1;
603 if(Damage > StrengthValue << 2 && RAND() & 3 && RAND() % (25 * Damage / StrengthValue) >= 100)
605 Break(Damager, Dir);
606 return true;
610 if(Type & ACID && IsBroken() && IsDestroyable(Damager))
612 int StrengthValue = GetStrengthValue();
614 if(!StrengthValue)
615 StrengthValue = 1;
617 if(Damage > StrengthValue << 4 && !(RAND() & 3) && RAND() % (100 * Damage / StrengthValue) >= 100)
619 Destroy(Damager, Dir);
620 return true;
624 return false;
627 void itemdatabase::InitDefaults (const itemprototype *NewProtoType, int NewConfig, cfestring &acfgstrname)
629 IsAbstract = false;
630 ProtoType = NewProtoType;
631 Config = NewConfig;
632 CfgStrName = acfgstrname;
634 if(NewConfig & BROKEN)
636 if(Adjective.GetSize())
637 Adjective.Insert(0, "broken ");
638 else
639 Adjective = CONST_S("broken");
641 DefaultSize >>= 1;
642 FormModifier >>= 2;
643 StrengthModifier >>= 1;
647 sLong item::GetNutritionValue() const
649 sLong NV = 0;
651 for(int c = 0; c < GetMaterials(); ++c)
652 if(GetMaterial(c))
653 NV += GetMaterial(c)->GetTotalNutritionValue();
655 return NV;
658 void item::SignalSpoil(material*)
660 if(!Exists())
661 return;
663 if(CanBeSeenByPlayer())
664 ADD_MESSAGE("%s spoils completely.", GetExtendedDescription().CStr());
666 truth Equipped = PLAYER->Equips(this);
667 Disappear();
669 if(Equipped)
670 game::AskForEscPress(CONST_S("Equipment destroyed!"));
673 item* item::DuplicateToStack(stack* CurrentStack, feuLong Flags)
675 item* Duplicated = Duplicate(Flags);
677 if(!Duplicated)
678 return 0;
680 CurrentStack->AddItem(Duplicated);
681 return Duplicated;
684 truth item::CanBePiledWith(citem* Item, ccharacter* Viewer) const
686 return (GetType() == Item->GetType()
687 && GetConfig() == Item->GetConfig()
688 && ItemFlags == Item->ItemFlags
689 && (WeightIsIrrelevant() || Weight == Item->Weight)
690 && MainMaterial->IsSameAs(Item->MainMaterial)
691 && MainMaterial->GetSpoilLevel() == Item->MainMaterial->GetSpoilLevel()
692 && MainMaterial->GetRustLevel() == Item->MainMaterial->GetRustLevel()
693 && Viewer->GetCWeaponSkillLevel(this) == Viewer->GetCWeaponSkillLevel(Item)
694 && Viewer->GetSWeaponSkillLevel(this) == Viewer->GetSWeaponSkillLevel(Item)
695 && !Fluid && !Item->Fluid
696 && !LifeExpectancy == !Item->LifeExpectancy);
699 void item::Break(character* Breaker, int)
701 if(CanBeSeenByPlayer())
702 ADD_MESSAGE("%s %s.", GetExtendedDescription().CStr(), GetBreakVerb());
704 if(Breaker && IsOnGround())
706 room* Room = GetRoom();
708 if(Room)
709 Room->HostileAction(Breaker);
712 item* Broken = GetProtoType()->Clone(this);
713 Broken->SetConfig(GetConfig() | BROKEN);
714 Broken->SetSize(Broken->GetSize() >> 1);
715 DonateFluidsTo(Broken);
716 DonateIDTo(Broken);
717 DonateSlotTo(Broken);
718 SendToHell();
720 if(PLAYER->Equips(Broken))
721 game::AskForEscPress(CONST_S("Equipment broken!"));
724 void item::Be()
726 MainMaterial->Be(ItemFlags);
728 if (Exists() && LifeExpectancy) {
729 if (LifeExpectancy == 1) {
730 if (CanBeSeenByPlayer()) ADD_MESSAGE("%s disappears.", GetExtendedDescription().CStr());
731 truth Equipped = PLAYER->Equips(this);
732 Disappear();
733 if (Equipped) game::AskForEscPress(CONST_S("Equipment destroyed!"));
734 } else {
735 --LifeExpectancy;
740 int item::GetOfferValue(int Receiver) const
742 /* Temporary */
744 int OfferValue = int(sqrt(GetTruePrice()));
746 if(Receiver == GetAttachedGod())
747 OfferValue <<= 1;
748 else
749 OfferValue >>= 1;
751 return OfferValue;
754 void item::SignalEnchantmentChange()
756 for(int c = 0; c < SquaresUnder; ++c)
757 if(Slot[c])
758 Slot[c]->SignalEnchantmentChange();
761 sLong item::GetEnchantedPrice(int Enchantment) const
763 return !PriceIsProportionalToEnchantment() ? item::GetPrice() : Max<int>(item::GetPrice() * Enchantment * Enchantment, 0);
766 item* item::Fix()
768 item* Fixed = this;
770 if(IsBroken())
772 Fixed = GetProtoType()->Clone(this);
773 Fixed->SetConfig(GetConfig() ^ BROKEN);
774 Fixed->SetSize(Fixed->GetSize() << 1);
775 DonateFluidsTo(Fixed);
776 DonateIDTo(Fixed);
777 DonateSlotTo(Fixed);
778 SendToHell();
781 return Fixed;
784 void item::DonateSlotTo(item* Item)
786 if(Slot[0])
788 Slot[0]->DonateTo(Item);
789 Slot[0] = 0;
791 for(int c = 1; c < SquaresUnder; ++c)
792 if(Slot[c])
794 Slot[c]->Empty();
795 Slot[c] = 0;
800 int item::GetSpoilLevel() const
802 return MainMaterial->GetSpoilLevel();
805 void item::SignalSpoilLevelChange(material*)
807 if(!IsAnimated() && GetSpoilLevel() && Slot[0] && Slot[0]->IsVisible())
808 for(int c = 0; c < SquaresUnder; ++c)
809 GetSquareUnder(c)->IncStaticAnimatedEntities();
811 SignalVolumeAndWeightChange(); // gum
812 UpdatePictures();
815 truth item::AllowSpoil() const
817 if(IsOnGround())
819 lsquare* Square = GetLSquareUnder();
820 int RoomNumber = Square->GetRoomIndex();
821 return !RoomNumber || Square->GetLevel()->GetRoom(RoomNumber)->AllowSpoil(this);
823 else
824 return true;
827 void item::ResetSpoiling()
829 for(int c = 0; c < GetMaterials(); ++c)
830 if(GetMaterial(c))
831 GetMaterial(c)->ResetSpoiling();
834 cchar* item::GetBaseToHitValueDescription() const
836 if(GetBaseToHitValue() < 10)
837 return ToHitValueDescription[Min(GetBaseToHitValue(), 6)];
838 else
839 return ToHitValueDescription[7];
842 cchar* item::GetBaseBlockValueDescription() const
844 if(GetBaseBlockValue() < 20)
845 return ToHitValueDescription[Min(GetBaseBlockValue() >> 1, 6)];
846 else
847 return ToHitValueDescription[7];
850 cchar* item::GetStrengthValueDescription() const
852 int SV = GetStrengthValue();
854 if(SV < 3)
855 return StrengthValueDescription[0];
856 else if(SV < 5)
857 return StrengthValueDescription[1];
858 else if(SV < 8)
859 return StrengthValueDescription[2];
860 else if(SV < 11)
861 return StrengthValueDescription[3];
862 else if(SV < 16)
863 return StrengthValueDescription[4];
864 else if(SV < 20)
865 return StrengthValueDescription[5];
866 else
867 return StrengthValueDescription[6];
870 void item::SpecialGenerationHandler()
872 if(HandleInPairs())
873 Slot[0]->AddFriendItem(Duplicate());
876 void item::SortAllItems(const sortdata& SortData) const
878 if(SortData.Sorter == 0 || (this->*SortData.Sorter)(SortData.Character))
879 SortData.AllItems.push_back(const_cast<item*>(this));
882 int item::GetAttachedGod() const
884 return DataBase->AttachedGod ? DataBase->AttachedGod : MainMaterial->GetAttachedGod();
887 sLong item::GetMaterialPrice() const
889 return MainMaterial->GetRawPrice();
892 sLong item::GetTruePrice() const
894 if(LifeExpectancy)
895 return 0;
897 sLong Price = Max(GetPrice(), GetMaterialPrice());
899 if(Spoils())
900 Price = Price * (100 - GetMaxSpoilPercentage()) / 500;
902 return Price;
905 #ifdef WIZARD
907 void item::AddAttackInfo(felist& List) const
909 festring Entry(40, ' ');
910 Entry << int(GetWeight());
911 Entry.Resize(50);
912 Entry << int(GetSize());
913 Entry.Resize(60);
914 Entry << int(GetStrengthRequirement());
915 Entry.Resize(70);
916 Entry << GetBaseMinDamage() << '-' << GetBaseMaxDamage();
917 List.AddEntry(Entry, LIGHT_GRAY);
920 void item::AddMiscellaneousInfo(felist& List) const
922 festring Entry(40, ' ');
923 Entry << int(GetTruePrice());
924 Entry.Resize(55);
925 Entry << GetOfferValue(0);
926 Entry.Resize(70);
927 Entry << int(GetNutritionValue());
928 List.AddEntry(Entry, LIGHT_GRAY);
931 #endif
934 void item::PreProcessForBone () {
935 if (IsQuestItem()) {
936 RemoveFromSlot();
937 SendToHell();
938 } else {
939 game::RemoveItemID(ID);
940 ID = -ID;
941 game::AddItemID(this, ID);
942 SetSteppedOn(false);
947 void item::PostProcessForBone () {
948 boneidmap::iterator BI = game::GetBoneItemIDMap().find(-ID);
949 game::RemoveItemID(ID);
951 if (BI == game::GetBoneItemIDMap().end()) {
952 feuLong NewID = game::CreateNewItemID(this);
953 game::GetBoneItemIDMap().insert(std::make_pair(-ID, NewID));
954 ID = NewID;
955 } else {
956 if (game::SearchItem(BI->second)) {
957 ID = BI->second;
958 game::AddItemID(this, ID);
961 for (idholder* I = CloneMotherID; I; I = I->Next) {
962 BI = game::GetBoneItemIDMap().find(I->ID);
963 if (BI == game::GetBoneItemIDMap().end()) {
964 feuLong NewCloneMotherID = game::CreateNewItemID(0);
965 game::GetBoneItemIDMap().insert(std::make_pair(I->ID, NewCloneMotherID));
966 I->ID = NewCloneMotherID;
967 } else {
968 I->ID = BI->second;
974 void item::SetConfig(int NewConfig, int SpecialFlags)
976 databasecreator<item>::InstallDataBase(this, NewConfig);
977 CalculateAll();
979 if(!(SpecialFlags & NO_PIC_UPDATE))
980 UpdatePictures();
983 god* item::GetMasterGod() const
985 return game::GetGod(GetConfig());
989 int itemprototype::CreateSpecialConfigurations (itemdatabase **TempConfig, int Configs, int Level) {
990 if (Level) return Configs;
992 if (TempConfig[0]->CreateDivineConfigurations) {
993 Configs = databasecreator<item>::CreateDivineConfigurations(this, TempConfig, Configs);
996 /* Gum solution */
997 if (TempConfig[0]->CreateLockConfigurations) {
998 const item::database*const* KeyConfigData = key::ProtoType.GetConfigData();
999 int KeyConfigSize = key::ProtoType.GetConfigSize();
1000 int OldConfigs = Configs;
1001 for (int c1 = 0; c1 < OldConfigs; ++c1) {
1002 if (!TempConfig[c1]->IsAbstract) {
1003 int BaseConfig = TempConfig[c1]->Config;
1004 int NewConfig = BaseConfig | BROKEN_LOCK;
1005 itemdatabase* ConfigDataBase = new itemdatabase(*TempConfig[c1]);
1006 festring lcfgname;
1007 lcfgname << TempConfig[c1]->CfgStrName;
1008 lcfgname << "|locked-broken";
1010 ConfigDataBase->InitDefaults(this, NewConfig, lcfgname);
1011 ConfigDataBase->PostFix << "with a broken lock";
1012 ConfigDataBase->Possibility = 0;
1013 TempConfig[Configs++] = ConfigDataBase;
1015 for (int c2 = 0; c2 < KeyConfigSize; ++c2) {
1016 festring xcfgname;
1017 xcfgname << TempConfig[c1]->CfgStrName;
1018 xcfgname << "|locked";
1019 NewConfig = BaseConfig|KeyConfigData[c2]->Config;
1020 ConfigDataBase = new itemdatabase(*TempConfig[c1]);
1021 ConfigDataBase->InitDefaults(this, NewConfig, xcfgname);
1022 ConfigDataBase->PostFix << "with ";
1023 if (KeyConfigData[c2]->UsesLongAdjectiveArticle) ConfigDataBase->PostFix << "an "; else ConfigDataBase->PostFix << "a ";
1024 ConfigDataBase->PostFix << KeyConfigData[c2]->Adjective << " lock";
1025 ConfigDataBase->Possibility = 0;
1026 TempConfig[Configs++] = ConfigDataBase;
1032 return Configs;
1035 void item::Draw(blitdata& BlitData) const
1037 cint AF = GraphicData.AnimationFrames;
1038 cint F = !(BlitData.CustomData & ALLOW_ANIMATE) || AF == 1 ? 0 : GET_TICK() & (AF - 1);
1039 cbitmap* P = GraphicData.Picture[F];
1041 if(BlitData.CustomData & ALLOW_ALPHA)
1042 P->AlphaLuminanceBlit(BlitData);
1043 else
1044 P->LuminanceMaskedBlit(BlitData);
1046 if(Fluid && ShowFluids())
1047 DrawFluids(BlitData);
1050 v2 item::GetLargeBitmapPos(v2 BasePos, int I) const
1052 cint SquareIndex = I ? I / (GraphicData.AnimationFrames >> 2) : 0;
1053 return v2(SquareIndex & 1 ? BasePos.X + 16 : BasePos.X, SquareIndex & 2 ? BasePos.Y + 16 : BasePos.Y);
1056 void item::LargeDraw(blitdata& BlitData) const
1058 cint TrueAF = GraphicData.AnimationFrames >> 2;
1059 cint SquareIndex = BlitData.CustomData & SQUARE_INDEX_MASK;
1060 cint F = !(BlitData.CustomData & ALLOW_ANIMATE) ? SquareIndex * TrueAF : SquareIndex * TrueAF + (GET_TICK() & (TrueAF - 1));
1061 cbitmap* P = GraphicData.Picture[F];
1063 if(BlitData.CustomData & ALLOW_ALPHA)
1064 P->AlphaLuminanceBlit(BlitData);
1065 else
1066 P->LuminanceMaskedBlit(BlitData);
1069 void item::DonateIDTo(item* Item)
1071 game::RemoveItemID(Item->ID);
1072 game::UpdateItemID(Item, ID);
1073 Item->ID = ID;
1074 ID = 0;
1077 void item::SignalRustLevelChange()
1079 SignalVolumeAndWeightChange();
1080 UpdatePictures();
1081 SendNewDrawAndMemorizedUpdateRequest();
1084 const rawbitmap* item::GetRawPicture() const
1086 return igraph::GetRawGraphic(GetGraphicsContainerIndex());
1089 void item::RemoveFluid(fluid *ToBeRemoved) {
1090 truth WasAnimated = IsAnimated();
1091 truth HasFluids = false;
1093 if (Fluid && ToBeRemoved) {
1094 for (int c = 0; c < /*SquaresUnder*/FluidCount; ++c) {
1095 fluid *F = Fluid[c];
1097 if (F == ToBeRemoved) {
1098 Fluid[c] = F->Next;
1099 } else if (F) {
1100 fluid *LF = F;
1102 for (F = F->Next; F; LF = F, F = F->Next) if (F == ToBeRemoved) { LF->Next = F->Next; break; }
1104 if (Fluid[c]) HasFluids = true;
1107 UpdatePictures();
1108 if (!HasFluids && Fluid) {
1109 delete [] Fluid;
1110 Fluid = 0;
1111 if (!IsAnimated() != !WasAnimated && Slot[0]->IsVisible()) GetSquareUnder()->DecStaticAnimatedEntities();
1113 if (ToBeRemoved) SignalEmitationDecrease(ToBeRemoved->GetEmitation());
1114 SignalVolumeAndWeightChange();
1118 void item::RemoveAllFluids () {
1119 if (Fluid) {
1120 for (int c = 0; c < /*SquaresUnder*/FluidCount; ) {
1121 //fprintf(stderr, "c: %d; SquaresUnder: %d\n", c, SquaresUnder);
1122 fluid *F = Fluid[c];
1123 if (F) {
1124 RemoveFluid(F);
1125 c = 0;
1126 if (!Fluid) break;
1127 } else {
1128 ++c;
1135 void item::AddFluid (liquid *ToBeAdded, festring LocationName, int SquareIndex, truth IsInside) {
1136 truth WasAnimated = IsAnimated();
1138 if (SquareIndex < 0) ABORT("item::AddFluid(): invalid SquareIndex: %d", SquareIndex);
1140 if (Fluid) {
1141 fluid *F = Fluid[SquareIndex];
1143 if (SquareIndex >= FluidCount) ABORT("item::AddFluid(): invalid SquareIndex: %d", SquareIndex);
1144 if (!F) {
1145 Fluid[SquareIndex] = new fluid(ToBeAdded, this, LocationName, IsInside);
1146 } else {
1147 fluid *LF;
1149 do {
1150 if (ToBeAdded->IsSameAs(F->GetLiquid())) {
1151 F->AddLiquidAndVolume(ToBeAdded->GetVolume());
1152 delete ToBeAdded;
1153 return;
1155 LF = F;
1156 F = F->Next;
1157 } while(F);
1158 LF->Next = new fluid(ToBeAdded, this, LocationName, IsInside);
1160 } else {
1161 FluidCount = SquaresUnder;
1162 Fluid = new fluid*[/*SquaresUnder*/FluidCount];
1163 if (SquareIndex >= FluidCount) ABORT("item::AddFluid(): invalid SquareIndex: %d", SquareIndex);
1164 memset(Fluid, 0, SquaresUnder*sizeof(fluid *));
1165 Fluid[SquareIndex] = new fluid(ToBeAdded, this, LocationName, IsInside);
1168 UpdatePictures();
1169 SignalVolumeAndWeightChange();
1170 SignalEmitationIncrease(ToBeAdded->GetEmitation());
1172 if (Slot[0]) {
1173 if (!IsAnimated() != !WasAnimated && Slot[0]->IsVisible()) {
1174 GetSquareUnder()->IncStaticAnimatedEntities();
1176 SendNewDrawAndMemorizedUpdateRequest();
1180 void item::SendNewDrawAndMemorizedUpdateRequest() const
1182 if(!game::IsInWilderness())
1183 for(int c = 0; c < SquaresUnder; ++c)
1184 if(Slot[c])
1186 lsquare *Square = GetLSquareUnder(c);
1187 if (Square) {
1188 Square->SendNewDrawRequest();
1189 Square->SendMemorizedUpdateRequest();
1194 void item::CalculateEmitation()
1196 object::CalculateEmitation();
1198 if(Fluid)
1199 for(int c = 0; c < /*SquaresUnder*/FluidCount; ++c)
1200 for(const fluid* F = Fluid[c]; F; F = F->Next)
1201 game::CombineLights(Emitation, F->GetEmitation());
1204 void item::FillFluidVector (fluidvector &Vector, int SquareIndex) const {
1205 if (Fluid) {
1206 if (SquareIndex < 0 || SquareIndex >= FluidCount) ABORT("item::FillFluidVector(): invalid SquareIndex: %d\n", SquareIndex);
1207 for (fluid *F = Fluid[SquareIndex]; F; F = F->Next) Vector.push_back(F);
1211 void item::SpillFluid(character*, liquid* Liquid, int SquareIndex)
1213 if(AllowFluids() && Liquid->GetVolume())
1214 AddFluid(Liquid, "", SquareIndex, false);
1215 else
1216 delete Liquid;
1219 void item::TryToRust (sLong LiquidModifier) {
1220 if (MainMaterial->TryToRust(LiquidModifier)) {
1221 if (CanBeSeenByPlayer()) {
1222 if (MainMaterial->GetRustLevel() == NOT_RUSTED) ADD_MESSAGE("%s rusts.", CHAR_NAME(DEFINITE));
1223 else ADD_MESSAGE("%s rusts more.", CHAR_NAME(DEFINITE));
1225 MainMaterial->SetRustLevel(MainMaterial->GetRustLevel() + 1);
1229 void item::CheckFluidGearPictures(v2 ShadowPos, int SpecialFlags, truth BodyArmor)
1231 if(Fluid)
1232 for(fluid* F = Fluid[0]; F; F = F->Next)
1233 F->CheckGearPicture(ShadowPos, SpecialFlags, BodyArmor);
1236 void item::DrawFluidGearPictures(blitdata& BlitData, int SpecialFlags) const
1238 if(Fluid)
1239 for(const fluid* F = Fluid[0]; F; F = F->Next)
1240 F->DrawGearPicture(BlitData, SpecialFlags);
1243 void item::DrawFluidBodyArmorPictures(blitdata& BlitData, int SpecialFlags) const
1245 if(Fluid)
1246 for(const fluid* F = Fluid[0]; F; F = F->Next)
1247 F->DrawBodyArmorPicture(BlitData, SpecialFlags);
1250 void item::DrawFluids(blitdata& BlitData) const
1252 cint SquareIndex = BlitData.CustomData & SQUARE_INDEX_MASK;
1254 for(const fluid* F = Fluid[SquareIndex]; F; F = F->Next)
1255 F->Draw(BlitData);
1258 void item::ReceiveAcid(material*, cfestring&, sLong Modifier)
1260 if(GetMainMaterial()->GetInteractionFlags() & CAN_DISSOLVE)
1262 int Damage = Modifier / 1000;
1264 if(Damage)
1266 Damage += RAND() % Damage;
1267 ReceiveDamage(0, Damage, ACID);
1272 void item::DonateFluidsTo(item* Item)
1274 if(Fluid)
1275 for(int c = 0; c < GetSquaresUnder(); ++c)
1276 for(fluid* F = Fluid[c]; F; F = F->Next)
1278 liquid* Liquid = F->GetLiquid();
1279 Item->AddFluid(Liquid->SpawnMoreLiquid(Liquid->GetVolume()), F->GetLocationName(), c, F->IsInside());
1283 void item::Destroy(character* Destroyer, int)
1285 if(CanBeSeenByPlayer())
1286 ADD_MESSAGE("%s is destroyed.", GetExtendedDescription().CStr());
1288 if(Destroyer && IsOnGround())
1290 room* Room = GetRoom();
1292 if(Room)
1293 Room->HostileAction(Destroyer);
1296 truth Equipped = PLAYER->Equips(this);
1297 RemoveFromSlot();
1298 SendToHell();
1300 if(Equipped)
1301 game::AskForEscPress(CONST_S("Equipment destroyed!"));
1304 void item::RemoveRust()
1306 for(int c = 0; c < GetMaterials(); ++c)
1307 if(GetMaterial(c))
1308 GetMaterial(c)->SetRustLevel(NOT_RUSTED);
1311 void item::SetSpoilPercentage(int Value)
1313 for(int c = 0; c < GetMaterials(); ++c)
1315 material* Material = GetMaterial(c);
1317 if(Material && Material->CanSpoil())
1318 Material->SetSpoilCounter(Material->GetSpoilModifier() * Value / 100);
1322 void item::RedistributeFluids()
1324 if(Fluid)
1325 for(int c = 0; c < GetSquaresUnder(); ++c)
1326 for(fluid* F = Fluid[c]; F; F = F->Next)
1327 F->Redistribute();
1330 material* item::GetConsumeMaterial(ccharacter* Consumer, materialpredicate Predicate) const
1332 return (MainMaterial->*Predicate)() && Consumer->CanConsume(MainMaterial) ? MainMaterial : 0;
1335 /* The parameter can only be MainMaterial */
1337 material* item::RemoveMaterial(material*)
1339 RemoveFromSlot();
1340 SendToHell();
1341 return 0;
1344 void item::InitMaterials(material* FirstMaterial, truth CallUpdatePictures)
1346 InitMaterial(MainMaterial, FirstMaterial, GetDefaultMainVolume());
1347 SignalVolumeAndWeightChange();
1349 if(CallUpdatePictures)
1350 UpdatePictures();
1353 void item::GenerateMaterials()
1355 int Chosen = RandomizeMaterialConfiguration();
1356 const fearray<sLong>& MMC = GetMainMaterialConfig();
1357 InitMaterial(MainMaterial,
1358 MAKE_MATERIAL(MMC.Data[MMC.Size == 1 ? 0 : Chosen]),
1359 GetDefaultMainVolume());
1362 void item::SignalSquarePositionChange(int Position)
1364 Flags &= ~SQUARE_POSITION_BITS;
1365 Flags |= Position << SQUARE_POSITION_SHIFT;
1368 truth item::Read(character* Reader)
1370 Reader->StartReading(this, GetReadDifficulty());
1371 return true;
1374 truth item::CanBeHardened(ccharacter*) const
1376 return MainMaterial->GetHardenedMaterial(this) != NONE;
1379 void item::SetLifeExpectancy(int Base, int RandPlus)
1381 LifeExpectancy = RandPlus > 1 ? Base + RAND_N(RandPlus) : Base;
1382 Enable();
1385 truth item::IsVeryCloseToSpoiling() const
1387 for(int c = 0; c < GetMaterials(); ++c)
1388 if(GetMaterial(c) && !GetMaterial(c)->IsVeryCloseToSpoiling())
1389 return false;
1391 return true;
1394 truth item::IsValuable() const
1396 if(DataBase->IsValuable)
1397 return true;
1399 for(int c = 0; c < GetMaterials(); ++c)
1401 material* M = GetMaterial(c);
1403 if(M && M->GetCommonFlags() & IS_VALUABLE)
1404 return true;
1407 return false;
1410 int item::GetHinderVisibilityBonus(ccharacter* Char) const
1412 int Bonus = 0;
1414 if(GetGearStates() & INFRA_VISION
1415 && !Char->TemporaryStateIsActivated(INFRA_VISION))
1416 Bonus += 20000;
1418 if(GetGearStates() & ESP
1419 && !Char->TemporaryStateIsActivated(ESP))
1420 Bonus += 20000;
1422 if(!game::IsDark(GetEmitation()))
1423 Bonus += 5000;
1425 return Bonus;
1428 sLong item::GetFixPrice() const
1430 item* Clone = GetProtoType()->Clone(this);
1431 Clone = Clone->Fix();
1432 Clone->RemoveRust();
1433 sLong FixPrice = Clone->GetTruePrice();
1434 Clone->SendToHell();
1435 return Max(sLong(3.5 * sqrt(FixPrice)), 10);
1438 void item::AddTrapName(festring& String, int Amount) const
1440 if(Amount == 1)
1441 AddName(String, DEFINITE);
1442 else
1444 String << Amount << ' ';
1445 AddName(String, PLURAL);
1449 truth item::Spoils() const
1451 for(int c = 0; c < GetMaterials(); ++c)
1453 cmaterial* Material = GetMaterial(c);
1455 if(Material && Material->Spoils())
1456 return true;
1459 return false;
1462 int item::GetMaxSpoilPercentage() const
1464 int MaxPercentage = 0;
1466 for(int c = 0; c < GetMaterials(); ++c)
1468 cmaterial* Material = GetMaterial(c);
1470 if(Material)
1471 MaxPercentage = Max(MaxPercentage, Material->GetSpoilPercentage());
1474 return MaxPercentage;
1477 truth item::HasPrice() const
1479 return GetPrice() || GetMaterialPrice();
1482 void item::Disappear()
1484 RemoveFromSlot();
1485 SendToHell();
1488 outputfile& operator<<(outputfile& SaveFile, const idholder* IdHolder)
1490 SaveFile << IdHolder->ID;
1491 return SaveFile;
1494 inputfile& operator>>(inputfile& SaveFile, idholder*& IdHolder)
1496 IdHolder = new idholder(ReadType(feuLong, SaveFile));
1497 return SaveFile;
1500 festring item::GetExtendedDescription() const
1502 if(!CanBeSeenByPlayer())
1503 return CONST_S("something");
1505 festring Desc;
1506 ccharacter* Carrier = FindCarrier();
1508 if(Carrier)
1510 if(Carrier->IsPlayer())
1512 Desc << "your ";
1513 AddName(Desc, UNARTICLED);
1514 return Desc;
1516 else if(Carrier->CanBeSeenByPlayer())
1518 Carrier->AddName(Desc, DEFINITE);
1519 Desc << "'s ";
1520 AddName(Desc, UNARTICLED);
1521 return Desc;
1525 AddName(Desc, DEFINITE);
1527 if(IsOnGround())
1528 GetLSquareUnder()->AddLocationDescription(Desc);
1530 return Desc;
1533 ccharacter* item::FindCarrier() const
1535 return Slot[0]->FindCarrier();
1538 /* returns 0 if not worn or wielded else the wearer */
1540 const character* item::GetWearer() const
1542 if(!GetSlot()->IsGearSlot())
1543 return 0;
1545 return FindCarrier();
1548 void itemlock::PostConstruct()
1550 /* Terrible gum solution! */
1552 if(!(GetVirtualConfig() & LOCK_BITS))
1554 int NormalLockTypes = 0;
1555 const itemdatabase*const* ConfigData = GetVirtualProtoType()->GetConfigData();
1556 int c, ConfigSize = GetVirtualProtoType()->GetConfigSize();
1558 for(c = 0; c < ConfigSize; ++c)
1559 if(ConfigData[c]->Config & LOCK_BITS
1560 && (ConfigData[c]->Config & ~LOCK_BITS) == GetVirtualConfig()
1561 && !(ConfigData[c]->Config & S_LOCK_ID))
1562 ++NormalLockTypes;
1564 int ChosenLock = RAND() % NormalLockTypes;
1566 for(c = 0; c < ConfigSize; ++c)
1567 if(ConfigData[c]->Config & LOCK_BITS
1568 && (ConfigData[c]->Config & ~LOCK_BITS) == GetVirtualConfig()
1569 && !(ConfigData[c]->Config & S_LOCK_ID)
1570 && !ChosenLock--)
1572 SetVirtualConfig(ConfigData[c]->Config, NO_PIC_UPDATE);
1573 break;
1578 truth itemlock::TryKey(item* Key, character* Applier)
1580 if(GetVirtualConfig() & BROKEN_LOCK)
1582 ADD_MESSAGE("The lock is broken.");
1583 return true;
1586 if(Key->CanOpenLockType(GetVirtualConfig()&LOCK_BITS))
1588 if(Locked)
1590 if(Applier->IsPlayer())
1591 ADD_MESSAGE("You unlock %s.", GetVirtualDescription(DEFINITE).CStr());
1592 else if(Applier->CanBeSeenByPlayer())
1593 ADD_MESSAGE("%s unlocks %s.", Applier->CHAR_NAME(DEFINITE), GetVirtualDescription(DEFINITE).CStr());
1595 else
1597 if(Applier->IsPlayer())
1598 ADD_MESSAGE("You lock %s.", GetVirtualDescription(DEFINITE).CStr());
1599 else if(Applier->CanBeSeenByPlayer())
1600 ADD_MESSAGE("%s locks %s.", Applier->CHAR_NAME(DEFINITE), GetVirtualDescription(DEFINITE).CStr());
1603 Locked = !Locked;
1605 else
1607 if(Applier->IsPlayer())
1608 ADD_MESSAGE("%s doesn't fit in the lock.", Key->CHAR_NAME(DEFINITE));
1609 else if(Applier->CanBeSeenByPlayer())
1610 ADD_MESSAGE("%s tries to fit %s in the lock, but fails.", Applier->CHAR_NAME(DEFINITE), Key->CHAR_NAME(DEFINITE));
1613 return true;
1616 void itemlock::Save(outputfile& SaveFile) const
1618 SaveFile << Locked;
1621 void itemlock::Load(inputfile& SaveFile)
1623 SaveFile >> Locked;
1626 truth item::IsBeverage(ccharacter*) const
1628 for(int c = 0; c < GetMaterials(); ++c)
1630 cmaterial* Material = GetMaterial(c);
1632 if(Material && (Material->GetCategoryFlags() & IS_BEVERAGE))
1633 return true;
1636 return false;
1639 void item::Haste()
1641 ItemFlags |= HASTE;
1642 ItemFlags &= ~SLOW;
1643 SendMemorizedUpdateRequest();
1646 void item::Slow()
1648 ItemFlags |= SLOW;
1649 ItemFlags &= ~HASTE;
1650 SendMemorizedUpdateRequest();
1653 void item::SendMemorizedUpdateRequest() const
1655 if(!game::IsInWilderness())
1656 for(int c = 0; c < SquaresUnder; ++c)
1657 if(Slot[c])
1659 lsquare* Square = GetLSquareUnder(c);
1660 Square->SendMemorizedUpdateRequest();
1665 truth item::AddStateDescription (festring& Name, truth Articled) const {
1666 truth res = false;
1667 if (Spoils()) {
1668 if ((ItemFlags&(HASTE|SLOW)) && Articled) { res = true; Name << "a "; }
1669 if (ItemFlags&HASTE) { res = true; Name << "hasted "; }
1670 if (ItemFlags&SLOW) { res = true; Name << "slowed "; }
1672 return res;
1676 truth item::Burn (character *who, v2 where, int dir) {
1677 //who->EditExperience(PERCEPTION, 150, 1 << 10);
1678 where += game::GetMoveVector(dir);
1679 area *ca = who->GetSquareUnder()->GetArea();
1680 if (where.X < 0 || where.Y < 0 || where.X >= ca->GetXSize() || where.Y >= ca->GetYSize()) return false;
1681 lsquare *sq = static_cast<lsquare *>(ca->GetSquare(where.X, where.Y));
1682 if (sq) {
1683 sq->ReceiveTrapDamage(who, 50, FIRE, dir);
1684 return true;
1686 return false;