diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index d20bb4619c..0c22114749 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -1171,9 +1171,6 @@ namespace EsmTool template <> void Record::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; diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index 88446e2d4f..e87f74218b 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -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); } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index fd0817d327..6506b13b4c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -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(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()) { - 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; diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 2b36c4577d..899acf603e 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -383,7 +383,7 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons case SelectWrapper::Function_PcAttribute: { - auto attribute = static_cast(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; } diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 5aaa1e7330..19f7d97176 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -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(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 attributes = mCreateClassDialog->getFavoriteAttributes(); - assert(attributes.size() == klass.mData.mAttribute.size()); - std::copy(attributes.begin(), attributes.end(), klass.mData.mAttribute.begin()); + std::vector 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 majorSkills = mCreateClassDialog->getMajorSkills(); std::vector minorSkills = mCreateClassDialog->getMinorSkills(); diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index d590c0cfd6..b5bd35b49c 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -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& value) override; void setValue(ESM::RefId id, const MWMechanics::SkillValue& value) override; void configureSkills(const std::vector& major, const std::vector& minor) override; @@ -56,7 +56,7 @@ namespace MWGui Resource::ResourceSystem* mResourceSystem; std::vector mPlayerMajorSkills, mPlayerMinorSkills; - std::map mPlayerAttributes; + std::map mPlayerAttributes; std::map mPlayerSkillValues; // Dialogs diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 202f18ad35..f71da8bdf5 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -260,8 +260,8 @@ namespace MWGui mSpecializationName->setCaption(specName); ToolTips::createSpecializationToolTip(mSpecializationName, specName, specialization); - mFavoriteAttribute[0]->setAttributeId(static_cast(klass->mData.mAttribute[0])); - mFavoriteAttribute[1]->setAttributeId(static_cast(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 CreateClassDialog::getFavoriteAttributes() const + std::vector CreateClassDialog::getFavoriteAttributes() const { - std::vector v; + std::vector 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) diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index 7cd7b01370..f89a0c7d88 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -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 getFavoriteAttributes() const; + std::vector getFavoriteAttributes() const; std::vector getMajorSkills() const; std::vector getMinorSkills() const; diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index 7b08d20c9b..04c3806c0e 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -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().find(mRaceId); - std::map attributes; + std::map attributes; for (const auto& [key, value] : mAttributeWidgets) attributes[key] = value->getAttributeValue(); diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index f1e764012e..6f594c60f0 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -3,7 +3,6 @@ #include "widgets.hpp" #include "windowbase.hpp" -#include #include #include @@ -38,7 +37,7 @@ namespace MWGui void setMagicka(const MWMechanics::DynamicStat& value); void setFatigue(const MWMechanics::DynamicStat& 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& major, const std::vector& minor); void setSkillValue(ESM::RefId id, const MWMechanics::SkillValue& value); @@ -90,7 +89,7 @@ namespace MWGui Widgets::MWDynamicStatPtr mHealth, mMagicka, mFatigue; - std::map mAttributeWidgets; + std::map mAttributeWidgets; std::vector mMajorSkills, mMinorSkills, mMiscSkills; std::map mSkillValues; diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index 7105112cca..a1ffce7d8b 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -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; diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index 6cd1e2a3ac..d6e8a56438 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -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); diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index 3347e9c269..5337e2f798 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -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(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().find(ESM::Skill::indexToRefId(effectInfo.mKey.mArg)); + const ESM::Skill* skill = store->get().find(effectInfo.mKey.mArg); sourcesDescription += " (" + skill->mName + ')'; } if (effect->mData.mFlags & ESM::MagicEffect::TargetAttribute) diff --git a/apps/openmw/mwgui/spellmodel.cpp b/apps/openmw/mwgui/spellmodel.cpp index 181ee28bea..f340d072e0 100644 --- a/apps/openmw/mwgui/spellmodel.cpp +++ b/apps/openmw/mwgui/spellmodel.cpp @@ -53,7 +53,8 @@ namespace MWGui if (effectId != -1) { const ESM::MagicEffect* magicEffect = store.get().find(effectId); - const ESM::Attribute* attribute = store.get().search(effect.mAttribute); + const ESM::Attribute* attribute + = store.get().search(ESM::Attribute::indexToRefId(effect.mAttribute)); const ESM::Skill* skill = store.get().search(ESM::Skill::indexToRefId(effect.mSkill)); std::string fullEffectName = MWMechanics::getMagicEffectString(*magicEffect, attribute, skill); diff --git a/apps/openmw/mwgui/statswatcher.cpp b/apps/openmw/mwgui/statswatcher.cpp index fe2493b754..90872346f7 100644 --- a/apps/openmw/mwgui/statswatcher.cpp +++ b/apps/openmw/mwgui/statswatcher.cpp @@ -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) diff --git a/apps/openmw/mwgui/statswatcher.hpp b/apps/openmw/mwgui/statswatcher.hpp index d4a9e54243..fafae4452c 100644 --- a/apps/openmw/mwgui/statswatcher.hpp +++ b/apps/openmw/mwgui/statswatcher.hpp @@ -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& 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 mWatchedAttributes; + std::map mWatchedAttributes; std::map mWatchedSkills; MWMechanics::DynamicStat mWatchedHealth; @@ -50,7 +50,7 @@ namespace MWGui std::set 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& value); void setValue(std::string_view id, const std::string& value); void setValue(std::string_view id, int value); diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index c493907b54..cb16ab6d15 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -157,7 +157,7 @@ namespace MWGui mMainWidget->castType()->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().find(skill->mData.mAttribute); + const ESM::Attribute* attr + = esmStore.get().find(ESM::Attribute::indexToRefId(skill->mData.mAttribute)); std::pair 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().find(faction->mData.mAttribute[0]); - const ESM::Attribute* attr2 = store.get().find(faction->mData.mAttribute[1]); + const ESM::Attribute* attr1 = store.get().find( + ESM::Attribute::indexToRefId(faction->mData.mAttribute[0])); + const ESM::Attribute* attr2 = store.get().find( + ESM::Attribute::indexToRefId(faction->mData.mAttribute[1])); text += "\n#{fontcolourhtml=normal}" + MyGUI::TextIterator::toTagsString(attr1->mName) + ": " + MyGUI::utility::toString(rankData.mAttribute1) + ", " diff --git a/apps/openmw/mwgui/statswindow.hpp b/apps/openmw/mwgui/statswindow.hpp index 92926c1180..cab8cd2eb2 100644 --- a/apps/openmw/mwgui/statswindow.hpp +++ b/apps/openmw/mwgui/statswindow.hpp @@ -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& 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 mMajorSkills, mMinorSkills, mMiscSkills; std::map mSkillValues; - std::map mAttributeWidgets; + std::map mAttributeWidgets; std::map> mSkillWidgetMap; std::map mFactionWidgetMap; FactionList mFactions; ///< Stores a list of factions and the current rank diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index e46aa2fc0f..6beee8d07b 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -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().find(skillId); - const ESM::Attribute* attr = store.get().find(skill->mData.mAttribute); + const ESM::Attribute* attr + = store.get().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().search(attributeId); diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index c1a6e4ef68..69f6856840 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -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); diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 2b5e64adf0..6929a7fdc8 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -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; diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index a161eed25b..debacfbed9 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -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("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().search(mEffectParams.mEffectID); const ESM::Attribute* attribute = store.get().search(mEffectParams.mAttribute); - const ESM::Skill* skill = store.get().search(ESM::Skill::indexToRefId(mEffectParams.mSkill)); + const ESM::Skill* skill = store.get().search(mEffectParams.mSkill); assert(magicEffect); diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index 03c21605f0..d562e4e07f 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -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; diff --git a/apps/openmw/mwlua/luabindings.cpp b/apps/openmw/mwlua/luabindings.cpp index ec0f09b59b..cadadc3a82 100644 --- a/apps/openmw/mwlua/luabindings.cpp +++ b/apps/openmw/mwlua/luabindings.cpp @@ -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); } diff --git a/apps/openmw/mwlua/magicbindings.cpp b/apps/openmw/mwlua/magicbindings.cpp index c1da27f279..d1dab50574 100644 --- a/apps/openmw/mwlua/magicbindings.cpp +++ b/apps/openmw/mwlua/magicbindings.cpp @@ -358,10 +358,10 @@ namespace MWLua }); effectParamsT["affectedAttribute"] = sol::readonly_property([](const ESM::ENAMstruct& params) -> sol::optional { - 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 { 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 { 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 { 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 { 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); } } diff --git a/apps/openmw/mwlua/stats.cpp b/apps/openmw/mwlua/stats.cpp index bbac88e6e2..c202e9dc33 100644 --- a/apps/openmw/mwlua/stats.cpp +++ b/apps/openmw/mwlua/stats.cpp @@ -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 sol::object get(const Context& context, std::string_view prop, G getter) const { - auto id = static_cast(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(i); - return AttributeStat{ std::move(object), index }; + ESM::RefId id = std::get(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(std::get(i)); + ESM::RefId id = std::get(i); auto& stats = ptr.getClass().getCreatureStats(ptr); auto stat = stats.getAttribute(id); float floatValue = LuaUtil::cast(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(id); + for (const ESM::Attribute& attribute : MWBase::Environment::get().getESMStore()->get()) + attributes[ESM::RefId(attribute.mId).serializeText()] = addIndexedAccessor(attribute.mId); } void addNpcStatsBindings(sol::table& npc, const Context& context) diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 8294f915e8..decbae765b 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -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); } diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index c804e0d1da..87497d9d7a 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -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); diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index 4c7dc14417..914de6da2b 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -45,9 +45,10 @@ std::set 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 MWMechanics::Alchemy::effectsDescription(const MWWorld: if (effectID != -1) { - const ESM::Attribute* attribute = store->get().search(data.mAttributes[i]); + const ESM::Attribute* attribute + = store->get().search(ESM::Attribute::indexToRefId(data.mAttributes[i])); const ESM::Skill* skill = store->get().search(ESM::Skill::indexToRefId(data.mSkills[i])); std::string effect = getMagicEffectString(*mgef.find(effectID), attribute, skill); diff --git a/apps/openmw/mwmechanics/autocalcspell.cpp b/apps/openmw/mwmechanics/autocalcspell.cpp index 13fcef073f..6581aacdd2 100644 --- a/apps/openmw/mwmechanics/autocalcspell.cpp +++ b/apps/openmw/mwmechanics/autocalcspell.cpp @@ -27,7 +27,7 @@ namespace MWMechanics }; std::vector autoCalcNpcSpells(const std::map& actorSkills, - const std::map& actorAttributes, const ESM::Race* race) + const std::map& actorAttributes, const ESM::Race* race) { const MWWorld::Store& gmst = MWBase::Environment::get().getESMStore()->get(); @@ -136,7 +136,7 @@ namespace MWMechanics } std::vector autoCalcPlayerSpells(const std::map& actorSkills, - const std::map& actorAttributes, const ESM::Race* race) + const std::map& 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& actorSkills, - const std::map& actorAttributes) + const std::map& 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& actorSkills, - const std::map& actorAttributes, ESM::RefId effectiveSchool) + const std::map& actorAttributes, ESM::RefId effectiveSchool) { if (spell->mData.mType != ESM::Spell::ST_Spell) return 100.f; diff --git a/apps/openmw/mwmechanics/autocalcspell.hpp b/apps/openmw/mwmechanics/autocalcspell.hpp index 7edfd8b75e..c7c8d5f733 100644 --- a/apps/openmw/mwmechanics/autocalcspell.hpp +++ b/apps/openmw/mwmechanics/autocalcspell.hpp @@ -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 autoCalcNpcSpells(const std::map& actorSkills, - const std::map& actorAttributes, const ESM::Race* race); + const std::map& actorAttributes, const ESM::Race* race); std::vector autoCalcPlayerSpells(const std::map& actorSkills, - const std::map& actorAttributes, const ESM::Race* race); + const std::map& actorAttributes, const ESM::Race* race); // Helpers bool attrSkillCheck(const ESM::Spell* spell, const std::map& actorSkills, - const std::map& actorAttributes); + const std::map& actorAttributes); void calcWeakestSchool(const ESM::Spell* spell, const std::map& actorSkills, ESM::RefId& effectiveSchool, float& skillTerm); float calcAutoCastChance(const ESM::Spell* spell, const std::map& actorSkills, - const std::map& actorAttributes, ESM::RefId effectiveSchool); + const std::map& actorAttributes, ESM::RefId effectiveSchool); } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 756ac1ef39..757bdf7c49 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -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(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(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]); diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index ffe6e40a91..f0a834bd33 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -40,7 +40,7 @@ namespace MWMechanics { static int sActorId; DrawState mDrawState; - std::map mAttributes; + std::map mAttributes; DynamicStat 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& 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& value); @@ -294,7 +294,7 @@ namespace MWMechanics bool wasTeleported() const { return mTeleported; } void setTeleported(bool v) { mTeleported = v; } - const std::map getAttributes() const { return mAttributes; } + const std::map getAttributes() const { return mAttributes; } }; } diff --git a/apps/openmw/mwmechanics/magiceffects.cpp b/apps/openmw/mwmechanics/magiceffects.cpp index b64799fe08..0d626c9e11 100644 --- a/apps/openmw/mwmechanics/magiceffects.cpp +++ b/apps/openmw/mwmechanics/magiceffects.cpp @@ -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().search(mId); - return getMagicEffectString(*magicEffect, store->get().search(mArg), - store->get().search(ESM::Skill::indexToRefId(mArg))); + return getMagicEffectString( + *magicEffect, store->get().search(mArg), store->get().search(mArg)); } bool operator<(const EffectKey& left, const EffectKey& right) diff --git a/apps/openmw/mwmechanics/magiceffects.hpp b/apps/openmw/mwmechanics/magiceffects.hpp index e03f3db21f..b9831c0250 100644 --- a/apps/openmw/mwmechanics/magiceffects.hpp +++ b/apps/openmw/mwmechanics/magiceffects.hpp @@ -5,6 +5,8 @@ #include #include +#include + 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) { diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index f49e8b1e09..29fe4dd2a6 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -150,7 +150,8 @@ namespace MWMechanics for (const ESM::Attribute& attribute : esmStore.get()) { - 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(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) diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index ecd0770d35..5c62eb8276 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -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(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]; diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 39db2dd30a..cae94414c9 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -36,7 +36,7 @@ namespace MWMechanics std::set mExpelled; std::map mFactionReputation; int mLevelProgress; // 0-10 - std::map + std::map mSkillIncreases; // number of skill increases for each attribute (resets after leveling up) std::vector mSpecIncreases; // number of skill increases for each specialization (accumulates throughout // the entire game) diff --git a/apps/openmw/mwmechanics/spelleffects.cpp b/apps/openmw/mwmechanics/spelleffects.cpp index ea2f2a5267..253c4015b5 100644 --- a/apps/openmw/mwmechanics/spelleffects.cpp +++ b/apps/openmw/mwmechanics/spelleffects.cpp @@ -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(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(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(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(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(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) { diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index 94f24800a2..776381e6b2 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -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; diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index f533926604..12da7cdde8 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -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(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(info.mArg); + auto id = ESM::Attribute::indexToRefId(info.mArg); AttributeValue attr = creatureStats->getAttribute(id); attr.setModifier(attr.getModifier() + info.mMagnitude); attr.damage(info.mMagnitude); diff --git a/apps/openmw/mwmechanics/summoning.cpp b/apps/openmw/mwmechanics/summoning.cpp index ceac555802..2af5a2dc83 100644 --- a/apps/openmw/mwmechanics/summoning.cpp +++ b/apps/openmw/mwmechanics/summoning.cpp @@ -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); diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 222820f3bf..0363f21fa6 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -109,10 +109,10 @@ namespace MWScript template 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 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 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(i); + ESM::RefId id = ESM::Attribute::indexToRefId(i); interpreter.installSegment5>(Compiler::Stats::opcodeGetAttribute + i, id); interpreter.installSegment5>( Compiler::Stats::opcodeGetAttributeExplicit + i, id); diff --git a/apps/openmw/mwworld/magiceffects.cpp b/apps/openmw/mwworld/magiceffects.cpp index ec9d0029c5..8b7ad79db2 100644 --- a/apps/openmw/mwworld/magiceffects.cpp +++ b/apps/openmw/mwworld/magiceffects.cpp @@ -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(actorId)); effect.mFlags |= ESM::ActiveEffect::Flag_Applied | ESM::ActiveEffect::Flag_Remove; found = true; break; diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 5fc020eab4..0d7afb559f 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -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(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(i); + auto id = ESM::Attribute::indexToRefId(i); auto attribute = npcStats.getAttribute(id); attribute.restore(attribute.getDamage()); attribute.setModifier(mSaveAttributes[i] - attribute.getBase()); diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index 3d88851672..a4b8548e52 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -1041,87 +1041,50 @@ namespace MWWorld // Attribute //========================================================================= - Store::Store() - { - mStatic.reserve(ESM::Attribute::Length); - } - const ESM::Attribute* Store::search(size_t index) const - { - if (index >= mStatic.size()) - { - return nullptr; - } - return &mStatic[index]; - } - - const ESM::Attribute* Store::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::setUp(const MWWorld::Store& 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::getSize() const - { - return mStatic.size(); - } - Store::iterator Store::begin() const - { - return mStatic.begin(); - } - Store::iterator Store::end() const - { - return mStatic.end(); - } // Dialogue //========================================================================= @@ -1339,7 +1302,7 @@ namespace MWWorld template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; -// template class MWWorld::Store; +template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; template class MWWorld::TypedDynamicStore; diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index bbf095fed1..5cb0d6935c 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -29,7 +30,6 @@ namespace ESM { - struct Attribute; struct LandTexture; struct MagicEffect; struct WeaponType; @@ -492,25 +492,14 @@ namespace MWWorld }; template <> - class Store : public IndexedStore + class Store : public TypedDynamicStore { - std::vector mStatic; + using TypedDynamicStore::setUp; public: - typedef std::vector::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& settings); - - size_t getSize() const; - iterator begin() const; - iterator end() const; }; template <> diff --git a/apps/openmw_test_suite/mwworld/test_store.cpp b/apps/openmw_test_suite/mwworld/test_store.cpp index f6faba0f13..d8890bc5ab 100644 --- a/apps/openmw_test_suite/mwworld/test_store.cpp +++ b/apps/openmw_test_suite/mwworld/test_store.cpp @@ -456,6 +456,8 @@ namespace { refId = ESM::RefId::esm3ExteriorCell(0, 0); } + else if constexpr (std::is_same_v) + refId = ESM::Attribute::Strength; else if constexpr (std::is_same_v) refId = ESM::Skill::Block; else @@ -485,6 +487,12 @@ namespace ESM::Dialogue* dialogue = nullptr; MWWorld::ESMStore esmStore; + if constexpr (std::is_same_v) + { + 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 == 39); + static_assert(std::tuple_size_v == 40); INSTANTIATE_TYPED_TEST_SUITE_P( RecordTypesTest, StoreSaveLoadTest, typename AsTestingTypes::Type); diff --git a/components/esm/attr.cpp b/components/esm/attr.cpp index 38119dbc0e..c8fc042849 100644 --- a/components/esm/attr.cpp +++ b/components/esm/attr.cpp @@ -1,25 +1,56 @@ #include "attr.hpp" -#include -#include -using namespace ESM; +#include +#include -const std::string Attribute::sAttributeNames[Attribute::Length] = { - "Strength", - "Intelligence", - "Willpower", - "Agility", - "Speed", - "Endurance", - "Personality", - "Luck", -}; +#include -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"); + } } diff --git a/components/esm/attr.hpp b/components/esm/attr.hpp index 9029306376..a28a90f14c 100644 --- a/components/esm/attr.hpp +++ b/components/esm/attr.hpp @@ -2,9 +2,15 @@ #define OPENMW_ESM_ATTR_H #include +#include + +#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 diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 617873d8e1..96d70f6fea 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -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 diff --git a/components/esm3/activespells.cpp b/components/esm3/activespells.cpp index 27d260fe72..8ce47b7719 100644 --- a/components/esm3/activespells.cpp +++ b/components/esm3/activespells.cpp @@ -2,11 +2,93 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "loadmgef.hpp" +#include "loadskil.hpp" + +#include + +#include 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& 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(&mArg)) + return *id; + return {}; + } + + int ActiveEffect::getActorId() const + { + if (const auto* id = std::get_if(&mArg)) + return *id; + return -1; + } } diff --git a/components/esm3/activespells.hpp b/components/esm3/activespells.hpp index 531a39b4c7..0e4e01eda3 100644 --- a/components/esm3/activespells.hpp +++ b/components/esm3/activespells.hpp @@ -7,6 +7,7 @@ #include "timestamp.hpp" #include +#include #include namespace ESM @@ -32,11 +33,14 @@ namespace ESM float mMagnitude; float mMinMagnitude; float mMaxMagnitude; - int32_t mArg; // skill or attribute + std::variant 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