1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-21 09:39:56 +00:00

Use RefId key for NPC skills

This commit is contained in:
Evil Eye 2023-06-05 21:21:30 +02:00
parent 1b956521fc
commit 47c1569961
29 changed files with 228 additions and 196 deletions

View File

@ -298,7 +298,7 @@ namespace MWClass
const MWWorld::LiveCellRef<ESM::Armor>* ref = ptr.get<ESM::Armor>();
int armorSkillType = getEquipmentSkill(ptr);
float armorSkill = actor.getClass().getSkill(actor, armorSkillType);
float armorSkill = actor.getClass().getSkill(actor, ESM::Skill::indexToRefId(armorSkillType));
int iBaseArmorSkill = MWBase::Environment::get()
.getESMStore()

View File

@ -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<ESM::Creature>* ref = ptr.get<ESM::Creature>();
const ESM::Skill* skillRecord = MWBase::Environment::get().getESMStore()->get<ESM::Skill>().find(skill);
const ESM::Skill* skillRecord = MWBase::Environment::get().getESMStore()->get<ESM::Skill>().find(id);
switch (skillRecord->mData.mSpecialization)
{

View File

@ -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;

View File

@ -181,10 +181,10 @@ 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);
}
}
}
@ -220,23 +220,19 @@ namespace
specBonus = 5;
}
npcStats.getSkill(skillIndex)
.setBase(std::min(round_ieee_754(npcStats.getSkill(skillIndex).getBase() + 5 + raceBonus + specBonus
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<ESM::RefId> spells = MWMechanics::autoCalcNpcSpells(skills, attributes, race);
std::vector<ESM::RefId> spells = MWMechanics::autoCalcNpcSpells(npcStats.getSkills(), attributes, race);
npcStats.getSpells().addAllToInstance(spells);
}
}
@ -315,7 +311,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);
@ -593,7 +589,7 @@ namespace MWClass
if (!weapon.isEmpty())
weapskill = weapon.getClass().getEquipmentSkill(weapon);
float hitchance = MWMechanics::getHitChance(ptr, victim, getSkill(ptr, weapskill));
float hitchance = MWMechanics::getHitChance(ptr, victim, getSkill(ptr, ESM::Skill::indexToRefId(weapskill)));
return Misc::Rng::roll0to99(world->getPrng()) < hitchance;
}
@ -1052,7 +1048,7 @@ namespace MWClass
const float encumbranceTerm = gmst.fJumpEncumbranceBase->mValue.getFloat()
+ gmst.fJumpEncumbranceMultiplier->mValue.getFloat() * (1.0f - Npc::getNormalizedEncumbrance(ptr));
float a = getSkill(ptr, ESM::Skill::Acrobatics);
float a = Class::getSkill(ptr, ESM::Skill::Acrobatics);
float b = 0.0f;
if (a > 50.0f)
{
@ -1185,7 +1181,7 @@ namespace MWClass
float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat();
float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat();
float unarmoredSkill = getSkill(ptr, ESM::Skill::Unarmored);
float unarmoredSkill = Class::getSkill(ptr, ESM::Skill::Unarmored);
float ratings[MWWorld::InventoryStore::Slots];
for (int i = 0; i < MWWorld::InventoryStore::Slots; i++)
@ -1355,9 +1351,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
@ -1550,7 +1546,7 @@ namespace MWClass
{
const GMST& gmst = getGmst();
return getWalkSpeed(ptr)
* (0.01f * getSkill(ptr, ESM::Skill::Athletics) * gmst.fAthleticsRunBonus->mValue.getFloat()
* (0.01f * Class::getSkill(ptr, ESM::Skill::Athletics) * gmst.fAthleticsRunBonus->mValue.getFloat()
+ gmst.fBaseRunMultiplier->mValue.getFloat());
}

View File

@ -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;

View File

@ -5,6 +5,7 @@
#include <components/esm3/loadcrea.hpp>
#include <components/esm3/loadfact.hpp>
#include <components/esm3/loadmgef.hpp>
#include <components/esm3/loadskil.hpp>
#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<int>(player.getClass().getNpcStats(player).getSkill(select.getArgument()).getModified());
{
ESM::RefId skill = ESM::Skill::indexToRefId(select.getArgument());
return static_cast<int>(player.getClass().getNpcStats(player).getSkill(skill).getModified());
}
case SelectWrapper::Function_FriendlyHit:
{
int hits = mActor.getClass().getCreatureStats(mActor).getFriendlyHits();

View File

@ -96,7 +96,7 @@ namespace MWGui
const ESM::Skill* skill = skillStore.searchRandom({}, prng);
skills.insert(skill);
MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill->mIndex);
MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill->mId);
if (skill->mIndex == ESM::Skill::Security || skill->mIndex == ESM::Skill::Sneak)
value.setBase(std::min(100.f, value.getBase() + 1));
else
@ -116,7 +116,7 @@ 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)
skillMsg = gmst.find("sNotifyMessage39")->mValue.getString();

View File

@ -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<ESM::Skill>())
{
mSkillValues.insert(std::make_pair(i, MWMechanics::SkillValue()));
mSkillWidgetMap.insert(std::make_pair(i, static_cast<MyGUI::TextBox*>(nullptr)));
mSkillValues.emplace(skill.mId, MWMechanics::SkillValue());
mSkillWidgetMap.emplace(skill.mIndex, static_cast<MyGUI::TextBox*>(nullptr));
}
MyGUI::Button* backButton;
@ -206,11 +206,12 @@ namespace MWGui
void ReviewDialog::setSkillValue(ESM::Skill::SkillEnum skillId, const MWMechanics::SkillValue& value)
{
mSkillValues[skillId] = value;
mSkillValues[ESM::Skill::indexToRefId(skillId)] = value;
MyGUI::TextBox* widget = mSkillWidgetMap[skillId];
if (widget)
{
float modified = static_cast<float>(value.getModified()), base = static_cast<float>(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)
@ -345,7 +346,7 @@ namespace MWGui
ESM::Skill::indexToRefId(skillIndex));
if (!skill) // Skip unknown skills
continue;
const MWMechanics::SkillValue& stat = mSkillValues.find(skill->mIndex)->second;
const MWMechanics::SkillValue& stat = mSkillValues.find(skill->mId)->second;
int base = stat.getBase();
int modified = stat.getModified();
@ -394,15 +395,11 @@ namespace MWGui
if (!mRaceId.empty())
race = MWBase::Environment::get().getESMStore()->get<ESM::Race>().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<ESM::RefId> selectedSpells = MWMechanics::autoCalcPlayerSpells(skills, attributes, race);
std::vector<ESM::RefId> selectedSpells = MWMechanics::autoCalcPlayerSpells(mSkillValues, attributes, race);
for (ESM::RefId& spellId : selectedSpells)
{
if (std::find(spells.begin(), spells.end(), spellId) == spells.end())

View File

@ -94,7 +94,7 @@ namespace MWGui
std::map<int, Widgets::MWAttributePtr> mAttributeWidgets;
SkillList mMajorSkills, mMinorSkills, mMiscSkills;
std::map<int, MWMechanics::SkillValue> mSkillValues;
std::map<ESM::RefId, MWMechanics::SkillValue> mSkillValues;
std::map<int, MyGUI::TextBox*> mSkillWidgetMap;
ESM::RefId mRaceId, mBirthSignId;
std::string mName;

View File

@ -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<ESM::Skill>())
{
if (stats.getSkill(i) != mWatchedSkills[i] || mWatchedStatsEmpty)
const auto& value = stats.getSkill(skill.mId);
if (value != mWatchedSkills[skill.mIndex] || mWatchedStatsEmpty)
{
mWatchedSkills[i] = stats.getSkill(i);
setValue((ESM::Skill::SkillEnum)i, stats.getSkill(i));
mWatchedSkills[skill.mIndex] = value;
setValue(ESM::Skill::SkillEnum(skill.mIndex), value);
}
}

View File

@ -237,7 +237,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();
@ -296,8 +296,9 @@ namespace MWGui
valueWidget->setUserString("Visible_SkillProgressVBox", "true");
valueWidget->setUserString("UserData^Hidden_SkillProgressVBox", "false");
setSkillProgress(nameWidget, value.getProgress(), parSkill);
setSkillProgress(valueWidget, value.getProgress(), parSkill);
ESM::RefId id = ESM::Skill::indexToRefId(parSkill);
setSkillProgress(nameWidget, value.getProgress(), id);
setSkillProgress(valueWidget, value.getProgress(), id);
}
else
{

View File

@ -72,7 +72,7 @@ namespace MWGui
MWMechanics::NpcStats const& actorStats(actor.getClass().getNpcStats(actor));
for (const ESM::Skill& skill : skillStore)
{
float value = getSkillForTraining(actorStats, skill.mIndex);
float value = getSkillForTraining(actorStats, skill.mId);
skills.emplace_back(&skill, value);
}
@ -92,7 +92,7 @@ namespace MWGui
{
const ESM::Skill* skill = skills[i].first;
int price = static_cast<int>(
pcStats.getSkill(skill->mIndex).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);
@ -135,22 +135,22 @@ namespace MWGui
const MWWorld::ESMStore& store = *MWBase::Environment::get().getESMStore();
int price = pcStats.getSkill(skill->mIndex).getBase()
int price = pcStats.getSkill(skill->mId).getBase()
* store.get<ESM::GameSetting>().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), skill->mIndex)
<= pcStats.getSkill(skill->mIndex).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
if (pcStats.getSkill(skill->mIndex).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;
@ -196,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)

View File

@ -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;

View File

@ -25,8 +25,8 @@ namespace
using SelfObject = MWLua::SelfObject;
using ObjectVariant = MWLua::ObjectVariant;
template <class T>
auto addIndexedAccessor(int index)
template <class T, class I>
auto addIndexedAccessor(I index)
{
return [index](const sol::object& o) { return T::create(ObjectVariant(o), index); };
}
@ -228,37 +228,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<ESM::Class>().find(ptr.get<ESM::NPC>()->mBase->mClass);
return ptr.getClass().getNpcStats(ptr).getSkillProgressRequirement(index, *cl);
return ptr.getClass().getNpcStats(ptr).getSkillProgressRequirement(id, *cl);
}
public:
template <class G>
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.getIf<ESM::IndexRefId>()->getValue(), prop,
[this, getter](
const MWWorld::Ptr& ptr) { return (ptr.getClass().getNpcStats(ptr).getSkill(mId).*getter)(); });
}
float getModified(const Context& context) const
@ -271,30 +270,33 @@ 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.getIf<ESM::IndexRefId>()->getValue(),
"progress", [this](const MWWorld::Ptr& ptr) {
return getProgress(ptr, mId, ptr.getClass().getNpcStats(ptr).getSkill(mId));
});
}
static std::optional<SkillStat> create(ObjectVariant object, int index)
static std::optional<SkillStat> create(ObjectVariant object, ESM::RefId id)
{
if (!object.ptr().getClass().isNpc())
return {};
return SkillStat{ std::move(object), 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, int(mId.getIf<ESM::IndexRefId>()->getValue()), prop }]
= value;
}
static void setValue(int index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
{
ESM::RefId id = ESM::Skill::indexToRefId(index);
auto& stats = ptr.getClass().getNpcStats(ptr);
auto stat = stats.getSkill(index);
auto stat = stats.getSkill(id);
float floatValue = LuaUtil::cast<float>(value);
if (prop == "base")
stat.setBase(floatValue);
@ -306,8 +308,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 +387,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<SkillStat>(id);
for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get<ESM::Skill>())
skills[Misc::StringUtils::lowerCase(ESM::Skill::sSkillNames[skill.mIndex])]
= addIndexedAccessor<SkillStat>(skill.mId);
}
}

View File

@ -27,7 +27,8 @@ namespace MWMechanics
ESM::RefId mWeakestSpell;
};
std::vector<ESM::RefId> autoCalcNpcSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race)
std::vector<ESM::RefId> autoCalcNpcSpells(
const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes, const ESM::Race* race)
{
const MWWorld::Store<ESM::GameSetting>& gmst
= MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
@ -148,7 +149,7 @@ namespace MWMechanics
}
std::vector<ESM::RefId> autoCalcPlayerSpells(
const int* actorSkills, const int* actorAttributes, const ESM::Race* race)
const std::map<ESM::RefId, SkillValue>& 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<ESM::RefId, SkillValue>& 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<ESM::RefId, SkillValue>& actorSkills,
int& effectiveSchool, float& skillTerm)
{
// Morrowind for some reason uses a formula slightly different from magicka cost calculation
float minChance = std::numeric_limits<float>::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 = ESM::Skill::indexToRefId(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<ESM::RefId, SkillValue>& actorSkills,
const int* actorAttributes, int effectiveSchool)
{
if (spell->mData.mType != ESM::Spell::ST_Spell)
return 100.f;
@ -314,8 +322,10 @@ namespace MWMechanics
return 100.f;
float skillTerm = 0;
if (effectiveSchool != -1)
skillTerm = 2.f * actorSkills[spellSchoolToSkill(effectiveSchool)];
ESM::RefId skill = ESM::Skill::indexToRefId(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

View File

@ -1,9 +1,12 @@
#ifndef OPENMW_AUTOCALCSPELL_H
#define OPENMW_AUTOCALCSPELL_H
#include "creaturestats.hpp"
#include <components/esm/refid.hpp>
#include <map>
#include <string>
#include <vector>
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<ESM::RefId> autoCalcNpcSpells(
const int* actorSkills, const int* actorAttributes, const ESM::Race* race);
const std::map<ESM::RefId, SkillValue>& actorSkills, const int* actorAttributes, const ESM::Race* race);
std::vector<ESM::RefId> autoCalcPlayerSpells(
const int* actorSkills, const int* actorAttributes, const ESM::Race* race);
const std::map<ESM::RefId, SkillValue>& 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<ESM::RefId, SkillValue>& 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<ESM::RefId, SkillValue>& 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<ESM::RefId, SkillValue>& actorSkills,
const int* actorAttributes, int effectiveSchool);
}

View File

@ -122,7 +122,8 @@ namespace MWMechanics
if (weapon.isEmpty())
attackerSkill = attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand);
else
attackerSkill = attacker.getClass().getSkill(attacker, weapon.getClass().getEquipmentSkill(weapon));
attackerSkill = attacker.getClass().getSkill(
attacker, ESM::Skill::indexToRefId(weapon.getClass().getEquipmentSkill(weapon)));
float attackerTerm = attackerSkill + 0.2f * attackerStats.getAttribute(ESM::Attribute::Agility).getModified()
+ 0.1f * attackerStats.getAttribute(ESM::Attribute::Luck).getModified();
attackerTerm *= attackerStats.getFatigueTerm();
@ -227,7 +228,7 @@ namespace MWMechanics
if (attacker == getPlayer())
MWBase::Environment::get().getWindowManager()->setEnemy(victim);
int skillValue = attacker.getClass().getSkill(attacker, weaponSkill);
int skillValue = attacker.getClass().getSkill(attacker, ESM::Skill::indexToRefId(weaponSkill));
if (Misc::Rng::roll0to99(world->getPrng()) >= getHitChance(attacker, victim, skillValue))
{

View File

@ -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);
@ -164,7 +164,7 @@ namespace MWMechanics
if (bonusIt != race->mData.mBonus.end())
bonus = bonusIt->mBonus;
npcStats.getSkill(skill.mIndex).setBase(5 + bonus);
npcStats.getSkill(skill.mId).setBase(5 + bonus);
}
for (const ESM::RefId& power : race->mPowers.mList)
@ -205,19 +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);
}
}
for (const ESM::Skill& skill : esmStore.get<ESM::Skill>())
{
if (skill.mData.mSpecialization == class_->mData.mSpecialization)
npcStats.getSkill(skill.mIndex).setBase(npcStats.getSkill(skill.mIndex).getBase() + 5);
npcStats.getSkill(skill.mId).setBase(npcStats.getSkill(skill.mId).getBase() + 5);
}
}
@ -226,16 +223,12 @@ namespace MWMechanics
if (mRaceSelected)
race = esmStore.get<ESM::Race>().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<ESM::RefId> selectedSpells = autoCalcPlayerSpells(skills, attributes, race);
std::vector<ESM::RefId> selectedSpells = autoCalcPlayerSpells(npcStats.getSkills(), attributes, race);
for (const ESM::RefId& spell : selectedSpells)
creatureStats.getSpells().add(spell);
@ -1914,7 +1907,7 @@ namespace MWMechanics
const ESM::Skill* acrobatics
= MWBase::Environment::get().getESMStore()->get<ESM::Skill>().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());
}

View File

@ -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<ESM::Skill>())
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<ESM::RefId, int>& 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<float>(1 + getSkill(skillIndex).getBase());
float progressRequirement = 1.f + getSkill(id).getBase();
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get<ESM::Skill>().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<ESM::Skill>().find(skillIndex);
if (skill->mData.mSpecialization == class_.mData.mSpecialization)
{
specialisationFactor = gmst.find("fSpecialSkillBonus")->mValue.getFloat();
@ -210,11 +212,11 @@ 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);
@ -224,7 +226,8 @@ void MWMechanics::NpcStats::useSkill(int skillIndex, const ESM::Class& class_, i
void MWMechanics::NpcStats::increaseSkill(
int skillIndex, const ESM::Class& class_, bool preserveProgress, bool readBook)
{
float base = getSkill(skillIndex).getBase();
const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get<ESM::Skill>().find(skillIndex);
float base = getSkill(skill->mId).getBase();
if (base >= 100.f)
return;
@ -251,7 +254,6 @@ void MWMechanics::NpcStats::increaseSkill(
}
}
const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get<ESM::Skill>().find(skillIndex);
mSkillIncreases[skill->mData.mAttribute] += increase;
mSpecIncreases[skill->mData.mSpecialization] += gmst.find("iLevelupSpecialization")->mValue.getInteger();
@ -275,9 +277,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 +390,10 @@ bool MWMechanics::NpcStats::hasSkillsForRank(const ESM::RefId& factionId, int ra
std::vector<int> 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<int>(getSkill(id).getBase()));
}
@ -470,8 +473,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<ESM::IndexRefId>()->getValue();
value.writeState(state.mSkills[index]);
}
state.mIsWerewolf = mIsWerewolf;
@ -524,7 +531,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;

View File

@ -3,6 +3,7 @@
#include "creaturestats.hpp"
#include <components/esm/refid.hpp>
#include <components/esm3/loadskil.hpp>
#include <map>
#include <set>
#include <string>
@ -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<ESM::RefId, SkillValue> mSkills; // SkillValue.mProgress used by the player only
int mReputation;
int mCrimeId;
@ -58,9 +59,13 @@ 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;
const SkillValue& getSkill(ESM::Skill::SkillEnum index) const
{
return getSkill(ESM::Skill::indexToRefId(index));
}
SkillValue& getSkill(ESM::RefId id);
void setSkill(ESM::RefId id, const SkillValue& value);
int getFactionRank(const ESM::RefId& faction) const;
const std::map<ESM::RefId, int>& getFactionRanks() const;
@ -79,7 +84,7 @@ 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);
///< Increase skill by usage.
@ -133,6 +138,8 @@ namespace MWMechanics
void readState(const ESM::CreatureStats& state);
void readState(const ESM::NpcStats& state);
const std::map<ESM::RefId, SkillValue>& getSkills() const { return mSkills; }
};
}

View File

@ -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

View File

@ -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;

View File

@ -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 = ESM::Skill::indexToRefId(item.getClass().getEquipmentSkill(item));
if (!skill.empty())
value = actor.getClass().getSkill(actor, skill);
}
else
@ -179,7 +179,9 @@ namespace MWMechanics
if (weapon.isEmpty())
return 0.f;
float skillMult = actor.getClass().getSkill(actor, weapon.getClass().getEquipmentSkill(weapon)) * 0.01f;
float skillMult
= actor.getClass().getSkill(actor, ESM::Skill::indexToRefId(weapon.getClass().getEquipmentSkill(weapon)))
* 0.01f;
float chopMult = fAIMeleeWeaponMult;
float bonusDamage = 0.f;

View File

@ -345,11 +345,11 @@ namespace MWScript
template <class R>
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 R>
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 R>
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<OpGetSkill<ImplicitRef>>(Compiler::Stats::opcodeGetSkill + i, i);
interpreter.installSegment5<OpGetSkill<ExplicitRef>>(Compiler::Stats::opcodeGetSkillExplicit + i, i);
ESM::RefId id = ESM::Skill::indexToRefId(i);
interpreter.installSegment5<OpGetSkill<ImplicitRef>>(Compiler::Stats::opcodeGetSkill + i, id);
interpreter.installSegment5<OpGetSkill<ExplicitRef>>(Compiler::Stats::opcodeGetSkillExplicit + i, id);
interpreter.installSegment5<OpSetSkill<ImplicitRef>>(Compiler::Stats::opcodeSetSkill + i, i);
interpreter.installSegment5<OpSetSkill<ExplicitRef>>(Compiler::Stats::opcodeSetSkillExplicit + i, i);
interpreter.installSegment5<OpSetSkill<ImplicitRef>>(Compiler::Stats::opcodeSetSkill + i, id);
interpreter.installSegment5<OpSetSkill<ExplicitRef>>(Compiler::Stats::opcodeSetSkillExplicit + i, id);
interpreter.installSegment5<OpModSkill<ImplicitRef>>(Compiler::Stats::opcodeModSkill + i, i);
interpreter.installSegment5<OpModSkill<ExplicitRef>>(Compiler::Stats::opcodeModSkillExplicit + i, i);
interpreter.installSegment5<OpModSkill<ImplicitRef>>(Compiler::Stats::opcodeModSkill + i, id);
interpreter.installSegment5<OpModSkill<ExplicitRef>>(Compiler::Stats::opcodeModSkillExplicit + i, id);
}
interpreter.installSegment5<OpGetPCCrimeLevel>(Compiler::Stats::opcodeGetPCCrimeLevel);

View File

@ -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");
}

View File

@ -14,6 +14,7 @@
#include "../mwmechanics/aisetting.hpp"
#include <components/esm/refid.hpp>
#include <components/esm3/loadskil.hpp>
namespace ESM
{
@ -340,7 +341,11 @@ 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;
float getSkill(const MWWorld::Ptr& ptr, ESM::Skill::SkillEnum index) const
{
return getSkill(ptr, ESM::Skill::indexToRefId(index));
};
virtual void readAdditionalState(const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const;
///< Read additional state from \a state into \a ptr.

View File

@ -285,7 +285,7 @@ void MWWorld::InventoryStore::autoEquipWeapon(TSlots& slots_)
for (int j = 0; j < static_cast<int>(weaponSkillsLength); ++j)
{
float skillValue = mActor.getClass().getSkill(mActor, static_cast<int>(weaponSkills[j]));
float skillValue = mActor.getClass().getSkill(mActor, weaponSkills[j]);
if (skillValue > max && !weaponSkillVisited[j])
{
max = skillValue;

View File

@ -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<ESM::GameSetting>& gmst
= MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
const auto& store = MWBase::Environment::get().getESMStore();
const MWWorld::Store<ESM::GameSetting>& gmst = store->get<ESM::GameSetting>();
MWMechanics::CreatureStats& creatureStats = getPlayer().getClass().getCreatureStats(getPlayer());
MWMechanics::NpcStats& npcStats = getPlayer().getClass().getNpcStats(getPlayer());
MWMechanics::DynamicStat<float> 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());
}
@ -109,7 +109,7 @@ namespace MWWorld
if (skill.mIndex == 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)

View File

@ -1,6 +1,7 @@
#ifndef GAME_MWWORLD_PLAYER_H
#define GAME_MWWORLD_PLAYER_H
#include <array>
#include <map>
#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<float, ESM::Skill::Length> mSaveSkills;
float mSaveAttributes[ESM::Attribute::Length];
bool mJumping;