Savegame: store ActiveSpells

pull/101/head
scrawl 11 years ago
parent 1141c1f3f2
commit 9052cc4a57

@ -620,7 +620,8 @@ namespace MWClass
// NOTE: 'object' and/or 'attacker' may be empty.
// Attacking peaceful NPCs is a crime
if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30)
// anything below 80 is considered peaceful (see Actors::updateActor)
if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80)
MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault);
getCreatureStats(ptr).setAttacked(true);

@ -53,9 +53,9 @@ namespace MWMechanics
{
const MWWorld::TimeStamp& start = iter->second.mTimeStamp;
const std::vector<Effect>& effects = iter->second.mEffects;
const std::vector<ActiveEffect>& effects = iter->second.mEffects;
for (std::vector<Effect>::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt)
for (std::vector<ActiveEffect>::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt)
{
int duration = effectIt->mDuration;
MWWorld::TimeStamp end = start;
@ -63,7 +63,7 @@ namespace MWMechanics
MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60);
if (end>now)
mEffects.add(effectIt->mKey, MWMechanics::EffectParam(effectIt->mMagnitude));
mEffects.add(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), MWMechanics::EffectParam(effectIt->mMagnitude));
}
}
}
@ -91,11 +91,11 @@ namespace MWMechanics
double ActiveSpells::timeToExpire (const TIterator& iterator) const
{
const std::vector<Effect>& effects = iterator->second.mEffects;
const std::vector<ActiveEffect>& effects = iterator->second.mEffects;
int duration = 0;
for (std::vector<Effect>::const_iterator iter (effects.begin());
for (std::vector<ActiveEffect>::const_iterator iter (effects.begin());
iter!=effects.end(); ++iter)
{
if (iter->mDuration > duration)
@ -132,7 +132,7 @@ namespace MWMechanics
return mSpells;
}
void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector<Effect> effects,
void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector<ActiveEffect> effects,
const std::string &displayName, int casterActorId)
{
bool exists = false;
@ -168,7 +168,7 @@ namespace MWMechanics
{
float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor();
for (std::vector<Effect>::const_iterator effectIt = it->second.mEffects.begin();
for (std::vector<ActiveEffect>::const_iterator effectIt = it->second.mEffects.begin();
effectIt != it->second.mEffects.end(); ++effectIt)
{
std::string name = it->second.mDisplayName;
@ -178,7 +178,7 @@ namespace MWMechanics
float magnitude = effectIt->mMagnitude;
if (magnitude)
visitor.visit(effectIt->mKey, name, it->second.mCasterActorId, magnitude, remainingTime);
visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->second.mCasterActorId, magnitude, remainingTime);
}
}
}
@ -200,10 +200,10 @@ namespace MWMechanics
{
for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it)
{
for (std::vector<Effect>::iterator effectIt = it->second.mEffects.begin();
for (std::vector<ActiveEffect>::iterator effectIt = it->second.mEffects.begin();
effectIt != it->second.mEffects.end();)
{
if (effectIt->mKey.mId == effectId)
if (effectIt->mEffectId == effectId)
effectIt = it->second.mEffects.erase(effectIt);
else
++effectIt;
@ -216,10 +216,10 @@ namespace MWMechanics
{
for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it)
{
for (std::vector<Effect>::iterator effectIt = it->second.mEffects.begin();
for (std::vector<ActiveEffect>::iterator effectIt = it->second.mEffects.begin();
effectIt != it->second.mEffects.end();)
{
const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mKey.mId);
const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectId);
if (effect->mData.mFlags & ESM::MagicEffect::CasterLinked
&& it->second.mCasterActorId == casterActorId)
effectIt = it->second.mEffects.erase(effectIt);
@ -229,4 +229,41 @@ namespace MWMechanics
}
mSpellsChanged = true;
}
void ActiveSpells::clear()
{
mSpells.clear();
mSpellsChanged = true;
}
void ActiveSpells::writeState(ESM::ActiveSpells &state) const
{
for (TContainer::const_iterator it = mSpells.begin(); it != mSpells.end(); ++it)
{
// Stupid copying of almost identical structures. ESM::TimeStamp <-> MWWorld::TimeStamp
ESM::ActiveSpells::ActiveSpellParams params;
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));
}
}
void ActiveSpells::readState(const ESM::ActiveSpells &state)
{
for (ESM::ActiveSpells::TContainer::const_iterator it = state.mSpells.begin(); it != state.mSpells.end(); ++it)
{
// Stupid copying of almost identical structures. ESM::TimeStamp <-> MWWorld::TimeStamp
ActiveSpellParams params;
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;
}
}
}

@ -10,6 +10,7 @@
#include "magiceffects.hpp"
#include <components/esm/defs.hpp>
#include <components/esm/activespells.hpp>
namespace MWMechanics
{
@ -21,19 +22,11 @@ namespace MWMechanics
{
public:
// Parameters of an effect concerning lasting effects.
// Note we are not using ENAMstruct since the magnitude may be modified by magic resistance, etc.
// It could also be a negative magnitude, in case of inversing an effect, e.g. Absorb spell causes damage on target, but heals the caster.
struct Effect
{
float mMagnitude;
EffectKey mKey;
float mDuration;
};
typedef ESM::ActiveEffect ActiveEffect;
struct ActiveSpellParams
{
std::vector<Effect> mEffects;
std::vector<ActiveEffect> mEffects;
MWWorld::TimeStamp mTimeStamp;
std::string mDisplayName;
@ -44,6 +37,9 @@ namespace MWMechanics
typedef std::multimap<std::string, ActiveSpellParams > TContainer;
typedef TContainer::const_iterator TIterator;
void readState (const ESM::ActiveSpells& state);
void writeState (ESM::ActiveSpells& state) const;
private:
mutable TContainer mSpells;
@ -77,7 +73,7 @@ namespace MWMechanics
/// \param effects
/// \param displayName Name for display in magic menu.
///
void addSpell (const std::string& id, bool stack, std::vector<Effect> effects,
void addSpell (const std::string& id, bool stack, std::vector<ActiveEffect> effects,
const std::string& displayName, int casterActorId);
/// Removes the active effects from this spell/potion/.. with \a id
@ -92,6 +88,9 @@ namespace MWMechanics
/// Remove all effects with CASTER_LINKED flag that were cast by \a casterActorId
void purge (int casterActorId);
/// Remove all spells
void clear();
bool isSpellActive (std::string id) const;
///< case insensitive

@ -959,6 +959,7 @@ namespace MWMechanics
// Reset magic effects and recalculate derived effects
// One case where we need this is to make sure bound items are removed upon death
stats.setMagicEffects(MWMechanics::MagicEffects());
stats.getActiveSpells().clear();
calculateCreatureStatModifiers(iter->first, 0);
MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false);

@ -496,7 +496,7 @@ namespace MWMechanics
state.mBlock = mBlock;
state.mMovementFlags = mMovementFlags;
state.mAttackStrength = mAttackStrength;
state.mFallHeight = mFallHeight;
state.mFallHeight = mFallHeight; // TODO: vertical velocity (move from PhysicActor to CreatureStats?)
state.mLastHitObject = mLastHitObject;
state.mRecalcDynamicStats = mRecalcDynamicStats;
state.mDrawState = mDrawState;
@ -504,6 +504,7 @@ namespace MWMechanics
state.mActorId = mActorId;
mSpells.writeState(state.mSpells);
mActiveSpells.writeState(state.mActiveSpells);
}
void CreatureStats::readState (const ESM::CreatureStats& state)
@ -542,6 +543,7 @@ namespace MWMechanics
mActorId = state.mActorId;
mSpells.readState(state.mSpells);
mActiveSpells.readState(state.mActiveSpells);
}
// Relates to NPC gold reset delay

@ -222,7 +222,7 @@ namespace MWMechanics
}
ESM::EffectList reflectedEffects;
std::vector<ActiveSpells::Effect> appliedLastingEffects;
std::vector<ActiveSpells::ActiveEffect> appliedLastingEffects;
bool firstAppliedEffect = true;
bool anyHarmfulEffect = false;
@ -332,8 +332,9 @@ namespace MWMechanics
bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration);
if (target.getClass().isActor() && hasDuration)
{
ActiveSpells::Effect effect;
effect.mKey = MWMechanics::EffectKey(*effectIt);
ActiveSpells::ActiveEffect effect;
effect.mEffectId = effectIt->mEffectID;
effect.mArg = MWMechanics::EffectKey(*effectIt).mArg;
effect.mDuration = effectIt->mDuration;
effect.mMagnitude = magnitude;
@ -345,8 +346,8 @@ namespace MWMechanics
{
if (effectIt->mEffectID == ESM::MagicEffect::AbsorbAttribute+i)
{
std::vector<ActiveSpells::Effect> effects;
ActiveSpells::Effect effect_ = effect;
std::vector<ActiveSpells::ActiveEffect> effects;
ActiveSpells::ActiveEffect effect_ = effect;
effect_.mMagnitude *= -1;
effects.push_back(effect_);
// Also make sure to set casterActorId = target, so that the effect on the caster gets purged when the target dies
@ -392,6 +393,7 @@ namespace MWMechanics
else
castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find ("VFX_DefaultHit");
// TODO: VFX are no longer active after saving/reloading the game
bool loop = magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx;
// Note: in case of non actor, a free effect should be fine as well
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target);

@ -45,7 +45,7 @@ add_component_dir (esm
loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate
npcstats creaturestats weatherstate quickkeys fogstate spellstate
npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells
)
add_component_dir (misc

@ -0,0 +1,56 @@
#include "activespells.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
namespace ESM
{
void ActiveSpells::save(ESMWriter &esm) const
{
for (TContainer::const_iterator it = mSpells.begin(); it != mSpells.end(); ++it)
{
esm.writeHNString ("ID__", it->first);
const ActiveSpellParams& params = it->second;
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)
{
esm.writeHNT ("MGEF", effectIt->mEffectId);
if (effectIt->mArg != -1)
esm.writeHNT ("ARG_", effectIt->mArg);
esm.writeHNT ("MAGN", effectIt->mMagnitude);
esm.writeHNT ("DURA", effectIt->mDuration);
}
}
}
void ActiveSpells::load(ESMReader &esm)
{
while (esm.isNextSub("ID__"))
{
std::string spellId = esm.getHString();
ActiveSpellParams params;
esm.getHNT (params.mCasterActorId, "CAST");
params.mDisplayName = esm.getHNString ("DISP");
esm.getHNT (params.mTimeStamp, "TIME");
while (esm.isNextSub("MGEF"))
{
ActiveEffect effect;
esm.getHT(effect.mEffectId);
effect.mArg = -1;
esm.getHNOT(effect.mArg, "ARG_");
esm.getHNT (effect.mMagnitude, "MAGN");
esm.getHNT (effect.mDuration, "DURA");
params.mEffects.push_back(effect);
}
mSpells.insert(std::make_pair(spellId, params));
}
}
}

@ -0,0 +1,45 @@
#ifndef OPENMW_ESM_ACTIVESPELLS_H
#define OPENMW_ESM_ACTIVESPELLS_H
#include "effectlist.hpp"
#include "defs.hpp"
#include <string>
#include <map>
namespace ESM
{
class ESMReader;
class ESMWriter;
// Parameters of an effect concerning lasting effects.
// Note we are not using ENAMstruct since the magnitude may be modified by magic resistance, etc.
// It could also be a negative magnitude, in case of inversing an effect, e.g. Absorb spell causes damage on target, but heals the caster.
struct ActiveEffect
{
int mEffectId;
float mMagnitude;
int mArg; // skill or attribute
float mDuration;
};
// format 0, saved games only
struct ActiveSpells
{
struct ActiveSpellParams
{
std::vector<ActiveEffect> mEffects;
ESM::TimeStamp mTimeStamp;
std::string mDisplayName;
int mCasterActorId;
};
typedef std::multimap<std::string, ActiveSpellParams > TContainer;
TContainer mSpells;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
}
#endif

@ -1,4 +1,3 @@
#include "creaturestats.hpp"
void ESM::CreatureStats::load (ESMReader &esm)
@ -76,6 +75,7 @@ void ESM::CreatureStats::load (ESMReader &esm)
esm.getHNOT (mActorId, "ACID");
mSpells.load(esm);
mActiveSpells.load(esm);
}
void ESM::CreatureStats::save (ESMWriter &esm) const
@ -153,4 +153,5 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
esm.writeHNT ("ACID", mActorId);
mSpells.save(esm);
mActiveSpells.save(esm);
}

@ -10,6 +10,7 @@
#include "defs.hpp"
#include "spellstate.hpp"
#include "activespells.hpp"
namespace ESM
{
@ -49,6 +50,7 @@ namespace ESM
int mLevel;
SpellState mSpells;
ActiveSpells mActiveSpells;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;

Loading…
Cancel
Save