1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-20 15:40:32 +00:00

Merge schools into skills

This commit is contained in:
Evil Eye 2023-06-26 20:42:52 +02:00
parent 9947a41c37
commit 73c2387708
18 changed files with 193 additions and 178 deletions

View File

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

View File

@ -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
{
ESXRecordT record2 = record.get();
record2.mData.mSchool = data.toInt();
record2.mData.mSchool = ESM::MagicSchool::indexToSkillRefId(data.toInt());
record.setModified(record2);
}

View File

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

View File

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

View File

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

View File

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

View File

@ -31,10 +31,10 @@ namespace MWMechanics
const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes);
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,
const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, int effectiveSchool);
const std::map<ESM::Attribute::AttributeID, AttributeValue>& actorAttributes, ESM::RefId effectiveSchool);
}

View File

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

View File

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

View File

@ -22,7 +22,6 @@
#include "actorutil.hpp"
#include "creaturestats.hpp"
#include "magicschool.hpp"
#include "spelleffects.hpp"
#include "spellutil.hpp"
#include "weapontype.hpp"
@ -89,7 +88,9 @@ namespace MWMechanics
if (!effect->mAreaSound.empty())
sndMgr->playSound3D(mHitPosition, effect->mAreaSound, 1.0f, 1.0f);
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
std::vector<MWWorld::Ptr> objects;
@ -298,8 +299,8 @@ namespace MWMechanics
mSourceName = item.getClass().getName(item);
mId = item.getCellRef().getRefId();
const ESM::Enchantment* enchantment
= MWBase::Environment::get().getESMStore()->get<ESM::Enchantment>().find(enchantmentName);
const auto& store = MWBase::Environment::get().getESMStore();
const ESM::Enchantment* enchantment = store->get<ESM::Enchantment>().find(enchantmentName);
mSlot = slot;
@ -330,17 +331,17 @@ namespace MWMechanics
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}");
// Failure sound
int school = 0;
ESM::RefId school = ESM::Skill::Alteration;
if (!enchantment->mEffects.mList.empty())
{
short effectId = enchantment->mEffects.mList.front().mEffectID;
const ESM::MagicEffect* magicEffect
= MWBase::Environment::get().getESMStore()->get<ESM::MagicEffect>().find(effectId);
const ESM::MagicEffect* magicEffect = store->get<ESM::MagicEffect>().find(effectId);
school = magicEffect->mData.mSchool;
}
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;
}
@ -396,7 +397,7 @@ namespace MWMechanics
mSourceName = spell->mName;
mId = spell->mId;
int school = 0;
ESM::RefId school = ESM::Skill::Alteration;
bool godmode = mCaster == MWMechanics::getPlayer() && MWBase::Environment::get().getWorld()->getGodModeState();
@ -424,7 +425,8 @@ namespace MWMechanics
{
// Failure sound
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;
}
}
@ -435,7 +437,7 @@ namespace MWMechanics
}
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
if (!mCaster.getClass().isActor())
@ -592,28 +594,30 @@ namespace MWMechanics
if (!effect->mCastSound.empty())
sndMgr->playSound3D(mCaster, effect->mCastSound, 1.0f, 1.0f);
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)
{
const auto& store = MWBase::Environment::get().getESMStore();
if (playNonLooping)
{
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
if (!magicEffect.mHitSound.empty())
sndMgr->playSound3D(target, magicEffect.mHitSound, 1.0f, 1.0f);
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
const ESM::Static* castStatic;
if (!magicEffect.mHit.empty())
castStatic = MWBase::Environment::get().getESMStore()->get<ESM::Static>().find(magicEffect.mHit);
castStatic = store->get<ESM::Static>().find(magicEffect.mHit);
else
castStatic = MWBase::Environment::get().getESMStore()->get<ESM::Static>().find(
ESM::RefId::stringRefId("VFX_DefaultHit"));
castStatic = store->get<ESM::Static>().find(ESM::RefId::stringRefId("VFX_DefaultHit"));
bool loop = (magicEffect.mData.mFlags & ESM::MagicEffect::ContinuousVfx) != 0;
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target);

View File

@ -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(
const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect, const EffectCostMethod method)
{
@ -153,7 +140,7 @@ namespace MWMechanics
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
float y = std::numeric_limits<float>::max();
@ -180,7 +167,7 @@ namespace MWMechanics
->mValue.getFloat();
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)
{
y = s - x;
@ -201,7 +188,7 @@ namespace MWMechanics
}
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
float baseChance = calcSpellBaseSuccessChance(spell, actor, effectiveSchool);
@ -239,23 +226,23 @@ namespace MWMechanics
}
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))
return getSpellSuccessChance(spell, actor, effectiveSchool, cap, checkMagicka);
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);
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);
return school;
}

View File

@ -18,8 +18,6 @@ namespace MWWorld
namespace MWMechanics
{
ESM::RefId spellSchoolToSkill(int school);
enum class EffectCostMethod
{
GameSpell,
@ -44,14 +42,14 @@ namespace MWMechanics
* @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.
*/
float calcSpellBaseSuccessChance(const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool);
float getSpellSuccessChance(const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool = nullptr,
bool cap = true, bool checkMagicka = true);
float getSpellSuccessChance(const ESM::RefId& spellId, const MWWorld::Ptr& actor, int* effectiveSchool = nullptr,
bool cap = true, bool checkMagicka = true);
float calcSpellBaseSuccessChance(const ESM::Spell* spell, const MWWorld::Ptr& actor, ESM::RefId* effectiveSchool);
float getSpellSuccessChance(const ESM::Spell* spell, const MWWorld::Ptr& actor,
ESM::RefId* effectiveSchool = nullptr, bool cap = true, bool checkMagicka = true);
float getSpellSuccessChance(const ESM::RefId& spellId, const MWWorld::Ptr& actor,
ESM::RefId* effectiveSchool = nullptr, bool cap = true, bool checkMagicka = true);
int getSpellSchool(const ESM::RefId& spellId, const MWWorld::Ptr& actor);
int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor);
ESM::RefId getSpellSchool(const ESM::RefId& spellId, 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.
bool spellIncreasesSkill(const ESM::Spell* spell);

View File

@ -42,7 +42,6 @@
#include "../mwmechanics/actorutil.hpp"
#include "../mwmechanics/combat.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/magicschool.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/weapontype.hpp"
@ -102,7 +101,11 @@ namespace
if (!magicEffect->mBoltSound.empty())
sounds.emplace(magicEffect->mBoltSound);
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);
}

View File

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

View File

@ -2,6 +2,7 @@
#include "esmreader.hpp"
#include "esmwriter.hpp"
#include "loadskil.hpp"
#include <components/misc/strings/algorithm.hpp>
@ -33,7 +34,20 @@ namespace ESM
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)
{
// don't allow mods to change fixed flags in the legacy format
@ -91,7 +105,17 @@ namespace ESM
{
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("PTEX", mParticle);
@ -558,7 +582,7 @@ namespace ESM
void MagicEffect::blank()
{
mRecordFlags = 0;
mData.mSchool = 0;
mData.mSchool = ESM::Skill::Alteration;
mData.mBaseCost = 0;
mData.mFlags = 0;
mData.mRed = 0;

View File

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

View File

@ -107,4 +107,30 @@ namespace ESM
return RefId();
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;
}
}

View File

@ -2,6 +2,7 @@
#define OPENMW_ESM_SKIL_H
#include <array>
#include <optional>
#include <string>
#include "components/esm/defs.hpp"
@ -13,6 +14,22 @@ namespace ESM
class ESMReader;
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
*
@ -47,6 +64,7 @@ namespace ESM
std::string mName;
std::string mIcon;
float mWerewolfValue{};
std::optional<MagicSchool> mSchool;
static constexpr IndexRefId Block{ sRecordId, 0 };
static constexpr IndexRefId Armorer{ sRecordId, 1 };