forked from mirror/openmw-tes3mp
Savegame: store ActiveSpells
This commit is contained in:
parent
1141c1f3f2
commit
9052cc4a57
11 changed files with 178 additions and 32 deletions
|
@ -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
|
||||
|
|
56
components/esm/activespells.cpp
Normal file
56
components/esm/activespells.cpp
Normal file
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
45
components/esm/activespells.hpp
Normal file
45
components/esm/activespells.hpp
Normal file
|
@ -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…
Reference in a new issue