Merge branch 'schooled' into 'master'

Merge magic schools into skills

See merge request OpenMW/openmw!3148
revert-6246b479
psi29a 2 years ago
commit fc5d73648a

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

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

@ -31,9 +31,6 @@
namespace MWGui
{
std::string ToolTips::sSchoolNames[] = { "#{sSchoolAlteration}", "#{sSchoolConjuration}", "#{sSchoolDestruction}",
"#{sSchoolIllusion}", "#{sSchoolMysticism}", "#{sSchoolRestoration}" };
ToolTips::ToolTips()
: Layout("openmw_tooltips.layout")
, mFocusToolTipX(0.0)
@ -227,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)
@ -250,8 +248,9 @@ namespace MWGui
spell)) // display school of spells that contribute to skill progress
{
MWWorld::Ptr player = MWMechanics::getPlayer();
int school = MWMechanics::getSpellSchool(spell, player);
info.text = "#{sSchool}: " + sSchoolNames[school];
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")
@ -944,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 +956,11 @@ namespace MWGui
widget->setUserString("ToolTipLayout", "MagicEffectToolTip");
widget->setUserString("Caption_MagicEffectName", "#{" + name + "}");
widget->setUserString("Caption_MagicEffectDescription", effect->mDescription);
widget->setUserString("Caption_MagicEffectSchool", "#{sSchool}: " + sSchoolNames[effect->mData.mSchool]);
widget->setUserString("Caption_MagicEffectSchool",
"#{sSchool}: "
+ MyGUI::TextIterator::toTagsString(
store->get<ESM::Skill>().find(effect->mData.mSchool)->mSchool->mName)
.asUTF8());
widget->setUserString("ImageTexture_MagicEffectImage", icon);
}

@ -122,8 +122,6 @@ namespace MWGui
/// Adjust position for a tooltip so that it doesn't leave the screen and does not obscure the mouse cursor
void position(MyGUI::IntPoint& position, MyGUI::IntSize size, MyGUI::IntSize viewportSize);
static std::string sSchoolNames[6];
int mHorizontalScrollIndex;
float mDelay;

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

@ -35,30 +35,18 @@ namespace MWMechanics
static const float fNPCbaseMagickaMult = gmst.find("fNPCbaseMagickaMult")->mValue.getFloat();
float baseMagicka = fNPCbaseMagickaMult * actorAttributes.at(ESM::Attribute::Intelligence).getBase();
static const std::string schools[]
= { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" };
static int iAutoSpellSchoolMax[6];
static bool init = false;
if (!init)
{
for (int i = 0; i < 6; ++i)
{
const std::string& gmstName = "iAutoSpell" + schools[i] + "Max";
iAutoSpellSchoolMax[i] = gmst.find(gmstName)->mValue.getInteger();
}
init = true;
}
std::map<int, SchoolCaps> schoolCaps;
for (int i = 0; i < 6; ++i)
std::map<ESM::RefId, SchoolCaps> schoolCaps;
for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get<ESM::Skill>())
{
if (!skill.mSchool)
continue;
SchoolCaps caps;
caps.mCount = 0;
caps.mLimit = iAutoSpellSchoolMax[i];
caps.mReachedLimit = iAutoSpellSchoolMax[i] <= 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;
@ -84,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)
@ -183,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))
@ -260,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();
@ -299,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)
@ -313,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;
@ -322,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();
}

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

@ -83,15 +83,14 @@ namespace MWMechanics
mHitPosition, static_cast<float>(effectInfo.mArea * 2));
// Play explosion sound (make sure to use NoTrack, since we will delete the projectile now)
static const std::string schools[]
= { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" };
{
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
if (!effect->mAreaSound.empty())
sndMgr->playSound3D(mHitPosition, effect->mAreaSound, 1.0f, 1.0f);
else
sndMgr->playSound3D(
mHitPosition, ESM::RefId::stringRefId(schools[effect->mData.mSchool] + " area"), 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;
@ -300,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;
@ -332,20 +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;
}
static const std::string schools[]
= { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" };
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
sndMgr->playSound3D(
mCaster, ESM::RefId::stringRefId("Spell Failure " + schools[school]), 1.0f, 1.0f);
mCaster, store->get<ESM::Skill>().find(school)->mSchool->mFailureSound, 1.0f, 1.0f);
}
return false;
}
@ -401,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();
@ -428,12 +424,9 @@ namespace MWMechanics
if (fail)
{
// Failure sound
static const std::string schools[]
= { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" };
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
sndMgr->playSound3D(
mCaster, ESM::RefId::stringRefId("Spell Failure " + schools[school]), 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;
}
}
@ -444,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())
@ -595,9 +588,6 @@ namespace MWMechanics
if (animation && !mCaster.getClass().isActor())
animation->addSpellCastGlow(effect);
static const std::string schools[]
= { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" };
addedEffects.push_back(Misc::ResourceHelpers::correctMeshPath(castStatic->mModel, vfs));
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
@ -605,32 +595,29 @@ namespace MWMechanics
sndMgr->playSound3D(mCaster, effect->mCastSound, 1.0f, 1.0f);
else
sndMgr->playSound3D(
mCaster, ESM::RefId::stringRefId(schools[effect->mData.mSchool] + " cast"), 1.0f, 1.0f);
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)
{
static const std::string schools[]
= { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" };
MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager();
if (!magicEffect.mHitSound.empty())
sndMgr->playSound3D(target, magicEffect.mHitSound, 1.0f, 1.0f);
else
sndMgr->playSound3D(
target, ESM::RefId::stringRefId(schools[magicEffect.mData.mSchool] + " hit"), 1.0f, 1.0f);
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);

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

@ -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);
int getSpellSchool(const ESM::RefId& spellId, const MWWorld::Ptr& actor);
int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor);
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);
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);

@ -98,12 +98,14 @@ namespace
else
projectileIDs.push_back(magicEffect->mBolt);
static const std::string schools[]
= { "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" };
if (!magicEffect->mBoltSound.empty())
sounds.emplace(magicEffect->mBoltSound);
else
sounds.emplace(ESM::RefId::stringRefId(schools[magicEffect->mData.mSchool] + " bolt"));
sounds.emplace(MWBase::Environment::get()
.getESMStore()
->get<ESM::Skill>()
.find(magicEffect->mData.mSchool)
->mSchool->mBoltSound);
projectileEffects.mList.push_back(*iter);
}

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

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

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

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

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

Loading…
Cancel
Save