diff --git a/apps/openmw/mwclass/actor.cpp b/apps/openmw/mwclass/actor.cpp index 4157a03bc0..152a4bbba9 100644 --- a/apps/openmw/mwclass/actor.cpp +++ b/apps/openmw/mwclass/actor.cpp @@ -43,20 +43,13 @@ namespace MWClass return; MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager(); - switch (shield->getClass().getEquipmentSkill(*shield)) - { - case ESM::Skill::LightArmor: - sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Light Armor Hit"), 1.0f, 1.0f); - break; - case ESM::Skill::MediumArmor: - sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Medium Armor Hit"), 1.0f, 1.0f); - break; - case ESM::Skill::HeavyArmor: - sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Heavy Armor Hit"), 1.0f, 1.0f); - break; - default: - return; - } + const ESM::RefId skill = shield->getClass().getEquipmentSkill(*shield); + if (skill == ESM::Skill::LightArmor) + sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Light Armor Hit"), 1.0f, 1.0f); + else if (skill == ESM::Skill::MediumArmor) + sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Medium Armor Hit"), 1.0f, 1.0f); + else if (skill == ESM::Skill::HeavyArmor) + sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Heavy Armor Hit"), 1.0f, 1.0f); } osg::Vec3f Actor::getRotationVector(const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 416a55336a..8b23349f09 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -112,7 +112,7 @@ namespace MWClass return std::make_pair(slots_, false); } - int Armor::getEquipmentSkill(const MWWorld::ConstPtr& ptr) const + ESM::RefId Armor::getEquipmentSkill(const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef* ref = ptr.get(); @@ -150,7 +150,7 @@ namespace MWClass } if (typeGmst.empty()) - return -1; + return {}; const MWWorld::Store& gmst = MWBase::Environment::get().getESMStore()->get(); @@ -178,7 +178,7 @@ namespace MWClass const ESM::RefId& Armor::getUpSoundId(const MWWorld::ConstPtr& ptr) const { - int es = getEquipmentSkill(ptr); + const ESM::RefId es = getEquipmentSkill(ptr); static const ESM::RefId lightUp = ESM::RefId::stringRefId("Item Armor Light Up"); static const ESM::RefId mediumUp = ESM::RefId::stringRefId("Item Armor Medium Up"); static const ESM::RefId heavyUp = ESM::RefId::stringRefId("Item Armor Heavy Up"); @@ -193,7 +193,7 @@ namespace MWClass const ESM::RefId& Armor::getDownSoundId(const MWWorld::ConstPtr& ptr) const { - int es = getEquipmentSkill(ptr); + const ESM::RefId es = getEquipmentSkill(ptr); static const ESM::RefId lightDown = ESM::RefId::stringRefId("Item Armor Light Down"); static const ESM::RefId mediumDown = ESM::RefId::stringRefId("Item Armor Medium Down"); static const ESM::RefId heavyDown = ESM::RefId::stringRefId("Item Armor Heavy Down"); @@ -232,7 +232,7 @@ namespace MWClass } else { - int armorType = getEquipmentSkill(ptr); + const ESM::RefId armorType = getEquipmentSkill(ptr); if (armorType == ESM::Skill::LightArmor) typeText = "#{sLight}"; else if (armorType == ESM::Skill::MediumArmor) @@ -297,7 +297,7 @@ namespace MWClass { const MWWorld::LiveCellRef* ref = ptr.get(); - int armorSkillType = getEquipmentSkill(ptr); + const ESM::RefId armorSkillType = getEquipmentSkill(ptr); float armorSkill = actor.getClass().getSkill(actor, armorSkillType); int iBaseArmorSkill = MWBase::Environment::get() diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index 0b01b643ba..d464360623 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -41,9 +41,7 @@ namespace MWClass ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? - int getEquipmentSkill(const MWWorld::ConstPtr& ptr) const override; - /// Return the index of the skill this item corresponds to when equipped or -1, if there is - /// no such skill. + ESM::RefId getEquipmentSkill(const MWWorld::ConstPtr& ptr) const override; MWGui::ToolTipInfo getToolTipInfo(const MWWorld::ConstPtr& ptr, int count) const override; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 06935ad2bd..fec0b4260f 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -101,14 +101,14 @@ namespace MWClass return std::make_pair(slots_, false); } - int Clothing::getEquipmentSkill(const MWWorld::ConstPtr& ptr) const + ESM::RefId Clothing::getEquipmentSkill(const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef* ref = ptr.get(); if (ref->mBase->mData.mType == ESM::Clothing::Shoes) return ESM::Skill::Unarmored; - return -1; + return {}; } int Clothing::getValue(const MWWorld::ConstPtr& ptr) const diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index 1c758ac68b..a1e8348713 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -33,9 +33,7 @@ namespace MWClass ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? - int getEquipmentSkill(const MWWorld::ConstPtr& ptr) const override; - /// Return the index of the skill this item corresponds to when equipped or -1, if there is - /// no such skill. + ESM::RefId getEquipmentSkill(const MWWorld::ConstPtr& ptr) const override; MWGui::ToolTipInfo getToolTipInfo(const MWWorld::ConstPtr& ptr, int count) const override; ///< @return the content of the tool tip to be displayed. raises exception if the object has no tooltip. diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 39de4a5b1d..1ff9c6375a 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -762,11 +762,11 @@ namespace MWClass throw std::runtime_error("Unexpected soundgen type: " + std::string(name)); } - float Creature::getSkill(const MWWorld::Ptr& ptr, int skill) const + float Creature::getSkill(const MWWorld::Ptr& ptr, ESM::RefId id) const { MWWorld::LiveCellRef* ref = ptr.get(); - const ESM::Skill* skillRecord = MWBase::Environment::get().getESMStore()->get().find(skill); + const ESM::Skill* skillRecord = MWBase::Environment::get().getESMStore()->get().find(id); switch (skillRecord->mData.mSpecialization) { diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 465c28aaaf..bd7101e93d 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -115,7 +115,7 @@ namespace MWClass bool canSwim(const MWWorld::ConstPtr& ptr) const override; bool canWalk(const MWWorld::ConstPtr& ptr) const override; - float getSkill(const MWWorld::Ptr& ptr, int skill) const override; + float getSkill(const MWWorld::Ptr& ptr, ESM::RefId id) const override; /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) int getBloodTexture(const MWWorld::ConstPtr& ptr) const override; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 9e3ca67759..5f475ec61d 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -111,20 +111,18 @@ namespace { float modifierSum = 0; - for (int j = 0; j < ESM::Skill::Length; ++j) + for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get()) { - const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get().find(j); - - if (skill->mData.mAttribute != attribute) + if (skill.mData.mAttribute != attribute) continue; // is this a minor or major skill? float add = 0.2f; for (const auto& skills : class_->mData.mSkills) { - if (skills[0] == j) + if (skills[0] == skill.mIndex) add = 0.5; - if (skills[1] == j) + if (skills[1] == skill.mIndex) add = 1.0; } modifierSum += add; @@ -181,15 +179,15 @@ namespace for (const auto& skills : class_->mData.mSkills) { - int index = skills[i]; - if (index >= 0 && index < ESM::Skill::Length) + ESM::RefId id = ESM::Skill::indexToRefId(skills[i]); + if (!id.empty()) { - npcStats.getSkill(index).setBase(npcStats.getSkill(index).getBase() + bonus); + npcStats.getSkill(id).setBase(npcStats.getSkill(id).getBase() + bonus); } } } - for (int skillIndex = 0; skillIndex < ESM::Skill::Length; ++skillIndex) + for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get()) { float majorMultiplier = 0.1f; float specMultiplier = 0.0f; @@ -198,14 +196,14 @@ namespace int specBonus = 0; auto bonusIt = std::find_if(race->mData.mBonus.begin(), race->mData.mBonus.end(), - [skillIndex](const auto& bonus) { return bonus.mSkill == skillIndex; }); + [&](const auto& bonus) { return bonus.mSkill == skill.mIndex; }); if (bonusIt != race->mData.mBonus.end()) raceBonus = bonusIt->mBonus; for (const auto& skills : class_->mData.mSkills) { // is this a minor or major skill? - if (std::find(skills.begin(), skills.end(), skillIndex) != skills.end()) + if (std::find(skills.begin(), skills.end(), skill.mIndex) != skills.end()) { majorMultiplier = 1.0f; break; @@ -213,30 +211,25 @@ namespace } // is this skill in the same Specialization as the class? - const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get().find(skillIndex); - if (skill->mData.mSpecialization == class_->mData.mSpecialization) + if (skill.mData.mSpecialization == class_->mData.mSpecialization) { specMultiplier = 0.5f; specBonus = 5; } - npcStats.getSkill(skillIndex) - .setBase(std::min(round_ieee_754(npcStats.getSkill(skillIndex).getBase() + 5 + raceBonus + specBonus - + (int(level) - 1) * (majorMultiplier + specMultiplier)), + npcStats.getSkill(skill.mId).setBase( + std::min(round_ieee_754(npcStats.getSkill(skill.mId).getBase() + 5 + raceBonus + specBonus + + (int(level) - 1) * (majorMultiplier + specMultiplier)), 100)); // Must gracefully handle level 0 } - int skills[ESM::Skill::Length]; - for (int i = 0; i < ESM::Skill::Length; ++i) - skills[i] = npcStats.getSkill(i).getBase(); - int attributes[ESM::Attribute::Length]; for (int i = 0; i < ESM::Attribute::Length; ++i) attributes[i] = npcStats.getAttribute(i).getBase(); if (!spellsInitialised) { - std::vector spells = MWMechanics::autoCalcNpcSpells(skills, attributes, race); + std::vector spells = MWMechanics::autoCalcNpcSpells(npcStats.getSkills(), attributes, race); npcStats.getSpells().addAllToInstance(spells); } } @@ -315,7 +308,7 @@ namespace MWClass gold = ref->mBase->mNpdt.mGold; for (size_t i = 0; i < ref->mBase->mNpdt.mSkills.size(); ++i) - data->mNpcStats.getSkill(i).setBase(ref->mBase->mNpdt.mSkills[i]); + data->mNpcStats.getSkill(ESM::Skill::indexToRefId(i)).setBase(ref->mBase->mNpdt.mSkills[i]); data->mNpcStats.setAttribute(ESM::Attribute::Strength, ref->mBase->mNpdt.mStrength); data->mNpcStats.setAttribute(ESM::Attribute::Intelligence, ref->mBase->mNpdt.mIntelligence); @@ -589,7 +582,7 @@ namespace MWClass victim = result.first; hitPosition = result.second; - int weapskill = ESM::Skill::HandToHand; + ESM::RefId weapskill = ESM::Skill::HandToHand; if (!weapon.isEmpty()) weapskill = weapon.getClass().getEquipmentSkill(weapon); @@ -658,7 +651,7 @@ namespace MWClass if (ptr == MWMechanics::getPlayer()) { - int weapskill = ESM::Skill::HandToHand; + ESM::RefId weapskill = ESM::Skill::HandToHand; if (!weapon.isEmpty()) weapskill = weapon.getClass().getEquipmentSkill(weapon); skillUsageSucceeded(ptr, weapskill, 0); @@ -849,21 +842,16 @@ namespace MWClass armor = *inv.unequipItem(armor); } + ESM::RefId skill = armor.getClass().getEquipmentSkill(armor); if (ptr == MWMechanics::getPlayer()) - skillUsageSucceeded(ptr, armor.getClass().getEquipmentSkill(armor), 0); + skillUsageSucceeded(ptr, skill, 0); - switch (armor.getClass().getEquipmentSkill(armor)) - { - case ESM::Skill::LightArmor: - sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Light Armor Hit"), 1.0f, 1.0f); - break; - case ESM::Skill::MediumArmor: - sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Medium Armor Hit"), 1.0f, 1.0f); - break; - case ESM::Skill::HeavyArmor: - sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Heavy Armor Hit"), 1.0f, 1.0f); - break; - } + if (skill == ESM::Skill::LightArmor) + sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Light Armor Hit"), 1.0f, 1.0f); + else if (skill == ESM::Skill::MediumArmor) + sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Medium Armor Hit"), 1.0f, 1.0f); + else if (skill == ESM::Skill::HeavyArmor) + sndMgr->playSound3D(ptr, ESM::RefId::stringRefId("Heavy Armor Hit"), 1.0f, 1.0f); } else if (ptr == MWMechanics::getPlayer()) skillUsageSucceeded(ptr, ESM::Skill::Unarmored, 0); @@ -1161,7 +1149,7 @@ namespace MWClass return cast.cast(recordId); } - void Npc::skillUsageSucceeded(const MWWorld::Ptr& ptr, int skill, int usageType, float extraFactor) const + void Npc::skillUsageSucceeded(const MWWorld::Ptr& ptr, ESM::RefId skill, int usageType, float extraFactor) const { MWMechanics::NpcStats& stats = getNpcStats(ptr); @@ -1311,18 +1299,13 @@ namespace MWClass if (boots == inv.end() || boots->getType() != ESM::Armor::sRecordId) return (name == "left") ? footBareLeft : footBareRight; - switch (boots->getClass().getEquipmentSkill(*boots)) - { - case ESM::Skill::LightArmor: - return (name == "left") ? footLightLeft : footLightRight; - break; - case ESM::Skill::MediumArmor: - return (name == "left") ? footMediumLeft : footMediumRight; - break; - case ESM::Skill::HeavyArmor: - return (name == "left") ? footHeavyLeft : footHeavyRight; - break; - } + ESM::RefId skill = boots->getClass().getEquipmentSkill(*boots); + if (skill == ESM::Skill::LightArmor) + return (name == "left") ? footLightLeft : footLightRight; + else if (skill == ESM::Skill::MediumArmor) + return (name == "left") ? footMediumLeft : footMediumRight; + else if (skill == ESM::Skill::HeavyArmor) + return (name == "left") ? footHeavyLeft : footHeavyRight; } return ESM::RefId(); } @@ -1355,9 +1338,9 @@ namespace MWClass return MWWorld::Ptr(cell.insert(ref), &cell); } - float Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const + float Npc::getSkill(const MWWorld::Ptr& ptr, ESM::RefId id) const { - return getNpcStats(ptr).getSkill(skill).getModified(); + return getNpcStats(ptr).getSkill(id).getModified(); } int Npc::getBloodTexture(const MWWorld::ConstPtr& ptr) const diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 9b8e229890..9b53143c4d 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -120,7 +120,7 @@ namespace MWClass /// @param rendering Indicates if the scale to adjust is for the rendering mesh, or for the collision mesh void skillUsageSucceeded( - const MWWorld::Ptr& ptr, int skill, int usageType, float extraFactor = 1.f) const override; + const MWWorld::Ptr& ptr, ESM::RefId skill, int usageType, float extraFactor = 1.f) const override; ///< Inform actor \a ptr that a skill use has succeeded. bool isEssential(const MWWorld::ConstPtr& ptr) const override; @@ -134,7 +134,7 @@ namespace MWClass std::string getModel(const MWWorld::ConstPtr& ptr) const override; - float getSkill(const MWWorld::Ptr& ptr, int skill) const override; + float getSkill(const MWWorld::Ptr& ptr, ESM::RefId id) const override; /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) int getBloodTexture(const MWWorld::ConstPtr& ptr) const override; diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 4eddc37f73..74e26a3b83 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -108,7 +108,7 @@ namespace MWClass return std::make_pair(slots_, stack); } - int Weapon::getEquipmentSkill(const MWWorld::ConstPtr& ptr) const + ESM::RefId Weapon::getEquipmentSkill(const MWWorld::ConstPtr& ptr) const { const MWWorld::LiveCellRef* ref = ptr.get(); int type = ref->mBase->mData.mType; diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index 336c1a89eb..110069341d 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -42,9 +42,7 @@ namespace MWClass ///< \return first: Return IDs of the slot this object can be equipped in; second: can object /// stay stacked when equipped? - int getEquipmentSkill(const MWWorld::ConstPtr& ptr) const override; - /// Return the index of the skill this item corresponds to when equipped or -1, if there is - /// no such skill. + ESM::RefId getEquipmentSkill(const MWWorld::ConstPtr& ptr) const override; int getValue(const MWWorld::ConstPtr& ptr) const override; ///< Return trade value of the object. Throws an exception, if the object can't be traded. diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index 31a76e949c..6858c76cfe 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/environment.hpp" @@ -385,9 +386,10 @@ int MWDialogue::Filter::getSelectStructInteger(const SelectWrapper& select) cons return player.getClass().getCreatureStats(player).getAttribute(select.getArgument()).getModified(); case SelectWrapper::Function_PcSkill: - - return static_cast(player.getClass().getNpcStats(player).getSkill(select.getArgument()).getModified()); - + { + ESM::RefId skill = ESM::Skill::indexToRefId(select.getArgument()); + return static_cast(player.getClass().getNpcStats(player).getSkill(skill).getModified()); + } case SelectWrapper::Function_FriendlyHit: { int hits = mActor.getClass().getCreatureStats(mActor).getFriendlyHits(); diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 586e8b766d..dc1608b9c7 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -99,7 +99,7 @@ namespace MWGui mPlayerAttributes.emplace(attribute.mId, MWMechanics::AttributeValue()); for (const auto& skill : store.get()) - mPlayerSkillValues.emplace(skill.second.mIndex, MWMechanics::SkillValue()); + mPlayerSkillValues.emplace(skill.mId, MWMechanics::SkillValue()); } void CharacterCreation::setValue(std::string_view id, const MWMechanics::AttributeValue& value) @@ -138,14 +138,14 @@ namespace MWGui } } - void CharacterCreation::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) + void CharacterCreation::setValue(ESM::RefId id, const MWMechanics::SkillValue& value) { - mPlayerSkillValues[parSkill] = value; + mPlayerSkillValues[id] = value; if (mReviewDialog) - mReviewDialog->setSkillValue(parSkill, value); + mReviewDialog->setSkillValue(id, value); } - void CharacterCreation::configureSkills(const SkillList& major, const SkillList& minor) + void CharacterCreation::configureSkills(const std::vector& major, const std::vector& minor) { if (mReviewDialog) mReviewDialog->configureSkills(major, minor); @@ -275,10 +275,9 @@ namespace MWGui mReviewDialog->setAttribute( static_cast(attributePair.first), attributePair.second); } - for (auto& skillPair : mPlayerSkillValues) + for (const auto& [skill, value] : mPlayerSkillValues) { - mReviewDialog->setSkillValue( - static_cast(skillPair.first), skillPair.second); + mReviewDialog->setSkillValue(skill, value); } mReviewDialog->configureSkills(mPlayerMajorSkills, mPlayerMinorSkills); @@ -476,14 +475,14 @@ namespace MWGui assert(attributes.size() == klass.mData.mAttribute.size()); std::copy(attributes.begin(), attributes.end(), klass.mData.mAttribute.begin()); - std::vector majorSkills = mCreateClassDialog->getMajorSkills(); - std::vector minorSkills = mCreateClassDialog->getMinorSkills(); + std::vector majorSkills = mCreateClassDialog->getMajorSkills(); + std::vector minorSkills = mCreateClassDialog->getMinorSkills(); assert(majorSkills.size() >= klass.mData.mSkills.size()); assert(minorSkills.size() >= klass.mData.mSkills.size()); for (size_t i = 0; i < klass.mData.mSkills.size(); ++i) { - klass.mData.mSkills[i][1] = majorSkills[i]; - klass.mData.mSkills[i][0] = minorSkills[i]; + klass.mData.mSkills[i][1] = majorSkills[i].getIf()->getValue(); + klass.mData.mSkills[i][0] = minorSkills[i].getIf()->getValue(); } MWBase::Environment::get().getMechanicsManager()->setPlayerClass(klass); diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index e2e6c3ca26..f9e76d1fc6 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -38,8 +38,6 @@ namespace MWGui class CharacterCreation : public StatsListener { public: - typedef std::vector SkillList; - CharacterCreation(osg::Group* parent, Resource::ResourceSystem* resourceSystem); virtual ~CharacterCreation(); @@ -48,8 +46,8 @@ namespace MWGui void setValue(std::string_view id, const MWMechanics::AttributeValue& value) override; void setValue(std::string_view id, const MWMechanics::DynamicStat& value) override; - void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) override; - void configureSkills(const SkillList& major, const SkillList& minor) override; + void setValue(ESM::RefId id, const MWMechanics::SkillValue& value) override; + void configureSkills(const std::vector& major, const std::vector& minor) override; void onFrame(float duration); @@ -57,9 +55,9 @@ namespace MWGui osg::Group* mParent; Resource::ResourceSystem* mResourceSystem; - SkillList mPlayerMajorSkills, mPlayerMinorSkills; + std::vector mPlayerMajorSkills, mPlayerMinorSkills; std::map mPlayerAttributes; - std::map mPlayerSkillValues; + std::map mPlayerSkillValues; // Dialogs std::unique_ptr mNameDialog; diff --git a/apps/openmw/mwgui/class.cpp b/apps/openmw/mwgui/class.cpp index 300683472f..b26d750191 100644 --- a/apps/openmw/mwgui/class.cpp +++ b/apps/openmw/mwgui/class.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -266,10 +267,12 @@ namespace MWGui for (size_t i = 0; i < klass->mData.mSkills.size(); ++i) { - mMinorSkill[i]->setSkillNumber(klass->mData.mSkills[i][0]); - mMajorSkill[i]->setSkillNumber(klass->mData.mSkills[i][1]); - ToolTips::createSkillToolTip(mMinorSkill[i], klass->mData.mSkills[i][0]); - ToolTips::createSkillToolTip(mMajorSkill[i], klass->mData.mSkills[i][1]); + ESM::RefId minor = ESM::Skill::indexToRefId(klass->mData.mSkills[i][0]); + ESM::RefId major = ESM::Skill::indexToRefId(klass->mData.mSkills[i][1]); + mMinorSkill[i]->setSkillId(minor); + mMajorSkill[i]->setSkillId(major); + ToolTips::createSkillToolTip(mMinorSkill[i], minor); + ToolTips::createSkillToolTip(mMajorSkill[i], major); } setClassImage(mClassImage, mCurrentClassId); @@ -514,24 +517,24 @@ namespace MWGui return v; } - std::vector CreateClassDialog::getMajorSkills() const + std::vector CreateClassDialog::getMajorSkills() const { - std::vector v; - v.reserve(5); - for (int i = 0; i < 5; i++) + std::vector v; + v.reserve(mMajorSkill.size()); + for (const auto& widget : mMajorSkill) { - v.push_back(mMajorSkill[i]->getSkillId()); + v.push_back(widget->getSkillId()); } return v; } - std::vector CreateClassDialog::getMinorSkills() const + std::vector CreateClassDialog::getMinorSkills() const { - std::vector v; - v.reserve(5); - for (int i = 0; i < 5; i++) + std::vector v; + v.reserve(mMinorSkill.size()); + for (const auto& widget : mMinorSkill) { - v.push_back(mMinorSkill[i]->getSkillId()); + v.push_back(widget->getSkillId()); } return v; } @@ -624,7 +627,7 @@ namespace MWGui void CreateClassDialog::onSkillSelected() { - ESM::Skill::SkillEnum id = mSkillDialog->getSkillId(); + ESM::RefId id = mSkillDialog->getSkillId(); // Avoid duplicate skills by swapping any skill field that matches the selected one for (Widgets::MWSkillPtr& skill : mSkills) @@ -793,43 +796,33 @@ namespace MWGui // Centre dialog center(); - for (int i = 0; i < 9; i++) + std::array, 3> specializations; + getWidget(specializations[ESM::Class::Combat].first, "CombatSkills"); + getWidget(specializations[ESM::Class::Magic].first, "MagicSkills"); + getWidget(specializations[ESM::Class::Stealth].first, "StealthSkills"); + for (auto& [widget, coord] : specializations) { - char theIndex = '0' + i; - getWidget(mCombatSkill[i], std::string("CombatSkill").append(1, theIndex)); - getWidget(mMagicSkill[i], std::string("MagicSkill").append(1, theIndex)); - getWidget(mStealthSkill[i], std::string("StealthSkill").append(1, theIndex)); + coord.width = widget->getCoord().width; + coord.height = 18; + while (widget->getChildCount() > 0) + MyGUI::Gui::getInstance().destroyWidget(widget->getChildAt(0)); } - - struct + for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get()) { - Widgets::MWSkillPtr widget; - ESM::Skill::SkillEnum skillId; - } mSkills[3][9] - = { { { mCombatSkill[0], ESM::Skill::Block }, { mCombatSkill[1], ESM::Skill::Armorer }, - { mCombatSkill[2], ESM::Skill::MediumArmor }, { mCombatSkill[3], ESM::Skill::HeavyArmor }, - { mCombatSkill[4], ESM::Skill::BluntWeapon }, { mCombatSkill[5], ESM::Skill::LongBlade }, - { mCombatSkill[6], ESM::Skill::Axe }, { mCombatSkill[7], ESM::Skill::Spear }, - { mCombatSkill[8], ESM::Skill::Athletics } }, - { { mMagicSkill[0], ESM::Skill::Enchant }, { mMagicSkill[1], ESM::Skill::Destruction }, - { mMagicSkill[2], ESM::Skill::Alteration }, { mMagicSkill[3], ESM::Skill::Illusion }, - { mMagicSkill[4], ESM::Skill::Conjuration }, { mMagicSkill[5], ESM::Skill::Mysticism }, - { mMagicSkill[6], ESM::Skill::Restoration }, { mMagicSkill[7], ESM::Skill::Alchemy }, - { mMagicSkill[8], ESM::Skill::Unarmored } }, - { { mStealthSkill[0], ESM::Skill::Security }, { mStealthSkill[1], ESM::Skill::Sneak }, - { mStealthSkill[2], ESM::Skill::Acrobatics }, { mStealthSkill[3], ESM::Skill::LightArmor }, - { mStealthSkill[4], ESM::Skill::ShortBlade }, { mStealthSkill[5], ESM::Skill::Marksman }, - { mStealthSkill[6], ESM::Skill::Mercantile }, { mStealthSkill[7], ESM::Skill::Speechcraft }, - { mStealthSkill[8], ESM::Skill::HandToHand } } }; - - for (int spec = 0; spec < 3; ++spec) + auto& [widget, coord] = specializations[skill.mData.mSpecialization]; + auto* skillWidget + = widget->createWidget("MW_StatNameButton", coord, MyGUI::Align::Default); + coord.top += coord.height; + skillWidget->setSkillId(skill.mId); + skillWidget->eventClicked += MyGUI::newDelegate(this, &SelectSkillDialog::onSkillClicked); + ToolTips::createSkillToolTip(skillWidget, skill.mId); + } + for (const auto& [widget, coord] : specializations) { - for (int i = 0; i < 9; ++i) - { - mSkills[spec][i].widget->setSkillId(mSkills[spec][i].skillId); - mSkills[spec][i].widget->eventClicked += MyGUI::newDelegate(this, &SelectSkillDialog::onSkillClicked); - ToolTips::createSkillToolTip(mSkills[spec][i].widget, mSkills[spec][i].widget->getSkillId()); - } + widget->setVisibleVScroll(false); + widget->setCanvasSize(MyGUI::IntSize(widget->getWidth(), std::max(widget->getHeight(), coord.top))); + widget->setVisibleVScroll(true); + widget->setViewOffset(MyGUI::IntPoint()); } MyGUI::Button* cancelButton; diff --git a/apps/openmw/mwgui/class.hpp b/apps/openmw/mwgui/class.hpp index dd66632784..1052dab581 100644 --- a/apps/openmw/mwgui/class.hpp +++ b/apps/openmw/mwgui/class.hpp @@ -1,6 +1,7 @@ #ifndef MWGUI_CLASS_H #define MWGUI_CLASS_H +#include #include #include @@ -218,7 +219,7 @@ namespace MWGui bool exit() override; - ESM::Skill::SkillEnum getSkillId() const { return mSkillId; } + ESM::RefId getSkillId() const { return mSkillId; } // Events typedef MyGUI::delegates::CMultiDelegate0 EventHandle_Void; @@ -238,11 +239,7 @@ namespace MWGui void onCancelClicked(MyGUI::Widget* _sender); private: - Widgets::MWSkillPtr mCombatSkill[9]; - Widgets::MWSkillPtr mMagicSkill[9]; - Widgets::MWSkillPtr mStealthSkill[9]; - - ESM::Skill::SkillEnum mSkillId; + ESM::RefId mSkillId; }; class DescriptionDialog : public WindowModal @@ -278,8 +275,8 @@ namespace MWGui std::string getDescription() const; ESM::Class::Specialization getSpecializationId() const; std::vector getFavoriteAttributes() const; - std::vector getMajorSkills() const; - std::vector getMinorSkills() const; + std::vector getMajorSkills() const; + std::vector getMinorSkills() const; void setNextButtonShow(bool shown); @@ -318,8 +315,8 @@ namespace MWGui MyGUI::EditBox* mEditName; MyGUI::TextBox* mSpecializationName; Widgets::MWAttributePtr mFavoriteAttribute0, mFavoriteAttribute1; - Widgets::MWSkillPtr mMajorSkill[5]; - Widgets::MWSkillPtr mMinorSkill[5]; + std::array mMajorSkill; + std::array mMinorSkill; std::vector mSkills; std::string mDescription; diff --git a/apps/openmw/mwgui/jailscreen.cpp b/apps/openmw/mwgui/jailscreen.cpp index ec0614a664..c6aefdd177 100644 --- a/apps/openmw/mwgui/jailscreen.cpp +++ b/apps/openmw/mwgui/jailscreen.cpp @@ -93,11 +93,11 @@ namespace MWGui for (int day = 0; day < mDays; ++day) { auto& prng = MWBase::Environment::get().getWorld()->getPrng(); - const ESM::Skill* skill = skillStore.find(Misc::Rng::rollDice(ESM::Skill::Length, prng)); + const ESM::Skill* skill = skillStore.searchRandom({}, prng); skills.insert(skill); - MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill->mIndex); - if (skill->mIndex == ESM::Skill::Security || skill->mIndex == ESM::Skill::Sneak) + MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill->mId); + if (skill->mId == ESM::Skill::Security || skill->mId == ESM::Skill::Sneak) value.setBase(std::min(100.f, value.getBase() + 1)); else value.setBase(std::max(0.f, value.getBase() - 1)); @@ -116,9 +116,9 @@ namespace MWGui for (const ESM::Skill* skill : skills) { - int skillValue = player.getClass().getNpcStats(player).getSkill(skill->mIndex).getBase(); + int skillValue = player.getClass().getNpcStats(player).getSkill(skill->mId).getBase(); std::string skillMsg = gmst.find("sNotifyMessage44")->mValue.getString(); - if (skill->mIndex == ESM::Skill::Sneak || skill->mIndex == ESM::Skill::Security) + if (skill->mId == ESM::Skill::Sneak || skill->mId == ESM::Skill::Security) skillMsg = gmst.find("sNotifyMessage39")->mValue.getString(); skillMsg = Misc::StringUtils::format(skillMsg, skill->mName, skillValue); diff --git a/apps/openmw/mwgui/race.cpp b/apps/openmw/mwgui/race.cpp index e239e641c7..6f1a615dd9 100644 --- a/apps/openmw/mwgui/race.cpp +++ b/apps/openmw/mwgui/race.cpp @@ -414,13 +414,14 @@ namespace MWGui const ESM::Race* race = store.get().find(mCurrentRaceId); for (const auto& bonus : race->mData.mBonus) { - if (bonus.mSkill < 0 || bonus.mSkill >= ESM::Skill::Length) // Skip unknown skill indexes + ESM::RefId skill = ESM::Skill::indexToRefId(bonus.mSkill); + if (skill.empty()) // Skip unknown skill indexes continue; skillWidget = mSkillList->createWidget("MW_StatNameValue", coord1, MyGUI::Align::Default); - skillWidget->setSkillId(ESM::Skill::SkillEnum(bonus.mSkill)); + skillWidget->setSkillId(skill); skillWidget->setSkillValue(Widgets::MWSkill::SkillValue(static_cast(bonus.mBonus), 0.f)); - ToolTips::createSkillToolTip(skillWidget, bonus.mSkill); + ToolTips::createSkillToolTip(skillWidget, skill); mSkillItems.push_back(skillWidget); diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index acea128f07..8887b9eb4f 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -90,10 +90,10 @@ namespace MWGui getWidget(mSkillView, "SkillView"); mSkillView->eventMouseWheel += MyGUI::newDelegate(this, &ReviewDialog::onMouseWheel); - for (int i = 0; i < ESM::Skill::Length; ++i) + for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get()) { - mSkillValues.insert(std::make_pair(i, MWMechanics::SkillValue())); - mSkillWidgetMap.insert(std::make_pair(i, static_cast(nullptr))); + mSkillValues.emplace(skill.mId, MWMechanics::SkillValue()); + mSkillWidgetMap.emplace(skill.mId, static_cast(nullptr)); } MyGUI::Button* backButton; @@ -204,13 +204,14 @@ namespace MWGui } } - void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::SkillValue& value) + void ReviewDialog::setSkillValue(ESM::RefId id, const MWMechanics::SkillValue& value) { - mSkillValues[skillId] = value; - MyGUI::TextBox* widget = mSkillWidgetMap[skillId]; + mSkillValues[id] = value; + MyGUI::TextBox* widget = mSkillWidgetMap[id]; if (widget) { - float modified = static_cast(value.getModified()), base = static_cast(value.getBase()); + float modified = value.getModified(); + float base = value.getBase(); std::string text = MyGUI::utility::toString(std::floor(modified)); std::string state = "normal"; if (modified > base) @@ -225,21 +226,21 @@ namespace MWGui mUpdateSkillArea = true; } - void ReviewDialog::configureSkills(const std::vector& major, const std::vector& minor) + void ReviewDialog::configureSkills(const std::vector& major, const std::vector& minor) { mMajorSkills = major; mMinorSkills = minor; // Update misc skills with the remaining skills not in major or minor - std::set skillSet; + std::set skillSet; std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin())); std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin())); mMiscSkills.clear(); const auto& store = MWBase::Environment::get().getWorld()->getStore().get(); - for (const auto& skill : store) + for (const ESM::Skill& skill : store) { - if (!skillSet.contains(skill.second.mIndex)) - mMiscSkills.push_back(skill.second.mIndex); + if (!skillSet.contains(skill.mId)) + mMiscSkills.push_back(skill.mId); } mUpdateSkillArea = true; @@ -327,8 +328,8 @@ namespace MWGui coord2.top += lineHeight; } - void ReviewDialog::addSkills(const SkillList& skills, const std::string& titleId, const std::string& titleDefault, - MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2) + void ReviewDialog::addSkills(const std::vector& skills, const std::string& titleId, + const std::string& titleDefault, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2) { // Add a line separator if there are items above if (!mSkillWidgets.empty()) @@ -339,12 +340,12 @@ namespace MWGui addGroup( MWBase::Environment::get().getWindowManager()->getGameSettingString(titleId, titleDefault), coord1, coord2); - for (const int& skillId : skills) + for (const ESM::RefId& skillId : skills) { const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get().search(skillId); - if (!skill) // Skip unknown skill indexes + if (!skill) // Skip unknown skills continue; - const MWMechanics::SkillValue& stat = mSkillValues.find(skillId)->second; + const MWMechanics::SkillValue& stat = mSkillValues.find(skill->mId)->second; int base = stat.getBase(); int modified = stat.getModified(); @@ -358,10 +359,10 @@ namespace MWGui for (int i = 0; i < 2; ++i) { - ToolTips::createSkillToolTip(mSkillWidgets[mSkillWidgets.size() - 1 - i], skillId); + ToolTips::createSkillToolTip(mSkillWidgets[mSkillWidgets.size() - 1 - i], skill->mId); } - mSkillWidgetMap[skillId] = widget; + mSkillWidgetMap[skill->mId] = widget; } } @@ -393,15 +394,11 @@ namespace MWGui if (!mRaceId.empty()) race = MWBase::Environment::get().getESMStore()->get().find(mRaceId); - int skills[ESM::Skill::Length]; - for (int i = 0; i < ESM::Skill::Length; ++i) - skills[i] = mSkillValues.find(i)->second.getBase(); - int attributes[ESM::Attribute::Length]; for (int i = 0; i < ESM::Attribute::Length; ++i) attributes[i] = mAttributeWidgets[i]->getAttributeValue().getBase(); - std::vector selectedSpells = MWMechanics::autoCalcPlayerSpells(skills, attributes, race); + std::vector selectedSpells = MWMechanics::autoCalcPlayerSpells(mSkillValues, attributes, race); for (ESM::RefId& spellId : selectedSpells) { if (std::find(spells.begin(), spells.end(), spellId) == spells.end()) diff --git a/apps/openmw/mwgui/review.hpp b/apps/openmw/mwgui/review.hpp index 54044a762a..dcb63d4d05 100644 --- a/apps/openmw/mwgui/review.hpp +++ b/apps/openmw/mwgui/review.hpp @@ -24,7 +24,6 @@ namespace MWGui CLASS_DIALOG, BIRTHSIGN_DIALOG }; - typedef std::vector SkillList; ReviewDialog(); @@ -41,8 +40,8 @@ namespace MWGui void setAttribute(ESM::Attribute::AttributeID attributeId, const MWMechanics::AttributeValue& value); - void configureSkills(const SkillList& major, const SkillList& minor); - void setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::SkillValue& value); + void configureSkills(const std::vector& major, const std::vector& minor); + void setSkillValue(ESM::RefId id, const MWMechanics::SkillValue& value); void onOpen() override; @@ -76,8 +75,8 @@ namespace MWGui void onMouseWheel(MyGUI::Widget* _sender, int _rel); private: - void addSkills(const SkillList& skills, const std::string& titleId, const std::string& titleDefault, - MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2); + void addSkills(const std::vector& skills, const std::string& titleId, + const std::string& titleDefault, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2); void addSeparator(MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2); void addGroup(std::string_view label, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2); MyGUI::TextBox* addValueItem(std::string_view text, const std::string& value, const std::string& state, @@ -93,9 +92,9 @@ namespace MWGui std::map mAttributeWidgets; - SkillList mMajorSkills, mMinorSkills, mMiscSkills; - std::map mSkillValues; - std::map mSkillWidgetMap; + std::vector mMajorSkills, mMinorSkills, mMiscSkills; + std::map mSkillValues; + std::map mSkillWidgetMap; ESM::RefId mRaceId, mBirthSignId; std::string mName; ESM::Class mKlass; diff --git a/apps/openmw/mwgui/spellcreationdialog.cpp b/apps/openmw/mwgui/spellcreationdialog.cpp index a1d3ea6f9f..d515724e5b 100644 --- a/apps/openmw/mwgui/spellcreationdialog.cpp +++ b/apps/openmw/mwgui/spellcreationdialog.cpp @@ -286,9 +286,9 @@ namespace MWGui exit(); } - void EditEffectDialog::setSkill(int skill) + void EditEffectDialog::setSkill(ESM::RefId skill) { - mEffect.mSkill = skill; + mEffect.mSkill = skill.getIf()->getValue(); eventEffectModified(mEffect); } diff --git a/apps/openmw/mwgui/spellcreationdialog.hpp b/apps/openmw/mwgui/spellcreationdialog.hpp index d79494dae2..cde0fcacd0 100644 --- a/apps/openmw/mwgui/spellcreationdialog.hpp +++ b/apps/openmw/mwgui/spellcreationdialog.hpp @@ -30,7 +30,7 @@ namespace MWGui void setConstantEffect(bool constant); - void setSkill(int skill); + void setSkill(ESM::RefId skill); void setAttribute(int attribute); void newEffect(const ESM::MagicEffect* effect); diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index 8e90166e51..e5eff6e178 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -83,7 +83,8 @@ namespace MWGui if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill) { - const ESM::Skill* skill = store->get().find(effectInfo.mKey.mArg); + const ESM::Skill* skill + = store->get().find(ESM::Skill::indexToRefId(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 473324e685..dafa009eef 100644 --- a/apps/openmw/mwgui/spellmodel.cpp +++ b/apps/openmw/mwgui/spellmodel.cpp @@ -55,7 +55,7 @@ namespace MWGui { const ESM::MagicEffect* magicEffect = store.get().find(effectId); const ESM::Attribute* attribute = store.get().search(effect.mAttribute); - const ESM::Skill* skill = store.get().search(effect.mSkill); + const ESM::Skill* skill = store.get().search(ESM::Skill::indexToRefId(effect.mSkill)); std::string fullEffectName = MWMechanics::getMagicEffectString(*magicEffect, attribute, skill); std::string convert = Utf8Stream::lowerCaseUtf8(fullEffectName); diff --git a/apps/openmw/mwgui/statswatcher.cpp b/apps/openmw/mwgui/statswatcher.cpp index 6ad574a5a2..dab6b9bf82 100644 --- a/apps/openmw/mwgui/statswatcher.cpp +++ b/apps/openmw/mwgui/statswatcher.cpp @@ -83,13 +83,13 @@ namespace MWGui } } - // Loop over ESM::Skill::SkillEnum - for (int i = 0; i < ESM::Skill::Length; ++i) + for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get()) { - if (stats.getSkill(i) != mWatchedSkills[i] || mWatchedStatsEmpty) + const auto& value = stats.getSkill(skill.mId); + if (value != mWatchedSkills[skill.mId] || mWatchedStatsEmpty) { - mWatchedSkills[i] = stats.getSkill(i); - setValue((ESM::Skill::SkillEnum)i, stats.getSkill(i)); + mWatchedSkills[skill.mId] = value; + setValue(skill.mId, value); } } @@ -125,13 +125,13 @@ namespace MWGui setValue("class", cls->mName); size_t size = cls->mData.mSkills.size(); - MWBase::WindowManager::SkillList majorSkills(size); - MWBase::WindowManager::SkillList minorSkills(size); + std::vector majorSkills(size); + std::vector minorSkills(size); for (size_t i = 0; i < size; ++i) { - minorSkills[i] = cls->mData.mSkills[i][0]; - majorSkills[i] = cls->mData.mSkills[i][1]; + minorSkills[i] = ESM::Skill::indexToRefId(cls->mData.mSkills[i][0]); + majorSkills[i] = ESM::Skill::indexToRefId(cls->mData.mSkills[i][1]); } configureSkills(majorSkills, minorSkills); @@ -157,12 +157,10 @@ namespace MWGui listener->setValue(id, value); } - void StatsWatcher::setValue(ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) + void StatsWatcher::setValue(ESM::RefId id, const MWMechanics::SkillValue& value) { - /// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we - /// allow custom skills. for (StatsListener* listener : mListeners) - listener->setValue(parSkill, value); + listener->setValue(id, value); } void StatsWatcher::setValue(std::string_view id, const MWMechanics::DynamicStat& value) @@ -183,7 +181,7 @@ namespace MWGui listener->setValue(id, value); } - void StatsWatcher::configureSkills(const std::vector& major, const std::vector& minor) + void StatsWatcher::configureSkills(const std::vector& major, const std::vector& minor) { for (StatsListener* listener : mListeners) listener->configureSkills(major, minor); diff --git a/apps/openmw/mwgui/statswatcher.hpp b/apps/openmw/mwgui/statswatcher.hpp index da49eed10b..e7339294da 100644 --- a/apps/openmw/mwgui/statswatcher.hpp +++ b/apps/openmw/mwgui/statswatcher.hpp @@ -1,6 +1,7 @@ #ifndef MWGUI_STATSWATCHER_H #define MWGUI_STATSWATCHER_H +#include #include #include @@ -22,8 +23,8 @@ namespace MWGui 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) {} - virtual void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) {} - virtual void configureSkills(const std::vector& major, const std::vector& minor) {} + virtual void setValue(ESM::RefId id, const MWMechanics::SkillValue& value) {} + virtual void configureSkills(const std::vector& major, const std::vector& minor) {} }; class StatsWatcher @@ -31,7 +32,7 @@ namespace MWGui MWWorld::Ptr mWatched; MWMechanics::AttributeValue mWatchedAttributes[ESM::Attribute::Length]; - MWMechanics::SkillValue mWatchedSkills[ESM::Skill::Length]; + std::map mWatchedSkills; MWMechanics::DynamicStat mWatchedHealth; MWMechanics::DynamicStat mWatchedMagicka; @@ -53,8 +54,8 @@ namespace MWGui 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); - void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value); - void configureSkills(const std::vector& major, const std::vector& minor); + void setValue(ESM::RefId id, const MWMechanics::SkillValue& value); + void configureSkills(const std::vector& major, const std::vector& minor); public: StatsWatcher(); diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index ee93c5720e..4bd82faa70 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -37,17 +37,8 @@ namespace MWGui : WindowPinnableBase("openmw_stats_window.layout") , NoDrop(drag, mMainWidget) , mSkillView(nullptr) - , mMajorSkills() - , mMinorSkills() - , mMiscSkills() - , mSkillValues() - , mSkillWidgetMap() - , mFactionWidgetMap() - , mFactions() - , mBirthSignId() , mReputation(0) , mBounty(0) - , mSkillWidgets() , mChanged(true) , mMinFullWidth(mMainWidget->getSize().width) { @@ -67,11 +58,10 @@ namespace MWGui getWidget(mLeftPane, "LeftPane"); getWidget(mRightPane, "RightPane"); - for (int i = 0; i < ESM::Skill::Length; ++i) + for (const ESM::Skill& skill : store.get()) { - mSkillValues.insert(std::make_pair(i, MWMechanics::SkillValue())); - mSkillWidgetMap.insert( - std::make_pair(i, std::make_pair((MyGUI::TextBox*)nullptr, (MyGUI::TextBox*)nullptr))); + mSkillValues.emplace(skill.mId, MWMechanics::SkillValue()); + mSkillWidgetMap.emplace(skill.mId, std::make_pair(nullptr, nullptr)); } MyGUI::Window* t = mMainWidget->castType(); @@ -237,7 +227,7 @@ namespace MWGui } } - void setSkillProgress(MyGUI::Widget* w, float progress, int skillId) + void setSkillProgress(MyGUI::Widget* w, float progress, ESM::RefId skillId) { MWWorld::Ptr player = MWMechanics::getPlayer(); const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore(); @@ -255,10 +245,10 @@ namespace MWGui w->setUserString("RangePosition_SkillProgress", MyGUI::utility::toString(progressPercent)); } - void StatsWindow::setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) + void StatsWindow::setValue(ESM::RefId id, const MWMechanics::SkillValue& value) { - mSkillValues[parSkill] = value; - std::pair widgets = mSkillWidgetMap[(int)parSkill]; + mSkillValues[id] = value; + std::pair widgets = mSkillWidgetMap[id]; MyGUI::TextBox* valueWidget = widgets.second; MyGUI::TextBox* nameWidget = widgets.first; if (valueWidget && nameWidget) @@ -296,8 +286,8 @@ namespace MWGui valueWidget->setUserString("Visible_SkillProgressVBox", "true"); valueWidget->setUserString("UserData^Hidden_SkillProgressVBox", "false"); - setSkillProgress(nameWidget, value.getProgress(), parSkill); - setSkillProgress(valueWidget, value.getProgress(), parSkill); + setSkillProgress(nameWidget, value.getProgress(), id); + setSkillProgress(valueWidget, value.getProgress(), id); } else { @@ -314,21 +304,21 @@ namespace MWGui } } - void StatsWindow::configureSkills(const std::vector& major, const std::vector& minor) + void StatsWindow::configureSkills(const std::vector& major, const std::vector& minor) { mMajorSkills = major; mMinorSkills = minor; // Update misc skills with the remaining skills not in major or minor - std::set skillSet; + std::set skillSet; std::copy(major.begin(), major.end(), std::inserter(skillSet, skillSet.begin())); std::copy(minor.begin(), minor.end(), std::inserter(skillSet, skillSet.begin())); mMiscSkills.clear(); const auto& store = MWBase::Environment::get().getWorld()->getStore().get(); for (const auto& skill : store) { - if (!skillSet.contains(skill.second.mIndex)) - mMiscSkills.push_back(skill.second.mIndex); + if (!skillSet.contains(skill.mId)) + mMiscSkills.push_back(skill.mId); } updateSkillArea(); @@ -492,8 +482,8 @@ namespace MWGui return skillNameWidget; } - void StatsWindow::addSkills(const SkillList& skills, const std::string& titleId, const std::string& titleDefault, - MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2) + void StatsWindow::addSkills(const std::vector& skills, const std::string& titleId, + const std::string& titleDefault, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2) { // Add a line separator if there are items above if (!mSkillWidgets.empty()) @@ -504,19 +494,18 @@ namespace MWGui addGroup( MWBase::Environment::get().getWindowManager()->getGameSettingString(titleId, titleDefault), coord1, coord2); - for (const int skillId : skills) + const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore(); + for (const ESM::RefId& skillId : skills) { - if (skillId < 0 || skillId >= ESM::Skill::Length) // Skip unknown skill indexes + const ESM::Skill* skill = esmStore.get().search(skillId); + if (!skill) // Skip unknown skills continue; - const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore(); - - const ESM::Skill* skill = esmStore.get().find(skillId); const ESM::Attribute* attr = esmStore.get().find(skill->mData.mAttribute); std::pair widgets = addValueItem(skill->mName, {}, "normal", coord1, coord2); - mSkillWidgetMap[skillId] = widgets; + mSkillWidgetMap[skill->mId] = widgets; for (int i = 0; i < 2; ++i) { @@ -532,7 +521,7 @@ namespace MWGui mSkillWidgets[mSkillWidgets.size() - 1 - i]->setUserString("Range_SkillProgress", "100"); } - setValue(static_cast(skillId), mSkillValues.find(skillId)->second); + setValue(skill->mId, mSkillValues.find(skill->mId)->second); } } @@ -640,13 +629,13 @@ namespace MWGui bool firstSkill = true; for (int id : faction->mData.mSkills) { - if (id != -1) + const ESM::Skill* skill = store.get().search(ESM::Skill::indexToRefId(id)); + if (skill) { if (!firstSkill) text += ", "; firstSkill = false; - const ESM::Skill* skill = store.get().find(id); text += MyGUI::TextIterator::toTagsString(skill->mName); } } diff --git a/apps/openmw/mwgui/statswindow.hpp b/apps/openmw/mwgui/statswindow.hpp index 6a781cd5cc..ca7a5a7e07 100644 --- a/apps/openmw/mwgui/statswindow.hpp +++ b/apps/openmw/mwgui/statswindow.hpp @@ -12,8 +12,6 @@ namespace MWGui public: typedef std::map FactionList; - typedef std::vector SkillList; - StatsWindow(DragAndDrop* drag); /// automatically updates all the data in the stats window, but only if it has changed. @@ -27,8 +25,8 @@ namespace MWGui 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; - void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) override; - void configureSkills(const SkillList& major, const SkillList& minor) override; + void setValue(ESM::RefId id, const MWMechanics::SkillValue& value) override; + void configureSkills(const std::vector& major, const std::vector& minor) override; void setReputation(int reputation) { @@ -47,8 +45,8 @@ namespace MWGui void onOpen() override { onWindowResize(mMainWidget->castType()); } private: - void addSkills(const SkillList& skills, const std::string& titleId, const std::string& titleDefault, - MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2); + void addSkills(const std::vector& skills, const std::string& titleId, + const std::string& titleDefault, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2); void addSeparator(MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2); void addGroup(std::string_view label, MyGUI::IntCoord& coord1, MyGUI::IntCoord& coord2); std::pair addValueItem(std::string_view text, const std::string& value, @@ -67,9 +65,9 @@ namespace MWGui MyGUI::ScrollView* mSkillView; - SkillList mMajorSkills, mMinorSkills, mMiscSkills; - std::map mSkillValues; - std::map> mSkillWidgetMap; + std::vector mMajorSkills, mMinorSkills, mMiscSkills; + std::map mSkillValues; + std::map> mSkillWidgetMap; std::map mFactionWidgetMap; FactionList mFactions; ///< Stores a list of factions and the current rank ESM::RefId mBirthSignId; diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 7ad3206055..265ccd8ec7 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -805,9 +805,9 @@ namespace MWGui mFocusToolTipY = min_y; } - void ToolTips::createSkillToolTip(MyGUI::Widget* widget, int skillId) + void ToolTips::createSkillToolTip(MyGUI::Widget* widget, ESM::RefId skillId) { - if (skillId == -1) + if (skillId.empty()) return; const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore(); @@ -846,7 +846,7 @@ namespace MWGui const MWWorld::Store& skills = MWBase::Environment::get().getESMStore()->get(); bool isFirst = true; - for (const auto& [_, skill] : skills) + for (const auto& skill : skills) { if (skill.mData.mSpecialization == specId) { diff --git a/apps/openmw/mwgui/tooltips.hpp b/apps/openmw/mwgui/tooltips.hpp index 9ed62d4e35..8246b0ca3b 100644 --- a/apps/openmw/mwgui/tooltips.hpp +++ b/apps/openmw/mwgui/tooltips.hpp @@ -93,7 +93,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, int skillId); + static void createSkillToolTip(MyGUI::Widget* widget, ESM::RefId skillId); static void createAttributeToolTip(MyGUI::Widget* widget, int attributeId); static void createSpecializationToolTip(MyGUI::Widget* widget, const std::string& name, int specId); static void createBirthsignToolTip(MyGUI::Widget* widget, const ESM::RefId& birthsignId); diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index 7b0e93edd0..785a6b48c2 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -21,24 +21,6 @@ #include "tooltips.hpp" -namespace -{ - // Sorts a container descending by skill value. If skill value is equal, sorts ascending by skill ID. - // pair - bool sortSkills(const std::pair& left, const std::pair& right) - { - if (left == right) - return false; - - if (left.second > right.second) - return true; - else if (left.second < right.second) - return false; - - return left.first < right.first; - } -} - namespace MWGui { @@ -80,34 +62,37 @@ namespace MWGui mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); + const auto& store = MWBase::Environment::get().getESMStore(); + const MWWorld::Store& gmst = store->get(); + const MWWorld::Store& skillStore = store->get(); + // NPC can train you in his best 3 skills - std::vector> skills; + std::vector> skills; MWMechanics::NpcStats const& actorStats(actor.getClass().getNpcStats(actor)); - for (int i = 0; i < ESM::Skill::Length; ++i) + for (const ESM::Skill& skill : skillStore) { - float value = getSkillForTraining(actorStats, i); + float value = getSkillForTraining(actorStats, skill.mId); - skills.emplace_back(i, value); + skills.emplace_back(&skill, value); } - std::sort(skills.begin(), skills.end(), sortSkills); + std::sort(skills.begin(), skills.end(), [](const auto& left, const auto& right) { + return std::tie(right.second, left.first->mId) < std::tie(left.second, right.first->mId); + }); MyGUI::EnumeratorWidgetPtr widgets = mTrainingOptions->getEnumerator(); MyGUI::Gui::getInstance().destroyWidgets(widgets); MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player); - const auto& store = MWBase::Environment::get().getESMStore(); - const MWWorld::Store& gmst = store->get(); - const MWWorld::Store& skillStore = store->get(); - const int lineHeight = MWBase::Environment::get().getWindowManager()->getFontHeight() + 2; for (int i = 0; i < 3; ++i) { + const ESM::Skill* skill = skills[i].first; int price = static_cast( - pcStats.getSkill(skills[i].first).getBase() * gmst.find("iTrainingMod")->mValue.getInteger()); + pcStats.getSkill(skill->mId).getBase() * gmst.find("iTrainingMod")->mValue.getInteger()); price = std::max(1, price); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); @@ -120,13 +105,12 @@ namespace MWGui button->setUserData(skills[i].first); button->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onTrainingSelected); - const ESM::Skill* skill = skillStore.find(skills[i].first); button->setCaptionWithReplacing( MyGUI::TextIterator::toTagsString(skill->mName) + " - " + MyGUI::utility::toString(price)); button->setSize(button->getTextSize().width + 12, button->getSize().height); - ToolTips::createSkillToolTip(button, skills[i].first); + ToolTips::createSkillToolTip(button, skill->mId); } center(); @@ -144,29 +128,29 @@ namespace MWGui void TrainingWindow::onTrainingSelected(MyGUI::Widget* sender) { - int skillId = *sender->getUserData(); + const ESM::Skill* skill = *sender->getUserData(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWMechanics::NpcStats& pcStats = player.getClass().getNpcStats(player); const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore(); - int price = pcStats.getSkill(skillId).getBase() + int price = pcStats.getSkill(skill->mId).getBase() * store.get().find("iTrainingMod")->mValue.getInteger(); price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mPtr, price, true); if (price > player.getClass().getContainerStore(player).count(MWWorld::ContainerStore::sGoldId)) return; - if (getSkillForTraining(mPtr.getClass().getNpcStats(mPtr), skillId) <= pcStats.getSkill(skillId).getBase()) + if (getSkillForTraining(mPtr.getClass().getNpcStats(mPtr), skill->mId) + <= pcStats.getSkill(skill->mId).getBase()) { MWBase::Environment::get().getWindowManager()->messageBox("#{sServiceTrainingWords}"); return; } // You can not train a skill above its governing attribute - const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get().find(skillId); - if (pcStats.getSkill(skillId).getBase() >= pcStats.getAttribute(skill->mData.mAttribute).getBase()) + if (pcStats.getSkill(skill->mId).getBase() >= pcStats.getAttribute(skill->mData.mAttribute).getBase()) { MWBase::Environment::get().getWindowManager()->messageBox("#{sNotifyMessage17}"); return; @@ -176,7 +160,7 @@ namespace MWGui MWWorld::LiveCellRef* playerRef = player.get(); const ESM::Class* class_ = store.get().find(playerRef->mBase->mClass); - pcStats.increaseSkill(skillId, *class_, true); + pcStats.increaseSkill(skill->mId, *class_, true); // remove gold player.getClass().getContainerStore(player).remove(MWWorld::ContainerStore::sGoldId, price); @@ -212,11 +196,11 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); } - float TrainingWindow::getSkillForTraining(const MWMechanics::NpcStats& stats, int skillId) const + float TrainingWindow::getSkillForTraining(const MWMechanics::NpcStats& stats, ESM::RefId id) const { if (mTrainingSkillBasedOnBaseSkill) - return stats.getSkill(skillId).getBase(); - return stats.getSkill(skillId).getModified(); + return stats.getSkill(id).getBase(); + return stats.getSkill(id).getModified(); } void TrainingWindow::onFrame(float dt) diff --git a/apps/openmw/mwgui/trainingwindow.hpp b/apps/openmw/mwgui/trainingwindow.hpp index 145c326150..c169022b25 100644 --- a/apps/openmw/mwgui/trainingwindow.hpp +++ b/apps/openmw/mwgui/trainingwindow.hpp @@ -42,7 +42,7 @@ namespace MWGui // Retrieve the base skill value if the setting 'training skills based on base skill' is set; // otherwise returns the modified skill - float getSkillForTraining(const MWMechanics::NpcStats& stats, int skillId) const; + float getSkillForTraining(const MWMechanics::NpcStats& stats, ESM::RefId id) const; MyGUI::Widget* mTrainingOptions; MyGUI::Button* mCancelButton; diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 8523a85550..194fa972af 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -28,28 +28,17 @@ namespace MWGui::Widgets /* MWSkill */ MWSkill::MWSkill() - : mSkillId(ESM::Skill::Length) - , mSkillNameWidget(nullptr) + : mSkillNameWidget(nullptr) , mSkillValueWidget(nullptr) { } - void MWSkill::setSkillId(ESM::Skill::SkillEnum skill) + void MWSkill::setSkillId(ESM::RefId skill) { mSkillId = skill; updateWidgets(); } - void MWSkill::setSkillNumber(int skill) - { - if (skill < 0) - setSkillId(ESM::Skill::Length); - else if (skill < ESM::Skill::Length) - setSkillId(static_cast(skill)); - else - throw std::runtime_error("Skill number out of range"); - } - void MWSkill::setSkillValue(const SkillValue& value) { mValue = value; @@ -374,7 +363,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(mEffectParams.mSkill); + const ESM::Skill* skill = store.get().search(ESM::Skill::indexToRefId(mEffectParams.mSkill)); assert(magicEffect); diff --git a/apps/openmw/mwgui/widgets.hpp b/apps/openmw/mwgui/widgets.hpp index 63a6fd0db9..90e9680a90 100644 --- a/apps/openmw/mwgui/widgets.hpp +++ b/apps/openmw/mwgui/widgets.hpp @@ -100,11 +100,10 @@ namespace MWGui typedef MWMechanics::Stat SkillValue; - void setSkillId(ESM::Skill::SkillEnum skillId); - void setSkillNumber(int skillId); + void setSkillId(ESM::RefId skillId); void setSkillValue(const SkillValue& value); - ESM::Skill::SkillEnum getSkillId() const { return mSkillId; } + ESM::RefId getSkillId() const { return mSkillId; } const SkillValue& getSkillValue() const { return mValue; } // Events @@ -125,7 +124,7 @@ namespace MWGui private: void updateWidgets(); - ESM::Skill::SkillEnum mSkillId; + ESM::RefId mSkillId; SkillValue mValue; MyGUI::TextBox* mSkillNameWidget; MyGUI::TextBox* mSkillValueWidget; diff --git a/apps/openmw/mwlua/localscripts.hpp b/apps/openmw/mwlua/localscripts.hpp index b57e99dca9..ea54321697 100644 --- a/apps/openmw/mwlua/localscripts.hpp +++ b/apps/openmw/mwlua/localscripts.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -21,11 +22,12 @@ namespace MWLua class CachedStat { public: - using Setter = void (*)(int, std::string_view, const MWWorld::Ptr&, const sol::object&); + using Index = std::variant; + using Setter = void (*)(const Index&, std::string_view, const MWWorld::Ptr&, const sol::object&); - CachedStat(Setter setter, int index, std::string_view prop) + CachedStat(Setter setter, Index index, std::string_view prop) : mSetter(setter) - , mIndex(index) + , mIndex(std::move(index)) , mProp(std::move(prop)) { } @@ -42,7 +44,7 @@ namespace MWLua private: Setter mSetter; // Function that updates a stat's property - int mIndex; // Optional index to disambiguate the stat + Index mIndex; // Optional index to disambiguate the stat std::string_view mProp; // Name of the stat's property }; diff --git a/apps/openmw/mwlua/stats.cpp b/apps/openmw/mwlua/stats.cpp index eddd366fd0..1687b202d7 100644 --- a/apps/openmw/mwlua/stats.cpp +++ b/apps/openmw/mwlua/stats.cpp @@ -24,9 +24,10 @@ namespace { using SelfObject = MWLua::SelfObject; using ObjectVariant = MWLua::ObjectVariant; + using Index = const SelfObject::CachedStat::Index&; template - auto addIndexedAccessor(int index) + auto addIndexedAccessor(Index index) { return [index](const sol::object& o) { return T::create(ObjectVariant(o), index); }; } @@ -40,7 +41,7 @@ namespace template sol::object getValue(const MWLua::Context& context, const ObjectVariant& obj, SelfObject::CachedStat::Setter setter, - int index, std::string_view prop, G getter) + Index index, std::string_view prop, G getter) { if (obj.isSelfObject()) { @@ -99,14 +100,14 @@ namespace MWLua return sol::make_object(context.mLua->sol(), ptr.getClass().getNpcStats(ptr).getLevelProgress()); } - static std::optional create(ObjectVariant object, int index) + static std::optional create(ObjectVariant object, Index) { if (!object.ptr().getClass().isActor()) return {}; return LevelStat{ std::move(object) }; } - static void setValue(int, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) + static void setValue(Index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) { auto& stats = ptr.getClass().getCreatureStats(ptr); if (prop == "current") @@ -135,10 +136,11 @@ namespace MWLua }); } - static std::optional create(ObjectVariant object, int index) + static std::optional create(ObjectVariant object, Index i) { if (!object.ptr().getClass().isActor()) return {}; + int index = std::get(i); return DynamicStat{ std::move(object), index }; } @@ -149,8 +151,9 @@ namespace MWLua obj->mStatsCache[SelfObject::CachedStat{ &DynamicStat::setValue, mIndex, prop }] = value; } - static void setValue(int index, 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(i); auto& stats = ptr.getClass().getCreatureStats(ptr); auto stat = stats.getDynamic(index); float floatValue = LuaUtil::cast(value); @@ -193,10 +196,11 @@ namespace MWLua return std::max(0.f, base - damage + modifier); // Should match AttributeValue::getModified } - static std::optional create(ObjectVariant object, int index) + static std::optional create(ObjectVariant object, Index i) { if (!object.ptr().getClass().isActor()) return {}; + int index = std::get(i); return AttributeStat{ std::move(object), index }; } @@ -207,8 +211,9 @@ namespace MWLua obj->mStatsCache[SelfObject::CachedStat{ &AttributeStat::setValue, mIndex, prop }] = value; } - static void setValue(int index, 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(i); auto& stats = ptr.getClass().getCreatureStats(ptr); auto stat = stats.getAttribute(index); float floatValue = LuaUtil::cast(value); @@ -228,37 +233,36 @@ namespace MWLua class SkillStat { ObjectVariant mObject; - int mIndex; + ESM::RefId mId; - SkillStat(ObjectVariant object, int index) + SkillStat(ObjectVariant object, ESM::RefId id) : mObject(std::move(object)) - , mIndex(index) + , mId(id) { } - static float getProgress(const MWWorld::Ptr& ptr, int index, const MWMechanics::SkillValue& stat) + static float getProgress(const MWWorld::Ptr& ptr, ESM::RefId id, const MWMechanics::SkillValue& stat) { float progress = stat.getProgress(); if (progress != 0.f) - progress /= getMaxProgress(ptr, index, stat); + progress /= getMaxProgress(ptr, id, stat); return progress; } - static float getMaxProgress(const MWWorld::Ptr& ptr, int index, const MWMechanics::SkillValue& stat) + static float getMaxProgress(const MWWorld::Ptr& ptr, ESM::RefId id, const MWMechanics::SkillValue& stat) { const auto& store = *MWBase::Environment::get().getESMStore(); const auto cl = store.get().find(ptr.get()->mBase->mClass); - return ptr.getClass().getNpcStats(ptr).getSkillProgressRequirement(index, *cl); + return ptr.getClass().getNpcStats(ptr).getSkillProgressRequirement(id, *cl); } public: template sol::object get(const Context& context, std::string_view prop, G getter) const { - return getValue( - context, mObject, &SkillStat::setValue, mIndex, prop, [this, getter](const MWWorld::Ptr& ptr) { - return (ptr.getClass().getNpcStats(ptr).getSkill(mIndex).*getter)(); - }); + return getValue(context, mObject, &SkillStat::setValue, mId, prop, [this, getter](const MWWorld::Ptr& ptr) { + return (ptr.getClass().getNpcStats(ptr).getSkill(mId).*getter)(); + }); } float getModified(const Context& context) const @@ -271,30 +275,31 @@ namespace MWLua sol::object getProgress(const Context& context) const { - return getValue( - context, mObject, &SkillStat::setValue, mIndex, "progress", [this](const MWWorld::Ptr& ptr) { - return getProgress(ptr, mIndex, ptr.getClass().getNpcStats(ptr).getSkill(mIndex)); - }); + return getValue(context, mObject, &SkillStat::setValue, mId, "progress", [this](const MWWorld::Ptr& ptr) { + return getProgress(ptr, mId, ptr.getClass().getNpcStats(ptr).getSkill(mId)); + }); } - static std::optional create(ObjectVariant object, int index) + static std::optional create(ObjectVariant object, Index index) { if (!object.ptr().getClass().isNpc()) return {}; - return SkillStat{ std::move(object), index }; + ESM::RefId id = std::get(index); + return SkillStat{ 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{ &SkillStat::setValue, mIndex, prop }] = value; + obj->mStatsCache[SelfObject::CachedStat{ &SkillStat::setValue, mId, prop }] = value; } - static void setValue(int index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) + static void setValue(Index index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) { + ESM::RefId id = std::get(index); auto& stats = ptr.getClass().getNpcStats(ptr); - auto stat = stats.getSkill(index); + auto stat = stats.getSkill(id); float floatValue = LuaUtil::cast(value); if (prop == "base") stat.setBase(floatValue); @@ -306,8 +311,8 @@ namespace MWLua else if (prop == "modifier") stat.setModifier(floatValue); else if (prop == "progress") - stat.setProgress(floatValue * getMaxProgress(ptr, index, stat)); - stats.setSkill(index, stat); + stat.setProgress(floatValue * getMaxProgress(ptr, id, stat)); + stats.setSkill(id, stat); } }; } @@ -385,7 +390,8 @@ namespace MWLua [context](const SkillStat& stat, const sol::object& value) { stat.cache(context, "progress", value); }); sol::table skills(context.mLua->sol(), sol::create); npcStats["skills"] = LuaUtil::makeReadOnly(skills); - for (int id = ESM::Skill::Block; id < ESM::Skill::Length; ++id) - skills[Misc::StringUtils::lowerCase(ESM::Skill::sSkillNames[id])] = addIndexedAccessor(id); + for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get()) + skills[Misc::StringUtils::lowerCase(ESM::Skill::sSkillNames[skill.mIndex])] + = addIndexedAccessor(skill.mId); } } diff --git a/apps/openmw/mwlua/types/book.cpp b/apps/openmw/mwlua/types/book.cpp index 2a4068f2fa..12daf52fd9 100644 --- a/apps/openmw/mwlua/types/book.cpp +++ b/apps/openmw/mwlua/types/book.cpp @@ -67,7 +67,7 @@ namespace MWLua sol::table skill(context.mLua->sol(), sol::create); book["SKILL"] = LuaUtil::makeStrictReadOnly(skill); book["createRecordDraft"] = tableToBook; - for (int id = ESM::Skill::Block; id < ESM::Skill::Length; ++id) + for (int id = 0; id < ESM::Skill::Length; ++id) { std::string skillName = Misc::StringUtils::lowerCase(ESM::Skill::sSkillNames[id]); skill[skillName] = skillName; diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index acab0edefa..3d5823ddf2 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -576,7 +576,7 @@ std::vector MWMechanics::Alchemy::effectsDescription(const MWWorld: if (effectID != -1) { const ESM::Attribute* attribute = store->get().search(data.mAttributes[i]); - const ESM::Skill* skill = store->get().search(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); effects.push_back(effect); diff --git a/apps/openmw/mwmechanics/autocalcspell.cpp b/apps/openmw/mwmechanics/autocalcspell.cpp index e2ad06854d..66ed8c7bab 100644 --- a/apps/openmw/mwmechanics/autocalcspell.cpp +++ b/apps/openmw/mwmechanics/autocalcspell.cpp @@ -27,7 +27,8 @@ namespace MWMechanics ESM::RefId mWeakestSpell; }; - std::vector autoCalcNpcSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race) + std::vector autoCalcNpcSpells( + const std::map& actorSkills, const int* actorAttributes, const ESM::Race* race) { const MWWorld::Store& gmst = MWBase::Environment::get().getESMStore()->get(); @@ -148,7 +149,7 @@ namespace MWMechanics } std::vector autoCalcPlayerSpells( - const int* actorSkills, const int* actorAttributes, const ESM::Race* race) + const std::map& actorSkills, const int* actorAttributes, const ESM::Race* race) { const MWWorld::ESMStore& esmStore = *MWBase::Environment::get().getESMStore(); @@ -226,7 +227,8 @@ namespace MWMechanics return selectedSpells; } - bool attrSkillCheck(const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes) + bool attrSkillCheck( + const ESM::Spell* spell, const std::map& actorSkills, const int* actorAttributes) { for (const auto& spellEffect : spell->mEffects.mList) { @@ -240,8 +242,9 @@ namespace MWMechanics if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)) { - assert(spellEffect.mSkill >= 0 && spellEffect.mSkill < ESM::Skill::Length); - if (actorSkills[spellEffect.mSkill] < iAutoSpellAttSkillMin) + ESM::RefId skill = ESM::Skill::indexToRefId(spellEffect.mSkill); + auto found = actorSkills.find(skill); + if (found == actorSkills.end() || found->second.getBase() < iAutoSpellAttSkillMin) return false; } @@ -256,7 +259,8 @@ namespace MWMechanics return true; } - void calcWeakestSchool(const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm) + void calcWeakestSchool(const ESM::Spell* spell, const std::map& actorSkills, + int& effectiveSchool, float& skillTerm) { // Morrowind for some reason uses a formula slightly different from magicka cost calculation float minChance = std::numeric_limits::max(); @@ -294,7 +298,11 @@ namespace MWMechanics if (effect.mRange == ESM::RT_Target) x *= 1.5f; - float s = 2.f * actorSkills[spellSchoolToSkill(magicEffect->mData.mSchool)]; + float s = 0.f; + ESM::RefId skill = spellSchoolToSkill(magicEffect->mData.mSchool); + auto found = actorSkills.find(skill); + if (found != actorSkills.end()) + s = 2.f * found->second.getBase(); if (s - x < minChance) { minChance = s - x; @@ -304,8 +312,8 @@ namespace MWMechanics } } - float calcAutoCastChance( - const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes, int effectiveSchool) + float calcAutoCastChance(const ESM::Spell* spell, const std::map& actorSkills, + const int* actorAttributes, int effectiveSchool) { if (spell->mData.mType != ESM::Spell::ST_Spell) return 100.f; @@ -315,7 +323,12 @@ namespace MWMechanics float skillTerm = 0; if (effectiveSchool != -1) - skillTerm = 2.f * actorSkills[spellSchoolToSkill(effectiveSchool)]; + { + ESM::RefId skill = spellSchoolToSkill(effectiveSchool); + auto found = actorSkills.find(skill); + if (found != actorSkills.end()) + skillTerm = 2.f * found->second.getBase(); + } else calcWeakestSchool( spell, actorSkills, effectiveSchool, skillTerm); // Note effectiveSchool is unused after this diff --git a/apps/openmw/mwmechanics/autocalcspell.hpp b/apps/openmw/mwmechanics/autocalcspell.hpp index c445321623..4c445b01ab 100644 --- a/apps/openmw/mwmechanics/autocalcspell.hpp +++ b/apps/openmw/mwmechanics/autocalcspell.hpp @@ -1,9 +1,12 @@ #ifndef OPENMW_AUTOCALCSPELL_H #define OPENMW_AUTOCALCSPELL_H +#include "creaturestats.hpp" #include +#include #include #include + namespace ESM { struct Spell; @@ -17,19 +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 int* actorSkills, const int* actorAttributes, const ESM::Race* race); + const std::map& actorSkills, const int* actorAttributes, const ESM::Race* race); std::vector autoCalcPlayerSpells( - const int* actorSkills, const int* actorAttributes, const ESM::Race* race); + const std::map& actorSkills, const int* actorAttributes, const ESM::Race* race); // Helpers - bool attrSkillCheck(const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes); + bool attrSkillCheck( + const ESM::Spell* spell, const std::map& actorSkills, const int* actorAttributes); - void calcWeakestSchool(const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm); + void calcWeakestSchool(const ESM::Spell* spell, const std::map& actorSkills, + int& effectiveSchool, float& skillTerm); - float calcAutoCastChance( - const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes, int effectiveSchool); + float calcAutoCastChance(const ESM::Spell* spell, const std::map& actorSkills, + const int* actorAttributes, int effectiveSchool); } diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 917d06bea6..b83eb1f10c 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -217,7 +217,7 @@ namespace MWMechanics bool validVictim = !victim.isEmpty() && victim.getClass().isActor(); - int weaponSkill = ESM::Skill::Marksman; + ESM::RefId weaponSkill = ESM::Skill::Marksman; if (!weapon.isEmpty()) weaponSkill = weapon.getClass().getEquipmentSkill(weapon); diff --git a/apps/openmw/mwmechanics/magiceffects.cpp b/apps/openmw/mwmechanics/magiceffects.cpp index b9918a398e..e75a1184a4 100644 --- a/apps/openmw/mwmechanics/magiceffects.cpp +++ b/apps/openmw/mwmechanics/magiceffects.cpp @@ -53,8 +53,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(mArg)); + return getMagicEffectString(*magicEffect, store->get().search(mArg), + store->get().search(ESM::Skill::indexToRefId(mArg))); } bool operator<(const EffectKey& left, const EffectKey& right) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index a2c2e85f3b..30154785ea 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -129,7 +129,7 @@ namespace MWMechanics creatureStats.getActiveSpells().clear(ptr); for (size_t i = 0; i < player->mNpdt.mSkills.size(); ++i) - npcStats.getSkill(i).setBase(player->mNpdt.mSkills[i]); + npcStats.getSkill(ESM::Skill::indexToRefId(i)).setBase(player->mNpdt.mSkills[i]); creatureStats.setAttribute(ESM::Attribute::Strength, player->mNpdt.mStrength); creatureStats.setAttribute(ESM::Attribute::Intelligence, player->mNpdt.mIntelligence); @@ -155,16 +155,16 @@ namespace MWMechanics creatureStats.setAttribute(i, male ? attribute.mMale : attribute.mFemale); } - for (int i = 0; i < 27; ++i) + for (const ESM::Skill& skill : esmStore.get()) { int bonus = 0; auto bonusIt = std::find_if(race->mData.mBonus.begin(), race->mData.mBonus.end(), - [i](const auto& bonus) { return bonus.mSkill == i; }); + [&](const auto& bonus) { return bonus.mSkill == skill.mIndex; }); if (bonusIt != race->mData.mBonus.end()) bonus = bonusIt->mBonus; - npcStats.getSkill(i).setBase(5 + bonus); + npcStats.getSkill(skill.mId).setBase(5 + bonus); } for (const ESM::RefId& power : race->mPowers.mList) @@ -205,29 +205,16 @@ namespace MWMechanics for (const auto& skills : class_->mData.mSkills) { - int index = skills[i]; - - if (index >= 0 && index < ESM::Skill::Length) - { - npcStats.getSkill(index).setBase(npcStats.getSkill(index).getBase() + bonus); - } + ESM::RefId id = ESM::Skill::indexToRefId(skills[i]); + if (!id.empty()) + npcStats.getSkill(id).setBase(npcStats.getSkill(id).getBase() + bonus); } } - const MWWorld::Store& skills = esmStore.get(); - - MWWorld::Store::iterator iter = skills.begin(); - for (; iter != skills.end(); ++iter) + for (const ESM::Skill& skill : esmStore.get()) { - if (iter->second.mData.mSpecialization == class_->mData.mSpecialization) - { - int index = iter->first; - - if (index >= 0 && index < 27) - { - npcStats.getSkill(index).setBase(npcStats.getSkill(index).getBase() + 5); - } - } + if (skill.mData.mSpecialization == class_->mData.mSpecialization) + npcStats.getSkill(skill.mId).setBase(npcStats.getSkill(skill.mId).getBase() + 5); } } @@ -236,16 +223,12 @@ namespace MWMechanics if (mRaceSelected) race = esmStore.get().find(player->mRace); - int skills[ESM::Skill::Length]; - for (int i = 0; i < ESM::Skill::Length; ++i) - skills[i] = npcStats.getSkill(i).getBase(); - int attributes[ESM::Attribute::Length]; for (int i = 0; i < ESM::Attribute::Length; ++i) attributes[i] = npcStats.getAttribute(i).getBase(); npcStats.updateHealth(); - std::vector selectedSpells = autoCalcPlayerSpells(skills, attributes, race); + std::vector selectedSpells = autoCalcPlayerSpells(npcStats.getSkills(), attributes, race); for (const ESM::RefId& spell : selectedSpells) creatureStats.getSpells().add(spell); @@ -1921,7 +1904,7 @@ namespace MWMechanics const ESM::Skill* acrobatics = MWBase::Environment::get().getESMStore()->get().find(ESM::Skill::Acrobatics); MWMechanics::NpcStats& stats = actor.getClass().getNpcStats(actor); - auto& skill = stats.getSkill(acrobatics->mIndex); + auto& skill = stats.getSkill(acrobatics->mId); skill.setModifier(acrobatics->mWerewolfValue - skill.getModified()); } diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index e9812e7931..b18c7cf616 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -30,6 +30,8 @@ MWMechanics::NpcStats::NpcStats() { mSkillIncreases.resize(ESM::Attribute::Length, 0); mSpecIncreases.resize(3, 0); + for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get()) + mSkills.emplace(skill.mId, SkillValue{}); } int MWMechanics::NpcStats::getBaseDisposition() const @@ -42,28 +44,28 @@ void MWMechanics::NpcStats::setBaseDisposition(int disposition) mDisposition = disposition; } -const MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill(int index) const +const MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill(ESM::RefId id) const { - if (index < 0 || index >= ESM::Skill::Length) - throw std::runtime_error("skill index out of range"); - - return mSkill[index]; + auto it = mSkills.find(id); + if (it == mSkills.end()) + throw std::runtime_error("skill not found"); + return it->second; } -MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill(int index) +MWMechanics::SkillValue& MWMechanics::NpcStats::getSkill(ESM::RefId id) { - if (index < 0 || index >= ESM::Skill::Length) - throw std::runtime_error("skill index out of range"); - - return mSkill[index]; + auto it = mSkills.find(id); + if (it == mSkills.end()) + throw std::runtime_error("skill not found"); + return it->second; } -void MWMechanics::NpcStats::setSkill(int index, const MWMechanics::SkillValue& value) +void MWMechanics::NpcStats::setSkill(ESM::RefId id, const MWMechanics::SkillValue& value) { - if (index < 0 || index >= ESM::Skill::Length) - throw std::runtime_error("skill index out of range"); - - mSkill[index] = value; + auto it = mSkills.find(id); + if (it == mSkills.end()) + throw std::runtime_error("skill not found"); + it->second = value; } const std::map& MWMechanics::NpcStats::getFactionRanks() const @@ -154,22 +156,23 @@ void MWMechanics::NpcStats::setFactionReputation(const ESM::RefId& faction, int mFactionReputation[faction] = value; } -float MWMechanics::NpcStats::getSkillProgressRequirement(int skillIndex, const ESM::Class& class_) const +float MWMechanics::NpcStats::getSkillProgressRequirement(ESM::RefId id, const ESM::Class& class_) const { - float progressRequirement = static_cast(1 + getSkill(skillIndex).getBase()); + float progressRequirement = 1.f + getSkill(id).getBase(); const MWWorld::Store& gmst = MWBase::Environment::get().getESMStore()->get(); + const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get().find(id); float typeFactor = gmst.find("fMiscSkillBonus")->mValue.getFloat(); for (const auto& skills : class_.mData.mSkills) { - if (skills[0] == skillIndex) + if (skills[0] == skill->mIndex) { typeFactor = gmst.find("fMinorSkillBonus")->mValue.getFloat(); break; } - else if (skills[1] == skillIndex) + else if (skills[1] == skill->mIndex) { typeFactor = gmst.find("fMajorSkillBonus")->mValue.getFloat(); break; @@ -183,7 +186,6 @@ float MWMechanics::NpcStats::getSkillProgressRequirement(int skillIndex, const E float specialisationFactor = 1; - const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get().find(skillIndex); if (skill->mData.mSpecialization == class_.mData.mSpecialization) { specialisationFactor = gmst.find("fSpecialSkillBonus")->mValue.getFloat(); @@ -196,9 +198,9 @@ float MWMechanics::NpcStats::getSkillProgressRequirement(int skillIndex, const E return progressRequirement; } -void MWMechanics::NpcStats::useSkill(int skillIndex, const ESM::Class& class_, int usageType, float extraFactor) +void MWMechanics::NpcStats::useSkill(ESM::RefId id, const ESM::Class& class_, int usageType, float extraFactor) { - const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get().find(skillIndex); + const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get().find(id); float skillGain = 1; if (usageType >= 4) throw std::runtime_error("skill usage type out of range"); @@ -210,21 +212,21 @@ void MWMechanics::NpcStats::useSkill(int skillIndex, const ESM::Class& class_, i } skillGain *= extraFactor; - MWMechanics::SkillValue& value = getSkill(skillIndex); + MWMechanics::SkillValue& value = getSkill(skill->mId); value.setProgress(value.getProgress() + skillGain); - if (int(value.getProgress()) >= int(getSkillProgressRequirement(skillIndex, class_))) + if (int(value.getProgress()) >= int(getSkillProgressRequirement(skill->mId, class_))) { // skill levelled up - increaseSkill(skillIndex, class_, false); + increaseSkill(skill->mId, class_, false); } } -void MWMechanics::NpcStats::increaseSkill( - int skillIndex, const ESM::Class& class_, bool preserveProgress, bool readBook) +void MWMechanics::NpcStats::increaseSkill(ESM::RefId id, const ESM::Class& class_, bool preserveProgress, bool readBook) { - float base = getSkill(skillIndex).getBase(); + const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get().find(id); + float base = getSkill(skill->mId).getBase(); if (base >= 100.f) return; @@ -237,13 +239,13 @@ void MWMechanics::NpcStats::increaseSkill( int increase = gmst.find("iLevelupMiscMultAttriubte")->mValue.getInteger(); // Note: GMST has a typo for (const auto& skills : class_.mData.mSkills) { - if (skills[0] == skillIndex) + if (skills[0] == skill->mIndex) { mLevelProgress += gmst.find("iLevelUpMinorMult")->mValue.getInteger(); increase = gmst.find("iLevelUpMinorMultAttribute")->mValue.getInteger(); break; } - else if (skills[1] == skillIndex) + else if (skills[1] == skill->mIndex) { mLevelProgress += gmst.find("iLevelUpMajorMult")->mValue.getInteger(); increase = gmst.find("iLevelUpMajorMultAttribute")->mValue.getInteger(); @@ -251,7 +253,6 @@ void MWMechanics::NpcStats::increaseSkill( } } - const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get().find(skillIndex); mSkillIncreases[skill->mData.mAttribute] += increase; mSpecIncreases[skill->mData.mSpecialization] += gmst.find("iLevelupSpecialization")->mValue.getInteger(); @@ -275,9 +276,9 @@ void MWMechanics::NpcStats::increaseSkill( MWBase::Environment::get().getWindowManager()->messageBox("#{sLevelUpMsg}", MWGui::ShowInDialogueMode_Never); } - getSkill(skillIndex).setBase(base); + getSkill(skill->mId).setBase(base); if (!preserveProgress) - getSkill(skillIndex).setProgress(0); + getSkill(skill->mId).setProgress(0); } int MWMechanics::NpcStats::getLevelProgress() const @@ -388,9 +389,10 @@ bool MWMechanics::NpcStats::hasSkillsForRank(const ESM::RefId& factionId, int ra std::vector skills; - for (int id : faction.mData.mSkills) + for (int index : faction.mData.mSkills) { - if (id != -1) + ESM::RefId id = ESM::Skill::indexToRefId(index); + if (!id.empty()) skills.push_back(static_cast(getSkill(id).getBase())); } @@ -470,8 +472,12 @@ void MWMechanics::NpcStats::writeState(ESM::NpcStats& state) const state.mDisposition = mDisposition; - for (size_t i = 0; i < state.mSkills.size(); ++i) - mSkill[i].writeState(state.mSkills[i]); + for (const auto& [id, value] : mSkills) + { + // TODO extend format + auto index = id.getIf()->getValue(); + value.writeState(state.mSkills[index]); + } state.mIsWerewolf = mIsWerewolf; @@ -524,7 +530,11 @@ void MWMechanics::NpcStats::readState(const ESM::NpcStats& state) mDisposition = state.mDisposition; for (size_t i = 0; i < state.mSkills.size(); ++i) - mSkill[i].readState(state.mSkills[i]); + { + // TODO extend format + ESM::RefId id = ESM::Skill::indexToRefId(i); + mSkills[id].readState(state.mSkills[i]); + } mIsWerewolf = state.mIsWerewolf; diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 57f4bbd1e4..b463d25c30 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -3,6 +3,7 @@ #include "creaturestats.hpp" #include +#include #include #include #include @@ -21,7 +22,7 @@ namespace MWMechanics class NpcStats : public CreatureStats { int mDisposition; - SkillValue mSkill[ESM::Skill::Length]; // SkillValue.mProgress used by the player only + std::map mSkills; // SkillValue.mProgress used by the player only int mReputation; int mCrimeId; @@ -58,9 +59,9 @@ namespace MWMechanics int getCrimeId() const; void setCrimeId(int id); - const SkillValue& getSkill(int index) const; - SkillValue& getSkill(int index); - void setSkill(int index, const SkillValue& value); + const SkillValue& getSkill(ESM::RefId id) const; + SkillValue& getSkill(ESM::RefId id); + void setSkill(ESM::RefId id, const SkillValue& value); int getFactionRank(const ESM::RefId& faction) const; const std::map& getFactionRanks() const; @@ -79,12 +80,12 @@ namespace MWMechanics bool isInFaction(const ESM::RefId& faction) const; - float getSkillProgressRequirement(int skillIndex, const ESM::Class& class_) const; + float getSkillProgressRequirement(ESM::RefId id, const ESM::Class& class_) const; - void useSkill(int skillIndex, const ESM::Class& class_, int usageType = -1, float extraFactor = 1.f); + void useSkill(ESM::RefId id, const ESM::Class& class_, int usageType = -1, float extraFactor = 1.f); ///< Increase skill by usage. - void increaseSkill(int skillIndex, const ESM::Class& class_, bool preserveProgress, bool readBook = false); + void increaseSkill(ESM::RefId id, const ESM::Class& class_, bool preserveProgress, bool readBook = false); int getLevelProgress() const; @@ -133,6 +134,8 @@ namespace MWMechanics void readState(const ESM::CreatureStats& state); void readState(const ESM::NpcStats& state); + + const std::map& getSkills() const { return mSkills; } }; } diff --git a/apps/openmw/mwmechanics/spelleffects.cpp b/apps/openmw/mwmechanics/spelleffects.cpp index 44729044a5..2302a6d70d 100644 --- a/apps/openmw/mwmechanics/spelleffects.cpp +++ b/apps/openmw/mwmechanics/spelleffects.cpp @@ -105,7 +105,7 @@ namespace void damageSkill(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude) { auto& npcStats = target.getClass().getNpcStats(target); - auto& skill = npcStats.getSkill(effect.mArg); + auto& skill = npcStats.getSkill(ESM::Skill::indexToRefId(effect.mArg)); if (effect.mEffectId == ESM::MagicEffect::DamageSkill) magnitude = std::min(skill.getModified(), magnitude); skill.damage(magnitude); @@ -114,14 +114,14 @@ namespace void restoreSkill(const MWWorld::Ptr& target, const ESM::ActiveEffect& effect, float magnitude) { auto& npcStats = target.getClass().getNpcStats(target); - auto& skill = npcStats.getSkill(effect.mArg); + auto& skill = npcStats.getSkill(ESM::Skill::indexToRefId(effect.mArg)); 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(effect.mArg); + auto& skill = npcStats.getSkill(ESM::Skill::indexToRefId(effect.mArg)); skill.setModifier(skill.getModifier() + magnitude); } @@ -668,7 +668,7 @@ namespace MWMechanics if (spellParams.getType() == ESM::ActiveSpells::Type_Ability) { auto& npcStats = target.getClass().getNpcStats(target); - SkillValue& skill = npcStats.getSkill(effect.mArg); + SkillValue& skill = npcStats.getSkill(ESM::Skill::indexToRefId(effect.mArg)); // Damage Skill abilities reduce base skill :todd: skill.setBase(std::max(skill.getBase() - effect.mMagnitude, 0.f)); } @@ -760,7 +760,7 @@ namespace MWMechanics { // Abilities affect base stats, but not for drain auto& npcStats = target.getClass().getNpcStats(target); - auto& skill = npcStats.getSkill(effect.mArg); + auto& skill = npcStats.getSkill(ESM::Skill::indexToRefId(effect.mArg)); skill.setBase(skill.getBase() + effect.mMagnitude); } else @@ -1218,7 +1218,7 @@ namespace MWMechanics if (spellParams.getType() == ESM::ActiveSpells::Type_Ability) { auto& npcStats = target.getClass().getNpcStats(target); - auto& skill = npcStats.getSkill(effect.mArg); + auto& skill = npcStats.getSkill(ESM::Skill::indexToRefId(effect.mArg)); skill.setBase(skill.getBase() - effect.mMagnitude); } else diff --git a/apps/openmw/mwmechanics/spellpriority.cpp b/apps/openmw/mwmechanics/spellpriority.cpp index 4e813ec37c..fbcfa9a9c1 100644 --- a/apps/openmw/mwmechanics/spellpriority.cpp +++ b/apps/openmw/mwmechanics/spellpriority.cpp @@ -565,7 +565,7 @@ namespace MWMechanics case ESM::MagicEffect::DrainSkill: if (enemy.isEmpty() || !enemy.getClass().isNpc()) return 0.f; - if (enemy.getClass().getSkill(enemy, effect.mSkill) <= 0) + if (enemy.getClass().getSkill(enemy, ESM::Skill::indexToRefId(effect.mSkill)) <= 0) return 0.f; break; @@ -600,7 +600,7 @@ namespace MWMechanics && (e != ESM::MagicEffect::BoundLongbow || effect.mEffectID == e || rateAmmo(actor, enemy, getWeaponType(ESM::Weapon::MarksmanBow)->mAmmoType) <= 0.f)) return 0.f; - ESM::Skill::SkillEnum skill = ESM::Skill::ShortBlade; + ESM::RefId skill = ESM::Skill::ShortBlade; if (effect.mEffectID == ESM::MagicEffect::BoundLongsword) skill = ESM::Skill::LongBlade; else if (effect.mEffectID == ESM::MagicEffect::BoundMace) diff --git a/apps/openmw/mwmechanics/spellutil.cpp b/apps/openmw/mwmechanics/spellutil.cpp index 60574880a1..a03b7577d3 100644 --- a/apps/openmw/mwmechanics/spellutil.cpp +++ b/apps/openmw/mwmechanics/spellutil.cpp @@ -15,9 +15,9 @@ namespace MWMechanics { - ESM::Skill::SkillEnum spellSchoolToSkill(int school) + ESM::RefId spellSchoolToSkill(int school) { - static const std::array schoolSkillArray{ + static const std::array schoolSkillArray{ ESM::Skill::Alteration, ESM::Skill::Conjuration, ESM::Skill::Destruction, diff --git a/apps/openmw/mwmechanics/spellutil.hpp b/apps/openmw/mwmechanics/spellutil.hpp index 40b1ea0ff0..8db37b9adf 100644 --- a/apps/openmw/mwmechanics/spellutil.hpp +++ b/apps/openmw/mwmechanics/spellutil.hpp @@ -17,7 +17,7 @@ namespace MWWorld namespace MWMechanics { - ESM::Skill::SkillEnum spellSchoolToSkill(int school); + ESM::RefId spellSchoolToSkill(int school); enum class EffectCostMethod { diff --git a/apps/openmw/mwmechanics/weaponpriority.cpp b/apps/openmw/mwmechanics/weaponpriority.cpp index 14ee1d7c02..c123a94056 100644 --- a/apps/openmw/mwmechanics/weaponpriority.cpp +++ b/apps/openmw/mwmechanics/weaponpriority.cpp @@ -120,8 +120,8 @@ namespace MWMechanics int value = 50.f; if (actor.getClass().isNpc()) { - int skill = item.getClass().getEquipmentSkill(item); - if (skill != -1) + ESM::RefId skill = item.getClass().getEquipmentSkill(item); + if (!skill.empty()) value = actor.getClass().getSkill(actor, skill); } else diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index e664dfbd45..edee3963e7 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -392,7 +392,7 @@ namespace MWScript return; } - int skill = it->getClass().getEquipmentSkill(*it); + ESM::RefId skill = it->getClass().getEquipmentSkill(*it); if (skill == ESM::Skill::HeavyArmor) runtime.push(2); else if (skill == ESM::Skill::MediumArmor) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 877fa79099..a3d2d89220 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -345,11 +345,11 @@ namespace MWScript template class OpGetSkill : public Interpreter::Opcode0 { - int mIndex; + ESM::RefId mId; public: - OpGetSkill(int index) - : mIndex(index) + OpGetSkill(ESM::RefId id) + : mId(id) { } @@ -357,7 +357,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Float value = ptr.getClass().getSkill(ptr, mIndex); + Interpreter::Type_Float value = ptr.getClass().getSkill(ptr, mId); runtime.push(value); } @@ -366,11 +366,11 @@ namespace MWScript template class OpSetSkill : public Interpreter::Opcode0 { - int mIndex; + ESM::RefId mId; public: - OpSetSkill(int index) - : mIndex(index) + OpSetSkill(ESM::RefId id) + : mId(id) { } @@ -383,18 +383,18 @@ namespace MWScript MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats(ptr); - stats.getSkill(mIndex).setBase(value, true); + stats.getSkill(mId).setBase(value, true); } }; template class OpModSkill : public Interpreter::Opcode0 { - int mIndex; + ESM::RefId mId; public: - OpModSkill(int index) - : mIndex(index) + OpModSkill(ESM::RefId id) + : mId(id) { } @@ -405,7 +405,7 @@ namespace MWScript Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); - MWMechanics::SkillValue& skill = ptr.getClass().getNpcStats(ptr).getSkill(mIndex); + MWMechanics::SkillValue& skill = ptr.getClass().getNpcStats(ptr).getSkill(mId); modStat(skill, value); } }; @@ -1364,14 +1364,15 @@ namespace MWScript for (int i = 0; i < Compiler::Stats::numberOfSkills; ++i) { - interpreter.installSegment5>(Compiler::Stats::opcodeGetSkill + i, i); - interpreter.installSegment5>(Compiler::Stats::opcodeGetSkillExplicit + i, i); + ESM::RefId id = ESM::Skill::indexToRefId(i); + interpreter.installSegment5>(Compiler::Stats::opcodeGetSkill + i, id); + interpreter.installSegment5>(Compiler::Stats::opcodeGetSkillExplicit + i, id); - interpreter.installSegment5>(Compiler::Stats::opcodeSetSkill + i, i); - interpreter.installSegment5>(Compiler::Stats::opcodeSetSkillExplicit + i, i); + interpreter.installSegment5>(Compiler::Stats::opcodeSetSkill + i, id); + interpreter.installSegment5>(Compiler::Stats::opcodeSetSkillExplicit + i, id); - interpreter.installSegment5>(Compiler::Stats::opcodeModSkill + i, i); - interpreter.installSegment5>(Compiler::Stats::opcodeModSkillExplicit + i, i); + interpreter.installSegment5>(Compiler::Stats::opcodeModSkill + i, id); + interpreter.installSegment5>(Compiler::Stats::opcodeModSkillExplicit + i, id); } interpreter.installSegment5(Compiler::Stats::opcodeGetPCCrimeLevel); diff --git a/apps/openmw/mwworld/actionapply.cpp b/apps/openmw/mwworld/actionapply.cpp index 17c4474727..bf4fce38ce 100644 --- a/apps/openmw/mwworld/actionapply.cpp +++ b/apps/openmw/mwworld/actionapply.cpp @@ -16,19 +16,4 @@ namespace MWWorld { actor.getClass().consume(getTarget(), actor); } - - ActionApplyWithSkill::ActionApplyWithSkill(const Ptr& object, const ESM::RefId& id, int skillIndex, int usageType) - : Action(false, object) - , mId(id) - , mSkillIndex(skillIndex) - , mUsageType(usageType) - { - } - - void ActionApplyWithSkill::executeImp(const Ptr& actor) - { - bool consumed = actor.getClass().consume(getTarget(), actor); - if (consumed && mUsageType != -1 && actor == MWMechanics::getPlayer()) - actor.getClass().skillUsageSucceeded(actor, mSkillIndex, mUsageType); - } } diff --git a/apps/openmw/mwworld/actionapply.hpp b/apps/openmw/mwworld/actionapply.hpp index f585179e6f..645b301915 100644 --- a/apps/openmw/mwworld/actionapply.hpp +++ b/apps/openmw/mwworld/actionapply.hpp @@ -16,18 +16,6 @@ namespace MWWorld public: ActionApply(const Ptr& object, const ESM::RefId& id); }; - - class ActionApplyWithSkill : public Action - { - ESM::RefId mId; - int mSkillIndex; - int mUsageType; - - void executeImp(const Ptr& actor) override; - - public: - ActionApplyWithSkill(const Ptr& object, const ESM::RefId& id, int skillIndex, int usageType); - }; } #endif diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index 10614f48da..e621eb3836 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -46,15 +47,15 @@ namespace MWWorld MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats(actor); // Skill gain from books - if (ref->mBase->mData.mSkillId >= 0 && ref->mBase->mData.mSkillId < ESM::Skill::Length - && !npcStats.hasBeenUsed(ref->mBase->mId)) + ESM::RefId skill = ESM::Skill::indexToRefId(ref->mBase->mData.mSkillId); + if (!skill.empty() && !npcStats.hasBeenUsed(ref->mBase->mId)) { MWWorld::LiveCellRef* playerRef = actor.get(); const ESM::Class* class_ = MWBase::Environment::get().getESMStore()->get().find(playerRef->mBase->mClass); - npcStats.increaseSkill(ref->mBase->mData.mSkillId, *class_, true, true); + npcStats.increaseSkill(skill, *class_, true, true); npcStats.flagAsUsed(ref->mBase->mId); } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 28777656c8..03e624a608 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -52,7 +52,7 @@ namespace MWWorld return false; } - void Class::skillUsageSucceeded(const MWWorld::Ptr& ptr, int skill, int usageType, float extraFactor) const + void Class::skillUsageSucceeded(const MWWorld::Ptr& ptr, ESM::RefId skill, int usageType, float extraFactor) const { throw std::runtime_error("class does not represent an actor"); } @@ -209,9 +209,9 @@ namespace MWWorld return std::make_pair(std::vector(), false); } - int Class::getEquipmentSkill(const ConstPtr& ptr) const + ESM::RefId Class::getEquipmentSkill(const ConstPtr& ptr) const { - return -1; + return {}; } int Class::getValue(const ConstPtr& ptr) const @@ -438,7 +438,7 @@ namespace MWWorld return canSwim(ptr) || canWalk(ptr) || canFly(ptr); } - float Class::getSkill(const MWWorld::Ptr& ptr, int skill) const + float Class::getSkill(const MWWorld::Ptr& ptr, ESM::RefId id) const { throw std::runtime_error("class does not support skills"); } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 59f228a4d4..fbd2b98a74 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -14,6 +14,7 @@ #include "../mwmechanics/aisetting.hpp" #include +#include namespace ESM { @@ -208,10 +209,9 @@ namespace MWWorld /// /// Default implementation: return (empty vector, false). - virtual int getEquipmentSkill(const ConstPtr& ptr) const; - /// Return the index of the skill this item corresponds to when equipped or -1, if there is - /// no such skill. - /// (default implementation: return -1) + virtual ESM::RefId getEquipmentSkill(const ConstPtr& ptr) const; + /// Return the index of the skill this item corresponds to when equipped. + /// (default implementation: return empty ref id) virtual int getValue(const ConstPtr& ptr) const; ///< Return trade value of the object. Throws an exception, if the object can't be traded. @@ -234,7 +234,7 @@ namespace MWWorld ///< Consume an item, e. g. a potion. virtual void skillUsageSucceeded( - const MWWorld::Ptr& ptr, int skill, int usageType, float extraFactor = 1.f) const; + const MWWorld::Ptr& ptr, ESM::RefId skill, int usageType, float extraFactor = 1.f) const; ///< Inform actor \a ptr that a skill use has succeeded. /// /// (default implementations: throws an exception) @@ -340,7 +340,7 @@ namespace MWWorld bool isPureLandCreature(const MWWorld::Ptr& ptr) const; bool isMobile(const MWWorld::Ptr& ptr) const; - virtual float getSkill(const MWWorld::Ptr& ptr, int skill) const; + virtual float getSkill(const MWWorld::Ptr& ptr, ESM::RefId id) const; virtual void readAdditionalState(const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const; ///< Read additional state from \a state into \a ptr. diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 34a298ae0d..e8204223aa 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -236,7 +236,7 @@ void MWWorld::InventoryStore::autoEquipWeapon(TSlots& slots_) return; } - static const ESM::Skill::SkillEnum weaponSkills[] = { + static const ESM::RefId weaponSkills[] = { ESM::Skill::LongBlade, ESM::Skill::Axe, ESM::Skill::Spear, @@ -285,7 +285,7 @@ void MWWorld::InventoryStore::autoEquipWeapon(TSlots& slots_) for (int j = 0; j < static_cast(weaponSkillsLength); ++j) { - float skillValue = mActor.getClass().getSkill(mActor, static_cast(weaponSkills[j])); + float skillValue = mActor.getClass().getSkill(mActor, weaponSkills[j]); if (skillValue > max && !weaponSkillVisited[j]) { max = skillValue; diff --git a/apps/openmw/mwworld/magiceffects.cpp b/apps/openmw/mwworld/magiceffects.cpp index 77525ad3df..d3e6db10a7 100644 --- a/apps/openmw/mwworld/magiceffects.cpp +++ b/apps/openmw/mwworld/magiceffects.cpp @@ -221,7 +221,7 @@ namespace MWWorld creatureStats.mAiSettings[i].mMod = 0.f; if (npcStats) { - for (std::size_t i = 0; i < ESM::Skill::Length; ++i) + for (std::size_t i = 0; i < npcStats->mSkills.size(); ++i) npcStats->mSkills[i].mMod = 0.f; } } diff --git a/apps/openmw/mwworld/player.cpp b/apps/openmw/mwworld/player.cpp index 5e3029af1e..bb70c46aa0 100644 --- a/apps/openmw/mwworld/player.cpp +++ b/apps/openmw/mwworld/player.cpp @@ -59,23 +59,23 @@ namespace MWWorld { MWMechanics::NpcStats& stats = getPlayer().getClass().getNpcStats(getPlayer()); - for (int i = 0; i < ESM::Skill::Length; ++i) - mSaveSkills[i] = stats.getSkill(i).getModified(); + for (size_t i = 0; i < mSaveSkills.size(); ++i) + mSaveSkills[i] = stats.getSkill(ESM::Skill::indexToRefId(i)).getModified(); for (int i = 0; i < ESM::Attribute::Length; ++i) mSaveAttributes[i] = stats.getAttribute(i).getModified(); } void Player::restoreStats() { - const MWWorld::Store& gmst - = MWBase::Environment::get().getESMStore()->get(); + const auto& store = MWBase::Environment::get().getESMStore(); + const MWWorld::Store& gmst = store->get(); MWMechanics::CreatureStats& creatureStats = getPlayer().getClass().getCreatureStats(getPlayer()); MWMechanics::NpcStats& npcStats = getPlayer().getClass().getNpcStats(getPlayer()); MWMechanics::DynamicStat health = creatureStats.getDynamic(0); creatureStats.setHealth(health.getBase() / gmst.find("fWereWolfHealth")->mValue.getFloat()); - for (int i = 0; i < ESM::Skill::Length; ++i) + for (size_t i = 0; i < mSaveSkills.size(); ++i) { - auto& skill = npcStats.getSkill(i); + auto& skill = npcStats.getSkill(ESM::Skill::indexToRefId(i)); skill.restore(skill.getDamage()); skill.setModifier(mSaveSkills[i] - skill.getBase()); } @@ -103,13 +103,13 @@ namespace MWWorld npcStats.setAttribute(attribute.mId, value); } - for (const auto& [_, skill] : store->get()) + for (const auto& skill : store->get()) { // Acrobatics is set separately for some reason. - if (skill.mIndex == ESM::Skill::Acrobatics) + if (skill.mId == ESM::Skill::Acrobatics) continue; - MWMechanics::SkillValue& value = npcStats.getSkill(skill.mIndex); + MWMechanics::SkillValue& value = npcStats.getSkill(skill.mId); value.setModifier(skill.mWerewolfValue - value.getModified()); } } @@ -251,10 +251,7 @@ namespace MWWorld mPreviousItems.clear(); mLastKnownExteriorPosition = osg::Vec3f(0, 0, 0); - for (int i = 0; i < ESM::Skill::Length; ++i) - { - mSaveSkills[i] = 0.f; - } + mSaveSkills.fill(0.f); for (int i = 0; i < ESM::Attribute::Length; ++i) { @@ -296,7 +293,7 @@ namespace MWWorld for (int i = 0; i < ESM::Attribute::Length; ++i) player.mSaveAttributes[i] = mSaveAttributes[i]; - for (int i = 0; i < ESM::Skill::Length; ++i) + for (size_t i = 0; i < mSaveSkills.size(); ++i) player.mSaveSkills[i] = mSaveSkills[i]; player.mPreviousItems = mPreviousItems; @@ -334,7 +331,7 @@ namespace MWWorld for (int i = 0; i < ESM::Attribute::Length; ++i) mSaveAttributes[i] = player.mSaveAttributes[i]; - for (int i = 0; i < ESM::Skill::Length; ++i) + for (size_t i = 0; i < mSaveSkills.size(); ++i) mSaveSkills[i] = player.mSaveSkills[i]; if (player.mObject.mNpcStats.mIsWerewolf) diff --git a/apps/openmw/mwworld/player.hpp b/apps/openmw/mwworld/player.hpp index 913ff00b02..12eadab10f 100644 --- a/apps/openmw/mwworld/player.hpp +++ b/apps/openmw/mwworld/player.hpp @@ -1,6 +1,7 @@ #ifndef GAME_MWWORLD_PLAYER_H #define GAME_MWWORLD_PLAYER_H +#include #include #include "../mwworld/livecellref.hpp" @@ -50,7 +51,7 @@ namespace MWWorld PreviousItems mPreviousItems; // Saved stats prior to becoming a werewolf - float mSaveSkills[ESM::Skill::Length]; + std::array mSaveSkills; float mSaveAttributes[ESM::Attribute::Length]; bool mJumping; diff --git a/apps/openmw/mwworld/store.cpp b/apps/openmw/mwworld/store.cpp index af281f4462..e219cc3cfb 100644 --- a/apps/openmw/mwworld/store.cpp +++ b/apps/openmw/mwworld/store.cpp @@ -114,7 +114,6 @@ namespace MWWorld // Need to instantiate these before they're used template class IndexedStore; - template class IndexedStore; template TypedDynamicStore::TypedDynamicStore() @@ -170,11 +169,19 @@ namespace MWWorld { if constexpr (std::is_same_v) { - std::vector results; - std::copy_if(mShared.begin(), mShared.end(), std::back_inserter(results), - [prefix](const T* item) { return item->mId.startsWith(prefix); }); - if (!results.empty()) - return results[Misc::Rng::rollDice(results.size(), prng)]; + if (prefix.empty()) + { + if (!mShared.empty()) + return mShared[Misc::Rng::rollDice(mShared.size(), prng)]; + } + else + { + std::vector results; + std::copy_if(mShared.begin(), mShared.end(), std::back_inserter(results), + [prefix](const T* item) { return item->mId.startsWith(prefix); }); + if (!results.empty()) + return results[Misc::Rng::rollDice(results.size(), prng)]; + } return nullptr; } else @@ -916,8 +923,6 @@ namespace MWWorld // Skill //========================================================================= - Store::Store() {} - void Store::setUp(const MWWorld::Store& settings) { constexpr std::string_view skillValues[ESM::Skill::Length][3] = { @@ -950,15 +955,13 @@ namespace MWWorld { "sSkillSpeechcraft", "icons\\k\\stealth_speechcraft.dds", "fWerewolfSpeechcraft" }, { "sSkillHandtohand", "icons\\k\\stealth_handtohand.dds", "fWerewolfHandtohand" }, }; - for (int i = 0; i < ESM::Skill::Length; ++i) + for (ESM::Skill* skill : mShared) { - auto found = mStatic.find(i); - if (found != mStatic.end()) + if (skill->mIndex >= 0) { - ESM::Skill& skill = found->second; - skill.mName = getGMSTString(settings, skillValues[i][0]); - skill.mIcon = skillValues[i][1]; - skill.mWerewolfValue = getGMSTFloat(settings, skillValues[i][2]); + skill->mName = getGMSTString(settings, skillValues[skill->mIndex][0]); + skill->mIcon = skillValues[skill->mIndex][1]; + skill->mWerewolfValue = getGMSTFloat(settings, skillValues[skill->mIndex][2]); } } } @@ -1363,7 +1366,7 @@ template class MWWorld::TypedDynamicStore; 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 2e73007113..47463dd91f 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -30,7 +31,6 @@ namespace ESM struct Attribute; struct LandTexture; struct MagicEffect; - struct Skill; struct WeaponType; class ESMReader; class ESMWriter; @@ -478,10 +478,12 @@ namespace MWWorld }; template <> - class Store : public IndexedStore + class Store : public TypedDynamicStore { + using TypedDynamicStore::setUp; + public: - Store(); + Store() = default; void setUp(const MWWorld::Store& settings); }; diff --git a/apps/openmw_test_suite/mwworld/test_store.cpp b/apps/openmw_test_suite/mwworld/test_store.cpp index ba54e4a5e0..eddc4137d0 100644 --- a/apps/openmw_test_suite/mwworld/test_store.cpp +++ b/apps/openmw_test_suite/mwworld/test_store.cpp @@ -496,7 +496,7 @@ namespace const RecordType* result = nullptr; if constexpr (std::is_same_v) result = esmStore.get().search(index, 0); - else if constexpr (ESM::hasIndex) + else if constexpr (ESM::hasIndex && !std::is_same_v) result = esmStore.get().search(index); else result = esmStore.get().search(refId); diff --git a/components/esm3/loadskil.cpp b/components/esm3/loadskil.cpp index 9fbbbdae2b..983ef8a152 100644 --- a/components/esm3/loadskil.cpp +++ b/components/esm3/loadskil.cpp @@ -37,11 +37,11 @@ namespace ESM "Handtohand", }; - Skill::SkillEnum Skill::stringToSkillId(std::string_view skill) + int Skill::stringToSkillId(std::string_view skill) { for (int id = 0; id < Skill::Length; ++id) if (Misc::StringUtils::ciEqual(sSkillNames[id], skill)) - return Skill::SkillEnum(id); + return id; throw std::logic_error("No such skill: " + std::string(skill)); } @@ -75,6 +75,8 @@ namespace ESM } if (!hasIndex) esm.fail("Missing INDX"); + else if (mIndex < 0 || mIndex >= Length) + esm.fail("Invalid INDX"); if (!hasData) esm.fail("Missing SKDT"); @@ -101,7 +103,7 @@ namespace ESM RefId Skill::indexToRefId(int index) { - if (index == -1) + if (index < 0 || index >= Length) return RefId(); return RefId::index(sRecordId, static_cast(index)); } diff --git a/components/esm3/loadskil.hpp b/components/esm3/loadskil.hpp index a0dc56f1d4..ea3dd6954b 100644 --- a/components/esm3/loadskil.hpp +++ b/components/esm3/loadskil.hpp @@ -41,47 +41,44 @@ namespace ESM // Skill index. Skils don't have an id ("NAME") like most records, // they only have a numerical index that matches one of the // hard-coded skills in the game. - int mIndex; + int mIndex{ -1 }; std::string mDescription; std::string mName; std::string mIcon; float mWerewolfValue{}; - enum SkillEnum - { - Block = 0, - Armorer = 1, - MediumArmor = 2, - HeavyArmor = 3, - BluntWeapon = 4, - LongBlade = 5, - Axe = 6, - Spear = 7, - Athletics = 8, - Enchant = 9, - Destruction = 10, - Alteration = 11, - Illusion = 12, - Conjuration = 13, - Mysticism = 14, - Restoration = 15, - Alchemy = 16, - Unarmored = 17, - Security = 18, - Sneak = 19, - Acrobatics = 20, - LightArmor = 21, - ShortBlade = 22, - Marksman = 23, - Mercantile = 24, - Speechcraft = 25, - HandToHand = 26, - Length - }; + static constexpr IndexRefId Block{ sRecordId, 0 }; + static constexpr IndexRefId Armorer{ sRecordId, 1 }; + static constexpr IndexRefId MediumArmor{ sRecordId, 2 }; + static constexpr IndexRefId HeavyArmor{ sRecordId, 3 }; + static constexpr IndexRefId BluntWeapon{ sRecordId, 4 }; + static constexpr IndexRefId LongBlade{ sRecordId, 5 }; + static constexpr IndexRefId Axe{ sRecordId, 6 }; + static constexpr IndexRefId Spear{ sRecordId, 7 }; + static constexpr IndexRefId Athletics{ sRecordId, 8 }; + static constexpr IndexRefId Enchant{ sRecordId, 9 }; + static constexpr IndexRefId Destruction{ sRecordId, 10 }; + static constexpr IndexRefId Alteration{ sRecordId, 11 }; + static constexpr IndexRefId Illusion{ sRecordId, 12 }; + static constexpr IndexRefId Conjuration{ sRecordId, 13 }; + static constexpr IndexRefId Mysticism{ sRecordId, 14 }; + static constexpr IndexRefId Restoration{ sRecordId, 15 }; + static constexpr IndexRefId Alchemy{ sRecordId, 16 }; + static constexpr IndexRefId Unarmored{ sRecordId, 17 }; + static constexpr IndexRefId Security{ sRecordId, 18 }; + static constexpr IndexRefId Sneak{ sRecordId, 19 }; + static constexpr IndexRefId Acrobatics{ sRecordId, 20 }; + static constexpr IndexRefId LightArmor{ sRecordId, 21 }; + static constexpr IndexRefId ShortBlade{ sRecordId, 22 }; + static constexpr IndexRefId Marksman{ sRecordId, 23 }; + static constexpr IndexRefId Mercantile{ sRecordId, 24 }; + static constexpr IndexRefId Speechcraft{ sRecordId, 25 }; + static constexpr IndexRefId HandToHand{ sRecordId, 26 }; + static constexpr int Length = 27; static const std::string sSkillNames[Length]; - static SkillEnum stringToSkillId(std::string_view skill); + static int stringToSkillId(std::string_view skill); void load(ESMReader& esm, bool& isDeleted); void save(ESMWriter& esm, bool isDeleted = false) const; diff --git a/components/esm3/loadweap.hpp b/components/esm3/loadweap.hpp index d3a2a1f053..bc2af28edd 100644 --- a/components/esm3/loadweap.hpp +++ b/components/esm3/loadweap.hpp @@ -109,13 +109,13 @@ namespace ESM ESM::RefId mSoundIdUp; std::string mAttachBone; std::string mSheathingBone; - Skill::SkillEnum mSkill; + ESM::RefId mSkill; Class mWeaponClass; int mAmmoType; int mFlags; WeaponType(std::string shortGroup, std::string longGroup, const std::string& soundId, std::string attachBone, - std::string sheathingBone, Skill::SkillEnum skill, Class weaponClass, int ammoType, int flags) + std::string sheathingBone, ESM::RefId skill, Class weaponClass, int ammoType, int flags) : mShortGroup(std::move(shortGroup)) , mLongGroup(std::move(longGroup)) , mSoundIdDown(ESM::RefId::stringRefId(soundId + " Down")) diff --git a/components/esm3/player.cpp b/components/esm3/player.cpp index 4c56c7488b..9c2cad86d1 100644 --- a/components/esm3/player.cpp +++ b/components/esm3/player.cpp @@ -73,7 +73,8 @@ namespace ESM mSaveSkills[i] = skill.mBase + skill.mMod - skill.mDamage; if (mObject.mNpcStats.mIsWerewolf) { - if (i == Skill::Acrobatics) + constexpr int Acrobatics = 20; + if (i == Acrobatics) mSetWerewolfAcrobatics = mObject.mNpcStats.mSkills[i].mBase != skill.mBase; mObject.mNpcStats.mSkills[i] = skill; } diff --git a/files/data/mygui/openmw_chargen_select_skill.layout b/files/data/mygui/openmw_chargen_select_skill.layout index c047d7158d..c449c7d0af 100644 --- a/files/data/mygui/openmw_chargen_select_skill.layout +++ b/files/data/mygui/openmw_chargen_select_skill.layout @@ -14,45 +14,27 @@ - - - - - - - - - + + + - - - - - - - - - + + + - - - - - - - - - + + +