Can anyone tell me how blocking works in IVAN? I think this is it, but I can't make head or tail of it.
SPOILER ALERT. Click here to see text.
SPOILER ALERT. Click here to see text.
/* * * Iter Vehemens ad Necem (IVAN) * Copyright (C) Timo Kiviluoto * Released under the GNU General * Public License * * See LICENSING which should be included * along with this file for more details * */ /* Compiled through itemset.cpp */ const char* ToHitValueDescription[] = { "unbelievably inaccurate", "extremely inaccurate", "inaccurate", "decently accurate", "accurate", "highly accurate", "extremely accurate", "unbelievably accurate" }; const char* StrengthValueDescription[] = { "fragile", "rather sturdy", "sturdy", "strong", "very strong", "extremely strong", "almost unbreakable" }; itemprototype::itemprototype(const itemprototype* Base, itemspawner Spawner, itemcloner Cloner, const char* ClassID) : Base(Base), Spawner(Spawner), Cloner(Cloner), ClassID(ClassID) { Index = protocontainer<item>::Add(this); } truth itemdatabase::AllowRandomInstantiation() const { return !(Config & S_LOCK_ID); } item::item() : Slot(0), CloneMotherID(0), Fluid(0), LifeExpectancy(0) { } truth item::IsOnGround() const { return Slot[0]->IsOnGround(); } truth item::IsSimiliarTo(item* Item) const { return Item->GetType() == GetType() && Item->GetConfig() == GetConfig(); } double item::GetBaseDamage() const { return sqrt(5e-5 * GetWeaponStrength()) + GetDamageBonus(); } int item::GetBaseMinDamage() const { return int(GetBaseDamage() * 0.75); } int item::GetBaseMaxDamage() const { return int(GetBaseDamage() * 1.25) + 1; } int item::GetBaseToHitValue() const { return int(10000. / (1000 + GetWeight()) + GetTHVBonus()); } int item::GetBaseBlockValue() const { return int((10000. / (1000 + GetWeight()) + GetTHVBonus()) * GetBlockModifier() / 10000.); } truth item::IsInCorrectSlot(int I) const { return I == RIGHT_WIELDED_INDEX || I == LEFT_WIELDED_INDEX; } truth item::IsInCorrectSlot() const { return IsInCorrectSlot(static_cast<gearslot*>(*Slot)->GetEquipmentIndex()); } int item::GetEquipmentIndex() const { return static_cast<gearslot*>(*Slot)->GetEquipmentIndex(); } int item::GetGraphicsContainerIndex() const { return GR_ITEM; } truth item::IsBroken() const { return GetConfig() & BROKEN; } const char* item::GetBreakVerb() const { return "breaks"; } square* item::GetSquareUnderEntity(int I) const { return GetSquareUnder(I); } square* item::GetSquareUnder(int I) const { return Slot[I] ? Slot[I]->GetSquareUnder() : 0; } lsquare* item::GetLSquareUnder(int I) const { return static_cast<lsquare*>(Slot[I]->GetSquareUnder()); } void item::SignalStackAdd(stackslot* StackSlot, void (stack::*)(item*, truth)) { Slot[0] = StackSlot; } truth item::IsAnimated() const { return GraphicData.AnimationFrames > 1 || (Fluid && ShowFluids()); } truth item::IsRusted() const { return MainMaterial->GetRustLevel() != NOT_RUSTED; } truth item::IsEatable(const character* Eater) const { return GetConsumeMaterial(Eater, &material::IsSolid) && IsConsumable(); } truth item::IsDrinkable(const character* Eater) const { return GetConsumeMaterial(Eater, &material::IsLiquid) && IsConsumable(); } pixelpredicate item::GetFluidPixelAllowedPredicate() const { return &rawbitmap::IsTransparent; } void item::Cannibalize() { Flags |= CANNIBALIZED; } void item::SetMainMaterial(material* NewMaterial, int SpecialFlags) { SetMaterial(MainMaterial, NewMaterial, GetDefaultMainVolume(), SpecialFlags); } void item::ChangeMainMaterial(material* NewMaterial, int SpecialFlags) { ChangeMaterial(MainMaterial, NewMaterial, GetDefaultMainVolume(), SpecialFlags); } void item::InitMaterials(const materialscript* M, const materialscript*, truth CUP) { InitMaterials(M->Instantiate(), CUP); } int item::GetMainMaterialRustLevel() const { return MainMaterial->GetRustLevel(); } item::item(const item& Item) : object(Item), Slot(0), Size(Item.Size), DataBase(Item.DataBase), Volume(Item.Volume), Weight(Item.Weight), Fluid(0), SquaresUnder(Item.SquaresUnder), LifeExpectancy(Item.LifeExpectancy) { Flags &= ENTITY_FLAGS|SQUARE_POSITION_BITS; ID = game::CreateNewItemID(this); CloneMotherID = new idholder(Item.ID); idholder* TI = CloneMotherID; for(idholder* II = Item.CloneMotherID; II; II = II->Next) TI = TI->Next = new idholder(II->ID); TI->Next = 0; Slot = new slot*[SquaresUnder]; for(int c = 0; c < SquaresUnder; ++c) Slot[c] = 0; } item::~item() { delete [] Slot; game::RemoveItemID(ID); fluid** FP = Fluid; if(FP) { for(int c = 0; c < SquaresUnder; ++c) for(fluid* F = FP[c]; F;) { fluid* ToDel = F; F = F->Next; delete ToDel; } delete [] FP; } } void item::Fly(character* Thrower, int Direction, int Force) { int Range = Force * 25 / Max(long(sqrt(GetWeight())), 1L); lsquare* LSquareUnder = GetLSquareUnder(); RemoveFromSlot(); LSquareUnder->GetStack()->AddItem(this, false); if(!Range || GetSquaresUnder() != 1) { if(GetLSquareUnder()->GetRoom()) GetLSquareUnder()->GetRoom()->AddItemEffect(this); return; } if(Direction == RANDOM_DIR) Direction = RAND() & 7; v2 StartingPos = GetPos(); v2 Pos = StartingPos; v2 DirVector = game::GetMoveVector(Direction); truth Breaks = false; double BaseDamage, BaseToHitValue; /*** check ***/ if(Thrower) { int Bonus = Thrower->IsHumanoid() ? Thrower->GetCWeaponSkill(GetWeaponCategory())->GetBonus() : 1000; BaseDamage = sqrt(5e-12 * GetWeaponStrength() * Force / Range) * Bonus; BaseToHitValue = 10 * Bonus * Thrower->GetMoveEase() / (500 + GetWeight()) * Thrower->GetAttribute(DEXTERITY) * sqrt(2.5e-8 * Thrower->GetAttribute(PERCEPTION)) / Range; } else { BaseDamage = sqrt(5e-6 * GetWeaponStrength() * Force / Range); BaseToHitValue = 10 * 100 / (500 + GetWeight()) / Range; } int RangeLeft; for(RangeLeft = Range; RangeLeft; --RangeLeft) { if(!GetLevel()->IsValidPos(Pos + DirVector)) break; lsquare* JustHit = GetNearLSquare(Pos + DirVector); if(!JustHit->IsFlyable()) { Breaks = true; JustHit->GetOLTerrain()->HasBeenHitByItem(Thrower, this, int(BaseDamage * sqrt(RangeLeft))); break; } else { clock_t StartTime = clock(); Pos += DirVector; RemoveFromSlot(); JustHit->GetStack()->AddItem(this, false); truth Draw = game::OnScreen(JustHit->GetPos()) && JustHit->CanBeSeenByPlayer(); if(Draw) game::DrawEverything(); if(JustHit->GetCharacter()) { int Damage = int(BaseDamage * sqrt(RangeLeft)); double ToHitValue = BaseToHitValue * RangeLeft; int Returned = HitCharacter(Thrower, JustHit->GetCharacter(), Damage, ToHitValue, Direction); if(Returned == HIT) Breaks = true; if(Returned != MISSED) break; } if(Draw) while(clock() - StartTime < 0.03 * CLOCKS_PER_SEC); } } if(Breaks) ReceiveDamage(Thrower, int(sqrt(GetWeight() * RangeLeft) / 10), THROW|PHYSICAL_DAMAGE, Direction); if(Exists() && GetLSquareUnder()->GetRoom()) GetLSquareUnder()->GetRoom()->AddItemEffect(this); } int item::HitCharacter(character* Thrower, character* Dude, int Damage, double ToHitValue, int Direction) { if(Dude->Catches(this)) return CATCHED; if(Thrower && !EffectIsGood()) Thrower->Hostility(Dude); if(Dude->DodgesFlyingItem(this, ToHitValue)) { if(Dude->IsPlayer()) ADD_MESSAGE("%s misses you.", CHAR_NAME(DEFINITE)); else if(Dude->CanBeSeenByPlayer()) ADD_MESSAGE("%s misses %s.", CHAR_NAME(DEFINITE), Dude->CHAR_NAME(DEFINITE)); return MISSED; } Dude->HasBeenHitByItem(Thrower, this, Damage, ToHitValue, Direction); return HIT; } double item::GetWeaponStrength() const { return GetFormModifier() * GetMainMaterial()->GetStrengthValue() * sqrt(GetMainMaterial()->GetWeight()); } int item::GetStrengthRequirement() const { double WeightTimesSize = GetWeight() * GetSize(); return int(1.25e-10 * WeightTimesSize * WeightTimesSize); } truth item::Apply(character* Applier) { if(Applier->IsPlayer()) ADD_MESSAGE("You can't apply this!"); return false; } /* Returns truth that tells whether the Polymorph really happened */ truth item::Polymorph(character* Polymorpher, stack* CurrentStack) { if(!IsPolymorphable()) return false; else { if(Polymorpher && IsOnGround()) { room* Room = GetRoom(); if(Room) Room->HostileAction(Polymorpher); } if(GetSquarePosition() != CENTER) { stack* Stack = CurrentStack->GetLSquareUnder()->GetStackOfAdjacentSquare(GetSquarePosition()); if(Stack) CurrentStack = Stack; } CurrentStack->AddItem(protosystem::BalancedCreateItem(0, MAX_PRICE, ANY_CATEGORY, 0, 0, 0, true)); RemoveFromSlot(); SendToHell(); return true; } } /* Returns whether the Eater must stop eating the item */ truth item::Consume(character* Eater, long Amount) { material* ConsumeMaterial = GetConsumeMaterial(Eater); if(!ConsumeMaterial) return true; if(Eater->IsPlayer() && !(Flags & CANNIBALIZED) && Eater->CheckCannibalism(ConsumeMaterial)) { game::DoEvilDeed(25); ADD_MESSAGE("You feel that this was an evil deed."); Cannibalize(); } ulong ID = GetID(); material* Garbage = ConsumeMaterial->EatEffect(Eater, Amount); item* NewConsuming = GetID() ? this : game::SearchItem(ID); material* NewConsumeMaterial = NewConsuming->GetConsumeMaterial(Eater); if(!NewConsuming->Exists() || !NewConsumeMaterial || !NewConsumeMaterial->IsSameAs(ConsumeMaterial)) ConsumeMaterial->FinishConsuming(Eater); delete Garbage; return !NewConsuming->Exists() || !NewConsumeMaterial; } truth item::CanBeEatenByAI(const character* Eater) const { material* ConsumeMaterial = GetConsumeMaterial(Eater); return (!Eater->IsPet() || !(Eater->GetCommandFlags() & DONT_CONSUME_ANYTHING_VALUABLE) || !IsValuable()) && IsConsumable() && ConsumeMaterial && ConsumeMaterial->CanBeEatenByAI(Eater); } void item::Save(outputfile& SaveFile) const { SaveFile << (ushort)GetType(); object::Save(SaveFile); SaveFile << (ushort)GetConfig(); SaveFile << (ushort)Flags; SaveFile << Size << ID << LifeExpectancy; SaveLinkedList(SaveFile, CloneMotherID); if(Fluid) { SaveFile.Put(true); for(int c = 0; c < SquaresUnder; ++c) SaveLinkedList(SaveFile, Fluid[c]); } else SaveFile.Put(false); } void item::Load(inputfile& SaveFile) { object::Load(SaveFile); databasecreator<item>::InstallDataBase(this, ReadType<ushort>(SaveFile)); Flags |= ReadType<ushort>(SaveFile) & ~ENTITY_FLAGS; SaveFile >> Size >> ID >> LifeExpectancy; LoadLinkedList(SaveFile, CloneMotherID); if(LifeExpectancy) Enable(); game::AddItemID(this, ID); if(SaveFile.Get()) { Fluid = new fluid*[SquaresUnder]; for(int c = 0; c < SquaresUnder; ++c) { LoadLinkedList(SaveFile, Fluid[c]); for(fluid* F = Fluid[c]; F; F = F->Next) F->SetMotherItem(this); } } } void item::TeleportRandomly() { if(GetSquaresUnder() == 1) // gum solution { lsquare* Square = GetNearLSquare(GetLevel()->GetRandomSquare()); MoveTo(Square->GetStack()); if(Square->CanBeSeenByPlayer()) ADD_MESSAGE("Suddenly %s appears!", CHAR_NAME(INDEFINITE)); } } int item::GetStrengthValue() const { return long(GetStrengthModifier()) * GetMainMaterial()->GetStrengthValue() / 2000; } void item::RemoveFromSlot() { for(int c = 0; c < SquaresUnder; ++c) if(Slot[c]) { try { Slot[c]->Empty(); } catch(quitrequest) { SendToHell(); throw; } Slot[c] = 0; } } void item::MoveTo(stack* Stack) { RemoveFromSlot(); Stack->AddItem(this); } const char* item::GetItemCategoryName(long Category) // convert to array { switch(Category) { case HELMET: return "Helmets"; case AMULET: return "Amulets"; case CLOAK: return "Cloaks"; case BODY_ARMOR: return "Body armors"; case WEAPON: return "Weapons"; case SHIELD: return "Shields"; case RING: return "Rings"; case GAUNTLET: return "Gauntlets"; case BELT: return "Belts"; case BOOT: return "Boots"; case FOOD: return "Food"; case POTION: return "Potions"; case SCROLL: return "Scrolls"; case BOOK: return "Books"; case WAND: return "Wands"; case TOOL: return "Tools"; case VALUABLE: return "Valuables"; case MISC: return "Miscellaneous items"; } return "Warezzz"; } int item::GetResistance(int Type) const { switch(Type&0xFFF) { case PHYSICAL_DAMAGE: return GetStrengthValue(); case SOUND: case ENERGY: case DRAIN: return 0; case FIRE: return GetFireResistance(); case POISON: return GetPoisonResistance(); case ELECTRICITY: return GetElectricityResistance(); case ACID: return GetAcidResistance(); } ABORT("Resistance lack detected!"); return 0; } truth item::Open(character* Char) { if(Char->IsPlayer()) ADD_MESSAGE("You can't open %s.", CHAR_NAME(DEFINITE)); return false; } item* itemprototype::SpawnAndLoad(inputfile& SaveFile) const { item* Item = Spawner(0, LOAD); Item->Load(SaveFile); Item->CalculateAll(); return Item; } void item::LoadDataBaseStats() { SetSize(GetDefaultSize()); } void item::Initialize(int NewConfig, int SpecialFlags) { CalculateSquaresUnder(); Slot = new slot*[SquaresUnder]; for(int c = 0; c < SquaresUnder; ++c) Slot[c] = 0; if(!(SpecialFlags & LOAD)) { ID = game::CreateNewItemID(this); databasecreator<item>::InstallDataBase(this, NewConfig); LoadDataBaseStats(); RandomizeVisualEffects(); Flags |= CENTER << SQUARE_POSITION_SHIFT; if(!(SpecialFlags & NO_MATERIALS)) GenerateMaterials(); } if(!(SpecialFlags & LOAD)) PostConstruct(); if(!(SpecialFlags & (LOAD|NO_MATERIALS))) { CalculateAll(); if(!(SpecialFlags & NO_PIC_UPDATE)) UpdatePictures(); } } truth item::ShowMaterial() const { if(GetMainMaterialConfig().Size == 1) return GetMainMaterial()->GetConfig() != GetMainMaterialConfig()[0]; else return true; } long item::GetBlockModifier() const { if(!IsShield(0)) return GetSize() * GetRoundness() << 1; else return GetSize() * GetRoundness() << 2; } truth item::CanBeSeenByPlayer() const { return CanBeSeenBy(PLAYER); } truth item::CanBeSeenBy(const character* Who) const { for(int c = 0; c < SquaresUnder; ++c) if(Slot[c] && Slot[c]->CanBeSeenBy(Who)) return true; return Who->IsPlayer() && game::GetSeeWholeMapCheatMode(); } festring item::GetDescription(int Case) const { if(CanBeSeenByPlayer()) return GetName(Case); else return CONST_S("something"); } void item::SignalVolumeAndWeightChange() { CalculateVolumeAndWeight(); for(int c = 0; c < SquaresUnder; ++c) if(Slot[c]) Slot[c]->SignalVolumeAndWeightChange(); } void item::CalculateVolumeAndWeight() { Volume = Weight = 0; for(int c = 0; c < GetMaterials(); ++c) if(GetMaterial(c)) { Volume += GetMaterial(c)->GetVolume(); Weight += GetMaterial(c)->GetWeight(); } } void item::SignalEmitationIncrease(col24 EmitationUpdate) { if(game::CompareLights(EmitationUpdate, Emitation) > 0) { game::CombineLights(Emitation, EmitationUpdate); for(int c = 0; c < SquaresUnder; ++c) if(Slot[c]) Slot[c]->SignalEmitationIncrease(EmitationUpdate); } } void item::SignalEmitationDecrease(col24 EmitationUpdate) { if(game::CompareLights(EmitationUpdate, Emitation) >= 0 && Emitation) { col24 Backup = Emitation; CalculateEmitation(); if(Backup != Emitation) for(int c = 0; c < SquaresUnder; ++c) if(Slot[c]) Slot[c]->SignalEmitationDecrease(EmitationUpdate); } } void item::CalculateAll() { CalculateVolumeAndWeight(); CalculateEmitation(); } /* Temporary and buggy. */ void item::WeaponSkillHit(int Hits) { if(Slot[0] && Slot[0]->IsGearSlot()) static_cast<arm*>(static_cast<gearslot*>(*Slot)->GetBodyPart())->WieldedSkillHit(Hits); } /* Returns 0 if item cannot be cloned */ item* item::Duplicate(ulong Flags) { if(!(Flags & IGNORE_PROHIBITIONS) && ((!(Flags & MIRROR_IMAGE) && !CanBeCloned()) || (Flags & MIRROR_IMAGE && (!CanBeMirrored() || (MainMaterial && !(MainMaterial->GetCommonFlags() & CAN_BE_MIRRORED)) || (GetSecondaryMaterial() && !(GetSecondaryMaterial()->GetCommonFlags() & CAN_BE_MIRRORED)))))) return 0; item* Clone = GetProtoType()->Clone(this); if(Flags & MIRROR_IMAGE) Clone->SetLifeExpectancy(Flags >> LE_BASE_SHIFT & LE_BASE_RANGE, Flags >> LE_RAND_SHIFT & LE_RAND_RANGE); idholder* I = new idholder(ID); I->Next = CloneMotherID; CloneMotherID = I; game::RemoveItemID(ID); ID = game::CreateNewItemID(this); Clone->UpdatePictures(); return Clone; } void item::AddInventoryEntry(const character*, festring& Entry, int Amount, truth ShowSpecialInfo) const { if(Amount == 1) AddName(Entry, INDEFINITE); else { Entry << Amount << ' '; AddName(Entry, PLURAL); } if(ShowSpecialInfo) Entry << " [" << GetWeight() * Amount << "g]"; } const itemdatabase* itemprototype::ChooseBaseForConfig(itemdatabase** TempConfig, int Configs, int ConfigNumber) { if(!(ConfigNumber & BROKEN)) return *TempConfig; else { ConfigNumber ^= BROKEN; for(int c = 0; c < Configs; ++c) if(TempConfig[c]->Config == ConfigNumber) return TempConfig[c]; return *TempConfig; } } truth item::ReceiveDamage(character* Damager, int Damage, int Type, int Dir) { if(CanBeBroken() && !IsBroken() && Type & (PHYSICAL_DAMAGE|SOUND|ENERGY|ACID)) { int StrengthValue = GetStrengthValue(); if(!StrengthValue) StrengthValue = 1; if(Damage > StrengthValue << 2 && RAND() & 3 && RAND() % (25 * Damage / StrengthValue) >= 100) { Break(Damager, Dir); return true; } } if(Type & ACID && IsBroken() && IsDestroyable(Damager)) { int StrengthValue = GetStrengthValue(); if(!StrengthValue) StrengthValue = 1; if(Damage > StrengthValue << 4 && !(RAND() & 3) && RAND() % (100 * Damage / StrengthValue) >= 100) { Destroy(Damager, Dir); return true; } } return false; } void itemdatabase::InitDefaults(const itemprototype* NewProtoType, int NewConfig) { IsAbstract = false; ProtoType = NewProtoType; Config = NewConfig; if(NewConfig & BROKEN) { if(Adjective.GetSize()) Adjective.Insert(0, "broken "); else Adjective = CONST_S("broken"); DefaultSize >>= 1; FormModifier >>= 2; StrengthModifier >>= 1; } } long item::GetNutritionValue() const { long NV = 0; for(int c = 0; c < GetMaterials(); ++c) if(GetMaterial(c)) NV += GetMaterial(c)->GetTotalNutritionValue(); return NV; } void item::SignalSpoil(material*) { if(!Exists()) return; if(CanBeSeenByPlayer()) ADD_MESSAGE("%s spoils completely.", GetExtendedDescription().CStr()); truth Equipped = PLAYER->Equips(this); Disappear(); if(Equipped) game::AskForKeyPress(CONST_S("Equipment destroyed! [press any key to continue]")); } item* item::DuplicateToStack(stack* CurrentStack, ulong Flags) { item* Duplicated = Duplicate(Flags); if(!Duplicated) return 0; CurrentStack->AddItem(Duplicated); return Duplicated; } truth item::CanBePiledWith(const item* Item, const character* Viewer) const { return GetType() == Item->GetType() && GetConfig() == Item->GetConfig() && (WeightIsIrrelevant() || Weight == Item->Weight) && MainMaterial->IsSameAs(Item->MainMaterial) && MainMaterial->GetSpoilLevel() == Item->MainMaterial->GetSpoilLevel() && MainMaterial->GetRustLevel() == Item->MainMaterial->GetRustLevel() && Viewer->GetCWeaponSkillLevel(this) == Viewer->GetCWeaponSkillLevel(Item) && Viewer->GetSWeaponSkillLevel(this) == Viewer->GetSWeaponSkillLevel(Item) && !Fluid && !Item->Fluid && !LifeExpectancy == !Item->LifeExpectancy; } void item::Break(character* Breaker, int) { if(CanBeSeenByPlayer()) ADD_MESSAGE("%s %s.", GetExtendedDescription().CStr(), GetBreakVerb()); if(Breaker && IsOnGround()) { room* Room = GetRoom(); if(Room) Room->HostileAction(Breaker); } item* Broken = GetProtoType()->Clone(this); Broken->SetConfig(GetConfig() | BROKEN); Broken->SetSize(Broken->GetSize() >> 1); DonateFluidsTo(Broken); DonateIDTo(Broken); DonateSlotTo(Broken); SendToHell(); if(PLAYER->Equips(Broken)) game::AskForKeyPress(CONST_S("Equipment broken! [press any key to continue]")); } void item::Be() { MainMaterial->Be(); if(Exists() && LifeExpectancy) if(LifeExpectancy == 1) { if(CanBeSeenByPlayer()) ADD_MESSAGE("%s disappears.", GetExtendedDescription().CStr()); truth Equipped = PLAYER->Equips(this); Disappear(); if(Equipped) game::AskForKeyPress(CONST_S("Equipment destroyed! [press any key to continue]")); } else --LifeExpectancy; } int item::GetOfferValue(int Receiver) const { /* Temporary */ int OfferValue = int(sqrt(GetTruePrice())); if(Receiver == GetAttachedGod()) OfferValue <<= 1; else OfferValue >>= 1; return OfferValue; } void item::SignalEnchantmentChange() { for(int c = 0; c < SquaresUnder; ++c) if(Slot[c]) Slot[c]->SignalEnchantmentChange(); } long item::GetEnchantedPrice(int Enchantment) const { return !PriceIsProportionalToEnchantment() ? item::GetPrice() : Max<int>(item::GetPrice() * Enchantment, 0); } item* item::Fix() { item* Fixed = this; if(IsBroken()) { Fixed = GetProtoType()->Clone(this); Fixed->SetConfig(GetConfig() ^ BROKEN); Fixed->SetSize(Fixed->GetSize() << 1); DonateFluidsTo(Fixed); DonateIDTo(Fixed); DonateSlotTo(Fixed); SendToHell(); } return Fixed; } void item::DonateSlotTo(item* Item) { if(Slot[0]) { Slot[0]->DonateTo(Item); Slot[0] = 0; for(int c = 1; c < SquaresUnder; ++c) if(Slot[c]) { Slot[c]->Empty(); Slot[c] = 0; } } } int item::GetSpoilLevel() const { return MainMaterial->GetSpoilLevel(); } void item::SignalSpoilLevelChange(material*) { if(!IsAnimated() && GetSpoilLevel() && Slot[0] && Slot[0]->IsVisible()) for(int c = 0; c < SquaresUnder; ++c) GetSquareUnder(c)->IncStaticAnimatedEntities(); SignalVolumeAndWeightChange(); // gum UpdatePictures(); } truth item::AllowSpoil() const { if(IsOnGround()) { lsquare* Square = GetLSquareUnder(); int RoomNumber = Square->GetRoomIndex(); return !RoomNumber || Square->GetLevel()->GetRoom(RoomNumber)->AllowSpoil(this); } else return true; } void item::ResetSpoiling() { for(int c = 0; c < GetMaterials(); ++c) if(GetMaterial(c)) GetMaterial(c)->ResetSpoiling(); } const char* item::GetBaseToHitValueDescription() const { if(GetBaseToHitValue() < 10) return ToHitValueDescription[Min(GetBaseToHitValue(), 6)]; else return ToHitValueDescription[7]; } const char* item::GetBaseBlockValueDescription() const { if(GetBaseBlockValue() < 20) return ToHitValueDescription[Min(GetBaseBlockValue() >> 1, 6)]; else return ToHitValueDescription[7]; } const char* item::GetStrengthValueDescription() const { int SV = GetStrengthValue(); if(SV < 3) return StrengthValueDescription[0]; else if(SV < 5) return StrengthValueDescription[1]; else if(SV < 8) return StrengthValueDescription[2]; else if(SV < 11) return StrengthValueDescription[3]; else if(SV < 16) return StrengthValueDescription[4]; else if(SV < 20) return StrengthValueDescription[5]; else return StrengthValueDescription[6]; } void item::SpecialGenerationHandler() { if(HandleInPairs()) Slot[0]->AddFriendItem(Duplicate()); } void item::SortAllItems(const sortdata& SortData) const { if(SortData.Sorter == 0 || (this->*SortData.Sorter)(SortData.Character)) SortData.AllItems.push_back(const_cast<item*>(this)); } int item::GetAttachedGod() const { return DataBase->AttachedGod ? DataBase->AttachedGod : MainMaterial->GetAttachedGod(); } long item::GetMaterialPrice() const { return MainMaterial->GetRawPrice(); } long item::GetTruePrice() const { if(LifeExpectancy) return 0; long Price = Max(GetPrice(), GetMaterialPrice()); if(WillSpoil()) Price = Price * (100 - GetMaxSpoilPercentage()) / 500; return Price; } #ifdef WIZARD void item::AddAttackInfo(felist& List) const { festring Entry(40, ' '); Entry << int(GetWeight()); Entry.Resize(50); Entry << int(GetSize()); Entry.Resize(60); Entry << int(GetStrengthRequirement()); Entry.Resize(70); Entry << GetBaseMinDamage() << '-' << GetBaseMaxDamage(); List.AddEntry(Entry, LIGHT_GRAY); } void item::AddMiscellaneousInfo(felist& List) const { festring Entry(40, ' '); Entry << int(GetTruePrice()); Entry.Resize(55); Entry << GetOfferValue(0); Entry.Resize(70); Entry << int(GetNutritionValue()); List.AddEntry(Entry, LIGHT_GRAY); } #endif void item::PreProcessForBone() { if(IsQuestItem()) { RemoveFromSlot(); SendToHell(); } else { game::RemoveItemID(ID); ID = -ID; game::AddItemID(this, ID); } } void item::PostProcessForBone() { boneidmap::iterator BI = game::GetBoneItemIDMap().find(-ID); game::RemoveItemID(ID); if(BI == game::GetBoneItemIDMap().end()) { ulong NewID = game::CreateNewItemID(this); game::GetBoneItemIDMap().insert(std::pair<ulong, ulong>(-ID, NewID)); ID = NewID; } else { if(game::SearchItem(BI->second)) int esko = esko = 2; ID = BI->second; game::AddItemID(this, ID); } for(idholder* I = CloneMotherID; I; I = I->Next) { BI = game::GetBoneItemIDMap().find(I->ID); if(BI == game::GetBoneItemIDMap().end()) { ulong NewCloneMotherID = game::CreateNewItemID(0); game::GetBoneItemIDMap().insert(std::pair<ulong, ulong>(I->ID, NewCloneMotherID)); I->ID = NewCloneMotherID; } else I->ID = BI->second; } } void item::SetConfig(int NewConfig, int SpecialFlags) { databasecreator<item>::InstallDataBase(this, NewConfig); CalculateAll(); if(!(SpecialFlags & NO_PIC_UPDATE)) UpdatePictures(); } god* item::GetMasterGod() const { return game::GetGod(GetConfig()); } int itemprototype::CreateSpecialConfigurations(itemdatabase** TempConfig, int Configs) { if(TempConfig[0]->CreateDivineConfigurations) Configs = databasecreator<item>::CreateDivineConfigurations(this, TempConfig, Configs); /* Gum solution */ if(TempConfig[0]->CreateLockConfigurations) { const item::database*const* KeyConfigData = key::ProtoType.GetConfigData(); int KeyConfigSize = key::ProtoType.GetConfigSize(); int OldConfigs = Configs; for(int c1 = 0; c1 < OldConfigs; ++c1) if(!TempConfig[c1]->IsAbstract) { int BaseConfig = TempConfig[c1]->Config; int NewConfig = BaseConfig | BROKEN_LOCK; itemdatabase* ConfigDataBase = new itemdatabase(*TempConfig[c1]); ConfigDataBase->InitDefaults(this, NewConfig); ConfigDataBase->PostFix << "with a broken lock"; ConfigDataBase->Possibility = 0; TempConfig[Configs++] = ConfigDataBase; for(int c2 = 0; c2 < KeyConfigSize; ++c2) { NewConfig = BaseConfig | KeyConfigData[c2]->Config; ConfigDataBase = new itemdatabase(*TempConfig[c1]); ConfigDataBase->InitDefaults(this, NewConfig); ConfigDataBase->PostFix << "with "; if(KeyConfigData[c2]->UsesLongAdjectiveArticle) ConfigDataBase->PostFix << "an "; else ConfigDataBase->PostFix << "a "; ConfigDataBase->PostFix << KeyConfigData[c2]->Adjective << " lock"; ConfigDataBase->Possibility = 0; TempConfig[Configs++] = ConfigDataBase; } } } return Configs; } void item::Draw(blitdata& BlitData) const { const int AF = GraphicData.AnimationFrames; const int F = !(BlitData.CustomData & ALLOW_ANIMATE) || AF == 1 ? 0 : GET_TICK() & (AF - 1); const bitmap* P = GraphicData.Picture[F]; if(BlitData.CustomData & ALLOW_ALPHA) P->AlphaLuminanceBlit(BlitData); else P->LuminanceMaskedBlit(BlitData); if(Fluid && ShowFluids()) DrawFluids(BlitData); } v2 item::GetLargeBitmapPos(v2 BasePos, int I) const { const int SquareIndex = I ? I / (GraphicData.AnimationFrames >> 2) : 0; return v2(SquareIndex & 1 ? BasePos.X + 16 : BasePos.X, SquareIndex & 2 ? BasePos.Y + 16 : BasePos.Y); } void item::LargeDraw(blitdata& BlitData) const { const int TrueAF = GraphicData.AnimationFrames >> 2; const int SquareIndex = BlitData.CustomData & SQUARE_INDEX_MASK; const int F = !(BlitData.CustomData & ALLOW_ANIMATE) ? SquareIndex * TrueAF : SquareIndex * TrueAF + (GET_TICK() & (TrueAF - 1)); const bitmap* P = GraphicData.Picture[F]; if(BlitData.CustomData & ALLOW_ALPHA) P->AlphaLuminanceBlit(BlitData); else P->LuminanceMaskedBlit(BlitData); } void item::DonateIDTo(item* Item) { game::RemoveItemID(Item->ID); game::UpdateItemID(Item, ID); Item->ID = ID; ID = 0; } void item::SignalRustLevelChange() { SignalVolumeAndWeightChange(); UpdatePictures(); SendNewDrawAndMemorizedUpdateRequest(); } const rawbitmap* item::GetRawPicture() const { return igraph::GetRawGraphic(GetGraphicsContainerIndex()); } void item::RemoveFluid(fluid* ToBeRemoved) { truth WasAnimated = IsAnimated(); truth HasFluids = false; for(int c = 0; c < SquaresUnder; ++c) { fluid* F = Fluid[c]; if(F == ToBeRemoved) Fluid[c] = F->Next; else if(F) { fluid* LF = F; for(F = F->Next; F; LF = F, F = F->Next) if(F == ToBeRemoved) { LF->Next = F->Next; break; } } if(Fluid[c]) HasFluids = true; } UpdatePictures(); if(!HasFluids) { delete [] Fluid; Fluid = 0; if(!IsAnimated() != !WasAnimated && Slot[0]->IsVisible()) GetSquareUnder()->DecStaticAnimatedEntities(); } SignalEmitationDecrease(ToBeRemoved->GetEmitation()); SignalVolumeAndWeightChange(); } void item::AddFluid(liquid* ToBeAdded, festring LocationName, int SquareIndex, truth IsInside) { truth WasAnimated = IsAnimated(); if(Fluid) { fluid* F = Fluid[SquareIndex]; if(!F) Fluid[SquareIndex] = new fluid(ToBeAdded, this, LocationName, IsInside); else { fluid* LF; do { if(ToBeAdded->IsSameAs(F->GetLiquid())) { F->AddLiquidAndVolume(ToBeAdded->GetVolume()); delete ToBeAdded; return; } LF = F; F = F->Next; } while(F); LF->Next = new fluid(ToBeAdded, this, LocationName, IsInside); } } else { Fluid = new fluid*[SquaresUnder]; memset(Fluid, 0, SquaresUnder * sizeof(fluid*)); Fluid[SquareIndex] = new fluid(ToBeAdded, this, LocationName, IsInside); } UpdatePictures(); SignalVolumeAndWeightChange(); SignalEmitationIncrease(ToBeAdded->GetEmitation()); if(Slot[0]) { if(!IsAnimated() != !WasAnimated && Slot[0]->IsVisible()) GetSquareUnder()->IncStaticAnimatedEntities(); SendNewDrawAndMemorizedUpdateRequest(); } } void item::SendNewDrawAndMemorizedUpdateRequest() const { if(!game::IsInWilderness()) for(int c = 0; c < SquaresUnder; ++c) if(Slot[c]) { lsquare* Square = GetLSquareUnder(c); Square->SendNewDrawRequest(); Square->SendMemorizedUpdateRequest(); } } void item::CalculateEmitation() { object::CalculateEmitation(); if(Fluid) for(int c = 0; c < SquaresUnder; ++c) for(const fluid* F = Fluid[c]; F; F = F->Next) game::CombineLights(Emitation, F->GetEmitation()); } void item::FillFluidVector(fluidvector& Vector, int SquareIndex) const { if(Fluid) for(fluid* F = Fluid[SquareIndex]; F; F = F->Next) Vector.push_back(F); } void item::SpillFluid(character*, liquid* Liquid, int SquareIndex) { if(AllowFluids() && Liquid->GetVolume()) AddFluid(Liquid, "", SquareIndex, false); else delete Liquid; } void item::TryToRust(long LiquidModifier) { if(MainMaterial->TryToRust(LiquidModifier)) { if(CanBeSeenByPlayer()) if(MainMaterial->GetRustLevel() == NOT_RUSTED) ADD_MESSAGE("%s rusts.", CHAR_NAME(DEFINITE)); else ADD_MESSAGE("%s rusts more.", CHAR_NAME(DEFINITE)); MainMaterial->SetRustLevel(MainMaterial->GetRustLevel() + 1); } } void item::CheckFluidGearPictures(v2 ShadowPos, int SpecialFlags, truth BodyArmor) { if(Fluid) for(fluid* F = Fluid[0]; F; F = F->Next) F->CheckGearPicture(ShadowPos, SpecialFlags, BodyArmor); } void item::DrawFluidGearPictures(blitdata& BlitData, int SpecialFlags) const { if(Fluid) for(const fluid* F = Fluid[0]; F; F = F->Next) F->DrawGearPicture(BlitData, SpecialFlags); } void item::DrawFluidBodyArmorPictures(blitdata& BlitData, int SpecialFlags) const { if(Fluid) for(const fluid* F = Fluid[0]; F; F = F->Next) F->DrawBodyArmorPicture(BlitData, SpecialFlags); } void item::DrawFluids(blitdata& BlitData) const { const int SquareIndex = BlitData.CustomData & SQUARE_INDEX_MASK; for(const fluid* F = Fluid[SquareIndex]; F; F = F->Next) F->Draw(BlitData); } void item::ReceiveAcid(material*, const festring&, long Modifier) { if(GetMainMaterial()->GetInteractionFlags() & CAN_DISSOLVE) { int Damage = Modifier / 1000; if(Damage) { Damage += RAND() % Damage; ReceiveDamage(0, Damage, ACID); } } } void item::DonateFluidsTo(item* Item) { if(Fluid) for(int c = 0; c < GetSquaresUnder(); ++c) for(fluid* F = Fluid[c]; F; F = F->Next) { liquid* Liquid = F->GetLiquid(); Item->AddFluid(Liquid->SpawnMoreLiquid(Liquid->GetVolume()), F->GetLocationName(), c, F->IsInside()); } } void item::Destroy(character* Destroyer, int) { if(CanBeSeenByPlayer()) ADD_MESSAGE("%s is destroyed.", GetExtendedDescription().CStr()); if(Destroyer && IsOnGround()) { room* Room = GetRoom(); if(Room) Room->HostileAction(Destroyer); } truth Equipped = PLAYER->Equips(this); RemoveFromSlot(); SendToHell(); if(Equipped) game::AskForKeyPress(CONST_S("Equipment destroyed! [press any key to continue]")); } void item::RemoveRust() { for(int c = 0; c < GetMaterials(); ++c) if(GetMaterial(c)) GetMaterial(c)->SetRustLevel(NOT_RUSTED); } void item::SetSpoilPercentage(int Value) { for(int c = 0; c < GetMaterials(); ++c) { material* Material = GetMaterial(c); if(Material && Material->CanSpoil()) Material->SetSpoilCounter(Material->GetSpoilModifier() * Value / 100); } } void item::RedistributeFluids() { if(Fluid) for(int c = 0; c < GetSquaresUnder(); ++c) for(fluid* F = Fluid[c]; F; F = F->Next) F->Redistribute(); } material* item::GetConsumeMaterial(const character* Consumer, materialpredicate Predicate) const { return (MainMaterial->*Predicate)() && Consumer->CanConsume(MainMaterial) ? MainMaterial : 0; } /* The parameter can only be MainMaterial */ material* item::RemoveMaterial(material*) { RemoveFromSlot(); SendToHell(); return 0; } void item::InitMaterials(material* FirstMaterial, truth CallUpdatePictures) { InitMaterial(MainMaterial, FirstMaterial, GetDefaultMainVolume()); SignalVolumeAndWeightChange(); if(CallUpdatePictures) UpdatePictures(); } void item::GenerateMaterials() { int Chosen = RandomizeMaterialConfiguration(); const fearray<long>& MMC = GetMainMaterialConfig(); InitMaterial(MainMaterial, MAKE_MATERIAL(MMC.Data[MMC.Size == 1 ? 0 : Chosen]), GetDefaultMainVolume()); } void item::SignalSquarePositionChange(int Position) { Flags &= ~SQUARE_POSITION_BITS; Flags |= Position << SQUARE_POSITION_SHIFT; } truth item::Read(character* Reader) { Reader->StartReading(this, GetReadDifficulty()); return true; } truth item::CanBeHardened(const character*) const { return MainMaterial->GetHardenedMaterial(this) != NONE; } void item::SetLifeExpectancy(int Base, int RandPlus) { LifeExpectancy = RandPlus > 1 ? Base + RAND_N(RandPlus) : Base; Enable(); } truth item::IsVeryCloseToSpoiling() const { for(int c = 0; c < GetMaterials(); ++c) if(GetMaterial(c) && !GetMaterial(c)->IsVeryCloseToSpoiling()) return false; return true; } truth item::IsValuable() const { if(DataBase->IsValuable) return true; for(int c = 0; c < GetMaterials(); ++c) { material* M = GetMaterial(c); if(M && M->GetCommonFlags() & IS_VALUABLE) return true; } return false; } int item::GetHinderVisibilityBonus(const character* Char) const { int Bonus = 0; if(GetGearStates() & INFRA_VISION && !Char->TemporaryStateIsActivated(INFRA_VISION)) Bonus += 20000; if(GetGearStates() & ESP && !Char->TemporaryStateIsActivated(ESP)) Bonus += 20000; if(!game::IsDark(GetEmitation())) Bonus += 5000; return Bonus; } long item::GetFixPrice() const { item* Clone = GetProtoType()->Clone(this); Clone = Clone->Fix(); Clone->RemoveRust(); long FixPrice = Clone->GetTruePrice(); Clone->SendToHell(); return Max(long(3.5 * sqrt(FixPrice)), 10L); } void item::AddTrapName(festring& String, int Amount) const { if(Amount == 1) AddName(String, DEFINITE); else { String << Amount << ' '; AddName(String, PLURAL); } } truth item::WillSpoil() const { for(int c = 0; c < GetMaterials(); ++c) { const material* Material = GetMaterial(c); if(Material && Material->Spoils()) return true; } return false; } int item::GetMaxSpoilPercentage() const { int MaxPercentage = 0; for(int c = 0; c < GetMaterials(); ++c) { const material* Material = GetMaterial(c); if(Material) MaxPercentage = Max(MaxPercentage, Material->GetSpoilPercentage()); } return MaxPercentage; } truth item::HasPrice() const { return GetPrice() || GetMaterialPrice(); } void item::Disappear() { RemoveFromSlot(); SendToHell(); } outputfile& operator<<(outputfile& SaveFile, const idholder* IdHolder) { SaveFile << IdHolder->ID; return SaveFile; } inputfile& operator>>(inputfile& SaveFile, idholder*& IdHolder) { IdHolder = new idholder(ReadType<ulong>(SaveFile)); return SaveFile; } festring item::GetExtendedDescription() const { if(!CanBeSeenByPlayer()) return CONST_S("something"); festring Desc; const character* Carrier = FindCarrier(); if(Carrier) { if(Carrier->IsPlayer()) { Desc << "your "; AddName(Desc, UNARTICLED); return Desc; } else if(Carrier->CanBeSeenByPlayer()) { Carrier->AddName(Desc, DEFINITE); Desc << "'s "; AddName(Desc, UNARTICLED); return Desc; } } AddName(Desc, DEFINITE); if(IsOnGround()) GetLSquareUnder()->AddLocationDescription(Desc); return Desc; } const character* item::FindCarrier() const { return Slot[0]->FindCarrier(); }