Use real time to update spell effects instead of game timestamps (bug #5165)

pull/578/head
Andrei Kortunov 5 years ago
parent 3ebbe14a62
commit b5833f3c59

@ -6,6 +6,7 @@
Bug #3676: NiParticleColorModifier isn't applied properly
Bug #4774: Guards are ignorant of an invisible player that tries to attack them
Bug #5108: Savegame bloating due to inefficient fog textures format
Bug #5165: Active spells should use real time intead of timestamps
Bug #5358: ForceGreeting always resets the dialogue window completely
Bug #5363: Enchantment autocalc not always 0/1
Bug #5364: Script fails/stops if trying to startscript an unknown script
@ -188,8 +189,8 @@
Bug #5158: Objects without a name don't fallback to their ID
Bug #5159: NiMaterialColorController can only control the diffuse color
Bug #5161: Creature companions can't be activated when they are knocked down
Bug #5164: Faction owned items handling is incorrect
Bug #5163: UserData is not copied during node cloning
Bug #5164: Faction owned items handling is incorrect
Bug #5166: Scripts still should be executed after player's death
Bug #5167: Player can select and cast spells before magic menu is enabled
Bug #5168: Force1stPerson and Force3rdPerson commands are not really force view change

@ -12,14 +12,12 @@
namespace MWMechanics
{
void ActiveSpells::update() const
void ActiveSpells::update(float duration) const
{
bool rebuild = false;
MWWorld::TimeStamp now = MWBase::Environment::get().getWorld()->getTimeStamp();
// Erase no longer active spells and effects
if (mLastUpdate!=now)
if (duration > 0)
{
TContainer::iterator iter (mSpells.begin());
while (iter!=mSpells.end())
@ -34,21 +32,20 @@ namespace MWMechanics
std::vector<ActiveEffect>& effects = iter->second.mEffects;
for (std::vector<ActiveEffect>::iterator effectIt = effects.begin(); effectIt != effects.end();)
{
MWWorld::TimeStamp start = iter->second.mTimeStamp;
MWWorld::TimeStamp end = start + static_cast<double>(effectIt->mDuration)*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60);
if (end <= now)
if (effectIt->mTimeLeft <= 0)
{
effectIt = effects.erase(effectIt);
rebuild = true;
}
else
{
effectIt->mTimeLeft -= duration;
++effectIt;
}
}
++iter;
}
}
mLastUpdate = now;
}
if (mSpellsChanged)
@ -63,24 +60,15 @@ namespace MWMechanics
void ActiveSpells::rebuildEffects() const
{
MWWorld::TimeStamp now = MWBase::Environment::get().getWorld()->getTimeStamp();
mEffects = MagicEffects();
for (TIterator iter (begin()); iter!=end(); ++iter)
{
const MWWorld::TimeStamp& start = iter->second.mTimeStamp;
const std::vector<ActiveEffect>& effects = iter->second.mEffects;
for (std::vector<ActiveEffect>::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt)
{
double duration = effectIt->mDuration;
MWWorld::TimeStamp end = start;
end += duration *
MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60);
if (end>now)
if (effectIt->mTimeLeft > 0)
mEffects.add(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), MWMechanics::EffectParam(effectIt->mMagnitude));
}
}
@ -88,12 +76,11 @@ namespace MWMechanics
ActiveSpells::ActiveSpells()
: mSpellsChanged (false)
, mLastUpdate (MWBase::Environment::get().getWorld()->getTimeStamp())
{}
const MagicEffects& ActiveSpells::getMagicEffects() const
{
update();
update(0.f);
return mEffects;
}
@ -116,19 +103,14 @@ namespace MWMechanics
for (std::vector<ActiveEffect>::const_iterator iter (effects.begin());
iter!=effects.end(); ++iter)
{
if (iter->mDuration > duration)
duration = iter->mDuration;
if (iter->mTimeLeft > duration)
duration = iter->mTimeLeft;
}
double scaledDuration = duration *
MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60);
double usedUp = MWBase::Environment::get().getWorld()->getTimeStamp() - iterator->second.mTimeStamp;
if (usedUp>=scaledDuration)
if (duration < 0)
return 0;
return scaledDuration-usedUp;
return duration;
}
bool ActiveSpells::isSpellActive(const std::string& id) const
@ -152,7 +134,6 @@ namespace MWMechanics
TContainer::iterator it(mSpells.find(id));
ActiveSpellParams params;
params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp();
params.mEffects = effects;
params.mDisplayName = displayName;
params.mCasterActorId = casterActorId;
@ -211,19 +192,15 @@ namespace MWMechanics
{
for (TContainer::const_iterator it = begin(); it != end(); ++it)
{
float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor();
for (std::vector<ActiveEffect>::const_iterator effectIt = it->second.mEffects.begin();
effectIt != it->second.mEffects.end(); ++effectIt)
{
std::string name = it->second.mDisplayName;
float remainingTime = effectIt->mDuration +
static_cast<float>(it->second.mTimeStamp - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale;
float magnitude = effectIt->mMagnitude;
if (magnitude)
visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->first, it->second.mCasterActorId, magnitude, remainingTime, effectIt->mDuration);
visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->first, it->second.mCasterActorId, magnitude, effectIt->mTimeLeft, effectIt->mDuration);
}
}
}
@ -316,7 +293,6 @@ namespace MWMechanics
params.mEffects = it->second.mEffects;
params.mCasterActorId = it->second.mCasterActorId;
params.mDisplayName = it->second.mDisplayName;
params.mTimeStamp = it->second.mTimeStamp.toEsm();
state.mSpells.insert (std::make_pair(it->first, params));
}
@ -331,7 +307,6 @@ namespace MWMechanics
params.mEffects = it->second.mEffects;
params.mCasterActorId = it->second.mCasterActorId;
params.mDisplayName = it->second.mDisplayName;
params.mTimeStamp = MWWorld::TimeStamp(it->second.mTimeStamp);
mSpells.insert (std::make_pair(it->first, params));
mSpellsChanged = true;

@ -44,15 +44,14 @@ namespace MWMechanics
TIterator end() const;
void update(float duration) const;
private:
mutable TContainer mSpells;
mutable MagicEffects mEffects;
mutable bool mSpellsChanged;
mutable MWWorld::TimeStamp mLastUpdate;
void update() const;
void rebuildEffects() const;
/// Add any effects that are in "from" and not in "addTo" to "addTo"

@ -1622,6 +1622,8 @@ namespace MWMechanics
player.getClass().getCreatureStats(player).setHitAttemptActorId(-1);
}
iter->first.getClass().getCreatureStats(iter->first).getActiveSpells().update(duration);
// For dead actors we need to remove looping spell particles
if (iter->first.getClass().getCreatureStats(iter->first).isDead())
ctrl->updateContinuousVfx();

@ -183,6 +183,7 @@ namespace MWMechanics
effect.mEffectId = effectIt->mEffectID;
effect.mArg = MWMechanics::EffectKey(*effectIt).mArg;
effect.mMagnitude = magnitude;
effect.mTimeLeft = 0.f;
// Avoid applying absorb effects if the caster is the target
// We still need the spell to be added
@ -225,6 +226,8 @@ namespace MWMechanics
{
effect.mDuration = hasDuration ? static_cast<float>(effectIt->mDuration) : 1.f;
effect.mTimeLeft = effect.mDuration;
targetEffects.add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(effect.mMagnitude));
// add to list of active effects, to apply in next frame

@ -16,7 +16,6 @@ namespace ESM
esm.writeHNT ("CAST", params.mCasterActorId);
esm.writeHNString ("DISP", params.mDisplayName);
esm.writeHNT ("TIME", params.mTimeStamp);
for (std::vector<ActiveEffect>::const_iterator effectIt = params.mEffects.begin(); effectIt != params.mEffects.end(); ++effectIt)
{
@ -25,12 +24,15 @@ namespace ESM
esm.writeHNT ("ARG_", effectIt->mArg);
esm.writeHNT ("MAGN", effectIt->mMagnitude);
esm.writeHNT ("DURA", effectIt->mDuration);
esm.writeHNT ("LEFT", effectIt->mTimeLeft);
}
}
}
void ActiveSpells::load(ESMReader &esm)
{
int format = esm.getFormat();
while (esm.isNextSub("ID__"))
{
std::string spellId = esm.getHString();
@ -38,7 +40,10 @@ namespace ESM
ActiveSpellParams params;
esm.getHNT (params.mCasterActorId, "CAST");
params.mDisplayName = esm.getHNString ("DISP");
esm.getHNT (params.mTimeStamp, "TIME");
// spell casting timestamp, no longer used
if (esm.isNextSub("TIME"))
esm.skipHSub();
while (esm.isNextSub("MGEF"))
{
@ -48,6 +53,11 @@ namespace ESM
esm.getHNOT(effect.mArg, "ARG_");
esm.getHNT (effect.mMagnitude, "MAGN");
esm.getHNT (effect.mDuration, "DURA");
if (format < 9)
effect.mTimeLeft = effect.mDuration;
else
esm.getHNT (effect.mTimeLeft, "LEFT");
params.mEffects.push_back(effect);
}
mSpells.insert(std::make_pair(spellId, params));

@ -21,6 +21,7 @@ namespace ESM
float mMagnitude;
int mArg; // skill or attribute
float mDuration;
float mTimeLeft;
};
// format 0, saved games only
@ -29,7 +30,6 @@ namespace ESM
struct ActiveSpellParams
{
std::vector<ActiveEffect> mEffects;
ESM::TimeStamp mTimeStamp;
std::string mDisplayName;
int mCasterActorId;
};

@ -5,7 +5,7 @@
#include "defs.hpp"
unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE;
int ESM::SavedGame::sCurrentFormat = 8;
int ESM::SavedGame::sCurrentFormat = 9;
void ESM::SavedGame::load (ESMReader &esm)
{

Loading…
Cancel
Save