Assign StringRefIds to attributes

macos_ci_fix
Evil Eye 1 year ago
parent 11ae1a1fcb
commit e660a9ca16

@ -1171,9 +1171,6 @@ namespace EsmTool
template <>
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 << " Description: " << mData.mDescription << std::endl;
std::cout << " Flags: " << raceFlags(mData.mData.mFlags) << std::endl;
@ -1185,8 +1182,8 @@ namespace EsmTool
std::cout << (male ? " Male:" : " Female:") << std::endl;
for (int j = 0; j < ESM::Attribute::Length; ++j)
std::cout << " " << sAttributeNames[j] << ": " << mData.mData.mAttributeValues[j].getValue(male)
<< std::endl;
std::cout << " " << ESM::Attribute::indexToRefId(j) << ": "
<< mData.mData.mAttributeValues[j].getValue(male) << std::endl;
std::cout << " Height: " << mData.mData.mHeight.getValue(male) << std::endl;
std::cout << " Weight: " << mData.mData.mWeight.getValue(male) << std::endl;

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

@ -93,7 +93,8 @@ namespace
int level = creatureStats.getLevel();
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);
}
@ -104,7 +105,7 @@ namespace
{
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);
}
}
@ -113,10 +114,11 @@ namespace
for (const ESM::Attribute& attribute : attributes)
{
float modifierSum = 0;
int attributeIndex = ESM::Attribute::refIdToIndex(attribute.mId);
for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get<ESM::Skill>())
{
if (skill.mData.mAttribute != attribute.mId)
if (skill.mData.mAttribute != attributeIndex)
continue;
// is this a minor or major skill?
@ -148,7 +150,8 @@ namespace
else if (class_->mData.mSpecialization == ESM::Class::Stealth)
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())
multiplier += 1;

@ -383,7 +383,7 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons
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();
}
case SelectWrapper::Function_PcSkill:
@ -654,9 +654,9 @@ bool MWDialogue::Filter::hasFactionRankSkillRequirements(
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
&& 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;
}

@ -103,7 +103,7 @@ namespace MWGui
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;
if (mReviewDialog)
@ -263,8 +263,7 @@ namespace MWGui
mReviewDialog->setFatigue(stats.getFatigue());
for (auto& attributePair : mPlayerAttributes)
{
mReviewDialog->setAttribute(
static_cast<ESM::Attribute::AttributeID>(attributePair.first), attributePair.second);
mReviewDialog->setAttribute(attributePair.first, attributePair.second);
}
for (const auto& [skill, value] : mPlayerSkillValues)
{
@ -462,9 +461,10 @@ namespace MWGui
klass.mData.mIsPlayable = 0x1;
klass.mRecordFlags = 0;
std::vector<int> attributes = mCreateClassDialog->getFavoriteAttributes();
assert(attributes.size() == klass.mData.mAttribute.size());
std::copy(attributes.begin(), attributes.end(), klass.mData.mAttribute.begin());
std::vector<ESM::RefId> attributes = mCreateClassDialog->getFavoriteAttributes();
assert(attributes.size() >= klass.mData.mAttribute.size());
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> minorSkills = mCreateClassDialog->getMinorSkills();

@ -44,7 +44,7 @@ namespace MWGui
// Show a dialog
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(ESM::RefId id, const MWMechanics::SkillValue& value) 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;
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;
// Dialogs

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

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

@ -200,7 +200,7 @@ namespace MWGui
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);
if (attr == mAttributeWidgets.end())
@ -412,7 +412,7 @@ namespace MWGui
if (!mRaceId.empty())
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)
attributes[key] = value->getAttributeValue();

@ -3,7 +3,6 @@
#include "widgets.hpp"
#include "windowbase.hpp"
#include <components/esm/attr.hpp>
#include <components/esm/refid.hpp>
#include <components/esm3/loadclas.hpp>
@ -38,7 +37,7 @@ namespace MWGui
void setMagicka(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 setSkillValue(ESM::RefId id, const MWMechanics::SkillValue& value);
@ -90,7 +89,7 @@ namespace MWGui
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::map<ESM::RefId, MWMechanics::SkillValue> mSkillValues;

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

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

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

@ -53,7 +53,8 @@ namespace MWGui
if (effectId != -1)
{
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));
std::string fullEffectName = MWMechanics::getMagicEffectString(*magicEffect, attribute, skill);

@ -43,7 +43,7 @@ namespace MWGui
if (value != mWatchedAttributes[attribute.mId] || mWatchedStatsEmpty)
{
mWatchedAttributes[attribute.mId] = value;
setValue(attribute.mId, value);
setAttribute(attribute.mId, value);
}
}
@ -150,10 +150,10 @@ namespace MWGui
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)
listener->setValue(id, value);
listener->setAttribute(id, value);
}
void StatsWatcher::setValue(ESM::RefId id, const MWMechanics::SkillValue& value)

@ -19,7 +19,7 @@ namespace MWGui
virtual ~StatsListener() = default;
/// 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, const std::string& value) {}
virtual void setValue(std::string_view, int value) {}
@ -31,7 +31,7 @@ namespace MWGui
{
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;
MWMechanics::DynamicStat<float> mWatchedHealth;
@ -50,7 +50,7 @@ namespace MWGui
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 std::string& value);
void setValue(std::string_view id, int value);

@ -157,7 +157,7 @@ namespace MWGui
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);
if (it != mAttributeWidgets.end())
@ -505,7 +505,8 @@ namespace MWGui
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
= addValueItem(skill->mName, {}, "normal", coord1, coord2);
@ -620,8 +621,10 @@ namespace MWGui
text += std::string("\n\n#{fontcolourhtml=header}#{sNextRank} ") + faction->mRanks[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* attr2 = store.get<ESM::Attribute>().find(faction->mData.mAttribute[1]);
const ESM::Attribute* attr1 = store.get<ESM::Attribute>().find(
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) + ": "
+ MyGUI::utility::toString(rankData.mAttribute1) + ", "

@ -22,7 +22,7 @@ namespace MWGui
void setPlayerName(const std::string& playerName);
/// 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 std::string& 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::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<std::string, MyGUI::Widget*> mFactionWidgetMap;
FactionList mFactions; ///< Stores a list of factions and the current rank

@ -225,8 +225,8 @@ namespace MWGui
{
Widgets::SpellEffectParams params;
params.mEffectID = spellEffect.mEffectID;
params.mSkill = spellEffect.mSkill;
params.mAttribute = spellEffect.mAttribute;
params.mSkill = ESM::Skill::indexToRefId(spellEffect.mSkill);
params.mAttribute = ESM::Attribute::indexToRefId(spellEffect.mAttribute);
params.mDuration = spellEffect.mDuration;
params.mMagnMin = spellEffect.mMagnMin;
params.mMagnMax = spellEffect.mMagnMax;
@ -804,7 +804,8 @@ namespace MWGui
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
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("ToolTipLayout", "SkillNoProgressToolTip");
@ -815,7 +816,7 @@ namespace MWGui
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
= 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
// system knows what to show in case this widget is hovered
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 createBirthsignToolTip(MyGUI::Widget* widget, const ESM::RefId& birthsignId);
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
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}");
return;

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

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

@ -179,11 +179,18 @@ namespace MWLua
skills[key] = id;
}
sol::table attribute(context.mLua->sol(), sol::create);
api["ATTRIBUTE"] = LuaUtil::makeStrictReadOnly(attribute);
for (int id = 0; id < ESM::Attribute::Length; ++id)
attribute[ESM::Attribute::sAttributeNames[id]]
= Misc::StringUtils::lowerCase(ESM::Attribute::sAttributeNames[id]);
// TODO: deprecate this and provide access to the store instead
sol::table attributes(context.mLua->sol(), sol::create);
api["ATTRIBUTE"] = LuaUtil::makeStrictReadOnly(attributes);
for (int i = 0; i < ESM::Attribute::Length; ++i)
{
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);
}

@ -358,10 +358,10 @@ namespace MWLua
});
effectParamsT["affectedAttribute"]
= sol::readonly_property([](const ESM::ENAMstruct& params) -> sol::optional<std::string> {
if (params.mAttribute >= 0 && params.mAttribute < ESM::Attribute::Length)
return Misc::StringUtils::lowerCase(ESM::Attribute::sAttributeNames[params.mAttribute]);
else
return sol::nullopt;
ESM::RefId id = ESM::Attribute::indexToRefId(params.mAttribute);
if (!id.empty())
return id.serializeText();
return sol::nullopt;
});
effectParamsT["range"]
= sol::readonly_property([](const ESM::ENAMstruct& params) -> int { return params.mRange; });
@ -419,23 +419,21 @@ namespace MWLua
return Misc::StringUtils::lowerCase(name);
});
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"]
= sol::readonly_property([magicEffectStore](const ESM::ActiveEffect& effect) -> sol::optional<std::string> {
auto* rec = magicEffectStore->find(effect.mEffectId);
if ((rec->mData.mFlags & ESM::MagicEffect::TargetSkill) && effect.mArg >= 0
&& effect.mArg < ESM::Skill::Length)
return ESM::Skill::indexToRefId(effect.mArg).serializeText();
if (rec->mData.mFlags & ESM::MagicEffect::TargetSkill)
return effect.getSkillOrAttribute().serializeText();
else
return sol::nullopt;
});
activeSpellEffectT["affectedAttribute"]
= sol::readonly_property([magicEffectStore](const ESM::ActiveEffect& effect) -> sol::optional<std::string> {
auto* rec = magicEffectStore->find(effect.mEffectId);
if ((rec->mData.mFlags & ESM::MagicEffect::TargetAttribute) && effect.mArg >= 0
&& effect.mArg < ESM::Attribute::Length)
return Misc::StringUtils::lowerCase(ESM::Attribute::sAttributeNames[effect.mArg]);
if (rec->mData.mFlags & ESM::MagicEffect::TargetAttribute)
return effect.getSkillOrAttribute().serializeText();
else
return sol::nullopt;
});
@ -542,20 +540,15 @@ namespace MWLua
= sol::readonly_property([magicEffectStore](const ActiveEffect& effect) -> sol::optional<std::string> {
auto* rec = magicEffectStore->find(effect.key.mId);
if (rec->mData.mFlags & ESM::MagicEffect::TargetSkill)
{
ESM::RefId id = ESM::Skill::indexToRefId(effect.key.mArg);
return id.serializeText();
}
return effect.key.mArg.serializeText();
return sol::nullopt;
});
activeEffectT["affectedAttribute"]
= sol::readonly_property([magicEffectStore](const ActiveEffect& effect) -> sol::optional<std::string> {
auto* rec = magicEffectStore->find(effect.key.mId);
if ((rec->mData.mFlags & ESM::MagicEffect::TargetAttribute) && effect.key.mArg >= 0
&& effect.key.mArg < ESM::Attribute::Length)
return Misc::StringUtils::lowerCase(ESM::Attribute::sAttributeNames[effect.key.mArg]);
else
return sol::nullopt;
if (rec->mData.mFlags & ESM::MagicEffect::TargetAttribute)
return effect.key.mArg.serializeText();
return sol::nullopt;
});
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
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)
{
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
{
ObjectVariant mObject;
int mIndex;
ESM::RefId mId;
AttributeStat(ObjectVariant object, int index)
AttributeStat(ObjectVariant object, ESM::RefId id)
: mObject(std::move(object))
, mIndex(index)
, mId(id)
{
}
@ -183,10 +183,9 @@ namespace MWLua
template <class G>
sol::object get(const Context& context, std::string_view prop, G getter) const
{
auto id = static_cast<ESM::Attribute::AttributeID>(mIndex);
return getValue(
context, mObject, &AttributeStat::setValue, mIndex, prop, [id, getter](const MWWorld::Ptr& ptr) {
return (ptr.getClass().getCreatureStats(ptr).getAttribute(id).*getter)();
context, mObject, &AttributeStat::setValue, mId, prop, [this, getter](const MWWorld::Ptr& ptr) {
return (ptr.getClass().getCreatureStats(ptr).getAttribute(mId).*getter)();
});
}
@ -202,20 +201,20 @@ namespace MWLua
{
if (!object.ptr().getClass().isActor())
return {};
int index = std::get<int>(i);
return AttributeStat{ std::move(object), index };
ESM::RefId id = std::get<ESM::RefId>(i);
return AttributeStat{ std::move(object), id };
}
void cache(const Context& context, std::string_view prop, const sol::object& value) const
{
SelfObject* obj = mObject.asSelfObject();
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)
{
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 stat = stats.getAttribute(id);
float floatValue = LuaUtil::cast<float>(value);
@ -370,9 +369,8 @@ namespace MWLua
addProp(context, attributeStatT, "modifier", &MWMechanics::AttributeValue::getModifier);
sol::table attributes(context.mLua->sol(), sol::create);
stats["attributes"] = LuaUtil::makeReadOnly(attributes);
for (int id = ESM::Attribute::Strength; id < ESM::Attribute::Length; ++id)
attributes[Misc::StringUtils::lowerCase(ESM::Attribute::sAttributeNames[id])]
= addIndexedAccessor<AttributeStat>(id);
for (const ESM::Attribute& attribute : MWBase::Environment::get().getESMStore()->get<ESM::Attribute>())
attributes[ESM::RefId(attribute.mId).serializeText()] = addIndexedAccessor<AttributeStat>(attribute.mId);
}
void addNpcStatsBindings(sol::table& npc, const Context& context)

@ -548,14 +548,13 @@ namespace MWMechanics
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(
[=](const ActiveSpellParams&, const ESM::ActiveEffect& effect) {
if (effectArg < 0)
if (effectArg.empty())
return effect.mEffectId == effectId;
else
return effect.mEffectId == effectId && effect.mArg == effectArg;
return effect.mEffectId == effectId && effect.getSkillOrAttribute() == effectArg;
},
ptr);
}

@ -134,7 +134,7 @@ namespace MWMechanics
void removeEffects(const MWWorld::Ptr& ptr, const ESM::RefId& 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(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)
if (ingredient->mBase->mData.mEffectID[i] != -1)
{
EffectKey key(ingredient->mBase->mData.mEffectID[i],
ingredient->mBase->mData.mSkills[i] != -1 ? ingredient->mBase->mData.mSkills[i]
: ingredient->mBase->mData.mAttributes[i]);
ESM::RefId arg = ESM::Skill::indexToRefId(ingredient->mBase->mData.mSkills[i]);
if (arg.empty())
arg = ESM::Attribute::indexToRefId(ingredient->mBase->mData.mAttributes[i]);
EffectKey key(ingredient->mBase->mData.mEffectID[i], arg);
if (seenEffects.insert(key).second)
++effects[key];
@ -203,9 +204,9 @@ void MWMechanics::Alchemy::updateEffects()
effect.mSkill = -1;
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)
effect.mAttribute = iter->mArg;
effect.mAttribute = ESM::Attribute::refIdToIndex(iter->mArg);
effect.mRange = 0;
effect.mArea = 0;
@ -576,7 +577,8 @@ std::vector<std::string> MWMechanics::Alchemy::effectsDescription(const MWWorld:
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]));
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,
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
= 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,
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();
@ -215,7 +215,7 @@ namespace MWMechanics
}
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)
{
@ -237,7 +237,8 @@ namespace MWMechanics
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)
return false;
}
@ -299,7 +300,7 @@ namespace MWMechanics
}
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)
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
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,
const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, const ESM::Race* race);
const std::map<ESM::RefId, AttributeValue>& actorAttributes, const ESM::Race* race);
// Helpers
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,
ESM::RefId& effectiveSchool, float& skillTerm);
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);
}
const AttributeValue& CreatureStats::getAttribute(ESM::Attribute::AttributeID id) const
const AttributeValue& CreatureStats::getAttribute(ESM::RefId id) const
{
return mAttributes.at(id);
}
@ -147,14 +147,14 @@ namespace MWMechanics
return mMagicEffects;
}
void CreatureStats::setAttribute(ESM::Attribute::AttributeID id, float base)
void CreatureStats::setAttribute(ESM::RefId id, float base)
{
AttributeValue current = getAttribute(id);
current.setBase(base);
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);
@ -531,7 +531,7 @@ namespace MWMechanics
void CreatureStats::writeState(ESM::CreatureStats& state) const
{
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)
mDynamic[i].writeState(state.mDynamic[i]);
@ -588,7 +588,7 @@ namespace MWMechanics
if (!state.mMissingACDT)
{
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)
mDynamic[i].readState(state.mDynamic[i]);

@ -40,7 +40,7 @@ namespace MWMechanics
{
static int sActorId;
DrawState mDrawState;
std::map<ESM::Attribute::AttributeID, AttributeValue> mAttributes;
std::map<ESM::RefId, AttributeValue> mAttributes;
DynamicStat<float> mDynamic[3]; // health, magicka, fatigue
Spells mSpells;
ActiveSpells mActiveSpells;
@ -113,7 +113,7 @@ namespace MWMechanics
/// @return total fall height
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;
@ -139,9 +139,9 @@ namespace MWMechanics
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
void setAttribute(ESM::Attribute::AttributeID id, float base);
void setAttribute(ESM::RefId id, float base);
void setHealth(const DynamicStat<float>& value);
@ -294,7 +294,7 @@ namespace MWMechanics
bool wasTeleported() const { return mTeleported; }
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()
: mId(0)
, mArg(-1)
{
}
EffectKey::EffectKey(const ESM::ENAMstruct& effect)
{
mId = effect.mEffectID;
mArg = -1;
mArg = ESM::Skill::indexToRefId(effect.mSkill);
if (effect.mSkill != -1)
mArg = effect.mSkill;
if (effect.mAttribute != -1)
ESM::RefId attribute = ESM::Attribute::indexToRefId(effect.mAttribute);
if (!attribute.empty())
{
if (mArg != -1)
if (!mArg.empty())
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 ESM::MagicEffect* magicEffect = store->get<ESM::MagicEffect>().search(mId);
return getMagicEffectString(*magicEffect, store->get<ESM::Attribute>().search(mArg),
store->get<ESM::Skill>().search(ESM::Skill::indexToRefId(mArg)));
return getMagicEffectString(
*magicEffect, store->get<ESM::Attribute>().search(mArg), store->get<ESM::Skill>().search(mArg));
}
bool operator<(const EffectKey& left, const EffectKey& right)

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

@ -150,7 +150,8 @@ namespace MWMechanics
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);
}
@ -193,11 +194,9 @@ namespace MWMechanics
for (int attribute : class_->mData.mAttribute)
{
if (attribute >= 0 && attribute < ESM::Attribute::Length)
{
auto id = static_cast<ESM::Attribute::AttributeID>(attribute);
ESM::RefId id = ESM::Attribute::indexToRefId(attribute);
if (!id.empty())
creatureStats.setAttribute(id, creatureStats.getAttribute(id).getBase() + 10);
}
}
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();
@ -488,7 +488,7 @@ void MWMechanics::NpcStats::writeState(ESM::NpcStats& state) const
state.mSkillIncrease.fill(0);
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)
state.mSpecIncreases[i] = mSpecIncreases[i];
@ -538,7 +538,7 @@ void MWMechanics::NpcStats::readState(const ESM::NpcStats& state)
mLevelProgress = state.mLevelProgress;
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)
mSpecIncreases[i] = state.mSpecIncreases[i];

@ -36,7 +36,7 @@ namespace MWMechanics
std::set<ESM::RefId> mExpelled;
std::map<ESM::RefId, int> mFactionReputation;
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)
std::vector<int> mSpecIncreases; // number of skill increases for each specialization (accumulates throughout
// the entire game)

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

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

@ -252,7 +252,7 @@ namespace MWMechanics
// Applied corprus effects are already in loaded stats modifiers
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);
attr.setModifier(attr.getModifier() - info.mMagnitude);
attr.damage(-info.mMagnitude);
@ -260,7 +260,7 @@ namespace MWMechanics
}
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);
attr.setModifier(attr.getModifier() + info.mMagnitude);
attr.damage(info.mMagnitude);

@ -166,7 +166,7 @@ namespace MWMechanics
auto& creatureStats = summoner.getClass().getCreatureStats(summoner);
creatureStats.getActiveSpells().purge(
[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);

@ -109,10 +109,10 @@ namespace MWScript
template <class R>
class OpGetAttribute : public Interpreter::Opcode0
{
ESM::Attribute::AttributeID mIndex;
ESM::RefId mIndex;
public:
OpGetAttribute(ESM::Attribute::AttributeID index)
OpGetAttribute(ESM::RefId index)
: mIndex(index)
{
}
@ -130,10 +130,10 @@ namespace MWScript
template <class R>
class OpSetAttribute : public Interpreter::Opcode0
{
ESM::Attribute::AttributeID mIndex;
ESM::RefId mIndex;
public:
OpSetAttribute(ESM::Attribute::AttributeID index)
OpSetAttribute(ESM::RefId index)
: mIndex(index)
{
}
@ -154,10 +154,10 @@ namespace MWScript
template <class R>
class OpModAttribute : public Interpreter::Opcode0
{
ESM::Attribute::AttributeID mIndex;
ESM::RefId mIndex;
public:
OpModAttribute(ESM::Attribute::AttributeID index)
OpModAttribute(ESM::RefId index)
: mIndex(index)
{
}
@ -1322,7 +1322,7 @@ namespace MWScript
{
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<ExplicitRef>>(
Compiler::Stats::opcodeGetAttributeExplicit + i, id);

@ -184,7 +184,7 @@ namespace MWWorld
}
for (const auto& [key, actorId] : creatureStats.mSummonedCreatureMap)
{
if (actorId == -1)
if (actorId < 0)
continue;
for (auto& params : creatureStats.mActiveSpells.mSpells)
{
@ -195,7 +195,7 @@ namespace MWWorld
{
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;
found = true;
break;

@ -63,7 +63,7 @@ namespace MWWorld
for (size_t i = 0; i < mSaveSkills.size(); ++i)
mSaveSkills[i] = stats.getSkill(ESM::Skill::indexToRefId(i)).getModified();
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()
@ -82,7 +82,7 @@ namespace MWWorld
}
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);
attribute.restore(attribute.getDamage());
attribute.setModifier(mSaveAttributes[i] - attribute.getBase());

@ -1041,87 +1041,50 @@ namespace MWWorld
// 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)
{
// TODO remove after !3084 gets merged
mStatic.clear();
mStatic.push_back({ .mId = ESM::Attribute::Strength,
insertStatic({ .mId = ESM::Attribute::Strength,
.mName = std::string{ getGMSTString(settings, "sAttributeStrength") },
.mDescription = std::string{ getGMSTString(settings, "sStrDesc") },
.mIcon = "icons\\k\\attribute_strength.dds",
.mWerewolfValue = getGMSTFloat(settings, "fWerewolfStrength") });
mStatic.push_back({ .mId = ESM::Attribute::Intelligence,
insertStatic({ .mId = ESM::Attribute::Intelligence,
.mName = std::string{ getGMSTString(settings, "sAttributeIntelligence") },
.mDescription = std::string{ getGMSTString(settings, "sIntDesc") },
.mIcon = "icons\\k\\attribute_int.dds",
// Oh, Bethesda. It's "Intelligence".
.mWerewolfValue = getGMSTFloat(settings, "fWerewolfIntellegence") });
mStatic.push_back({ .mId = ESM::Attribute::Willpower,
insertStatic({ .mId = ESM::Attribute::Willpower,
.mName = std::string{ getGMSTString(settings, "sAttributeWillpower") },
.mDescription = std::string{ getGMSTString(settings, "sWilDesc") },
.mIcon = "icons\\k\\attribute_wilpower.dds",
.mWerewolfValue = getGMSTFloat(settings, "fWerewolfWillpower") });
mStatic.push_back({ .mId = ESM::Attribute::Agility,
insertStatic({ .mId = ESM::Attribute::Agility,
.mName = std::string{ getGMSTString(settings, "sAttributeAgility") },
.mDescription = std::string{ getGMSTString(settings, "sAgiDesc") },
.mIcon = "icons\\k\\attribute_agility.dds",
.mWerewolfValue = getGMSTFloat(settings, "fWerewolfAgility") });
mStatic.push_back({ .mId = ESM::Attribute::Speed,
insertStatic({ .mId = ESM::Attribute::Speed,
.mName = std::string{ getGMSTString(settings, "sAttributeSpeed") },
.mDescription = std::string{ getGMSTString(settings, "sSpdDesc") },
.mIcon = "icons\\k\\attribute_speed.dds",
.mWerewolfValue = getGMSTFloat(settings, "fWerewolfSpeed") });
mStatic.push_back({ .mId = ESM::Attribute::Endurance,
insertStatic({ .mId = ESM::Attribute::Endurance,
.mName = std::string{ getGMSTString(settings, "sAttributeEndurance") },
.mDescription = std::string{ getGMSTString(settings, "sEndDesc") },
.mIcon = "icons\\k\\attribute_endurance.dds",
.mWerewolfValue = getGMSTFloat(settings, "fWerewolfEndurance") });
mStatic.push_back({ .mId = ESM::Attribute::Personality,
insertStatic({ .mId = ESM::Attribute::Personality,
.mName = std::string{ getGMSTString(settings, "sAttributePersonality") },
.mDescription = std::string{ getGMSTString(settings, "sPerDesc") },
.mIcon = "icons\\k\\attribute_personality.dds",
.mWerewolfValue = getGMSTFloat(settings, "fWerewolfPersonality") });
mStatic.push_back({ .mId = ESM::Attribute::Luck,
insertStatic({ .mId = ESM::Attribute::Luck,
.mName = std::string{ getGMSTString(settings, "sAttributeLuck") },
.mDescription = std::string{ getGMSTString(settings, "sLucDesc") },
.mIcon = "icons\\k\\attribute_luck.dds",
.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
//=========================================================================
@ -1339,7 +1302,7 @@ namespace MWWorld
template class MWWorld::TypedDynamicStore<ESM::Activator>;
template class MWWorld::TypedDynamicStore<ESM::Apparatus>;
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::BodyPart>;
template class MWWorld::TypedDynamicStore<ESM::Book>;

@ -9,6 +9,7 @@
#include <unordered_map>
#include <vector>
#include <components/esm/attr.hpp>
#include <components/esm/refid.hpp>
#include <components/esm/util.hpp>
#include <components/esm3/loadcell.hpp>
@ -29,7 +30,6 @@
namespace ESM
{
struct Attribute;
struct LandTexture;
struct MagicEffect;
struct WeaponType;
@ -492,25 +492,14 @@ namespace MWWorld
};
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:
typedef std::vector<ESM::Attribute>::const_iterator iterator;
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;
Store() = default;
void setUp(const MWWorld::Store<ESM::GameSetting>& settings);
size_t getSize() const;
iterator begin() const;
iterator end() const;
};
template <>

@ -456,6 +456,8 @@ namespace
{
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>)
refId = ESM::Skill::Block;
else
@ -485,6 +487,12 @@ namespace
ESM::Dialogue* dialogue = nullptr;
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");
ASSERT_NO_THROW(esmStore.load(reader, &dummyListener, dialogue));
esmStore.setUp();
@ -575,7 +583,7 @@ namespace
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(
RecordTypesTest, StoreSaveLoadTest, typename AsTestingTypes<RecordTypesWithSave>::Type);

@ -1,25 +1,56 @@
#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] = {
"Strength",
"Intelligence",
"Willpower",
"Agility",
"Speed",
"Endurance",
"Personality",
"Luck",
};
#include <stdexcept>
Attribute::AttributeID Attribute::stringToAttributeId(std::string_view attribute)
namespace ESM
{
for (int id = 0; id < Attribute::Length; ++id)
if (Misc::StringUtils::ciEqual(sAttributeNames[id], attribute))
return Attribute::AttributeID(id);
const Attribute::AttributeID Attribute::Strength("Strength");
const Attribute::AttributeID Attribute::Intelligence("Intelligence");
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
#include <string>
#include <string_view>
#include "defs.hpp"
#include "refid.hpp"
namespace ESM
{
class ESMReader;
class ESMWriter;
/*
* Attribute definitions
@ -12,26 +18,30 @@ namespace ESM
struct Attribute
{
enum AttributeID
{
Strength = 0,
Intelligence = 1,
Willpower = 2,
Agility = 3,
Speed = 4,
Endurance = 5,
Personality = 6,
Luck = 7,
Length = 8
};
constexpr static RecNameInts sRecordId = REC_ATTR;
/// Return a string descriptor for this record type. Currently used for debugging / error logs only.
static std::string_view getRecordType() { return "Attribute"; }
using AttributeID = StringRefId;
static const AttributeID Strength;
static const AttributeID Intelligence;
static const AttributeID Willpower;
static const AttributeID Agility;
static const AttributeID Speed;
static const AttributeID Endurance;
static const AttributeID Personality;
static const AttributeID Luck;
static constexpr int Length = 8;
AttributeID mId;
std::string mName, mDescription, mIcon;
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

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

@ -2,11 +2,93 @@
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include "loadmgef.hpp"
#include "loadskil.hpp"
#include <components/esm/attr.hpp>
#include <cstdint>
namespace ESM
{
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)
{
for (const auto& params : spells)
@ -27,8 +109,9 @@ namespace ESM
for (auto& effect : params.mEffects)
{
esm.writeHNT("MGEF", effect.mEffectId);
if (effect.mArg != -1)
esm.writeHNT("ARG_", effect.mArg);
int arg = std::visit(ToInt{ effect.mEffectId }, effect.mArg);
if (arg != -1)
esm.writeHNT("ARG_", arg);
esm.writeHNT("MAGN", effect.mMagnitude);
esm.writeHNT("MAGN", effect.mMinMagnitude);
esm.writeHNT("MAGN", effect.mMaxMagnitude);
@ -81,8 +164,17 @@ namespace ESM
{
ActiveEffect effect;
esm.getHT(effect.mEffectId);
effect.mArg = -1;
esm.getHNOT(effect.mArg, "ARG_");
int32_t arg = -1;
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");
if (format <= MaxClearModifiersFormatVersion)
{
@ -112,10 +204,7 @@ namespace ESM
}
}
}
}
namespace ESM
{
void ActiveSpells::save(ESMWriter& esm) const
{
saveImpl(esm, mSpells, "ID__");
@ -127,4 +216,18 @@ namespace ESM
loadImpl(esm, mSpells, "ID__");
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 <string>
#include <variant>
#include <vector>
namespace ESM
@ -32,11 +33,14 @@ namespace ESM
float mMagnitude;
float mMinMagnitude;
float mMaxMagnitude;
int32_t mArg; // skill or attribute
std::variant<RefId, int> mArg; // skill, attribute, or summon
float mDuration;
float mTimeLeft;
int32_t mEffectIndex;
int32_t mFlags;
RefId getSkillOrAttribute() const;
int getActorId() const;
};
// format 0, saved games only

Loading…
Cancel
Save