diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index 8227fcfcf0..d20bb4619c 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -1264,7 +1264,8 @@ namespace EsmTool template <> void Record::print() { - std::cout << " ID: " << skillLabel(mData.mIndex) << " (" << mData.mIndex << ")" << std::endl; + int index = ESM::Skill::refIdToIndex(mData.mId); + std::cout << " ID: " << skillLabel(index) << " (" << index << ")" << std::endl; std::cout << " Description: " << mData.mDescription << std::endl; std::cout << " Governing Attribute: " << attributeLabel(mData.mData.mAttribute) << " (" << mData.mData.mAttribute << ")" << std::endl; diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 9ee2a8bc48..f604608c7b 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -201,11 +201,10 @@ void CSMDoc::Document::createBase() addGmsts(); - for (int i = 0; i < 27; ++i) + for (int i = 0; i < ESM::Skill::Length; ++i) { ESM::Skill record; - record.mIndex = i; - record.mId = ESM::Skill::indexToRefId(record.mIndex); + record.mId = *ESM::Skill::indexToRefId(i).getIf(); record.blank(); getData().getSkills().add(record); diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 17207ae154..9db6b3b042 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -15,6 +15,7 @@ #include #include +#include #include #include "collectionbase.hpp" @@ -86,6 +87,13 @@ namespace CSMWorld return ESM::RefId::stringRefId(LandTexture::createUniqueRecordId(record.mPluginIndex, record.mIndex)); } + inline void setRecordId(const ESM::RefId& id, ESM::Skill& record) + { + if (const auto* skillId = id.getIf()) + record.mId = *skillId; + throw std::runtime_error("Invalid skill id: " + id.toDebugString()); + } + /// \brief Single-type record collection template class Collection : public CollectionBase diff --git a/apps/opencs/model/world/columnimp.cpp b/apps/opencs/model/world/columnimp.cpp index 7d43d1e4f3..b514550479 100644 --- a/apps/opencs/model/world/columnimp.cpp +++ b/apps/opencs/model/world/columnimp.cpp @@ -26,8 +26,6 @@ namespace CSMWorld { switch (value.getRecordType()) { - case ESM::REC_SKIL: - return ESM::Skill::sSkillNames[value.getValue()]; case ESM::REC_MGEF: return std::string(ESM::MagicEffect::sIndexNames[value.getValue()]); default: @@ -335,10 +333,10 @@ namespace CSMWorld std::optional getSkillIndex(std::string_view value) { - const auto it = std::find(std::begin(ESM::Skill::sSkillNames), std::end(ESM::Skill::sSkillNames), value); - if (it == std::end(ESM::Skill::sSkillNames)) + int index = ESM::Skill::refIdToIndex(ESM::RefId::stringRefId(value)); + if (index < 0) return std::nullopt; - return static_cast(it - std::begin(ESM::Skill::sSkillNames)); + return static_cast(index); } std::string getStringId(ESM::RefId value) diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 3f137fadb7..5e5ff83fcf 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -409,7 +409,8 @@ namespace CSMWorld QVariant get(const Record& record) const override { - return QString::fromStdString(ESM::Skill::sSkillNames[record.get().mData.getSkill(mIndex, mMajor)]); + return QString::fromStdString( + ESM::Skill::indexToRefId(record.get().mData.getSkill(mIndex, mMajor)).getRefIdString()); } void set(Record& record, const QVariant& data) override diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 59aefc715f..bdba113038 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -121,11 +121,12 @@ namespace // is this a minor or major skill? float add = 0.2f; + int index = ESM::Skill::refIdToIndex(skill.mId); for (const auto& skills : class_->mData.mSkills) { - if (skills[0] == skill.mIndex) + if (skills[0] == index) add = 0.5; - if (skills[1] == skill.mIndex) + if (skills[1] == index) add = 1.0; } modifierSum += add; @@ -199,15 +200,16 @@ namespace int raceBonus = 0; int specBonus = 0; + int index = ESM::Skill::refIdToIndex(skill.mId); auto bonusIt = std::find_if(race->mData.mBonus.begin(), race->mData.mBonus.end(), - [&](const auto& bonus) { return bonus.mSkill == skill.mIndex; }); + [&](const auto& bonus) { return bonus.mSkill == index; }); if (bonusIt != race->mData.mBonus.end()) raceBonus = bonusIt->mBonus; for (const auto& skills : class_->mData.mSkills) { // is this a minor or major skill? - if (std::find(skills.begin(), skills.end(), skill.mIndex) != skills.end()) + if (std::find(skills.begin(), skills.end(), index) != skills.end()) { majorMultiplier = 1.0f; break; diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 114238482d..3987264fc6 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -471,8 +471,8 @@ namespace MWGui assert(minorSkills.size() >= klass.mData.mSkills.size()); for (size_t i = 0; i < klass.mData.mSkills.size(); ++i) { - klass.mData.mSkills[i][1] = majorSkills[i].getIf()->getValue(); - klass.mData.mSkills[i][0] = minorSkills[i].getIf()->getValue(); + klass.mData.mSkills[i][1] = ESM::Skill::refIdToIndex(majorSkills[i]); + klass.mData.mSkills[i][0] = ESM::Skill::refIdToIndex(minorSkills[i]); } MWBase::Environment::get().getMechanicsManager()->setPlayerClass(klass); diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 0c326049e8..7105112cca 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -287,7 +287,7 @@ namespace MWGui void EditEffectDialog::setSkill(ESM::RefId skill) { - mEffect.mSkill = skill.getIf()->getValue(); + mEffect.mSkill = ESM::Skill::refIdToIndex(skill); eventEffectModified(mEffect); } diff --git a/apps/openmw/mwlua/luabindings.cpp b/apps/openmw/mwlua/luabindings.cpp index 5d4c538538..e922eb7db5 100644 --- a/apps/openmw/mwlua/luabindings.cpp +++ b/apps/openmw/mwlua/luabindings.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/statemanager.hpp" @@ -156,10 +157,18 @@ namespace MWLua return sol::nil; }; - sol::table skill(context.mLua->sol(), sol::create); - api["SKILL"] = LuaUtil::makeStrictReadOnly(skill); - for (int id = 0; id < ESM::Skill::Length; ++id) - skill[ESM::Skill::sSkillNames[id]] = Misc::StringUtils::lowerCase(ESM::Skill::sSkillNames[id]); + // TODO: deprecate this and provide access to the store instead + sol::table skills(context.mLua->sol(), sol::create); + api["SKILL"] = LuaUtil::makeStrictReadOnly(skills); + for (int i = 0; i < ESM::Skill::Length; ++i) + { + ESM::RefId skill = ESM::Skill::indexToRefId(i); + std::string id = skill.serializeText(); + std::string key = Misc::StringUtils::lowerCase(skill.getRefIdString()); + // force first character to uppercase for backwards compatability + key[0] += 'A' - 'a'; + skills[key] = id; + } sol::table attribute(context.mLua->sol(), sol::create); api["ATTRIBUTE"] = LuaUtil::makeStrictReadOnly(attribute); diff --git a/apps/openmw/mwlua/magicbindings.cpp b/apps/openmw/mwlua/magicbindings.cpp index 51d43b8523..586c50be6d 100644 --- a/apps/openmw/mwlua/magicbindings.cpp +++ b/apps/openmw/mwlua/magicbindings.cpp @@ -309,10 +309,10 @@ namespace MWLua }); effectParamsT["affectedSkill"] = sol::readonly_property([](const ESM::ENAMstruct& params) -> sol::optional { - if (params.mSkill >= 0 && params.mSkill < ESM::Skill::Length) - return Misc::StringUtils::lowerCase(ESM::Skill::sSkillNames[params.mSkill]); - else - return sol::nullopt; + ESM::RefId id = ESM::Skill::indexToRefId(params.mSkill); + if (!id.empty()) + return id.serializeText(); + return sol::nullopt; }); effectParamsT["affectedAttribute"] = sol::readonly_property([](const ESM::ENAMstruct& params) -> sol::optional { @@ -383,11 +383,12 @@ namespace MWLua activeEffectT["affectedSkill"] = sol::readonly_property([magicEffectStore](const ActiveEffect& effect) -> sol::optional { auto* rec = magicEffectStore->find(effect.key.mId); - if ((rec->mData.mFlags & ESM::MagicEffect::TargetSkill) && effect.key.mArg >= 0 - && effect.key.mArg < ESM::Skill::Length) - return Misc::StringUtils::lowerCase(ESM::Skill::sSkillNames[effect.key.mArg]); - else - return sol::nullopt; + if (rec->mData.mFlags & ESM::MagicEffect::TargetSkill) + { + ESM::RefId id = ESM::Skill::indexToRefId(effect.key.mArg); + return id.serializeText(); + } + return sol::nullopt; }); activeEffectT["affectedAttribute"] = sol::readonly_property([magicEffectStore](const ActiveEffect& effect) -> sol::optional { @@ -620,7 +621,10 @@ namespace MWLua key = MWMechanics::EffectKey(id, ESM::Attribute::stringToAttributeId(argStr.value())); if (rec->mData.mFlags & ESM::MagicEffect::TargetSkill) - key = MWMechanics::EffectKey(id, ESM::Skill::stringToSkillId(argStr.value())); + { + ESM::RefId skill = ESM::RefId::deserializeText(argStr.value()); + key = MWMechanics::EffectKey(id, ESM::Skill::refIdToIndex(skill)); + } } return key; diff --git a/apps/openmw/mwlua/stats.cpp b/apps/openmw/mwlua/stats.cpp index da4d2a4614..1eb96ebad0 100644 --- a/apps/openmw/mwlua/stats.cpp +++ b/apps/openmw/mwlua/stats.cpp @@ -392,7 +392,6 @@ namespace MWLua sol::table skills(context.mLua->sol(), sol::create); npcStats["skills"] = LuaUtil::makeReadOnly(skills); for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get()) - skills[Misc::StringUtils::lowerCase(ESM::Skill::sSkillNames[skill.mIndex])] - = addIndexedAccessor(skill.mId); + skills[ESM::RefId(skill.mId).serializeText()] = addIndexedAccessor(skill.mId); } } diff --git a/apps/openmw/mwlua/types/book.cpp b/apps/openmw/mwlua/types/book.cpp index f7120e529a..4b40837604 100644 --- a/apps/openmw/mwlua/types/book.cpp +++ b/apps/openmw/mwlua/types/book.cpp @@ -41,18 +41,14 @@ namespace book.mData.mValue = rec["value"]; book.mData.mIsScroll = rec["isScroll"]; - std::string_view skill = rec["skill"].get(); + ESM::RefId skill = ESM::RefId::deserializeText(rec["skill"].get()); book.mData.mSkillId = -1; - if (skill.length() > 0) + if (!skill.empty()) { - for (std::size_t i = 0; i < std::size(ESM::Skill::sSkillNames); ++i) - { - if (Misc::StringUtils::ciEqual(ESM::Skill::sSkillNames[i], skill)) - book.mData.mSkillId = i; - } + book.mData.mSkillId = ESM::Skill::refIdToIndex(skill); if (book.mData.mSkillId == -1) - throw std::runtime_error("Incorrect skill: " + std::string(skill)); + throw std::runtime_error("Incorrect skill: " + skill.toDebugString()); } return book; } @@ -69,7 +65,7 @@ namespace MWLua book["createRecordDraft"] = tableToBook; for (int id = 0; id < ESM::Skill::Length; ++id) { - std::string skillName = Misc::StringUtils::lowerCase(ESM::Skill::sSkillNames[id]); + std::string skillName = ESM::Skill::indexToRefId(id).serializeText(); skill[skillName] = skillName; } @@ -100,10 +96,10 @@ namespace MWLua record["enchantCapacity"] = sol::readonly_property([](const ESM::Book& rec) -> float { return rec.mData.mEnchant * 0.1f; }); record["skill"] = sol::readonly_property([](const ESM::Book& rec) -> sol::optional { - if (rec.mData.mSkillId >= 0) - return Misc::StringUtils::lowerCase(ESM::Skill::sSkillNames[rec.mData.mSkillId]); - else - return sol::nullopt; + ESM::RefId skill = ESM::Skill::indexToRefId(rec.mData.mSkillId); + if (!skill.empty()) + return skill.serializeText(); + return sol::nullopt; }); } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 135c8e4660..8d89e81f12 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -158,9 +158,9 @@ namespace MWMechanics for (const ESM::Skill& skill : esmStore.get()) { int bonus = 0; - + int index = ESM::Skill::refIdToIndex(skill.mId); auto bonusIt = std::find_if(race->mData.mBonus.begin(), race->mData.mBonus.end(), - [&](const auto& bonus) { return bonus.mSkill == skill.mIndex; }); + [&](const auto& bonus) { return bonus.mSkill == index; }); if (bonusIt != race->mData.mBonus.end()) bonus = bonusIt->mBonus; diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index ce779418af..f182a09944 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -155,15 +155,15 @@ float MWMechanics::NpcStats::getSkillProgressRequirement(ESM::RefId id, const ES const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get().find(id); float typeFactor = gmst.find("fMiscSkillBonus")->mValue.getFloat(); - + int index = ESM::Skill::refIdToIndex(skill->mId); for (const auto& skills : class_.mData.mSkills) { - if (skills[0] == skill->mIndex) + if (skills[0] == index) { typeFactor = gmst.find("fMinorSkillBonus")->mValue.getFloat(); break; } - else if (skills[1] == skill->mIndex) + else if (skills[1] == index) { typeFactor = gmst.find("fMajorSkillBonus")->mValue.getFloat(); break; @@ -228,15 +228,16 @@ void MWMechanics::NpcStats::increaseSkill(ESM::RefId id, const ESM::Class& class // is this a minor or major skill? int increase = gmst.find("iLevelupMiscMultAttriubte")->mValue.getInteger(); // Note: GMST has a typo + int index = ESM::Skill::refIdToIndex(skill->mId); for (const auto& skills : class_.mData.mSkills) { - if (skills[0] == skill->mIndex) + if (skills[0] == index) { mLevelProgress += gmst.find("iLevelUpMinorMult")->mValue.getInteger(); increase = gmst.find("iLevelUpMinorMultAttribute")->mValue.getInteger(); break; } - else if (skills[1] == skill->mIndex) + else if (skills[1] == index) { mLevelProgress += gmst.find("iLevelUpMajorMult")->mValue.getInteger(); increase = gmst.find("iLevelUpMajorMultAttribute")->mValue.getInteger(); @@ -463,7 +464,7 @@ void MWMechanics::NpcStats::writeState(ESM::NpcStats& state) const for (const auto& [id, value] : mSkills) { // TODO extend format - auto index = id.getIf()->getValue(); + auto index = ESM::Skill::refIdToIndex(id); value.writeState(state.mSkills[index]); } diff --git a/apps/openmw/mwmechanics/weapontype.cpp b/apps/openmw/mwmechanics/weapontype.cpp index 0612ca1a2e..9dd5842f58 100644 --- a/apps/openmw/mwmechanics/weapontype.cpp +++ b/apps/openmw/mwmechanics/weapontype.cpp @@ -18,253 +18,325 @@ namespace MWMechanics template <> struct Weapon { - inline static const ESM::WeaponType sValue{ /* short group */ "", - /* long group */ "", - /* sound ID */ "", - /* attach bone */ "", - /* sheath bone */ "", - /* usage skill */ ESM::Skill::HandToHand, - /* weapon class*/ ESM::WeaponType::Melee, - /* ammo type */ ESM::Weapon::None, - /* flags */ 0 }; + inline static const ESM::WeaponType& getValue() + { + static const ESM::WeaponType value{ /* short group */ "", + /* long group */ "", + /* sound ID */ "", + /* attach bone */ "", + /* sheath bone */ "", + /* usage skill */ ESM::Skill::HandToHand, + /* weapon class*/ ESM::WeaponType::Melee, + /* ammo type */ ESM::Weapon::None, + /* flags */ 0 }; + return value; + } }; template <> struct Weapon { - inline static const ESM::WeaponType sValue{ /* short group */ "1h", - /* long group */ "pickprobe", - /* sound ID */ "", - /* attach bone */ "", - /* sheath bone */ "", - /* usage skill */ ESM::Skill::Security, - /* weapon class*/ ESM::WeaponType::Melee, - /* ammo type */ ESM::Weapon::None, - /* flags */ 0 }; + inline static const ESM::WeaponType& getValue() + { + static const ESM::WeaponType value{ /* short group */ "1h", + /* long group */ "pickprobe", + /* sound ID */ "", + /* attach bone */ "", + /* sheath bone */ "", + /* usage skill */ ESM::Skill::Security, + /* weapon class*/ ESM::WeaponType::Melee, + /* ammo type */ ESM::Weapon::None, + /* flags */ 0 }; + return value; + } }; template <> struct Weapon { - inline static const ESM::WeaponType sValue{ /* short group */ "spell", - /* long group */ "spellcast", - /* sound ID */ "", - /* attach bone */ "", - /* sheath bone */ "", - /* usage skill */ ESM::Skill::HandToHand, - /* weapon class*/ ESM::WeaponType::Melee, - /* ammo type */ ESM::Weapon::None, - /* flags */ ESM::WeaponType::TwoHanded }; + inline static const ESM::WeaponType& getValue() + { + static const ESM::WeaponType value{ /* short group */ "spell", + /* long group */ "spellcast", + /* sound ID */ "", + /* attach bone */ "", + /* sheath bone */ "", + /* usage skill */ ESM::Skill::HandToHand, + /* weapon class*/ ESM::WeaponType::Melee, + /* ammo type */ ESM::Weapon::None, + /* flags */ ESM::WeaponType::TwoHanded }; + return value; + } }; template <> struct Weapon { - inline static const ESM::WeaponType sValue{ /* short group */ "hh", - /* long group */ "handtohand", - /* sound ID */ "", - /* attach bone */ "", - /* sheath bone */ "", - /* usage skill */ ESM::Skill::HandToHand, - /* weapon class*/ ESM::WeaponType::Melee, - /* ammo type */ ESM::Weapon::None, - /* flags */ ESM::WeaponType::TwoHanded }; + inline static const ESM::WeaponType& getValue() + { + static const ESM::WeaponType value{ /* short group */ "hh", + /* long group */ "handtohand", + /* sound ID */ "", + /* attach bone */ "", + /* sheath bone */ "", + /* usage skill */ ESM::Skill::HandToHand, + /* weapon class*/ ESM::WeaponType::Melee, + /* ammo type */ ESM::Weapon::None, + /* flags */ ESM::WeaponType::TwoHanded }; + return value; + } }; template <> struct Weapon { - inline static const ESM::WeaponType sValue{ /* short group */ "1s", - /* long group */ "shortbladeonehand", - /* sound ID */ "Item Weapon Shortblade", - /* attach bone */ "Weapon Bone", - /* sheath bone */ "Bip01 ShortBladeOneHand", - /* usage skill */ ESM::Skill::ShortBlade, - /* weapon class*/ ESM::WeaponType::Melee, - /* ammo type */ ESM::Weapon::None, - /* flags */ ESM::WeaponType::HasHealth }; + inline static const ESM::WeaponType& getValue() + { + static const ESM::WeaponType value{ /* short group */ "1s", + /* long group */ "shortbladeonehand", + /* sound ID */ "Item Weapon Shortblade", + /* attach bone */ "Weapon Bone", + /* sheath bone */ "Bip01 ShortBladeOneHand", + /* usage skill */ ESM::Skill::ShortBlade, + /* weapon class*/ ESM::WeaponType::Melee, + /* ammo type */ ESM::Weapon::None, + /* flags */ ESM::WeaponType::HasHealth }; + return value; + } }; template <> struct Weapon { - inline static const ESM::WeaponType sValue{ /* short group */ "1h", - /* long group */ "weapononehand", - /* sound ID */ "Item Weapon Longblade", - /* attach bone */ "Weapon Bone", - /* sheath bone */ "Bip01 LongBladeOneHand", - /* usage skill */ ESM::Skill::LongBlade, - /* weapon class*/ ESM::WeaponType::Melee, - /* ammo type */ ESM::Weapon::None, - /* flags */ ESM::WeaponType::HasHealth }; + inline static const ESM::WeaponType& getValue() + { + static const ESM::WeaponType value{ /* short group */ "1h", + /* long group */ "weapononehand", + /* sound ID */ "Item Weapon Longblade", + /* attach bone */ "Weapon Bone", + /* sheath bone */ "Bip01 LongBladeOneHand", + /* usage skill */ ESM::Skill::LongBlade, + /* weapon class*/ ESM::WeaponType::Melee, + /* ammo type */ ESM::Weapon::None, + /* flags */ ESM::WeaponType::HasHealth }; + return value; + } }; template <> struct Weapon { - inline static const ESM::WeaponType sValue{ /* short group */ "1b", - /* long group */ "bluntonehand", - /* sound ID */ "Item Weapon Blunt", - /* attach bone */ "Weapon Bone", - /* sheath bone */ "Bip01 BluntOneHand", - /* usage skill */ ESM::Skill::BluntWeapon, - /* weapon class*/ ESM::WeaponType::Melee, - /* ammo type */ ESM::Weapon::None, - /* flags */ ESM::WeaponType::HasHealth }; + inline static const ESM::WeaponType& getValue() + { + static const ESM::WeaponType value{ /* short group */ "1b", + /* long group */ "bluntonehand", + /* sound ID */ "Item Weapon Blunt", + /* attach bone */ "Weapon Bone", + /* sheath bone */ "Bip01 BluntOneHand", + /* usage skill */ ESM::Skill::BluntWeapon, + /* weapon class*/ ESM::WeaponType::Melee, + /* ammo type */ ESM::Weapon::None, + /* flags */ ESM::WeaponType::HasHealth }; + return value; + } }; template <> struct Weapon { - inline static const ESM::WeaponType sValue{ /* short group */ "1b", - /* long group */ "bluntonehand", - /* sound ID */ "Item Weapon Blunt", - /* attach bone */ "Weapon Bone", - /* sheath bone */ "Bip01 LongBladeOneHand", - /* usage skill */ ESM::Skill::Axe, - /* weapon class*/ ESM::WeaponType::Melee, - /* ammo type */ ESM::Weapon::None, - /* flags */ ESM::WeaponType::HasHealth }; + inline static const ESM::WeaponType& getValue() + { + static const ESM::WeaponType value{ /* short group */ "1b", + /* long group */ "bluntonehand", + /* sound ID */ "Item Weapon Blunt", + /* attach bone */ "Weapon Bone", + /* sheath bone */ "Bip01 LongBladeOneHand", + /* usage skill */ ESM::Skill::Axe, + /* weapon class*/ ESM::WeaponType::Melee, + /* ammo type */ ESM::Weapon::None, + /* flags */ ESM::WeaponType::HasHealth }; + return value; + } }; template <> struct Weapon { - inline static const ESM::WeaponType sValue{ /* short group */ "2c", - /* long group */ "weapontwohand", - /* sound ID */ "Item Weapon Longblade", - /* attach bone */ "Weapon Bone", - /* sheath bone */ "Bip01 LongBladeTwoClose", - /* usage skill */ ESM::Skill::LongBlade, - /* weapon class*/ ESM::WeaponType::Melee, - /* ammo type */ ESM::Weapon::None, - /* flags */ ESM::WeaponType::HasHealth | ESM::WeaponType::TwoHanded }; + inline static const ESM::WeaponType& getValue() + { + static const ESM::WeaponType value{ /* short group */ "2c", + /* long group */ "weapontwohand", + /* sound ID */ "Item Weapon Longblade", + /* attach bone */ "Weapon Bone", + /* sheath bone */ "Bip01 LongBladeTwoClose", + /* usage skill */ ESM::Skill::LongBlade, + /* weapon class*/ ESM::WeaponType::Melee, + /* ammo type */ ESM::Weapon::None, + /* flags */ ESM::WeaponType::HasHealth | ESM::WeaponType::TwoHanded }; + return value; + } }; template <> struct Weapon { - inline static const ESM::WeaponType sValue{ /* short group */ "2b", - /* long group */ "blunttwohand", - /* sound ID */ "Item Weapon Blunt", - /* attach bone */ "Weapon Bone", - /* sheath bone */ "Bip01 AxeTwoClose", - /* usage skill */ ESM::Skill::Axe, - /* weapon class*/ ESM::WeaponType::Melee, - /* ammo type */ ESM::Weapon::None, - /* flags */ ESM::WeaponType::HasHealth | ESM::WeaponType::TwoHanded }; + inline static const ESM::WeaponType& getValue() + { + static const ESM::WeaponType value{ /* short group */ "2b", + /* long group */ "blunttwohand", + /* sound ID */ "Item Weapon Blunt", + /* attach bone */ "Weapon Bone", + /* sheath bone */ "Bip01 AxeTwoClose", + /* usage skill */ ESM::Skill::Axe, + /* weapon class*/ ESM::WeaponType::Melee, + /* ammo type */ ESM::Weapon::None, + /* flags */ ESM::WeaponType::HasHealth | ESM::WeaponType::TwoHanded }; + return value; + } }; template <> struct Weapon { - inline static const ESM::WeaponType sValue{ /* short group */ "2b", - /* long group */ "blunttwohand", - /* sound ID */ "Item Weapon Blunt", - /* attach bone */ "Weapon Bone", - /* sheath bone */ "Bip01 BluntTwoClose", - /* usage skill */ ESM::Skill::BluntWeapon, - /* weapon class*/ ESM::WeaponType::Melee, - /* ammo type */ ESM::Weapon::None, - /* flags */ ESM::WeaponType::HasHealth | ESM::WeaponType::TwoHanded }; + inline static const ESM::WeaponType& getValue() + { + static const ESM::WeaponType value{ /* short group */ "2b", + /* long group */ "blunttwohand", + /* sound ID */ "Item Weapon Blunt", + /* attach bone */ "Weapon Bone", + /* sheath bone */ "Bip01 BluntTwoClose", + /* usage skill */ ESM::Skill::BluntWeapon, + /* weapon class*/ ESM::WeaponType::Melee, + /* ammo type */ ESM::Weapon::None, + /* flags */ ESM::WeaponType::HasHealth | ESM::WeaponType::TwoHanded }; + return value; + } }; template <> struct Weapon { - inline static const ESM::WeaponType sValue{ /* short group */ "2w", - /* long group */ "weapontwowide", - /* sound ID */ "Item Weapon Blunt", - /* attach bone */ "Weapon Bone", - /* sheath bone */ "Bip01 BluntTwoWide", - /* usage skill */ ESM::Skill::BluntWeapon, - /* weapon class*/ ESM::WeaponType::Melee, - /* ammo type */ ESM::Weapon::None, - /* flags */ ESM::WeaponType::HasHealth | ESM::WeaponType::TwoHanded }; + inline static const ESM::WeaponType& getValue() + { + static const ESM::WeaponType value{ /* short group */ "2w", + /* long group */ "weapontwowide", + /* sound ID */ "Item Weapon Blunt", + /* attach bone */ "Weapon Bone", + /* sheath bone */ "Bip01 BluntTwoWide", + /* usage skill */ ESM::Skill::BluntWeapon, + /* weapon class*/ ESM::WeaponType::Melee, + /* ammo type */ ESM::Weapon::None, + /* flags */ ESM::WeaponType::HasHealth | ESM::WeaponType::TwoHanded }; + return value; + } }; template <> struct Weapon { - inline static const ESM::WeaponType sValue{ /* short group */ "2w", - /* long group */ "weapontwowide", - /* sound ID */ "Item Weapon Spear", - /* attach bone */ "Weapon Bone", - /* sheath bone */ "Bip01 SpearTwoWide", - /* usage skill */ ESM::Skill::Spear, - /* weapon class*/ ESM::WeaponType::Melee, - /* ammo type */ ESM::Weapon::None, - /* flags */ ESM::WeaponType::HasHealth | ESM::WeaponType::TwoHanded }; + inline static const ESM::WeaponType& getValue() + { + static const ESM::WeaponType value{ /* short group */ "2w", + /* long group */ "weapontwowide", + /* sound ID */ "Item Weapon Spear", + /* attach bone */ "Weapon Bone", + /* sheath bone */ "Bip01 SpearTwoWide", + /* usage skill */ ESM::Skill::Spear, + /* weapon class*/ ESM::WeaponType::Melee, + /* ammo type */ ESM::Weapon::None, + /* flags */ ESM::WeaponType::HasHealth | ESM::WeaponType::TwoHanded }; + return value; + } }; template <> struct Weapon { - inline static const ESM::WeaponType sValue{ /* short group */ "bow", - /* long group */ "bowandarrow", - /* sound ID */ "Item Weapon Bow", - /* attach bone */ "Weapon Bone Left", - /* sheath bone */ "Bip01 MarksmanBow", - /* usage skill */ ESM::Skill::Marksman, - /* weapon class*/ ESM::WeaponType::Ranged, - /* ammo type */ ESM::Weapon::Arrow, - /* flags */ ESM::WeaponType::HasHealth | ESM::WeaponType::TwoHanded }; + inline static const ESM::WeaponType& getValue() + { + static const ESM::WeaponType value{ /* short group */ "bow", + /* long group */ "bowandarrow", + /* sound ID */ "Item Weapon Bow", + /* attach bone */ "Weapon Bone Left", + /* sheath bone */ "Bip01 MarksmanBow", + /* usage skill */ ESM::Skill::Marksman, + /* weapon class*/ ESM::WeaponType::Ranged, + /* ammo type */ ESM::Weapon::Arrow, + /* flags */ ESM::WeaponType::HasHealth | ESM::WeaponType::TwoHanded }; + return value; + } }; template <> struct Weapon { - inline static const ESM::WeaponType sValue{ /* short group */ "crossbow", - /* long group */ "crossbow", - /* sound ID */ "Item Weapon Crossbow", - /* attach bone */ "Weapon Bone", - /* sheath bone */ "Bip01 MarksmanCrossbow", - /* usage skill */ ESM::Skill::Marksman, - /* weapon class*/ ESM::WeaponType::Ranged, - /* ammo type */ ESM::Weapon::Bolt, - /* flags */ ESM::WeaponType::HasHealth | ESM::WeaponType::TwoHanded }; + inline static const ESM::WeaponType& getValue() + { + static const ESM::WeaponType value{ /* short group */ "crossbow", + /* long group */ "crossbow", + /* sound ID */ "Item Weapon Crossbow", + /* attach bone */ "Weapon Bone", + /* sheath bone */ "Bip01 MarksmanCrossbow", + /* usage skill */ ESM::Skill::Marksman, + /* weapon class*/ ESM::WeaponType::Ranged, + /* ammo type */ ESM::Weapon::Bolt, + /* flags */ ESM::WeaponType::HasHealth | ESM::WeaponType::TwoHanded }; + return value; + } }; template <> struct Weapon { - inline static const ESM::WeaponType sValue{ /* short group */ "1t", - /* long group */ "throwweapon", - /* sound ID */ "Item Weapon Blunt", - /* attach bone */ "Weapon Bone", - /* sheath bone */ "Bip01 MarksmanThrown", - /* usage skill */ ESM::Skill::Marksman, - /* weapon class*/ ESM::WeaponType::Thrown, - /* ammo type */ ESM::Weapon::None, - /* flags */ 0 }; + inline static const ESM::WeaponType& getValue() + { + static const ESM::WeaponType value{ /* short group */ "1t", + /* long group */ "throwweapon", + /* sound ID */ "Item Weapon Blunt", + /* attach bone */ "Weapon Bone", + /* sheath bone */ "Bip01 MarksmanThrown", + /* usage skill */ ESM::Skill::Marksman, + /* weapon class*/ ESM::WeaponType::Thrown, + /* ammo type */ ESM::Weapon::None, + /* flags */ 0 }; + return value; + } }; template <> struct Weapon { - inline static const ESM::WeaponType sValue{ /* short group */ "", - /* long group */ "", - /* sound ID */ "Item Ammo", - /* attach bone */ "Bip01 Arrow", - /* sheath bone */ "", - /* usage skill */ ESM::Skill::Marksman, - /* weapon class*/ ESM::WeaponType::Ammo, - /* ammo type */ ESM::Weapon::None, - /* flags */ 0 }; + inline static const ESM::WeaponType& getValue() + { + static const ESM::WeaponType value{ /* short group */ "", + /* long group */ "", + /* sound ID */ "Item Ammo", + /* attach bone */ "Bip01 Arrow", + /* sheath bone */ "", + /* usage skill */ ESM::Skill::Marksman, + /* weapon class*/ ESM::WeaponType::Ammo, + /* ammo type */ ESM::Weapon::None, + /* flags */ 0 }; + return value; + } }; template <> struct Weapon { - inline static const ESM::WeaponType sValue{ /* short group */ "", - /* long group */ "", - /* sound ID */ "Item Ammo", - /* attach bone */ "ArrowBone", - /* sheath bone */ "", - /* usage skill */ ESM::Skill::Marksman, - /* weapon class*/ ESM::WeaponType::Ammo, - /* ammo type */ ESM::Weapon::None, - /* flags */ 0 }; + inline static const ESM::WeaponType& getValue() + { + static const ESM::WeaponType value{ /* short group */ "", + /* long group */ "", + /* sound ID */ "Item Ammo", + /* attach bone */ "ArrowBone", + /* sheath bone */ "", + /* usage skill */ ESM::Skill::Marksman, + /* weapon class*/ ESM::WeaponType::Ammo, + /* ammo type */ ESM::Weapon::None, + /* flags */ 0 }; + return value; + } }; MWWorld::ContainerStoreIterator getActiveWeapon(const MWWorld::Ptr& actor, int* weaptype) @@ -305,43 +377,43 @@ namespace MWMechanics switch (static_cast(weaponType)) { case ESM::Weapon::PickProbe: - return &Weapon::sValue; + return &Weapon::getValue(); case ESM::Weapon::HandToHand: - return &Weapon::sValue; + return &Weapon::getValue(); case ESM::Weapon::Spell: - return &Weapon::sValue; + return &Weapon::getValue(); case ESM::Weapon::None: - return &Weapon::sValue; + return &Weapon::getValue(); case ESM::Weapon::ShortBladeOneHand: - return &Weapon::sValue; + return &Weapon::getValue(); case ESM::Weapon::LongBladeOneHand: - return &Weapon::sValue; + return &Weapon::getValue(); case ESM::Weapon::LongBladeTwoHand: - return &Weapon::sValue; + return &Weapon::getValue(); case ESM::Weapon::BluntOneHand: - return &Weapon::sValue; + return &Weapon::getValue(); case ESM::Weapon::BluntTwoClose: - return &Weapon::sValue; + return &Weapon::getValue(); case ESM::Weapon::BluntTwoWide: - return &Weapon::sValue; + return &Weapon::getValue(); case ESM::Weapon::SpearTwoWide: - return &Weapon::sValue; + return &Weapon::getValue(); case ESM::Weapon::AxeOneHand: - return &Weapon::sValue; + return &Weapon::getValue(); case ESM::Weapon::AxeTwoHand: - return &Weapon::sValue; + return &Weapon::getValue(); case ESM::Weapon::MarksmanBow: - return &Weapon::sValue; + return &Weapon::getValue(); case ESM::Weapon::MarksmanCrossbow: - return &Weapon::sValue; + return &Weapon::getValue(); case ESM::Weapon::MarksmanThrown: - return &Weapon::sValue; + return &Weapon::getValue(); case ESM::Weapon::Arrow: - return &Weapon::sValue; + return &Weapon::getValue(); case ESM::Weapon::Bolt: - return &Weapon::sValue; + return &Weapon::getValue(); } - return &Weapon::sValue; + return &Weapon::getValue(); } } diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 2b69ec82c4..a2114648a0 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -962,12 +962,14 @@ namespace MWWorld }; for (ESM::Skill* skill : mShared) { - if (skill->mIndex >= 0) + int index = ESM::Skill::refIdToIndex(skill->mId); + if (index >= 0) { - skill->mName = getGMSTString(settings, skillValues[skill->mIndex][0]); - skill->mIcon = skillValues[skill->mIndex][1]; - skill->mWerewolfValue = getGMSTFloat(settings, skillValues[skill->mIndex][2]); - const auto& school = skillValues[skill->mIndex][3]; + const auto& values = skillValues[index]; + skill->mName = getGMSTString(settings, values[0]); + skill->mIcon = values[1]; + skill->mWerewolfValue = getGMSTFloat(settings, values[2]); + const auto& school = values[3]; if (!school.empty()) { if (!skill->mSchool) diff --git a/apps/openmw_test_suite/mwworld/test_store.cpp b/apps/openmw_test_suite/mwworld/test_store.cpp index 2b5e460627..e325dcd08e 100644 --- a/apps/openmw_test_suite/mwworld/test_store.cpp +++ b/apps/openmw_test_suite/mwworld/test_store.cpp @@ -462,6 +462,8 @@ namespace { refId = ESM::RefId::esm3ExteriorCell(0, 0); } + else if constexpr (std::is_same_v) + refId = ESM::Skill::Block; else refId = ESM::StringRefId(stringId); @@ -496,7 +498,7 @@ namespace const RecordType* result = nullptr; if constexpr (std::is_same_v) result = esmStore.get().search(index, 0); - else if constexpr (ESM::hasIndex && !std::is_same_v) + else if constexpr (ESM::hasIndex) result = esmStore.get().search(index); else result = esmStore.get().search(refId); diff --git a/components/esm3/loadskil.cpp b/components/esm3/loadskil.cpp index d435501e05..8661230e8c 100644 --- a/components/esm3/loadskil.cpp +++ b/components/esm3/loadskil.cpp @@ -5,46 +5,37 @@ #include +#include + namespace ESM { - const std::string Skill::sSkillNames[Length] = { - "Block", - "Armorer", - "Mediumarmor", - "Heavyarmor", - "Bluntweapon", - "Longblade", - "Axe", - "Spear", - "Athletics", - "Enchant", - "Destruction", - "Alteration", - "Illusion", - "Conjuration", - "Mysticism", - "Restoration", - "Alchemy", - "Unarmored", - "Security", - "Sneak", - "Acrobatics", - "Lightarmor", - "Shortblade", - "Marksman", - "Mercantile", - "Speechcraft", - "Handtohand", - }; - - int Skill::stringToSkillId(std::string_view skill) - { - for (int id = 0; id < Skill::Length; ++id) - if (Misc::StringUtils::ciEqual(sSkillNames[id], skill)) - return id; - - throw std::logic_error("No such skill: " + std::string(skill)); - } + const SkillId Skill::Block("Block"); + const SkillId Skill::Armorer("Armorer"); + const SkillId Skill::MediumArmor("MediumArmor"); + const SkillId Skill::HeavyArmor("HeavyArmor"); + const SkillId Skill::BluntWeapon("BluntWeapon"); + const SkillId Skill::LongBlade("LongBlade"); + const SkillId Skill::Axe("Axe"); + const SkillId Skill::Spear("Spear"); + const SkillId Skill::Athletics("Athletics"); + const SkillId Skill::Enchant("Enchant"); + const SkillId Skill::Destruction("Destruction"); + const SkillId Skill::Alteration("Alteration"); + const SkillId Skill::Illusion("Illusion"); + const SkillId Skill::Conjuration("Conjuration"); + const SkillId Skill::Mysticism("Mysticism"); + const SkillId Skill::Restoration("Restoration"); + const SkillId Skill::Alchemy("Alchemy"); + const SkillId Skill::Unarmored("Unarmored"); + const SkillId Skill::Security("Security"); + const SkillId Skill::Sneak("Sneak"); + const SkillId Skill::Acrobatics("Acrobatics"); + const SkillId Skill::LightArmor("LightArmor"); + const SkillId Skill::ShortBlade("ShortBlade"); + const SkillId Skill::Marksman("Marksman"); + const SkillId Skill::Mercantile("Mercantile"); + const SkillId Skill::Speechcraft("Speechcraft"); + const SkillId Skill::HandToHand("HandToHand"); void Skill::load(ESMReader& esm, bool& isDeleted) { @@ -53,13 +44,14 @@ namespace ESM bool hasIndex = false; bool hasData = false; + int32_t index = -1; while (esm.hasMoreSubs()) { esm.getSubName(); switch (esm.retSubName().toInt()) { case fourCC("INDX"): - esm.getHT(mIndex); + esm.getHT(index); hasIndex = true; break; case fourCC("SKDT"): @@ -75,19 +67,17 @@ namespace ESM } if (!hasIndex) esm.fail("Missing INDX"); - else if (mIndex < 0 || mIndex >= Length) + else if (index < 0 || index >= Length) esm.fail("Invalid INDX"); if (!hasData) esm.fail("Missing SKDT"); - // create an ID from the index and the name (only used in the editor and likely to change in the - // future) - mId = indexToRefId(mIndex); + mId = *indexToRefId(index).getIf(); } void Skill::save(ESMWriter& esm, bool /*isDeleted*/) const { - esm.writeHNT("INDX", mIndex); + esm.writeHNT("INDX", refIdToIndex(mId)); esm.writeHNT("SKDT", mData, 24); esm.writeHNOString("DESC", mDescription); } @@ -101,11 +91,51 @@ namespace ESM mDescription.clear(); } + static const RefId sSkills[Skill::Length] = { + Skill::Block, + Skill::Armorer, + Skill::MediumArmor, + Skill::HeavyArmor, + Skill::BluntWeapon, + Skill::LongBlade, + Skill::Axe, + Skill::Spear, + Skill::Athletics, + Skill::Enchant, + Skill::Destruction, + Skill::Alteration, + Skill::Illusion, + Skill::Conjuration, + Skill::Mysticism, + Skill::Restoration, + Skill::Alchemy, + Skill::Unarmored, + Skill::Security, + Skill::Sneak, + Skill::Acrobatics, + Skill::LightArmor, + Skill::ShortBlade, + Skill::Marksman, + Skill::Mercantile, + Skill::Speechcraft, + Skill::HandToHand, + }; + RefId Skill::indexToRefId(int index) { if (index < 0 || index >= Length) return RefId(); - return RefId::index(sRecordId, static_cast(index)); + return sSkills[index]; + } + + int Skill::refIdToIndex(RefId id) + { + for (int i = 0; i < Length; ++i) + { + if (sSkills[i] == id) + return i; + } + return -1; } const std::array sMagicSchools = { diff --git a/components/esm3/loadskil.hpp b/components/esm3/loadskil.hpp index 637c44d0f8..dac7745d3f 100644 --- a/components/esm3/loadskil.hpp +++ b/components/esm3/loadskil.hpp @@ -14,6 +14,8 @@ namespace ESM class ESMReader; class ESMWriter; + using SkillId = StringRefId; + struct MagicSchool { ESM::RefId mAreaSound; @@ -43,7 +45,7 @@ namespace ESM static std::string_view getRecordType() { return "Skill"; } unsigned int mRecordFlags; - RefId mId; + SkillId mId; struct SKDTstruct { @@ -55,48 +57,40 @@ namespace ESM }; // Total size: 24 bytes SKDTstruct mData; - // Skill index. Skils don't have an id ("NAME") like most records, - // they only have a numerical index that matches one of the - // hard-coded skills in the game. - int mIndex{ -1 }; - std::string mDescription; std::string mName; std::string mIcon; float mWerewolfValue{}; std::optional mSchool; - static constexpr IndexRefId Block{ sRecordId, 0 }; - static constexpr IndexRefId Armorer{ sRecordId, 1 }; - static constexpr IndexRefId MediumArmor{ sRecordId, 2 }; - static constexpr IndexRefId HeavyArmor{ sRecordId, 3 }; - static constexpr IndexRefId BluntWeapon{ sRecordId, 4 }; - static constexpr IndexRefId LongBlade{ sRecordId, 5 }; - static constexpr IndexRefId Axe{ sRecordId, 6 }; - static constexpr IndexRefId Spear{ sRecordId, 7 }; - static constexpr IndexRefId Athletics{ sRecordId, 8 }; - static constexpr IndexRefId Enchant{ sRecordId, 9 }; - static constexpr IndexRefId Destruction{ sRecordId, 10 }; - static constexpr IndexRefId Alteration{ sRecordId, 11 }; - static constexpr IndexRefId Illusion{ sRecordId, 12 }; - static constexpr IndexRefId Conjuration{ sRecordId, 13 }; - static constexpr IndexRefId Mysticism{ sRecordId, 14 }; - static constexpr IndexRefId Restoration{ sRecordId, 15 }; - static constexpr IndexRefId Alchemy{ sRecordId, 16 }; - static constexpr IndexRefId Unarmored{ sRecordId, 17 }; - static constexpr IndexRefId Security{ sRecordId, 18 }; - static constexpr IndexRefId Sneak{ sRecordId, 19 }; - static constexpr IndexRefId Acrobatics{ sRecordId, 20 }; - static constexpr IndexRefId LightArmor{ sRecordId, 21 }; - static constexpr IndexRefId ShortBlade{ sRecordId, 22 }; - static constexpr IndexRefId Marksman{ sRecordId, 23 }; - static constexpr IndexRefId Mercantile{ sRecordId, 24 }; - static constexpr IndexRefId Speechcraft{ sRecordId, 25 }; - static constexpr IndexRefId HandToHand{ sRecordId, 26 }; + static const SkillId Block; + static const SkillId Armorer; + static const SkillId MediumArmor; + static const SkillId HeavyArmor; + static const SkillId BluntWeapon; + static const SkillId LongBlade; + static const SkillId Axe; + static const SkillId Spear; + static const SkillId Athletics; + static const SkillId Enchant; + static const SkillId Destruction; + static const SkillId Alteration; + static const SkillId Illusion; + static const SkillId Conjuration; + static const SkillId Mysticism; + static const SkillId Restoration; + static const SkillId Alchemy; + static const SkillId Unarmored; + static const SkillId Security; + static const SkillId Sneak; + static const SkillId Acrobatics; + static const SkillId LightArmor; + static const SkillId ShortBlade; + static const SkillId Marksman; + static const SkillId Mercantile; + static const SkillId Speechcraft; + static const SkillId HandToHand; static constexpr int Length = 27; - static const std::string sSkillNames[Length]; - - static int stringToSkillId(std::string_view skill); void load(ESMReader& esm, bool& isDeleted); void save(ESMWriter& esm, bool isDeleted = false) const; @@ -105,6 +99,7 @@ namespace ESM ///< Set record to default state (does not touch the ID/index). static RefId indexToRefId(int index); + static int refIdToIndex(RefId id); }; } #endif diff --git a/components/esm3/npcstats.cpp b/components/esm3/npcstats.cpp index e2ee4f50d4..c34205f6c2 100644 --- a/components/esm3/npcstats.cpp +++ b/components/esm3/npcstats.cpp @@ -49,7 +49,7 @@ namespace ESM mWerewolfDeprecatedData = true; std::vector> skills(mSkills.begin(), mSkills.end()); - for (int i = 0; i < ESM::Skill::Length; ++i) + for (size_t i = 0; i < std::size(mSkills); ++i) { StatState skill; skill.load(esm, intFallback); diff --git a/components/esm3/player.cpp b/components/esm3/player.cpp index 9c2cad86d1..3b52f8d779 100644 --- a/components/esm3/player.cpp +++ b/components/esm3/player.cpp @@ -54,7 +54,7 @@ namespace ESM = esm.getFormatVersion() <= MaxClearModifiersFormatVersion && !mObject.mNpcStats.mIsWerewolf; if (esm.hasMoreSubs()) { - for (int i = 0; i < Attribute::Length; ++i) + for (size_t i = 0; i < std::size(mSaveAttributes); ++i) { StatState attribute; attribute.load(esm, intFallback); @@ -64,7 +64,7 @@ namespace ESM if (mObject.mNpcStats.mIsWerewolf) mObject.mCreatureStats.mAttributes[i] = attribute; } - for (int i = 0; i < Skill::Length; ++i) + for (size_t i = 0; i < std::size(mSaveSkills); ++i) { StatState skill; skill.load(esm, intFallback);