finished adding content from CLIVAN; not tested yet
[k8-i-v-a-n.git] / src / game / item.cpp
blob4f0201ec88558cccd846ce22fcf96dddb51e0114
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)
60 truth item::IsOnGround () const { return Slot[0]->IsOnGround(); }
61 truth item::IsSimiliarTo (item *Item) const { return Item->GetType() == GetType() && Item->GetConfig() == GetConfig(); }
62 double item::GetBaseDamage () const { return sqrt(5e-5*GetWeaponStrength())+GetDamageBonus(); }
63 int item::GetBaseMinDamage () const { return int(GetBaseDamage()*0.75); }
64 int item::GetBaseMaxDamage () const { return int(GetBaseDamage()*1.25)+1; }
65 int item::GetBaseToHitValue () const { return int(10000.0/(1000+GetWeight())+GetTHVBonus()); }
66 int item::GetBaseBlockValue () const { return int((10000.0/(1000+GetWeight())+GetTHVBonus())*GetBlockModifier()/10000.0); }
67 truth item::IsInCorrectSlot (int I) const { return I == RIGHT_WIELDED_INDEX || I == LEFT_WIELDED_INDEX; }
68 truth item::IsInCorrectSlot () const { return IsInCorrectSlot(static_cast<gearslot *>(*Slot)->GetEquipmentIndex()); }
69 int item::GetEquipmentIndex () const { return static_cast<gearslot *>(*Slot)->GetEquipmentIndex(); }
70 int item::GetGraphicsContainerIndex () const { return GR_ITEM; }
71 truth item::IsBroken () const { return GetConfig () & BROKEN; }
72 truth item::IsFood() const { return DataBase->Category & FOOD; }
73 cchar *item::GetBreakVerb () const { return "breaks"; }
74 square *item::GetSquareUnderEntity (int I) const { return GetSquareUnder(I); }
75 square *item::GetSquareUnder (int I) const { return Slot[I] ? Slot[I]->GetSquareUnder () : 0; }
76 lsquare *item::GetLSquareUnder (int I) const { return static_cast<lsquare *>(Slot[I]->GetSquareUnder ()); }
77 void item::SignalStackAdd (stackslot *StackSlot, void (stack::*)(item*, truth)) { Slot[0] = StackSlot; }
78 truth item::IsAnimated () const { return GraphicData.AnimationFrames > 1 || (Fluid && ShowFluids ()); }
79 truth item::IsRusted () const { return MainMaterial->GetRustLevel () != NOT_RUSTED; }
80 truth item::IsEatable (ccharacter *Eater) const { return GetConsumeMaterial(Eater, &material::IsSolid) && IsConsumable (); }
81 truth item::IsDrinkable (ccharacter *Eater) const { return GetConsumeMaterial(Eater, &material::IsLiquid) && IsConsumable (); }
82 pixelpredicate item::GetFluidPixelAllowedPredicate () const { return &rawbitmap::IsTransparent; }
83 void item::Cannibalize () { Flags |= CANNIBALIZED; }
84 void item::SetMainMaterial (material *NewMaterial, int SpecialFlags) { SetMaterial(MainMaterial, NewMaterial, GetDefaultMainVolume (), SpecialFlags); }
85 void item::ChangeMainMaterial (material *NewMaterial, int SpecialFlags) { ChangeMaterial(MainMaterial, NewMaterial, GetDefaultMainVolume (), SpecialFlags); }
86 void item::InitMaterials (const materialscript *M, const materialscript *, truth CUP) { InitMaterials(M->Instantiate (), CUP); }
87 int item::GetMainMaterialRustLevel () const { return MainMaterial->GetRustLevel(); }
90 item::item (citem &Item) :
91 object(Item),
92 Slot(0),
93 Size(Item.Size),
94 DataBase(Item.DataBase),
95 Volume(Item.Volume),
96 Weight(Item.Weight),
97 Fluid(0),
98 FluidCount(0),
99 SquaresUnder(Item.SquaresUnder),
100 LifeExpectancy(Item.LifeExpectancy),
101 ItemFlags(Item.ItemFlags)
103 Flags &= ENTITY_FLAGS|SQUARE_POSITION_BITS;
104 ID = game::CreateNewItemID(this);
105 CloneMotherID = new idholder(Item.ID);
106 idholder* TI = CloneMotherID;
108 for (idholder* II = Item.CloneMotherID; II; II = II->Next) TI = TI->Next = new idholder(II->ID);
109 TI->Next = 0;
110 Slot = new slot*[SquaresUnder];
111 for (int c = 0; c < SquaresUnder; ++c) Slot[c] = 0;
115 item::~item () {
116 delete [] Slot;
117 game::RemoveItemID(ID);
118 fluid **FP = Fluid;
119 if (FP) {
120 for (int c = 0; c < /*SquaresUnder*/FluidCount; ++c) {
121 for (fluid* F = FP[c]; F;) {
122 fluid *ToDel = F;
124 F = F->Next;
125 delete ToDel;
128 delete [] FP;
133 void item::Fly (character *Thrower, int Direction, int Force) {
134 int Range = Force*25/Max(sLong(sqrt(GetWeight())), 1);
135 lsquare *LSquareUnder = GetLSquareUnder();
136 RemoveFromSlot();
137 LSquareUnder->GetStack()->AddItem(this, false);
138 if (!Range || GetSquaresUnder() != 1) {
139 if (GetLSquareUnder()->GetRoom()) GetLSquareUnder()->GetRoom()->AddItemEffect(this);
140 return;
142 if (Direction == RANDOM_DIR) Direction = RAND()&7;
143 v2 StartingPos = GetPos();
144 v2 Pos = StartingPos;
145 v2 DirVector = game::GetMoveVector(Direction);
146 truth Breaks = false;
147 double BaseDamage, BaseToHitValue;
148 /*** check ***/
149 if (Thrower) {
150 int Bonus = Thrower->IsHumanoid() ? Thrower->GetCWeaponSkill(GetWeaponCategory())->GetBonus() : 1000;
151 BaseDamage = sqrt(5e-12 * GetWeaponStrength() * Force / Range) * Bonus;
152 BaseToHitValue = 10 * Bonus * Thrower->GetMoveEase() / (500 + GetWeight()) * Thrower->GetAttribute(DEXTERITY) * sqrt(2.5e-8 * Thrower->GetAttribute(PERCEPTION)) / Range;
153 } else {
154 BaseDamage = sqrt(5e-6 * GetWeaponStrength() * Force / Range);
155 BaseToHitValue = 10 * 100 / (500 + GetWeight()) / Range;
157 int RangeLeft;
158 for (RangeLeft = Range; RangeLeft; --RangeLeft) {
159 if (!GetLevel()->IsValidPos(Pos+DirVector)) break;
160 lsquare *JustHit = GetNearLSquare(Pos+DirVector);
161 if (!JustHit->IsFlyable()) {
162 Breaks = true;
163 JustHit->GetOLTerrain()->HasBeenHitByItem(Thrower, this, int(BaseDamage*sqrt(RangeLeft)));
164 break;
165 } else {
166 clock_t StartTime = clock();
167 Pos += DirVector;
168 RemoveFromSlot();
169 JustHit->GetStack()->AddItem(this, false);
170 truth Draw = game::OnScreen(JustHit->GetPos()) && JustHit->CanBeSeenByPlayer();
171 if (Draw) game::DrawEverything();
172 if (JustHit->GetCharacter()) {
173 int Damage = int(BaseDamage * sqrt(RangeLeft));
174 double ToHitValue = BaseToHitValue*RangeLeft;
175 int Returned = HitCharacter(Thrower, JustHit->GetCharacter(), Damage, ToHitValue, Direction);
176 if (Returned == HIT) Breaks = true;
177 if (Returned != MISSED) break;
179 if (Draw) { while (clock()-StartTime < 0.03 * CLOCKS_PER_SEC); } //FIXME
182 if (Breaks) ReceiveDamage(Thrower, int(sqrt(GetWeight()*RangeLeft) / 10), THROW|PHYSICAL_DAMAGE, Direction);
183 if (Exists() && GetLSquareUnder()->GetRoom()) GetLSquareUnder()->GetRoom()->AddItemEffect(this);
187 int item::HitCharacter (character *Thrower, character *Dude, int Damage, double ToHitValue, int Direction) {
188 if (Dude->Catches(this)) return CATCHED;
189 if (Thrower && !EffectIsGood()) Thrower->Hostility(Dude);
190 if (Dude->DodgesFlyingItem(this, ToHitValue)) {
191 if (Dude->IsPlayer()) ADD_MESSAGE("%s misses you.", CHAR_NAME(DEFINITE));
192 else if (Dude->CanBeSeenByPlayer()) ADD_MESSAGE("%s misses %s.", CHAR_NAME(DEFINITE), Dude->CHAR_NAME(DEFINITE));
193 return MISSED;
195 Dude->HasBeenHitByItem(Thrower, this, Damage, ToHitValue, Direction);
196 return HIT;
200 double item::GetWeaponStrength () const {
201 return GetFormModifier() * GetMainMaterial()->GetStrengthValue() * sqrt(GetMainMaterial()->GetWeight());
205 int item::GetStrengthRequirement () const {
206 double WeightTimesSize = GetWeight() * GetSize();
207 return int(1.25e-10 * WeightTimesSize * WeightTimesSize);
211 truth item::Apply (character *Applier) {
212 if (Applier->IsPlayer()) ADD_MESSAGE("You can't apply this!");
213 return false;
217 /* Returns truth that tells whether the Polymorph really happened */
218 truth item::Polymorph (character *Polymorpher, stack *CurrentStack) {
219 if (!IsPolymorphable()) return false;
220 if (Polymorpher && IsOnGround()) {
221 room *Room = GetRoom();
222 if (Room) Room->HostileAction(Polymorpher);
224 if (GetSquarePosition() != CENTER) {
225 stack *Stack = CurrentStack->GetLSquareUnder()->GetStackOfAdjacentSquare(GetSquarePosition());
226 if (Stack) CurrentStack = Stack;
228 CurrentStack->AddItem(protosystem::BalancedCreateItem(0, MAX_PRICE, ANY_CATEGORY, 0, 0, 0, true));
229 RemoveFromSlot();
230 SendToHell();
231 return true;
235 /* Returns whether the Eater must stop eating the item */
236 truth item::Consume (character *Eater, sLong Amount) {
237 material *ConsumeMaterial = GetConsumeMaterial(Eater);
238 if (!ConsumeMaterial) return true;
239 if (Eater->IsPlayer() && !(Flags & CANNIBALIZED) && Eater->CheckCannibalism(ConsumeMaterial)) {
240 game::DoEvilDeed(25);
241 ADD_MESSAGE("You feel that this was an evil deed.");
242 Cannibalize();
244 feuLong ID = GetID();
245 material *Garbage = ConsumeMaterial->EatEffect(Eater, Amount);
246 item *NewConsuming = GetID() ? this : game::SearchItem(ID);
247 material *NewConsumeMaterial = NewConsuming->GetConsumeMaterial(Eater);
248 if (!NewConsuming->Exists() || !NewConsumeMaterial || !NewConsumeMaterial->IsSameAs(ConsumeMaterial))
249 ConsumeMaterial->FinishConsuming(Eater);
250 delete Garbage;
251 return !NewConsuming->Exists() || !NewConsumeMaterial;
255 truth item::CanBeEatenByAI (ccharacter *Eater) const {
256 material *ConsumeMaterial = GetConsumeMaterial(Eater);
257 return (!Eater->IsPet()
258 || !(Eater->GetCommandFlags() & DONT_CONSUME_ANYTHING_VALUABLE)
259 || !IsValuable())
260 && IsConsumable()
261 && ConsumeMaterial && ConsumeMaterial->CanBeEatenByAI(Eater);
265 void item::Save (outputfile &SaveFile) const {
266 SaveFile << (uShort)GetType();
267 object::Save(SaveFile);
268 SaveFile << (uShort)0;
269 SaveFile << mIsStepedOn;
270 SaveFile << (uShort)GetConfig();
271 SaveFile << (uShort)Flags;
272 SaveFile << Size << ID << LifeExpectancy << ItemFlags;
273 SaveLinkedList(SaveFile, CloneMotherID);
274 if (Fluid) {
275 SaveFile.Put(true);
276 SaveFile << FluidCount;
277 for (int c = 0; c < /*SquaresUnder*/FluidCount; ++c) SaveLinkedList(SaveFile, Fluid[c]);
278 } else {
279 SaveFile.Put(false);
284 void item::Load (inputfile &SaveFile) {
285 object::Load(SaveFile);
286 int ver = ReadType<uShort>(SaveFile);
287 if (ver != 0) ABORT("invalid item version in savefile: %d", ver);
288 SaveFile >> mIsStepedOn;
289 databasecreator<item>::InstallDataBase(this, ReadType<uShort>(SaveFile));
290 Flags |= ReadType<uShort>(SaveFile) & ~ENTITY_FLAGS;
291 SaveFile >> Size >> ID >> LifeExpectancy >> ItemFlags;
292 LoadLinkedList(SaveFile, CloneMotherID);
293 if (LifeExpectancy) Enable();
294 game::AddItemID(this, ID);
295 if (SaveFile.Get()) {
296 SaveFile >> FluidCount;
297 Fluid = new fluid*[/*SquaresUnder*/FluidCount];
298 for (int c = 0; c < /*SquaresUnder*/FluidCount; ++c) {
299 LoadLinkedList(SaveFile, Fluid[c]);
300 for (fluid *F = Fluid[c]; F; F = F->Next) F->SetMotherItem(this);
302 } else {
303 if (Fluid) Fluid = 0; //FIXME: MEMORY LEAK
304 FluidCount = 0;
309 void item::TeleportRandomly () {
310 if (GetSquaresUnder() == 1) {
311 // gum solution
312 lsquare *Square = GetNearLSquare(GetLevel()->GetRandomSquare());
313 MoveTo(Square->GetStack());
314 if (Square->CanBeSeenByPlayer()) ADD_MESSAGE("Suddenly %s appears!", CHAR_NAME(INDEFINITE));
319 int item::GetStrengthValue () const {
320 return sLong(GetStrengthModifier()) * GetMainMaterial()->GetStrengthValue() / 2000;
324 void item::RemoveFromSlot () {
325 for (int c = 0; c < SquaresUnder; ++c) {
326 if (Slot[c]) {
327 try {
328 Slot[c]->Empty();
329 } catch (quitrequest) {
330 SendToHell();
331 throw;
333 Slot[c] = 0;
339 void item::MoveTo (stack *Stack) {
340 RemoveFromSlot();
341 Stack->AddItem(this);
345 cchar *item::GetItemCategoryName (sLong Category) {
346 // convert to array
347 switch (Category) {
348 case HELMET: return "Helmets";
349 case AMULET: return "Amulets";
350 case CLOAK: return "Cloaks";
351 case BODY_ARMOR: return "Body armors";
352 case WEAPON: return "Weapons";
353 case SHIELD: return "Shields";
354 case RING: return "Rings";
355 case GAUNTLET: return "Gauntlets";
356 case BELT: return "Belts";
357 case BOOT: return "Boots";
358 case FOOD: return "Food";
359 case POTION: return "Potions";
360 case SCROLL: return "Scrolls";
361 case BOOK: return "Books";
362 case WAND: return "Wands";
363 case TOOL: return "Tools";
364 case VALUABLE: return "Valuables";
365 case MISC: return "Miscellaneous items";
367 return "Warezzz";
371 int item::GetResistance (int Type) const {
372 switch (Type&0xFFF) {
373 case PHYSICAL_DAMAGE: return GetStrengthValue();
374 case SOUND:
375 case ENERGY:
376 case DRAIN:
377 case MUSTARD_GAS_DAMAGE:
378 return 0;
379 case FIRE: return GetFireResistance();
380 case POISON: return GetPoisonResistance();
381 case ELECTRICITY: return GetElectricityResistance();
382 case ACID: return GetAcidResistance();
384 ABORT("Resistance lack detected!");
385 return 0;
389 truth item::Open (character *Char) {
390 if (Char->IsPlayer()) ADD_MESSAGE("You can't open %s.", CHAR_NAME(DEFINITE));
391 return false;
395 item *itemprototype::SpawnAndLoad (inputfile &SaveFile) const {
396 item *Item = Spawner(0, LOAD);
397 Item->Load(SaveFile);
398 Item->CalculateAll();
399 return Item;
403 void item::LoadDataBaseStats () {
404 SetSize(GetDefaultSize());
408 void item::Initialize (int NewConfig, int SpecialFlags) {
409 CalculateSquaresUnder();
410 Slot = new slot*[SquaresUnder];
411 for (int c = 0; c < SquaresUnder; ++c) Slot[c] = 0;
412 if (!(SpecialFlags & LOAD)) {
413 ID = game::CreateNewItemID(this);
414 databasecreator<item>::InstallDataBase(this, NewConfig);
415 LoadDataBaseStats();
416 RandomizeVisualEffects();
417 Flags |= CENTER << SQUARE_POSITION_SHIFT;
418 if (!(SpecialFlags & NO_MATERIALS)) GenerateMaterials();
420 if (!(SpecialFlags & LOAD)) PostConstruct();
421 if (!(SpecialFlags & (LOAD|NO_MATERIALS))) {
422 CalculateAll();
423 if (!(SpecialFlags & NO_PIC_UPDATE)) UpdatePictures();
428 truth item::ShowMaterial () const {
429 if (GetMainMaterialConfig().Size == 1) return GetMainMaterial()->GetConfig() != GetMainMaterialConfig()[0];
430 //FIXME: gum solution
431 if (isBone()) {
432 // never show the material for 'bone bone'
433 if (GetMainMaterial()->GetConfig() == BONE) return false;
435 return true;
439 sLong item::GetBlockModifier() const
441 if(!IsShield(0))
442 return GetSize() * GetRoundness() << 1;
443 else
444 return GetSize() * GetRoundness() << 2;
447 truth item::CanBeSeenByPlayer() const
449 return CanBeSeenBy(PLAYER);
452 truth item::CanBeSeenBy(ccharacter* Who) const
454 for(int c = 0; c < SquaresUnder; ++c)
455 if(Slot[c] && Slot[c]->CanBeSeenBy(Who))
456 return true;
458 return Who->IsPlayer() && game::GetSeeWholeMapCheatMode();
461 festring item::GetDescription(int Case) const
463 if(CanBeSeenByPlayer())
464 return GetName(Case);
465 else
466 return CONST_S("something");
469 void item::SignalVolumeAndWeightChange()
471 CalculateVolumeAndWeight();
473 for(int c = 0; c < SquaresUnder; ++c)
474 if(Slot[c])
475 Slot[c]->SignalVolumeAndWeightChange();
478 void item::CalculateVolumeAndWeight()
480 Volume = Weight = 0;
482 for(int c = 0; c < GetMaterials(); ++c)
484 cmaterial* Material = GetMaterial(c);
486 if(Material)
488 Volume += Material->GetVolume();
489 Weight += Material->GetWeight();
494 void item::SignalEmitationIncrease(col24 EmitationUpdate)
496 if(game::CompareLights(EmitationUpdate, Emitation) > 0)
498 game::CombineLights(Emitation, EmitationUpdate);
500 for(int c = 0; c < SquaresUnder; ++c)
501 if(Slot[c])
502 Slot[c]->SignalEmitationIncrease(EmitationUpdate);
506 void item::SignalEmitationDecrease(col24 EmitationUpdate)
508 if(game::CompareLights(EmitationUpdate, Emitation) >= 0 && Emitation)
510 col24 Backup = Emitation;
511 CalculateEmitation();
513 if(Backup != Emitation)
514 for(int c = 0; c < SquaresUnder; ++c)
515 if(Slot[c])
516 Slot[c]->SignalEmitationDecrease(EmitationUpdate);
520 void item::CalculateAll()
522 CalculateVolumeAndWeight();
523 CalculateEmitation();
526 /* Temporary and buggy. */
528 void item::WeaponSkillHit(int Hits)
530 if(Slot[0] && Slot[0]->IsGearSlot())
531 static_cast<arm*>(static_cast<gearslot*>(*Slot)->GetBodyPart())->WieldedSkillHit(Hits);
534 /* Returns 0 if item cannot be cloned */
536 item* item::Duplicate(feuLong Flags)
538 if(!(Flags & IGNORE_PROHIBITIONS)
539 && ((!(Flags & MIRROR_IMAGE) && !CanBeCloned())
540 || (Flags & MIRROR_IMAGE && (!CanBeMirrored()
541 || (MainMaterial
542 && !(MainMaterial->GetCommonFlags() & CAN_BE_MIRRORED))
543 || (GetSecondaryMaterial()
544 && !(GetSecondaryMaterial()->GetCommonFlags() & CAN_BE_MIRRORED))))))
545 return 0;
547 item* Clone = GetProtoType()->Clone(this);
549 if(Flags & MIRROR_IMAGE)
550 Clone->SetLifeExpectancy(Flags >> LE_BASE_SHIFT & LE_BASE_RANGE,
551 Flags >> LE_RAND_SHIFT & LE_RAND_RANGE);
553 idholder* I = new idholder(ID);
554 I->Next = CloneMotherID;
555 CloneMotherID = I;
556 game::RemoveItemID(ID);
557 ID = game::CreateNewItemID(this);
558 Clone->UpdatePictures();
559 return Clone;
562 void item::AddInventoryEntry(ccharacter*, festring& Entry, int Amount, truth ShowSpecialInfo) const
564 if(Amount == 1)
565 AddName(Entry, INDEFINITE);
566 else
568 Entry << Amount << ' ';
569 AddName(Entry, PLURAL);
572 if(ShowSpecialInfo)
573 Entry << " [" << GetWeight() * Amount << "g]";
576 const itemdatabase* itemprototype::ChooseBaseForConfig(itemdatabase** TempConfig, int Configs, int ConfigNumber)
578 if(!(ConfigNumber & BROKEN))
579 return *TempConfig;
580 else
582 ConfigNumber ^= BROKEN;
584 for(int c = 0; c < Configs; ++c)
585 if(TempConfig[c]->Config == ConfigNumber)
586 return TempConfig[c];
588 return *TempConfig;
592 truth item::ReceiveDamage(character* Damager, int Damage, int Type, int Dir)
594 if(CanBeBroken() && !IsBroken() && Type & (PHYSICAL_DAMAGE|SOUND|ENERGY|ACID))
596 int StrengthValue = GetStrengthValue();
598 if(!StrengthValue)
599 StrengthValue = 1;
601 if(Damage > StrengthValue << 2 && RAND() & 3 && RAND() % (25 * Damage / StrengthValue) >= 100)
603 Break(Damager, Dir);
604 return true;
608 if(Type & ACID && IsBroken() && IsDestroyable(Damager))
610 int StrengthValue = GetStrengthValue();
612 if(!StrengthValue)
613 StrengthValue = 1;
615 if(Damage > StrengthValue << 4 && !(RAND() & 3) && RAND() % (100 * Damage / StrengthValue) >= 100)
617 Destroy(Damager, Dir);
618 return true;
622 return false;
625 void itemdatabase::InitDefaults(const itemprototype* NewProtoType, int NewConfig)
627 IsAbstract = false;
628 ProtoType = NewProtoType;
629 Config = NewConfig;
631 if(NewConfig & BROKEN)
633 if(Adjective.GetSize())
634 Adjective.Insert(0, "broken ");
635 else
636 Adjective = CONST_S("broken");
638 DefaultSize >>= 1;
639 FormModifier >>= 2;
640 StrengthModifier >>= 1;
644 sLong item::GetNutritionValue() const
646 sLong NV = 0;
648 for(int c = 0; c < GetMaterials(); ++c)
649 if(GetMaterial(c))
650 NV += GetMaterial(c)->GetTotalNutritionValue();
652 return NV;
655 void item::SignalSpoil(material*)
657 if(!Exists())
658 return;
660 if(CanBeSeenByPlayer())
661 ADD_MESSAGE("%s spoils completely.", GetExtendedDescription().CStr());
663 truth Equipped = PLAYER->Equips(this);
664 Disappear();
666 if(Equipped)
667 game::AskForEscPress(CONST_S("Equipment destroyed!"));
670 item* item::DuplicateToStack(stack* CurrentStack, feuLong Flags)
672 item* Duplicated = Duplicate(Flags);
674 if(!Duplicated)
675 return 0;
677 CurrentStack->AddItem(Duplicated);
678 return Duplicated;
681 truth item::CanBePiledWith(citem* Item, ccharacter* Viewer) const
683 return (GetType() == Item->GetType()
684 && GetConfig() == Item->GetConfig()
685 && ItemFlags == Item->ItemFlags
686 && (WeightIsIrrelevant() || Weight == Item->Weight)
687 && MainMaterial->IsSameAs(Item->MainMaterial)
688 && MainMaterial->GetSpoilLevel() == Item->MainMaterial->GetSpoilLevel()
689 && MainMaterial->GetRustLevel() == Item->MainMaterial->GetRustLevel()
690 && Viewer->GetCWeaponSkillLevel(this) == Viewer->GetCWeaponSkillLevel(Item)
691 && Viewer->GetSWeaponSkillLevel(this) == Viewer->GetSWeaponSkillLevel(Item)
692 && !Fluid && !Item->Fluid
693 && !LifeExpectancy == !Item->LifeExpectancy);
696 void item::Break(character* Breaker, int)
698 if(CanBeSeenByPlayer())
699 ADD_MESSAGE("%s %s.", GetExtendedDescription().CStr(), GetBreakVerb());
701 if(Breaker && IsOnGround())
703 room* Room = GetRoom();
705 if(Room)
706 Room->HostileAction(Breaker);
709 item* Broken = GetProtoType()->Clone(this);
710 Broken->SetConfig(GetConfig() | BROKEN);
711 Broken->SetSize(Broken->GetSize() >> 1);
712 DonateFluidsTo(Broken);
713 DonateIDTo(Broken);
714 DonateSlotTo(Broken);
715 SendToHell();
717 if(PLAYER->Equips(Broken))
718 game::AskForEscPress(CONST_S("Equipment broken!"));
721 void item::Be()
723 MainMaterial->Be(ItemFlags);
725 if (Exists() && LifeExpectancy) {
726 if (LifeExpectancy == 1) {
727 if (CanBeSeenByPlayer()) ADD_MESSAGE("%s disappears.", GetExtendedDescription().CStr());
728 truth Equipped = PLAYER->Equips(this);
729 Disappear();
730 if (Equipped) game::AskForEscPress(CONST_S("Equipment destroyed!"));
731 } else {
732 --LifeExpectancy;
737 int item::GetOfferValue(int Receiver) const
739 /* Temporary */
741 int OfferValue = int(sqrt(GetTruePrice()));
743 if(Receiver == GetAttachedGod())
744 OfferValue <<= 1;
745 else
746 OfferValue >>= 1;
748 return OfferValue;
751 void item::SignalEnchantmentChange()
753 for(int c = 0; c < SquaresUnder; ++c)
754 if(Slot[c])
755 Slot[c]->SignalEnchantmentChange();
758 sLong item::GetEnchantedPrice(int Enchantment) const
760 return !PriceIsProportionalToEnchantment() ? item::GetPrice() : Max<int>(item::GetPrice() * Enchantment * Enchantment, 0);
763 item* item::Fix()
765 item* Fixed = this;
767 if(IsBroken())
769 Fixed = GetProtoType()->Clone(this);
770 Fixed->SetConfig(GetConfig() ^ BROKEN);
771 Fixed->SetSize(Fixed->GetSize() << 1);
772 DonateFluidsTo(Fixed);
773 DonateIDTo(Fixed);
774 DonateSlotTo(Fixed);
775 SendToHell();
778 return Fixed;
781 void item::DonateSlotTo(item* Item)
783 if(Slot[0])
785 Slot[0]->DonateTo(Item);
786 Slot[0] = 0;
788 for(int c = 1; c < SquaresUnder; ++c)
789 if(Slot[c])
791 Slot[c]->Empty();
792 Slot[c] = 0;
797 int item::GetSpoilLevel() const
799 return MainMaterial->GetSpoilLevel();
802 void item::SignalSpoilLevelChange(material*)
804 if(!IsAnimated() && GetSpoilLevel() && Slot[0] && Slot[0]->IsVisible())
805 for(int c = 0; c < SquaresUnder; ++c)
806 GetSquareUnder(c)->IncStaticAnimatedEntities();
808 SignalVolumeAndWeightChange(); // gum
809 UpdatePictures();
812 truth item::AllowSpoil() const
814 if(IsOnGround())
816 lsquare* Square = GetLSquareUnder();
817 int RoomNumber = Square->GetRoomIndex();
818 return !RoomNumber || Square->GetLevel()->GetRoom(RoomNumber)->AllowSpoil(this);
820 else
821 return true;
824 void item::ResetSpoiling()
826 for(int c = 0; c < GetMaterials(); ++c)
827 if(GetMaterial(c))
828 GetMaterial(c)->ResetSpoiling();
831 cchar* item::GetBaseToHitValueDescription() const
833 if(GetBaseToHitValue() < 10)
834 return ToHitValueDescription[Min(GetBaseToHitValue(), 6)];
835 else
836 return ToHitValueDescription[7];
839 cchar* item::GetBaseBlockValueDescription() const
841 if(GetBaseBlockValue() < 20)
842 return ToHitValueDescription[Min(GetBaseBlockValue() >> 1, 6)];
843 else
844 return ToHitValueDescription[7];
847 cchar* item::GetStrengthValueDescription() const
849 int SV = GetStrengthValue();
851 if(SV < 3)
852 return StrengthValueDescription[0];
853 else if(SV < 5)
854 return StrengthValueDescription[1];
855 else if(SV < 8)
856 return StrengthValueDescription[2];
857 else if(SV < 11)
858 return StrengthValueDescription[3];
859 else if(SV < 16)
860 return StrengthValueDescription[4];
861 else if(SV < 20)
862 return StrengthValueDescription[5];
863 else
864 return StrengthValueDescription[6];
867 void item::SpecialGenerationHandler()
869 if(HandleInPairs())
870 Slot[0]->AddFriendItem(Duplicate());
873 void item::SortAllItems(const sortdata& SortData) const
875 if(SortData.Sorter == 0 || (this->*SortData.Sorter)(SortData.Character))
876 SortData.AllItems.push_back(const_cast<item*>(this));
879 int item::GetAttachedGod() const
881 return DataBase->AttachedGod ? DataBase->AttachedGod : MainMaterial->GetAttachedGod();
884 sLong item::GetMaterialPrice() const
886 return MainMaterial->GetRawPrice();
889 sLong item::GetTruePrice() const
891 if(LifeExpectancy)
892 return 0;
894 sLong Price = Max(GetPrice(), GetMaterialPrice());
896 if(Spoils())
897 Price = Price * (100 - GetMaxSpoilPercentage()) / 500;
899 return Price;
902 #ifdef WIZARD
904 void item::AddAttackInfo(felist& List) const
906 festring Entry(40, ' ');
907 Entry << int(GetWeight());
908 Entry.Resize(50);
909 Entry << int(GetSize());
910 Entry.Resize(60);
911 Entry << int(GetStrengthRequirement());
912 Entry.Resize(70);
913 Entry << GetBaseMinDamage() << '-' << GetBaseMaxDamage();
914 List.AddEntry(Entry, LIGHT_GRAY);
917 void item::AddMiscellaneousInfo(felist& List) const
919 festring Entry(40, ' ');
920 Entry << int(GetTruePrice());
921 Entry.Resize(55);
922 Entry << GetOfferValue(0);
923 Entry.Resize(70);
924 Entry << int(GetNutritionValue());
925 List.AddEntry(Entry, LIGHT_GRAY);
928 #endif
931 void item::PreProcessForBone () {
932 if (IsQuestItem()) {
933 RemoveFromSlot();
934 SendToHell();
935 } else {
936 game::RemoveItemID(ID);
937 ID = -ID;
938 game::AddItemID(this, ID);
939 SetSteppedOn(false);
944 void item::PostProcessForBone () {
945 boneidmap::iterator BI = game::GetBoneItemIDMap().find(-ID);
946 game::RemoveItemID(ID);
948 if (BI == game::GetBoneItemIDMap().end()) {
949 feuLong NewID = game::CreateNewItemID(this);
950 game::GetBoneItemIDMap().insert(std::make_pair(-ID, NewID));
951 ID = NewID;
952 } else {
953 if (game::SearchItem(BI->second)) {
954 ID = BI->second;
955 game::AddItemID(this, ID);
958 for (idholder* I = CloneMotherID; I; I = I->Next) {
959 BI = game::GetBoneItemIDMap().find(I->ID);
960 if (BI == game::GetBoneItemIDMap().end()) {
961 feuLong NewCloneMotherID = game::CreateNewItemID(0);
962 game::GetBoneItemIDMap().insert(std::make_pair(I->ID, NewCloneMotherID));
963 I->ID = NewCloneMotherID;
964 } else {
965 I->ID = BI->second;
971 void item::SetConfig(int NewConfig, int SpecialFlags)
973 databasecreator<item>::InstallDataBase(this, NewConfig);
974 CalculateAll();
976 if(!(SpecialFlags & NO_PIC_UPDATE))
977 UpdatePictures();
980 god* item::GetMasterGod() const
982 return game::GetGod(GetConfig());
985 int itemprototype::CreateSpecialConfigurations(itemdatabase** TempConfig, int Configs, int Level)
987 if(Level)
988 return Configs;
990 if(TempConfig[0]->CreateDivineConfigurations)
991 Configs = databasecreator<item>::CreateDivineConfigurations(this, TempConfig, Configs);
993 /* Gum solution */
995 if(TempConfig[0]->CreateLockConfigurations)
997 const item::database*const* KeyConfigData = key::ProtoType.GetConfigData();
998 int KeyConfigSize = key::ProtoType.GetConfigSize();
999 int OldConfigs = Configs;
1001 for(int c1 = 0; c1 < OldConfigs; ++c1)
1002 if(!TempConfig[c1]->IsAbstract)
1004 int BaseConfig = TempConfig[c1]->Config;
1005 int NewConfig = BaseConfig | BROKEN_LOCK;
1006 itemdatabase* ConfigDataBase = new itemdatabase(*TempConfig[c1]);
1007 ConfigDataBase->InitDefaults(this, NewConfig);
1008 ConfigDataBase->PostFix << "with a broken lock";
1009 ConfigDataBase->Possibility = 0;
1010 TempConfig[Configs++] = ConfigDataBase;
1012 for(int c2 = 0; c2 < KeyConfigSize; ++c2)
1014 NewConfig = BaseConfig | KeyConfigData[c2]->Config;
1015 ConfigDataBase = new itemdatabase(*TempConfig[c1]);
1016 ConfigDataBase->InitDefaults(this, NewConfig);
1017 ConfigDataBase->PostFix << "with ";
1019 if(KeyConfigData[c2]->UsesLongAdjectiveArticle)
1020 ConfigDataBase->PostFix << "an ";
1021 else
1022 ConfigDataBase->PostFix << "a ";
1024 ConfigDataBase->PostFix << KeyConfigData[c2]->Adjective << " lock";
1025 ConfigDataBase->Possibility = 0;
1026 TempConfig[Configs++] = ConfigDataBase;
1031 return Configs;
1034 void item::Draw(blitdata& BlitData) const
1036 cint AF = GraphicData.AnimationFrames;
1037 cint F = !(BlitData.CustomData & ALLOW_ANIMATE) || AF == 1 ? 0 : GET_TICK() & (AF - 1);
1038 cbitmap* P = GraphicData.Picture[F];
1040 if(BlitData.CustomData & ALLOW_ALPHA)
1041 P->AlphaLuminanceBlit(BlitData);
1042 else
1043 P->LuminanceMaskedBlit(BlitData);
1045 if(Fluid && ShowFluids())
1046 DrawFluids(BlitData);
1049 v2 item::GetLargeBitmapPos(v2 BasePos, int I) const
1051 cint SquareIndex = I ? I / (GraphicData.AnimationFrames >> 2) : 0;
1052 return v2(SquareIndex & 1 ? BasePos.X + 16 : BasePos.X, SquareIndex & 2 ? BasePos.Y + 16 : BasePos.Y);
1055 void item::LargeDraw(blitdata& BlitData) const
1057 cint TrueAF = GraphicData.AnimationFrames >> 2;
1058 cint SquareIndex = BlitData.CustomData & SQUARE_INDEX_MASK;
1059 cint F = !(BlitData.CustomData & ALLOW_ANIMATE) ? SquareIndex * TrueAF : SquareIndex * TrueAF + (GET_TICK() & (TrueAF - 1));
1060 cbitmap* P = GraphicData.Picture[F];
1062 if(BlitData.CustomData & ALLOW_ALPHA)
1063 P->AlphaLuminanceBlit(BlitData);
1064 else
1065 P->LuminanceMaskedBlit(BlitData);
1068 void item::DonateIDTo(item* Item)
1070 game::RemoveItemID(Item->ID);
1071 game::UpdateItemID(Item, ID);
1072 Item->ID = ID;
1073 ID = 0;
1076 void item::SignalRustLevelChange()
1078 SignalVolumeAndWeightChange();
1079 UpdatePictures();
1080 SendNewDrawAndMemorizedUpdateRequest();
1083 const rawbitmap* item::GetRawPicture() const
1085 return igraph::GetRawGraphic(GetGraphicsContainerIndex());
1088 void item::RemoveFluid(fluid *ToBeRemoved) {
1089 truth WasAnimated = IsAnimated();
1090 truth HasFluids = false;
1092 if (Fluid && ToBeRemoved) {
1093 for (int c = 0; c < /*SquaresUnder*/FluidCount; ++c) {
1094 fluid *F = Fluid[c];
1096 if (F == ToBeRemoved) {
1097 Fluid[c] = F->Next;
1098 } else if (F) {
1099 fluid *LF = F;
1101 for (F = F->Next; F; LF = F, F = F->Next) if (F == ToBeRemoved) { LF->Next = F->Next; break; }
1103 if (Fluid[c]) HasFluids = true;
1106 UpdatePictures();
1107 if (!HasFluids && Fluid) {
1108 delete [] Fluid;
1109 Fluid = 0;
1110 if (!IsAnimated() != !WasAnimated && Slot[0]->IsVisible()) GetSquareUnder()->DecStaticAnimatedEntities();
1112 if (ToBeRemoved) SignalEmitationDecrease(ToBeRemoved->GetEmitation());
1113 SignalVolumeAndWeightChange();
1117 void item::RemoveAllFluids () {
1118 if (Fluid) {
1119 for (int c = 0; c < /*SquaresUnder*/FluidCount; ) {
1120 //fprintf(stderr, "c: %d; SquaresUnder: %d\n", c, SquaresUnder);
1121 fluid *F = Fluid[c];
1122 if (F) {
1123 RemoveFluid(F);
1124 c = 0;
1125 if (!Fluid) break;
1126 } else {
1127 ++c;
1134 void item::AddFluid (liquid *ToBeAdded, festring LocationName, int SquareIndex, truth IsInside) {
1135 truth WasAnimated = IsAnimated();
1137 if (SquareIndex < 0) ABORT("item::AddFluid(): invalid SquareIndex: %d", SquareIndex);
1139 if (Fluid) {
1140 fluid *F = Fluid[SquareIndex];
1142 if (SquareIndex >= FluidCount) ABORT("item::AddFluid(): invalid SquareIndex: %d", SquareIndex);
1143 if (!F) {
1144 Fluid[SquareIndex] = new fluid(ToBeAdded, this, LocationName, IsInside);
1145 } else {
1146 fluid *LF;
1148 do {
1149 if (ToBeAdded->IsSameAs(F->GetLiquid())) {
1150 F->AddLiquidAndVolume(ToBeAdded->GetVolume());
1151 delete ToBeAdded;
1152 return;
1154 LF = F;
1155 F = F->Next;
1156 } while(F);
1157 LF->Next = new fluid(ToBeAdded, this, LocationName, IsInside);
1159 } else {
1160 FluidCount = SquaresUnder;
1161 Fluid = new fluid*[/*SquaresUnder*/FluidCount];
1162 if (SquareIndex >= FluidCount) ABORT("item::AddFluid(): invalid SquareIndex: %d", SquareIndex);
1163 memset(Fluid, 0, SquaresUnder*sizeof(fluid *));
1164 Fluid[SquareIndex] = new fluid(ToBeAdded, this, LocationName, IsInside);
1167 UpdatePictures();
1168 SignalVolumeAndWeightChange();
1169 SignalEmitationIncrease(ToBeAdded->GetEmitation());
1171 if (Slot[0]) {
1172 if (!IsAnimated() != !WasAnimated && Slot[0]->IsVisible()) {
1173 GetSquareUnder()->IncStaticAnimatedEntities();
1175 SendNewDrawAndMemorizedUpdateRequest();
1179 void item::SendNewDrawAndMemorizedUpdateRequest() const
1181 if(!game::IsInWilderness())
1182 for(int c = 0; c < SquaresUnder; ++c)
1183 if(Slot[c])
1185 lsquare* Square = GetLSquareUnder(c);
1186 Square->SendNewDrawRequest();
1187 Square->SendMemorizedUpdateRequest();
1191 void item::CalculateEmitation()
1193 object::CalculateEmitation();
1195 if(Fluid)
1196 for(int c = 0; c < /*SquaresUnder*/FluidCount; ++c)
1197 for(const fluid* F = Fluid[c]; F; F = F->Next)
1198 game::CombineLights(Emitation, F->GetEmitation());
1201 void item::FillFluidVector (fluidvector &Vector, int SquareIndex) const {
1202 if (Fluid) {
1203 if (SquareIndex < 0 || SquareIndex >= FluidCount) ABORT("item::FillFluidVector(): invalid SquareIndex: %d\n", SquareIndex);
1204 for (fluid *F = Fluid[SquareIndex]; F; F = F->Next) Vector.push_back(F);
1208 void item::SpillFluid(character*, liquid* Liquid, int SquareIndex)
1210 if(AllowFluids() && Liquid->GetVolume())
1211 AddFluid(Liquid, "", SquareIndex, false);
1212 else
1213 delete Liquid;
1216 void item::TryToRust (sLong LiquidModifier) {
1217 if (MainMaterial->TryToRust(LiquidModifier)) {
1218 if (CanBeSeenByPlayer()) {
1219 if (MainMaterial->GetRustLevel() == NOT_RUSTED) ADD_MESSAGE("%s rusts.", CHAR_NAME(DEFINITE));
1220 else ADD_MESSAGE("%s rusts more.", CHAR_NAME(DEFINITE));
1222 MainMaterial->SetRustLevel(MainMaterial->GetRustLevel() + 1);
1226 void item::CheckFluidGearPictures(v2 ShadowPos, int SpecialFlags, truth BodyArmor)
1228 if(Fluid)
1229 for(fluid* F = Fluid[0]; F; F = F->Next)
1230 F->CheckGearPicture(ShadowPos, SpecialFlags, BodyArmor);
1233 void item::DrawFluidGearPictures(blitdata& BlitData, int SpecialFlags) const
1235 if(Fluid)
1236 for(const fluid* F = Fluid[0]; F; F = F->Next)
1237 F->DrawGearPicture(BlitData, SpecialFlags);
1240 void item::DrawFluidBodyArmorPictures(blitdata& BlitData, int SpecialFlags) const
1242 if(Fluid)
1243 for(const fluid* F = Fluid[0]; F; F = F->Next)
1244 F->DrawBodyArmorPicture(BlitData, SpecialFlags);
1247 void item::DrawFluids(blitdata& BlitData) const
1249 cint SquareIndex = BlitData.CustomData & SQUARE_INDEX_MASK;
1251 for(const fluid* F = Fluid[SquareIndex]; F; F = F->Next)
1252 F->Draw(BlitData);
1255 void item::ReceiveAcid(material*, cfestring&, sLong Modifier)
1257 if(GetMainMaterial()->GetInteractionFlags() & CAN_DISSOLVE)
1259 int Damage = Modifier / 1000;
1261 if(Damage)
1263 Damage += RAND() % Damage;
1264 ReceiveDamage(0, Damage, ACID);
1269 void item::DonateFluidsTo(item* Item)
1271 if(Fluid)
1272 for(int c = 0; c < GetSquaresUnder(); ++c)
1273 for(fluid* F = Fluid[c]; F; F = F->Next)
1275 liquid* Liquid = F->GetLiquid();
1276 Item->AddFluid(Liquid->SpawnMoreLiquid(Liquid->GetVolume()), F->GetLocationName(), c, F->IsInside());
1280 void item::Destroy(character* Destroyer, int)
1282 if(CanBeSeenByPlayer())
1283 ADD_MESSAGE("%s is destroyed.", GetExtendedDescription().CStr());
1285 if(Destroyer && IsOnGround())
1287 room* Room = GetRoom();
1289 if(Room)
1290 Room->HostileAction(Destroyer);
1293 truth Equipped = PLAYER->Equips(this);
1294 RemoveFromSlot();
1295 SendToHell();
1297 if(Equipped)
1298 game::AskForEscPress(CONST_S("Equipment destroyed!"));
1301 void item::RemoveRust()
1303 for(int c = 0; c < GetMaterials(); ++c)
1304 if(GetMaterial(c))
1305 GetMaterial(c)->SetRustLevel(NOT_RUSTED);
1308 void item::SetSpoilPercentage(int Value)
1310 for(int c = 0; c < GetMaterials(); ++c)
1312 material* Material = GetMaterial(c);
1314 if(Material && Material->CanSpoil())
1315 Material->SetSpoilCounter(Material->GetSpoilModifier() * Value / 100);
1319 void item::RedistributeFluids()
1321 if(Fluid)
1322 for(int c = 0; c < GetSquaresUnder(); ++c)
1323 for(fluid* F = Fluid[c]; F; F = F->Next)
1324 F->Redistribute();
1327 material* item::GetConsumeMaterial(ccharacter* Consumer, materialpredicate Predicate) const
1329 return (MainMaterial->*Predicate)() && Consumer->CanConsume(MainMaterial) ? MainMaterial : 0;
1332 /* The parameter can only be MainMaterial */
1334 material* item::RemoveMaterial(material*)
1336 RemoveFromSlot();
1337 SendToHell();
1338 return 0;
1341 void item::InitMaterials(material* FirstMaterial, truth CallUpdatePictures)
1343 InitMaterial(MainMaterial, FirstMaterial, GetDefaultMainVolume());
1344 SignalVolumeAndWeightChange();
1346 if(CallUpdatePictures)
1347 UpdatePictures();
1350 void item::GenerateMaterials()
1352 int Chosen = RandomizeMaterialConfiguration();
1353 const fearray<sLong>& MMC = GetMainMaterialConfig();
1354 InitMaterial(MainMaterial,
1355 MAKE_MATERIAL(MMC.Data[MMC.Size == 1 ? 0 : Chosen]),
1356 GetDefaultMainVolume());
1359 void item::SignalSquarePositionChange(int Position)
1361 Flags &= ~SQUARE_POSITION_BITS;
1362 Flags |= Position << SQUARE_POSITION_SHIFT;
1365 truth item::Read(character* Reader)
1367 Reader->StartReading(this, GetReadDifficulty());
1368 return true;
1371 truth item::CanBeHardened(ccharacter*) const
1373 return MainMaterial->GetHardenedMaterial(this) != NONE;
1376 void item::SetLifeExpectancy(int Base, int RandPlus)
1378 LifeExpectancy = RandPlus > 1 ? Base + RAND_N(RandPlus) : Base;
1379 Enable();
1382 truth item::IsVeryCloseToSpoiling() const
1384 for(int c = 0; c < GetMaterials(); ++c)
1385 if(GetMaterial(c) && !GetMaterial(c)->IsVeryCloseToSpoiling())
1386 return false;
1388 return true;
1391 truth item::IsValuable() const
1393 if(DataBase->IsValuable)
1394 return true;
1396 for(int c = 0; c < GetMaterials(); ++c)
1398 material* M = GetMaterial(c);
1400 if(M && M->GetCommonFlags() & IS_VALUABLE)
1401 return true;
1404 return false;
1407 int item::GetHinderVisibilityBonus(ccharacter* Char) const
1409 int Bonus = 0;
1411 if(GetGearStates() & INFRA_VISION
1412 && !Char->TemporaryStateIsActivated(INFRA_VISION))
1413 Bonus += 20000;
1415 if(GetGearStates() & ESP
1416 && !Char->TemporaryStateIsActivated(ESP))
1417 Bonus += 20000;
1419 if(!game::IsDark(GetEmitation()))
1420 Bonus += 5000;
1422 return Bonus;
1425 sLong item::GetFixPrice() const
1427 item* Clone = GetProtoType()->Clone(this);
1428 Clone = Clone->Fix();
1429 Clone->RemoveRust();
1430 sLong FixPrice = Clone->GetTruePrice();
1431 Clone->SendToHell();
1432 return Max(sLong(3.5 * sqrt(FixPrice)), 10);
1435 void item::AddTrapName(festring& String, int Amount) const
1437 if(Amount == 1)
1438 AddName(String, DEFINITE);
1439 else
1441 String << Amount << ' ';
1442 AddName(String, PLURAL);
1446 truth item::Spoils() const
1448 for(int c = 0; c < GetMaterials(); ++c)
1450 cmaterial* Material = GetMaterial(c);
1452 if(Material && Material->Spoils())
1453 return true;
1456 return false;
1459 int item::GetMaxSpoilPercentage() const
1461 int MaxPercentage = 0;
1463 for(int c = 0; c < GetMaterials(); ++c)
1465 cmaterial* Material = GetMaterial(c);
1467 if(Material)
1468 MaxPercentage = Max(MaxPercentage, Material->GetSpoilPercentage());
1471 return MaxPercentage;
1474 truth item::HasPrice() const
1476 return GetPrice() || GetMaterialPrice();
1479 void item::Disappear()
1481 RemoveFromSlot();
1482 SendToHell();
1485 outputfile& operator<<(outputfile& SaveFile, const idholder* IdHolder)
1487 SaveFile << IdHolder->ID;
1488 return SaveFile;
1491 inputfile& operator>>(inputfile& SaveFile, idholder*& IdHolder)
1493 IdHolder = new idholder(ReadType<feuLong>(SaveFile));
1494 return SaveFile;
1497 festring item::GetExtendedDescription() const
1499 if(!CanBeSeenByPlayer())
1500 return CONST_S("something");
1502 festring Desc;
1503 ccharacter* Carrier = FindCarrier();
1505 if(Carrier)
1507 if(Carrier->IsPlayer())
1509 Desc << "your ";
1510 AddName(Desc, UNARTICLED);
1511 return Desc;
1513 else if(Carrier->CanBeSeenByPlayer())
1515 Carrier->AddName(Desc, DEFINITE);
1516 Desc << "'s ";
1517 AddName(Desc, UNARTICLED);
1518 return Desc;
1522 AddName(Desc, DEFINITE);
1524 if(IsOnGround())
1525 GetLSquareUnder()->AddLocationDescription(Desc);
1527 return Desc;
1530 ccharacter* item::FindCarrier() const
1532 return Slot[0]->FindCarrier();
1535 /* returns 0 if not worn or wielded else the wearer */
1537 const character* item::GetWearer() const
1539 if(!GetSlot()->IsGearSlot())
1540 return 0;
1542 return FindCarrier();
1545 void itemlock::PostConstruct()
1547 /* Terrible gum solution! */
1549 if(!(GetVirtualConfig() & LOCK_BITS))
1551 int NormalLockTypes = 0;
1552 const itemdatabase*const* ConfigData = GetVirtualProtoType()->GetConfigData();
1553 int c, ConfigSize = GetVirtualProtoType()->GetConfigSize();
1555 for(c = 0; c < ConfigSize; ++c)
1556 if(ConfigData[c]->Config & LOCK_BITS
1557 && (ConfigData[c]->Config & ~LOCK_BITS) == GetVirtualConfig()
1558 && !(ConfigData[c]->Config & S_LOCK_ID))
1559 ++NormalLockTypes;
1561 int ChosenLock = RAND() % NormalLockTypes;
1563 for(c = 0; c < ConfigSize; ++c)
1564 if(ConfigData[c]->Config & LOCK_BITS
1565 && (ConfigData[c]->Config & ~LOCK_BITS) == GetVirtualConfig()
1566 && !(ConfigData[c]->Config & S_LOCK_ID)
1567 && !ChosenLock--)
1569 SetVirtualConfig(ConfigData[c]->Config, NO_PIC_UPDATE);
1570 break;
1575 truth itemlock::TryKey(item* Key, character* Applier)
1577 if(GetVirtualConfig() & BROKEN_LOCK)
1579 ADD_MESSAGE("The lock is broken.");
1580 return true;
1583 if(Key->CanOpenLockType(GetVirtualConfig()&LOCK_BITS))
1585 if(Locked)
1587 if(Applier->IsPlayer())
1588 ADD_MESSAGE("You unlock %s.", GetVirtualDescription(DEFINITE).CStr());
1589 else if(Applier->CanBeSeenByPlayer())
1590 ADD_MESSAGE("%s unlocks %s.", Applier->CHAR_NAME(DEFINITE), GetVirtualDescription(DEFINITE).CStr());
1592 else
1594 if(Applier->IsPlayer())
1595 ADD_MESSAGE("You lock %s.", GetVirtualDescription(DEFINITE).CStr());
1596 else if(Applier->CanBeSeenByPlayer())
1597 ADD_MESSAGE("%s locks %s.", Applier->CHAR_NAME(DEFINITE), GetVirtualDescription(DEFINITE).CStr());
1600 Locked = !Locked;
1602 else
1604 if(Applier->IsPlayer())
1605 ADD_MESSAGE("%s doesn't fit in the lock.", Key->CHAR_NAME(DEFINITE));
1606 else if(Applier->CanBeSeenByPlayer())
1607 ADD_MESSAGE("%s tries to fit %s in the lock, but fails.", Applier->CHAR_NAME(DEFINITE), Key->CHAR_NAME(DEFINITE));
1610 return true;
1613 void itemlock::Save(outputfile& SaveFile) const
1615 SaveFile << Locked;
1618 void itemlock::Load(inputfile& SaveFile)
1620 SaveFile >> Locked;
1623 truth item::IsBeverage(ccharacter*) const
1625 for(int c = 0; c < GetMaterials(); ++c)
1627 cmaterial* Material = GetMaterial(c);
1629 if(Material && (Material->GetCategoryFlags() & IS_BEVERAGE))
1630 return true;
1633 return false;
1636 void item::Haste()
1638 ItemFlags |= HASTE;
1639 ItemFlags &= ~SLOW;
1640 SendMemorizedUpdateRequest();
1643 void item::Slow()
1645 ItemFlags |= SLOW;
1646 ItemFlags &= ~HASTE;
1647 SendMemorizedUpdateRequest();
1650 void item::SendMemorizedUpdateRequest() const
1652 if(!game::IsInWilderness())
1653 for(int c = 0; c < SquaresUnder; ++c)
1654 if(Slot[c])
1656 lsquare* Square = GetLSquareUnder(c);
1657 Square->SendMemorizedUpdateRequest();
1661 truth item::AddStateDescription(festring& Name, truth Articled) const
1663 if(!Spoils())
1664 return false;
1666 if((ItemFlags & (HASTE|SLOW)) && Articled)
1667 Name << "a ";
1669 if(ItemFlags & HASTE)
1670 Name << "hasted ";
1672 if(ItemFlags & SLOW)
1673 Name << "slowed ";
1675 return true;
1679 truth item::Burn (character *who, v2 where, int dir) {
1680 //who->EditExperience(PERCEPTION, 150, 1 << 10);
1681 where += game::GetMoveVector(dir);
1682 area *ca = who->GetSquareUnder()->GetArea();
1683 if (where.X < 0 || where.Y < 0 || where.X >= ca->GetXSize() || where.Y >= ca->GetYSize()) return false;
1684 lsquare *sq = static_cast<lsquare *>(ca->GetSquare(where.X, where.Y));
1685 if (sq) {
1686 sq->ReceiveTrapDamage(who, 50, FIRE, dir);
1687 return true;
1689 return false;