mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-31 17:45:35 +00:00
Merge branch 'recalc_mana' into 'master'
Don't wait a frame to recalculate magicka Closes #3792 See merge request OpenMW/openmw!1323
This commit is contained in:
commit
c0dca5371b
11 changed files with 67 additions and 89 deletions
|
@ -5,6 +5,7 @@
|
|||
Bug #3246: ESSImporter: Most NPCs are dead on save load
|
||||
Bug #3514: Editing a reference's position after loading an esp file makes the reference disappear
|
||||
Bug #3737: Scripts from The Underground 2 .esp do not play (all patched versions)
|
||||
Bug #3792: 1 frame late magicka recalc breaks early scripted magicka reactions to Intelligence change
|
||||
Bug #3846: Strings starting with "-" fail to compile if not enclosed in quotes
|
||||
Bug #3905: Great House Dagoth issues
|
||||
Bug #4203: Resurrecting an actor should close the loot GUI
|
||||
|
|
|
@ -112,7 +112,10 @@ namespace MWClass
|
|||
{
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
{
|
||||
std::unique_ptr<CreatureCustomData> data (new CreatureCustomData);
|
||||
auto tempData = std::make_unique<CreatureCustomData>();
|
||||
CreatureCustomData* data = tempData.get();
|
||||
MWMechanics::CreatureCustomDataResetter resetter(ptr);
|
||||
ptr.getRefData().setCustomData(std::move(tempData));
|
||||
|
||||
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
|
||||
|
||||
|
@ -156,10 +159,7 @@ namespace MWClass
|
|||
|
||||
data->mCreatureStats.setGoldPool(ref->mBase->mData.mGold);
|
||||
|
||||
data->mCreatureStats.setNeedRecalcDynamicStats(false);
|
||||
|
||||
// store
|
||||
ptr.getRefData().setCustomData(std::move(data));
|
||||
resetter.mPtr = {};
|
||||
|
||||
getContainerStore(ptr).fill(ref->mBase->mInventory, ptr.getCellRef().getRefId());
|
||||
|
||||
|
|
|
@ -303,7 +303,11 @@ namespace MWClass
|
|||
{
|
||||
if (!ptr.getRefData().getCustomData())
|
||||
{
|
||||
std::unique_ptr<NpcCustomData> data(new NpcCustomData);
|
||||
bool recalculate = false;
|
||||
auto tempData = std::make_unique<NpcCustomData>();
|
||||
NpcCustomData* data = tempData.get();
|
||||
MWMechanics::CreatureCustomDataResetter resetter(ptr);
|
||||
ptr.getRefData().setCustomData(std::move(tempData));
|
||||
|
||||
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
|
||||
|
||||
|
@ -334,8 +338,6 @@ namespace MWClass
|
|||
data->mNpcStats.setLevel(ref->mBase->mNpdt.mLevel);
|
||||
data->mNpcStats.setBaseDisposition(ref->mBase->mNpdt.mDisposition);
|
||||
data->mNpcStats.setReputation(ref->mBase->mNpdt.mReputation);
|
||||
|
||||
data->mNpcStats.setNeedRecalcDynamicStats(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -351,7 +353,7 @@ namespace MWClass
|
|||
autoCalculateAttributes(ref->mBase, data->mNpcStats);
|
||||
autoCalculateSkills(ref->mBase, data->mNpcStats, ptr, spellsInitialised);
|
||||
|
||||
data->mNpcStats.setNeedRecalcDynamicStats(true);
|
||||
recalculate = true;
|
||||
}
|
||||
|
||||
// Persistent actors with 0 health do not play death animation
|
||||
|
@ -387,7 +389,9 @@ namespace MWClass
|
|||
data->mNpcStats.setGoldPool(gold);
|
||||
|
||||
// store
|
||||
ptr.getRefData().setCustomData(std::move(data));
|
||||
resetter.mPtr = {};
|
||||
if(recalculate)
|
||||
data->mNpcStats.recalculateMagicka();
|
||||
|
||||
// inventory
|
||||
// setting ownership is used to make the NPC auto-equip his initial equipment only, and not bartered items
|
||||
|
|
|
@ -240,10 +240,7 @@ namespace MWMechanics
|
|||
{
|
||||
// magic effects
|
||||
adjustMagicEffects (ptr, duration);
|
||||
if (ptr.getClass().getCreatureStats(ptr).needToRecalcDynamicStats())
|
||||
calculateDynamicStats (ptr);
|
||||
|
||||
calculateCreatureStatModifiers (ptr, duration);
|
||||
// fatigue restoration
|
||||
calculateRestoration(ptr, duration);
|
||||
}
|
||||
|
@ -654,29 +651,6 @@ namespace MWMechanics
|
|||
updateSummons(creature, mTimerDisposeSummonsCorpses == 0.f);
|
||||
}
|
||||
|
||||
void Actors::calculateDynamicStats (const MWWorld::Ptr& ptr)
|
||||
{
|
||||
CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr);
|
||||
|
||||
float intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified();
|
||||
|
||||
float base = 1.f;
|
||||
if (ptr == getPlayer())
|
||||
base = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fPCbaseMagickaMult")->mValue.getFloat();
|
||||
else
|
||||
base = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fNPCbaseMagickaMult")->mValue.getFloat();
|
||||
|
||||
double magickaFactor = base +
|
||||
creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyMaximumMagicka)).getMagnitude() * 0.1;
|
||||
|
||||
DynamicStat<float> magicka = creatureStats.getMagicka();
|
||||
float diff = (static_cast<int>(magickaFactor*intelligence)) - magicka.getBase();
|
||||
float currentToBaseRatio = magicka.getBase() > 0 ? magicka.getCurrent() / magicka.getBase() : 0;
|
||||
magicka.setModified(magicka.getModified() + diff, 0);
|
||||
magicka.setCurrent(magicka.getBase() * currentToBaseRatio, false, true);
|
||||
creatureStats.setMagicka(magicka);
|
||||
}
|
||||
|
||||
void Actors::restoreDynamicStats (const MWWorld::Ptr& ptr, double hours, bool sleep)
|
||||
{
|
||||
MWMechanics::CreatureStats& stats = ptr.getClass().getCreatureStats (ptr);
|
||||
|
@ -771,14 +745,6 @@ namespace MWMechanics
|
|||
stats.setFatigue (fatigue);
|
||||
}
|
||||
|
||||
void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration)
|
||||
{
|
||||
CreatureStats &creatureStats = ptr.getClass().getCreatureStats(ptr);
|
||||
|
||||
if (creatureStats.needToRecalcDynamicStats())
|
||||
calculateDynamicStats(ptr);
|
||||
}
|
||||
|
||||
bool Actors::isAttackPreparing(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
PtrActorMap::iterator it = mActors.find(ptr);
|
||||
|
@ -1711,10 +1677,6 @@ namespace MWMechanics
|
|||
if (iter->first.getType() == ESM::Creature::sRecordId)
|
||||
soulTrap(iter->first);
|
||||
|
||||
// Magic effects will be reset later, and the magic effect that could kill the actor
|
||||
// needs to be determined now
|
||||
calculateCreatureStatModifiers(iter->first, 0);
|
||||
|
||||
if (cls.isEssential(iter->first))
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}");
|
||||
}
|
||||
|
@ -1730,8 +1692,6 @@ namespace MWMechanics
|
|||
// Make sure spell effects are removed
|
||||
purgeSpellEffects(stats.getActorId());
|
||||
|
||||
// Reset dynamic stats, attributes and skills
|
||||
calculateCreatureStatModifiers(iter->first, 0);
|
||||
stats.getMagicEffects().add(ESM::MagicEffect::Vampirism, vampirism);
|
||||
|
||||
if (isPlayer)
|
||||
|
@ -1816,10 +1776,6 @@ namespace MWMechanics
|
|||
continue;
|
||||
|
||||
adjustMagicEffects (iter->first, duration);
|
||||
if (iter->first.getClass().getCreatureStats(iter->first).needToRecalcDynamicStats())
|
||||
calculateDynamicStats (iter->first);
|
||||
|
||||
calculateCreatureStatModifiers (iter->first, duration);
|
||||
|
||||
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(iter->first);
|
||||
if (animation)
|
||||
|
@ -2209,7 +2165,6 @@ namespace MWMechanics
|
|||
void Actors::updateMagicEffects(const MWWorld::Ptr &ptr)
|
||||
{
|
||||
adjustMagicEffects(ptr, 0.f);
|
||||
calculateCreatureStatModifiers(ptr, 0.f);
|
||||
}
|
||||
|
||||
bool Actors::isReadyToBlock(const MWWorld::Ptr &ptr) const
|
||||
|
|
|
@ -43,10 +43,6 @@ namespace MWMechanics
|
|||
|
||||
void adjustMagicEffects (const MWWorld::Ptr& creature, float duration);
|
||||
|
||||
void calculateDynamicStats (const MWWorld::Ptr& ptr);
|
||||
|
||||
void calculateCreatureStatModifiers (const MWWorld::Ptr& ptr, float duration);
|
||||
|
||||
void calculateRestoration (const MWWorld::Ptr& ptr, float duration);
|
||||
|
||||
void updateDrowning (const MWWorld::Ptr& ptr, float duration, bool isKnockedOut, bool isPlayer);
|
||||
|
|
|
@ -29,4 +29,12 @@ namespace MWMechanics
|
|||
const MWMechanics::MagicEffects& effects = actor.getClass().getCreatureStats(actor).getMagicEffects();
|
||||
return effects.get(ESM::MagicEffect::WaterWalking).getMagnitude() > 0;
|
||||
}
|
||||
|
||||
CreatureCustomDataResetter::CreatureCustomDataResetter(const MWWorld::Ptr& ptr) : mPtr(ptr) {}
|
||||
|
||||
CreatureCustomDataResetter::~CreatureCustomDataResetter()
|
||||
{
|
||||
if(!mPtr.isEmpty())
|
||||
mPtr.getRefData().setCustomData({});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,6 +86,14 @@ namespace MWMechanics
|
|||
template void modifyBaseInventory<ESM::Creature>(const std::string& actorId, const std::string& itemId, int amount);
|
||||
template void modifyBaseInventory<ESM::NPC>(const std::string& actorId, const std::string& itemId, int amount);
|
||||
template void modifyBaseInventory<ESM::Container>(const std::string& containerId, const std::string& itemId, int amount);
|
||||
|
||||
struct CreatureCustomDataResetter
|
||||
{
|
||||
MWWorld::Ptr mPtr;
|
||||
|
||||
CreatureCustomDataResetter(const MWWorld::Ptr& ptr);
|
||||
~CreatureCustomDataResetter();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
|
||||
|
@ -22,7 +23,7 @@ namespace MWMechanics
|
|||
mTalkedTo (false), mAlarmed (false), mAttacked (false),
|
||||
mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false),
|
||||
mHitRecovery(false), mBlock(false), mMovementFlags(0),
|
||||
mFallHeight(0), mRecalcMagicka(false), mLastRestock(0,0), mGoldPool(0), mActorId(-1), mHitAttemptActorId(-1),
|
||||
mFallHeight(0), mLastRestock(0,0), mGoldPool(0), mActorId(-1), mHitAttemptActorId(-1),
|
||||
mDeathAnimation(-1), mTimeOfDeath(), mSideMovementAngle(0), mLevel (0)
|
||||
{
|
||||
for (int i=0; i<4; ++i)
|
||||
|
@ -146,7 +147,7 @@ namespace MWMechanics
|
|||
mAttributes[index] = value;
|
||||
|
||||
if (index == ESM::Attribute::Intelligence)
|
||||
mRecalcMagicka = true;
|
||||
recalculateMagicka();
|
||||
else if (index == ESM::Attribute::Strength ||
|
||||
index == ESM::Attribute::Willpower ||
|
||||
index == ESM::Attribute::Agility ||
|
||||
|
@ -208,11 +209,10 @@ namespace MWMechanics
|
|||
|
||||
void CreatureStats::modifyMagicEffects(const MagicEffects &effects)
|
||||
{
|
||||
if (effects.get(ESM::MagicEffect::FortifyMaximumMagicka).getModifier()
|
||||
!= mMagicEffects.get(ESM::MagicEffect::FortifyMaximumMagicka).getModifier())
|
||||
mRecalcMagicka = true;
|
||||
|
||||
bool recalc = effects.get(ESM::MagicEffect::FortifyMaximumMagicka).getModifier() != mMagicEffects.get(ESM::MagicEffect::FortifyMaximumMagicka).getModifier();
|
||||
mMagicEffects.setModifiers(effects);
|
||||
if(recalc)
|
||||
recalculateMagicka();
|
||||
}
|
||||
|
||||
void CreatureStats::setAiSetting (AiSetting index, Stat<int> value)
|
||||
|
@ -400,19 +400,26 @@ namespace MWMechanics
|
|||
return height;
|
||||
}
|
||||
|
||||
bool CreatureStats::needToRecalcDynamicStats()
|
||||
void CreatureStats::recalculateMagicka()
|
||||
{
|
||||
if (mRecalcMagicka)
|
||||
{
|
||||
mRecalcMagicka = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
auto world = MWBase::Environment::get().getWorld();
|
||||
float intelligence = getAttribute(ESM::Attribute::Intelligence).getModified();
|
||||
|
||||
void CreatureStats::setNeedRecalcDynamicStats(bool val)
|
||||
{
|
||||
mRecalcMagicka = val;
|
||||
float base = 1.f;
|
||||
const auto& player = world->getPlayerPtr();
|
||||
if (this == &player.getClass().getCreatureStats(player))
|
||||
base = world->getStore().get<ESM::GameSetting>().find("fPCbaseMagickaMult")->mValue.getFloat();
|
||||
else
|
||||
base = world->getStore().get<ESM::GameSetting>().find("fNPCbaseMagickaMult")->mValue.getFloat();
|
||||
|
||||
double magickaFactor = base + mMagicEffects.get(EffectKey(ESM::MagicEffect::FortifyMaximumMagicka)).getMagnitude() * 0.1;
|
||||
|
||||
DynamicStat<float> magicka = getMagicka();
|
||||
float diff = (static_cast<int>(magickaFactor*intelligence)) - magicka.getBase();
|
||||
float currentToBaseRatio = magicka.getBase() > 0 ? magicka.getCurrent() / magicka.getBase() : 0;
|
||||
magicka.setModified(magicka.getModified() + diff, 0);
|
||||
magicka.setCurrent(magicka.getBase() * currentToBaseRatio, false, true);
|
||||
setMagicka(magicka);
|
||||
}
|
||||
|
||||
void CreatureStats::setKnockedDown(bool value)
|
||||
|
@ -532,7 +539,6 @@ namespace MWMechanics
|
|||
state.mFallHeight = mFallHeight; // TODO: vertical velocity (move from PhysicActor to CreatureStats?)
|
||||
state.mLastHitObject = mLastHitObject;
|
||||
state.mLastHitAttemptObject = mLastHitAttemptObject;
|
||||
state.mRecalcDynamicStats = mRecalcMagicka;
|
||||
state.mDrawState = mDrawState;
|
||||
state.mLevel = mLevel;
|
||||
state.mActorId = mActorId;
|
||||
|
@ -586,7 +592,6 @@ namespace MWMechanics
|
|||
mFallHeight = state.mFallHeight;
|
||||
mLastHitObject = state.mLastHitObject;
|
||||
mLastHitAttemptObject = state.mLastHitAttemptObject;
|
||||
mRecalcMagicka = state.mRecalcDynamicStats;
|
||||
mDrawState = DrawState_(state.mDrawState);
|
||||
mLevel = state.mLevel;
|
||||
mActorId = state.mActorId;
|
||||
|
@ -627,6 +632,8 @@ namespace MWMechanics
|
|||
if (state.mHasAiSettings)
|
||||
for (int i=0; i<4; ++i)
|
||||
mAiSettings[i].readState(state.mAiSettings[i]);
|
||||
if(state.mRecalcDynamicStats)
|
||||
recalculateMagicka();
|
||||
}
|
||||
|
||||
void CreatureStats::setLastRestockTime(MWWorld::TimeStamp tradeTime)
|
||||
|
|
|
@ -65,8 +65,6 @@ namespace MWMechanics
|
|||
std::string mLastHitObject; // The last object to hit this actor
|
||||
std::string mLastHitAttemptObject; // The last object to attempt to hit this actor
|
||||
|
||||
bool mRecalcMagicka;
|
||||
|
||||
// For merchants: the last time items were restocked and gold pool refilled.
|
||||
MWWorld::TimeStamp mLastRestock;
|
||||
|
||||
|
@ -103,8 +101,7 @@ namespace MWMechanics
|
|||
DrawState_ getDrawState() const;
|
||||
void setDrawState(DrawState_ state);
|
||||
|
||||
bool needToRecalcDynamicStats();
|
||||
void setNeedRecalcDynamicStats(bool val);
|
||||
void recalculateMagicka();
|
||||
|
||||
float getFallHeight() const;
|
||||
void addToFallHeight(float height);
|
||||
|
|
|
@ -77,7 +77,7 @@ namespace MWMechanics
|
|||
MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr);
|
||||
MWMechanics::NpcStats& npcStats = ptr.getClass().getNpcStats (ptr);
|
||||
|
||||
npcStats.setNeedRecalcDynamicStats(true);
|
||||
npcStats.recalculateMagicka();
|
||||
|
||||
const ESM::NPC *player = ptr.get<ESM::NPC>()->mBase;
|
||||
|
||||
|
@ -222,7 +222,6 @@ namespace MWMechanics
|
|||
|
||||
// forced update and current value adjustments
|
||||
mActors.updateActor (ptr, 0);
|
||||
mActors.updateActor (ptr, 0);
|
||||
|
||||
for (int i=0; i<3; ++i)
|
||||
{
|
||||
|
|
|
@ -279,7 +279,7 @@ namespace
|
|||
namespace MWMechanics
|
||||
{
|
||||
|
||||
void applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const ActiveSpells::ActiveSpellParams& spellParams, ESM::ActiveEffect& effect, bool& invalid, bool& receivedMagicDamage)
|
||||
void applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const ActiveSpells::ActiveSpellParams& spellParams, ESM::ActiveEffect& effect, bool& invalid, bool& receivedMagicDamage, bool& recalculateMagicka)
|
||||
{
|
||||
const auto world = MWBase::Environment::get().getWorld();
|
||||
bool godmode = target == getPlayer() && world->getGodModeState();
|
||||
|
@ -609,7 +609,7 @@ void applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, co
|
|||
fortifySkill(target, effect, effect.mMagnitude);
|
||||
break;
|
||||
case ESM::MagicEffect::FortifyMaximumMagicka:
|
||||
target.getClass().getCreatureStats(target).setNeedRecalcDynamicStats(true);
|
||||
recalculateMagicka = true;
|
||||
break;
|
||||
case ESM::MagicEffect::AbsorbHealth:
|
||||
case ESM::MagicEffect::AbsorbMagicka:
|
||||
|
@ -687,13 +687,14 @@ bool applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, Ac
|
|||
const auto world = MWBase::Environment::get().getWorld();
|
||||
bool invalid = false;
|
||||
bool receivedMagicDamage = false;
|
||||
bool recalculateMagicka = false;
|
||||
if(effect.mEffectId == ESM::MagicEffect::Corprus && spellParams.shouldWorsen())
|
||||
{
|
||||
spellParams.worsen();
|
||||
for(auto& otherEffect : spellParams.getEffects())
|
||||
{
|
||||
if(isCorprusEffect(otherEffect))
|
||||
applyMagicEffect(target, caster, spellParams, otherEffect, invalid, receivedMagicDamage);
|
||||
applyMagicEffect(target, caster, spellParams, otherEffect, invalid, receivedMagicDamage, recalculateMagicka);
|
||||
}
|
||||
if(target == getPlayer())
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicCorprusWorsens}");
|
||||
|
@ -815,7 +816,7 @@ bool applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, Ac
|
|||
if(effect.mEffectId == ESM::MagicEffect::Corprus)
|
||||
spellParams.worsen();
|
||||
else
|
||||
applyMagicEffect(target, caster, spellParams, effect, invalid, receivedMagicDamage);
|
||||
applyMagicEffect(target, caster, spellParams, effect, invalid, receivedMagicDamage, recalculateMagicka);
|
||||
effect.mMagnitude = magnitude;
|
||||
magnitudes.add(EffectKey(effect.mEffectId, effect.mArg), EffectParam(effect.mMagnitude - oldMagnitude));
|
||||
}
|
||||
|
@ -832,6 +833,8 @@ bool applyMagicEffect(const MWWorld::Ptr& target, const MWWorld::Ptr& caster, Ac
|
|||
effect.mFlags |= ESM::ActiveEffect::Flag_Applied | ESM::ActiveEffect::Flag_Remove;
|
||||
if (receivedMagicDamage && target == getPlayer())
|
||||
MWBase::Environment::get().getWindowManager()->activateHitOverlay(false);
|
||||
if(recalculateMagicka)
|
||||
target.getClass().getCreatureStats(target).recalculateMagicka();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -981,7 +984,7 @@ void removeMagicEffect(const MWWorld::Ptr& target, ActiveSpells::ActiveSpellPara
|
|||
fortifySkill(target, effect, -effect.mMagnitude);
|
||||
break;
|
||||
case ESM::MagicEffect::FortifyMaximumMagicka:
|
||||
target.getClass().getCreatureStats(target).setNeedRecalcDynamicStats(true);
|
||||
target.getClass().getCreatureStats(target).recalculateMagicka();
|
||||
break;
|
||||
case ESM::MagicEffect::AbsorbAttribute:
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue