diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index e2dffdbde..5c3cd0dcc 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -38,7 +38,7 @@ opencs_units (model/tools opencs_units_noqt (model/tools mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck - birthsigncheck spellcheck + birthsigncheck spellcheck referenceablecheck ) diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp new file mode 100644 index 000000000..dab61bfff --- /dev/null +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -0,0 +1,1095 @@ +#include "referenceablecheck.hpp" +#include "../world/record.hpp" +#include "../world/universalid.hpp" +#include + +CSMTools::ReferenceableCheckStage::ReferenceableCheckStage( + const CSMWorld::RefIdData& referenceable, const CSMWorld::IdCollection& races, + const CSMWorld::IdCollection& classes, + const CSMWorld::IdCollection& faction) + : + mReferencables(referenceable), + mClasses(classes), + mRaces(races), + mFactions(faction), + mPlayerPresent(false) +{ +} + +void CSMTools::ReferenceableCheckStage::perform(int stage, std::vector< std::string >& messages) +{ + //Checks for books, than, when stage is above mBooksSize goes to other checks, with (stage - PrevSum) as stage. + const int bookSize(mReferencables.getBooks().getSize()); + + if (stage < bookSize) + { + bookCheck(stage, mReferencables.getBooks(), messages); + return; + } + + stage -= bookSize; + + const int activatorSize(mReferencables.getActivators().getSize()); + + if (stage < activatorSize) + { + activatorCheck(stage, mReferencables.getActivators(), messages); + return; + } + + stage -= activatorSize; + + const int potionSize(mReferencables.getPotions().getSize()); + + if (stage < potionSize) + { + potionCheck(stage, mReferencables.getPotions(), messages); + return; + } + + stage -= potionSize; + + const int apparatusSize(mReferencables.getApparati().getSize()); + + if (stage < apparatusSize) + { + apparatusCheck(stage, mReferencables.getApparati(), messages); + return; + } + + stage -= apparatusSize; + + const int armorSize(mReferencables.getArmors().getSize()); + + if (stage < armorSize) + { + armorCheck(stage, mReferencables.getArmors(), messages); + return; + } + + stage -= armorSize; + + const int clothingSize(mReferencables.getClothing().getSize()); + + if (stage < clothingSize) + { + clothingCheck(stage, mReferencables.getClothing(), messages); + return; + } + + stage -= clothingSize; + + const int containerSize(mReferencables.getContainers().getSize()); + + if (stage < containerSize) + { + containerCheck(stage, mReferencables.getContainers(), messages); + return; + } + + stage -= containerSize; + + const int doorSize(mReferencables.getDoors().getSize()); + + if (stage < doorSize) + { + doorCheck(stage, mReferencables.getDoors(), messages); + return; + } + + stage -= doorSize; + + const int ingredientSize(mReferencables.getIngredients().getSize()); + + if (stage < ingredientSize) + { + ingredientCheck(stage, mReferencables.getIngredients(), messages); + return; + } + + stage -= ingredientSize; + + const int creatureLevListSize(mReferencables.getCreatureLevelledLists().getSize()); + + if (stage < creatureLevListSize) + { + creaturesLevListCheck(stage, mReferencables.getCreatureLevelledLists(), messages); + return; + } + + stage -= creatureLevListSize; + + const int itemLevelledListSize(mReferencables.getItemLevelledList().getSize()); + + if (stage < itemLevelledListSize) + { + itemLevelledListCheck(stage, mReferencables.getItemLevelledList(), messages); + return; + } + + stage -= itemLevelledListSize; + + const int lightSize(mReferencables.getLights().getSize()); + + if (stage < lightSize) + { + lightCheck(stage, mReferencables.getLights(), messages); + return; + } + + stage -= lightSize; + + const int lockpickSize(mReferencables.getLocpicks().getSize()); + + if (stage < lockpickSize) + { + lockpickCheck(stage, mReferencables.getLocpicks(), messages); + return; + } + + stage -= lockpickSize; + + const int miscSize(mReferencables.getMiscellaneous().getSize()); + + if (stage < miscSize) + { + miscCheck(stage, mReferencables.getMiscellaneous(), messages); + return; + } + + stage -= miscSize; + + const int npcSize(mReferencables.getNPCs().getSize()); + + if (stage < npcSize) + { + npcCheck(stage, mReferencables.getNPCs(), messages); + return; + } + + stage -= npcSize; + + const int weaponSize(mReferencables.getWeapons().getSize()); + + if (stage < weaponSize) + { + weaponCheck(stage, mReferencables.getWeapons(), messages); + return; + } + + stage -= weaponSize; + + const int probeSize(mReferencables.getProbes().getSize()); + + if (stage < probeSize) + { + probeCheck(stage, mReferencables.getProbes(), messages); + return; + } + + stage -= probeSize; + + const int repairSize(mReferencables.getRepairs().getSize()); + + if (stage < repairSize) + { + repairCheck(stage, mReferencables.getRepairs(), messages); + return; + } + + stage -= repairSize; + + const int staticSize(mReferencables.getStatics().getSize()); + + if (stage < staticSize) + { + staticCheck(stage, mReferencables.getStatics(), messages); + return; + } + + stage -= staticSize; + + const int creatureSize(mReferencables.getCreatures().getSize()); + + if (stage < creatureSize) + { + creatureCheck(stage, mReferencables.getCreatures(), messages); + return; + } +// if we come that far, we are about to perform our last, final check. + finalCheck(messages); + return; +} + +int CSMTools::ReferenceableCheckStage::setup() +{ + mPlayerPresent = false; + return mReferencables.getSize() + 1; +} + +void CSMTools::ReferenceableCheckStage::bookCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Book >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::Book& book = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Book, book.mId); + + inventoryItemCheck(book, messages, id.toString(), true); +} + +void CSMTools::ReferenceableCheckStage::activatorCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Activator >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::Activator& activator = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Activator, activator.mId); + + //Checking for model, IIRC all activators should have a model + if (activator.mModel.empty()) + { + messages.push_back(id.toString() + "|" + activator.mId + " has no model"); + } +} + +void CSMTools::ReferenceableCheckStage::potionCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Potion >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::Potion& potion = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Potion, potion.mId); + + inventoryItemCheck(potion, messages, id.toString()); + //IIRC potion can have empty effects list just fine. +} + + +void CSMTools::ReferenceableCheckStage::apparatusCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Apparatus >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::Apparatus& apparatus = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Apparatus, apparatus.mId); + + inventoryItemCheck(apparatus, messages, id.toString()); + + toolCheck(apparatus, messages, id.toString()); +} + +void CSMTools::ReferenceableCheckStage::armorCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Armor >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::Armor& armor = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Armor, armor.mId); + + inventoryItemCheck(armor, messages, id.toString(), true); + + //checking for armor class, armor should have poistive armor class, but 0 is considered legal + if (armor.mData.mArmor < 0) + { + messages.push_back(id.toString() + "|" + armor.mId + " has negative armor class"); + } + + //checking for health. Only positive numbers are allowed, or 0 is illegal + if (armor.mData.mHealth <= 0) + { + messages.push_back(id.toString() + "|" + armor.mId + " has non positive health"); + } +} + +void CSMTools::ReferenceableCheckStage::clothingCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Clothing >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::Clothing& clothing = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Clothing, clothing.mId); + inventoryItemCheck(clothing, messages, id.toString(), true); +} + +void CSMTools::ReferenceableCheckStage::containerCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Container >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::Container& container = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Container, container.mId); + + //Checking for model, IIRC all containers should have a model + if (container.mModel.empty()) + { + messages.push_back(id.toString() + "|" + container.mId + " has no model"); + } + + //Checking for capacity (weight) + if (container.mWeight < 0) //0 is allowed + { + messages.push_back(id.toString() + "|" + container.mId + " has negative weight (capacity)"); + } + + //checking for name + if (container.mName.empty()) + { + messages.push_back(id.toString() + "|" + container.mId + " has an empty name"); + } +} + +void CSMTools::ReferenceableCheckStage::creatureCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Creature >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::Creature& creature = (dynamic_cast&>(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Creature, creature.mId); + + if (creature.mModel.empty()) + { + messages.push_back(id.toString() + "|" + creature.mId + " has no model"); + } + + if (creature.mName.empty()) + { + messages.push_back(id.toString() + "|" + creature.mId + " has an empty name"); + } + + //stats checks + if (creature.mData.mLevel < 1) + { + messages.push_back(id.toString() + "|" + creature.mId + " has non-postive level"); + } + + if (creature.mData.mStrength < 0) + { + messages.push_back(id.toString() + "|" + creature.mId + " has negative strength"); + } + + if (creature.mData.mIntelligence < 0) + { + messages.push_back(id.toString() + "|" + creature.mId + " has negative intelligence"); + } + + if (creature.mData.mWillpower < 0) + { + messages.push_back(id.toString() + "|" + creature.mId + " has negative willpower"); + } + + if (creature.mData.mAgility < 0) + { + messages.push_back(id.toString() + "|" + creature.mId + " has negative agility"); + } + + if (creature.mData.mSpeed < 0) + { + messages.push_back(id.toString() + "|" + creature.mId + " has negative speed"); + } + + if (creature.mData.mEndurance < 0) + { + messages.push_back(id.toString() + "|" + creature.mId + " has negative endurance"); + } + + if (creature.mData.mPersonality < 0) + { + messages.push_back(id.toString() + "|" + creature.mId + " has negative personality"); + } + + if (creature.mData.mLuck < 0) + { + messages.push_back(id.toString() + "|" + creature.mId + " has negative luck"); + } + + if (creature.mData.mHealth < 0) + { + messages.push_back(id.toString() + "|" + creature.mId + " has negative health"); + } + + if (creature.mData.mSoul < 0) + { + messages.push_back(id.toString() + "|" + creature.mId + " has negative soul value"); + } + + for (int i = 0; i < 6; ++i) + { + if (creature.mData.mAttack[i] < 0) + { + messages.push_back(id.toString() + "|" + creature.mId + " has negative attack strength"); + break; + } + } + + //TODO, find meaning of other values + if (creature.mData.mGold < 0) //It seems that this is for gold in merchant creatures + { + messages.push_back(id.toString() + "|" + creature.mId + " has negative gold "); + } +} + +void CSMTools::ReferenceableCheckStage::doorCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Door >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::Door& Door = (dynamic_cast&>(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Door, Door.mId); + + //usual, name or model + if (Door.mName.empty()) + { + messages.push_back(id.toString() + "|" + Door.mId + " has an empty name"); + } + + if (Door.mModel.empty()) + { + messages.push_back(id.toString() + "|" + Door.mId + " has no model"); + } + + //TODO, check what static unsigned int sRecordId; is for +} + +void CSMTools::ReferenceableCheckStage::ingredientCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Ingredient >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::Ingredient& Ingredient = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Ingredient, Ingredient.mId); + + inventoryItemCheck(Ingredient, messages, id.toString()); +} + +void CSMTools::ReferenceableCheckStage::creaturesLevListCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::CreatureLevList >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::CreatureLevList& CreatureLevList = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_CreatureLevelledList, CreatureLevList.mId); //CreatureLevList but Type_CreatureLevelledList :/ + + listCheck(CreatureLevList, messages, id.toString()); +} + +void CSMTools::ReferenceableCheckStage::itemLevelledListCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::ItemLevList >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::ItemLevList& ItemLevList = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_ItemLevelledList, ItemLevList.mId); + + listCheck(ItemLevList, messages, id.toString()); +} + +void CSMTools::ReferenceableCheckStage::lightCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Light >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::Light& light = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Light, light.mId); + + if (light.mData.mRadius < 0) + { + messages.push_back(id.toString() + "|" + light.mId + " has negative light radius"); + } + + if (light.mData.mFlags & ESM::Light::Carry) + { + inventoryItemCheck(light, messages, id.toString()); + + if (light.mData.mTime == 0) + { + messages.push_back(id.toString() + "|" + light.mId + " has zero duration"); + } + } +} + +void CSMTools::ReferenceableCheckStage::lockpickCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Lockpick >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::Lockpick& lockpick = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Lockpick, lockpick.mId); + + inventoryItemCheck(lockpick, messages, id.toString()); + + toolCheck(lockpick, messages, id.toString(), true); +} + +void CSMTools::ReferenceableCheckStage::miscCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Miscellaneous >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::Miscellaneous& miscellaneous = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Miscellaneous, miscellaneous.mId); + + inventoryItemCheck(miscellaneous, messages, id.toString()); +} + +void CSMTools::ReferenceableCheckStage::npcCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::NPC >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::NPC& npc = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Npc, npc.mId); + + short level(npc.mNpdt52.mLevel); + char disposition(npc.mNpdt52.mDisposition); + char reputation(npc.mNpdt52.mReputation); + char rank(npc.mNpdt52.mRank); + //Don't know what unknown is for + int gold(npc.mNpdt52.mGold); + + //Detect if player is present + if (Misc::StringUtils::ciEqual(npc.mId, "player")) //Happy now, scrawl? + { + mPlayerPresent = true; + } + + if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated + { + if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0008 = autocalculated flag + { + messages.push_back(id.toString() + "|" + npc.mId + " mNpdtType or flags mismatch!"); //should not happend? + return; + } + + level = npc.mNpdt12.mLevel; + disposition = npc.mNpdt12.mDisposition; + reputation = npc.mNpdt12.mReputation; + rank = npc.mNpdt12.mRank; + gold = npc.mNpdt12.mGold; + } + else + { + if (npc.mNpdt52.mMana < 0) + { + messages.push_back(id.toString() + "|" + npc.mId + " mana has negative value"); + } + + if (npc.mNpdt52.mFatigue < 0) + { + messages.push_back(id.toString() + "|" + npc.mId + " fatigue has negative value"); + } + + if (npc.mNpdt52.mAgility == 0) + { + messages.push_back(id.toString() + "|" + npc.mId + " agility has zero value"); + } + + if (npc.mNpdt52.mEndurance == 0) + { + messages.push_back(id.toString() + "|" + npc.mId + " endurance has zero value"); + } + + if (npc.mNpdt52.mIntelligence == 0) + { + messages.push_back(id.toString() + "|" + npc.mId + " intelligence has zero value"); + } + + if (npc.mNpdt52.mLuck == 0) + { + messages.push_back(id.toString() + "|" + npc.mId + " luck has zero value"); + } + + if (npc.mNpdt52.mPersonality == 0) + { + messages.push_back(id.toString() + "|" + npc.mId + " personality has zero value"); + } + + if (npc.mNpdt52.mStrength == 0) + { + messages.push_back(id.toString() + "|" + npc.mId + " strength has zero value"); + } + + if (npc.mNpdt52.mSpeed == 0) + { + messages.push_back(id.toString() + "|" + npc.mId + " speed has zero value"); + } + + if (npc.mNpdt52.mWillpower == 0) + { + messages.push_back(id.toString() + "|" + npc.mId + " willpower has zero value"); + } + } + + if (level < 1) + { + messages.push_back(id.toString() + "|" + npc.mId + " level is non positive"); + } + + if (gold < 0) + { + messages.push_back(id.toString() + "|" + npc.mId + " gold has negative value"); + } + + if (npc.mName.empty()) + { + messages.push_back(id.toString() + "|" + npc.mId + " has any empty name"); + } + + if (npc.mClass.empty()) + { + messages.push_back(id.toString() + "|" + npc.mId + " has any empty class"); + } + else //checking if there is such class + { + if (mClasses.searchId(npc.mClass) == -1) + { + messages.push_back(id.toString() + "|" + npc.mId + " has invalid class"); + } + } + + if (npc.mRace.empty()) + { + messages.push_back(id.toString() + "|" + npc.mId + " has any empty race"); + } + else //checking if there is a such race + { + if (mRaces.searchId(npc.mRace) == -1) + { + messages.push_back(id.toString() + "|" + npc.mId + " has invalid race"); + } + } + + if (disposition < 0) + { + messages.push_back(id.toString() + "|" + npc.mId + " has negative disposition"); + } + + if (reputation < 0) //It seems that no character in Morrowind.esm have negative reputation. I'm assuming that negative reputation is invalid + { + messages.push_back(id.toString() + "|" + npc.mId + " has negative reputation"); + } + + if (npc.mFaction.empty() == false) + { + if (rank < 0) + { + messages.push_back(id.toString() + "|" + npc.mId + " has negative rank"); + } + + if (mFactions.searchId(npc.mFaction) == -1) + { + messages.push_back(id.toString() + "|" + npc.mId + " has invalid faction"); + } + } + + if (npc.mHead.empty()) + { + messages.push_back(id.toString() + "|" + npc.mId + " has no head"); + } + + if (npc.mHair.empty()) + { + messages.push_back(id.toString() + "|" + npc.mId + " has no hair"); + } + + //TODO: reputation, Disposition, rank, everything else +} + +void CSMTools::ReferenceableCheckStage::weaponCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Weapon >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::Weapon& weapon = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Weapon, weapon.mId); + + //TODO, It seems that this stuff for spellcasting is obligatory and In fact We should check if records are present + if + ( //THOSE ARE HARDCODED! + !(weapon.mId == "VFX_Hands" || + weapon.mId == "VFX_Absorb" || + weapon.mId == "VFX_Reflect" || + weapon.mId == "VFX_DefaultBolt" || + //TODO I don't know how to get full list of effects :/ + //DANGER!, ACHTUNG! FIXME! The following is the list of the magical bolts, valid for Morrowind.esm. However those are not hardcoded. + weapon.mId == "magic_bolt" || + weapon.mId == "shock_bolt" || + weapon.mId == "shield_bolt" || + weapon.mId == "VFX_DestructBolt" || + weapon.mId == "VFX_PoisonBolt" || + weapon.mId == "VFX_RestoreBolt" || + weapon.mId == "VFX_AlterationBolt" || + weapon.mId == "VFX_ConjureBolt" || + weapon.mId == "VFX_FrostBolt" || + weapon.mId == "VFX_MysticismBolt" || + weapon.mId == "VFX_IllusionBolt" || + weapon.mId == "VFX_Multiple2" || + weapon.mId == "VFX_Multiple3" || + weapon.mId == "VFX_Multiple4" || + weapon.mId == "VFX_Multiple5" || + weapon.mId == "VFX_Multiple6" || + weapon.mId == "VFX_Multiple7" || + weapon.mId == "VFX_Multiple8" || + weapon.mId == "VFX_Multiple9")) + { + inventoryItemCheck(weapon, messages, id.toString(), true); + + if (!(weapon.mData.mType == ESM::Weapon::MarksmanBow || + weapon.mData.mType == ESM::Weapon::MarksmanCrossbow || + weapon.mData.mType == ESM::Weapon::MarksmanThrown || + weapon.mData.mType == ESM::Weapon::Arrow || + weapon.mData.mType == ESM::Weapon::Bolt)) + { + if (weapon.mData.mSlash[0] > weapon.mData.mSlash[1]) + { + messages.push_back(id.toString() + "|" + weapon.mId + " has minimum slash damage higher than maximum"); + } + + if (weapon.mData.mThrust[0] > weapon.mData.mThrust[1]) + { + messages.push_back(id.toString() + "|" + weapon.mId + " has minimum thrust damage higher than maximum"); + } + } + + if (weapon.mData.mChop[0] > weapon.mData.mChop[1]) + { + messages.push_back(id.toString() + "|" + weapon.mId + " has minimum chop damage higher than maximum"); + } + + if (!(weapon.mData.mType == ESM::Weapon::Arrow || + weapon.mData.mType == ESM::Weapon::Bolt || + weapon.mData.mType == ESM::Weapon::MarksmanThrown)) + { + //checking of health + if (weapon.mData.mHealth <= 0) + { + messages.push_back(id.toString() + "|" + weapon.mId + " has non-positivie health"); + } + + if (weapon.mData.mReach < 0) + { + messages.push_back(id.toString() + "|" + weapon.mId + " has negative reach"); + } + } + } +} + +void CSMTools::ReferenceableCheckStage::probeCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Probe >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::Probe& probe = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Probe, probe.mId); + + inventoryItemCheck(probe, messages, id.toString()); + toolCheck(probe, messages, id.toString(), true); +} + +void CSMTools::ReferenceableCheckStage::repairCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Repair >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::Repair& repair = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Repair, repair.mId); + + inventoryItemCheck(repair, messages, id.toString()); + toolCheck(repair, messages, id.toString(), true); +} + +void CSMTools::ReferenceableCheckStage::staticCheck( + int stage, + const CSMWorld::RefIdDataContainer< ESM::Static >& records, + std::vector< std::string >& messages) +{ + const CSMWorld::RecordBase& baseRecord = records.getRecord(stage); + + if (baseRecord.isDeleted()) + { + return; + } + + const ESM::Static& staticElement = (dynamic_cast& >(baseRecord)).get(); + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Static, staticElement.mId); + + if (staticElement.mModel.empty()) + { + messages.push_back(id.toString() + "|" + staticElement.mId + " has no model"); + } +} + +//final check + +void CSMTools::ReferenceableCheckStage::finalCheck(std::vector< std::string >& messages) +{ + if (!mPlayerPresent) + { + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Npc); + messages.push_back(id.toString() + "| There is no player record"); + } +} + + +//Templates begins here + +template void CSMTools::ReferenceableCheckStage::inventoryItemCheck( + const ITEM& someItem, + std::vector< std::string >& messages, + const std::string& someID, bool enchantable) +{ + if (someItem.mName.empty()) + { + messages.push_back(someID + "|" + someItem.mId + " has an empty name"); + } + + //Checking for weight + if (someItem.mData.mWeight < 0) + { + messages.push_back(someID + "|" + someItem.mId + " has negative weight"); + } + + //Checking for value + if (someItem.mData.mValue < 0) + { + messages.push_back(someID + "|" + someItem.mId + " has negative value"); + } + +//checking for model + if (someItem.mModel.empty()) + { + messages.push_back(someID + "|" + someItem.mId + " has no model"); + } + + //checking for icon + if (someItem.mIcon.empty()) + { + messages.push_back(someID + "|" + someItem.mId + " has no icon"); + } + + if (enchantable) + { + if (someItem.mData.mEnchant < 0) + { + messages.push_back(someID + "|" + someItem.mId + " has negative enchantment"); + } + } +} + +template void CSMTools::ReferenceableCheckStage::inventoryItemCheck( + const ITEM& someItem, + std::vector< std::string >& messages, + const std::string& someID) +{ + if (someItem.mName.empty()) + { + messages.push_back(someID + "|" + someItem.mId + " has an empty name"); + } + + //Checking for weight + if (someItem.mData.mWeight < 0) + { + messages.push_back(someID + "|" + someItem.mId + " has negative weight"); + } + + //Checking for value + if (someItem.mData.mValue < 0) + { + messages.push_back(someID + "|" + someItem.mId + " has negative value"); + } + + //checking for model + if (someItem.mModel.empty()) + { + messages.push_back(someID + "|" + someItem.mId + " has no model"); + } + + //checking for icon + if (someItem.mIcon.empty()) + { + messages.push_back(someID + "|" + someItem.mId + " has no icon"); + } +} + +template void CSMTools::ReferenceableCheckStage::toolCheck( + const TOOL& someTool, + std::vector< std::string >& messages, + const std::string& someID, bool canBeBroken) +{ + if (someTool.mData.mQuality <= 0) + { + messages.push_back(someID + "|" + someTool.mId + " has non-positive quality"); + } + + if (canBeBroken) + { + if (someTool.mData.mUses <= 0) + { + messages.push_back(someID + "|" + someTool.mId + " has non-positive uses count"); + } + } +} + +template void CSMTools::ReferenceableCheckStage::toolCheck( + const TOOL& someTool, + std::vector< std::string >& messages, + const std::string& someID) +{ + if (someTool.mData.mQuality <= 0) + { + messages.push_back(someID + "|" + someTool.mId + " has non-positive quality"); + } +} + +template void CSMTools::ReferenceableCheckStage::listCheck( + const LIST& someList, + std::vector< std::string >& messages, + const std::string& someID) +{ + for (unsigned i = 0; i < someList.mList.size(); ++i) + { + if (mReferencables.searchId(someList.mList[i].mId).first == -1) + { + messages.push_back(someID + "|" + someList.mId + " contains item without referencable"); + } + + if (someList.mList[i].mLevel < 1) + { + messages.push_back(someID + "|" + someList.mId + " contains item with non-positive level"); + } + } +} +// kate: indent-mode cstyle; indent-width 4; replace-tabs on; diff --git a/apps/opencs/model/tools/referenceablecheck.hpp b/apps/opencs/model/tools/referenceablecheck.hpp new file mode 100644 index 000000000..338983cc7 --- /dev/null +++ b/apps/opencs/model/tools/referenceablecheck.hpp @@ -0,0 +1,78 @@ +#ifndef REFERENCEABLECHECKSTAGE_H +#define REFERENCEABLECHECKSTAGE_H + +#include "../world/universalid.hpp" +#include "../doc/stage.hpp" +#include "../world/data.hpp" +#include "../world/refiddata.hpp" + +namespace CSMTools +{ + class ReferenceableCheckStage : public CSMDoc::Stage + { + public: + ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable, + const CSMWorld::IdCollection& races, + const CSMWorld::IdCollection& classes, + const CSMWorld::IdCollection& factions); + + virtual void perform(int stage, std::vector< std::string >& messages); + virtual int setup(); + + private: + //CONCRETE CHECKS + void bookCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Book >& records, std::vector< std::string >& messages); + void activatorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Activator >& records, std::vector< std::string >& messages); + void potionCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void apparatusCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void armorCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void clothingCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void containerCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void creatureCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void doorCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void ingredientCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void creaturesLevListCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void itemLevelledListCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void lightCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void lockpickCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void miscCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void npcCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void weaponCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void probeCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void repairCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + void staticCheck(int stage, const CSMWorld::RefIdDataContainer& records, std::vector& messages); + + //FINAL CHECK + void finalCheck(std::vector& messages); + + //TEMPLATE CHECKS + template void inventoryItemCheck(const ITEM& someItem, + std::vector& messages, + const std::string& someID, + bool enchantable); //for all enchantable items. + + template void inventoryItemCheck(const ITEM& someItem, + std::vector& messages, + const std::string& someID); //for non-enchantable items. + + template void toolCheck(const TOOL& someTool, + std::vector& messages, + const std::string& someID, + bool canbebroken); //for tools with uses. + + template void toolCheck(const TOOL& someTool, + std::vector& messages, + const std::string& someID); //for tools without uses. + + template void listCheck(const LIST& someList, + std::vector< std::string >& messages, + const std::string& someID); + + const CSMWorld::RefIdData& mReferencables; + const CSMWorld::IdCollection& mRaces; + const CSMWorld::IdCollection& mClasses; + const CSMWorld::IdCollection& mFactions; + bool mPlayerPresent; + }; +} +#endif // REFERENCEABLECHECKSTAGE_H diff --git a/apps/opencs/model/tools/reportmodel.cpp b/apps/opencs/model/tools/reportmodel.cpp index b12531875..d88361746 100644 --- a/apps/opencs/model/tools/reportmodel.cpp +++ b/apps/opencs/model/tools/reportmodel.cpp @@ -68,4 +68,4 @@ void CSMTools::ReportModel::add (const std::string& row) const CSMWorld::UniversalId& CSMTools::ReportModel::getUniversalId (int row) const { return mRows.at (row).first; -} \ No newline at end of file +} diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index cd4653280..64e39ad2f 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -19,6 +19,7 @@ #include "regioncheck.hpp" #include "birthsigncheck.hpp" #include "spellcheck.hpp" +#include "referenceablecheck.hpp" CSMDoc::Operation *CSMTools::Tools::get (int type) { @@ -74,6 +75,8 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier() mVerifier->appendStage (new BirthsignCheckStage (mData.getBirthsigns())); mVerifier->appendStage (new SpellCheckStage (mData.getSpells())); + + mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions())); } return mVerifier; @@ -138,4 +141,5 @@ void CSMTools::Tools::verifierMessage (const QString& message, int type) if (iter!=mActiveReports.end()) mReports[iter->second]->add (message.toStdString()); -} \ No newline at end of file +} + diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 86a542c5c..9ed526818 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -549,3 +549,9 @@ void CSMWorld::RefIdCollection::save (int index, ESM::ESMWriter& writer) const { mData.save (index, writer); } + +const CSMWorld::RefIdData& CSMWorld::RefIdCollection::getDataSet() const +{ + return mData; +} + diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index 5ff4a70bf..328680d85 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -107,7 +107,10 @@ namespace CSMWorld /// \return Success? void save (int index, ESM::ESMWriter& writer) const; + + const RefIdData& getDataSet() const; //I can't figure out a better name for this one :( }; } #endif + diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 8f59b0fe7..65990c1d4 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -230,4 +230,104 @@ void CSMWorld::RefIdData::save (int index, ESM::ESMWriter& writer) const throw std::logic_error ("invalid local index type"); iter->second->save (localIndex.first, writer); +} + +const CSMWorld::RefIdDataContainer< ESM::Book >& CSMWorld::RefIdData::getBooks() const +{ + return mBooks; +} + +const CSMWorld::RefIdDataContainer< ESM::Activator >& CSMWorld::RefIdData::getActivators() const +{ + return mActivators; +} + +const CSMWorld::RefIdDataContainer< ESM::Potion >& CSMWorld::RefIdData::getPotions() const +{ + return mPotions; +} + +const CSMWorld::RefIdDataContainer< ESM::Apparatus >& CSMWorld::RefIdData::getApparati() const +{ + return mApparati; +} + +const CSMWorld::RefIdDataContainer< ESM::Armor >& CSMWorld::RefIdData::getArmors() const +{ + return mArmors; +} + +const CSMWorld::RefIdDataContainer< ESM::Clothing >& CSMWorld::RefIdData::getClothing() const +{ + return mClothing; +} + +const CSMWorld::RefIdDataContainer< ESM::Container >& CSMWorld::RefIdData::getContainers() const +{ + return mContainers; +} + +const CSMWorld::RefIdDataContainer< ESM::Creature >& CSMWorld::RefIdData::getCreatures() const +{ + return mCreatures; +} + +const CSMWorld::RefIdDataContainer< ESM::Door >& CSMWorld::RefIdData::getDoors() const +{ + return mDoors; +} + +const CSMWorld::RefIdDataContainer< ESM::Ingredient >& CSMWorld::RefIdData::getIngredients() const +{ + return mIngredients; +} + +const CSMWorld::RefIdDataContainer< ESM::CreatureLevList >& CSMWorld::RefIdData::getCreatureLevelledLists() const +{ + return mCreatureLevelledLists; +} + +const CSMWorld::RefIdDataContainer< ESM::ItemLevList >& CSMWorld::RefIdData::getItemLevelledList() const +{ + return mItemLevelledLists; +} + +const CSMWorld::RefIdDataContainer< ESM::Light >& CSMWorld::RefIdData::getLights() const +{ + return mLights; +} + +const CSMWorld::RefIdDataContainer< ESM::Lockpick >& CSMWorld::RefIdData::getLocpicks() const +{ + return mLockpicks; +} + +const CSMWorld::RefIdDataContainer< ESM::Miscellaneous >& CSMWorld::RefIdData::getMiscellaneous() const +{ + return mMiscellaneous; +} + +const CSMWorld::RefIdDataContainer< ESM::NPC >& CSMWorld::RefIdData::getNPCs() const +{ + return mNpcs; +} + +const CSMWorld::RefIdDataContainer< ESM::Weapon >& CSMWorld::RefIdData::getWeapons() const +{ + return mWeapons; +} + +const CSMWorld::RefIdDataContainer< ESM::Probe >& CSMWorld::RefIdData::getProbes() const +{ + return mProbes; +} + +const CSMWorld::RefIdDataContainer< ESM::Repair >& CSMWorld::RefIdData::getRepairs() const +{ + return mRepairs; +} + +const CSMWorld::RefIdDataContainer< ESM::Static >& CSMWorld::RefIdData::getStatics() const +{ + return mStatics; } \ No newline at end of file diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 9595ab23b..a204334b3 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -219,7 +219,33 @@ namespace CSMWorld /// \param listDeleted include deleted record in the list void save (int index, ESM::ESMWriter& writer) const; + + //RECORD CONTAINERS ACCESS METHODS + const RefIdDataContainer& getBooks() const; + const RefIdDataContainer& getActivators() const; + const RefIdDataContainer& getPotions() const; + const RefIdDataContainer& getApparati() const; + const RefIdDataContainer& getArmors() const; + const RefIdDataContainer& getClothing() const; + const RefIdDataContainer& getContainers() const; + const RefIdDataContainer& getCreatures() const; + const RefIdDataContainer& getDoors() const; + const RefIdDataContainer& getIngredients() const; + const RefIdDataContainer& getCreatureLevelledLists() const; + const RefIdDataContainer& getItemLevelledList() const; + const RefIdDataContainer& getLights() const; + const RefIdDataContainer& getLocpicks() const; + const RefIdDataContainer& getMiscellaneous() const; + const RefIdDataContainer& getNPCs() const; + const RefIdDataContainer& getWeapons() const; + const RefIdDataContainer& getProbes() const; + const RefIdDataContainer& getRepairs() const; + const RefIdDataContainer& getStatics() const; }; } #endif + + + + diff --git a/components/esm/loadcrea.hpp b/components/esm/loadcrea.hpp index 9e48f7c1a..b5ea49508 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -66,7 +66,7 @@ struct Creature int mCombat, mMagic, mStealth; // Don't know yet. int mAttack[6]; // AttackMin1, AttackMax1, ditto2, ditto3 int mGold; - }; // 96 bytes + }; // 96 byte NPDTstruct mData; diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index 1eac8d64f..08f678b45 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -105,7 +105,7 @@ struct NPC char mNpdtType; NPDTstruct52 mNpdt52; - NPDTstruct12 mNpdt12; // Use this if npdt52.gold == -10 + NPDTstruct12 mNpdt12; //for autocalculated characters int mFlags;