mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-28 16:09:43 +00:00
Autocalculate enchantment costs and charges
This commit is contained in:
parent
0755954b78
commit
e9bcad4e05
17 changed files with 114 additions and 41 deletions
|
@ -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…
Reference in a new issue