Autocalculate enchantment costs and charges

revert-6246b479
Evil Eye 11 months ago
parent 0755954b78
commit e9bcad4e05

@ -55,6 +55,7 @@
Bug #7307: Alchemy "Magic Effect" search string does not match on tool tip for effects related to attributes
Bug #7413: Generated wilderness cells don't spawn fish
Bug #7415: Unbreakable lock discrepancies
Bug #7428: AutoCalc flag is not used to calculate enchantment costs
Feature #3537: Shader-based water ripples
Feature #5492: Let rain and snow collide with statics
Feature #6447: Add LOD support to Object Paging

@ -12,6 +12,8 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwmechanics/spellutil.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
@ -199,8 +201,8 @@ namespace MWGui
break;
line.mCharge->setVisible(true);
line.mCharge->setValue(
static_cast<int>(line.mItemPtr.getCellRef().getEnchantmentCharge()), ench->mData.mCharge);
line.mCharge->setValue(static_cast<int>(line.mItemPtr.getCellRef().getEnchantmentCharge()),
MWMechanics::getEnchantmentCharge(*ench));
break;
}
}

@ -24,6 +24,7 @@
#include "../mwworld/nullaction.hpp"
#include "../mwmechanics/alchemy.hpp"
#include "../mwmechanics/spellutil.hpp"
namespace
{
@ -112,8 +113,8 @@ namespace
if (ench->mData.mType == ESM::Enchantment::ConstantEffect)
leftChargePercent = 101;
else
leftChargePercent = static_cast<int>(
left.mBase.getCellRef().getNormalizedEnchantmentCharge(ench->mData.mCharge) * 100);
leftChargePercent
= static_cast<int>(left.mBase.getCellRef().getNormalizedEnchantmentCharge(*ench) * 100);
}
}
@ -126,8 +127,8 @@ namespace
if (ench->mData.mType == ESM::Enchantment::ConstantEffect)
rightChargePercent = 101;
else
rightChargePercent = static_cast<int>(
right.mBase.getCellRef().getNormalizedEnchantmentCharge(ench->mData.mCharge) * 100);
rightChargePercent
= static_cast<int>(right.mBase.getCellRef().getNormalizedEnchantmentCharge(*ench) * 100);
}
}
@ -304,8 +305,8 @@ namespace MWGui
return false;
}
if (base.getCellRef().getEnchantmentCharge() >= ench->mData.mCharge
|| base.getCellRef().getEnchantmentCharge() == -1)
if (base.getCellRef().getEnchantmentCharge() == -1
|| base.getCellRef().getEnchantmentCharge() >= MWMechanics::getEnchantmentCharge(*ench))
return false;
}

@ -153,13 +153,12 @@ namespace MWGui
&& item.getClass().canBeEquipped(item, mActor).first == 0)
continue;
int castCost
= MWMechanics::getEffectiveEnchantmentCastCost(static_cast<float>(enchant->mData.mCost), mActor);
int castCost = MWMechanics::getEffectiveEnchantmentCastCost(*enchant, mActor);
std::string cost = std::to_string(castCost);
int currentCharge = int(item.getCellRef().getEnchantmentCharge());
if (currentCharge == -1)
currentCharge = enchant->mData.mCharge;
currentCharge = MWMechanics::getEnchantmentCharge(*enchant);
std::string charge = std::to_string(currentCharge);
newSpell.mCostColumn = cost + "/" + charge;

@ -539,7 +539,7 @@ namespace MWGui
if (enchant->mData.mType == ESM::Enchantment::WhenStrikes
|| enchant->mData.mType == ESM::Enchantment::WhenUsed)
{
int maxCharge = enchant->mData.mCharge;
const int maxCharge = MWMechanics::getEnchantmentCharge(*enchant);
int charge = (info.remainingEnchantCharge == -1) ? maxCharge : info.remainingEnchantCharge;
const int chargeWidth = 204;

@ -1384,8 +1384,7 @@ namespace MWGui
mSelectedSpell = ESM::RefId();
const ESM::Enchantment* ench = mStore->get<ESM::Enchantment>().find(item.getClass().getEnchantment(item));
int chargePercent
= static_cast<int>(item.getCellRef().getNormalizedEnchantmentCharge(ench->mData.mCharge) * 100);
int chargePercent = static_cast<int>(item.getCellRef().getNormalizedEnchantmentCharge(*ench) * 100);
mHud->setSelectedEnchantItem(item, chargePercent);
mSpellWindow->setTitle(item.getClass().getName(item));
}

@ -15,6 +15,7 @@
#include "actorutil.hpp"
#include "creaturestats.hpp"
#include "spellutil.hpp"
namespace MWMechanics
{
@ -68,8 +69,9 @@ namespace MWMechanics
const ESM::Enchantment* enchantment
= MWBase::Environment::get().getESMStore()->get<ESM::Enchantment>().find(
item.getClass().getEnchantment(item));
item.getCellRef().setEnchantmentCharge(std::min(
item.getCellRef().getEnchantmentCharge() + restored, static_cast<float>(enchantment->mData.mCharge)));
const int maxCharge = MWMechanics::getEnchantmentCharge(*enchantment);
item.getCellRef().setEnchantmentCharge(
std::min(item.getCellRef().getEnchantmentCharge() + restored, static_cast<float>(maxCharge)));
MWBase::Environment::get().getWindowManager()->playSound(ESM::RefId::stringRefId("Enchant Success"));

@ -319,10 +319,11 @@ namespace MWMechanics
if (!godmode
&& (type == ESM::Enchantment::WhenUsed || (!isProjectile && type == ESM::Enchantment::WhenStrikes)))
{
int castCost = getEffectiveEnchantmentCastCost(static_cast<float>(enchantment->mData.mCost), mCaster);
int castCost = getEffectiveEnchantmentCastCost(*enchantment, mCaster);
if (item.getCellRef().getEnchantmentCharge() == -1)
item.getCellRef().setEnchantmentCharge(static_cast<float>(enchantment->mData.mCharge));
item.getCellRef().setEnchantmentCharge(
static_cast<float>(MWMechanics::getEnchantmentCharge(*enchantment)));
if (item.getCellRef().getEnchantmentCharge() < castCost)
{

@ -296,8 +296,7 @@ namespace
{
const ESM::Enchantment* enchantment = esmStore.get<ESM::Enchantment>().search(spellId);
if (enchantment)
spellCost = MWMechanics::getEffectiveEnchantmentCastCost(
static_cast<float>(enchantment->mData.mCost), caster);
spellCost = MWMechanics::getEffectiveEnchantmentCastCost(*enchantment, caster);
}
// Magicka is increased by the cost of the spell

@ -171,7 +171,7 @@ namespace MWMechanics
if (actor.getClass().isNpc() && !store.isEquipped(ptr))
return 0.f;
int castCost = getEffectiveEnchantmentCastCost(static_cast<float>(enchantment->mData.mCost), actor);
int castCost = getEffectiveEnchantmentCastCost(*enchantment, actor);
if (ptr.getCellRef().getEnchantmentCharge() != -1 && ptr.getCellRef().getEnchantmentCharge() < castCost)
return 0.f;

@ -2,6 +2,7 @@
#include <limits>
#include <components/esm3/loadench.hpp>
#include <components/esm3/loadmgef.hpp>
#include "../mwbase/environment.hpp"
@ -15,6 +16,27 @@
namespace MWMechanics
{
namespace
{
float getTotalCost(const ESM::EffectList& list, const EffectCostMethod method = EffectCostMethod::GameSpell)
{
float cost = 0;
for (const ESM::ENAMstruct& effect : list.mList)
{
float effectCost = std::max(0.f, MWMechanics::calcEffectCost(effect, nullptr, method));
// This is applied to the whole spell cost for each effect when
// creating spells, but is only applied on the effect itself in TES:CS.
if (effect.mRange == ESM::RT_Target)
effectCost *= 1.5;
cost += effectCost;
}
return cost;
}
}
ESM::RefId spellSchoolToSkill(int school)
{
static const std::array<ESM::RefId, 6> schoolSkillArray{
@ -39,6 +61,11 @@ namespace MWMechanics
bool appliedOnce = magicEffect->mData.mFlags & ESM::MagicEffect::AppliedOnce;
int minMagn = hasMagnitude ? effect.mMagnMin : 1;
int maxMagn = hasMagnitude ? effect.mMagnMax : 1;
if (method != EffectCostMethod::GameEnchantment)
{
minMagn = std::max(1, minMagn);
maxMagn = std::max(1, maxMagn);
}
int duration = hasDuration ? effect.mDuration : 1;
if (!appliedOnce)
duration = std::max(1, duration);
@ -52,7 +79,7 @@ namespace MWMechanics
minArea = 1;
}
float x = 0.5 * (std::max(1, minMagn) + std::max(1, maxMagn));
float x = 0.5 * (minMagn + maxMagn);
x *= 0.1 * magicEffect->mData.mBaseCost;
x *= durationOffset + duration;
x += 0.05 * std::max(minArea, effect.mArea) * magicEffect->mData.mBaseCost;
@ -65,19 +92,7 @@ namespace MWMechanics
if (!(spell.mData.mFlags & ESM::Spell::F_Autocalc))
return spell.mData.mCost;
float cost = 0;
for (const ESM::ENAMstruct& effect : spell.mEffects.mList)
{
float effectCost = std::max(0.f, MWMechanics::calcEffectCost(effect));
// This is applied to the whole spell cost for each effect when
// creating spells, but is only applied on the effect itself in TES:CS.
if (effect.mRange == ESM::RT_Target)
effectCost *= 1.5;
cost += effectCost;
}
float cost = getTotalCost(spell.mEffects);
return std::round(cost);
}
@ -94,6 +109,50 @@ namespace MWMechanics
return static_cast<int>((result < 1) ? 1 : result);
}
int getEffectiveEnchantmentCastCost(const ESM::Enchantment& enchantment, const MWWorld::Ptr& actor)
{
float castCost;
if (enchantment.mData.mFlags & ESM::Enchantment::Autocalc)
castCost = getTotalCost(enchantment.mEffects, EffectCostMethod::GameEnchantment);
else
castCost = static_cast<float>(enchantment.mData.mCost);
return getEffectiveEnchantmentCastCost(castCost, actor);
}
int getEnchantmentCharge(const ESM::Enchantment& enchantment)
{
if (enchantment.mData.mFlags & ESM::Enchantment::Autocalc)
{
int charge
= static_cast<int>(std::round(getTotalCost(enchantment.mEffects, EffectCostMethod::GameEnchantment)));
const auto& store = MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
switch (enchantment.mData.mType)
{
case ESM::Enchantment::CastOnce:
{
static const int iMagicItemChargeOnce = store.find("iMagicItemChargeOnce")->mValue.getInteger();
return charge * iMagicItemChargeOnce;
}
case ESM::Enchantment::WhenStrikes:
{
static const int iMagicItemChargeStrike = store.find("iMagicItemChargeStrike")->mValue.getInteger();
return charge * iMagicItemChargeStrike;
}
case ESM::Enchantment::WhenUsed:
{
static const int iMagicItemChargeUse = store.find("iMagicItemChargeUse")->mValue.getInteger();
return charge * iMagicItemChargeUse;
}
case ESM::Enchantment::ConstantEffect:
{
static const int iMagicItemChargeConst = store.find("iMagicItemChargeConst")->mValue.getInteger();
return charge * iMagicItemChargeConst;
}
}
}
return enchantment.mData.mCharge;
}
float calcSpellBaseSuccessChance(const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool)
{
// Morrowind for some reason uses a formula slightly different from magicka cost calculation

@ -6,6 +6,7 @@
namespace ESM
{
struct ENAMstruct;
struct Enchantment;
struct MagicEffect;
struct Spell;
}
@ -23,6 +24,7 @@ namespace MWMechanics
{
GameSpell,
PlayerSpell,
GameEnchantment,
};
float calcEffectCost(const ESM::ENAMstruct& effect, const ESM::MagicEffect* magicEffect = nullptr,
@ -30,6 +32,8 @@ namespace MWMechanics
int calcSpellCost(const ESM::Spell& spell);
int getEffectiveEnchantmentCastCost(float castCost, const MWWorld::Ptr& actor);
int getEffectiveEnchantmentCastCost(const ESM::Enchantment& enchantment, const MWWorld::Ptr& actor);
int getEnchantmentCharge(const ESM::Enchantment& enchantment);
/**
* @param spell spell to cast

@ -108,7 +108,7 @@ namespace MWMechanics
const ESM::Enchantment* enchantment = world->getStore().get<ESM::Enchantment>().find(weapon->mEnchant);
if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
{
int castCost = getEffectiveEnchantmentCastCost(static_cast<float>(enchantment->mData.mCost), actor);
int castCost = getEffectiveEnchantmentCastCost(*enchantment, actor);
float charge = item.getCellRef().getEnchantmentCharge();
if (charge == -1 || charge >= castCost || weapclass == ESM::WeaponType::Thrown

@ -10,6 +10,7 @@
#include <apps/openmw/mwbase/environment.hpp>
#include <apps/openmw/mwbase/world.hpp>
#include <apps/openmw/mwmechanics/spellutil.hpp>
#include <apps/openmw/mwworld/esmstore.hpp>
namespace MWWorld
@ -129,8 +130,9 @@ namespace MWWorld
mCellRef.mVariant);
}
float CellRef::getNormalizedEnchantmentCharge(int maxCharge) const
float CellRef::getNormalizedEnchantmentCharge(const ESM::Enchantment& enchantment) const
{
const int maxCharge = MWMechanics::getEnchantmentCharge(enchantment);
if (maxCharge == 0)
{
return 0;

@ -9,6 +9,7 @@
namespace ESM
{
struct Enchantment;
struct ObjectState;
}
@ -87,7 +88,7 @@ namespace MWWorld
float getEnchantmentCharge() const;
// Remaining enchantment charge rescaled to the supplied maximum charge (such as one of the enchantment).
float getNormalizedEnchantmentCharge(int maxCharge) const;
float getNormalizedEnchantmentCharge(const ESM::Enchantment& enchantment) const;
void setEnchantmentCharge(float charge);

@ -55,6 +55,7 @@
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/recharge.hpp"
#include "../mwmechanics/spellutil.hpp"
#include "class.hpp"
#include "containerstore.hpp"
@ -1248,7 +1249,8 @@ namespace MWWorld
if (enchantment->mData.mType == ESM::Enchantment::WhenUsed
|| enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
mRechargingItems.emplace_back(ptr.getBase(), static_cast<float>(enchantment->mData.mCharge));
mRechargingItems.emplace_back(
ptr.getBase(), static_cast<float>(MWMechanics::getEnchantmentCharge(*enchantment)));
}
Ptr MWWorld::CellStore::getMovedActor(int actorId) const

@ -16,6 +16,7 @@
#include "../mwmechanics/levelledlist.hpp"
#include "../mwmechanics/recharge.hpp"
#include "../mwmechanics/spellutil.hpp"
#include "class.hpp"
#include "esmstore.hpp"
@ -268,7 +269,7 @@ bool MWWorld::ContainerStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2)
{
const ESM::Enchantment* enchantment = MWBase::Environment::get().getESMStore()->get<ESM::Enchantment>().find(
ptr1.getClass().getEnchantment(ptr1));
float maxCharge = static_cast<float>(enchantment->mData.mCharge);
const float maxCharge = static_cast<float>(MWMechanics::getEnchantmentCharge(*enchantment));
float enchantCharge1
= ptr1.getCellRef().getEnchantmentCharge() == -1 ? maxCharge : ptr1.getCellRef().getEnchantmentCharge();
float enchantCharge2
@ -507,7 +508,7 @@ void MWWorld::ContainerStore::updateRechargingItems()
if (enchantment->mData.mType == ESM::Enchantment::WhenUsed
|| enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
mRechargingItems.emplace_back(it, static_cast<float>(enchantment->mData.mCharge));
mRechargingItems.emplace_back(it, static_cast<float>(MWMechanics::getEnchantmentCharge(*enchantment)));
}
}
}

Loading…
Cancel
Save