forked from mirror/openmw-tes3mp
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.
This commit is contained in:
parent
a0f9a6718f
commit
ee2b81763e
9 changed files with 124 additions and 18 deletions
|
@ -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);
|
||||
|
||||
// TODO: VFX_SummonStart, VFX_SummonEnd
|
||||
creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, creatureActorId));
|
||||
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);
|
||||
}
|
||||
|
||||
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…
Reference in a new issue