Assign StringRefIds to attributes

macos_ci_fix
Evil Eye 1 year ago
parent 11ae1a1fcb
commit e660a9ca16

@ -1171,9 +1171,6 @@ namespace EsmTool
template <> template <>
void Record<ESM::Race>::print() void Record<ESM::Race>::print()
{ {
static const char* sAttributeNames[ESM::Attribute::Length]
= { "Strength", "Intelligence", "Willpower", "Agility", "Speed", "Endurance", "Personality", "Luck" };
std::cout << " Name: " << mData.mName << std::endl; std::cout << " Name: " << mData.mName << std::endl;
std::cout << " Description: " << mData.mDescription << std::endl; std::cout << " Description: " << mData.mDescription << std::endl;
std::cout << " Flags: " << raceFlags(mData.mData.mFlags) << std::endl; std::cout << " Flags: " << raceFlags(mData.mData.mFlags) << std::endl;
@ -1185,8 +1182,8 @@ namespace EsmTool
std::cout << (male ? " Male:" : " Female:") << std::endl; std::cout << (male ? " Male:" : " Female:") << std::endl;
for (int j = 0; j < ESM::Attribute::Length; ++j) for (int j = 0; j < ESM::Attribute::Length; ++j)
std::cout << " " << sAttributeNames[j] << ": " << mData.mData.mAttributeValues[j].getValue(male) std::cout << " " << ESM::Attribute::indexToRefId(j) << ": "
<< std::endl; << mData.mData.mAttributeValues[j].getValue(male) << std::endl;
std::cout << " Height: " << mData.mData.mHeight.getValue(male) << std::endl; std::cout << " Height: " << mData.mData.mHeight.getValue(male) << std::endl;
std::cout << " Weight: " << mData.mData.mWeight.getValue(male) << std::endl; std::cout << " Weight: " << mData.mData.mWeight.getValue(male) << std::endl;

@ -138,12 +138,9 @@ namespace MWClass
continue; continue;
MWGui::Widgets::SpellEffectParams params; MWGui::Widgets::SpellEffectParams params;
params.mEffectID = ref->mBase->mData.mEffectID[i]; params.mEffectID = ref->mBase->mData.mEffectID[i];
params.mAttribute = ref->mBase->mData.mAttributes[i]; params.mAttribute = ESM::Attribute::indexToRefId(ref->mBase->mData.mAttributes[i]);
params.mSkill = ref->mBase->mData.mSkills[i]; params.mSkill = ESM::Skill::indexToRefId(ref->mBase->mData.mSkills[i]);
params.mKnown = alchemySkill >= fWortChanceValue * (i + 1);
params.mKnown = ((i == 0 && alchemySkill >= fWortChanceValue)
|| (i == 1 && alchemySkill >= fWortChanceValue * 2) || (i == 2 && alchemySkill >= fWortChanceValue * 3)
|| (i == 3 && alchemySkill >= fWortChanceValue * 4));
list.push_back(params); list.push_back(params);
} }

@ -93,7 +93,8 @@ namespace
int level = creatureStats.getLevel(); int level = creatureStats.getLevel();
for (const ESM::Attribute& attribute : attributes) for (const ESM::Attribute& attribute : attributes)
{ {
const ESM::Race::MaleFemale& value = race->mData.mAttributeValues[attribute.mId]; const ESM::Race::MaleFemale& value
= race->mData.mAttributeValues[ESM::Attribute::refIdToIndex(attribute.mId)];
creatureStats.setAttribute(attribute.mId, male ? value.mMale : value.mFemale); creatureStats.setAttribute(attribute.mId, male ? value.mMale : value.mFemale);
} }
@ -104,7 +105,7 @@ namespace
{ {
if (attribute >= 0 && attribute < ESM::Attribute::Length) if (attribute >= 0 && attribute < ESM::Attribute::Length)
{ {
auto id = static_cast<ESM::Attribute::AttributeID>(attribute); auto id = ESM::Attribute::indexToRefId(attribute);
creatureStats.setAttribute(id, creatureStats.getAttribute(id).getBase() + 10); creatureStats.setAttribute(id, creatureStats.getAttribute(id).getBase() + 10);
} }
} }
@ -113,10 +114,11 @@ namespace
for (const ESM::Attribute& attribute : attributes) for (const ESM::Attribute& attribute : attributes)
{ {
float modifierSum = 0; float modifierSum = 0;
int attributeIndex = ESM::Attribute::refIdToIndex(attribute.mId);
for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get<ESM::Skill>()) for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get<ESM::Skill>())
{ {
if (skill.mData.mAttribute != attribute.mId) if (skill.mData.mAttribute != attributeIndex)
continue; continue;
// is this a minor or major skill? // is this a minor or major skill?
@ -148,7 +150,8 @@ namespace
else if (class_->mData.mSpecialization == ESM::Class::Stealth) else if (class_->mData.mSpecialization == ESM::Class::Stealth)
multiplier += 1; multiplier += 1;
if (std::find(class_->mData.mAttribute.begin(), class_->mData.mAttribute.end(), ESM::Attribute::Endurance) if (std::find(class_->mData.mAttribute.begin(), class_->mData.mAttribute.end(),
ESM::Attribute::refIdToIndex(ESM::Attribute::Endurance))
!= class_->mData.mAttribute.end()) != class_->mData.mAttribute.end())
multiplier += 1; multiplier += 1;

@ -383,7 +383,7 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons
case SelectWrapper::Function_PcAttribute: case SelectWrapper::Function_PcAttribute:
{ {
auto attribute = static_cast<ESM::Attribute::AttributeID>(select.getArgument()); ESM::RefId attribute = ESM::Attribute::indexToRefId(select.getArgument());
return player.getClass().getCreatureStats(player).getAttribute(attribute).getModified(); return player.getClass().getCreatureStats(player).getAttribute(attribute).getModified();
} }
case SelectWrapper::Function_PcSkill: case SelectWrapper::Function_PcSkill:
@ -654,9 +654,9 @@ bool MWDialogue::Filter::hasFactionRankSkillRequirements(
MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
return stats.getAttribute(ESM::Attribute::AttributeID(faction.mData.mAttribute[0])).getBase() return stats.getAttribute(ESM::Attribute::indexToRefId(faction.mData.mAttribute[0])).getBase()
>= faction.mData.mRankData[rank].mAttribute1 >= faction.mData.mRankData[rank].mAttribute1
&& stats.getAttribute(ESM::Attribute::AttributeID(faction.mData.mAttribute[1])).getBase() && stats.getAttribute(ESM::Attribute::indexToRefId(faction.mData.mAttribute[1])).getBase()
>= faction.mData.mRankData[rank].mAttribute2; >= faction.mData.mRankData[rank].mAttribute2;
} }

@ -103,7 +103,7 @@ namespace MWGui
mPlayerSkillValues.emplace(skill.mId, MWMechanics::SkillValue()); mPlayerSkillValues.emplace(skill.mId, MWMechanics::SkillValue());
} }
void CharacterCreation::setValue(ESM::Attribute::AttributeID id, const MWMechanics::AttributeValue& value) void CharacterCreation::setAttribute(ESM::RefId id, const MWMechanics::AttributeValue& value)
{ {
mPlayerAttributes[id] = value; mPlayerAttributes[id] = value;
if (mReviewDialog) if (mReviewDialog)
@ -263,8 +263,7 @@ namespace MWGui
mReviewDialog->setFatigue(stats.getFatigue()); mReviewDialog->setFatigue(stats.getFatigue());
for (auto& attributePair : mPlayerAttributes) for (auto& attributePair : mPlayerAttributes)
{ {
mReviewDialog->setAttribute( mReviewDialog->setAttribute(attributePair.first, attributePair.second);
static_cast<ESM::Attribute::AttributeID>(attributePair.first), attributePair.second);
} }
for (const auto& [skill, value] : mPlayerSkillValues) for (const auto& [skill, value] : mPlayerSkillValues)
{ {
@ -462,9 +461,10 @@ namespace MWGui
klass.mData.mIsPlayable = 0x1; klass.mData.mIsPlayable = 0x1;
klass.mRecordFlags = 0; klass.mRecordFlags = 0;
std::vector<int> attributes = mCreateClassDialog->getFavoriteAttributes(); std::vector<ESM::RefId> attributes = mCreateClassDialog->getFavoriteAttributes();
assert(attributes.size() == klass.mData.mAttribute.size()); assert(attributes.size() >= klass.mData.mAttribute.size());
std::copy(attributes.begin(), attributes.end(), klass.mData.mAttribute.begin()); for (size_t i = 0; i < klass.mData.mAttribute.size(); ++i)
klass.mData.mAttribute[i] = ESM::Attribute::refIdToIndex(attributes[i]);
std::vector<ESM::RefId> majorSkills = mCreateClassDialog->getMajorSkills(); std::vector<ESM::RefId> majorSkills = mCreateClassDialog->getMajorSkills();
std::vector<ESM::RefId> minorSkills = mCreateClassDialog->getMinorSkills(); std::vector<ESM::RefId> minorSkills = mCreateClassDialog->getMinorSkills();

@ -44,7 +44,7 @@ namespace MWGui
// Show a dialog // Show a dialog
void spawnDialog(const char id); void spawnDialog(const char id);
void setValue(ESM::Attribute::AttributeID id, const MWMechanics::AttributeValue& value) override; void setAttribute(ESM::RefId id, const MWMechanics::AttributeValue& value) override;
void setValue(std::string_view id, const MWMechanics::DynamicStat<float>& value) override; void setValue(std::string_view id, const MWMechanics::DynamicStat<float>& value) override;
void setValue(ESM::RefId id, const MWMechanics::SkillValue& value) override; void setValue(ESM::RefId id, const MWMechanics::SkillValue& value) override;
void configureSkills(const std::vector<ESM::RefId>& major, const std::vector<ESM::RefId>& minor) override; void configureSkills(const std::vector<ESM::RefId>& major, const std::vector<ESM::RefId>& minor) override;
@ -56,7 +56,7 @@ namespace MWGui
Resource::ResourceSystem* mResourceSystem; Resource::ResourceSystem* mResourceSystem;
std::vector<ESM::RefId> mPlayerMajorSkills, mPlayerMinorSkills; std::vector<ESM::RefId> mPlayerMajorSkills, mPlayerMinorSkills;
std::map<int, MWMechanics::AttributeValue> mPlayerAttributes; std::map<ESM::RefId, MWMechanics::AttributeValue> mPlayerAttributes;
std::map<ESM::RefId, MWMechanics::SkillValue> mPlayerSkillValues; std::map<ESM::RefId, MWMechanics::SkillValue> mPlayerSkillValues;
// Dialogs // Dialogs

@ -260,8 +260,8 @@ namespace MWGui
mSpecializationName->setCaption(specName); mSpecializationName->setCaption(specName);
ToolTips::createSpecializationToolTip(mSpecializationName, specName, specialization); ToolTips::createSpecializationToolTip(mSpecializationName, specName, specialization);
mFavoriteAttribute[0]->setAttributeId(static_cast<ESM::Attribute::AttributeID>(klass->mData.mAttribute[0])); mFavoriteAttribute[0]->setAttributeId(ESM::Attribute::indexToRefId(klass->mData.mAttribute[0]));
mFavoriteAttribute[1]->setAttributeId(static_cast<ESM::Attribute::AttributeID>(klass->mData.mAttribute[1])); mFavoriteAttribute[1]->setAttributeId(ESM::Attribute::indexToRefId(klass->mData.mAttribute[1]));
ToolTips::createAttributeToolTip(mFavoriteAttribute[0], mFavoriteAttribute[0]->getAttributeId()); ToolTips::createAttributeToolTip(mFavoriteAttribute[0], mFavoriteAttribute[0]->getAttributeId());
ToolTips::createAttributeToolTip(mFavoriteAttribute[1], mFavoriteAttribute[1]->getAttributeId()); ToolTips::createAttributeToolTip(mFavoriteAttribute[1], mFavoriteAttribute[1]->getAttributeId());
@ -509,9 +509,9 @@ namespace MWGui
return mSpecializationId; return mSpecializationId;
} }
std::vector<int> CreateClassDialog::getFavoriteAttributes() const std::vector<ESM::RefId> CreateClassDialog::getFavoriteAttributes() const
{ {
std::vector<int> v; std::vector<ESM::RefId> v;
v.push_back(mFavoriteAttribute0->getAttributeId()); v.push_back(mFavoriteAttribute0->getAttributeId());
v.push_back(mFavoriteAttribute1->getAttributeId()); v.push_back(mFavoriteAttribute1->getAttributeId());
return v; return v;
@ -599,7 +599,7 @@ namespace MWGui
void CreateClassDialog::onAttributeSelected() void CreateClassDialog::onAttributeSelected()
{ {
ESM::Attribute::AttributeID id = mAttribDialog->getAttributeId(); ESM::RefId id = mAttribDialog->getAttributeId();
if (mAffectedAttribute == mFavoriteAttribute0) if (mAffectedAttribute == mFavoriteAttribute0)
{ {
if (mFavoriteAttribute1->getAttributeId() == id) if (mFavoriteAttribute1->getAttributeId() == id)

@ -188,7 +188,7 @@ namespace MWGui
bool exit() override; bool exit() override;
ESM::Attribute::AttributeID getAttributeId() const { return mAttributeId; } ESM::RefId getAttributeId() const { return mAttributeId; }
// Events // Events
typedef MyGUI::delegates::MultiDelegate<> EventHandle_Void; typedef MyGUI::delegates::MultiDelegate<> EventHandle_Void;
@ -208,7 +208,7 @@ namespace MWGui
void onCancelClicked(MyGUI::Widget* _sender); void onCancelClicked(MyGUI::Widget* _sender);
private: private:
ESM::Attribute::AttributeID mAttributeId; ESM::RefId mAttributeId;
}; };
class SelectSkillDialog : public WindowModal class SelectSkillDialog : public WindowModal
@ -274,7 +274,7 @@ namespace MWGui
std::string getName() const; std::string getName() const;
std::string getDescription() const; std::string getDescription() const;
ESM::Class::Specialization getSpecializationId() const; ESM::Class::Specialization getSpecializationId() const;
std::vector<int> getFavoriteAttributes() const; std::vector<ESM::RefId> getFavoriteAttributes() const;
std::vector<ESM::RefId> getMajorSkills() const; std::vector<ESM::RefId> getMajorSkills() const;
std::vector<ESM::RefId> getMinorSkills() const; std::vector<ESM::RefId> getMinorSkills() const;

@ -200,7 +200,7 @@ namespace MWGui
mFatigue->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr); mFatigue->setUserString("Caption_HealthDescription", "#{sFatDesc}\n" + valStr);
} }
void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::AttributeValue& value) void ReviewDialog::setAttribute(ESM::RefId attributeId, const MWMechanics::AttributeValue& value)
{ {
auto attr = mAttributeWidgets.find(attributeId); auto attr = mAttributeWidgets.find(attributeId);
if (attr == mAttributeWidgets.end()) if (attr == mAttributeWidgets.end())
@ -412,7 +412,7 @@ namespace MWGui
if (!mRaceId.empty()) if (!mRaceId.empty())
race = MWBase::Environment::get().getESMStore()->get<ESM::Race>().find(mRaceId); race = MWBase::Environment::get().getESMStore()->get<ESM::Race>().find(mRaceId);
std::map<ESM::Attribute::AttributeID, MWMechanics::AttributeValue> attributes; std::map<ESM::RefId, MWMechanics::AttributeValue> attributes;
for (const auto& [key, value] : mAttributeWidgets) for (const auto& [key, value] : mAttributeWidgets)
attributes[key] = value->getAttributeValue(); attributes[key] = value->getAttributeValue();

@ -3,7 +3,6 @@
#include "widgets.hpp" #include "widgets.hpp"
#include "windowbase.hpp" #include "windowbase.hpp"
#include <components/esm/attr.hpp>
#include <components/esm/refid.hpp> #include <components/esm/refid.hpp>
#include <components/esm3/loadclas.hpp> #include <components/esm3/loadclas.hpp>
@ -38,7 +37,7 @@ namespace MWGui
void setMagicka(const MWMechanics::DynamicStat<float>& value); void setMagicka(const MWMechanics::DynamicStat<float>& value);
void setFatigue(const MWMechanics::DynamicStat<float>& value); void setFatigue(const MWMechanics::DynamicStat<float>& value);
void setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::AttributeValue& value); void setAttribute(ESM::RefId attributeId, const MWMechanics::AttributeValue& value);
void configureSkills(const std::vector<ESM::RefId>& major, const std::vector<ESM::RefId>& minor); void configureSkills(const std::vector<ESM::RefId>& major, const std::vector<ESM::RefId>& minor);
void setSkillValue(ESM::RefId id, const MWMechanics::SkillValue& value); void setSkillValue(ESM::RefId id, const MWMechanics::SkillValue& value);
@ -90,7 +89,7 @@ namespace MWGui
Widgets::MWDynamicStatPtr mHealth, mMagicka, mFatigue; Widgets::MWDynamicStatPtr mHealth, mMagicka, mFatigue;
std::map<ESM::Attribute::AttributeID, Widgets::MWAttributePtr> mAttributeWidgets; std::map<ESM::RefId, Widgets::MWAttributePtr> mAttributeWidgets;
std::vector<ESM::RefId> mMajorSkills, mMinorSkills, mMiscSkills; std::vector<ESM::RefId> mMajorSkills, mMinorSkills, mMiscSkills;
std::map<ESM::RefId, MWMechanics::SkillValue> mSkillValues; std::map<ESM::RefId, MWMechanics::SkillValue> mSkillValues;

@ -291,9 +291,9 @@ namespace MWGui
eventEffectModified(mEffect); eventEffectModified(mEffect);
} }
void EditEffectDialog::setAttribute(int attribute) void EditEffectDialog::setAttribute(ESM::RefId attribute)
{ {
mEffect.mAttribute = attribute; mEffect.mAttribute = ESM::Attribute::refIdToIndex(attribute);
eventEffectModified(mEffect); eventEffectModified(mEffect);
} }
@ -689,8 +689,8 @@ namespace MWGui
{ {
Widgets::SpellEffectParams params; Widgets::SpellEffectParams params;
params.mEffectID = effectInfo.mEffectID; params.mEffectID = effectInfo.mEffectID;
params.mSkill = effectInfo.mSkill; params.mSkill = ESM::Skill::indexToRefId(effectInfo.mSkill);
params.mAttribute = effectInfo.mAttribute; params.mAttribute = ESM::Attribute::indexToRefId(effectInfo.mAttribute);
params.mDuration = effectInfo.mDuration; params.mDuration = effectInfo.mDuration;
params.mMagnMin = effectInfo.mMagnMin; params.mMagnMin = effectInfo.mMagnMin;
params.mMagnMax = effectInfo.mMagnMax; params.mMagnMax = effectInfo.mMagnMax;

@ -31,7 +31,7 @@ namespace MWGui
void setConstantEffect(bool constant); void setConstantEffect(bool constant);
void setSkill(ESM::RefId skill); void setSkill(ESM::RefId skill);
void setAttribute(int attribute); void setAttribute(ESM::RefId attribute);
void newEffect(const ESM::MagicEffect* effect); void newEffect(const ESM::MagicEffect* effect);
void editEffect(ESM::ENAMstruct effect); void editEffect(ESM::ENAMstruct effect);

@ -36,7 +36,7 @@ namespace MWGui
if (!(effect.mFlags & ESM::ActiveEffect::Flag_Applied)) if (!(effect.mFlags & ESM::ActiveEffect::Flag_Applied))
continue; continue;
MagicEffectInfo newEffectSource; MagicEffectInfo newEffectSource;
newEffectSource.mKey = MWMechanics::EffectKey(effect.mEffectId, effect.mArg); newEffectSource.mKey = MWMechanics::EffectKey(effect.mEffectId, effect.getSkillOrAttribute());
newEffectSource.mMagnitude = static_cast<int>(effect.mMagnitude); newEffectSource.mMagnitude = static_cast<int>(effect.mMagnitude);
newEffectSource.mPermanent = effect.mDuration == -1.f; newEffectSource.mPermanent = effect.mDuration == -1.f;
newEffectSource.mRemainingTime = effect.mTimeLeft; newEffectSource.mRemainingTime = effect.mTimeLeft;
@ -82,8 +82,7 @@ namespace MWGui
if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill) if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill)
{ {
const ESM::Skill* skill const ESM::Skill* skill = store->get<ESM::Skill>().find(effectInfo.mKey.mArg);
= store->get<ESM::Skill>().find(ESM::Skill::indexToRefId(effectInfo.mKey.mArg));
sourcesDescription += " (" + skill->mName + ')'; sourcesDescription += " (" + skill->mName + ')';
} }
if (effect->mData.mFlags & ESM::MagicEffect::TargetAttribute) if (effect->mData.mFlags & ESM::MagicEffect::TargetAttribute)

@ -53,7 +53,8 @@ namespace MWGui
if (effectId != -1) if (effectId != -1)
{ {
const ESM::MagicEffect* magicEffect = store.get<ESM::MagicEffect>().find(effectId); const ESM::MagicEffect* magicEffect = store.get<ESM::MagicEffect>().find(effectId);
const ESM::Attribute* attribute = store.get<ESM::Attribute>().search(effect.mAttribute); const ESM::Attribute* attribute
= store.get<ESM::Attribute>().search(ESM::Attribute::indexToRefId(effect.mAttribute));
const ESM::Skill* skill = store.get<ESM::Skill>().search(ESM::Skill::indexToRefId(effect.mSkill)); const ESM::Skill* skill = store.get<ESM::Skill>().search(ESM::Skill::indexToRefId(effect.mSkill));
std::string fullEffectName = MWMechanics::getMagicEffectString(*magicEffect, attribute, skill); std::string fullEffectName = MWMechanics::getMagicEffectString(*magicEffect, attribute, skill);

@ -43,7 +43,7 @@ namespace MWGui
if (value != mWatchedAttributes[attribute.mId] || mWatchedStatsEmpty) if (value != mWatchedAttributes[attribute.mId] || mWatchedStatsEmpty)
{ {
mWatchedAttributes[attribute.mId] = value; mWatchedAttributes[attribute.mId] = value;
setValue(attribute.mId, value); setAttribute(attribute.mId, value);
} }
} }
@ -150,10 +150,10 @@ namespace MWGui
mListeners.erase(listener); mListeners.erase(listener);
} }
void StatsWatcher::setValue(ESM::Attribute::AttributeID id, const MWMechanics::AttributeValue& value) void StatsWatcher::setAttribute(ESM::RefId id, const MWMechanics::AttributeValue& value)
{ {
for (StatsListener* listener : mListeners) for (StatsListener* listener : mListeners)
listener->setValue(id, value); listener->setAttribute(id, value);
} }
void StatsWatcher::setValue(ESM::RefId id, const MWMechanics::SkillValue& value) void StatsWatcher::setValue(ESM::RefId id, const MWMechanics::SkillValue& value)

@ -19,7 +19,7 @@ namespace MWGui
virtual ~StatsListener() = default; virtual ~StatsListener() = default;
/// Set value for the given ID. /// Set value for the given ID.
virtual void setValue(ESM::Attribute::AttributeID id, const MWMechanics::AttributeValue& value) {} virtual void setAttribute(ESM::RefId id, const MWMechanics::AttributeValue& value) {}
virtual void setValue(std::string_view id, const MWMechanics::DynamicStat<float>& value) {} virtual void setValue(std::string_view id, const MWMechanics::DynamicStat<float>& value) {}
virtual void setValue(std::string_view, const std::string& value) {} virtual void setValue(std::string_view, const std::string& value) {}
virtual void setValue(std::string_view, int value) {} virtual void setValue(std::string_view, int value) {}
@ -31,7 +31,7 @@ namespace MWGui
{ {
MWWorld::Ptr mWatched; MWWorld::Ptr mWatched;
std::map<ESM::Attribute::AttributeID, MWMechanics::AttributeValue> mWatchedAttributes; std::map<ESM::RefId, MWMechanics::AttributeValue> mWatchedAttributes;
std::map<ESM::RefId, MWMechanics::SkillValue> mWatchedSkills; std::map<ESM::RefId, MWMechanics::SkillValue> mWatchedSkills;
MWMechanics::DynamicStat<float> mWatchedHealth; MWMechanics::DynamicStat<float> mWatchedHealth;
@ -50,7 +50,7 @@ namespace MWGui
std::set<StatsListener*> mListeners; std::set<StatsListener*> mListeners;
void setValue(ESM::Attribute::AttributeID id, const MWMechanics::AttributeValue& value); void setAttribute(ESM::RefId id, const MWMechanics::AttributeValue& value);
void setValue(std::string_view id, const MWMechanics::DynamicStat<float>& value); void setValue(std::string_view id, const MWMechanics::DynamicStat<float>& value);
void setValue(std::string_view id, const std::string& value); void setValue(std::string_view id, const std::string& value);
void setValue(std::string_view id, int value); void setValue(std::string_view id, int value);

@ -157,7 +157,7 @@ namespace MWGui
mMainWidget->castType<MyGUI::Window>()->setCaption(playerName); mMainWidget->castType<MyGUI::Window>()->setCaption(playerName);
} }
void StatsWindow::setValue(ESM::Attribute::AttributeID id, const MWMechanics::AttributeValue& value) void StatsWindow::setAttribute(ESM::RefId id, const MWMechanics::AttributeValue& value)
{ {
auto it = mAttributeWidgets.find(id); auto it = mAttributeWidgets.find(id);
if (it != mAttributeWidgets.end()) if (it != mAttributeWidgets.end())
@ -505,7 +505,8 @@ namespace MWGui
continue; continue;
} }
const ESM::Attribute* attr = esmStore.get<ESM::Attribute>().find(skill->mData.mAttribute); const ESM::Attribute* attr
= esmStore.get<ESM::Attribute>().find(ESM::Attribute::indexToRefId(skill->mData.mAttribute));
std::pair<MyGUI::TextBox*, MyGUI::TextBox*> widgets std::pair<MyGUI::TextBox*, MyGUI::TextBox*> widgets
= addValueItem(skill->mName, {}, "normal", coord1, coord2); = addValueItem(skill->mName, {}, "normal", coord1, coord2);
@ -620,8 +621,10 @@ namespace MWGui
text += std::string("\n\n#{fontcolourhtml=header}#{sNextRank} ") + faction->mRanks[rank + 1]; text += std::string("\n\n#{fontcolourhtml=header}#{sNextRank} ") + faction->mRanks[rank + 1];
const ESM::RankData& rankData = faction->mData.mRankData[rank + 1]; const ESM::RankData& rankData = faction->mData.mRankData[rank + 1];
const ESM::Attribute* attr1 = store.get<ESM::Attribute>().find(faction->mData.mAttribute[0]); const ESM::Attribute* attr1 = store.get<ESM::Attribute>().find(
const ESM::Attribute* attr2 = store.get<ESM::Attribute>().find(faction->mData.mAttribute[1]); ESM::Attribute::indexToRefId(faction->mData.mAttribute[0]));
const ESM::Attribute* attr2 = store.get<ESM::Attribute>().find(
ESM::Attribute::indexToRefId(faction->mData.mAttribute[1]));
text += "\n#{fontcolourhtml=normal}" + MyGUI::TextIterator::toTagsString(attr1->mName) + ": " text += "\n#{fontcolourhtml=normal}" + MyGUI::TextIterator::toTagsString(attr1->mName) + ": "
+ MyGUI::utility::toString(rankData.mAttribute1) + ", " + MyGUI::utility::toString(rankData.mAttribute1) + ", "

@ -22,7 +22,7 @@ namespace MWGui
void setPlayerName(const std::string& playerName); void setPlayerName(const std::string& playerName);
/// Set value for the given ID. /// Set value for the given ID.
void setValue(ESM::Attribute::AttributeID id, const MWMechanics::AttributeValue& value) override; void setAttribute(ESM::RefId id, const MWMechanics::AttributeValue& value) override;
void setValue(std::string_view id, const MWMechanics::DynamicStat<float>& value) override; void setValue(std::string_view id, const MWMechanics::DynamicStat<float>& value) override;
void setValue(std::string_view id, const std::string& value) override; void setValue(std::string_view id, const std::string& value) override;
void setValue(std::string_view id, int value) override; void setValue(std::string_view id, int value) override;
@ -68,7 +68,7 @@ namespace MWGui
std::vector<ESM::RefId> mMajorSkills, mMinorSkills, mMiscSkills; std::vector<ESM::RefId> mMajorSkills, mMinorSkills, mMiscSkills;
std::map<ESM::RefId, MWMechanics::SkillValue> mSkillValues; std::map<ESM::RefId, MWMechanics::SkillValue> mSkillValues;
std::map<ESM::Attribute::AttributeID, MyGUI::TextBox*> mAttributeWidgets; std::map<ESM::RefId, MyGUI::TextBox*> mAttributeWidgets;
std::map<ESM::RefId, std::pair<MyGUI::TextBox*, MyGUI::TextBox*>> mSkillWidgetMap; std::map<ESM::RefId, std::pair<MyGUI::TextBox*, MyGUI::TextBox*>> mSkillWidgetMap;
std::map<std::string, MyGUI::Widget*> mFactionWidgetMap; std::map<std::string, MyGUI::Widget*> mFactionWidgetMap;
FactionList mFactions; ///< Stores a list of factions and the current rank FactionList mFactions; ///< Stores a list of factions and the current rank

@ -225,8 +225,8 @@ namespace MWGui
{ {
Widgets::SpellEffectParams params; Widgets::SpellEffectParams params;
params.mEffectID = spellEffect.mEffectID; params.mEffectID = spellEffect.mEffectID;
params.mSkill = spellEffect.mSkill; params.mSkill = ESM::Skill::indexToRefId(spellEffect.mSkill);
params.mAttribute = spellEffect.mAttribute; params.mAttribute = ESM::Attribute::indexToRefId(spellEffect.mAttribute);
params.mDuration = spellEffect.mDuration; params.mDuration = spellEffect.mDuration;
params.mMagnMin = spellEffect.mMagnMin; params.mMagnMin = spellEffect.mMagnMin;
params.mMagnMax = spellEffect.mMagnMax; params.mMagnMax = spellEffect.mMagnMax;
@ -804,7 +804,8 @@ namespace MWGui
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore(); const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
const ESM::Skill* skill = store.get<ESM::Skill>().find(skillId); const ESM::Skill* skill = store.get<ESM::Skill>().find(skillId);
const ESM::Attribute* attr = store.get<ESM::Attribute>().find(skill->mData.mAttribute); const ESM::Attribute* attr
= store.get<ESM::Attribute>().find(ESM::Attribute::indexToRefId(skill->mData.mAttribute));
widget->setUserString("ToolTipType", "Layout"); widget->setUserString("ToolTipType", "Layout");
widget->setUserString("ToolTipLayout", "SkillNoProgressToolTip"); widget->setUserString("ToolTipLayout", "SkillNoProgressToolTip");
@ -815,7 +816,7 @@ namespace MWGui
widget->setUserString("ImageTexture_SkillNoProgressImage", skill->mIcon); widget->setUserString("ImageTexture_SkillNoProgressImage", skill->mIcon);
} }
void ToolTips::createAttributeToolTip(MyGUI::Widget* widget, ESM::Attribute::AttributeID attributeId) void ToolTips::createAttributeToolTip(MyGUI::Widget* widget, ESM::RefId attributeId)
{ {
const ESM::Attribute* attribute const ESM::Attribute* attribute
= MWBase::Environment::get().getESMStore()->get<ESM::Attribute>().search(attributeId); = MWBase::Environment::get().getESMStore()->get<ESM::Attribute>().search(attributeId);

@ -92,7 +92,7 @@ namespace MWGui
// these do not create an actual tooltip, but they fill in the data that is required so the tooltip // these do not create an actual tooltip, but they fill in the data that is required so the tooltip
// system knows what to show in case this widget is hovered // system knows what to show in case this widget is hovered
static void createSkillToolTip(MyGUI::Widget* widget, ESM::RefId skillId); static void createSkillToolTip(MyGUI::Widget* widget, ESM::RefId skillId);
static void createAttributeToolTip(MyGUI::Widget* widget, ESM::Attribute::AttributeID attributeId); static void createAttributeToolTip(MyGUI::Widget* widget, ESM::RefId attributeId);
static void createSpecializationToolTip(MyGUI::Widget* widget, const std::string& name, int specId); static void createSpecializationToolTip(MyGUI::Widget* widget, const std::string& name, int specId);
static void createBirthsignToolTip(MyGUI::Widget* widget, const ESM::RefId& birthsignId); static void createBirthsignToolTip(MyGUI::Widget* widget, const ESM::RefId& birthsignId);
static void createRaceToolTip(MyGUI::Widget* widget, const ESM::Race* playerRace); static void createRaceToolTip(MyGUI::Widget* widget, const ESM::Race* playerRace);

@ -165,7 +165,7 @@ namespace MWGui
// You can not train a skill above its governing attribute // You can not train a skill above its governing attribute
if (pcStats.getSkill(skill->mId).getBase() if (pcStats.getSkill(skill->mId).getBase()
>= pcStats.getAttribute(ESM::Attribute::AttributeID(skill->mData.mAttribute)).getBase()) >= pcStats.getAttribute(ESM::Attribute::indexToRefId(skill->mData.mAttribute)).getBase())
{ {
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage17}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage17}");
return; return;

@ -101,13 +101,12 @@ namespace MWGui::Widgets
/* MWAttribute */ /* MWAttribute */
MWAttribute::MWAttribute() MWAttribute::MWAttribute()
: mId(ESM::Attribute::Length) : mAttributeNameWidget(nullptr)
, mAttributeNameWidget(nullptr)
, mAttributeValueWidget(nullptr) , mAttributeValueWidget(nullptr)
{ {
} }
void MWAttribute::setAttributeId(ESM::Attribute::AttributeID attributeId) void MWAttribute::setAttributeId(ESM::RefId attributeId)
{ {
mId = attributeId; mId = attributeId;
updateWidgets(); updateWidgets();
@ -204,8 +203,8 @@ namespace MWGui::Widgets
= creator->createWidget<MWSpellEffect>("MW_EffectImage", coord, MyGUI::Align::Default); = creator->createWidget<MWSpellEffect>("MW_EffectImage", coord, MyGUI::Align::Default);
SpellEffectParams params; SpellEffectParams params;
params.mEffectID = effectInfo.mEffectID; params.mEffectID = effectInfo.mEffectID;
params.mSkill = effectInfo.mSkill; params.mSkill = ESM::Skill::indexToRefId(effectInfo.mSkill);
params.mAttribute = effectInfo.mAttribute; params.mAttribute = ESM::Attribute::indexToRefId(effectInfo.mAttribute);
params.mDuration = effectInfo.mDuration; params.mDuration = effectInfo.mDuration;
params.mMagnMin = effectInfo.mMagnMin; params.mMagnMin = effectInfo.mMagnMin;
params.mMagnMax = effectInfo.mMagnMax; params.mMagnMax = effectInfo.mMagnMax;
@ -315,8 +314,8 @@ namespace MWGui::Widgets
{ {
SpellEffectParams params; SpellEffectParams params;
params.mEffectID = effectInfo.mEffectID; params.mEffectID = effectInfo.mEffectID;
params.mSkill = effectInfo.mSkill; params.mSkill = ESM::Skill::indexToRefId(effectInfo.mSkill);
params.mAttribute = effectInfo.mAttribute; params.mAttribute = ESM::Attribute::indexToRefId(effectInfo.mAttribute);
params.mDuration = effectInfo.mDuration; params.mDuration = effectInfo.mDuration;
params.mMagnMin = effectInfo.mMagnMin; params.mMagnMin = effectInfo.mMagnMin;
params.mMagnMax = effectInfo.mMagnMax; params.mMagnMax = effectInfo.mMagnMax;
@ -358,7 +357,7 @@ namespace MWGui::Widgets
const ESM::MagicEffect* magicEffect = store.get<ESM::MagicEffect>().search(mEffectParams.mEffectID); const ESM::MagicEffect* magicEffect = store.get<ESM::MagicEffect>().search(mEffectParams.mEffectID);
const ESM::Attribute* attribute = store.get<ESM::Attribute>().search(mEffectParams.mAttribute); const ESM::Attribute* attribute = store.get<ESM::Attribute>().search(mEffectParams.mAttribute);
const ESM::Skill* skill = store.get<ESM::Skill>().search(ESM::Skill::indexToRefId(mEffectParams.mSkill)); const ESM::Skill* skill = store.get<ESM::Skill>().search(mEffectParams.mSkill);
assert(magicEffect); assert(magicEffect);

@ -41,8 +41,6 @@ namespace MWGui
, mNoMagnitude(false) , mNoMagnitude(false)
, mKnown(true) , mKnown(true)
, mEffectID(-1) , mEffectID(-1)
, mSkill(-1)
, mAttribute(-1)
, mMagnMin(-1) , mMagnMin(-1)
, mMagnMax(-1) , mMagnMax(-1)
, mRange(-1) , mRange(-1)
@ -60,8 +58,7 @@ namespace MWGui
// value of -1 here means the effect is unknown to the player // value of -1 here means the effect is unknown to the player
short mEffectID; short mEffectID;
// value of -1 here means there is no skill/attribute ESM::RefId mSkill, mAttribute;
signed char mSkill, mAttribute;
// value of -1 here means the value is unavailable // value of -1 here means the value is unavailable
int mMagnMin, mMagnMax, mRange, mDuration; int mMagnMin, mMagnMax, mRange, mDuration;
@ -138,10 +135,10 @@ namespace MWGui
typedef MWMechanics::AttributeValue AttributeValue; typedef MWMechanics::AttributeValue AttributeValue;
void setAttributeId(ESM::Attribute::AttributeID attributeId); void setAttributeId(ESM::RefId attributeId);
void setAttributeValue(const AttributeValue& value); void setAttributeValue(const AttributeValue& value);
ESM::Attribute::AttributeID getAttributeId() const { return mId; } ESM::RefId getAttributeId() const { return mId; }
const AttributeValue& getAttributeValue() const { return mValue; } const AttributeValue& getAttributeValue() const { return mValue; }
// Events // Events
@ -162,7 +159,7 @@ namespace MWGui
private: private:
void updateWidgets(); void updateWidgets();
ESM::Attribute::AttributeID mId; ESM::RefId mId;
AttributeValue mValue; AttributeValue mValue;
MyGUI::TextBox* mAttributeNameWidget; MyGUI::TextBox* mAttributeNameWidget;
MyGUI::TextBox* mAttributeValueWidget; MyGUI::TextBox* mAttributeValueWidget;

@ -179,11 +179,18 @@ namespace MWLua
skills[key] = id; skills[key] = id;
} }
sol::table attribute(context.mLua->sol(), sol::create); // TODO: deprecate this and provide access to the store instead
api["ATTRIBUTE"] = LuaUtil::makeStrictReadOnly(attribute); sol::table attributes(context.mLua->sol(), sol::create);
for (int id = 0; id < ESM::Attribute::Length; ++id) api["ATTRIBUTE"] = LuaUtil::makeStrictReadOnly(attributes);
attribute[ESM::Attribute::sAttributeNames[id]] for (int i = 0; i < ESM::Attribute::Length; ++i)
= Misc::StringUtils::lowerCase(ESM::Attribute::sAttributeNames[id]); {
ESM::RefId attribute = ESM::Attribute::indexToRefId(i);
std::string id = attribute.serializeText();
std::string key = Misc::StringUtils::lowerCase(attribute.getRefIdString());
// force first character to uppercase for backwards compatability
key[0] += 'A' - 'a';
attributes[key] = id;
}
return LuaUtil::makeReadOnly(api); return LuaUtil::makeReadOnly(api);
} }

@ -358,10 +358,10 @@ namespace MWLua
}); });
effectParamsT["affectedAttribute"] effectParamsT["affectedAttribute"]
= sol::readonly_property([](const ESM::ENAMstruct& params) -> sol::optional<std::string> { = sol::readonly_property([](const ESM::ENAMstruct& params) -> sol::optional<std::string> {
if (params.mAttribute >= 0 && params.mAttribute < ESM::Attribute::Length) ESM::RefId id = ESM::Attribute::indexToRefId(params.mAttribute);
return Misc::StringUtils::lowerCase(ESM::Attribute::sAttributeNames[params.mAttribute]); if (!id.empty())
else return id.serializeText();
return sol::nullopt; return sol::nullopt;
}); });
effectParamsT["range"] effectParamsT["range"]
= sol::readonly_property([](const ESM::ENAMstruct& params) -> int { return params.mRange; }); = sol::readonly_property([](const ESM::ENAMstruct& params) -> int { return params.mRange; });
@ -419,23 +419,21 @@ namespace MWLua
return Misc::StringUtils::lowerCase(name); return Misc::StringUtils::lowerCase(name);
}); });
activeSpellEffectT["name"] = sol::readonly_property([](const ESM::ActiveEffect& effect) -> std::string { activeSpellEffectT["name"] = sol::readonly_property([](const ESM::ActiveEffect& effect) -> std::string {
return MWMechanics::EffectKey(effect.mEffectId, effect.mArg).toString(); return MWMechanics::EffectKey(effect.mEffectId, effect.getSkillOrAttribute()).toString();
}); });
activeSpellEffectT["affectedSkill"] activeSpellEffectT["affectedSkill"]
= sol::readonly_property([magicEffectStore](const ESM::ActiveEffect& effect) -> sol::optional<std::string> { = sol::readonly_property([magicEffectStore](const ESM::ActiveEffect& effect) -> sol::optional<std::string> {
auto* rec = magicEffectStore->find(effect.mEffectId); auto* rec = magicEffectStore->find(effect.mEffectId);
if ((rec->mData.mFlags & ESM::MagicEffect::TargetSkill) && effect.mArg >= 0 if (rec->mData.mFlags & ESM::MagicEffect::TargetSkill)
&& effect.mArg < ESM::Skill::Length) return effect.getSkillOrAttribute().serializeText();
return ESM::Skill::indexToRefId(effect.mArg).serializeText();
else else
return sol::nullopt; return sol::nullopt;
}); });
activeSpellEffectT["affectedAttribute"] activeSpellEffectT["affectedAttribute"]
= sol::readonly_property([magicEffectStore](const ESM::ActiveEffect& effect) -> sol::optional<std::string> { = sol::readonly_property([magicEffectStore](const ESM::ActiveEffect& effect) -> sol::optional<std::string> {
auto* rec = magicEffectStore->find(effect.mEffectId); auto* rec = magicEffectStore->find(effect.mEffectId);
if ((rec->mData.mFlags & ESM::MagicEffect::TargetAttribute) && effect.mArg >= 0 if (rec->mData.mFlags & ESM::MagicEffect::TargetAttribute)
&& effect.mArg < ESM::Attribute::Length) return effect.getSkillOrAttribute().serializeText();
return Misc::StringUtils::lowerCase(ESM::Attribute::sAttributeNames[effect.mArg]);
else else
return sol::nullopt; return sol::nullopt;
}); });
@ -542,20 +540,15 @@ namespace MWLua
= sol::readonly_property([magicEffectStore](const ActiveEffect& effect) -> sol::optional<std::string> { = sol::readonly_property([magicEffectStore](const ActiveEffect& effect) -> sol::optional<std::string> {
auto* rec = magicEffectStore->find(effect.key.mId); auto* rec = magicEffectStore->find(effect.key.mId);
if (rec->mData.mFlags & ESM::MagicEffect::TargetSkill) if (rec->mData.mFlags & ESM::MagicEffect::TargetSkill)
{ return effect.key.mArg.serializeText();
ESM::RefId id = ESM::Skill::indexToRefId(effect.key.mArg);
return id.serializeText();
}
return sol::nullopt; return sol::nullopt;
}); });
activeEffectT["affectedAttribute"] activeEffectT["affectedAttribute"]
= sol::readonly_property([magicEffectStore](const ActiveEffect& effect) -> sol::optional<std::string> { = sol::readonly_property([magicEffectStore](const ActiveEffect& effect) -> sol::optional<std::string> {
auto* rec = magicEffectStore->find(effect.key.mId); auto* rec = magicEffectStore->find(effect.key.mId);
if ((rec->mData.mFlags & ESM::MagicEffect::TargetAttribute) && effect.key.mArg >= 0 if (rec->mData.mFlags & ESM::MagicEffect::TargetAttribute)
&& effect.key.mArg < ESM::Attribute::Length) return effect.key.mArg.serializeText();
return Misc::StringUtils::lowerCase(ESM::Attribute::sAttributeNames[effect.key.mArg]); return sol::nullopt;
else
return sol::nullopt;
}); });
activeEffectT["magnitude"] activeEffectT["magnitude"]
@ -777,12 +770,15 @@ namespace MWLua
{ {
// MWLua exposes attributes and skills as strings, so we have to convert them back to IDs here // MWLua exposes attributes and skills as strings, so we have to convert them back to IDs here
if (rec->mData.mFlags & ESM::MagicEffect::TargetAttribute) if (rec->mData.mFlags & ESM::MagicEffect::TargetAttribute)
key = MWMechanics::EffectKey(id, ESM::Attribute::stringToAttributeId(argStr.value())); {
ESM::RefId attribute = ESM::RefId::deserializeText(argStr.value());
key = MWMechanics::EffectKey(id, attribute);
}
if (rec->mData.mFlags & ESM::MagicEffect::TargetSkill) if (rec->mData.mFlags & ESM::MagicEffect::TargetSkill)
{ {
ESM::RefId skill = ESM::RefId::deserializeText(argStr.value()); ESM::RefId skill = ESM::RefId::deserializeText(argStr.value());
key = MWMechanics::EffectKey(id, ESM::Skill::refIdToIndex(skill)); key = MWMechanics::EffectKey(id, skill);
} }
} }

@ -171,11 +171,11 @@ namespace MWLua
class AttributeStat class AttributeStat
{ {
ObjectVariant mObject; ObjectVariant mObject;
int mIndex; ESM::RefId mId;
AttributeStat(ObjectVariant object, int index) AttributeStat(ObjectVariant object, ESM::RefId id)
: mObject(std::move(object)) : mObject(std::move(object))
, mIndex(index) , mId(id)
{ {
} }
@ -183,10 +183,9 @@ namespace MWLua
template <class G> template <class G>
sol::object get(const Context& context, std::string_view prop, G getter) const sol::object get(const Context& context, std::string_view prop, G getter) const
{ {
auto id = static_cast<ESM::Attribute::AttributeID>(mIndex);
return getValue( return getValue(
context, mObject, &AttributeStat::setValue, mIndex, prop, [id, getter](const MWWorld::Ptr& ptr) { context, mObject, &AttributeStat::setValue, mId, prop, [this, getter](const MWWorld::Ptr& ptr) {
return (ptr.getClass().getCreatureStats(ptr).getAttribute(id).*getter)(); return (ptr.getClass().getCreatureStats(ptr).getAttribute(mId).*getter)();
}); });
} }
@ -202,20 +201,20 @@ namespace MWLua
{ {
if (!object.ptr().getClass().isActor()) if (!object.ptr().getClass().isActor())
return {}; return {};
int index = std::get<int>(i); ESM::RefId id = std::get<ESM::RefId>(i);
return AttributeStat{ std::move(object), index }; return AttributeStat{ std::move(object), id };
} }
void cache(const Context& context, std::string_view prop, const sol::object& value) const void cache(const Context& context, std::string_view prop, const sol::object& value) const
{ {
SelfObject* obj = mObject.asSelfObject(); SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj); addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &AttributeStat::setValue, mIndex, prop }] = value; obj->mStatsCache[SelfObject::CachedStat{ &AttributeStat::setValue, mId, prop }] = value;
} }
static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
{ {
auto id = static_cast<ESM::Attribute::AttributeID>(std::get<int>(i)); ESM::RefId id = std::get<ESM::RefId>(i);
auto& stats = ptr.getClass().getCreatureStats(ptr); auto& stats = ptr.getClass().getCreatureStats(ptr);
auto stat = stats.getAttribute(id); auto stat = stats.getAttribute(id);
float floatValue = LuaUtil::cast<float>(value); float floatValue = LuaUtil::cast<float>(value);
@ -370,9 +369,8 @@ namespace MWLua
addProp(context, attributeStatT, "modifier", &MWMechanics::AttributeValue::getModifier); addProp(context, attributeStatT, "modifier", &MWMechanics::AttributeValue::getModifier);
sol::table attributes(context.mLua->sol(), sol::create); sol::table attributes(context.mLua->sol(), sol::create);
stats["attributes"] = LuaUtil::makeReadOnly(attributes); stats["attributes"] = LuaUtil::makeReadOnly(attributes);
for (int id = ESM::Attribute::Strength; id < ESM::Attribute::Length; ++id) for (const ESM::Attribute& attribute : MWBase::Environment::get().getESMStore()->get<ESM::Attribute>())
attributes[Misc::StringUtils::lowerCase(ESM::Attribute::sAttributeNames[id])] attributes[ESM::RefId(attribute.mId).serializeText()] = addIndexedAccessor<AttributeStat>(attribute.mId);
= addIndexedAccessor<AttributeStat>(id);
} }
void addNpcStatsBindings(sol::table& npc, const Context& context) void addNpcStatsBindings(sol::table& npc, const Context& context)

@ -548,14 +548,13 @@ namespace MWMechanics
purge([=](const ActiveSpellParams& params) { return params.mId == id; }, ptr); purge([=](const ActiveSpellParams& params) { return params.mId == id; }, ptr);
} }
void ActiveSpells::purgeEffect(const MWWorld::Ptr& ptr, int effectId, int effectArg) void ActiveSpells::purgeEffect(const MWWorld::Ptr& ptr, int effectId, ESM::RefId effectArg)
{ {
purge( purge(
[=](const ActiveSpellParams&, const ESM::ActiveEffect& effect) { [=](const ActiveSpellParams&, const ESM::ActiveEffect& effect) {
if (effectArg < 0) if (effectArg.empty())
return effect.mEffectId == effectId; return effect.mEffectId == effectId;
else return effect.mEffectId == effectId && effect.getSkillOrAttribute() == effectArg;
return effect.mEffectId == effectId && effect.mArg == effectArg;
}, },
ptr); ptr);
} }

@ -134,7 +134,7 @@ namespace MWMechanics
void removeEffects(const MWWorld::Ptr& ptr, const ESM::RefId& id); void removeEffects(const MWWorld::Ptr& ptr, const ESM::RefId& id);
/// Remove all active effects with this effect id /// Remove all active effects with this effect id
void purgeEffect(const MWWorld::Ptr& ptr, int effectId, int effectArg = -1); void purgeEffect(const MWWorld::Ptr& ptr, int effectId, ESM::RefId effectArg = {});
void purge(EffectPredicate predicate, const MWWorld::Ptr& ptr); void purge(EffectPredicate predicate, const MWWorld::Ptr& ptr);
void purge(ParamsPredicate predicate, const MWWorld::Ptr& ptr); void purge(ParamsPredicate predicate, const MWWorld::Ptr& ptr);

@ -45,9 +45,10 @@ std::set<MWMechanics::EffectKey> MWMechanics::Alchemy::listEffects() const
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
if (ingredient->mBase->mData.mEffectID[i] != -1) if (ingredient->mBase->mData.mEffectID[i] != -1)
{ {
EffectKey key(ingredient->mBase->mData.mEffectID[i], ESM::RefId arg = ESM::Skill::indexToRefId(ingredient->mBase->mData.mSkills[i]);
ingredient->mBase->mData.mSkills[i] != -1 ? ingredient->mBase->mData.mSkills[i] if (arg.empty())
: ingredient->mBase->mData.mAttributes[i]); arg = ESM::Attribute::indexToRefId(ingredient->mBase->mData.mAttributes[i]);
EffectKey key(ingredient->mBase->mData.mEffectID[i], arg);
if (seenEffects.insert(key).second) if (seenEffects.insert(key).second)
++effects[key]; ++effects[key];
@ -203,9 +204,9 @@ void MWMechanics::Alchemy::updateEffects()
effect.mSkill = -1; effect.mSkill = -1;
if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)
effect.mSkill = iter->mArg; effect.mSkill = ESM::Skill::refIdToIndex(iter->mArg);
else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)
effect.mAttribute = iter->mArg; effect.mAttribute = ESM::Attribute::refIdToIndex(iter->mArg);
effect.mRange = 0; effect.mRange = 0;
effect.mArea = 0; effect.mArea = 0;
@ -576,7 +577,8 @@ std::vector<std::string> MWMechanics::Alchemy::effectsDescription(const MWWorld:
if (effectID != -1) if (effectID != -1)
{ {
const ESM::Attribute* attribute = store->get<ESM::Attribute>().search(data.mAttributes[i]); const ESM::Attribute* attribute
= store->get<ESM::Attribute>().search(ESM::Attribute::indexToRefId(data.mAttributes[i]));
const ESM::Skill* skill = store->get<ESM::Skill>().search(ESM::Skill::indexToRefId(data.mSkills[i])); const ESM::Skill* skill = store->get<ESM::Skill>().search(ESM::Skill::indexToRefId(data.mSkills[i]));
std::string effect = getMagicEffectString(*mgef.find(effectID), attribute, skill); std::string effect = getMagicEffectString(*mgef.find(effectID), attribute, skill);

@ -27,7 +27,7 @@ namespace MWMechanics
}; };
std::vector<ESM::RefId> autoCalcNpcSpells(const std::map<ESM::RefId, SkillValue>& actorSkills, std::vector<ESM::RefId> autoCalcNpcSpells(const std::map<ESM::RefId, SkillValue>& actorSkills,
const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, const ESM::Race* race) const std::map<ESM::RefId, AttributeValue>& actorAttributes, const ESM::Race* race)
{ {
const MWWorld::Store<ESM::GameSetting>& gmst const MWWorld::Store<ESM::GameSetting>& gmst
= MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>(); = MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
@ -136,7 +136,7 @@ namespace MWMechanics
} }
std::vector<ESM::RefId> autoCalcPlayerSpells(const std::map<ESM::RefId, SkillValue>& actorSkills, std::vector<ESM::RefId> autoCalcPlayerSpells(const std::map<ESM::RefId, SkillValue>& actorSkills,
const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, const ESM::Race* race) const std::map<ESM::RefId, AttributeValue>& actorAttributes, const ESM::Race* race)
{ {
const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore(); const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore();
@ -215,7 +215,7 @@ namespace MWMechanics
} }
bool attrSkillCheck(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, bool attrSkillCheck(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills,
const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes) const std::map<ESM::RefId, AttributeValue>& actorAttributes)
{ {
for (const auto& spellEffect : spell->mEffects.mList) for (const auto& spellEffect : spell->mEffects.mList)
{ {
@ -237,7 +237,8 @@ namespace MWMechanics
if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)) if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute))
{ {
auto found = actorAttributes.find(ESM::Attribute::AttributeID(spellEffect.mAttribute)); ESM::RefId attribute = ESM::Attribute::indexToRefId(spellEffect.mAttribute);
auto found = actorAttributes.find(attribute);
if (found == actorAttributes.end() || found->second.getBase() < iAutoSpellAttSkillMin) if (found == actorAttributes.end() || found->second.getBase() < iAutoSpellAttSkillMin)
return false; return false;
} }
@ -299,7 +300,7 @@ namespace MWMechanics
} }
float calcAutoCastChance(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, float calcAutoCastChance(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills,
const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, ESM::RefId effectiveSchool) const std::map<ESM::RefId, AttributeValue>& actorAttributes, ESM::RefId effectiveSchool)
{ {
if (spell->mData.mType != ESM::Spell::ST_Spell) if (spell->mData.mType != ESM::Spell::ST_Spell)
return 100.f; return 100.f;

@ -20,21 +20,21 @@ namespace MWMechanics
/// @note We might want to move this code to a component later, so the editor can use it for preview purposes /// @note We might want to move this code to a component later, so the editor can use it for preview purposes
std::vector<ESM::RefId> autoCalcNpcSpells(const std::map<ESM::RefId, SkillValue>& actorSkills, std::vector<ESM::RefId> autoCalcNpcSpells(const std::map<ESM::RefId, SkillValue>& actorSkills,
const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, const ESM::Race* race); const std::map<ESM::RefId, AttributeValue>& actorAttributes, const ESM::Race* race);
std::vector<ESM::RefId> autoCalcPlayerSpells(const std::map<ESM::RefId, SkillValue>& actorSkills, std::vector<ESM::RefId> autoCalcPlayerSpells(const std::map<ESM::RefId, SkillValue>& actorSkills,
const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, const ESM::Race* race); const std::map<ESM::RefId, AttributeValue>& actorAttributes, const ESM::Race* race);
// Helpers // Helpers
bool attrSkillCheck(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, bool attrSkillCheck(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills,
const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes); const std::map<ESM::RefId, AttributeValue>& actorAttributes);
void calcWeakestSchool(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, void calcWeakestSchool(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills,
ESM::RefId& effectiveSchool, float& skillTerm); ESM::RefId& effectiveSchool, float& skillTerm);
float calcAutoCastChance(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, float calcAutoCastChance(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills,
const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, ESM::RefId effectiveSchool); const std::map<ESM::RefId, AttributeValue>& actorAttributes, ESM::RefId effectiveSchool);
} }

@ -78,7 +78,7 @@ namespace MWMechanics
return fFatigueBase - fFatigueMult * (1 - normalised); return fFatigueBase - fFatigueMult * (1 - normalised);
} }
const AttributeValue& CreatureStats::getAttribute(ESM::Attribute::AttributeID id) const const AttributeValue& CreatureStats::getAttribute(ESM::RefId id) const
{ {
return mAttributes.at(id); return mAttributes.at(id);
} }
@ -147,14 +147,14 @@ namespace MWMechanics
return mMagicEffects; return mMagicEffects;
} }
void CreatureStats::setAttribute(ESM::Attribute::AttributeID id, float base) void CreatureStats::setAttribute(ESM::RefId id, float base)
{ {
AttributeValue current = getAttribute(id); AttributeValue current = getAttribute(id);
current.setBase(base); current.setBase(base);
setAttribute(id, current); setAttribute(id, current);
} }
void CreatureStats::setAttribute(ESM::Attribute::AttributeID id, const AttributeValue& value) void CreatureStats::setAttribute(ESM::RefId id, const AttributeValue& value)
{ {
const AttributeValue& currentValue = mAttributes.at(id); const AttributeValue& currentValue = mAttributes.at(id);
@ -531,7 +531,7 @@ namespace MWMechanics
void CreatureStats::writeState(ESM::CreatureStats& state) const void CreatureStats::writeState(ESM::CreatureStats& state) const
{ {
for (size_t i = 0; i < state.mAttributes.size(); ++i) for (size_t i = 0; i < state.mAttributes.size(); ++i)
getAttribute(static_cast<ESM::Attribute::AttributeID>(i)).writeState(state.mAttributes[i]); getAttribute(ESM::Attribute::indexToRefId(i)).writeState(state.mAttributes[i]);
for (size_t i = 0; i < state.mDynamic.size(); ++i) for (size_t i = 0; i < state.mDynamic.size(); ++i)
mDynamic[i].writeState(state.mDynamic[i]); mDynamic[i].writeState(state.mDynamic[i]);
@ -588,7 +588,7 @@ namespace MWMechanics
if (!state.mMissingACDT) if (!state.mMissingACDT)
{ {
for (size_t i = 0; i < state.mAttributes.size(); ++i) for (size_t i = 0; i < state.mAttributes.size(); ++i)
mAttributes[static_cast<ESM::Attribute::AttributeID>(i)].readState(state.mAttributes[i]); mAttributes[ESM::Attribute::indexToRefId(i)].readState(state.mAttributes[i]);
for (size_t i = 0; i < state.mDynamic.size(); ++i) for (size_t i = 0; i < state.mDynamic.size(); ++i)
mDynamic[i].readState(state.mDynamic[i]); mDynamic[i].readState(state.mDynamic[i]);

@ -40,7 +40,7 @@ namespace MWMechanics
{ {
static int sActorId; static int sActorId;
DrawState mDrawState; DrawState mDrawState;
std::map<ESM::Attribute::AttributeID, AttributeValue> mAttributes; std::map<ESM::RefId, AttributeValue> mAttributes;
DynamicStat<float> mDynamic[3]; // health, magicka, fatigue DynamicStat<float> mDynamic[3]; // health, magicka, fatigue
Spells mSpells; Spells mSpells;
ActiveSpells mActiveSpells; ActiveSpells mActiveSpells;
@ -113,7 +113,7 @@ namespace MWMechanics
/// @return total fall height /// @return total fall height
float land(bool isPlayer = false); float land(bool isPlayer = false);
const AttributeValue& getAttribute(ESM::Attribute::AttributeID id) const; const AttributeValue& getAttribute(ESM::RefId id) const;
const DynamicStat<float>& getHealth() const; const DynamicStat<float>& getHealth() const;
@ -139,9 +139,9 @@ namespace MWMechanics
MagicEffects& getMagicEffects(); MagicEffects& getMagicEffects();
void setAttribute(ESM::Attribute::AttributeID id, const AttributeValue& value); void setAttribute(ESM::RefId id, const AttributeValue& value);
// Shortcut to set only the base // Shortcut to set only the base
void setAttribute(ESM::Attribute::AttributeID id, float base); void setAttribute(ESM::RefId id, float base);
void setHealth(const DynamicStat<float>& value); void setHealth(const DynamicStat<float>& value);
@ -294,7 +294,7 @@ namespace MWMechanics
bool wasTeleported() const { return mTeleported; } bool wasTeleported() const { return mTeleported; }
void setTeleported(bool v) { mTeleported = v; } void setTeleported(bool v) { mTeleported = v; }
const std::map<ESM::Attribute::AttributeID, AttributeValue> getAttributes() const { return mAttributes; } const std::map<ESM::RefId, AttributeValue> getAttributes() const { return mAttributes; }
}; };
} }

@ -27,24 +27,21 @@ namespace MWMechanics
{ {
EffectKey::EffectKey() EffectKey::EffectKey()
: mId(0) : mId(0)
, mArg(-1)
{ {
} }
EffectKey::EffectKey(const ESM::ENAMstruct& effect) EffectKey::EffectKey(const ESM::ENAMstruct& effect)
{ {
mId = effect.mEffectID; mId = effect.mEffectID;
mArg = -1; mArg = ESM::Skill::indexToRefId(effect.mSkill);
if (effect.mSkill != -1) ESM::RefId attribute = ESM::Attribute::indexToRefId(effect.mAttribute);
mArg = effect.mSkill; if (!attribute.empty())
if (effect.mAttribute != -1)
{ {
if (mArg != -1) if (!mArg.empty())
throw std::runtime_error("magic effect can't have both a skill and an attribute argument"); throw std::runtime_error("magic effect can't have both a skill and an attribute argument");
mArg = effect.mAttribute; mArg = attribute;
} }
} }
@ -52,8 +49,8 @@ namespace MWMechanics
{ {
const auto& store = MWBase::Environment::get().getESMStore(); const auto& store = MWBase::Environment::get().getESMStore();
const ESM::MagicEffect* magicEffect = store->get<ESM::MagicEffect>().search(mId); const ESM::MagicEffect* magicEffect = store->get<ESM::MagicEffect>().search(mId);
return getMagicEffectString(*magicEffect, store->get<ESM::Attribute>().search(mArg), return getMagicEffectString(
store->get<ESM::Skill>().search(ESM::Skill::indexToRefId(mArg))); *magicEffect, store->get<ESM::Attribute>().search(mArg), store->get<ESM::Skill>().search(mArg));
} }
bool operator<(const EffectKey& left, const EffectKey& right) bool operator<(const EffectKey& left, const EffectKey& right)

@ -5,6 +5,8 @@
#include <optional> #include <optional>
#include <string> #include <string>
#include <components/esm/refid.hpp>
namespace ESM namespace ESM
{ {
struct Attribute; struct Attribute;
@ -20,11 +22,11 @@ namespace MWMechanics
struct EffectKey struct EffectKey
{ {
int mId; int mId;
int mArg; // skill or ability ESM::RefId mArg; // skill or ability
EffectKey(); EffectKey();
EffectKey(int id, int arg = -1) EffectKey(int id, ESM::RefId arg = {})
: mId(id) : mId(id)
, mArg(arg) , mArg(arg)
{ {

@ -150,7 +150,8 @@ namespace MWMechanics
for (const ESM::Attribute& attribute : esmStore.get<ESM::Attribute>()) for (const ESM::Attribute& attribute : esmStore.get<ESM::Attribute>())
{ {
const ESM::Race::MaleFemale& value = race->mData.mAttributeValues[attribute.mId]; const ESM::Race::MaleFemale& value
= race->mData.mAttributeValues[ESM::Attribute::refIdToIndex(attribute.mId)];
creatureStats.setAttribute(attribute.mId, male ? value.mMale : value.mFemale); creatureStats.setAttribute(attribute.mId, male ? value.mMale : value.mFemale);
} }
@ -193,11 +194,9 @@ namespace MWMechanics
for (int attribute : class_->mData.mAttribute) for (int attribute : class_->mData.mAttribute)
{ {
if (attribute >= 0 && attribute < ESM::Attribute::Length) ESM::RefId id = ESM::Attribute::indexToRefId(attribute);
{ if (!id.empty())
auto id = static_cast<ESM::Attribute::AttributeID>(attribute);
creatureStats.setAttribute(id, creatureStats.getAttribute(id).getBase() + 10); creatureStats.setAttribute(id, creatureStats.getAttribute(id).getBase() + 10);
}
} }
for (int i = 0; i < 2; ++i) for (int i = 0; i < 2; ++i)

@ -246,7 +246,7 @@ void MWMechanics::NpcStats::increaseSkill(ESM::RefId id, const ESM::Class& class
} }
} }
mSkillIncreases[ESM::Attribute::AttributeID(skill->mData.mAttribute)] += increase; mSkillIncreases[ESM::Attribute::indexToRefId(skill->mData.mAttribute)] += increase;
mSpecIncreases[skill->mData.mSpecialization] += gmst.find("iLevelupSpecialization")->mValue.getInteger(); mSpecIncreases[skill->mData.mSpecialization] += gmst.find("iLevelupSpecialization")->mValue.getInteger();
@ -488,7 +488,7 @@ void MWMechanics::NpcStats::writeState(ESM::NpcStats& state) const
state.mSkillIncrease.fill(0); state.mSkillIncrease.fill(0);
for (const auto& [key, value] : mSkillIncreases) for (const auto& [key, value] : mSkillIncreases)
state.mSkillIncrease[key] = value; state.mSkillIncrease[ESM::Attribute::refIdToIndex(key)] = value;
for (size_t i = 0; i < state.mSpecIncreases.size(); ++i) for (size_t i = 0; i < state.mSpecIncreases.size(); ++i)
state.mSpecIncreases[i] = mSpecIncreases[i]; state.mSpecIncreases[i] = mSpecIncreases[i];
@ -538,7 +538,7 @@ void MWMechanics::NpcStats::readState(const ESM::NpcStats& state)
mLevelProgress = state.mLevelProgress; mLevelProgress = state.mLevelProgress;
for (size_t i = 0; i < state.mSkillIncrease.size(); ++i) for (size_t i = 0; i < state.mSkillIncrease.size(); ++i)
mSkillIncreases[static_cast<ESM::Attribute::AttributeID>(i)] = state.mSkillIncrease[i]; mSkillIncreases[ESM::Attribute::indexToRefId(i)] = state.mSkillIncrease[i];
for (size_t i = 0; i < state.mSpecIncreases.size(); ++i) for (size_t i = 0; i < state.mSpecIncreases.size(); ++i)
mSpecIncreases[i] = state.mSpecIncreases[i]; mSpecIncreases[i] = state.mSpecIncreases[i];

@ -36,7 +36,7 @@ namespace MWMechanics
std::set<ESM::RefId> mExpelled; std::set<ESM::RefId> mExpelled;
std::map<ESM::RefId, int> mFactionReputation; std::map<ESM::RefId, int> mFactionReputation;
int mLevelProgress; // 0-10 int mLevelProgress; // 0-10
std::map<ESM::Attribute::AttributeID, int> std::map<ESM::RefId, int>
mSkillIncreases; // number of skill increases for each attribute (resets after leveling up) mSkillIncreases; // number of skill increases for each attribute (resets after leveling up)
std::vector<int> mSpecIncreases; // number of skill increases for each specialization (accumulates throughout std::vector<int> mSpecIncreases; // number of skill increases for each specialization (accumulates throughout
// the entire game) // the entire game)

@ -80,7 +80,7 @@ namespace
void damageAttribute(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude) void damageAttribute(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude)
{ {
auto& creatureStats = target.getClass().getCreatureStats(target); auto& creatureStats = target.getClass().getCreatureStats(target);
auto attribute = static_cast<ESM::Attribute::AttributeID>(effect.mArg); auto attribute = effect.getSkillOrAttribute();
auto attr = creatureStats.getAttribute(attribute); auto attr = creatureStats.getAttribute(attribute);
if (effect.mEffectId == ESM::MagicEffect::DamageAttribute) if (effect.mEffectId == ESM::MagicEffect::DamageAttribute)
magnitude = std::min(attr.getModified(), magnitude); magnitude = std::min(attr.getModified(), magnitude);
@ -91,7 +91,7 @@ namespace
void restoreAttribute(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude) void restoreAttribute(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude)
{ {
auto& creatureStats = target.getClass().getCreatureStats(target); auto& creatureStats = target.getClass().getCreatureStats(target);
auto attribute = static_cast<ESM::Attribute::AttributeID>(effect.mArg); auto attribute = effect.getSkillOrAttribute();
auto attr = creatureStats.getAttribute(attribute); auto attr = creatureStats.getAttribute(attribute);
attr.restore(magnitude); attr.restore(magnitude);
creatureStats.setAttribute(attribute, attr); creatureStats.setAttribute(attribute, attr);
@ -100,7 +100,7 @@ namespace
void fortifyAttribute(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude) void fortifyAttribute(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude)
{ {
auto& creatureStats = target.getClass().getCreatureStats(target); auto& creatureStats = target.getClass().getCreatureStats(target);
auto attribute = static_cast<ESM::Attribute::AttributeID>(effect.mArg); auto attribute = effect.getSkillOrAttribute();
auto attr = creatureStats.getAttribute(attribute); auto attr = creatureStats.getAttribute(attribute);
attr.setModifier(attr.getModifier() + magnitude); attr.setModifier(attr.getModifier() + magnitude);
creatureStats.setAttribute(attribute, attr); creatureStats.setAttribute(attribute, attr);
@ -109,7 +109,7 @@ namespace
void damageSkill(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude) void damageSkill(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude)
{ {
auto& npcStats = target.getClass().getNpcStats(target); auto& npcStats = target.getClass().getNpcStats(target);
auto& skill = npcStats.getSkill(ESM::Skill::indexToRefId(effect.mArg)); auto& skill = npcStats.getSkill(effect.getSkillOrAttribute());
if (effect.mEffectId == ESM::MagicEffect::DamageSkill) if (effect.mEffectId == ESM::MagicEffect::DamageSkill)
magnitude = std::min(skill.getModified(), magnitude); magnitude = std::min(skill.getModified(), magnitude);
skill.damage(magnitude); skill.damage(magnitude);
@ -118,14 +118,14 @@ namespace
void restoreSkill(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude) void restoreSkill(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude)
{ {
auto& npcStats = target.getClass().getNpcStats(target); auto& npcStats = target.getClass().getNpcStats(target);
auto& skill = npcStats.getSkill(ESM::Skill::indexToRefId(effect.mArg)); auto& skill = npcStats.getSkill(effect.getSkillOrAttribute());
skill.restore(magnitude); skill.restore(magnitude);
} }
void fortifySkill(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude) void fortifySkill(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude)
{ {
auto& npcStats = target.getClass().getNpcStats(target); auto& npcStats = target.getClass().getNpcStats(target);
auto& skill = npcStats.getSkill(ESM::Skill::indexToRefId(effect.mArg)); auto& skill = npcStats.getSkill(effect.getSkillOrAttribute());
skill.setModifier(skill.getModifier() + magnitude); skill.setModifier(skill.getModifier() + magnitude);
} }
@ -670,7 +670,7 @@ namespace MWMechanics
if (spellParams.getType() == ESM::ActiveSpells::Type_Ability) if (spellParams.getType() == ESM::ActiveSpells::Type_Ability)
{ {
auto& npcStats = target.getClass().getNpcStats(target); auto& npcStats = target.getClass().getNpcStats(target);
SkillValue& skill = npcStats.getSkill(ESM::Skill::indexToRefId(effect.mArg)); SkillValue& skill = npcStats.getSkill(effect.getSkillOrAttribute());
// Damage Skill abilities reduce base skill :todd: // Damage Skill abilities reduce base skill :todd:
skill.setBase(std::max(skill.getBase() - effect.mMagnitude, 0.f)); skill.setBase(std::max(skill.getBase() - effect.mMagnitude, 0.f));
} }
@ -741,7 +741,7 @@ namespace MWMechanics
if (spellParams.getType() == ESM::ActiveSpells::Type_Ability) if (spellParams.getType() == ESM::ActiveSpells::Type_Ability)
{ {
auto& creatureStats = target.getClass().getCreatureStats(target); auto& creatureStats = target.getClass().getCreatureStats(target);
auto attribute = static_cast<ESM::Attribute::AttributeID>(effect.mArg); auto attribute = effect.getSkillOrAttribute();
AttributeValue attr = creatureStats.getAttribute(attribute); AttributeValue attr = creatureStats.getAttribute(attribute);
attr.setBase(attr.getBase() + effect.mMagnitude); attr.setBase(attr.getBase() + effect.mMagnitude);
creatureStats.setAttribute(attribute, attr); creatureStats.setAttribute(attribute, attr);
@ -762,7 +762,7 @@ namespace MWMechanics
{ {
// Abilities affect base stats, but not for drain // Abilities affect base stats, but not for drain
auto& npcStats = target.getClass().getNpcStats(target); auto& npcStats = target.getClass().getNpcStats(target);
auto& skill = npcStats.getSkill(ESM::Skill::indexToRefId(effect.mArg)); auto& skill = npcStats.getSkill(effect.getSkillOrAttribute());
skill.setBase(skill.getBase() + effect.mMagnitude); skill.setBase(skill.getBase() + effect.mMagnitude);
} }
else else
@ -1033,7 +1033,8 @@ namespace MWMechanics
applyMagicEffect(target, caster, spellParams, effect, invalid, receivedMagicDamage, affectedHealth, applyMagicEffect(target, caster, spellParams, effect, invalid, receivedMagicDamage, affectedHealth,
recalculateMagicka); recalculateMagicka);
effect.mMagnitude = magnitude; effect.mMagnitude = magnitude;
magnitudes.add(EffectKey(effect.mEffectId, effect.mArg), EffectParam(effect.mMagnitude - oldMagnitude)); magnitudes.add(EffectKey(effect.mEffectId, effect.getSkillOrAttribute()),
EffectParam(effect.mMagnitude - oldMagnitude));
} }
effect.mTimeLeft -= dt; effect.mTimeLeft -= dt;
if (invalid) if (invalid)
@ -1145,13 +1146,14 @@ namespace MWMechanics
case ESM::MagicEffect::SummonCreature04: case ESM::MagicEffect::SummonCreature04:
case ESM::MagicEffect::SummonCreature05: case ESM::MagicEffect::SummonCreature05:
{ {
if (effect.mArg != -1) int actorId = effect.getActorId();
MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(target, effect.mArg); if (actorId != -1)
MWBase::Environment::get().getMechanicsManager()->cleanupSummonedCreature(target, actorId);
auto& summons = target.getClass().getCreatureStats(target).getSummonedCreatureMap(); auto& summons = target.getClass().getCreatureStats(target).getSummonedCreatureMap();
auto [begin, end] = summons.equal_range(effect.mEffectId); auto [begin, end] = summons.equal_range(effect.mEffectId);
for (auto it = begin; it != end; ++it) for (auto it = begin; it != end; ++it)
{ {
if (it->second == effect.mArg) if (it->second == actorId)
{ {
summons.erase(it); summons.erase(it);
break; break;
@ -1205,7 +1207,7 @@ namespace MWMechanics
if (spellParams.getType() == ESM::ActiveSpells::Type_Ability) if (spellParams.getType() == ESM::ActiveSpells::Type_Ability)
{ {
auto& creatureStats = target.getClass().getCreatureStats(target); auto& creatureStats = target.getClass().getCreatureStats(target);
auto attribute = static_cast<ESM::Attribute::AttributeID>(effect.mArg); auto attribute = effect.getSkillOrAttribute();
AttributeValue attr = creatureStats.getAttribute(attribute); AttributeValue attr = creatureStats.getAttribute(attribute);
attr.setBase(attr.getBase() - effect.mMagnitude); attr.setBase(attr.getBase() - effect.mMagnitude);
creatureStats.setAttribute(attribute, attr); creatureStats.setAttribute(attribute, attr);
@ -1221,7 +1223,7 @@ namespace MWMechanics
if (spellParams.getType() == ESM::ActiveSpells::Type_Ability) if (spellParams.getType() == ESM::ActiveSpells::Type_Ability)
{ {
auto& npcStats = target.getClass().getNpcStats(target); auto& npcStats = target.getClass().getNpcStats(target);
auto& skill = npcStats.getSkill(ESM::Skill::indexToRefId(effect.mArg)); auto& skill = npcStats.getSkill(effect.getSkillOrAttribute());
skill.setBase(skill.getBase() - effect.mMagnitude); skill.setBase(skill.getBase() - effect.mMagnitude);
} }
else else
@ -1277,7 +1279,7 @@ namespace MWMechanics
if (!(effect.mFlags & ESM::ActiveEffect::Flag_Applied)) if (!(effect.mFlags & ESM::ActiveEffect::Flag_Applied))
return; return;
auto& magnitudes = target.getClass().getCreatureStats(target).getMagicEffects(); auto& magnitudes = target.getClass().getCreatureStats(target).getMagicEffects();
magnitudes.add(EffectKey(effect.mEffectId, effect.mArg), EffectParam(-effect.mMagnitude)); magnitudes.add(EffectKey(effect.mEffectId, effect.getSkillOrAttribute()), EffectParam(-effect.mMagnitude));
removeMagicEffect(target, spellParams, effect); removeMagicEffect(target, spellParams, effect);
if (magnitudes.getOrDefault(effect.mEffectId).getMagnitude() <= 0.f) if (magnitudes.getOrDefault(effect.mEffectId).getMagnitude() <= 0.f)
{ {

@ -573,7 +573,7 @@ namespace MWMechanics
if (!enemy.isEmpty() if (!enemy.isEmpty()
&& enemy.getClass() && enemy.getClass()
.getCreatureStats(enemy) .getCreatureStats(enemy)
.getAttribute(ESM::Attribute::AttributeID(effect.mAttribute)) .getAttribute(ESM::Attribute::indexToRefId(effect.mAttribute))
.getModified() .getModified()
<= 0) <= 0)
return 0.f; return 0.f;

@ -252,7 +252,7 @@ namespace MWMechanics
// Applied corprus effects are already in loaded stats modifiers // Applied corprus effects are already in loaded stats modifiers
if (info.mId == ESM::MagicEffect::FortifyAttribute) if (info.mId == ESM::MagicEffect::FortifyAttribute)
{ {
auto id = static_cast<ESM::Attribute::AttributeID>(info.mArg); auto id = ESM::Attribute::indexToRefId(info.mArg);
AttributeValue attr = creatureStats->getAttribute(id); AttributeValue attr = creatureStats->getAttribute(id);
attr.setModifier(attr.getModifier() - info.mMagnitude); attr.setModifier(attr.getModifier() - info.mMagnitude);
attr.damage(-info.mMagnitude); attr.damage(-info.mMagnitude);
@ -260,7 +260,7 @@ namespace MWMechanics
} }
else if (info.mId == ESM::MagicEffect::DrainAttribute) else if (info.mId == ESM::MagicEffect::DrainAttribute)
{ {
auto id = static_cast<ESM::Attribute::AttributeID>(info.mArg); auto id = ESM::Attribute::indexToRefId(info.mArg);
AttributeValue attr = creatureStats->getAttribute(id); AttributeValue attr = creatureStats->getAttribute(id);
attr.setModifier(attr.getModifier() + info.mMagnitude); attr.setModifier(attr.getModifier() + info.mMagnitude);
attr.damage(info.mMagnitude); attr.damage(info.mMagnitude);

@ -166,7 +166,7 @@ namespace MWMechanics
auto& creatureStats = summoner.getClass().getCreatureStats(summoner); auto& creatureStats = summoner.getClass().getCreatureStats(summoner);
creatureStats.getActiveSpells().purge( creatureStats.getActiveSpells().purge(
[summon](const auto& spell, const auto& effect) { [summon](const auto& spell, const auto& effect) {
return effect.mEffectId == summon.first && effect.mArg == summon.second; return effect.mEffectId == summon.first && effect.getActorId() == summon.second;
}, },
summoner); summoner);

@ -109,10 +109,10 @@ namespace MWScript
template <class R> template <class R>
class OpGetAttribute : public Interpreter::Opcode0 class OpGetAttribute : public Interpreter::Opcode0
{ {
ESM::Attribute::AttributeID mIndex; ESM::RefId mIndex;
public: public:
OpGetAttribute(ESM::Attribute::AttributeID index) OpGetAttribute(ESM::RefId index)
: mIndex(index) : mIndex(index)
{ {
} }
@ -130,10 +130,10 @@ namespace MWScript
template <class R> template <class R>
class OpSetAttribute : public Interpreter::Opcode0 class OpSetAttribute : public Interpreter::Opcode0
{ {
ESM::Attribute::AttributeID mIndex; ESM::RefId mIndex;
public: public:
OpSetAttribute(ESM::Attribute::AttributeID index) OpSetAttribute(ESM::RefId index)
: mIndex(index) : mIndex(index)
{ {
} }
@ -154,10 +154,10 @@ namespace MWScript
template <class R> template <class R>
class OpModAttribute : public Interpreter::Opcode0 class OpModAttribute : public Interpreter::Opcode0
{ {
ESM::Attribute::AttributeID mIndex; ESM::RefId mIndex;
public: public:
OpModAttribute(ESM::Attribute::AttributeID index) OpModAttribute(ESM::RefId index)
: mIndex(index) : mIndex(index)
{ {
} }
@ -1322,7 +1322,7 @@ namespace MWScript
{ {
for (int i = 0; i < Compiler::Stats::numberOfAttributes; ++i) for (int i = 0; i < Compiler::Stats::numberOfAttributes; ++i)
{ {
auto id = static_cast<ESM::Attribute::AttributeID>(i); ESM::RefId id = ESM::Attribute::indexToRefId(i);
interpreter.installSegment5<OpGetAttribute<ImplicitRef>>(Compiler::Stats::opcodeGetAttribute + i, id); interpreter.installSegment5<OpGetAttribute<ImplicitRef>>(Compiler::Stats::opcodeGetAttribute + i, id);
interpreter.installSegment5<OpGetAttribute<ExplicitRef>>( interpreter.installSegment5<OpGetAttribute<ExplicitRef>>(
Compiler::Stats::opcodeGetAttributeExplicit + i, id); Compiler::Stats::opcodeGetAttributeExplicit + i, id);

@ -184,7 +184,7 @@ namespace MWWorld
} }
for (const auto& [key, actorId] : creatureStats.mSummonedCreatureMap) for (const auto& [key, actorId] : creatureStats.mSummonedCreatureMap)
{ {
if (actorId == -1) if (actorId < 0)
continue; continue;
for (auto& params : creatureStats.mActiveSpells.mSpells) for (auto& params : creatureStats.mActiveSpells.mSpells)
{ {
@ -195,7 +195,7 @@ namespace MWWorld
{ {
if (effect.mEffectId == key.mEffectId && effect.mEffectIndex == key.mEffectIndex) if (effect.mEffectId == key.mEffectId && effect.mEffectIndex == key.mEffectIndex)
{ {
effect.mArg = actorId; effect.mArg = ESM::RefId::generated(static_cast<uint64_t>(actorId));
effect.mFlags |= ESM::ActiveEffect::Flag_Applied | ESM::ActiveEffect::Flag_Remove; effect.mFlags |= ESM::ActiveEffect::Flag_Applied | ESM::ActiveEffect::Flag_Remove;
found = true; found = true;
break; break;

@ -63,7 +63,7 @@ namespace MWWorld
for (size_t i = 0; i < mSaveSkills.size(); ++i) for (size_t i = 0; i < mSaveSkills.size(); ++i)
mSaveSkills[i] = stats.getSkill(ESM::Skill::indexToRefId(i)).getModified(); mSaveSkills[i] = stats.getSkill(ESM::Skill::indexToRefId(i)).getModified();
for (size_t i = 0; i < mSaveAttributes.size(); ++i) for (size_t i = 0; i < mSaveAttributes.size(); ++i)
mSaveAttributes[i] = stats.getAttribute(static_cast<ESM::Attribute::AttributeID>(i)).getModified(); mSaveAttributes[i] = stats.getAttribute(ESM::Attribute::indexToRefId(i)).getModified();
} }
void Player::restoreStats() void Player::restoreStats()
@ -82,7 +82,7 @@ namespace MWWorld
} }
for (size_t i = 0; i < mSaveAttributes.size(); ++i) for (size_t i = 0; i < mSaveAttributes.size(); ++i)
{ {
auto id = static_cast<ESM::Attribute::AttributeID>(i); auto id = ESM::Attribute::indexToRefId(i);
auto attribute = npcStats.getAttribute(id); auto attribute = npcStats.getAttribute(id);
attribute.restore(attribute.getDamage()); attribute.restore(attribute.getDamage());
attribute.setModifier(mSaveAttributes[i] - attribute.getBase()); attribute.setModifier(mSaveAttributes[i] - attribute.getBase());

@ -1041,87 +1041,50 @@ namespace MWWorld
// Attribute // Attribute
//========================================================================= //=========================================================================
Store<ESM::Attribute>::Store()
{
mStatic.reserve(ESM::Attribute::Length);
}
const ESM::Attribute* Store<ESM::Attribute>::search(size_t index) const
{
if (index >= mStatic.size())
{
return nullptr;
}
return &mStatic[index];
}
const ESM::Attribute* Store<ESM::Attribute>::find(size_t index) const
{
const ESM::Attribute* ptr = search(index);
if (ptr == nullptr)
{
const std::string msg = "Attribute with index " + std::to_string(index) + " not found";
throw std::runtime_error(msg);
}
return ptr;
}
void Store<ESM::Attribute>::setUp(const MWWorld::Store<ESM::GameSetting>& settings) void Store<ESM::Attribute>::setUp(const MWWorld::Store<ESM::GameSetting>& settings)
{ {
// TODO remove after !3084 gets merged insertStatic({ .mId = ESM::Attribute::Strength,
mStatic.clear();
mStatic.push_back({ .mId = ESM::Attribute::Strength,
.mName = std::string{ getGMSTString(settings, "sAttributeStrength") }, .mName = std::string{ getGMSTString(settings, "sAttributeStrength") },
.mDescription = std::string{ getGMSTString(settings, "sStrDesc") }, .mDescription = std::string{ getGMSTString(settings, "sStrDesc") },
.mIcon = "icons\\k\\attribute_strength.dds", .mIcon = "icons\\k\\attribute_strength.dds",
.mWerewolfValue = getGMSTFloat(settings, "fWerewolfStrength") }); .mWerewolfValue = getGMSTFloat(settings, "fWerewolfStrength") });
mStatic.push_back({ .mId = ESM::Attribute::Intelligence, insertStatic({ .mId = ESM::Attribute::Intelligence,
.mName = std::string{ getGMSTString(settings, "sAttributeIntelligence") }, .mName = std::string{ getGMSTString(settings, "sAttributeIntelligence") },
.mDescription = std::string{ getGMSTString(settings, "sIntDesc") }, .mDescription = std::string{ getGMSTString(settings, "sIntDesc") },
.mIcon = "icons\\k\\attribute_int.dds", .mIcon = "icons\\k\\attribute_int.dds",
// Oh, Bethesda. It's "Intelligence". // Oh, Bethesda. It's "Intelligence".
.mWerewolfValue = getGMSTFloat(settings, "fWerewolfIntellegence") }); .mWerewolfValue = getGMSTFloat(settings, "fWerewolfIntellegence") });
mStatic.push_back({ .mId = ESM::Attribute::Willpower, insertStatic({ .mId = ESM::Attribute::Willpower,
.mName = std::string{ getGMSTString(settings, "sAttributeWillpower") }, .mName = std::string{ getGMSTString(settings, "sAttributeWillpower") },
.mDescription = std::string{ getGMSTString(settings, "sWilDesc") }, .mDescription = std::string{ getGMSTString(settings, "sWilDesc") },
.mIcon = "icons\\k\\attribute_wilpower.dds", .mIcon = "icons\\k\\attribute_wilpower.dds",
.mWerewolfValue = getGMSTFloat(settings, "fWerewolfWillpower") }); .mWerewolfValue = getGMSTFloat(settings, "fWerewolfWillpower") });
mStatic.push_back({ .mId = ESM::Attribute::Agility, insertStatic({ .mId = ESM::Attribute::Agility,
.mName = std::string{ getGMSTString(settings, "sAttributeAgility") }, .mName = std::string{ getGMSTString(settings, "sAttributeAgility") },
.mDescription = std::string{ getGMSTString(settings, "sAgiDesc") }, .mDescription = std::string{ getGMSTString(settings, "sAgiDesc") },
.mIcon = "icons\\k\\attribute_agility.dds", .mIcon = "icons\\k\\attribute_agility.dds",
.mWerewolfValue = getGMSTFloat(settings, "fWerewolfAgility") }); .mWerewolfValue = getGMSTFloat(settings, "fWerewolfAgility") });
mStatic.push_back({ .mId = ESM::Attribute::Speed, insertStatic({ .mId = ESM::Attribute::Speed,
.mName = std::string{ getGMSTString(settings, "sAttributeSpeed") }, .mName = std::string{ getGMSTString(settings, "sAttributeSpeed") },
.mDescription = std::string{ getGMSTString(settings, "sSpdDesc") }, .mDescription = std::string{ getGMSTString(settings, "sSpdDesc") },
.mIcon = "icons\\k\\attribute_speed.dds", .mIcon = "icons\\k\\attribute_speed.dds",
.mWerewolfValue = getGMSTFloat(settings, "fWerewolfSpeed") }); .mWerewolfValue = getGMSTFloat(settings, "fWerewolfSpeed") });
mStatic.push_back({ .mId = ESM::Attribute::Endurance, insertStatic({ .mId = ESM::Attribute::Endurance,
.mName = std::string{ getGMSTString(settings, "sAttributeEndurance") }, .mName = std::string{ getGMSTString(settings, "sAttributeEndurance") },
.mDescription = std::string{ getGMSTString(settings, "sEndDesc") }, .mDescription = std::string{ getGMSTString(settings, "sEndDesc") },
.mIcon = "icons\\k\\attribute_endurance.dds", .mIcon = "icons\\k\\attribute_endurance.dds",
.mWerewolfValue = getGMSTFloat(settings, "fWerewolfEndurance") }); .mWerewolfValue = getGMSTFloat(settings, "fWerewolfEndurance") });
mStatic.push_back({ .mId = ESM::Attribute::Personality, insertStatic({ .mId = ESM::Attribute::Personality,
.mName = std::string{ getGMSTString(settings, "sAttributePersonality") }, .mName = std::string{ getGMSTString(settings, "sAttributePersonality") },
.mDescription = std::string{ getGMSTString(settings, "sPerDesc") }, .mDescription = std::string{ getGMSTString(settings, "sPerDesc") },
.mIcon = "icons\\k\\attribute_personality.dds", .mIcon = "icons\\k\\attribute_personality.dds",
.mWerewolfValue = getGMSTFloat(settings, "fWerewolfPersonality") }); .mWerewolfValue = getGMSTFloat(settings, "fWerewolfPersonality") });
mStatic.push_back({ .mId = ESM::Attribute::Luck, insertStatic({ .mId = ESM::Attribute::Luck,
.mName = std::string{ getGMSTString(settings, "sAttributeLuck") }, .mName = std::string{ getGMSTString(settings, "sAttributeLuck") },
.mDescription = std::string{ getGMSTString(settings, "sLucDesc") }, .mDescription = std::string{ getGMSTString(settings, "sLucDesc") },
.mIcon = "icons\\k\\attribute_luck.dds", .mIcon = "icons\\k\\attribute_luck.dds",
.mWerewolfValue = getGMSTFloat(settings, "fWerewolfLuck") }); .mWerewolfValue = getGMSTFloat(settings, "fWerewolfLuck") });
} }
size_t Store<ESM::Attribute>::getSize() const
{
return mStatic.size();
}
Store<ESM::Attribute>::iterator Store<ESM::Attribute>::begin() const
{
return mStatic.begin();
}
Store<ESM::Attribute>::iterator Store<ESM::Attribute>::end() const
{
return mStatic.end();
}
// Dialogue // Dialogue
//========================================================================= //=========================================================================
@ -1339,7 +1302,7 @@ namespace MWWorld
template class MWWorld::TypedDynamicStore<ESM::Activator>; template class MWWorld::TypedDynamicStore<ESM::Activator>;
template class MWWorld::TypedDynamicStore<ESM::Apparatus>; template class MWWorld::TypedDynamicStore<ESM::Apparatus>;
template class MWWorld::TypedDynamicStore<ESM::Armor>; template class MWWorld::TypedDynamicStore<ESM::Armor>;
// template class MWWorld::Store<ESM::Attribute>; template class MWWorld::TypedDynamicStore<ESM::Attribute>;
template class MWWorld::TypedDynamicStore<ESM::BirthSign>; template class MWWorld::TypedDynamicStore<ESM::BirthSign>;
template class MWWorld::TypedDynamicStore<ESM::BodyPart>; template class MWWorld::TypedDynamicStore<ESM::BodyPart>;
template class MWWorld::TypedDynamicStore<ESM::Book>; template class MWWorld::TypedDynamicStore<ESM::Book>;

@ -9,6 +9,7 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <components/esm/attr.hpp>
#include <components/esm/refid.hpp> #include <components/esm/refid.hpp>
#include <components/esm/util.hpp> #include <components/esm/util.hpp>
#include <components/esm3/loadcell.hpp> #include <components/esm3/loadcell.hpp>
@ -29,7 +30,6 @@
namespace ESM namespace ESM
{ {
struct Attribute;
struct LandTexture; struct LandTexture;
struct MagicEffect; struct MagicEffect;
struct WeaponType; struct WeaponType;
@ -492,25 +492,14 @@ namespace MWWorld
}; };
template <> template <>
class Store<ESM::Attribute> : public IndexedStore<ESM::Attribute> class Store<ESM::Attribute> : public TypedDynamicStore<ESM::Attribute>
{ {
std::vector<ESM::Attribute> mStatic; using TypedDynamicStore<ESM::Attribute>::setUp;
public: public:
typedef std::vector<ESM::Attribute>::const_iterator iterator; Store() = default;
Store();
const ESM::Attribute* search(size_t index) const;
// calls `search` and throws an exception if not found
const ESM::Attribute* find(size_t index) const;
void setUp(const MWWorld::Store<ESM::GameSetting>& settings); void setUp(const MWWorld::Store<ESM::GameSetting>& settings);
size_t getSize() const;
iterator begin() const;
iterator end() const;
}; };
template <> template <>

@ -456,6 +456,8 @@ namespace
{ {
refId = ESM::RefId::esm3ExteriorCell(0, 0); refId = ESM::RefId::esm3ExteriorCell(0, 0);
} }
else if constexpr (std::is_same_v<RecordType, ESM::Attribute>)
refId = ESM::Attribute::Strength;
else if constexpr (std::is_same_v<RecordType, ESM::Skill>) else if constexpr (std::is_same_v<RecordType, ESM::Skill>)
refId = ESM::Skill::Block; refId = ESM::Skill::Block;
else else
@ -485,6 +487,12 @@ namespace
ESM::Dialogue* dialogue = nullptr; ESM::Dialogue* dialogue = nullptr;
MWWorld::ESMStore esmStore; MWWorld::ESMStore esmStore;
if constexpr (std::is_same_v<RecordType, ESM::Attribute>)
{
ASSERT_ANY_THROW(getEsmFile(record, false, formatVersion));
continue;
}
reader.open(getEsmFile(record, false, formatVersion), "filename"); reader.open(getEsmFile(record, false, formatVersion), "filename");
ASSERT_NO_THROW(esmStore.load(reader, &dummyListener, dialogue)); ASSERT_NO_THROW(esmStore.load(reader, &dummyListener, dialogue));
esmStore.setUp(); esmStore.setUp();
@ -575,7 +583,7 @@ namespace
REGISTER_TYPED_TEST_SUITE_P(StoreSaveLoadTest, shouldNotChangeRefId); REGISTER_TYPED_TEST_SUITE_P(StoreSaveLoadTest, shouldNotChangeRefId);
static_assert(std::tuple_size_v<RecordTypesWithSave> == 39); static_assert(std::tuple_size_v<RecordTypesWithSave> == 40);
INSTANTIATE_TYPED_TEST_SUITE_P( INSTANTIATE_TYPED_TEST_SUITE_P(
RecordTypesTest, StoreSaveLoadTest, typename AsTestingTypes<RecordTypesWithSave>::Type); RecordTypesTest, StoreSaveLoadTest, typename AsTestingTypes<RecordTypesWithSave>::Type);

@ -1,25 +1,56 @@
#include "attr.hpp" #include "attr.hpp"
#include <components/misc/strings/algorithm.hpp>
#include <stdexcept>
using namespace ESM; #include <components/esm3/esmreader.hpp>
#include <components/esm3/esmwriter.hpp>
const std::string Attribute::sAttributeNames[Attribute::Length] = { #include <stdexcept>
"Strength",
"Intelligence",
"Willpower",
"Agility",
"Speed",
"Endurance",
"Personality",
"Luck",
};
Attribute::AttributeID Attribute::stringToAttributeId(std::string_view attribute) namespace ESM
{ {
for (int id = 0; id < Attribute::Length; ++id) const Attribute::AttributeID Attribute::Strength("Strength");
if (Misc::StringUtils::ciEqual(sAttributeNames[id], attribute)) const Attribute::AttributeID Attribute::Intelligence("Intelligence");
return Attribute::AttributeID(id); const Attribute::AttributeID Attribute::Willpower("Willpower");
const Attribute::AttributeID Attribute::Agility("Agility");
const Attribute::AttributeID Attribute::Speed("Speed");
const Attribute::AttributeID Attribute::Endurance("Endurance");
const Attribute::AttributeID Attribute::Personality("Personality");
const Attribute::AttributeID Attribute::Luck("Luck");
static const RefId sAttributes[Attribute::Length] = {
Attribute::Strength,
Attribute::Intelligence,
Attribute::Willpower,
Attribute::Agility,
Attribute::Speed,
Attribute::Endurance,
Attribute::Personality,
Attribute::Luck,
};
RefId Attribute::indexToRefId(int index)
{
if (index < 0 || index >= Length)
return RefId();
return sAttributes[index];
}
int Attribute::refIdToIndex(RefId id)
{
for (int i = 0; i < Length; ++i)
{
if (sAttributes[i] == id)
return i;
}
return -1;
}
void Attribute::load(ESMReader& esm, bool& isDeleted)
{
throw std::runtime_error("Attribute loading not yet implemented");
}
throw std::logic_error("No such attribute: " + std::string(attribute)); void Attribute::save(ESMWriter& esm, bool isDeleted) const
{
throw std::runtime_error("Attribute saving not yet implemented");
}
} }

@ -2,9 +2,15 @@
#define OPENMW_ESM_ATTR_H #define OPENMW_ESM_ATTR_H
#include <string> #include <string>
#include <string_view>
#include "defs.hpp"
#include "refid.hpp"
namespace ESM namespace ESM
{ {
class ESMReader;
class ESMWriter;
/* /*
* Attribute definitions * Attribute definitions
@ -12,26 +18,30 @@ namespace ESM
struct Attribute struct Attribute
{ {
enum AttributeID constexpr static RecNameInts sRecordId = REC_ATTR;
{ /// Return a string descriptor for this record type. Currently used for debugging / error logs only.
Strength = 0, static std::string_view getRecordType() { return "Attribute"; }
Intelligence = 1,
Willpower = 2, using AttributeID = StringRefId;
Agility = 3, static const AttributeID Strength;
Speed = 4, static const AttributeID Intelligence;
Endurance = 5, static const AttributeID Willpower;
Personality = 6, static const AttributeID Agility;
Luck = 7, static const AttributeID Speed;
Length = 8 static const AttributeID Endurance;
}; static const AttributeID Personality;
static const AttributeID Luck;
static constexpr int Length = 8;
AttributeID mId; AttributeID mId;
std::string mName, mDescription, mIcon; std::string mName, mDescription, mIcon;
float mWerewolfValue{}; float mWerewolfValue{};
static const std::string sAttributeNames[Length]; void load(ESMReader& esm, bool& isDeleted);
void save(ESMWriter& esm, bool isDeleted = false) const;
static AttributeID stringToAttributeId(std::string_view attribute); static RefId indexToRefId(int index);
static int refIdToIndex(RefId id);
}; };
} }
#endif #endif

@ -180,6 +180,8 @@ namespace ESM
// format 21 - Random state in saved games. // format 21 - Random state in saved games.
REC_RAND = esm3Recname("RAND"), // Random state. REC_RAND = esm3Recname("RAND"), // Random state.
REC_ATTR = esm3Recname("ATTR"), // Attribute
REC_AACT4 = esm4Recname(ESM4::REC_AACT), // Action REC_AACT4 = esm4Recname(ESM4::REC_AACT), // Action
REC_ACHR4 = esm4Recname(ESM4::REC_ACHR), // Actor Reference REC_ACHR4 = esm4Recname(ESM4::REC_ACHR), // Actor Reference
REC_ACTI4 = esm4Recname(ESM4::REC_ACTI), // Activator REC_ACTI4 = esm4Recname(ESM4::REC_ACTI), // Activator

@ -2,11 +2,93 @@
#include "esmreader.hpp" #include "esmreader.hpp"
#include "esmwriter.hpp" #include "esmwriter.hpp"
#include "loadmgef.hpp"
#include "loadskil.hpp"
#include <components/esm/attr.hpp>
#include <cstdint>
namespace ESM namespace ESM
{ {
namespace namespace
{ {
bool isSummon(int effectId)
{
switch (effectId)
{
case MagicEffect::SummonScamp:
case MagicEffect::SummonClannfear:
case MagicEffect::SummonDaedroth:
case MagicEffect::SummonDremora:
case MagicEffect::SummonAncestralGhost:
case MagicEffect::SummonSkeletalMinion:
case MagicEffect::SummonBonewalker:
case MagicEffect::SummonGreaterBonewalker:
case MagicEffect::SummonBonelord:
case MagicEffect::SummonWingedTwilight:
case MagicEffect::SummonHunger:
case MagicEffect::SummonGoldenSaint:
case MagicEffect::SummonFlameAtronach:
case MagicEffect::SummonFrostAtronach:
case MagicEffect::SummonStormAtronach:
case MagicEffect::SummonCenturionSphere:
case MagicEffect::SummonFabricant:
case MagicEffect::SummonWolf:
case MagicEffect::SummonBear:
case MagicEffect::SummonBonewolf:
case MagicEffect::SummonCreature04:
case MagicEffect::SummonCreature05:
return true;
}
return false;
}
bool affectsAttribute(int effectId)
{
switch (effectId)
{
case MagicEffect::DrainAttribute:
case MagicEffect::DamageAttribute:
case MagicEffect::RestoreAttribute:
case MagicEffect::FortifyAttribute:
case MagicEffect::AbsorbAttribute:
return true;
}
return false;
}
bool affectsSkill(int effectId)
{
switch (effectId)
{
case MagicEffect::DrainSkill:
case MagicEffect::DamageSkill:
case MagicEffect::RestoreSkill:
case MagicEffect::FortifySkill:
case MagicEffect::AbsorbSkill:
return true;
}
return false;
}
struct ToInt
{
int effectId;
int operator()(const ESM::RefId& id) const
{
if (!id.empty())
{
if (affectsAttribute(effectId))
return ESM::Attribute::refIdToIndex(id);
else if (affectsSkill(effectId))
return ESM::Skill::refIdToIndex(id);
}
return -1;
}
int operator()(int actor) const { return actor; }
};
void saveImpl(ESMWriter& esm, const std::vector<ActiveSpells::ActiveSpellParams>& spells, NAME tag) void saveImpl(ESMWriter& esm, const std::vector<ActiveSpells::ActiveSpellParams>& spells, NAME tag)
{ {
for (const auto& params : spells) for (const auto& params : spells)
@ -27,8 +109,9 @@ namespace ESM
for (auto& effect : params.mEffects) for (auto& effect : params.mEffects)
{ {
esm.writeHNT("MGEF", effect.mEffectId); esm.writeHNT("MGEF", effect.mEffectId);
if (effect.mArg != -1) int arg = std::visit(ToInt{ effect.mEffectId }, effect.mArg);
esm.writeHNT("ARG_", effect.mArg); if (arg != -1)
esm.writeHNT("ARG_", arg);
esm.writeHNT("MAGN", effect.mMagnitude); esm.writeHNT("MAGN", effect.mMagnitude);
esm.writeHNT("MAGN", effect.mMinMagnitude); esm.writeHNT("MAGN", effect.mMinMagnitude);
esm.writeHNT("MAGN", effect.mMaxMagnitude); esm.writeHNT("MAGN", effect.mMaxMagnitude);
@ -81,8 +164,17 @@ namespace ESM
{ {
ActiveEffect effect; ActiveEffect effect;
esm.getHT(effect.mEffectId); esm.getHT(effect.mEffectId);
effect.mArg = -1; int32_t arg = -1;
esm.getHNOT(effect.mArg, "ARG_"); esm.getHNOT(arg, "ARG_");
if (arg >= 0)
{
if (isSummon(effect.mEffectId))
effect.mArg = arg;
else if (affectsAttribute(effect.mEffectId))
effect.mArg = ESM::Attribute::indexToRefId(arg);
else if (affectsSkill(effect.mEffectId))
effect.mArg = ESM::Skill::indexToRefId(arg);
}
esm.getHNT(effect.mMagnitude, "MAGN"); esm.getHNT(effect.mMagnitude, "MAGN");
if (format <= MaxClearModifiersFormatVersion) if (format <= MaxClearModifiersFormatVersion)
{ {
@ -112,10 +204,7 @@ namespace ESM
} }
} }
} }
}
namespace ESM
{
void ActiveSpells::save(ESMWriter& esm) const void ActiveSpells::save(ESMWriter& esm) const
{ {
saveImpl(esm, mSpells, "ID__"); saveImpl(esm, mSpells, "ID__");
@ -127,4 +216,18 @@ namespace ESM
loadImpl(esm, mSpells, "ID__"); loadImpl(esm, mSpells, "ID__");
loadImpl(esm, mQueue, "QID_"); loadImpl(esm, mQueue, "QID_");
} }
RefId ActiveEffect::getSkillOrAttribute() const
{
if (const auto* id = std::get_if<ESM::RefId>(&mArg))
return *id;
return {};
}
int ActiveEffect::getActorId() const
{
if (const auto* id = std::get_if<int>(&mArg))
return *id;
return -1;
}
} }

@ -7,6 +7,7 @@
#include "timestamp.hpp" #include "timestamp.hpp"
#include <string> #include <string>
#include <variant>
#include <vector> #include <vector>
namespace ESM namespace ESM
@ -32,11 +33,14 @@ namespace ESM
float mMagnitude; float mMagnitude;
float mMinMagnitude; float mMinMagnitude;
float mMaxMagnitude; float mMaxMagnitude;
int32_t mArg; // skill or attribute std::variant<RefId, int> mArg; // skill, attribute, or summon
float mDuration; float mDuration;
float mTimeLeft; float mTimeLeft;
int32_t mEffectIndex; int32_t mEffectIndex;
int32_t mFlags; int32_t mFlags;
RefId getSkillOrAttribute() const;
int getActorId() const;
}; };
// format 0, saved games only // format 0, saved games only

Loading…
Cancel
Save