cosmetix
[k8-i-v-a-n.git] / src / game / item.cpp
blobfba4dda23e155bcc56622a2ea92d43b678001c03
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, 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;
307 const fearray<festring> &lt = GetLevelTags();
308 if (lt.Size > 1) {
309 fprintf(stderr, "====\n");
310 for (uInt f = 0; f < lt.Size; ++f) fprintf(stderr, " %u: [%s]\n", f, lt[f].CStr());
316 void item::TeleportRandomly () {
317 if (GetSquaresUnder() == 1) {
318 // gum solution
319 lsquare *Square = GetNearLSquare(GetLevel()->GetRandomSquare());
320 MoveTo(Square->GetStack());
321 if (Square->CanBeSeenByPlayer()) ADD_MESSAGE("Suddenly %s appears!", CHAR_NAME(INDEFINITE));
326 int item::GetStrengthValue () const {
327 return sLong(GetStrengthModifier()) * GetMainMaterial()->GetStrengthValue() / 2000;
331 void item::RemoveFromSlot () {
332 for (int c = 0; c < SquaresUnder; ++c) {
333 if (Slot[c]) {
334 try {
335 Slot[c]->Empty();
336 } catch (quitrequest) {
337 SendToHell();
338 throw;
340 Slot[c] = 0;
346 void item::MoveTo (stack *Stack) {
347 RemoveFromSlot();
348 Stack->AddItem(this);
352 cchar *item::GetItemCategoryName (sLong Category) {
353 // convert to array
354 switch (Category) {
355 case HELMET: return "Helmets";
356 case AMULET: return "Amulets";
357 case CLOAK: return "Cloaks";
358 case BODY_ARMOR: return "Body armors";
359 case WEAPON: return "Weapons";
360 case SHIELD: return "Shields";
361 case RING: return "Rings";
362 case GAUNTLET: return "Gauntlets";
363 case BELT: return "Belts";
364 case BOOT: return "Boots";
365 case FOOD: return "Food";
366 case POTION: return "Potions";
367 case SCROLL: return "Scrolls";
368 case BOOK: return "Books";
369 case WAND: return "Wands";
370 case TOOL: return "Tools";
371 case VALUABLE: return "Valuables";
372 case MISC: return "Miscellaneous items";
374 return "Warezzz";
378 int item::GetResistance (int Type) const {
379 switch (Type&0xFFF) {
380 case PHYSICAL_DAMAGE: return GetStrengthValue();
381 case SOUND:
382 case ENERGY:
383 case DRAIN:
384 case MUSTARD_GAS_DAMAGE:
385 return 0;
386 case FIRE: return GetFireResistance();
387 case POISON: return GetPoisonResistance();
388 case ELECTRICITY: return GetElectricityResistance();
389 case ACID: return GetAcidResistance();
391 ABORT("Resistance lack detected!");
392 return 0;
396 truth item::Open (character *Char) {
397 if (Char->IsPlayer()) ADD_MESSAGE("You can't open %s.", CHAR_NAME(DEFINITE));
398 return false;
402 item *itemprototype::SpawnAndLoad (inputfile &SaveFile) const {
403 item *Item = Spawner(0, LOAD);
404 Item->Load(SaveFile);
405 Item->CalculateAll();
406 return Item;
410 void item::LoadDataBaseStats () {
411 SetSize(GetDefaultSize());
415 void item::Initialize (int NewConfig, int SpecialFlags) {
416 CalculateSquaresUnder();
417 Slot = new slot*[SquaresUnder];
418 for (int c = 0; c < SquaresUnder; ++c) Slot[c] = 0;
419 if (!(SpecialFlags & LOAD)) {
420 ID = game::CreateNewItemID(this);
421 databasecreator<item>::InstallDataBase(this, NewConfig);
422 LoadDataBaseStats();
423 RandomizeVisualEffects();
424 Flags |= CENTER << SQUARE_POSITION_SHIFT;
425 if (!(SpecialFlags & NO_MATERIALS)) GenerateMaterials();
427 if (!(SpecialFlags & LOAD)) PostConstruct();
428 if (!(SpecialFlags & (LOAD|NO_MATERIALS))) {
429 CalculateAll();
430 if (!(SpecialFlags & NO_PIC_UPDATE)) UpdatePictures();
435 truth item::ShowMaterial () const {
436 if (GetMainMaterialConfig().Size == 1) return GetMainMaterial()->GetConfig() != GetMainMaterialConfig()[0];
437 //FIXME: gum solution
438 if (isBone()) {
439 // never show the material for 'bone bone'
440 if (GetMainMaterial()->GetConfig() == BONE) return false;
442 return true;
446 sLong item::GetBlockModifier() const
448 if(!IsShield(0))
449 return GetSize() * GetRoundness() << 1;
450 else
451 return GetSize() * GetRoundness() << 2;
454 truth item::CanBeSeenByPlayer() const
456 return CanBeSeenBy(PLAYER);
459 truth item::CanBeSeenBy(ccharacter* Who) const
461 for(int c = 0; c < SquaresUnder; ++c)
462 if(Slot[c] && Slot[c]->CanBeSeenBy(Who))
463 return true;
465 return Who->IsPlayer() && game::GetSeeWholeMapCheatMode();
468 festring item::GetDescription(int Case) const
470 if(CanBeSeenByPlayer())
471 return GetName(Case);
472 else
473 return CONST_S("something");
476 void item::SignalVolumeAndWeightChange()
478 CalculateVolumeAndWeight();
480 for(int c = 0; c < SquaresUnder; ++c)
481 if(Slot[c])
482 Slot[c]->SignalVolumeAndWeightChange();
485 void item::CalculateVolumeAndWeight()
487 Volume = Weight = 0;
489 for(int c = 0; c < GetMaterials(); ++c)
491 cmaterial* Material = GetMaterial(c);
493 if(Material)
495 Volume += Material->GetVolume();
496 Weight += Material->GetWeight();
501 void item::SignalEmitationIncrease(col24 EmitationUpdate)
503 if(game::CompareLights(EmitationUpdate, Emitation) > 0)
505 game::CombineLights(Emitation, EmitationUpdate);
507 for(int c = 0; c < SquaresUnder; ++c)
508 if(Slot[c])
509 Slot[c]->SignalEmitationIncrease(EmitationUpdate);
513 void item::SignalEmitationDecrease(col24 EmitationUpdate)
515 if(game::CompareLights(EmitationUpdate, Emitation) >= 0 && Emitation)
517 col24 Backup = Emitation;
518 CalculateEmitation();
520 if(Backup != Emitation)
521 for(int c = 0; c < SquaresUnder; ++c)
522 if(Slot[c])
523 Slot[c]->SignalEmitationDecrease(EmitationUpdate);
527 void item::CalculateAll()
529 CalculateVolumeAndWeight();
530 CalculateEmitation();
533 /* Temporary and buggy. */
535 void item::WeaponSkillHit(int Hits)
537 if(Slot[0] && Slot[0]->IsGearSlot())
538 static_cast<arm*>(static_cast<gearslot*>(*Slot)->GetBodyPart())->WieldedSkillHit(Hits);
541 /* Returns 0 if item cannot be cloned */
543 item* item::Duplicate(feuLong Flags)
545 if(!(Flags & IGNORE_PROHIBITIONS)
546 && ((!(Flags & MIRROR_IMAGE) && !CanBeCloned())
547 || (Flags & MIRROR_IMAGE && (!CanBeMirrored()
548 || (MainMaterial
549 && !(MainMaterial->GetCommonFlags() & CAN_BE_MIRRORED))
550 || (GetSecondaryMaterial()
551 && !(GetSecondaryMaterial()->GetCommonFlags() & CAN_BE_MIRRORED))))))
552 return 0;
554 item* Clone = GetProtoType()->Clone(this);
556 if(Flags & MIRROR_IMAGE)
557 Clone->SetLifeExpectancy(Flags >> LE_BASE_SHIFT & LE_BASE_RANGE,
558 Flags >> LE_RAND_SHIFT & LE_RAND_RANGE);
560 idholder* I = new idholder(ID);
561 I->Next = CloneMotherID;
562 CloneMotherID = I;
563 game::RemoveItemID(ID);
564 ID = game::CreateNewItemID(this);
565 Clone->UpdatePictures();
566 return Clone;
569 void item::AddInventoryEntry(ccharacter*, festring& Entry, int Amount, truth ShowSpecialInfo) const
571 if(Amount == 1)
572 AddName(Entry, INDEFINITE);
573 else
575 Entry << Amount << ' ';
576 AddName(Entry, PLURAL);
579 if(ShowSpecialInfo)
580 Entry << " [" << GetWeight() * Amount << "g]";
583 const itemdatabase* itemprototype::ChooseBaseForConfig(itemdatabase** TempConfig, int Configs, int ConfigNumber)
585 if(!(ConfigNumber & BROKEN))
586 return *TempConfig;
587 else
589 ConfigNumber ^= BROKEN;
591 for(int c = 0; c < Configs; ++c)
592 if(TempConfig[c]->Config == ConfigNumber)
593 return TempConfig[c];
595 return *TempConfig;
599 truth item::ReceiveDamage(character* Damager, int Damage, int Type, int Dir)
601 if(CanBeBroken() && !IsBroken() && Type & (PHYSICAL_DAMAGE|SOUND|ENERGY|ACID))
603 int StrengthValue = GetStrengthValue();
605 if(!StrengthValue)
606 StrengthValue = 1;
608 if(Damage > StrengthValue << 2 && RAND() & 3 && RAND() % (25 * Damage / StrengthValue) >= 100)
610 Break(Damager, Dir);
611 return true;
615 if(Type & ACID && IsBroken() && IsDestroyable(Damager))
617 int StrengthValue = GetStrengthValue();
619 if(!StrengthValue)
620 StrengthValue = 1;
622 if(Damage > StrengthValue << 4 && !(RAND() & 3) && RAND() % (100 * Damage / StrengthValue) >= 100)
624 Destroy(Damager, Dir);
625 return true;
629 return false;
632 void itemdatabase::InitDefaults(const itemprototype* NewProtoType, int NewConfig)
634 IsAbstract = false;
635 ProtoType = NewProtoType;
636 Config = NewConfig;
638 if(NewConfig & BROKEN)
640 if(Adjective.GetSize())
641 Adjective.Insert(0, "broken ");
642 else
643 Adjective = CONST_S("broken");
645 DefaultSize >>= 1;
646 FormModifier >>= 2;
647 StrengthModifier >>= 1;
651 sLong item::GetNutritionValue() const
653 sLong NV = 0;
655 for(int c = 0; c < GetMaterials(); ++c)
656 if(GetMaterial(c))
657 NV += GetMaterial(c)->GetTotalNutritionValue();
659 return NV;
662 void item::SignalSpoil(material*)
664 if(!Exists())
665 return;
667 if(CanBeSeenByPlayer())
668 ADD_MESSAGE("%s spoils completely.", GetExtendedDescription().CStr());
670 truth Equipped = PLAYER->Equips(this);
671 Disappear();
673 if(Equipped)
674 game::AskForEscPress(CONST_S("Equipment destroyed!"));
677 item* item::DuplicateToStack(stack* CurrentStack, feuLong Flags)
679 item* Duplicated = Duplicate(Flags);
681 if(!Duplicated)
682 return 0;
684 CurrentStack->AddItem(Duplicated);
685 return Duplicated;
688 truth item::CanBePiledWith(citem* Item, ccharacter* Viewer) const
690 return (GetType() == Item->GetType()
691 && GetConfig() == Item->GetConfig()
692 && ItemFlags == Item->ItemFlags
693 && (WeightIsIrrelevant() || Weight == Item->Weight)
694 && MainMaterial->IsSameAs(Item->MainMaterial)
695 && MainMaterial->GetSpoilLevel() == Item->MainMaterial->GetSpoilLevel()
696 && MainMaterial->GetRustLevel() == Item->MainMaterial->GetRustLevel()
697 && Viewer->GetCWeaponSkillLevel(this) == Viewer->GetCWeaponSkillLevel(Item)
698 && Viewer->GetSWeaponSkillLevel(this) == Viewer->GetSWeaponSkillLevel(Item)
699 && !Fluid && !Item->Fluid
700 && !LifeExpectancy == !Item->LifeExpectancy);
703 void item::Break(character* Breaker, int)
705 if(CanBeSeenByPlayer())
706 ADD_MESSAGE("%s %s.", GetExtendedDescription().CStr(), GetBreakVerb());
708 if(Breaker && IsOnGround())
710 room* Room = GetRoom();
712 if(Room)
713 Room->HostileAction(Breaker);
716 item* Broken = GetProtoType()->Clone(this);
717 Broken->SetConfig(GetConfig() | BROKEN);
718 Broken->SetSize(Broken->GetSize() >> 1);
719 DonateFluidsTo(Broken);
720 DonateIDTo(Broken);
721 DonateSlotTo(Broken);
722 SendToHell();
724 if(PLAYER->Equips(Broken))
725 game::AskForEscPress(CONST_S("Equipment broken!"));
728 void item::Be()
730 MainMaterial->Be(ItemFlags);
732 if (Exists() && LifeExpectancy) {
733 if (LifeExpectancy == 1) {
734 if (CanBeSeenByPlayer()) ADD_MESSAGE("%s disappears.", GetExtendedDescription().CStr());
735 truth Equipped = PLAYER->Equips(this);
736 Disappear();
737 if (Equipped) game::AskForEscPress(CONST_S("Equipment destroyed!"));
738 } else {
739 --LifeExpectancy;
744 int item::GetOfferValue(int Receiver) const
746 /* Temporary */
748 int OfferValue = int(sqrt(GetTruePrice()));
750 if(Receiver == GetAttachedGod())
751 OfferValue <<= 1;
752 else
753 OfferValue >>= 1;
755 return OfferValue;
758 void item::SignalEnchantmentChange()
760 for(int c = 0; c < SquaresUnder; ++c)
761 if(Slot[c])
762 Slot[c]->SignalEnchantmentChange();
765 sLong item::GetEnchantedPrice(int Enchantment) const
767 return !PriceIsProportionalToEnchantment() ? item::GetPrice() : Max<int>(item::GetPrice() * Enchantment * Enchantment, 0);
770 item* item::Fix()
772 item* Fixed = this;
774 if(IsBroken())
776 Fixed = GetProtoType()->Clone(this);
777 Fixed->SetConfig(GetConfig() ^ BROKEN);
778 Fixed->SetSize(Fixed->GetSize() << 1);
779 DonateFluidsTo(Fixed);
780 DonateIDTo(Fixed);
781 DonateSlotTo(Fixed);
782 SendToHell();
785 return Fixed;
788 void item::DonateSlotTo(item* Item)
790 if(Slot[0])
792 Slot[0]->DonateTo(Item);
793 Slot[0] = 0;
795 for(int c = 1; c < SquaresUnder; ++c)
796 if(Slot[c])
798 Slot[c]->Empty();
799 Slot[c] = 0;
804 int item::GetSpoilLevel() const
806 return MainMaterial->GetSpoilLevel();
809 void item::SignalSpoilLevelChange(material*)
811 if(!IsAnimated() && GetSpoilLevel() && Slot[0] && Slot[0]->IsVisible())
812 for(int c = 0; c < SquaresUnder; ++c)
813 GetSquareUnder(c)->IncStaticAnimatedEntities();
815 SignalVolumeAndWeightChange(); // gum
816 UpdatePictures();
819 truth item::AllowSpoil() const
821 if(IsOnGround())
823 lsquare* Square = GetLSquareUnder();
824 int RoomNumber = Square->GetRoomIndex();
825 return !RoomNumber || Square->GetLevel()->GetRoom(RoomNumber)->AllowSpoil(this);
827 else
828 return true;
831 void item::ResetSpoiling()
833 for(int c = 0; c < GetMaterials(); ++c)
834 if(GetMaterial(c))
835 GetMaterial(c)->ResetSpoiling();
838 cchar* item::GetBaseToHitValueDescription() const
840 if(GetBaseToHitValue() < 10)
841 return ToHitValueDescription[Min(GetBaseToHitValue(), 6)];
842 else
843 return ToHitValueDescription[7];
846 cchar* item::GetBaseBlockValueDescription() const
848 if(GetBaseBlockValue() < 20)
849 return ToHitValueDescription[Min(GetBaseBlockValue() >> 1, 6)];
850 else
851 return ToHitValueDescription[7];
854 cchar* item::GetStrengthValueDescription() const
856 int SV = GetStrengthValue();
858 if(SV < 3)
859 return StrengthValueDescription[0];
860 else if(SV < 5)
861 return StrengthValueDescription[1];
862 else if(SV < 8)
863 return StrengthValueDescription[2];
864 else if(SV < 11)
865 return StrengthValueDescription[3];
866 else if(SV < 16)
867 return StrengthValueDescription[4];
868 else if(SV < 20)
869 return StrengthValueDescription[5];
870 else
871 return StrengthValueDescription[6];
874 void item::SpecialGenerationHandler()
876 if(HandleInPairs())
877 Slot[0]->AddFriendItem(Duplicate());
880 void item::SortAllItems(const sortdata& SortData) const
882 if(SortData.Sorter == 0 || (this->*SortData.Sorter)(SortData.Character))
883 SortData.AllItems.push_back(const_cast<item*>(this));
886 int item::GetAttachedGod() const
888 return DataBase->AttachedGod ? DataBase->AttachedGod : MainMaterial->GetAttachedGod();
891 sLong item::GetMaterialPrice() const
893 return MainMaterial->GetRawPrice();
896 sLong item::GetTruePrice() const
898 if(LifeExpectancy)
899 return 0;
901 sLong Price = Max(GetPrice(), GetMaterialPrice());
903 if(Spoils())
904 Price = Price * (100 - GetMaxSpoilPercentage()) / 500;
906 return Price;
909 #ifdef WIZARD
911 void item::AddAttackInfo(felist& List) const
913 festring Entry(40, ' ');
914 Entry << int(GetWeight());
915 Entry.Resize(50);
916 Entry << int(GetSize());
917 Entry.Resize(60);
918 Entry << int(GetStrengthRequirement());
919 Entry.Resize(70);
920 Entry << GetBaseMinDamage() << '-' << GetBaseMaxDamage();
921 List.AddEntry(Entry, LIGHT_GRAY);
924 void item::AddMiscellaneousInfo(felist& List) const
926 festring Entry(40, ' ');
927 Entry << int(GetTruePrice());
928 Entry.Resize(55);
929 Entry << GetOfferValue(0);
930 Entry.Resize(70);
931 Entry << int(GetNutritionValue());
932 List.AddEntry(Entry, LIGHT_GRAY);
935 #endif
938 void item::PreProcessForBone () {
939 if (IsQuestItem()) {
940 RemoveFromSlot();
941 SendToHell();
942 } else {
943 game::RemoveItemID(ID);
944 ID = -ID;
945 game::AddItemID(this, ID);
946 SetSteppedOn(false);
951 void item::PostProcessForBone () {
952 boneidmap::iterator BI = game::GetBoneItemIDMap().find(-ID);
953 game::RemoveItemID(ID);
955 if (BI == game::GetBoneItemIDMap().end()) {
956 feuLong NewID = game::CreateNewItemID(this);
957 game::GetBoneItemIDMap().insert(std::make_pair(-ID, NewID));
958 ID = NewID;
959 } else {
960 if (game::SearchItem(BI->second)) {
961 ID = BI->second;
962 game::AddItemID(this, ID);
965 for (idholder* I = CloneMotherID; I; I = I->Next) {
966 BI = game::GetBoneItemIDMap().find(I->ID);
967 if (BI == game::GetBoneItemIDMap().end()) {
968 feuLong NewCloneMotherID = game::CreateNewItemID(0);
969 game::GetBoneItemIDMap().insert(std::make_pair(I->ID, NewCloneMotherID));
970 I->ID = NewCloneMotherID;
971 } else {
972 I->ID = BI->second;
978 void item::SetConfig(int NewConfig, int SpecialFlags)
980 databasecreator<item>::InstallDataBase(this, NewConfig);
981 CalculateAll();
983 if(!(SpecialFlags & NO_PIC_UPDATE))
984 UpdatePictures();
987 god* item::GetMasterGod() const
989 return game::GetGod(GetConfig());
992 int itemprototype::CreateSpecialConfigurations(itemdatabase** TempConfig, int Configs, int Level)
994 if(Level)
995 return Configs;
997 if(TempConfig[0]->CreateDivineConfigurations)
998 Configs = databasecreator<item>::CreateDivineConfigurations(this, TempConfig, Configs);
1000 /* Gum solution */
1002 if(TempConfig[0]->CreateLockConfigurations)
1004 const item::database*const* KeyConfigData = key::ProtoType.GetConfigData();
1005 int KeyConfigSize = key::ProtoType.GetConfigSize();
1006 int OldConfigs = Configs;
1008 for(int c1 = 0; c1 < OldConfigs; ++c1)
1009 if(!TempConfig[c1]->IsAbstract)
1011 int BaseConfig = TempConfig[c1]->Config;
1012 int NewConfig = BaseConfig | BROKEN_LOCK;
1013 itemdatabase* ConfigDataBase = new itemdatabase(*TempConfig[c1]);
1014 ConfigDataBase->InitDefaults(this, NewConfig);
1015 ConfigDataBase->PostFix << "with a broken lock";
1016 ConfigDataBase->Possibility = 0;
1017 TempConfig[Configs++] = ConfigDataBase;
1019 for(int c2 = 0; c2 < KeyConfigSize; ++c2)
1021 NewConfig = BaseConfig | KeyConfigData[c2]->Config;
1022 ConfigDataBase = new itemdatabase(*TempConfig[c1]);
1023 ConfigDataBase->InitDefaults(this, NewConfig);
1024 ConfigDataBase->PostFix << "with ";
1026 if(KeyConfigData[c2]->UsesLongAdjectiveArticle)
1027 ConfigDataBase->PostFix << "an ";
1028 else
1029 ConfigDataBase->PostFix << "a ";
1031 ConfigDataBase->PostFix << KeyConfigData[c2]->Adjective << " lock";
1032 ConfigDataBase->Possibility = 0;
1033 TempConfig[Configs++] = ConfigDataBase;
1038 return Configs;
1041 void item::Draw(blitdata& BlitData) const
1043 cint AF = GraphicData.AnimationFrames;
1044 cint F = !(BlitData.CustomData & ALLOW_ANIMATE) || AF == 1 ? 0 : GET_TICK() & (AF - 1);
1045 cbitmap* P = GraphicData.Picture[F];
1047 if(BlitData.CustomData & ALLOW_ALPHA)
1048 P->AlphaLuminanceBlit(BlitData);
1049 else
1050 P->LuminanceMaskedBlit(BlitData);
1052 if(Fluid && ShowFluids())
1053 DrawFluids(BlitData);
1056 v2 item::GetLargeBitmapPos(v2 BasePos, int I) const
1058 cint SquareIndex = I ? I / (GraphicData.AnimationFrames >> 2) : 0;
1059 return v2(SquareIndex & 1 ? BasePos.X + 16 : BasePos.X, SquareIndex & 2 ? BasePos.Y + 16 : BasePos.Y);
1062 void item::LargeDraw(blitdata& BlitData) const
1064 cint TrueAF = GraphicData.AnimationFrames >> 2;
1065 cint SquareIndex = BlitData.CustomData & SQUARE_INDEX_MASK;
1066 cint F = !(BlitData.CustomData & ALLOW_ANIMATE) ? SquareIndex * TrueAF : SquareIndex * TrueAF + (GET_TICK() & (TrueAF - 1));
1067 cbitmap* P = GraphicData.Picture[F];
1069 if(BlitData.CustomData & ALLOW_ALPHA)
1070 P->AlphaLuminanceBlit(BlitData);
1071 else
1072 P->LuminanceMaskedBlit(BlitData);
1075 void item::DonateIDTo(item* Item)
1077 game::RemoveItemID(Item->ID);
1078 game::UpdateItemID(Item, ID);
1079 Item->ID = ID;
1080 ID = 0;
1083 void item::SignalRustLevelChange()
1085 SignalVolumeAndWeightChange();
1086 UpdatePictures();
1087 SendNewDrawAndMemorizedUpdateRequest();
1090 const rawbitmap* item::GetRawPicture() const
1092 return igraph::GetRawGraphic(GetGraphicsContainerIndex());
1095 void item::RemoveFluid(fluid *ToBeRemoved) {
1096 truth WasAnimated = IsAnimated();
1097 truth HasFluids = false;
1099 if (Fluid && ToBeRemoved) {
1100 for (int c = 0; c < /*SquaresUnder*/FluidCount; ++c) {
1101 fluid *F = Fluid[c];
1103 if (F == ToBeRemoved) {
1104 Fluid[c] = F->Next;
1105 } else if (F) {
1106 fluid *LF = F;
1108 for (F = F->Next; F; LF = F, F = F->Next) if (F == ToBeRemoved) { LF->Next = F->Next; break; }
1110 if (Fluid[c]) HasFluids = true;
1113 UpdatePictures();
1114 if (!HasFluids && Fluid) {
1115 delete [] Fluid;
1116 Fluid = 0;
1117 if (!IsAnimated() != !WasAnimated && Slot[0]->IsVisible()) GetSquareUnder()->DecStaticAnimatedEntities();
1119 if (ToBeRemoved) SignalEmitationDecrease(ToBeRemoved->GetEmitation());
1120 SignalVolumeAndWeightChange();
1124 void item::RemoveAllFluids () {
1125 if (Fluid) {
1126 for (int c = 0; c < /*SquaresUnder*/FluidCount; ) {
1127 //fprintf(stderr, "c: %d; SquaresUnder: %d\n", c, SquaresUnder);
1128 fluid *F = Fluid[c];
1129 if (F) {
1130 RemoveFluid(F);
1131 c = 0;
1132 if (!Fluid) break;
1133 } else {
1134 ++c;
1141 void item::AddFluid (liquid *ToBeAdded, festring LocationName, int SquareIndex, truth IsInside) {
1142 truth WasAnimated = IsAnimated();
1144 if (SquareIndex < 0) ABORT("item::AddFluid(): invalid SquareIndex: %d", SquareIndex);
1146 if (Fluid) {
1147 fluid *F = Fluid[SquareIndex];
1149 if (SquareIndex >= FluidCount) ABORT("item::AddFluid(): invalid SquareIndex: %d", SquareIndex);
1150 if (!F) {
1151 Fluid[SquareIndex] = new fluid(ToBeAdded, this, LocationName, IsInside);
1152 } else {
1153 fluid *LF;
1155 do {
1156 if (ToBeAdded->IsSameAs(F->GetLiquid())) {
1157 F->AddLiquidAndVolume(ToBeAdded->GetVolume());
1158 delete ToBeAdded;
1159 return;
1161 LF = F;
1162 F = F->Next;
1163 } while(F);
1164 LF->Next = new fluid(ToBeAdded, this, LocationName, IsInside);
1166 } else {
1167 FluidCount = SquaresUnder;
1168 Fluid = new fluid*[/*SquaresUnder*/FluidCount];
1169 if (SquareIndex >= FluidCount) ABORT("item::AddFluid(): invalid SquareIndex: %d", SquareIndex);
1170 memset(Fluid, 0, SquaresUnder*sizeof(fluid *));
1171 Fluid[SquareIndex] = new fluid(ToBeAdded, this, LocationName, IsInside);
1174 UpdatePictures();
1175 SignalVolumeAndWeightChange();
1176 SignalEmitationIncrease(ToBeAdded->GetEmitation());
1178 if (Slot[0]) {
1179 if (!IsAnimated() != !WasAnimated && Slot[0]->IsVisible()) {
1180 GetSquareUnder()->IncStaticAnimatedEntities();
1182 SendNewDrawAndMemorizedUpdateRequest();
1186 void item::SendNewDrawAndMemorizedUpdateRequest() const
1188 if(!game::IsInWilderness())
1189 for(int c = 0; c < SquaresUnder; ++c)
1190 if(Slot[c])
1192 lsquare *Square = GetLSquareUnder(c);
1193 if (Square) {
1194 Square->SendNewDrawRequest();
1195 Square->SendMemorizedUpdateRequest();
1200 void item::CalculateEmitation()
1202 object::CalculateEmitation();
1204 if(Fluid)
1205 for(int c = 0; c < /*SquaresUnder*/FluidCount; ++c)
1206 for(const fluid* F = Fluid[c]; F; F = F->Next)
1207 game::CombineLights(Emitation, F->GetEmitation());
1210 void item::FillFluidVector (fluidvector &Vector, int SquareIndex) const {
1211 if (Fluid) {
1212 if (SquareIndex < 0 || SquareIndex >= FluidCount) ABORT("item::FillFluidVector(): invalid SquareIndex: %d\n", SquareIndex);
1213 for (fluid *F = Fluid[SquareIndex]; F; F = F->Next) Vector.push_back(F);
1217 void item::SpillFluid(character*, liquid* Liquid, int SquareIndex)
1219 if(AllowFluids() && Liquid->GetVolume())
1220 AddFluid(Liquid, "", SquareIndex, false);
1221 else
1222 delete Liquid;
1225 void item::TryToRust (sLong LiquidModifier) {
1226 if (MainMaterial->TryToRust(LiquidModifier)) {
1227 if (CanBeSeenByPlayer()) {
1228 if (MainMaterial->GetRustLevel() == NOT_RUSTED) ADD_MESSAGE("%s rusts.", CHAR_NAME(DEFINITE));
1229 else ADD_MESSAGE("%s rusts more.", CHAR_NAME(DEFINITE));
1231 MainMaterial->SetRustLevel(MainMaterial->GetRustLevel() + 1);
1235 void item::CheckFluidGearPictures(v2 ShadowPos, int SpecialFlags, truth BodyArmor)
1237 if(Fluid)
1238 for(fluid* F = Fluid[0]; F; F = F->Next)
1239 F->CheckGearPicture(ShadowPos, SpecialFlags, BodyArmor);
1242 void item::DrawFluidGearPictures(blitdata& BlitData, int SpecialFlags) const
1244 if(Fluid)
1245 for(const fluid* F = Fluid[0]; F; F = F->Next)
1246 F->DrawGearPicture(BlitData, SpecialFlags);
1249 void item::DrawFluidBodyArmorPictures(blitdata& BlitData, int SpecialFlags) const
1251 if(Fluid)
1252 for(const fluid* F = Fluid[0]; F; F = F->Next)
1253 F->DrawBodyArmorPicture(BlitData, SpecialFlags);
1256 void item::DrawFluids(blitdata& BlitData) const
1258 cint SquareIndex = BlitData.CustomData & SQUARE_INDEX_MASK;
1260 for(const fluid* F = Fluid[SquareIndex]; F; F = F->Next)
1261 F->Draw(BlitData);
1264 void item::ReceiveAcid(material*, cfestring&, sLong Modifier)
1266 if(GetMainMaterial()->GetInteractionFlags() & CAN_DISSOLVE)
1268 int Damage = Modifier / 1000;
1270 if(Damage)
1272 Damage += RAND() % Damage;
1273 ReceiveDamage(0, Damage, ACID);
1278 void item::DonateFluidsTo(item* Item)
1280 if(Fluid)
1281 for(int c = 0; c < GetSquaresUnder(); ++c)
1282 for(fluid* F = Fluid[c]; F; F = F->Next)
1284 liquid* Liquid = F->GetLiquid();
1285 Item->AddFluid(Liquid->SpawnMoreLiquid(Liquid->GetVolume()), F->GetLocationName(), c, F->IsInside());
1289 void item::Destroy(character* Destroyer, int)
1291 if(CanBeSeenByPlayer())
1292 ADD_MESSAGE("%s is destroyed.", GetExtendedDescription().CStr());
1294 if(Destroyer && IsOnGround())
1296 room* Room = GetRoom();
1298 if(Room)
1299 Room->HostileAction(Destroyer);
1302 truth Equipped = PLAYER->Equips(this);
1303 RemoveFromSlot();
1304 SendToHell();
1306 if(Equipped)
1307 game::AskForEscPress(CONST_S("Equipment destroyed!"));
1310 void item::RemoveRust()
1312 for(int c = 0; c < GetMaterials(); ++c)
1313 if(GetMaterial(c))
1314 GetMaterial(c)->SetRustLevel(NOT_RUSTED);
1317 void item::SetSpoilPercentage(int Value)
1319 for(int c = 0; c < GetMaterials(); ++c)
1321 material* Material = GetMaterial(c);
1323 if(Material && Material->CanSpoil())
1324 Material->SetSpoilCounter(Material->GetSpoilModifier() * Value / 100);
1328 void item::RedistributeFluids()
1330 if(Fluid)
1331 for(int c = 0; c < GetSquaresUnder(); ++c)
1332 for(fluid* F = Fluid[c]; F; F = F->Next)
1333 F->Redistribute();
1336 material* item::GetConsumeMaterial(ccharacter* Consumer, materialpredicate Predicate) const
1338 return (MainMaterial->*Predicate)() && Consumer->CanConsume(MainMaterial) ? MainMaterial : 0;
1341 /* The parameter can only be MainMaterial */
1343 material* item::RemoveMaterial(material*)
1345 RemoveFromSlot();
1346 SendToHell();
1347 return 0;
1350 void item::InitMaterials(material* FirstMaterial, truth CallUpdatePictures)
1352 InitMaterial(MainMaterial, FirstMaterial, GetDefaultMainVolume());
1353 SignalVolumeAndWeightChange();
1355 if(CallUpdatePictures)
1356 UpdatePictures();
1359 void item::GenerateMaterials()
1361 int Chosen = RandomizeMaterialConfiguration();
1362 const fearray<sLong>& MMC = GetMainMaterialConfig();
1363 InitMaterial(MainMaterial,
1364 MAKE_MATERIAL(MMC.Data[MMC.Size == 1 ? 0 : Chosen]),
1365 GetDefaultMainVolume());
1368 void item::SignalSquarePositionChange(int Position)
1370 Flags &= ~SQUARE_POSITION_BITS;
1371 Flags |= Position << SQUARE_POSITION_SHIFT;
1374 truth item::Read(character* Reader)
1376 Reader->StartReading(this, GetReadDifficulty());
1377 return true;
1380 truth item::CanBeHardened(ccharacter*) const
1382 return MainMaterial->GetHardenedMaterial(this) != NONE;
1385 void item::SetLifeExpectancy(int Base, int RandPlus)
1387 LifeExpectancy = RandPlus > 1 ? Base + RAND_N(RandPlus) : Base;
1388 Enable();
1391 truth item::IsVeryCloseToSpoiling() const
1393 for(int c = 0; c < GetMaterials(); ++c)
1394 if(GetMaterial(c) && !GetMaterial(c)->IsVeryCloseToSpoiling())
1395 return false;
1397 return true;
1400 truth item::IsValuable() const
1402 if(DataBase->IsValuable)
1403 return true;
1405 for(int c = 0; c < GetMaterials(); ++c)
1407 material* M = GetMaterial(c);
1409 if(M && M->GetCommonFlags() & IS_VALUABLE)
1410 return true;
1413 return false;
1416 int item::GetHinderVisibilityBonus(ccharacter* Char) const
1418 int Bonus = 0;
1420 if(GetGearStates() & INFRA_VISION
1421 && !Char->TemporaryStateIsActivated(INFRA_VISION))
1422 Bonus += 20000;
1424 if(GetGearStates() & ESP
1425 && !Char->TemporaryStateIsActivated(ESP))
1426 Bonus += 20000;
1428 if(!game::IsDark(GetEmitation()))
1429 Bonus += 5000;
1431 return Bonus;
1434 sLong item::GetFixPrice() const
1436 item* Clone = GetProtoType()->Clone(this);
1437 Clone = Clone->Fix();
1438 Clone->RemoveRust();
1439 sLong FixPrice = Clone->GetTruePrice();
1440 Clone->SendToHell();
1441 return Max(sLong(3.5 * sqrt(FixPrice)), 10);
1444 void item::AddTrapName(festring& String, int Amount) const
1446 if(Amount == 1)
1447 AddName(String, DEFINITE);
1448 else
1450 String << Amount << ' ';
1451 AddName(String, PLURAL);
1455 truth item::Spoils() const
1457 for(int c = 0; c < GetMaterials(); ++c)
1459 cmaterial* Material = GetMaterial(c);
1461 if(Material && Material->Spoils())
1462 return true;
1465 return false;
1468 int item::GetMaxSpoilPercentage() const
1470 int MaxPercentage = 0;
1472 for(int c = 0; c < GetMaterials(); ++c)
1474 cmaterial* Material = GetMaterial(c);
1476 if(Material)
1477 MaxPercentage = Max(MaxPercentage, Material->GetSpoilPercentage());
1480 return MaxPercentage;
1483 truth item::HasPrice() const
1485 return GetPrice() || GetMaterialPrice();
1488 void item::Disappear()
1490 RemoveFromSlot();
1491 SendToHell();
1494 outputfile& operator<<(outputfile& SaveFile, const idholder* IdHolder)
1496 SaveFile << IdHolder->ID;
1497 return SaveFile;
1500 inputfile& operator>>(inputfile& SaveFile, idholder*& IdHolder)
1502 IdHolder = new idholder(ReadType<feuLong>(SaveFile));
1503 return SaveFile;
1506 festring item::GetExtendedDescription() const
1508 if(!CanBeSeenByPlayer())
1509 return CONST_S("something");
1511 festring Desc;
1512 ccharacter* Carrier = FindCarrier();
1514 if(Carrier)
1516 if(Carrier->IsPlayer())
1518 Desc << "your ";
1519 AddName(Desc, UNARTICLED);
1520 return Desc;
1522 else if(Carrier->CanBeSeenByPlayer())
1524 Carrier->AddName(Desc, DEFINITE);
1525 Desc << "'s ";
1526 AddName(Desc, UNARTICLED);
1527 return Desc;
1531 AddName(Desc, DEFINITE);
1533 if(IsOnGround())
1534 GetLSquareUnder()->AddLocationDescription(Desc);
1536 return Desc;
1539 ccharacter* item::FindCarrier() const
1541 return Slot[0]->FindCarrier();
1544 /* returns 0 if not worn or wielded else the wearer */
1546 const character* item::GetWearer() const
1548 if(!GetSlot()->IsGearSlot())
1549 return 0;
1551 return FindCarrier();
1554 void itemlock::PostConstruct()
1556 /* Terrible gum solution! */
1558 if(!(GetVirtualConfig() & LOCK_BITS))
1560 int NormalLockTypes = 0;
1561 const itemdatabase*const* ConfigData = GetVirtualProtoType()->GetConfigData();
1562 int c, ConfigSize = GetVirtualProtoType()->GetConfigSize();
1564 for(c = 0; c < ConfigSize; ++c)
1565 if(ConfigData[c]->Config & LOCK_BITS
1566 && (ConfigData[c]->Config & ~LOCK_BITS) == GetVirtualConfig()
1567 && !(ConfigData[c]->Config & S_LOCK_ID))
1568 ++NormalLockTypes;
1570 int ChosenLock = RAND() % NormalLockTypes;
1572 for(c = 0; c < ConfigSize; ++c)
1573 if(ConfigData[c]->Config & LOCK_BITS
1574 && (ConfigData[c]->Config & ~LOCK_BITS) == GetVirtualConfig()
1575 && !(ConfigData[c]->Config & S_LOCK_ID)
1576 && !ChosenLock--)
1578 SetVirtualConfig(ConfigData[c]->Config, NO_PIC_UPDATE);
1579 break;
1584 truth itemlock::TryKey(item* Key, character* Applier)
1586 if(GetVirtualConfig() & BROKEN_LOCK)
1588 ADD_MESSAGE("The lock is broken.");
1589 return true;
1592 if(Key->CanOpenLockType(GetVirtualConfig()&LOCK_BITS))
1594 if(Locked)
1596 if(Applier->IsPlayer())
1597 ADD_MESSAGE("You unlock %s.", GetVirtualDescription(DEFINITE).CStr());
1598 else if(Applier->CanBeSeenByPlayer())
1599 ADD_MESSAGE("%s unlocks %s.", Applier->CHAR_NAME(DEFINITE), GetVirtualDescription(DEFINITE).CStr());
1601 else
1603 if(Applier->IsPlayer())
1604 ADD_MESSAGE("You lock %s.", GetVirtualDescription(DEFINITE).CStr());
1605 else if(Applier->CanBeSeenByPlayer())
1606 ADD_MESSAGE("%s locks %s.", Applier->CHAR_NAME(DEFINITE), GetVirtualDescription(DEFINITE).CStr());
1609 Locked = !Locked;
1611 else
1613 if(Applier->IsPlayer())
1614 ADD_MESSAGE("%s doesn't fit in the lock.", Key->CHAR_NAME(DEFINITE));
1615 else if(Applier->CanBeSeenByPlayer())
1616 ADD_MESSAGE("%s tries to fit %s in the lock, but fails.", Applier->CHAR_NAME(DEFINITE), Key->CHAR_NAME(DEFINITE));
1619 return true;
1622 void itemlock::Save(outputfile& SaveFile) const
1624 SaveFile << Locked;
1627 void itemlock::Load(inputfile& SaveFile)
1629 SaveFile >> Locked;
1632 truth item::IsBeverage(ccharacter*) const
1634 for(int c = 0; c < GetMaterials(); ++c)
1636 cmaterial* Material = GetMaterial(c);
1638 if(Material && (Material->GetCategoryFlags() & IS_BEVERAGE))
1639 return true;
1642 return false;
1645 void item::Haste()
1647 ItemFlags |= HASTE;
1648 ItemFlags &= ~SLOW;
1649 SendMemorizedUpdateRequest();
1652 void item::Slow()
1654 ItemFlags |= SLOW;
1655 ItemFlags &= ~HASTE;
1656 SendMemorizedUpdateRequest();
1659 void item::SendMemorizedUpdateRequest() const
1661 if(!game::IsInWilderness())
1662 for(int c = 0; c < SquaresUnder; ++c)
1663 if(Slot[c])
1665 lsquare* Square = GetLSquareUnder(c);
1666 Square->SendMemorizedUpdateRequest();
1670 truth item::AddStateDescription(festring& Name, truth Articled) const
1672 if(!Spoils())
1673 return false;
1675 if((ItemFlags & (HASTE|SLOW)) && Articled)
1676 Name << "a ";
1678 if(ItemFlags & HASTE)
1679 Name << "hasted ";
1681 if(ItemFlags & SLOW)
1682 Name << "slowed ";
1684 return true;
1688 truth item::Burn (character *who, v2 where, int dir) {
1689 //who->EditExperience(PERCEPTION, 150, 1 << 10);
1690 where += game::GetMoveVector(dir);
1691 area *ca = who->GetSquareUnder()->GetArea();
1692 if (where.X < 0 || where.Y < 0 || where.X >= ca->GetXSize() || where.Y >= ca->GetYSize()) return false;
1693 lsquare *sq = static_cast<lsquare *>(ca->GetSquare(where.X, where.Y));
1694 if (sq) {
1695 sq->ReceiveTrapDamage(who, 50, FIRE, dir);
1696 return true;
1698 return false;