#include "refidadapterimp.hpp" #include #include #include #include #include #include "nestedtablewrapper.hpp" #include "usertype.hpp" namespace { int is_even(double d) { double int_part; modf(d / 2.0, &int_part); return 2.0 * int_part == d; } int round_ieee_754(double d) { double i = floor(d); d -= i; if(d < 0.5) return static_cast(i); if(d > 0.5) return static_cast(i) + 1; if(is_even(i)) return static_cast(i); return static_cast(i) + 1; } std::vector autoCalculateAttributes (const ESM::NPC &npc, const ESM::Race& race, const ESM::Class& class_, const CSMWorld::IdCollection& skillTable) { // race bonus bool male = (npc.mFlags & ESM::NPC::Female) == 0; if (npc.mNpdtType != ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) return std::vector(); short level = npc.mNpdt12.mLevel; int attr[ESM::Attribute::Length]; for (int i = 0; i < ESM::Attribute::Length; ++i) { const ESM::Race::MaleFemale& attribute = race.mData.mAttributeValues[i]; attr[i] = male ? attribute.mMale : attribute.mFemale; } // class bonus for (int i = 0; i < 2; ++i) { int attribute = class_.mData.mAttribute[i]; if (attribute >= 0 && attribute < ESM::Attribute::Length) { attr[attribute] = attr[attribute] + 10; } // else log an error } std::vector result(ESM::Attribute::Length); // skill bonus for (int attribute = 0; attribute < ESM::Attribute::Length; ++attribute) { float modifierSum = 0; for (int j = 0; j < ESM::Skill::Length; ++j) { // if the skill does not exist, throws std::runtime_error ("invalid ID: " + id) const ESM::Skill& skill = skillTable.getRecord(ESM::Skill::indexToId(j)).get(); if (skill.mData.mAttribute != attribute) continue; // is this a minor or major skill? float add = 0.2f; for (int k = 0; k < 5; ++k) { if (class_.mData.mSkills[k][0] == j) add = 0.5; } for (int k = 0; k < 5; ++k) { if (class_.mData.mSkills[k][1] == j) add = 1.0; } modifierSum += add; } result[attribute] = std::min(round_ieee_754(attr[attribute] + (level-1) * modifierSum), 100); } return result; } std::vector autoCalculateSkills (const ESM::NPC &npc, const ESM::Race& race, const ESM::Class& class_, const CSMWorld::IdCollection& skillTable) { unsigned char skills[ESM::Skill::Length]; for (int i = 0; i < ESM::Skill::Length; ++i) skills[i] = 0; for (int i = 0; i < 2; ++i) { int bonus = (i == 0) ? 10 : 25; for (int i2 = 0; i2 < 5; ++i2) { int index = class_.mData.mSkills[i2][i]; if (index >= 0 && index < ESM::Skill::Length) { skills[index] = bonus; } } } std::vector result(ESM::Skill::Length); for (int skillIndex = 0; skillIndex < ESM::Skill::Length; ++skillIndex) { float majorMultiplier = 0.1f; float specMultiplier = 0.0f; int raceBonus = 0; int specBonus = 0; for (int raceSkillIndex = 0; raceSkillIndex < 7; ++raceSkillIndex) { if (race.mData.mBonus[raceSkillIndex].mSkill == skillIndex) { raceBonus = race.mData.mBonus[raceSkillIndex].mBonus; break; } } for (int k = 0; k < 5; ++k) { // is this a minor or major skill? if ((class_.mData.mSkills[k][0] == skillIndex) || (class_.mData.mSkills[k][1] == skillIndex)) { majorMultiplier = 1.0f; break; } } // is this skill in the same Specialization as the class? const ESM::Skill& skill = skillTable.getRecord(ESM::Skill::indexToId(skillIndex)).get(); if (skill.mData.mSpecialization == class_.mData.mSpecialization) { specMultiplier = 0.5f; specBonus = 5; } // Must gracefully handle level 0 result[skillIndex] = std::min(round_ieee_754(skills[skillIndex] + 5 + raceBonus + specBonus + (npc.mNpdt12.mLevel-1) * (majorMultiplier + specMultiplier)), 100); } return result; } unsigned short autoCalculateHealth(const ESM::NPC &npc, const ESM::Class& class_, const std::vector& attr) { int multiplier = 3; if (class_.mData.mSpecialization == ESM::Class::Combat) multiplier += 2; else if (class_.mData.mSpecialization == ESM::Class::Stealth) multiplier += 1; if (class_.mData.mAttribute[0] == ESM::Attribute::Endurance || class_.mData.mAttribute[1] == ESM::Attribute::Endurance) multiplier += 1; return floor(0.5f * (attr[ESM::Attribute::Strength]+ attr[ESM::Attribute::Endurance])) + multiplier * (npc.mNpdt12.mLevel-1); } unsigned short autoCalculateMana(const std::vector& attr) { return attr[ESM::Attribute::Intelligence] * 2; } unsigned short autoCalculateFatigue(const std::vector& attr) { return attr[ESM::Attribute::Strength] + attr[ESM::Attribute::Willpower] + attr[ESM::Attribute::Agility] + attr[ESM::Attribute::Endurance]; } } CSMWorld::PotionColumns::PotionColumns (const InventoryColumns& columns) : InventoryColumns (columns) {} CSMWorld::PotionRefIdAdapter::PotionRefIdAdapter (const PotionColumns& columns, const RefIdColumn *autoCalc) : InventoryRefIdAdapter (UniversalId::Type_Potion, columns), mColumns(columns), mAutoCalc (autoCalc) {} QVariant CSMWorld::PotionRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Potion))); if (column==mAutoCalc) return record.get().mData.mAutoCalc!=0; if (column==mColumns.mEffects) return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() return InventoryRefIdAdapter::getData (column, data, index); } void CSMWorld::PotionRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Potion))); ESM::Potion potion = record.get(); if (column==mAutoCalc) potion.mData.mAutoCalc = value.toInt(); else { InventoryRefIdAdapter::setData (column, data, index, value); return; } record.setModified(potion); } CSMWorld::ApparatusRefIdAdapter::ApparatusRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *type, const RefIdColumn *quality) : InventoryRefIdAdapter (UniversalId::Type_Apparatus, columns), mType (type), mQuality (quality) {} QVariant CSMWorld::ApparatusRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Apparatus))); if (column==mType) return record.get().mData.mType; if (column==mQuality) return record.get().mData.mQuality; return InventoryRefIdAdapter::getData (column, data, index); } void CSMWorld::ApparatusRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Apparatus))); ESM::Apparatus apparatus = record.get(); if (column==mType) apparatus.mData.mType = value.toInt(); else if (column==mQuality) apparatus.mData.mQuality = value.toFloat(); else { InventoryRefIdAdapter::setData (column, data, index, value); return; } record.setModified(apparatus); } CSMWorld::ArmorRefIdAdapter::ArmorRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *type, const RefIdColumn *health, const RefIdColumn *armor, const RefIdColumn *partRef) : EnchantableRefIdAdapter (UniversalId::Type_Armor, columns), mType (type), mHealth (health), mArmor (armor), mPartRef(partRef) {} QVariant CSMWorld::ArmorRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Armor))); if (column==mType) return record.get().mData.mType; if (column==mHealth) return record.get().mData.mHealth; if (column==mArmor) return record.get().mData.mArmor; if (column==mPartRef) return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() return EnchantableRefIdAdapter::getData (column, data, index); } void CSMWorld::ArmorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Armor))); ESM::Armor armor = record.get(); if (column==mType) armor.mData.mType = value.toInt(); else if (column==mHealth) armor.mData.mHealth = value.toInt(); else if (column==mArmor) armor.mData.mArmor = value.toInt(); else { EnchantableRefIdAdapter::setData (column, data, index, value); return; } record.setModified(armor); } CSMWorld::BookRefIdAdapter::BookRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *scroll, const RefIdColumn *skill) : EnchantableRefIdAdapter (UniversalId::Type_Book, columns), mScroll (scroll), mSkill (skill) {} QVariant CSMWorld::BookRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Book))); if (column==mScroll) return record.get().mData.mIsScroll!=0; if (column==mSkill) return record.get().mData.mSkillID; return EnchantableRefIdAdapter::getData (column, data, index); } void CSMWorld::BookRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Book))); ESM::Book book = record.get(); if (column==mScroll) book.mData.mIsScroll = value.toInt(); else if (column==mSkill) book.mData.mSkillID = value.toInt(); else { EnchantableRefIdAdapter::setData (column, data, index, value); return; } record.setModified(book); } CSMWorld::ClothingRefIdAdapter::ClothingRefIdAdapter (const EnchantableColumns& columns, const RefIdColumn *type, const RefIdColumn *partRef) : EnchantableRefIdAdapter (UniversalId::Type_Clothing, columns), mType (type), mPartRef(partRef) {} QVariant CSMWorld::ClothingRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Clothing))); if (column==mType) return record.get().mData.mType; if (column==mPartRef) return true; // to show nested tables in dialogue subview, see IdTree::hasChildren() return EnchantableRefIdAdapter::getData (column, data, index); } void CSMWorld::ClothingRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Clothing))); ESM::Clothing clothing = record.get(); if (column==mType) clothing.mData.mType = value.toInt(); else { EnchantableRefIdAdapter::setData (column, data, index, value); return; } record.setModified(clothing); } CSMWorld::ContainerRefIdAdapter::ContainerRefIdAdapter (const NameColumns& columns, const RefIdColumn *weight, const RefIdColumn *organic, const RefIdColumn *respawn, const RefIdColumn *content) : NameRefIdAdapter (UniversalId::Type_Container, columns), mWeight (weight), mOrganic (organic), mRespawn (respawn), mContent(content) {} QVariant CSMWorld::ContainerRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Container))); if (column==mWeight) return record.get().mWeight; if (column==mOrganic) return (record.get().mFlags & ESM::Container::Organic)!=0; if (column==mRespawn) return (record.get().mFlags & ESM::Container::Respawn)!=0; if (column==mContent) return true; // Required to show nested tables in dialogue subview return NameRefIdAdapter::getData (column, data, index); } void CSMWorld::ContainerRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Container))); ESM::Container container = record.get(); if (column==mWeight) container.mWeight = value.toFloat(); else if (column==mOrganic) { if (value.toInt()) container.mFlags |= ESM::Container::Organic; else container.mFlags &= ~ESM::Container::Organic; } else if (column==mRespawn) { if (value.toInt()) container.mFlags |= ESM::Container::Respawn; else container.mFlags &= ~ESM::Container::Respawn; } else { NameRefIdAdapter::setData (column, data, index, value); return; } record.setModified(container); } CSMWorld::CreatureColumns::CreatureColumns (const ActorColumns& actorColumns) : ActorColumns (actorColumns), mType(NULL), mSoul(NULL), mScale(NULL), mOriginal(NULL), mCombat(NULL), mMagic(NULL), mStealth(NULL) {} CSMWorld::CreatureRefIdAdapter::CreatureRefIdAdapter (const CreatureColumns& columns) : ActorRefIdAdapter (UniversalId::Type_Creature, columns), mColumns (columns) {} QVariant CSMWorld::CreatureRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); if (column==mColumns.mType) return record.get().mData.mType; if (column==mColumns.mSoul) return record.get().mData.mSoul; if (column==mColumns.mScale) return record.get().mScale; if (column==mColumns.mOriginal) return QString::fromUtf8 (record.get().mOriginal.c_str()); if (column==mColumns.mCombat) return static_cast (record.get().mData.mCombat); if (column==mColumns.mMagic) return static_cast (record.get().mData.mMagic); if (column==mColumns.mStealth) return static_cast (record.get().mData.mStealth); std::map::const_iterator iter = mColumns.mFlags.find (column); if (iter!=mColumns.mFlags.end()) return (record.get().mFlags & iter->second)!=0; return ActorRefIdAdapter::getData (column, data, index); } void CSMWorld::CreatureRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); ESM::Creature creature = record.get(); if (column==mColumns.mType) creature.mData.mType = value.toInt(); else if (column==mColumns.mSoul) creature.mData.mSoul = value.toInt(); else if (column==mColumns.mScale) creature.mScale = value.toFloat(); else if (column==mColumns.mOriginal) creature.mOriginal = value.toString().toUtf8().constData(); else if (column==mColumns.mCombat) creature.mData.mCombat = value.toInt(); else if (column==mColumns.mMagic) creature.mData.mMagic = value.toInt(); else if (column==mColumns.mStealth) creature.mData.mStealth = value.toInt(); else { std::map::const_iterator iter = mColumns.mFlags.find (column); if (iter!=mColumns.mFlags.end()) { if (value.toInt()!=0) creature.mFlags |= iter->second; else creature.mFlags &= ~iter->second; } else { ActorRefIdAdapter::setData (column, data, index, value); return; } } record.setModified(creature); } CSMWorld::DoorRefIdAdapter::DoorRefIdAdapter (const NameColumns& columns, const RefIdColumn *openSound, const RefIdColumn *closeSound) : NameRefIdAdapter (UniversalId::Type_Door, columns), mOpenSound (openSound), mCloseSound (closeSound) {} QVariant CSMWorld::DoorRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Door))); if (column==mOpenSound) return QString::fromUtf8 (record.get().mOpenSound.c_str()); if (column==mCloseSound) return QString::fromUtf8 (record.get().mCloseSound.c_str()); return NameRefIdAdapter::getData (column, data, index); } void CSMWorld::DoorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Door))); ESM::Door door = record.get(); if (column==mOpenSound) door.mOpenSound = value.toString().toUtf8().constData(); else if (column==mCloseSound) door.mCloseSound = value.toString().toUtf8().constData(); else { NameRefIdAdapter::setData (column, data, index, value); return; } record.setModified(door); } CSMWorld::LightColumns::LightColumns (const InventoryColumns& columns) : InventoryColumns (columns) {} CSMWorld::LightRefIdAdapter::LightRefIdAdapter (const LightColumns& columns) : InventoryRefIdAdapter (UniversalId::Type_Light, columns), mColumns (columns) {} QVariant CSMWorld::LightRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Light))); if (column==mColumns.mTime) return record.get().mData.mTime; if (column==mColumns.mRadius) return record.get().mData.mRadius; if (column==mColumns.mColor) return record.get().mData.mColor; if (column==mColumns.mSound) return QString::fromUtf8 (record.get().mSound.c_str()); std::map::const_iterator iter = mColumns.mFlags.find (column); if (iter!=mColumns.mFlags.end()) return (record.get().mData.mFlags & iter->second)!=0; return InventoryRefIdAdapter::getData (column, data, index); } void CSMWorld::LightRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Light))); ESM::Light light = record.get(); if (column==mColumns.mTime) light.mData.mTime = value.toInt(); else if (column==mColumns.mRadius) light.mData.mRadius = value.toInt(); else if (column==mColumns.mColor) light.mData.mColor = value.toInt(); else if (column==mColumns.mSound) light.mSound = value.toString().toUtf8().constData(); else { std::map::const_iterator iter = mColumns.mFlags.find (column); if (iter!=mColumns.mFlags.end()) { if (value.toInt()!=0) light.mData.mFlags |= iter->second; else light.mData.mFlags &= ~iter->second; } else { InventoryRefIdAdapter::setData (column, data, index, value); return; } } record.setModified (light); } CSMWorld::MiscRefIdAdapter::MiscRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *key) : InventoryRefIdAdapter (UniversalId::Type_Miscellaneous, columns), mKey (key) {} QVariant CSMWorld::MiscRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Miscellaneous))); if (column==mKey) return record.get().mData.mIsKey!=0; return InventoryRefIdAdapter::getData (column, data, index); } void CSMWorld::MiscRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Miscellaneous))); ESM::Miscellaneous misc = record.get(); if (column==mKey) misc.mData.mIsKey = value.toInt(); else { InventoryRefIdAdapter::setData (column, data, index, value); return; } record.setModified(misc); } CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns) : ActorColumns (actorColumns), mRace(NULL), mClass(NULL), mFaction(NULL), mHair(NULL), mHead(NULL), mAttributes(NULL), mSkills(NULL), mMisc(NULL) {} CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns, const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& skills) : ActorRefIdAdapter (UniversalId::Type_Npc, columns), mColumns (columns), mRaceTable(races), mClassTable(classes), mSkillTable(skills) {} QVariant CSMWorld::NpcRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); if (column==mColumns.mRace) return QString::fromUtf8 (record.get().mRace.c_str()); if (column==mColumns.mClass) return QString::fromUtf8 (record.get().mClass.c_str()); if (column==mColumns.mFaction) return QString::fromUtf8 (record.get().mFaction.c_str()); if (column==mColumns.mHair) return QString::fromUtf8 (record.get().mHair.c_str()); if (column==mColumns.mHead) return QString::fromUtf8 (record.get().mHead.c_str()); if (column==mColumns.mAttributes || column==mColumns.mSkills) { if ((record.get().mFlags & ESM::NPC::Autocalc) != 0) return QVariant(QVariant::UserType); else return true; } if (column==mColumns.mMisc) return true; std::map::const_iterator iter = mColumns.mFlags.find (column); if (iter!=mColumns.mFlags.end()) return (record.get().mFlags & iter->second)!=0; return ActorRefIdAdapter::getData (column, data, index); } void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); ESM::NPC npc = record.get(); if (column==mColumns.mRace) npc.mRace = value.toString().toUtf8().constData(); else if (column==mColumns.mClass) npc.mClass = value.toString().toUtf8().constData(); else if (column==mColumns.mFaction) npc.mFaction = value.toString().toUtf8().constData(); else if (column==mColumns.mHair) npc.mHair = value.toString().toUtf8().constData(); else if (column==mColumns.mHead) npc.mHead = value.toString().toUtf8().constData(); else { std::map::const_iterator iter = mColumns.mFlags.find (column); if (iter!=mColumns.mFlags.end()) { if (value.toInt()!=0) npc.mFlags |= iter->second; else npc.mFlags &= ~iter->second; if (iter->second == ESM::NPC::Autocalc) { if(value.toInt() != 0) { npc.mNpdtType = ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS; // if the race/class does not exist, throws std::runtime_error ("invalid ID: " + id) const ESM::Race& race = mRaceTable.getRecord(npc.mRace).get(); const ESM::Class& class_ = mClassTable.getRecord(npc.mClass).get(); std::vector attr = autoCalculateAttributes(npc, race, class_, mSkillTable); if (attr.empty()) return; std::vector skills = autoCalculateSkills(npc, race, class_, mSkillTable); ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt52; npcStruct.mLevel = npc.mNpdt12.mLevel; npcStruct.mStrength = attr[ESM::Attribute::Strength]; npcStruct.mIntelligence = attr[ESM::Attribute::Intelligence]; npcStruct.mWillpower = attr[ESM::Attribute::Willpower]; npcStruct.mAgility = attr[ESM::Attribute::Agility]; npcStruct.mSpeed = attr[ESM::Attribute::Speed]; npcStruct.mEndurance = attr[ESM::Attribute::Endurance]; npcStruct.mPersonality = attr[ESM::Attribute::Personality]; npcStruct.mLuck = attr[ESM::Attribute::Luck]; for (int i = 0; i < ESM::Skill::Length; ++i) { npcStruct.mSkills[i] = skills[i]; } npcStruct.mHealth = autoCalculateHealth(npc, class_, attr); npcStruct.mMana = autoCalculateMana(attr); npcStruct.mFatigue = autoCalculateFatigue(attr); npcStruct.mDisposition = npc.mNpdt12.mDisposition; npcStruct.mReputation = npc.mNpdt12.mReputation; npcStruct.mRank = npc.mNpdt12.mRank; npcStruct.mGold = npc.mNpdt12.mGold; } else npc.mNpdtType = ESM::NPC::NPC_DEFAULT; } } else { ActorRefIdAdapter::setData (column, data, index, value); return; } } record.setModified (npc); } CSMWorld::NpcAttributesRefIdAdapter::NpcAttributesRefIdAdapter(const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& skills) : mRaceTable(races), mClassTable(classes), mSkillTable(skills) {} void CSMWorld::NpcAttributesRefIdAdapter::addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const { // Do nothing, this table cannot be changed by the user } void CSMWorld::NpcAttributesRefIdAdapter::removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const { // Do nothing, this table cannot be changed by the user } void CSMWorld::NpcAttributesRefIdAdapter::setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); ESM::NPC npc = record.get(); // store the whole struct npc.mNpdt52 = static_cast > &>(nestedTable).mNestedTable.at(0); record.setModified (npc); } CSMWorld::NestedTableWrapperBase* CSMWorld::NpcAttributesRefIdAdapter::nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); // return the whole struct std::vector wrap; wrap.push_back(record.get().mNpdt52); // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(wrap); } QVariant CSMWorld::NpcAttributesRefIdAdapter::getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); const ESM::NPC npc = record.get(); const ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt52; if (subColIndex == 0) switch (subRowIndex) { case 0: return QString("Strength"); case 1: return QString("Intelligence"); case 2: return QString("Willpower"); case 3: return QString("Agility"); case 4: return QString("Speed"); case 5: return QString("Endurance"); case 6: return QString("Personality"); case 7: return QString("Luck"); default: return QVariant(); // throw an exception here? } else if (subColIndex == 1) // It may be possible to have mNpdt52 values different to autocalculated ones when // first loaded, so re-calculate if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) { const ESM::Race& race = mRaceTable.getRecord(npc.mRace).get(); const ESM::Class& class_ = mClassTable.getRecord(npc.mClass).get(); std::vector attr = autoCalculateAttributes(npc, race, class_, mSkillTable); if (attr.empty()) return QVariant(); switch (subRowIndex) { case 0: return static_cast(attr[ESM::Attribute::Strength]); case 1: return static_cast(attr[ESM::Attribute::Intelligence]); case 2: return static_cast(attr[ESM::Attribute::Willpower]); case 3: return static_cast(attr[ESM::Attribute::Agility]); case 4: return static_cast(attr[ESM::Attribute::Speed]); case 5: return static_cast(attr[ESM::Attribute::Endurance]); case 6: return static_cast(attr[ESM::Attribute::Personality]); case 7: return static_cast(attr[ESM::Attribute::Luck]); default: return QVariant(); // throw an exception here? } } else switch (subRowIndex) { case 0: return static_cast(npcStruct.mStrength); case 1: return static_cast(npcStruct.mIntelligence); case 2: return static_cast(npcStruct.mWillpower); case 3: return static_cast(npcStruct.mAgility); case 4: return static_cast(npcStruct.mSpeed); case 5: return static_cast(npcStruct.mEndurance); case 6: return static_cast(npcStruct.mPersonality); case 7: return static_cast(npcStruct.mLuck); default: return QVariant(); // throw an exception here? } else return QVariant(); // throw an exception here? } void CSMWorld::NpcAttributesRefIdAdapter::setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc))); ESM::NPC npc = record.get(); ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt52; if (subColIndex == 1) switch(subRowIndex) { case 0: npcStruct.mStrength = static_cast(value.toInt()); break; case 1: npcStruct.mIntelligence = static_cast(value.toInt()); break; case 2: npcStruct.mWillpower = static_cast(value.toInt()); break; case 3: npcStruct.mAgility = static_cast(value.toInt()); break; case 4: npcStruct.mSpeed = static_cast(value.toInt()); break; case 5: npcStruct.mEndurance = static_cast(value.toInt()); break; case 6: npcStruct.mPersonality = static_cast(value.toInt()); break; case 7: npcStruct.mLuck = static_cast(value.toInt()); break; default: return; // throw an exception here? } else return; // throw an exception here? record.setModified (npc); } int CSMWorld::NpcAttributesRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const { return 2; } int CSMWorld::NpcAttributesRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const { // There are 8 attributes return 8; } CSMWorld::NpcSkillsRefIdAdapter::NpcSkillsRefIdAdapter(const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& skills) : mRaceTable(races), mClassTable(classes), mSkillTable(skills) {} void CSMWorld::NpcSkillsRefIdAdapter::addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const { // Do nothing, this table cannot be changed by the user } void CSMWorld::NpcSkillsRefIdAdapter::removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const { // Do nothing, this table cannot be changed by the user } void CSMWorld::NpcSkillsRefIdAdapter::setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); ESM::NPC npc = record.get(); // store the whole struct npc.mNpdt52 = static_cast > &>(nestedTable).mNestedTable.at(0); record.setModified (npc); } CSMWorld::NestedTableWrapperBase* CSMWorld::NpcSkillsRefIdAdapter::nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); // return the whole struct std::vector wrap; wrap.push_back(record.get().mNpdt52); // deleted by dtor of NestedTableStoring return new NestedTableWrapper >(wrap); } QVariant CSMWorld::NpcSkillsRefIdAdapter::getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); const ESM::NPC npc = record.get(); if (subRowIndex < 0 || subRowIndex >= ESM::Skill::Length) throw std::runtime_error ("index out of range"); if (subColIndex == 0) return QString(ESM::Skill::sSkillNames[subRowIndex].c_str()); else if (subColIndex == 1) { // It may be possible to have mNpdt52 values different to autocalculated ones when // first loaded, so re-calculate if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) { // if the race/class does not exist, throws std::runtime_error ("invalid ID: " + id) const ESM::Race& race = mRaceTable.getRecord(npc.mRace).get(); const ESM::Class& class_ = mClassTable.getRecord(npc.mClass).get(); std::vector skills = autoCalculateSkills(npc, race, class_, mSkillTable); int value = static_cast(skills[subRowIndex]); return static_cast(skills[subRowIndex]); } else { const ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt52; return static_cast(npcStruct.mSkills[subRowIndex]); } } else return QVariant(); // throw an exception here? } void CSMWorld::NpcSkillsRefIdAdapter::setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc))); ESM::NPC npc = record.get(); ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt52; if (subRowIndex < 0 || subRowIndex >= ESM::Skill::Length) throw std::runtime_error ("index out of range"); if (subColIndex == 1) npcStruct.mSkills[subRowIndex] = static_cast(value.toInt()); else return; // throw an exception here? record.setModified (npc); } int CSMWorld::NpcSkillsRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const { return 2; } int CSMWorld::NpcSkillsRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const { // There are 27 skills return ESM::Skill::Length; } CSMWorld::NpcMiscRefIdAdapter::NpcMiscRefIdAdapter(const CSMWorld::IdCollection& races, const CSMWorld::IdCollection& classes, const CSMWorld::IdCollection& skills) : mRaceTable(races), mClassTable(classes), mSkillTable(skills) {} CSMWorld::NpcMiscRefIdAdapter::~NpcMiscRefIdAdapter() {} void CSMWorld::NpcMiscRefIdAdapter::addNestedRow (const RefIdColumn *column, RefIdData& data, int index, int position) const { throw std::logic_error ("cannot add a row to a fixed table"); } void CSMWorld::NpcMiscRefIdAdapter::removeNestedRow (const RefIdColumn *column, RefIdData& data, int index, int rowToRemove) const { throw std::logic_error ("cannot remove a row to a fixed table"); } void CSMWorld::NpcMiscRefIdAdapter::setNestedTable (const RefIdColumn* column, RefIdData& data, int index, const NestedTableWrapperBase& nestedTable) const { throw std::logic_error ("table operation not supported"); } CSMWorld::NestedTableWrapperBase* CSMWorld::NpcMiscRefIdAdapter::nestedTable (const RefIdColumn* column, const RefIdData& data, int index) const { throw std::logic_error ("table operation not supported"); } QVariant CSMWorld::NpcMiscRefIdAdapter::getNestedData (const RefIdColumn *column, const RefIdData& data, int index, int subRowIndex, int subColIndex) const { const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); const ESM::NPC npc = record.get(); bool autoCalc = (npc.mFlags & ESM::NPC::Autocalc) != 0; // It may be possible to have mNpdt52 values different to autocalculated ones when // first loaded, so re-calculate if (autoCalc) { // if the race/class does not exist, throws std::runtime_error ("invalid ID: " + id) const ESM::Race& race = mRaceTable.getRecord(npc.mRace).get(); const ESM::Class& class_ = mClassTable.getRecord(npc.mClass).get(); std::vector attr = autoCalculateAttributes(npc, race, class_, mSkillTable); if (attr.empty()) return QVariant(); switch (subColIndex) { case 0: return static_cast(npc.mNpdt12.mLevel); case 1: { UserInt i(0); // unknown return QVariant(QVariant::fromValue(i)); } case 2: { UserInt i(autoCalculateHealth(npc, class_, attr)); return QVariant(QVariant::fromValue(i)); } case 3: { UserInt i(autoCalculateMana(attr)); return QVariant(QVariant::fromValue(i)); } case 4: { UserInt i(autoCalculateFatigue(attr)); return QVariant(QVariant::fromValue(i)); } case 5: return static_cast(record.get().mNpdt12.mDisposition); case 6: return static_cast(record.get().mNpdt12.mReputation); case 7: return static_cast(record.get().mNpdt12.mRank); case 8: return record.get().mNpdt12.mGold; case 9: return record.get().mPersistent == true; default: return QVariant(); // throw an exception here? } } else switch (subColIndex) { case 0: return static_cast(record.get().mNpdt52.mLevel); case 1: return static_cast(record.get().mNpdt52.mFactionID); case 2: return static_cast(record.get().mNpdt52.mHealth); case 3: return static_cast(record.get().mNpdt52.mMana); case 4: return static_cast(record.get().mNpdt52.mFatigue); case 5: return static_cast(record.get().mNpdt52.mDisposition); case 6: return static_cast(record.get().mNpdt52.mReputation); case 7: return static_cast(record.get().mNpdt52.mRank); case 8: return record.get().mNpdt52.mGold; case 9: return record.get().mPersistent == true; default: return QVariant(); // throw an exception here? } } void CSMWorld::NpcMiscRefIdAdapter::setNestedData (const RefIdColumn *column, RefIdData& data, int row, const QVariant& value, int subRowIndex, int subColIndex) const { Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (row, UniversalId::Type_Npc))); ESM::NPC npc = record.get(); bool autoCalc = (record.get().mFlags & ESM::NPC::Autocalc) != 0; if (autoCalc) switch(subColIndex) { case 0: { npc.mNpdt12.mLevel = static_cast(value.toInt()); break; const ESM::Race& race = mRaceTable.getRecord(npc.mRace).get(); const ESM::Class& class_ = mClassTable.getRecord(npc.mClass).get(); std::vector attr = autoCalculateAttributes(npc, race, class_, mSkillTable); if (attr.empty()) return; ESM::NPC::NPDTstruct52& npcStruct = npc.mNpdt52; std::vector skills = autoCalculateSkills(npc, race, class_, mSkillTable); npcStruct.mLevel = npc.mNpdt12.mLevel; npcStruct.mStrength = attr[ESM::Attribute::Strength]; npcStruct.mIntelligence = attr[ESM::Attribute::Intelligence]; npcStruct.mWillpower = attr[ESM::Attribute::Willpower]; npcStruct.mAgility = attr[ESM::Attribute::Agility]; npcStruct.mSpeed = attr[ESM::Attribute::Speed]; npcStruct.mEndurance = attr[ESM::Attribute::Endurance]; npcStruct.mPersonality = attr[ESM::Attribute::Personality]; npcStruct.mLuck = attr[ESM::Attribute::Luck]; for (int i = 0; i < ESM::Skill::Length; ++i) { npcStruct.mSkills[i] = skills[i]; } npcStruct.mHealth = autoCalculateHealth(npc, class_, attr); npcStruct.mMana = autoCalculateMana(attr); npcStruct.mFatigue = autoCalculateFatigue(attr); break; } case 1: return; case 2: return; case 3: return; case 4: return; case 5: { npc.mNpdt12.mDisposition = static_cast(value.toInt()); npc.mNpdt52.mDisposition = npc.mNpdt12.mDisposition; break; } case 6: { npc.mNpdt12.mReputation = static_cast(value.toInt()); npc.mNpdt52.mReputation = npc.mNpdt12.mReputation; break; } case 7: { npc.mNpdt12.mRank = static_cast(value.toInt()); npc.mNpdt52.mRank = npc.mNpdt12.mRank; break; } case 8: { npc.mNpdt12.mGold = value.toInt(); npc.mNpdt52.mGold = npc.mNpdt12.mGold; break; } case 9: npc.mPersistent = value.toBool(); break; default: return; // throw an exception here? } else switch(subColIndex) { case 0: { npc.mNpdt52.mLevel = static_cast(value.toInt()); npc.mNpdt12.mLevel = npc.mNpdt52.mLevel; break; } case 1: npc.mNpdt52.mFactionID = static_cast(value.toInt()); break; case 2: npc.mNpdt52.mHealth = static_cast(value.toInt()); break; case 3: npc.mNpdt52.mMana = static_cast(value.toInt()); break; case 4: npc.mNpdt52.mFatigue = static_cast(value.toInt()); break; case 5: { npc.mNpdt52.mDisposition = static_cast(value.toInt()); npc.mNpdt12.mDisposition = npc.mNpdt52.mDisposition; break; } case 6: { npc.mNpdt52.mReputation = static_cast(value.toInt()); npc.mNpdt12.mReputation = npc.mNpdt52.mReputation; break; } case 7: { npc.mNpdt52.mRank = static_cast(value.toInt()); npc.mNpdt12.mRank = npc.mNpdt52.mRank; break; } case 8: { npc.mNpdt52.mGold = value.toInt(); npc.mNpdt12.mGold = npc.mNpdt52.mGold; break; } case 9: npc.mPersistent = value.toBool(); break; default: return; // throw an exception here? } record.setModified (npc); } int CSMWorld::NpcMiscRefIdAdapter::getNestedColumnsCount(const RefIdColumn *column, const RefIdData& data) const { return 10; // Level, FactionID, Health, Mana, Fatigue, Disposition, Reputation, Rank, Gold, Persist } int CSMWorld::NpcMiscRefIdAdapter::getNestedRowsCount(const RefIdColumn *column, const RefIdData& data, int index) const { return 1; // fixed at size 1 } CSMWorld::WeaponColumns::WeaponColumns (const EnchantableColumns& columns) : EnchantableColumns (columns) {} CSMWorld::WeaponRefIdAdapter::WeaponRefIdAdapter (const WeaponColumns& columns) : EnchantableRefIdAdapter (UniversalId::Type_Weapon, columns), mColumns (columns) {} QVariant CSMWorld::WeaponRefIdAdapter::getData (const RefIdColumn *column, const RefIdData& data, int index) const { const Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Weapon))); if (column==mColumns.mType) return record.get().mData.mType; if (column==mColumns.mHealth) return record.get().mData.mHealth; if (column==mColumns.mSpeed) return record.get().mData.mSpeed; if (column==mColumns.mReach) return record.get().mData.mReach; for (int i=0; i<2; ++i) { if (column==mColumns.mChop[i]) return record.get().mData.mChop[i]; if (column==mColumns.mSlash[i]) return record.get().mData.mSlash[i]; if (column==mColumns.mThrust[i]) return record.get().mData.mThrust[i]; } std::map::const_iterator iter = mColumns.mFlags.find (column); if (iter!=mColumns.mFlags.end()) return (record.get().mData.mFlags & iter->second)!=0; return EnchantableRefIdAdapter::getData (column, data, index); } void CSMWorld::WeaponRefIdAdapter::setData (const RefIdColumn *column, RefIdData& data, int index, const QVariant& value) const { Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Weapon))); if (column==mColumns.mType) record.get().mData.mType = value.toInt(); else if (column==mColumns.mHealth) record.get().mData.mHealth = value.toInt(); else if (column==mColumns.mSpeed) record.get().mData.mSpeed = value.toFloat(); else if (column==mColumns.mReach) record.get().mData.mReach = value.toFloat(); else if (column==mColumns.mChop[0]) record.get().mData.mChop[0] = value.toInt(); else if (column==mColumns.mChop[1]) record.get().mData.mChop[1] = value.toInt(); else if (column==mColumns.mSlash[0]) record.get().mData.mSlash[0] = value.toInt(); else if (column==mColumns.mSlash[1]) record.get().mData.mSlash[1] = value.toInt(); else if (column==mColumns.mThrust[0]) record.get().mData.mThrust[0] = value.toInt(); else if (column==mColumns.mThrust[1]) record.get().mData.mThrust[1] = value.toInt(); else { std::map::const_iterator iter = mColumns.mFlags.find (column); if (iter!=mColumns.mFlags.end()) { if (value.toInt()!=0) record.get().mData.mFlags |= iter->second; else record.get().mData.mFlags &= ~iter->second; } else EnchantableRefIdAdapter::setData (column, data, index, value); } }