Merge schools into skills

revert-6246b479
Evil Eye 2 years ago
parent 9947a41c37
commit 73c2387708

@ -1032,8 +1032,8 @@ namespace EsmTool
std::cout << " Area Static: " << mData.mArea << std::endl; std::cout << " Area Static: " << mData.mArea << std::endl;
if (!mData.mAreaSound.empty()) if (!mData.mAreaSound.empty())
std::cout << " Area Sound: " << mData.mAreaSound << std::endl; std::cout << " Area Sound: " << mData.mAreaSound << std::endl;
std::cout << " School: " << schoolLabel(mData.mData.mSchool) << " (" << mData.mData.mSchool << ")" std::cout << " School: " << schoolLabel(ESM::MagicSchool::skillRefIdToIndex(mData.mData.mSchool)) << " ("
<< std::endl; << mData.mData.mSchool << ")" << std::endl;
std::cout << " Base Cost: " << mData.mData.mBaseCost << std::endl; std::cout << " Base Cost: " << mData.mData.mBaseCost << std::endl;
std::cout << " Unknown 1: " << mData.mData.mUnknown1 << std::endl; std::cout << " Unknown 1: " << mData.mData.mUnknown1 << std::endl;
std::cout << " Speed: " << mData.mData.mSpeed << std::endl; std::cout << " Speed: " << mData.mData.mSpeed << std::endl;

@ -2040,13 +2040,16 @@ namespace CSMWorld
{ {
} }
QVariant get(const Record<ESXRecordT>& record) const override { return record.get().mData.mSchool; } QVariant get(const Record<ESXRecordT>& record) const override
{
return ESM::MagicSchool::skillRefIdToIndex(record.get().mData.mSchool);
}
void set(Record<ESXRecordT>& record, const QVariant& data) override void set(Record<ESXRecordT>& record, const QVariant& data) override
{ {
ESXRecordT record2 = record.get(); ESXRecordT record2 = record.get();
record2.mData.mSchool = data.toInt(); record2.mData.mSchool = ESM::MagicSchool::indexToSkillRefId(data.toInt());
record.setModified(record2); record.setModified(record2);
} }

@ -99,7 +99,7 @@ add_openmw_dir (mwmechanics
aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellcasting spellresistance aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellcasting spellresistance
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction summoning disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction summoning
character actors objects aistate trading weaponpriority spellpriority weapontype spellutil character actors objects aistate trading weaponpriority spellpriority weapontype spellutil
spelleffects magicschool spelleffects
) )
add_openmw_dir (mwstate add_openmw_dir (mwstate

@ -20,7 +20,6 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/magicschool.hpp"
#include "../mwmechanics/spellutil.hpp" #include "../mwmechanics/spellutil.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
@ -225,8 +224,9 @@ namespace MWGui
{ {
ToolTipInfo info; ToolTipInfo info;
const ESM::Spell* spell = MWBase::Environment::get().getESMStore()->get<ESM::Spell>().find( const auto& store = MWBase::Environment::get().getESMStore();
ESM::RefId::deserialize(focus->getUserString("Spell"))); const ESM::Spell* spell
= store->get<ESM::Spell>().find(ESM::RefId::deserialize(focus->getUserString("Spell")));
info.caption = spell->mName; info.caption = spell->mName;
Widgets::SpellEffectList effects; Widgets::SpellEffectList effects;
for (const ESM::ENAMstruct& spellEffect : spell->mEffects.mList) for (const ESM::ENAMstruct& spellEffect : spell->mEffects.mList)
@ -248,8 +248,9 @@ namespace MWGui
spell)) // display school of spells that contribute to skill progress spell)) // display school of spells that contribute to skill progress
{ {
MWWorld::Ptr player = MWMechanics::getPlayer(); MWWorld::Ptr player = MWMechanics::getPlayer();
const auto& school = MWMechanics::getMagicSchool(MWMechanics::getSpellSchool(spell, player)); const auto& school
info.text = "#{sSchool}: " + MyGUI::TextIterator::toTagsString(school.mName).asUTF8(); = store->get<ESM::Skill>().find(MWMechanics::getSpellSchool(spell, player))->mSchool;
info.text = "#{sSchool}: " + MyGUI::TextIterator::toTagsString(school->mName).asUTF8();
} }
const std::string& cost = focus->getUserString("SpellCost"); const std::string& cost = focus->getUserString("SpellCost");
if (!cost.empty() && cost != "0") if (!cost.empty() && cost != "0")
@ -942,7 +943,8 @@ namespace MWGui
void ToolTips::createMagicEffectToolTip(MyGUI::Widget* widget, short id) void ToolTips::createMagicEffectToolTip(MyGUI::Widget* widget, short id)
{ {
const ESM::MagicEffect* effect = MWBase::Environment::get().getESMStore()->get<ESM::MagicEffect>().find(id); const auto& store = MWBase::Environment::get().getESMStore();
const ESM::MagicEffect* effect = store->get<ESM::MagicEffect>().find(id);
const std::string& name = ESM::MagicEffect::indexToGmstString(id); const std::string& name = ESM::MagicEffect::indexToGmstString(id);
std::string icon = effect->mIcon; std::string icon = effect->mIcon;
@ -956,7 +958,9 @@ namespace MWGui
widget->setUserString("Caption_MagicEffectDescription", effect->mDescription); widget->setUserString("Caption_MagicEffectDescription", effect->mDescription);
widget->setUserString("Caption_MagicEffectSchool", widget->setUserString("Caption_MagicEffectSchool",
"#{sSchool}: " "#{sSchool}: "
+ MyGUI::TextIterator::toTagsString(MWMechanics::getMagicSchool(effect->mData.mSchool).mName).asUTF8()); + MyGUI::TextIterator::toTagsString(
store->get<ESM::Skill>().find(effect->mData.mSchool)->mSchool->mName)
.asUTF8());
widget->setUserString("ImageTexture_MagicEffectImage", icon); widget->setUserString("ImageTexture_MagicEffectImage", icon);
} }

@ -358,8 +358,8 @@ namespace MWLua
.find(ESM::MagicEffect::indexToGmstString(rec.mIndex)) .find(ESM::MagicEffect::indexToGmstString(rec.mIndex))
->mValue.getString(); ->mValue.getString();
}); });
magicEffectT["school"] magicEffectT["school"] = sol::readonly_property(
= sol::readonly_property([](const ESM::MagicEffect& rec) -> int { return rec.mData.mSchool; }); [](const ESM::MagicEffect& rec) -> int { return ESM::MagicSchool::skillRefIdToIndex(rec.mData.mSchool); });
magicEffectT["baseCost"] magicEffectT["baseCost"]
= sol::readonly_property([](const ESM::MagicEffect& rec) -> float { return rec.mData.mBaseCost; }); = sol::readonly_property([](const ESM::MagicEffect& rec) -> float { return rec.mData.mBaseCost; });
magicEffectT["color"] = sol::readonly_property([](const ESM::MagicEffect& rec) -> Misc::Color { magicEffectT["color"] = sol::readonly_property([](const ESM::MagicEffect& rec) -> Misc::Color {

@ -13,7 +13,6 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "magicschool.hpp"
#include "spellutil.hpp" #include "spellutil.hpp"
namespace MWMechanics namespace MWMechanics
@ -36,17 +35,18 @@ namespace MWMechanics
static const float fNPCbaseMagickaMult = gmst.find("fNPCbaseMagickaMult")->mValue.getFloat(); static const float fNPCbaseMagickaMult = gmst.find("fNPCbaseMagickaMult")->mValue.getFloat();
float baseMagicka = fNPCbaseMagickaMult * actorAttributes.at(ESM::Attribute::Intelligence).getBase(); float baseMagicka = fNPCbaseMagickaMult * actorAttributes.at(ESM::Attribute::Intelligence).getBase();
std::map<int, SchoolCaps> schoolCaps; std::map<ESM::RefId, SchoolCaps> schoolCaps;
for (int i = 0; i < MagicSchool::Length; ++i) for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get<ESM::Skill>())
{ {
const MagicSchool& school = getMagicSchool(i); if (!skill.mSchool)
continue;
SchoolCaps caps; SchoolCaps caps;
caps.mCount = 0; caps.mCount = 0;
caps.mLimit = school.mAutoCalcMax; caps.mLimit = skill.mSchool->mAutoCalcMax;
caps.mReachedLimit = school.mAutoCalcMax <= 0; caps.mReachedLimit = skill.mSchool->mAutoCalcMax <= 0;
caps.mMinCost = std::numeric_limits<int>::max(); caps.mMinCost = std::numeric_limits<int>::max();
caps.mWeakestSpell = ESM::RefId(); caps.mWeakestSpell = ESM::RefId();
schoolCaps[i] = caps; schoolCaps[skill.mId] = caps;
} }
std::vector<ESM::RefId> selectedSpells; std::vector<ESM::RefId> selectedSpells;
@ -72,10 +72,10 @@ namespace MWMechanics
if (!attrSkillCheck(&spell, actorSkills, actorAttributes)) if (!attrSkillCheck(&spell, actorSkills, actorAttributes))
continue; continue;
int school; ESM::RefId school;
float skillTerm; float skillTerm;
calcWeakestSchool(&spell, actorSkills, school, skillTerm); calcWeakestSchool(&spell, actorSkills, school, skillTerm);
assert(school >= 0 && school < 6); assert(!school.empty());
SchoolCaps& cap = schoolCaps[school]; SchoolCaps& cap = schoolCaps[school];
if (cap.mReachedLimit && spellCost <= cap.mMinCost) if (cap.mReachedLimit && spellCost <= cap.mMinCost)
@ -171,7 +171,7 @@ namespace MWMechanics
static const float fAutoPCSpellChance static const float fAutoPCSpellChance
= esmStore.get<ESM::GameSetting>().find("fAutoPCSpellChance")->mValue.getFloat(); = esmStore.get<ESM::GameSetting>().find("fAutoPCSpellChance")->mValue.getFloat();
if (calcAutoCastChance(&spell, actorSkills, actorAttributes, -1) < fAutoPCSpellChance) if (calcAutoCastChance(&spell, actorSkills, actorAttributes, {}) < fAutoPCSpellChance)
continue; continue;
if (!attrSkillCheck(&spell, actorSkills, actorAttributes)) if (!attrSkillCheck(&spell, actorSkills, actorAttributes))
@ -248,7 +248,7 @@ namespace MWMechanics
} }
void calcWeakestSchool(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, void calcWeakestSchool(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills,
int& effectiveSchool, float& skillTerm) ESM::RefId& effectiveSchool, float& skillTerm)
{ {
// Morrowind for some reason uses a formula slightly different from magicka cost calculation // Morrowind for some reason uses a formula slightly different from magicka cost calculation
float minChance = std::numeric_limits<float>::max(); float minChance = std::numeric_limits<float>::max();
@ -287,8 +287,7 @@ namespace MWMechanics
x *= 1.5f; x *= 1.5f;
float s = 0.f; float s = 0.f;
ESM::RefId skill = spellSchoolToSkill(magicEffect->mData.mSchool); auto found = actorSkills.find(magicEffect->mData.mSchool);
auto found = actorSkills.find(skill);
if (found != actorSkills.end()) if (found != actorSkills.end())
s = 2.f * found->second.getBase(); s = 2.f * found->second.getBase();
if (s - x < minChance) if (s - x < minChance)
@ -301,7 +300,7 @@ namespace MWMechanics
} }
float calcAutoCastChance(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, float calcAutoCastChance(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills,
const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, int effectiveSchool) const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, ESM::RefId effectiveSchool)
{ {
if (spell->mData.mType != ESM::Spell::ST_Spell) if (spell->mData.mType != ESM::Spell::ST_Spell)
return 100.f; return 100.f;
@ -310,10 +309,9 @@ namespace MWMechanics
return 100.f; return 100.f;
float skillTerm = 0; float skillTerm = 0;
if (effectiveSchool != -1) if (!effectiveSchool.empty())
{ {
ESM::RefId skill = spellSchoolToSkill(effectiveSchool); auto found = actorSkills.find(effectiveSchool);
auto found = actorSkills.find(skill);
if (found != actorSkills.end()) if (found != actorSkills.end())
skillTerm = 2.f * found->second.getBase(); skillTerm = 2.f * found->second.getBase();
} }

@ -31,10 +31,10 @@ namespace MWMechanics
const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes); const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes);
void calcWeakestSchool(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, void calcWeakestSchool(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills,
int& effectiveSchool, float& skillTerm); ESM::RefId& effectiveSchool, float& skillTerm);
float calcAutoCastChance(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills, float calcAutoCastChance(const ESM::Spell* spell, const std::map<ESM::RefId, SkillValue>& actorSkills,
const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, int effectiveSchool); const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, ESM::RefId effectiveSchool);
} }

@ -1,39 +0,0 @@
#include "magicschool.hpp"
#include <components/esm3/loadgmst.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp"
namespace
{
std::array<MWMechanics::MagicSchool, MWMechanics::MagicSchool::Length> initSchools()
{
const MWWorld::Store<ESM::GameSetting>& gmst
= MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
std::array<MWMechanics::MagicSchool, MWMechanics::MagicSchool::Length> out;
const std::string schools[]
= { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" };
for (size_t i = 0; i < out.size(); ++i)
{
out[i].mAreaSound = ESM::RefId::stringRefId(schools[i] + " area");
out[i].mBoltSound = ESM::RefId::stringRefId(schools[i] + " bolt");
out[i].mCastSound = ESM::RefId::stringRefId(schools[i] + " cast");
out[i].mFailureSound = ESM::RefId::stringRefId("Spell Failure " + schools[i]);
out[i].mHitSound = ESM::RefId::stringRefId(schools[i] + " hit");
out[i].mName = gmst.find("sSchool" + schools[i])->mValue.getString();
out[i].mAutoCalcMax = gmst.find("iAutoSpell" + schools[i] + "Max")->mValue.getInteger();
}
return out;
}
}
namespace MWMechanics
{
const MagicSchool& getMagicSchool(int index)
{
static const std::array<MagicSchool, MagicSchool::Length> sSchools = initSchools();
return sSchools[index];
}
}

@ -1,26 +0,0 @@
#ifndef GAME_MWMECHANICS_MAGICSCHOOL_H
#define GAME_MWMECHANICS_MAGICSCHOOL_H
#include <components/esm/refid.hpp>
#include <array>
namespace MWMechanics
{
struct MagicSchool
{
ESM::RefId mAreaSound;
ESM::RefId mBoltSound;
ESM::RefId mCastSound;
ESM::RefId mFailureSound;
ESM::RefId mHitSound;
std::string mName;
int mAutoCalcMax;
static constexpr int Length = 6;
};
const MagicSchool& getMagicSchool(int index);
}
#endif

@ -22,7 +22,6 @@
#include "actorutil.hpp" #include "actorutil.hpp"
#include "creaturestats.hpp" #include "creaturestats.hpp"
#include "magicschool.hpp"
#include "spelleffects.hpp" #include "spelleffects.hpp"
#include "spellutil.hpp" #include "spellutil.hpp"
#include "weapontype.hpp" #include "weapontype.hpp"
@ -89,7 +88,9 @@ namespace MWMechanics
if (!effect->mAreaSound.empty()) if (!effect->mAreaSound.empty())
sndMgr->playSound3D(mHitPosition, effect->mAreaSound, 1.0f, 1.0f); sndMgr->playSound3D(mHitPosition, effect->mAreaSound, 1.0f, 1.0f);
else else
sndMgr->playSound3D(mHitPosition, getMagicSchool(effect->mData.mSchool).mAreaSound, 1.0f, 1.0f); sndMgr->playSound3D(mHitPosition,
world->getStore().get<ESM::Skill>().find(effect->mData.mSchool)->mSchool->mAreaSound, 1.0f,
1.0f);
} }
// Get the actors in range of the effect // Get the actors in range of the effect
std::vector<MWWorld::Ptr> objects; std::vector<MWWorld::Ptr> objects;
@ -298,8 +299,8 @@ namespace MWMechanics
mSourceName = item.getClass().getName(item); mSourceName = item.getClass().getName(item);
mId = item.getCellRef().getRefId(); mId = item.getCellRef().getRefId();
const ESM::Enchantment* enchantment const auto& store = MWBase::Environment::get().getESMStore();
= MWBase::Environment::get().getESMStore()->get<ESM::Enchantment>().find(enchantmentName); const ESM::Enchantment* enchantment = store->get<ESM::Enchantment>().find(enchantmentName);
mSlot = slot; mSlot = slot;
@ -330,17 +331,17 @@ namespace MWMechanics
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}");
// Failure sound // Failure sound
int school = 0; ESM::RefId school = ESM::Skill::Alteration;
if (!enchantment->mEffects.mList.empty()) if (!enchantment->mEffects.mList.empty())
{ {
short effectId = enchantment->mEffects.mList.front().mEffectID; short effectId = enchantment->mEffects.mList.front().mEffectID;
const ESM::MagicEffect* magicEffect const ESM::MagicEffect* magicEffect = store->get<ESM::MagicEffect>().find(effectId);
= MWBase::Environment::get().getESMStore()->get<ESM::MagicEffect>().find(effectId);
school = magicEffect->mData.mSchool; school = magicEffect->mData.mSchool;
} }
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
sndMgr->playSound3D(mCaster, getMagicSchool(school).mFailureSound, 1.0f, 1.0f); sndMgr->playSound3D(
mCaster, store->get<ESM::Skill>().find(school)->mSchool->mFailureSound, 1.0f, 1.0f);
} }
return false; return false;
} }
@ -396,7 +397,7 @@ namespace MWMechanics
mSourceName = spell->mName; mSourceName = spell->mName;
mId = spell->mId; mId = spell->mId;
int school = 0; ESM::RefId school = ESM::Skill::Alteration;
bool godmode = mCaster == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState(); bool godmode = mCaster == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
@ -424,7 +425,8 @@ namespace MWMechanics
{ {
// Failure sound // Failure sound
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
sndMgr->playSound3D(mCaster, getMagicSchool(school).mFailureSound, 1.0f, 1.0f); const ESM::Skill* skill = MWBase::Environment::get().getESMStore()->get<ESM::Skill>().find(school);
sndMgr->playSound3D(mCaster, skill->mSchool->mFailureSound, 1.0f, 1.0f);
return false; return false;
} }
} }
@ -435,7 +437,7 @@ namespace MWMechanics
} }
if (!mManualSpell && mCaster == getPlayer() && spellIncreasesSkill(spell)) if (!mManualSpell && mCaster == getPlayer() && spellIncreasesSkill(spell))
mCaster.getClass().skillUsageSucceeded(mCaster, spellSchoolToSkill(school), 0); mCaster.getClass().skillUsageSucceeded(mCaster, school, 0);
// A non-actor doesn't play its spell cast effects from a character controller, so play them here // A non-actor doesn't play its spell cast effects from a character controller, so play them here
if (!mCaster.getClass().isActor()) if (!mCaster.getClass().isActor())
@ -592,28 +594,30 @@ namespace MWMechanics
if (!effect->mCastSound.empty()) if (!effect->mCastSound.empty())
sndMgr->playSound3D(mCaster, effect->mCastSound, 1.0f, 1.0f); sndMgr->playSound3D(mCaster, effect->mCastSound, 1.0f, 1.0f);
else else
sndMgr->playSound3D(mCaster, getMagicSchool(effect->mData.mSchool).mCastSound, 1.0f, 1.0f); sndMgr->playSound3D(
mCaster, store.get<ESM::Skill>().find(effect->mData.mSchool)->mSchool->mCastSound, 1.0f, 1.0f);
} }
} }
void playEffects(const MWWorld::Ptr& target, const ESM::MagicEffect& magicEffect, bool playNonLooping) void playEffects(const MWWorld::Ptr& target, const ESM::MagicEffect& magicEffect, bool playNonLooping)
{ {
const auto& store = MWBase::Environment::get().getESMStore();
if (playNonLooping) if (playNonLooping)
{ {
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
if (!magicEffect.mHitSound.empty()) if (!magicEffect.mHitSound.empty())
sndMgr->playSound3D(target, magicEffect.mHitSound, 1.0f, 1.0f); sndMgr->playSound3D(target, magicEffect.mHitSound, 1.0f, 1.0f);
else else
sndMgr->playSound3D(target, getMagicSchool(magicEffect.mData.mSchool).mHitSound, 1.0f, 1.0f); sndMgr->playSound3D(
target, store->get<ESM::Skill>().find(magicEffect.mData.mSchool)->mSchool->mHitSound, 1.0f, 1.0f);
} }
// Add VFX // Add VFX
const ESM::Static* castStatic; const ESM::Static* castStatic;
if (!magicEffect.mHit.empty()) if (!magicEffect.mHit.empty())
castStatic = MWBase::Environment::get().getESMStore()->get<ESM::Static>().find(magicEffect.mHit); castStatic = store->get<ESM::Static>().find(magicEffect.mHit);
else else
castStatic = MWBase::Environment::get().getESMStore()->get<ESM::Static>().find( castStatic = store->get<ESM::Static>().find(ESM::RefId::stringRefId("VFX_DefaultHit"));
ESM::RefId::stringRefId("VFX_DefaultHit"));
bool loop = (magicEffect.mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0; bool loop = (magicEffect.mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0;
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target); MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target);

@ -37,19 +37,6 @@ namespace MWMechanics
} }
} }
ESM::RefId spellSchoolToSkill(int school)
{
static const std::array<ESM::RefId, 6> schoolSkillArray{
ESM::Skill::Alteration,
ESM::Skill::Conjuration,
ESM::Skill::Destruction,
ESM::Skill::Illusion,
ESM::Skill::Mysticism,
ESM::Skill::Restoration,
};
return schoolSkillArray.at(school);
}
float calcEffectCost( float calcEffectCost(
const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect, const EffectCostMethod method) const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect, const EffectCostMethod method)
{ {
@ -153,7 +140,7 @@ namespace MWMechanics
return enchantment.mData.mCharge; return enchantment.mData.mCharge;
} }
float calcSpellBaseSuccessChance(const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool) float calcSpellBaseSuccessChance(const ESM::Spell* spell, const MWWorld::Ptr& actor, ESM::RefId* effectiveSchool)
{ {
// Morrowind for some reason uses a formula slightly different from magicka cost calculation // Morrowind for some reason uses a formula slightly different from magicka cost calculation
float y = std::numeric_limits<float>::max(); float y = std::numeric_limits<float>::max();
@ -180,7 +167,7 @@ namespace MWMechanics
->mValue.getFloat(); ->mValue.getFloat();
x *= fEffectCostMult; x *= fEffectCostMult;
float s = 2.0f * actor.getClass().getSkill(actor, spellSchoolToSkill(magicEffect->mData.mSchool)); float s = 2.0f * actor.getClass().getSkill(actor, magicEffect->mData.mSchool);
if (s - x < y) if (s - x < y)
{ {
y = s - x; y = s - x;
@ -201,7 +188,7 @@ namespace MWMechanics
} }
float getSpellSuccessChance( float getSpellSuccessChance(
const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap, bool checkMagicka) const ESM::Spell* spell, const MWWorld::Ptr& actor, ESM::RefId* effectiveSchool, bool cap, bool checkMagicka)
{ {
// NB: Base chance is calculated here because the effective school pointer must be filled // NB: Base chance is calculated here because the effective school pointer must be filled
float baseChance = calcSpellBaseSuccessChance(spell, actor, effectiveSchool); float baseChance = calcSpellBaseSuccessChance(spell, actor, effectiveSchool);
@ -239,23 +226,23 @@ namespace MWMechanics
} }
float getSpellSuccessChance( float getSpellSuccessChance(
const ESM::RefId& spellId, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap, bool checkMagicka) const ESM::RefId& spellId, const MWWorld::Ptr& actor, ESM::RefId* effectiveSchool, bool cap, bool checkMagicka)
{ {
if (const auto spell = MWBase::Environment::get().getESMStore()->get<ESM::Spell>().search(spellId)) if (const auto spell = MWBase::Environment::get().getESMStore()->get<ESM::Spell>().search(spellId))
return getSpellSuccessChance(spell, actor, effectiveSchool, cap, checkMagicka); return getSpellSuccessChance(spell, actor, effectiveSchool, cap, checkMagicka);
return 0.f; return 0.f;
} }
int getSpellSchool(const ESM::RefId& spellId, const MWWorld::Ptr& actor) ESM::RefId getSpellSchool(const ESM::RefId& spellId, const MWWorld::Ptr& actor)
{ {
int school = 0; ESM::RefId school;
getSpellSuccessChance(spellId, actor, &school); getSpellSuccessChance(spellId, actor, &school);
return school; return school;
} }
int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor) ESM::RefId getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor)
{ {
int school = 0; ESM::RefId school;
getSpellSuccessChance(spell, actor, &school); getSpellSuccessChance(spell, actor, &school);
return school; return school;
} }

@ -18,8 +18,6 @@ namespace MWWorld
namespace MWMechanics namespace MWMechanics
{ {
ESM::RefId spellSchoolToSkill(int school);
enum class EffectCostMethod enum class EffectCostMethod
{ {
GameSpell, GameSpell,
@ -44,14 +42,14 @@ namespace MWMechanics
* @note actor can be an NPC or a creature * @note actor can be an NPC or a creature
* @return success chance from 0 to 100 (in percent), if cap=false then chance above 100 may be returned. * @return success chance from 0 to 100 (in percent), if cap=false then chance above 100 may be returned.
*/ */
float calcSpellBaseSuccessChance(const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool); float calcSpellBaseSuccessChance(const ESM::Spell* spell, const MWWorld::Ptr& actor, ESM::RefId* effectiveSchool);
float getSpellSuccessChance(const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = nullptr, float getSpellSuccessChance(const ESM::Spell* spell, const MWWorld::Ptr& actor,
bool cap = true, bool checkMagicka = true); ESM::RefId* effectiveSchool = nullptr, bool cap = true, bool checkMagicka = true);
float getSpellSuccessChance(const ESM::RefId& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = nullptr, float getSpellSuccessChance(const ESM::RefId& spellId, const MWWorld::Ptr& actor,
bool cap = true, bool checkMagicka = true); ESM::RefId* effectiveSchool = nullptr, bool cap = true, bool checkMagicka = true);
int getSpellSchool(const ESM::RefId& spellId, const MWWorld::Ptr& actor); ESM::RefId getSpellSchool(const ESM::RefId& spellId, const MWWorld::Ptr& actor);
int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor); ESM::RefId getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor);
/// Get whether or not the given spell contributes to skill progress. /// Get whether or not the given spell contributes to skill progress.
bool spellIncreasesSkill(const ESM::Spell* spell); bool spellIncreasesSkill(const ESM::Spell* spell);

@ -42,7 +42,6 @@
#include "../mwmechanics/actorutil.hpp" #include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/combat.hpp" #include "../mwmechanics/combat.hpp"
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/magicschool.hpp"
#include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/weapontype.hpp" #include "../mwmechanics/weapontype.hpp"
@ -102,7 +101,11 @@ namespace
if (!magicEffect->mBoltSound.empty()) if (!magicEffect->mBoltSound.empty())
sounds.emplace(magicEffect->mBoltSound); sounds.emplace(magicEffect->mBoltSound);
else else
sounds.emplace(MWMechanics::getMagicSchool(magicEffect->mData.mSchool).mBoltSound); sounds.emplace(MWBase::Environment::get()
.getESMStore()
->get<ESM::Skill>()
.find(magicEffect->mData.mSchool)
->mSchool->mBoltSound);
projectileEffects.mList.push_back(*iter); projectileEffects.mList.push_back(*iter);
} }

@ -932,35 +932,35 @@ namespace MWWorld
void Store<ESM::Skill>::setUp(const MWWorld::Store<ESM::GameSetting>& settings) void Store<ESM::Skill>::setUp(const MWWorld::Store<ESM::GameSetting>& settings)
{ {
constexpr std::string_view skillValues[ESM::Skill::Length][3] = { constexpr std::string_view skillValues[ESM::Skill::Length][4] = {
{ "sSkillBlock", "icons\\k\\combat_block.dds", "fWerewolfBlock" }, { "sSkillBlock", "icons\\k\\combat_block.dds", "fWerewolfBlock", {} },
{ "sSkillArmorer", "icons\\k\\combat_armor.dds", "fWerewolfArmorer" }, { "sSkillArmorer", "icons\\k\\combat_armor.dds", "fWerewolfArmorer", {} },
{ "sSkillMediumarmor", "icons\\k\\combat_mediumarmor.dds", "fWerewolfMediumarmor" }, { "sSkillMediumarmor", "icons\\k\\combat_mediumarmor.dds", "fWerewolfMediumarmor", {} },
{ "sSkillHeavyarmor", "icons\\k\\combat_heavyarmor.dds", "fWerewolfHeavyarmor" }, { "sSkillHeavyarmor", "icons\\k\\combat_heavyarmor.dds", "fWerewolfHeavyarmor", {} },
{ "sSkillBluntweapon", "icons\\k\\combat_blunt.dds", "fWerewolfBluntweapon" }, { "sSkillBluntweapon", "icons\\k\\combat_blunt.dds", "fWerewolfBluntweapon", {} },
{ "sSkillLongblade", "icons\\k\\combat_longblade.dds", "fWerewolfLongblade" }, { "sSkillLongblade", "icons\\k\\combat_longblade.dds", "fWerewolfLongblade", {} },
{ "sSkillAxe", "icons\\k\\combat_axe.dds", "fWerewolfAxe" }, { "sSkillAxe", "icons\\k\\combat_axe.dds", "fWerewolfAxe", {} },
{ "sSkillSpear", "icons\\k\\combat_spear.dds", "fWerewolfSpear" }, { "sSkillSpear", "icons\\k\\combat_spear.dds", "fWerewolfSpear", {} },
{ "sSkillAthletics", "icons\\k\\combat_athletics.dds", "fWerewolfAthletics" }, { "sSkillAthletics", "icons\\k\\combat_athletics.dds", "fWerewolfAthletics", {} },
{ "sSkillEnchant", "icons\\k\\magic_enchant.dds", "fWerewolfEnchant" }, { "sSkillEnchant", "icons\\k\\magic_enchant.dds", "fWerewolfEnchant", {} },
{ "sSkillDestruction", "icons\\k\\magic_destruction.dds", "fWerewolfDestruction" }, { "sSkillDestruction", "icons\\k\\magic_destruction.dds", "fWerewolfDestruction", "destruction" },
{ "sSkillAlteration", "icons\\k\\magic_alteration.dds", "fWerewolfAlteration" }, { "sSkillAlteration", "icons\\k\\magic_alteration.dds", "fWerewolfAlteration", "alteration" },
{ "sSkillIllusion", "icons\\k\\magic_illusion.dds", "fWerewolfIllusion" }, { "sSkillIllusion", "icons\\k\\magic_illusion.dds", "fWerewolfIllusion", "illusion" },
{ "sSkillConjuration", "icons\\k\\magic_conjuration.dds", "fWerewolfConjuration" }, { "sSkillConjuration", "icons\\k\\magic_conjuration.dds", "fWerewolfConjuration", "conjuration" },
{ "sSkillMysticism", "icons\\k\\magic_mysticism.dds", "fWerewolfMysticism" }, { "sSkillMysticism", "icons\\k\\magic_mysticism.dds", "fWerewolfMysticism", "mysticism" },
{ "sSkillRestoration", "icons\\k\\magic_restoration.dds", "fWerewolfRestoration" }, { "sSkillRestoration", "icons\\k\\magic_restoration.dds", "fWerewolfRestoration", "restoration" },
{ "sSkillAlchemy", "icons\\k\\magic_alchemy.dds", "fWerewolfAlchemy" }, { "sSkillAlchemy", "icons\\k\\magic_alchemy.dds", "fWerewolfAlchemy", {} },
{ "sSkillUnarmored", "icons\\k\\magic_unarmored.dds", "fWerewolfUnarmored" }, { "sSkillUnarmored", "icons\\k\\magic_unarmored.dds", "fWerewolfUnarmored", {} },
{ "sSkillSecurity", "icons\\k\\stealth_security.dds", "fWerewolfSecurity" }, { "sSkillSecurity", "icons\\k\\stealth_security.dds", "fWerewolfSecurity", {} },
{ "sSkillSneak", "icons\\k\\stealth_sneak.dds", "fWerewolfSneak" }, { "sSkillSneak", "icons\\k\\stealth_sneak.dds", "fWerewolfSneak", {} },
{ "sSkillAcrobatics", "icons\\k\\stealth_acrobatics.dds", "fWerewolfAcrobatics" }, { "sSkillAcrobatics", "icons\\k\\stealth_acrobatics.dds", "fWerewolfAcrobatics", {} },
{ "sSkillLightarmor", "icons\\k\\stealth_lightarmor.dds", "fWerewolfLightarmor" }, { "sSkillLightarmor", "icons\\k\\stealth_lightarmor.dds", "fWerewolfLightarmor", {} },
{ "sSkillShortblade", "icons\\k\\stealth_shortblade.dds", "fWerewolfShortblade" }, { "sSkillShortblade", "icons\\k\\stealth_shortblade.dds", "fWerewolfShortblade", {} },
{ "sSkillMarksman", "icons\\k\\stealth_marksman.dds", "fWerewolfMarksman" }, { "sSkillMarksman", "icons\\k\\stealth_marksman.dds", "fWerewolfMarksman", {} },
// "Mercantile"! >_< // "Mercantile"! >_<
{ "sSkillMercantile", "icons\\k\\stealth_mercantile.dds", "fWerewolfMerchantile" }, { "sSkillMercantile", "icons\\k\\stealth_mercantile.dds", "fWerewolfMerchantile", {} },
{ "sSkillSpeechcraft", "icons\\k\\stealth_speechcraft.dds", "fWerewolfSpeechcraft" }, { "sSkillSpeechcraft", "icons\\k\\stealth_speechcraft.dds", "fWerewolfSpeechcraft", {} },
{ "sSkillHandtohand", "icons\\k\\stealth_handtohand.dds", "fWerewolfHandtohand" }, { "sSkillHandtohand", "icons\\k\\stealth_handtohand.dds", "fWerewolfHandtohand", {} },
}; };
for (ESM::Skill* skill : mShared) for (ESM::Skill* skill : mShared)
{ {
@ -969,6 +969,21 @@ namespace MWWorld
skill->mName = getGMSTString(settings, skillValues[skill->mIndex][0]); skill->mName = getGMSTString(settings, skillValues[skill->mIndex][0]);
skill->mIcon = skillValues[skill->mIndex][1]; skill->mIcon = skillValues[skill->mIndex][1];
skill->mWerewolfValue = getGMSTFloat(settings, skillValues[skill->mIndex][2]); skill->mWerewolfValue = getGMSTFloat(settings, skillValues[skill->mIndex][2]);
const auto& school = skillValues[skill->mIndex][3];
if (!school.empty())
{
if (!skill->mSchool)
skill->mSchool = ESM::MagicSchool{};
const std::string id{ school };
skill->mSchool->mAreaSound = ESM::RefId::stringRefId(id + " area");
skill->mSchool->mBoltSound = ESM::RefId::stringRefId(id + " bolt");
skill->mSchool->mCastSound = ESM::RefId::stringRefId(id + " cast");
skill->mSchool->mFailureSound = ESM::RefId::stringRefId("Spell Failure " + id);
skill->mSchool->mHitSound = ESM::RefId::stringRefId(id + " hit");
const std::string name = "sSchool" + id;
skill->mSchool->mName = getGMSTString(settings, name);
skill->mSchool->mAutoCalcMax = int(getGMSTFloat(settings, "iAutoSpell" + id + "Max"));
}
} }
} }
} }

@ -2,6 +2,7 @@
#include "esmreader.hpp" #include "esmreader.hpp"
#include "esmwriter.hpp" #include "esmwriter.hpp"
#include "loadskil.hpp"
#include <components/misc/strings/algorithm.hpp> #include <components/misc/strings/algorithm.hpp>
@ -33,7 +34,20 @@ namespace ESM
mId = indexToRefId(mIndex); mId = indexToRefId(mIndex);
esm.getHNTSized<36>(mData, "MEDT"); esm.getSubNameIs("MEDT");
esm.getSubHeader();
int school;
esm.getT(school);
mData.mSchool = MagicSchool::indexToSkillRefId(school);
esm.getT(mData.mBaseCost);
esm.getT(mData.mFlags);
esm.getT(mData.mRed);
esm.getT(mData.mGreen);
esm.getT(mData.mBlue);
esm.getT(mData.mUnknown1);
esm.getT(mData.mSpeed);
esm.getT(mData.mUnknown2);
if (esm.getFormatVersion() == DefaultFormatVersion) if (esm.getFormatVersion() == DefaultFormatVersion)
{ {
// don't allow mods to change fixed flags in the legacy format // don't allow mods to change fixed flags in the legacy format
@ -91,7 +105,17 @@ namespace ESM
{ {
esm.writeHNT("INDX", mIndex); esm.writeHNT("INDX", mIndex);
esm.writeHNT("MEDT", mData, 36); esm.startSubRecord("MEDT");
esm.writeT(MagicSchool::skillRefIdToIndex(mData.mSchool));
esm.writeT(mData.mBaseCost);
esm.writeT(mData.mFlags);
esm.writeT(mData.mRed);
esm.writeT(mData.mGreen);
esm.writeT(mData.mBlue);
esm.writeT(mData.mUnknown1);
esm.writeT(mData.mSpeed);
esm.writeT(mData.mUnknown2);
esm.endRecord("MEDT");
esm.writeHNOCString("ITEX", mIcon); esm.writeHNOCString("ITEX", mIcon);
esm.writeHNOCString("PTEX", mParticle); esm.writeHNOCString("PTEX", mParticle);
@ -558,7 +582,7 @@ namespace ESM
void MagicEffect::blank() void MagicEffect::blank()
{ {
mRecordFlags = 0; mRecordFlags = 0;
mData.mSchool = 0; mData.mSchool = ESM::Skill::Alteration;
mData.mBaseCost = 0; mData.mBaseCost = 0;
mData.mFlags = 0; mData.mFlags = 0;
mData.mRed = 0; mData.mRed = 0;

@ -70,7 +70,7 @@ namespace ESM
struct MEDTstruct struct MEDTstruct
{ {
int mSchool; // SpellSchool, see defs.hpp RefId mSchool; // Skill id
float mBaseCost; float mBaseCost;
int mFlags; int mFlags;
// Glow color for enchanted items with this effect // Glow color for enchanted items with this effect

@ -107,4 +107,30 @@ namespace ESM
return RefId(); return RefId();
return RefId::index(sRecordId, static_cast<std::uint32_t>(index)); return RefId::index(sRecordId, static_cast<std::uint32_t>(index));
} }
const std::array<RefId, MagicSchool::Length> sMagicSchools = {
Skill::Alteration,
Skill::Conjuration,
Skill::Destruction,
Skill::Illusion,
Skill::Mysticism,
Skill::Restoration,
};
RefId MagicSchool::indexToSkillRefId(int index)
{
if (index < 0 || index >= Length)
return {};
return sMagicSchools[index];
}
int MagicSchool::skillRefIdToIndex(RefId id)
{
for (size_t i = 0; i < sMagicSchools.size(); ++i)
{
if (id == sMagicSchools[i])
return static_cast<int>(i);
}
return -1;
}
} }

@ -2,6 +2,7 @@
#define OPENMW_ESM_SKIL_H #define OPENMW_ESM_SKIL_H
#include <array> #include <array>
#include <optional>
#include <string> #include <string>
#include "components/esm/defs.hpp" #include "components/esm/defs.hpp"
@ -13,6 +14,22 @@ namespace ESM
class ESMReader; class ESMReader;
class ESMWriter; class ESMWriter;
struct MagicSchool
{
ESM::RefId mAreaSound;
ESM::RefId mBoltSound;
ESM::RefId mCastSound;
ESM::RefId mFailureSound;
ESM::RefId mHitSound;
std::string mName;
int mAutoCalcMax;
static constexpr int Length = 6;
static RefId indexToSkillRefId(int index);
static int skillRefIdToIndex(RefId id);
};
/* /*
* Skill information * Skill information
* *
@ -47,6 +64,7 @@ namespace ESM
std::string mName; std::string mName;
std::string mIcon; std::string mIcon;
float mWerewolfValue{}; float mWerewolfValue{};
std::optional<MagicSchool> mSchool;
static constexpr IndexRefId Block{ sRecordId, 0 }; static constexpr IndexRefId Block{ sRecordId, 0 };
static constexpr IndexRefId Armorer{ sRecordId, 1 }; static constexpr IndexRefId Armorer{ sRecordId, 1 };

Loading…
Cancel
Save