1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-21 08:39:45 +00:00

Allow for more than 8 attributes

This commit is contained in:
Evil Eye 2023-06-19 20:41:54 +02:00
parent 8edbb9f005
commit abcebd49d2
29 changed files with 183 additions and 170 deletions

View file

@ -88,11 +88,12 @@ namespace
bool male = (npc->mFlags & ESM::NPC::Female) == 0; bool male = (npc->mFlags & ESM::NPC::Female) == 0;
const auto& attributes = MWBase::Environment::get().getESMStore()->get<ESM::Attribute>();
int level = creatureStats.getLevel(); int level = creatureStats.getLevel();
for (int i = 0; i < ESM::Attribute::Length; ++i) for (const ESM::Attribute& attribute : attributes)
{ {
const ESM::Race::MaleFemale& attribute = race->mData.mAttributeValues[i]; const ESM::Race::MaleFemale& value = race->mData.mAttributeValues[attribute.mId];
creatureStats.setAttribute(i, male ? attribute.mMale : attribute.mFemale); creatureStats.setAttribute(attribute.mId, male ? value.mMale : value.mFemale);
} }
// class bonus // class bonus
@ -102,18 +103,19 @@ namespace
{ {
if (attribute >= 0 && attribute < ESM::Attribute::Length) if (attribute >= 0 && attribute < ESM::Attribute::Length)
{ {
creatureStats.setAttribute(attribute, creatureStats.getAttribute(attribute).getBase() + 10); auto id = static_cast<ESM::Attribute::AttributeID>(attribute);
creatureStats.setAttribute(id, creatureStats.getAttribute(id).getBase() + 10);
} }
} }
// skill bonus // skill bonus
for (int attribute = 0; attribute < ESM::Attribute::Length; ++attribute) for (const ESM::Attribute& attribute : attributes)
{ {
float modifierSum = 0; float modifierSum = 0;
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) if (skill.mData.mAttribute != attribute.mId)
continue; continue;
// is this a minor or major skill? // is this a minor or major skill?
@ -127,9 +129,10 @@ namespace
} }
modifierSum += add; modifierSum += add;
} }
creatureStats.setAttribute(attribute, creatureStats.setAttribute(attribute.mId,
std::min( std::min(
round_ieee_754(creatureStats.getAttribute(attribute).getBase() + (level - 1) * modifierSum), 100)); round_ieee_754(creatureStats.getAttribute(attribute.mId).getBase() + (level - 1) * modifierSum),
100));
} }
// initial health // initial health
@ -223,13 +226,10 @@ namespace
100)); // Must gracefully handle level 0 100)); // Must gracefully handle level 0
} }
int attributes[ESM::Attribute::Length];
for (int i = 0; i < ESM::Attribute::Length; ++i)
attributes[i] = npcStats.getAttribute(i).getBase();
if (!spellsInitialised) if (!spellsInitialised)
{ {
std::vector<ESM::RefId> spells = MWMechanics::autoCalcNpcSpells(npcStats.getSkills(), attributes, race); std::vector<ESM::RefId> spells
= MWMechanics::autoCalcNpcSpells(npcStats.getSkills(), npcStats.getAttributes(), race);
npcStats.getSpells().addAllToInstance(spells); npcStats.getSpells().addAllToInstance(spells);
} }
} }

View file

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

View file

@ -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(klass->mData.mAttribute[0]); mFavoriteAttribute[0]->setAttributeId(static_cast<ESM::Attribute::AttributeID>(klass->mData.mAttribute[0]));
mFavoriteAttribute[1]->setAttributeId(klass->mData.mAttribute[1]); mFavoriteAttribute[1]->setAttributeId(static_cast<ESM::Attribute::AttributeID>(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());
@ -776,8 +776,7 @@ namespace MWGui
void SelectAttributeDialog::onAttributeClicked(Widgets::MWAttributePtr _sender) void SelectAttributeDialog::onAttributeClicked(Widgets::MWAttributePtr _sender)
{ {
// TODO: Change MWAttribute to set and get AttributeID enum instead of int mAttributeId = _sender->getAttributeId();
mAttributeId = static_cast<ESM::Attribute::AttributeID>(_sender->getAttributeId());
eventItemSelected(); eventItemSelected();
} }

View file

@ -3,6 +3,7 @@
#include <MyGUI_Button.h> #include <MyGUI_Button.h>
#include <MyGUI_EditBox.h> #include <MyGUI_EditBox.h>
#include <MyGUI_ImageBox.h> #include <MyGUI_ImageBox.h>
#include <MyGUI_ScrollView.h>
#include <MyGUI_TextBox.h> #include <MyGUI_TextBox.h>
#include <components/fallback/fallback.hpp> #include <components/fallback/fallback.hpp>
@ -50,9 +51,9 @@ namespace MWGui
size_t i = 0; size_t i = 0;
for (const ESM::Attribute& attribute : store) for (const ESM::Attribute& attribute : store)
{ {
auto& widgets = mAttributeWidgets.emplace_back();
const int offset = sCols[i / perCol]; const int offset = sCols[i / perCol];
const int row = static_cast<int>(i % perCol); const int row = static_cast<int>(i % perCol);
Widgets widgets;
widgets.mMultiplier = mAssignWidget->createWidget<MyGUI::TextBox>( widgets.mMultiplier = mAssignWidget->createWidget<MyGUI::TextBox>(
"SandTextVCenter", { offset, 20 * row, 100, 20 }, MyGUI::Align::Default); "SandTextVCenter", { offset, 20 * row, 100, 20 }, MyGUI::Align::Default);
auto* hbox = mAssignWidget->createWidget<Gui::HBox>( auto* hbox = mAssignWidget->createWidget<Gui::HBox>(
@ -68,8 +69,15 @@ namespace MWGui
widgets.mButton->setUserString("ImageTexture_AttributeImage", attribute.mIcon); widgets.mButton->setUserString("ImageTexture_AttributeImage", attribute.mIcon);
widgets.mButton->setCaption(attribute.mName); widgets.mButton->setCaption(attribute.mName);
widgets.mValue = hbox->createWidget<Gui::AutoSizedTextBox>("SandText", {}, MyGUI::Align::Default); widgets.mValue = hbox->createWidget<Gui::AutoSizedTextBox>("SandText", {}, MyGUI::Align::Default);
mAttributeWidgets.emplace(attribute.mId, widgets);
++i; ++i;
} }
mAssignWidget->setVisibleVScroll(false);
mAssignWidget->setCanvasSize(MyGUI::IntSize(
mAssignWidget->getWidth(), std::max(mAssignWidget->getHeight(), static_cast<int>(20 * perCol))));
mAssignWidget->setVisibleVScroll(true);
mAssignWidget->setViewOffset(MyGUI::IntPoint());
} }
for (unsigned int i = 0; i < sMaxCoins; ++i) for (unsigned int i = 0; i < sMaxCoins; ++i)
@ -136,11 +144,12 @@ namespace MWGui
const auto& attribute = mSpentAttributes[i]; const auto& attribute = mSpentAttributes[i];
const auto& widgets = mAttributeWidgets[attribute]; const auto& widgets = mAttributeWidgets[attribute];
int xdiff = widgets.mMultiplier->getCaption().empty() ? 0 : 20; const int xdiff = widgets.mMultiplier->getCaption().empty() ? 0 : 20;
const auto* hbox = widgets.mButton->getParent();
MyGUI::IntPoint pos = widgets.mButton->getAbsolutePosition() - mAssignWidget->getAbsolutePosition() MyGUI::IntPoint pos = hbox->getPosition();
- MyGUI::IntPoint(22 + xdiff, 0); pos.left -= 22 + xdiff;
pos.top += (widgets.mButton->getHeight() - image->getHeight()) / 2; pos.top += (hbox->getHeight() - image->getHeight()) / 2;
image->setPosition(pos); image->setPosition(pos);
} }

View file

@ -28,9 +28,9 @@ namespace MWGui
MyGUI::EditBox* mLevelDescription; MyGUI::EditBox* mLevelDescription;
MyGUI::Widget* mCoinBox; MyGUI::Widget* mCoinBox;
MyGUI::Widget* mAssignWidget; MyGUI::ScrollView* mAssignWidget;
std::vector<Widgets> mAttributeWidgets; std::map<ESM::Attribute::AttributeID, Widgets> mAttributeWidgets;
std::vector<MyGUI::ImageBox*> mCoins; std::vector<MyGUI::ImageBox*> mCoins;
std::vector<ESM::Attribute::AttributeID> mSpentAttributes; std::vector<ESM::Attribute::AttributeID> mSpentAttributes;

View file

@ -201,7 +201,7 @@ namespace MWGui
void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::AttributeValue& value) void ReviewDialog::setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::AttributeValue& value)
{ {
std::map<int, Widgets::MWAttributePtr>::iterator attr = mAttributeWidgets.find(static_cast<int>(attributeId)); auto attr = mAttributeWidgets.find(attributeId);
if (attr == mAttributeWidgets.end()) if (attr == mAttributeWidgets.end())
return; return;
@ -402,9 +402,9 @@ 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);
int attributes[ESM::Attribute::Length]; std::map<ESM::Attribute::AttributeID, MWMechanics::AttributeValue> attributes;
for (int i = 0; i < ESM::Attribute::Length; ++i) for (const auto& [key, value] : mAttributeWidgets)
attributes[i] = mAttributeWidgets[i]->getAttributeValue().getBase(); attributes[key] = value->getAttributeValue();
std::vector<ESM::RefId> selectedSpells = MWMechanics::autoCalcPlayerSpells(mSkillValues, attributes, race); std::vector<ESM::RefId> selectedSpells = MWMechanics::autoCalcPlayerSpells(mSkillValues, attributes, race);
for (ESM::RefId& spellId : selectedSpells) for (ESM::RefId& spellId : selectedSpells)

View file

@ -90,7 +90,7 @@ namespace MWGui
Widgets::MWDynamicStatPtr mHealth, mMagicka, mFatigue; Widgets::MWDynamicStatPtr mHealth, mMagicka, mFatigue;
std::map<int, Widgets::MWAttributePtr> mAttributeWidgets; std::map<ESM::Attribute::AttributeID, 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;

View file

@ -31,7 +31,7 @@ namespace MWGui
{ {
MWWorld::Ptr mWatched; MWWorld::Ptr mWatched;
MWMechanics::AttributeValue mWatchedAttributes[ESM::Attribute::Length]; std::map<ESM::Attribute::AttributeID, 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;

View file

@ -823,7 +823,7 @@ namespace MWGui
widget->setUserString("ImageTexture_SkillNoProgressImage", skill->mIcon); widget->setUserString("ImageTexture_SkillNoProgressImage", skill->mIcon);
} }
void ToolTips::createAttributeToolTip(MyGUI::Widget* widget, int attributeId) void ToolTips::createAttributeToolTip(MyGUI::Widget* widget, ESM::Attribute::AttributeID 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);

View file

@ -94,7 +94,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, int attributeId); static void createAttributeToolTip(MyGUI::Widget* widget, ESM::Attribute::AttributeID 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);

View file

@ -150,7 +150,8 @@ 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() >= pcStats.getAttribute(skill->mData.mAttribute).getBase()) if (pcStats.getSkill(skill->mId).getBase()
>= pcStats.getAttribute(ESM::Attribute::AttributeID(skill->mData.mAttribute)).getBase())
{ {
MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage17}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage17}");
return; return;

View file

@ -102,13 +102,13 @@ namespace MWGui::Widgets
/* MWAttribute */ /* MWAttribute */
MWAttribute::MWAttribute() MWAttribute::MWAttribute()
: mId(-1) : mId(ESM::Attribute::Length)
, mAttributeNameWidget(nullptr) , mAttributeNameWidget(nullptr)
, mAttributeValueWidget(nullptr) , mAttributeValueWidget(nullptr)
{ {
} }
void MWAttribute::setAttributeId(int attributeId) void MWAttribute::setAttributeId(ESM::Attribute::AttributeID attributeId)
{ {
mId = attributeId; mId = attributeId;
updateWidgets(); updateWidgets();
@ -154,8 +154,6 @@ namespace MWGui::Widgets
} }
} }
MWAttribute::~MWAttribute() {}
void MWAttribute::initialiseOverride() void MWAttribute::initialiseOverride()
{ {
Base::initialiseOverride(); Base::initialiseOverride();

View file

@ -7,6 +7,7 @@
#include <MyGUI_TextBox.h> #include <MyGUI_TextBox.h>
#include <MyGUI_Widget.h> #include <MyGUI_Widget.h>
#include <components/esm/attr.hpp>
#include <components/esm/refid.hpp> #include <components/esm/refid.hpp>
#include <components/esm3/effectlist.hpp> #include <components/esm3/effectlist.hpp>
#include <components/esm3/loadskil.hpp> #include <components/esm3/loadskil.hpp>
@ -32,8 +33,6 @@ namespace MWGui
{ {
class MWEffectList; class MWEffectList;
void fixTexturePath(std::string& path);
struct SpellEffectParams struct SpellEffectParams
{ {
SpellEffectParams() SpellEffectParams()
@ -139,10 +138,10 @@ namespace MWGui
typedef MWMechanics::AttributeValue AttributeValue; typedef MWMechanics::AttributeValue AttributeValue;
void setAttributeId(int attributeId); void setAttributeId(ESM::Attribute::AttributeID attributeId);
void setAttributeValue(const AttributeValue& value); void setAttributeValue(const AttributeValue& value);
int getAttributeId() const { return mId; } ESM::Attribute::AttributeID getAttributeId() const { return mId; }
const AttributeValue& getAttributeValue() const { return mValue; } const AttributeValue& getAttributeValue() const { return mValue; }
// Events // Events
@ -154,7 +153,7 @@ namespace MWGui
EventHandle_AttributeVoid eventClicked; EventHandle_AttributeVoid eventClicked;
protected: protected:
virtual ~MWAttribute(); ~MWAttribute() override = default;
void initialiseOverride() override; void initialiseOverride() override;
@ -163,7 +162,7 @@ namespace MWGui
private: private:
void updateWidgets(); void updateWidgets();
int mId; ESM::Attribute::AttributeID mId;
AttributeValue mValue; AttributeValue mValue;
MyGUI::TextBox* mAttributeNameWidget; MyGUI::TextBox* mAttributeNameWidget;
MyGUI::TextBox* mAttributeValueWidget; MyGUI::TextBox* mAttributeValueWidget;

View file

@ -182,9 +182,10 @@ 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, [this, getter](const MWWorld::Ptr& ptr) { context, mObject, &AttributeStat::setValue, mIndex, prop, [id, getter](const MWWorld::Ptr& ptr) {
return (ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex).*getter)(); return (ptr.getClass().getCreatureStats(ptr).getAttribute(id).*getter)();
}); });
} }
@ -213,9 +214,9 @@ namespace MWLua
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)
{ {
int index = std::get<int>(i); auto id = static_cast<ESM::Attribute::AttributeID>(std::get<int>(i));
auto& stats = ptr.getClass().getCreatureStats(ptr); auto& stats = ptr.getClass().getCreatureStats(ptr);
auto stat = stats.getAttribute(index); auto stat = stats.getAttribute(id);
float floatValue = LuaUtil::cast<float>(value); float floatValue = LuaUtil::cast<float>(value);
if (prop == "base") if (prop == "base")
stat.setBase(floatValue); stat.setBase(floatValue);
@ -226,7 +227,7 @@ namespace MWLua
} }
else if (prop == "modifier") else if (prop == "modifier")
stat.setModifier(floatValue); stat.setModifier(floatValue);
stats.setAttribute(index, stat); stats.setAttribute(id, stat);
} }
}; };

View file

@ -27,13 +27,13 @@ namespace MWMechanics
ESM::RefId mWeakestSpell; ESM::RefId mWeakestSpell;
}; };
std::vector<ESM::RefId> autoCalcNpcSpells( std::vector<ESM::RefId> autoCalcNpcSpells(const std::map<ESM::RefId, SkillValue>& actorSkills,
const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes, const ESM::Race* race) const std::map<ESM::Attribute::AttributeID, 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>();
static const float fNPCbaseMagickaMult = gmst.find("fNPCbaseMagickaMult")->mValue.getFloat(); static const float fNPCbaseMagickaMult = gmst.find("fNPCbaseMagickaMult")->mValue.getFloat();
float baseMagicka = fNPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence]; float baseMagicka = fNPCbaseMagickaMult * actorAttributes.at(ESM::Attribute::Intelligence).getBase();
static const std::string schools[] static const std::string schools[]
= { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" }; = { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" };
@ -148,15 +148,15 @@ namespace MWMechanics
return selectedSpells; return selectedSpells;
} }
std::vector<ESM::RefId> autoCalcPlayerSpells( std::vector<ESM::RefId> autoCalcPlayerSpells(const std::map<ESM::RefId, SkillValue>& actorSkills,
const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes, const ESM::Race* race) const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, const ESM::Race* race)
{ {
const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore(); const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore();
static const float fPCbaseMagickaMult static const float fPCbaseMagickaMult
= esmStore.get<ESM::GameSetting>().find("fPCbaseMagickaMult")->mValue.getFloat(); = esmStore.get<ESM::GameSetting>().find("fPCbaseMagickaMult")->mValue.getFloat();
float baseMagicka = fPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence]; float baseMagicka = fPCbaseMagickaMult * actorAttributes.at(ESM::Attribute::Intelligence).getBase();
bool reachedLimit = false; bool reachedLimit = false;
const ESM::Spell* weakestSpell = nullptr; const ESM::Spell* weakestSpell = nullptr;
int minCost = std::numeric_limits<int>::max(); int minCost = std::numeric_limits<int>::max();
@ -227,8 +227,8 @@ namespace MWMechanics
return selectedSpells; return selectedSpells;
} }
bool attrSkillCheck( bool attrSkillCheck(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills,
const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes) const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes)
{ {
for (const auto& spellEffect : spell->mEffects.mList) for (const auto& spellEffect : spell->mEffects.mList)
{ {
@ -250,8 +250,8 @@ namespace MWMechanics
if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)) if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute))
{ {
assert(spellEffect.mAttribute >= 0 && spellEffect.mAttribute < ESM::Attribute::Length); auto found = actorAttributes.find(ESM::Attribute::AttributeID(spellEffect.mAttribute));
if (actorAttributes[spellEffect.mAttribute] < iAutoSpellAttSkillMin) if (found == actorAttributes.end() || found->second.getBase() < iAutoSpellAttSkillMin)
return false; return false;
} }
} }
@ -313,7 +313,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 int* actorAttributes, int effectiveSchool) const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, int effectiveSchool)
{ {
if (spell->mData.mType != ESM::Spell::ST_Spell) if (spell->mData.mType != ESM::Spell::ST_Spell)
return 100.f; return 100.f;
@ -334,7 +334,8 @@ namespace MWMechanics
spell, actorSkills, effectiveSchool, skillTerm); // Note effectiveSchool is unused after this spell, actorSkills, effectiveSchool, skillTerm); // Note effectiveSchool is unused after this
float castChance = skillTerm - MWMechanics::calcSpellCost(*spell) float castChance = skillTerm - MWMechanics::calcSpellCost(*spell)
+ 0.2f * actorAttributes[ESM::Attribute::Willpower] + 0.1f * actorAttributes[ESM::Attribute::Luck]; + 0.2f * actorAttributes.at(ESM::Attribute::Willpower).getBase()
+ 0.1f * actorAttributes.at(ESM::Attribute::Luck).getBase();
return castChance; return castChance;
} }
} }

View file

@ -19,22 +19,22 @@ namespace MWMechanics
/// Contains algorithm for calculating an NPC's spells based on stats /// Contains algorithm for calculating an NPC's spells based on stats
/// @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( std::vector<ESM::RefId> autoCalcNpcSpells(const std::map<ESM::RefId, SkillValue>& actorSkills,
const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes, const ESM::Race* race); const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, const ESM::Race* race);
std::vector<ESM::RefId> autoCalcPlayerSpells( std::vector<ESM::RefId> autoCalcPlayerSpells(const std::map<ESM::RefId, SkillValue>& actorSkills,
const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes, const ESM::Race* race); const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, const ESM::Race* race);
// Helpers // Helpers
bool attrSkillCheck( bool attrSkillCheck(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills,
const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes); const std::map<ESM::Attribute::AttributeID, 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,
int& effectiveSchool, float& skillTerm); int& 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 int* actorAttributes, int effectiveSchool); const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, int effectiveSchool);
} }

View file

@ -46,6 +46,10 @@ namespace MWMechanics
, mLevel(0) , mLevel(0)
, mAttackingOrSpell(false) , mAttackingOrSpell(false)
{ {
for (const ESM::Attribute& attribute : MWBase::Environment::get().getESMStore()->get<ESM::Attribute>())
{
mAttributes.emplace(attribute.mId, AttributeValue{});
}
} }
const AiSequence& CreatureStats::getAiSequence() const const AiSequence& CreatureStats::getAiSequence() const
@ -74,13 +78,9 @@ namespace MWMechanics
return fFatigueBase - fFatigueMult * (1 - normalised); return fFatigueBase - fFatigueMult * (1 - normalised);
} }
const AttributeValue& CreatureStats::getAttribute(int index) const const AttributeValue& CreatureStats::getAttribute(ESM::Attribute::AttributeID id) const
{ {
if (index < 0 || index > 7) return mAttributes.at(id);
{
throw std::runtime_error("attribute index is out of range");
}
return mAttributes[index];
} }
const DynamicStat<float>& CreatureStats::getHealth() const const DynamicStat<float>& CreatureStats::getHealth() const
@ -147,30 +147,25 @@ namespace MWMechanics
return mMagicEffects; return mMagicEffects;
} }
void CreatureStats::setAttribute(int index, float base) void CreatureStats::setAttribute(ESM::Attribute::AttributeID id, float base)
{ {
AttributeValue current = getAttribute(index); AttributeValue current = getAttribute(id);
current.setBase(base); current.setBase(base);
setAttribute(index, current); setAttribute(id, current);
} }
void CreatureStats::setAttribute(int index, const AttributeValue& value) void CreatureStats::setAttribute(ESM::Attribute::AttributeID id, const AttributeValue& value)
{ {
if (index < 0 || index > 7) const AttributeValue& currentValue = mAttributes.at(id);
{
throw std::runtime_error("attribute index is out of range");
}
const AttributeValue& currentValue = mAttributes[index];
if (value != currentValue) if (value != currentValue)
{ {
mAttributes[index] = value; mAttributes[id] = value;
if (index == ESM::Attribute::Intelligence) if (id == ESM::Attribute::Intelligence)
recalculateMagicka(); recalculateMagicka();
else if (index == ESM::Attribute::Strength || index == ESM::Attribute::Willpower else if (id == ESM::Attribute::Strength || id == ESM::Attribute::Willpower || id == ESM::Attribute::Agility
|| index == ESM::Attribute::Agility || index == ESM::Attribute::Endurance) || id == ESM::Attribute::Endurance)
{ {
float strength = getAttribute(ESM::Attribute::Strength).getModified(); float strength = getAttribute(ESM::Attribute::Strength).getModified();
float willpower = getAttribute(ESM::Attribute::Willpower).getModified(); float willpower = getAttribute(ESM::Attribute::Willpower).getModified();
@ -536,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)
mAttributes[i].writeState(state.mAttributes[i]); getAttribute(static_cast<ESM::Attribute::AttributeID>(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]);
@ -593,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[i].readState(state.mAttributes[i]); mAttributes[static_cast<ESM::Attribute::AttributeID>(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]);

View file

@ -40,7 +40,7 @@ namespace MWMechanics
{ {
static int sActorId; static int sActorId;
DrawState mDrawState; DrawState mDrawState;
AttributeValue mAttributes[ESM::Attribute::Length]; std::map<ESM::Attribute::AttributeID, 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(int index) const; const AttributeValue& getAttribute(ESM::Attribute::AttributeID id) const;
const DynamicStat<float>& getHealth() const; const DynamicStat<float>& getHealth() const;
@ -139,9 +139,9 @@ namespace MWMechanics
MagicEffects& getMagicEffects(); MagicEffects& getMagicEffects();
void setAttribute(int index, const AttributeValue& value); void setAttribute(ESM::Attribute::AttributeID id, const AttributeValue& value);
// Shortcut to set only the base // Shortcut to set only the base
void setAttribute(int index, float base); void setAttribute(ESM::Attribute::AttributeID id, float base);
void setHealth(const DynamicStat<float>& value); void setHealth(const DynamicStat<float>& value);
@ -293,6 +293,8 @@ 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; }
}; };
} }

View file

@ -148,11 +148,11 @@ namespace MWMechanics
bool male = (player->mFlags & ESM::NPC::Female) == 0; bool male = (player->mFlags & ESM::NPC::Female) == 0;
for (int i = 0; i < ESM::Attribute::Length; ++i) for (const ESM::Attribute& attribute : esmStore.get<ESM::Attribute>())
{ {
const ESM::Race::MaleFemale& attribute = race->mData.mAttributeValues[i]; const ESM::Race::MaleFemale& value = race->mData.mAttributeValues[attribute.mId];
creatureStats.setAttribute(i, male ? attribute.mMale : attribute.mFemale); creatureStats.setAttribute(attribute.mId, male ? value.mMale : value.mFemale);
} }
for (const ESM::Skill& skill : esmStore.get<ESM::Skill>()) for (const ESM::Skill& skill : esmStore.get<ESM::Skill>())
@ -195,7 +195,8 @@ namespace MWMechanics
{ {
if (attribute >= 0 && attribute < ESM::Attribute::Length) if (attribute >= 0 && attribute < ESM::Attribute::Length)
{ {
creatureStats.setAttribute(attribute, creatureStats.getAttribute(attribute).getBase() + 10); auto id = static_cast<ESM::Attribute::AttributeID>(attribute);
creatureStats.setAttribute(id, creatureStats.getAttribute(id).getBase() + 10);
} }
} }
@ -223,12 +224,10 @@ namespace MWMechanics
if (mRaceSelected) if (mRaceSelected)
race = esmStore.get<ESM::Race>().find(player->mRace); race = esmStore.get<ESM::Race>().find(player->mRace);
int attributes[ESM::Attribute::Length];
for (int i = 0; i < ESM::Attribute::Length; ++i)
attributes[i] = npcStats.getAttribute(i).getBase();
npcStats.updateHealth(); npcStats.updateHealth();
std::vector<ESM::RefId> selectedSpells = autoCalcPlayerSpells(npcStats.getSkills(), attributes, race); std::vector<ESM::RefId> selectedSpells
= autoCalcPlayerSpells(npcStats.getSkills(), npcStats.getAttributes(), race);
for (const ESM::RefId& spell : selectedSpells) for (const ESM::RefId& spell : selectedSpells)
creatureStats.getSpells().add(spell); creatureStats.getSpells().add(spell);

View file

@ -28,7 +28,6 @@ MWMechanics::NpcStats::NpcStats()
, mTimeToStartDrowning(-1.0) // set breath to special value, it will be replaced during actor update , mTimeToStartDrowning(-1.0) // set breath to special value, it will be replaced during actor update
, mIsWerewolf(false) , mIsWerewolf(false)
{ {
mSkillIncreases.resize(ESM::Attribute::Length, 0);
mSpecIncreases.resize(3, 0); mSpecIncreases.resize(3, 0);
for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get<ESM::Skill>()) for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get<ESM::Skill>())
mSkills.emplace(skill.mId, SkillValue{}); mSkills.emplace(skill.mId, SkillValue{});
@ -246,7 +245,7 @@ void MWMechanics::NpcStats::increaseSkill(ESM::RefId id, const ESM::Class& class
} }
} }
mSkillIncreases[skill->mData.mAttribute] += increase; mSkillIncreases[ESM::Attribute::AttributeID(skill->mData.mAttribute)] += increase;
mSpecIncreases[skill->mData.mSpecialization] += gmst.find("iLevelupSpecialization")->mValue.getInteger(); mSpecIncreases[skill->mData.mSpecialization] += gmst.find("iLevelupSpecialization")->mValue.getInteger();
@ -286,8 +285,7 @@ void MWMechanics::NpcStats::levelUp()
mLevelProgress -= gmst.find("iLevelUpTotal")->mValue.getInteger(); mLevelProgress -= gmst.find("iLevelUpTotal")->mValue.getInteger();
mLevelProgress = std::max(0, mLevelProgress); // might be necessary when levelup was invoked via console mLevelProgress = std::max(0, mLevelProgress); // might be necessary when levelup was invoked via console
for (int i = 0; i < ESM::Attribute::Length; ++i) mSkillIncreases.clear();
mSkillIncreases[i] = 0;
const float endurance = getAttribute(ESM::Attribute::Endurance).getBase(); const float endurance = getAttribute(ESM::Attribute::Endurance).getBase();
@ -312,14 +310,12 @@ void MWMechanics::NpcStats::updateHealth()
setHealth(floor(0.5f * (strength + endurance))); setHealth(floor(0.5f * (strength + endurance)));
} }
int MWMechanics::NpcStats::getLevelupAttributeMultiplier(int attribute) const int MWMechanics::NpcStats::getLevelupAttributeMultiplier(ESM::Attribute::AttributeID attribute) const
{ {
int num = mSkillIncreases[attribute]; auto it = mSkillIncreases.find(attribute);
if (it == mSkillIncreases.end() || it->second == 0)
if (num == 0)
return 1; return 1;
int num = std::min(10, it->second);
num = std::min(10, num);
// iLevelUp01Mult - iLevelUp10Mult // iLevelUp01Mult - iLevelUp10Mult
std::stringstream gmst; std::stringstream gmst;
@ -489,7 +485,7 @@ void MWMechanics::NpcStats::writeState(ESM::NpcStats& state) const
state.mLevelProgress = mLevelProgress; state.mLevelProgress = mLevelProgress;
for (size_t i = 0; i < state.mSkillIncrease.size(); ++i) for (size_t i = 0; i < state.mSkillIncrease.size(); ++i)
state.mSkillIncrease[i] = mSkillIncreases[i]; state.mSkillIncrease[i] = mSkillIncreases.at(static_cast<ESM::Attribute::AttributeID>(i));
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 +534,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[i] = state.mSkillIncrease[i]; mSkillIncreases[static_cast<ESM::Attribute::AttributeID>(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];

View file

@ -36,7 +36,8 @@ 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::vector<int> mSkillIncreases; // number of skill increases for each attribute (resets after leveling up) std::map<ESM::Attribute::AttributeID, 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 std::vector<int> mSpecIncreases; // number of skill increases for each specialization (accumulates throughout
// the entire game) // the entire game)
std::set<ESM::RefId> mUsedIds; std::set<ESM::RefId> mUsedIds;
@ -87,7 +88,7 @@ namespace MWMechanics
int getLevelProgress() const; int getLevelProgress() const;
int getLevelupAttributeMultiplier(int attribute) const; int getLevelupAttributeMultiplier(ESM::Attribute::AttributeID attribute) const;
int getSkillIncreasesForSpecialization(int spec) const; int getSkillIncreasesForSpecialization(int spec) const;

View file

@ -79,27 +79,30 @@ 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 attr = creatureStats.getAttribute(effect.mArg); auto attribute = static_cast<ESM::Attribute::AttributeID>(effect.mArg);
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);
attr.damage(magnitude); attr.damage(magnitude);
creatureStats.setAttribute(effect.mArg, attr); creatureStats.setAttribute(attribute, attr);
} }
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 attr = creatureStats.getAttribute(effect.mArg); auto attribute = static_cast<ESM::Attribute::AttributeID>(effect.mArg);
auto attr = creatureStats.getAttribute(attribute);
attr.restore(magnitude); attr.restore(magnitude);
creatureStats.setAttribute(effect.mArg, attr); creatureStats.setAttribute(attribute, attr);
} }
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 attr = creatureStats.getAttribute(effect.mArg); auto attribute = static_cast<ESM::Attribute::AttributeID>(effect.mArg);
auto attr = creatureStats.getAttribute(attribute);
attr.setModifier(attr.getModifier() + magnitude); attr.setModifier(attr.getModifier() + magnitude);
creatureStats.setAttribute(effect.mArg, attr); creatureStats.setAttribute(attribute, attr);
} }
void damageSkill(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude) void damageSkill(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude)
@ -740,9 +743,10 @@ 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);
AttributeValue attr = creatureStats.getAttribute(effect.mArg); auto attribute = static_cast<ESM::Attribute::AttributeID>(effect.mArg);
AttributeValue attr = creatureStats.getAttribute(attribute);
attr.setBase(attr.getBase() + effect.mMagnitude); attr.setBase(attr.getBase() + effect.mMagnitude);
creatureStats.setAttribute(effect.mArg, attr); creatureStats.setAttribute(attribute, attr);
} }
else else
fortifyAttribute(target, effect, effect.mMagnitude); fortifyAttribute(target, effect, effect.mMagnitude);
@ -1203,9 +1207,10 @@ 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);
AttributeValue attr = creatureStats.getAttribute(effect.mArg); auto attribute = static_cast<ESM::Attribute::AttributeID>(effect.mArg);
AttributeValue attr = creatureStats.getAttribute(attribute);
attr.setBase(attr.getBase() - effect.mMagnitude); attr.setBase(attr.getBase() - effect.mMagnitude);
creatureStats.setAttribute(effect.mArg, attr); creatureStats.setAttribute(attribute, attr);
} }
else else
fortifyAttribute(target, effect, -effect.mMagnitude); fortifyAttribute(target, effect, -effect.mMagnitude);

View file

@ -540,7 +540,11 @@ namespace MWMechanics
case ESM::MagicEffect::DamageAttribute: case ESM::MagicEffect::DamageAttribute:
case ESM::MagicEffect::DrainAttribute: case ESM::MagicEffect::DrainAttribute:
if (!enemy.isEmpty() if (!enemy.isEmpty()
&& enemy.getClass().getCreatureStats(enemy).getAttribute(effect.mAttribute).getModified() <= 0) && enemy.getClass()
.getCreatureStats(enemy)
.getAttribute(ESM::Attribute::AttributeID(effect.mAttribute))
.getModified()
<= 0)
return 0.f; return 0.f;
{ {
if (effect.mAttribute >= 0 && effect.mAttribute < ESM::Attribute::Length) if (effect.mAttribute >= 0 && effect.mAttribute < ESM::Attribute::Length)

View file

@ -247,23 +247,24 @@ namespace MWMechanics
return; return;
// Note: if target actor has the Restore attribute effects, stats will be restored. // Note: if target actor has the Restore attribute effects, stats will be restored.
for (std::vector<ESM::SpellState::PermanentSpellEffectInfo>::const_iterator effectIt = it->second.begin(); for (const ESM::SpellState::PermanentSpellEffectInfo& info : it->second)
effectIt != it->second.end(); ++effectIt)
{ {
// Applied corprus effects are already in loaded stats modifiers // Applied corprus effects are already in loaded stats modifiers
if (effectIt->mId == ESM::MagicEffect::FortifyAttribute) if (info.mId == ESM::MagicEffect::FortifyAttribute)
{ {
AttributeValue attr = creatureStats->getAttribute(effectIt->mArg); auto id = static_cast<ESM::Attribute::AttributeID>(info.mArg);
attr.setModifier(attr.getModifier() - effectIt->mMagnitude); AttributeValue attr = creatureStats->getAttribute(id);
attr.damage(-effectIt->mMagnitude); attr.setModifier(attr.getModifier() - info.mMagnitude);
creatureStats->setAttribute(effectIt->mArg, attr); attr.damage(-info.mMagnitude);
creatureStats->setAttribute(id, attr);
} }
else if (effectIt->mId == ESM::MagicEffect::DrainAttribute) else if (info.mId == ESM::MagicEffect::DrainAttribute)
{ {
AttributeValue attr = creatureStats->getAttribute(effectIt->mArg); auto id = static_cast<ESM::Attribute::AttributeID>(info.mArg);
attr.setModifier(attr.getModifier() + effectIt->mMagnitude); AttributeValue attr = creatureStats->getAttribute(id);
attr.damage(effectIt->mMagnitude); attr.setModifier(attr.getModifier() + info.mMagnitude);
creatureStats->setAttribute(effectIt->mArg, attr); attr.damage(info.mMagnitude);
creatureStats->setAttribute(id, attr);
} }
} }
} }

View file

@ -109,10 +109,10 @@ namespace MWScript
template <class R> template <class R>
class OpGetAttribute : public Interpreter::Opcode0 class OpGetAttribute : public Interpreter::Opcode0
{ {
int mIndex; ESM::Attribute::AttributeID mIndex;
public: public:
OpGetAttribute(int index) OpGetAttribute(ESM::Attribute::AttributeID 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
{ {
int mIndex; ESM::Attribute::AttributeID mIndex;
public: public:
OpSetAttribute(int index) OpSetAttribute(ESM::Attribute::AttributeID 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
{ {
int mIndex; ESM::Attribute::AttributeID mIndex;
public: public:
OpModAttribute(int index) OpModAttribute(ESM::Attribute::AttributeID index)
: mIndex(index) : mIndex(index)
{ {
} }
@ -1322,17 +1322,18 @@ namespace MWScript
{ {
for (int i = 0; i < Compiler::Stats::numberOfAttributes; ++i) for (int i = 0; i < Compiler::Stats::numberOfAttributes; ++i)
{ {
interpreter.installSegment5<OpGetAttribute<ImplicitRef>>(Compiler::Stats::opcodeGetAttribute + i, i); auto id = static_cast<ESM::Attribute::AttributeID>(i);
interpreter.installSegment5<OpGetAttribute<ImplicitRef>>(Compiler::Stats::opcodeGetAttribute + i, id);
interpreter.installSegment5<OpGetAttribute<ExplicitRef>>( interpreter.installSegment5<OpGetAttribute<ExplicitRef>>(
Compiler::Stats::opcodeGetAttributeExplicit + i, i); Compiler::Stats::opcodeGetAttributeExplicit + i, id);
interpreter.installSegment5<OpSetAttribute<ImplicitRef>>(Compiler::Stats::opcodeSetAttribute + i, i); interpreter.installSegment5<OpSetAttribute<ImplicitRef>>(Compiler::Stats::opcodeSetAttribute + i, id);
interpreter.installSegment5<OpSetAttribute<ExplicitRef>>( interpreter.installSegment5<OpSetAttribute<ExplicitRef>>(
Compiler::Stats::opcodeSetAttributeExplicit + i, i); Compiler::Stats::opcodeSetAttributeExplicit + i, id);
interpreter.installSegment5<OpModAttribute<ImplicitRef>>(Compiler::Stats::opcodeModAttribute + i, i); interpreter.installSegment5<OpModAttribute<ImplicitRef>>(Compiler::Stats::opcodeModAttribute + i, id);
interpreter.installSegment5<OpModAttribute<ExplicitRef>>( interpreter.installSegment5<OpModAttribute<ExplicitRef>>(
Compiler::Stats::opcodeModAttributeExplicit + i, i); Compiler::Stats::opcodeModAttributeExplicit + i, id);
} }
for (int i = 0; i < Compiler::Stats::numberOfDynamics; ++i) for (int i = 0; i < Compiler::Stats::numberOfDynamics; ++i)

View file

@ -61,8 +61,8 @@ 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 (int i = 0; i < ESM::Attribute::Length; ++i) for (size_t i = 0; i < mSaveAttributes.size(); ++i)
mSaveAttributes[i] = stats.getAttribute(i).getModified(); mSaveAttributes[i] = stats.getAttribute(static_cast<ESM::Attribute::AttributeID>(i)).getModified();
} }
void Player::restoreStats() void Player::restoreStats()
@ -79,12 +79,13 @@ namespace MWWorld
skill.restore(skill.getDamage()); skill.restore(skill.getDamage());
skill.setModifier(mSaveSkills[i] - skill.getBase()); skill.setModifier(mSaveSkills[i] - skill.getBase());
} }
for (int i = 0; i < ESM::Attribute::Length; ++i) for (size_t i = 0; i < mSaveAttributes.size(); ++i)
{ {
auto attribute = npcStats.getAttribute(i); auto id = static_cast<ESM::Attribute::AttributeID>(i);
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());
npcStats.setAttribute(i, attribute); npcStats.setAttribute(id, attribute);
} }
} }
@ -252,11 +253,7 @@ namespace MWWorld
mLastKnownExteriorPosition = osg::Vec3f(0, 0, 0); mLastKnownExteriorPosition = osg::Vec3f(0, 0, 0);
mSaveSkills.fill(0.f); mSaveSkills.fill(0.f);
mSaveAttributes.fill(0.f);
for (int i = 0; i < ESM::Attribute::Length; ++i)
{
mSaveAttributes[i] = 0.f;
}
mMarkedPosition.pos[0] = 0; mMarkedPosition.pos[0] = 0;
mMarkedPosition.pos[1] = 0; mMarkedPosition.pos[1] = 0;
@ -291,7 +288,7 @@ namespace MWWorld
else else
player.mHasMark = false; player.mHasMark = false;
for (int i = 0; i < ESM::Attribute::Length; ++i) for (size_t i = 0; i < mSaveAttributes.size(); ++i)
player.mSaveAttributes[i] = mSaveAttributes[i]; player.mSaveAttributes[i] = mSaveAttributes[i];
for (size_t i = 0; i < mSaveSkills.size(); ++i) for (size_t i = 0; i < mSaveSkills.size(); ++i)
player.mSaveSkills[i] = mSaveSkills[i]; player.mSaveSkills[i] = mSaveSkills[i];
@ -329,7 +326,7 @@ namespace MWWorld
mPlayer.load(player.mObject); mPlayer.load(player.mObject);
for (int i = 0; i < ESM::Attribute::Length; ++i) for (size_t i = 0; i < mSaveAttributes.size(); ++i)
mSaveAttributes[i] = player.mSaveAttributes[i]; mSaveAttributes[i] = player.mSaveAttributes[i];
for (size_t i = 0; i < mSaveSkills.size(); ++i) for (size_t i = 0; i < mSaveSkills.size(); ++i)
mSaveSkills[i] = player.mSaveSkills[i]; mSaveSkills[i] = player.mSaveSkills[i];

View file

@ -52,7 +52,7 @@ namespace MWWorld
// Saved stats prior to becoming a werewolf // Saved stats prior to becoming a werewolf
std::array<float, ESM::Skill::Length> mSaveSkills; std::array<float, ESM::Skill::Length> mSaveSkills;
float mSaveAttributes[ESM::Attribute::Length]; std::array<float, ESM::Attribute::Length> mSaveAttributes;
bool mJumping; bool mJumping;

View file

@ -10,7 +10,7 @@
</Widget> </Widget>
<!-- Attribute list --> <!-- Attribute list -->
<Widget type="ScrollView" skin="MW_ScrollView" position="0 28 216 172" name="Attributes" align="Left Top"> <Widget type="ScrollView" skin="MW_ScrollView" position="0 28 216 144" name="Attributes" align="Left Top">
<Property key="CanvasAlign" value="Left"/> <Property key="CanvasAlign" value="Left"/>
</Widget> </Widget>

View file

@ -27,9 +27,10 @@
<UserString key="VStretch" value="false"/> <UserString key="VStretch" value="false"/>
</Widget> </Widget>
<Widget type="Widget" skin="" position="0 280 420 84" name="AssignWidget"> <Widget type="ScrollView" skin="MW_ScrollView" position="0 280 420 84" name="AssignWidget">
<UserString key="HStretch" value="false"/> <UserString key="HStretch" value="false"/>
<UserString key="VStretch" value="false"/> <UserString key="VStretch" value="false"/>
<Property key="CanvasAlign" value="Left"/>
</Widget> </Widget>
<Widget type="HBox" skin="" position="0 0 330 24"> <Widget type="HBox" skin="" position="0 0 330 24">