Savegame: store ActiveSpells

deque
scrawl 11 years ago
parent 1141c1f3f2
commit 9052cc4a57

@ -620,7 +620,8 @@ namespace MWClass
// NOTE: 'object' and/or 'attacker' may be empty. // NOTE: 'object' and/or 'attacker' may be empty.
// Attacking peaceful NPCs is a crime // 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); MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault);
getCreatureStats(ptr).setAttacked(true); getCreatureStats(ptr).setAttacked(true);

@ -53,9 +53,9 @@ namespace MWMechanics
{ {
const MWWorld::TimeStamp& start = iter->second.mTimeStamp; 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; int duration = effectIt->mDuration;
MWWorld::TimeStamp end = start; MWWorld::TimeStamp end = start;
@ -63,7 +63,7 @@ namespace MWMechanics
MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60); MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60);
if (end>now) 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 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; 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) iter!=effects.end(); ++iter)
{ {
if (iter->mDuration > duration) if (iter->mDuration > duration)
@ -132,7 +132,7 @@ namespace MWMechanics
return mSpells; 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) const std::string &displayName, int casterActorId)
{ {
bool exists = false; bool exists = false;
@ -168,7 +168,7 @@ namespace MWMechanics
{ {
float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor(); 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) effectIt != it->second.mEffects.end(); ++effectIt)
{ {
std::string name = it->second.mDisplayName; std::string name = it->second.mDisplayName;
@ -178,7 +178,7 @@ namespace MWMechanics
float magnitude = effectIt->mMagnitude; float magnitude = effectIt->mMagnitude;
if (magnitude) 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 (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();) effectIt != it->second.mEffects.end();)
{ {
if (effectIt->mKey.mId == effectId) if (effectIt->mEffectId == effectId)
effectIt = it->second.mEffects.erase(effectIt); effectIt = it->second.mEffects.erase(effectIt);
else else
++effectIt; ++effectIt;
@ -216,10 +216,10 @@ namespace MWMechanics
{ {
for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) 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();) 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 if (effect->mData.mFlags & ESM::MagicEffect::CasterLinked
&& it->second.mCasterActorId == casterActorId) && it->second.mCasterActorId == casterActorId)
effectIt = it->second.mEffects.erase(effectIt); effectIt = it->second.mEffects.erase(effectIt);
@ -229,4 +229,41 @@ namespace MWMechanics
} }
mSpellsChanged = true; 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 "magiceffects.hpp"
#include <components/esm/defs.hpp> #include <components/esm/defs.hpp>
#include <components/esm/activespells.hpp>
namespace MWMechanics namespace MWMechanics
{ {
@ -21,19 +22,11 @@ namespace MWMechanics
{ {
public: public:
// Parameters of an effect concerning lasting effects. typedef ESM::ActiveEffect ActiveEffect;
// 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;
};
struct ActiveSpellParams struct ActiveSpellParams
{ {
std::vector<Effect> mEffects; std::vector<ActiveEffect> mEffects;
MWWorld::TimeStamp mTimeStamp; MWWorld::TimeStamp mTimeStamp;
std::string mDisplayName; std::string mDisplayName;
@ -44,6 +37,9 @@ namespace MWMechanics
typedef std::multimap<std::string, ActiveSpellParams > TContainer; typedef std::multimap<std::string, ActiveSpellParams > TContainer;
typedef TContainer::const_iterator TIterator; typedef TContainer::const_iterator TIterator;
void readState (const ESM::ActiveSpells& state);
void writeState (ESM::ActiveSpells& state) const;
private: private:
mutable TContainer mSpells; mutable TContainer mSpells;
@ -77,7 +73,7 @@ namespace MWMechanics
/// \param effects /// \param effects
/// \param displayName Name for display in magic menu. /// \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); const std::string& displayName, int casterActorId);
/// Removes the active effects from this spell/potion/.. with \a id /// 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 /// Remove all effects with CASTER_LINKED flag that were cast by \a casterActorId
void purge (int casterActorId); void purge (int casterActorId);
/// Remove all spells
void clear();
bool isSpellActive (std::string id) const; bool isSpellActive (std::string id) const;
///< case insensitive ///< case insensitive

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

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

@ -222,7 +222,7 @@ namespace MWMechanics
} }
ESM::EffectList reflectedEffects; ESM::EffectList reflectedEffects;
std::vector<ActiveSpells::Effect> appliedLastingEffects; std::vector<ActiveSpells::ActiveEffect> appliedLastingEffects;
bool firstAppliedEffect = true; bool firstAppliedEffect = true;
bool anyHarmfulEffect = false; bool anyHarmfulEffect = false;
@ -332,8 +332,9 @@ namespace MWMechanics
bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration);
if (target.getClass().isActor() && hasDuration) if (target.getClass().isActor() && hasDuration)
{ {
ActiveSpells::Effect effect; ActiveSpells::ActiveEffect effect;
effect.mKey = MWMechanics::EffectKey(*effectIt); effect.mEffectId = effectIt->mEffectID;
effect.mArg = MWMechanics::EffectKey(*effectIt).mArg;
effect.mDuration = effectIt->mDuration; effect.mDuration = effectIt->mDuration;
effect.mMagnitude = magnitude; effect.mMagnitude = magnitude;
@ -345,8 +346,8 @@ namespace MWMechanics
{ {
if (effectIt->mEffectID == ESM::MagicEffect::AbsorbAttribute+i) if (effectIt->mEffectID == ESM::MagicEffect::AbsorbAttribute+i)
{ {
std::vector<ActiveSpells::Effect> effects; std::vector<ActiveSpells::ActiveEffect> effects;
ActiveSpells::Effect effect_ = effect; ActiveSpells::ActiveEffect effect_ = effect;
effect_.mMagnitude *= -1; effect_.mMagnitude *= -1;
effects.push_back(effect_); effects.push_back(effect_);
// Also make sure to set casterActorId = target, so that the effect on the caster gets purged when the target dies // 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 else
castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find ("VFX_DefaultHit"); 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; bool loop = magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx;
// Note: in case of non actor, a free effect should be fine as well // Note: in case of non actor, a free effect should be fine as well
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target); 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 loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter 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 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 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" #include "creaturestats.hpp"
void ESM::CreatureStats::load (ESMReader &esm) void ESM::CreatureStats::load (ESMReader &esm)
@ -76,6 +75,7 @@ void ESM::CreatureStats::load (ESMReader &esm)
esm.getHNOT (mActorId, "ACID"); esm.getHNOT (mActorId, "ACID");
mSpells.load(esm); mSpells.load(esm);
mActiveSpells.load(esm);
} }
void ESM::CreatureStats::save (ESMWriter &esm) const void ESM::CreatureStats::save (ESMWriter &esm) const
@ -153,4 +153,5 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
esm.writeHNT ("ACID", mActorId); esm.writeHNT ("ACID", mActorId);
mSpells.save(esm); mSpells.save(esm);
mActiveSpells.save(esm);
} }

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

Loading…
Cancel
Save