Savegame: Store AiSettings and summoned creatures

CreatureStats state is now completely stored (Closes #1174)

Also play VFX_Summon_Start and VFX_Summon_End visual effects.
pull/164/head
scrawl 11 years ago
parent a0f9a6718f
commit ee2b81763e

@ -515,6 +515,8 @@ namespace MWBase
/// Spawn a blood effect for \a ptr at \a worldPosition
virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition) = 0;
virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const Ogre::Vector3& worldPos) = 0;
virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects,
const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0;

@ -20,6 +20,8 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwrender/animation.hpp"
#include "npcstats.hpp"
#include "creaturestats.hpp"
#include "movement.hpp"
@ -529,7 +531,8 @@ namespace MWMechanics
for (std::map<int, std::string>::iterator it = summonMap.begin(); it != summonMap.end(); ++it)
{
bool found = creatureStats.mSummonedCreatures.find(it->first) != creatureStats.mSummonedCreatures.end();
std::map<int, int>& creatureMap = creatureStats.getSummonedCreatureMap();
bool found = creatureMap.find(it->first) != creatureMap.end();
int magnitude = creatureStats.getMagicEffects().get(it->first).mMagnitude;
if (found != (magnitude > 0))
{
@ -563,17 +566,25 @@ namespace MWMechanics
summonedCreatureStats.getAiSequence().stack(package, ref.getPtr());
int creatureActorId = summonedCreatureStats.getActorId();
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos);
MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos);
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(placed);
if (anim)
{
const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()
.search("VFX_Summon_Start");
if (fx)
anim->addEffect("meshes\\" + fx->mModel, -1, false);
}
// TODO: VFX_SummonStart, VFX_SummonEnd
creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, creatureActorId));
creatureMap.insert(std::make_pair(it->first, creatureActorId));
}
}
else
{
// Summon lifetime has expired. Try to delete the creature.
int actorId = creatureStats.mSummonedCreatures[it->first];
creatureStats.mSummonedCreatures.erase(it->first);
int actorId = creatureMap[it->first];
creatureMap.erase(it->first);
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(actorId);
if (!ptr.isEmpty())
@ -581,24 +592,38 @@ namespace MWMechanics
// TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation
// plays though, which is a rather lame exploit in vanilla.
MWBase::Environment::get().getWorld()->deleteObject(ptr);
creatureStats.mSummonedCreatures.erase(it->first);
const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()
.search("VFX_Summon_End");
if (fx)
MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel,
"", Ogre::Vector3(ptr.getRefData().getPosition().pos));
}
else
{
// We didn't find the creature. It's probably in an inactive cell.
// Add to graveyard so we can delete it when the cell becomes active.
creatureStats.mSummonGraveyard.push_back(actorId);
std::vector<int>& graveyard = creatureStats.getSummonedCreatureGraveyard();
graveyard.push_back(actorId);
}
}
}
}
for (std::vector<int>::iterator it = creatureStats.mSummonGraveyard.begin(); it != creatureStats.mSummonGraveyard.end(); )
std::vector<int>& graveyard = creatureStats.getSummonedCreatureGraveyard();
for (std::vector<int>::iterator it = graveyard.begin(); it != graveyard.end(); )
{
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(*it);
if (!ptr.isEmpty())
{
it = creatureStats.mSummonGraveyard.erase(it);
it = graveyard.erase(it);
const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()
.search("VFX_Summon_End");
if (fx)
MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel,
"", Ogre::Vector3(ptr.getRefData().getPosition().pos));
MWBase::Environment::get().getWorld()->deleteObject(ptr);
}
else

@ -504,6 +504,13 @@ namespace MWMechanics
mSpells.writeState(state.mSpells);
mActiveSpells.writeState(state.mActiveSpells);
mAiSequence.writeState(state.mAiSequence);
state.mSummonedCreatureMap = mSummonedCreatures;
state.mSummonGraveyard = mSummonGraveyard;
state.mHasAiSettings = true;
for (int i=0; i<4; ++i)
mAiSettings[i].writeState (state.mAiSettings[i]);
}
void CreatureStats::readState (const ESM::CreatureStats& state)
@ -545,6 +552,13 @@ namespace MWMechanics
mSpells.readState(state.mSpells);
mActiveSpells.readState(state.mActiveSpells);
mAiSequence.readState(state.mAiSequence);
mSummonedCreatures = state.mSummonedCreatureMap;
mSummonGraveyard = state.mSummonGraveyard;
if (state.mHasAiSettings)
for (int i=0; i<4; ++i)
mAiSettings[i].readState(state.mAiSettings[i]);
}
void CreatureStats::setLastRestockTime(MWWorld::TimeStamp tradeTime)
@ -605,4 +619,14 @@ namespace MWMechanics
{
mDeathAnimation = index;
}
std::map<int, int>& CreatureStats::getSummonedCreatureMap()
{
return mSummonedCreatures;
}
std::vector<int>& CreatureStats::getSummonedCreatureGraveyard()
{
return mSummonGraveyard;
}
}

@ -67,6 +67,12 @@ namespace MWMechanics
// The index of the death animation that was played
unsigned char mDeathAnimation;
// <ESM::MagicEffect index, ActorId>
std::map<int, int> mSummonedCreatures;
// Contains ActorIds of summoned creatures with an expired lifetime that have not been deleted yet.
// This may be necessary when the creature is in an inactive cell.
std::vector<int> mSummonGraveyard;
protected:
// These two are only set by NpcStats, but they are declared in CreatureStats to prevent using virtual methods.
bool mIsWerewolf;
@ -208,6 +214,9 @@ namespace MWMechanics
void setBlock(bool value);
bool getBlock() const;
std::map<int, int>& getSummonedCreatureMap();
std::vector<int>& getSummonedCreatureGraveyard();
enum Flag
{
Flag_ForceRun = 1,
@ -233,13 +242,6 @@ namespace MWMechanics
// TODO: Put it somewhere else?
std::set<int> mBoundItems;
// TODO: store in savegame
// TODO: encapsulate?
// <ESM::MagicEffect index, actor index>
std::map<int, int> mSummonedCreatures;
// Contains summoned creatures with an expired lifetime that have not been deleted yet.
std::vector<int> mSummonGraveyard;
void writeState (ESM::CreatureStats& state) const;
void readState (const ESM::CreatureStats& state);

@ -210,7 +210,7 @@ public:
/**
* @brief Add an effect mesh attached to a bone or the insert scene node
* @param model
* @param effectId An ID for this effect. Note that adding the same ID again won't add another effect.
* @param effectId An ID for this effect by which you can identify it later. If this is not wanted, set to -1.
* @param loop Loop the effect. If false, it is removed automatically after it finishes playing. If true,
* you need to remove it manually using removeEffect when the effect should end.
* @param bonename Bone to attach to, or empty string to use the scene node instead

@ -2704,6 +2704,11 @@ namespace MWWorld
mRendering->spawnEffect(model, texture, worldPosition);
}
void World::spawnEffect(const std::string &model, const std::string &textureOverride, const Vector3 &worldPos)
{
mRendering->spawnEffect(model, textureOverride, worldPos);
}
void World::explodeSpell(const Vector3 &origin, const ESM::EffectList &effects, const Ptr &caster,
const std::string& id, const std::string& sourceName)
{

@ -585,6 +585,8 @@ namespace MWWorld
/// Spawn a blood effect for \a ptr at \a worldPosition
virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition);
virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const Ogre::Vector3& worldPos);
virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects,
const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName);

@ -80,6 +80,31 @@ void ESM::CreatureStats::load (ESMReader &esm)
mSpells.load(esm);
mActiveSpells.load(esm);
mAiSequence.load(esm);
while (esm.isNextSub("SUMM"))
{
int magicEffect;
esm.getHT(magicEffect);
int actorId;
esm.getHNT (actorId, "ACID");
mSummonedCreatureMap[magicEffect] = actorId;
}
while (esm.isNextSub("GRAV"))
{
int actorId;
esm.getHT(actorId);
mSummonGraveyard.push_back(actorId);
}
mHasAiSettings = false;
esm.getHNOT(mHasAiSettings, "AISE");
if (mHasAiSettings)
{
for (int i=0; i<4; ++i)
mAiSettings[i].load(esm);
}
}
void ESM::CreatureStats::save (ESMWriter &esm) const
@ -162,4 +187,19 @@ void ESM::CreatureStats::save (ESMWriter &esm) const
mSpells.save(esm);
mActiveSpells.save(esm);
mAiSequence.save(esm);
for (std::map<int, int>::const_iterator it = mSummonedCreatureMap.begin(); it != mSummonedCreatureMap.end(); ++it)
{
esm.writeHNT ("SUMM", it->first);
esm.writeHNT ("ACID", it->second);
}
for (std::vector<int>::const_iterator it = mSummonGraveyard.begin(); it != mSummonGraveyard.end(); ++it)
{
esm.writeHNT ("GRAV", *it);
}
esm.writeHNT("AISE", mHasAiSettings);
for (int i=0; i<4; ++i)
mAiSettings[i].save(esm);
}

@ -26,6 +26,12 @@ namespace ESM
AiSequence::AiSequence mAiSequence;
bool mHasAiSettings;
StatState<int> mAiSettings[4];
std::map<int, int> mSummonedCreatureMap;
std::vector<int> mSummonGraveyard;
ESM::TimeStamp mTradeTime;
int mGoldPool;
int mActorId;

Loading…
Cancel
Save