Savegame: store projectiles

deque
scrawl 11 years ago
parent 15368188dc
commit e266aff561

@ -255,7 +255,8 @@ namespace MWBase
virtual void changeToExteriorCell (const ESM::Position& position) = 0;
///< Move to exterior cell.
virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position) = 0;
virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool detectWorldSpaceChange=true) = 0;
///< @param detectWorldSpaceChange if true, clean up worldspace-specific data when the world space changes
virtual const ESM::Cell *getExterior (const std::string& cellName) const = 0;
///< Return a cell matching the given name or a 0-pointer, if there is no such cell.

@ -785,7 +785,7 @@ namespace MWMechanics
// Update witness crime id
npcStats.setCrimeId(-1);
}
else if (!creatureStats.isHostile())
else if (!creatureStats.isHostile() && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue)
{
if (ptr.getClass().isClass(ptr, "Guard"))
creatureStats.getAiSequence().stack(AiPursue(player.getClass().getId(player)), ptr);

@ -694,15 +694,8 @@ Shadows* RenderingManager::getShadows()
return mShadows;
}
void RenderingManager::switchToInterior()
void RenderingManager::notifyWorldSpaceChanged()
{
// TODO: also do this when switching worldspace
mEffectManager->clear();
}
void RenderingManager::switchToExterior()
{
// TODO: also do this when switching worldspace
mEffectManager->clear();
}
@ -1061,6 +1054,7 @@ void RenderingManager::spawnEffect(const std::string &model, const std::string &
void RenderingManager::clear()
{
mLocalMap->clear();
notifyWorldSpaceChanged();
}
} // namespace

@ -163,8 +163,7 @@ public:
Shadows* getShadows();
void switchToInterior();
void switchToExterior();
void notifyWorldSpaceChanged();
void getTriangleBatchCount(unsigned int &triangles, unsigned int &batches);

@ -319,6 +319,8 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl
case ESM::REC_WTHR:
case ESM::REC_DYNA:
case ESM::REC_ACTC:
case ESM::REC_PROJ:
case ESM::REC_MPRJ:
MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap);
break;
@ -361,7 +363,11 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl
ESM::CellId cellId = ptr.getCell()->getCell()->getCellId();
MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition());
// Use detectWorldSpaceChange=false, otherwise some of the data we just loaded would be cleared again
MWBase::Environment::get().getWorld()->changeToCell (cellId, ptr.getRefData().getPosition(), false);
// Do not trigger erroneous cellChanged events
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
}
catch (const std::exception& e)
{

@ -4,6 +4,8 @@
#include <libs/openengine/bullet/physic.hpp>
#include <components/esm/projectilestate.hpp>
#include "../mwworld/manualref.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp"
@ -67,10 +69,12 @@ namespace MWWorld
MagicBoltState state;
state.mSourceName = sourceName;
state.mId = spellId;
state.mId = model;
state.mSpellId = spellId;
state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
state.mSpeed = speed;
state.mStack = stack;
state.mSoundId = sound;
// Only interested in "on target" effects
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.mList.begin());
@ -99,7 +103,7 @@ namespace MWWorld
state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
state.mBowId = bow.getCellRef().mRefID;
state.mVelocity = orient.yAxis() * speed;
state.mProjectileId = projectile.getCellRef().mRefID;
state.mId = projectile.getCellRef().mRefID;
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), projectile.getCellRef().mRefID);
MWWorld::Ptr ptr = ref.getPtr();
@ -159,7 +163,7 @@ namespace MWWorld
{
MWMechanics::CastSpell cast(caster, obstacle);
cast.mHitPosition = pos;
cast.mId = it->mId;
cast.mId = it->mSpellId;
cast.mSourceName = it->mSourceName;
cast.mStack = it->mStack;
cast.inflict(obstacle, caster, it->mEffects, ESM::RT_Target, false, true);
@ -170,7 +174,7 @@ namespace MWWorld
if (hit)
{
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId);
MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, it->mId, it->mSourceName);
MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, it->mSpellId, it->mSourceName);
MWBase::Environment::get().getSoundManager()->stopSound(it->mSound);
@ -224,7 +228,7 @@ namespace MWWorld
}
else if (obstacle.getClass().isActor())
{
MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mProjectileId);
MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mId);
// Try to get a Ptr to the bow that was used. It might no longer exist.
MWWorld::Ptr bow = projectileRef.getPtr();
@ -270,4 +274,127 @@ namespace MWWorld
mMagicBolts.clear();
}
void ProjectileManager::write(ESM::ESMWriter &writer, Loading::Listener &progress) const
{
for (std::vector<ProjectileState>::const_iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it)
{
writer.startRecord(ESM::REC_PROJ);
ESM::ProjectileState state;
state.mId = it->mId;
state.mPosition = it->mNode->getPosition();
state.mOrientation = it->mNode->getOrientation();
state.mActorId = it->mActorId;
state.mBowId = it->mBowId;
state.mVelocity = it->mVelocity;
state.save(writer);
writer.endRecord(ESM::REC_PROJ);
progress.increaseProgress();
}
for (std::vector<MagicBoltState>::const_iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it)
{
writer.startRecord(ESM::REC_MPRJ);
ESM::MagicBoltState state;
state.mId = it->mId;
state.mPosition = it->mNode->getPosition();
state.mOrientation = it->mNode->getOrientation();
state.mActorId = it->mActorId;
state.mSpellId = it->mSpellId;
state.mEffects = it->mEffects;
state.mSound = it->mSoundId;
state.mSourceName = it->mSourceName;
state.mSpeed = it->mSpeed;
state.mStack = it->mStack;
state.save(writer);
writer.endRecord(ESM::REC_MPRJ);
progress.increaseProgress();
}
}
bool ProjectileManager::readRecord(ESM::ESMReader &reader, int32_t type)
{
if (type == ESM::REC_PROJ)
{
ESM::ProjectileState esm;
esm.load(reader);
ProjectileState state;
state.mActorId = esm.mActorId;
state.mBowId = esm.mBowId;
state.mVelocity = esm.mVelocity;
state.mId = esm.mId;
std::string model;
try
{
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), esm.mId);
MWWorld::Ptr ptr = ref.getPtr();
model = ptr.getClass().getModel(ptr);
}
catch(...)
{
return true;
}
state.mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(esm.mPosition, esm.mOrientation);
createModel(state, model);
mProjectiles.push_back(state);
return true;
}
else if (type == ESM::REC_MPRJ)
{
ESM::MagicBoltState esm;
esm.load(reader);
MagicBoltState state;
state.mSourceName = esm.mSourceName;
state.mId = esm.mId;
state.mSpellId = esm.mSpellId;
state.mActorId = esm.mActorId;
state.mSpeed = esm.mSpeed;
state.mStack = esm.mStack;
state.mEffects = esm.mEffects;
std::string model;
try
{
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), esm.mId);
MWWorld::Ptr ptr = ref.getPtr();
model = ptr.getClass().getModel(ptr);
}
catch(...)
{
return true;
}
state.mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(esm.mPosition, esm.mOrientation);
createModel(state, model);
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
state.mSound = sndMgr->playManualSound3D(esm.mPosition, esm.mSound, 1.0f, 1.0f,
MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop);
mMagicBolts.push_back(state);
return true;
}
return false;
}
int ProjectileManager::countSavedGameRecords() const
{
return mMagicBolts.size() + mProjectiles.size();
}
}

@ -20,6 +20,11 @@ namespace Physic
}
}
namespace Loading
{
class Listener;
}
namespace Ogre
{
class SceneManager;
@ -46,6 +51,10 @@ namespace MWWorld
/// Removes all current projectiles. Should be called when switching to a new worldspace.
void clear();
void write (ESM::ESMWriter& writer, Loading::Listener& progress) const;
bool readRecord (ESM::ESMReader& reader, int32_t type);
int countSavedGameRecords() const;
private:
OEngine::Physic::PhysicEngine& mPhysEngine;
Ogre::SceneManager* mSceneMgr;
@ -54,15 +63,17 @@ namespace MWWorld
{
NifOgre::ObjectScenePtr mObject;
Ogre::SceneNode* mNode;
// Actor who shot this projectile
int mActorId;
// MW-id of this projectile
std::string mId;
};
struct MagicBoltState : public State
{
// Id of spell or enchantment to apply when it hits
std::string mId;
// Actor who casted this projectile
int mActorId;
std::string mSpellId;
// Name of item to display as effect source in magic menu (in case we casted an enchantment)
std::string mSourceName;
@ -74,16 +85,11 @@ namespace MWWorld
bool mStack;
MWBase::SoundPtr mSound;
std::string mSoundId;
};
struct ProjectileState : public State
{
// Actor who shot this projectile
int mActorId;
// RefID of the projectile
std::string mProjectileId;
// RefID of the bow or crossbow the actor was using when this projectile was fired (may be empty)
std::string mBowId;

@ -237,6 +237,8 @@ namespace MWWorld
{
mRendering->clear();
mProjectileManager->clear();
mLocalScripts.clear();
mPlayer->clear();
@ -275,6 +277,7 @@ namespace MWWorld
mCells.countSavedGameRecords()
+mStore.countSavedGameRecords()
+mGlobalVariables.countSavedGameRecords()
+mProjectileManager->countSavedGameRecords()
+1 // player record
+1 // weather record
+1; // actorId counter
@ -298,6 +301,7 @@ namespace MWWorld
mGlobalVariables.write (writer, progress);
mPlayer->write (writer, progress);
mWeatherManager->write (writer, progress);
mProjectileManager->write (writer, progress);
}
void World::readRecord (ESM::ESMReader& reader, int32_t type,
@ -313,7 +317,8 @@ namespace MWWorld
!mGlobalVariables.readRecord (reader, type) &&
!mPlayer->readRecord (reader, type) &&
!mWeatherManager->readRecord (reader, type) &&
!mCells.readRecord (reader, type, contentFileMap))
!mCells.readRecord (reader, type, contentFileMap) &&
!mProjectileManager->readRecord (reader, type))
{
throw std::runtime_error ("unknown record in saved game");
}
@ -808,9 +813,13 @@ namespace MWWorld
void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position)
{
// changed worldspace
mProjectileManager->clear();
mRendering->switchToInterior();
if (mCurrentWorldSpace != cellName)
{
// changed worldspace
mProjectileManager->clear();
mRendering->notifyWorldSpaceChanged();
mCurrentWorldSpace = cellName;
}
removeContainerScripts(getPlayerPtr());
mWorldScene->changeToInteriorCell(cellName, position);
@ -819,19 +828,22 @@ namespace MWWorld
void World::changeToExteriorCell (const ESM::Position& position)
{
if (!getPlayerPtr().getCell()->getCell()->isExterior())
if (mCurrentWorldSpace != "sys::default") // FIXME
{
// changed worldspace
mProjectileManager->clear();
mRendering->switchToExterior();
mRendering->notifyWorldSpaceChanged();
}
removeContainerScripts(getPlayerPtr());
mWorldScene->changeToExteriorCell(position);
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
}
void World::changeToCell (const ESM::CellId& cellId, const ESM::Position& position)
void World::changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool detectWorldSpaceChange)
{
if (!detectWorldSpaceChange)
mCurrentWorldSpace = cellId.mWorldspace;
if (cellId.mPaged)
changeToExteriorCell (position);
else

@ -75,6 +75,8 @@ namespace MWWorld
Cells mCells;
std::string mCurrentWorldSpace;
OEngine::Physic::PhysicEngine* mPhysEngine;
boost::shared_ptr<ProjectileManager> mProjectileManager;
@ -311,7 +313,8 @@ namespace MWWorld
virtual void changeToExteriorCell (const ESM::Position& position);
///< Move to exterior cell.
virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position);
virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position, bool detectWorldSpaceChange=true);
///< @param detectWorldSpaceChange if true, clean up worldspace-specific data when the world space changes
virtual const ESM::Cell *getExterior (const std::string& cellName) const;
///< Return a cell matching the given name or a 0-pointer, if there is no such cell.

@ -41,7 +41,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 activespells creaturelevliststate doorstate
npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate
)
add_component_dir (misc

@ -109,6 +109,8 @@ enum RecNameInts
REC_DYNA = FourCC<'D','Y','N','A'>::value,
REC_ASPL = FourCC<'A','S','P','L'>::value,
REC_ACTC = FourCC<'A','C','T','C'>::value,
REC_MPRJ = FourCC<'M','P','R','J'>::value,
REC_PROJ = FourCC<'P','R','O','J'>::value,
// format 1
REC_FILT = 0x544C4946

@ -0,0 +1,65 @@
#include "projectilestate.hpp"
#include "esmwriter.hpp"
#include "esmreader.hpp"
namespace ESM
{
void BaseProjectileState::save(ESMWriter &esm) const
{
esm.writeHNString ("ID__", mId);
esm.writeHNT ("VEC3", mPosition);
esm.writeHNT ("QUAT", mOrientation);
esm.writeHNT ("ACTO", mActorId);
}
void BaseProjectileState::load(ESMReader &esm)
{
mId = esm.getHNString("ID__");
esm.getHNT (mPosition, "VEC3");
esm.getHNT (mOrientation, "QUAT");
esm.getHNT (mActorId, "ACTO");
}
void MagicBoltState::save(ESMWriter &esm) const
{
BaseProjectileState::save(esm);
esm.writeHNString ("SPEL", mSpellId);
esm.writeHNString ("SRCN", mSourceName);
mEffects.save(esm);
esm.writeHNT ("SPED", mSpeed);
esm.writeHNT ("STCK", mStack);
esm.writeHNString ("SOUN", mSound);
}
void MagicBoltState::load(ESMReader &esm)
{
BaseProjectileState::load(esm);
mSpellId = esm.getHNString("SPEL");
mSourceName = esm.getHNString ("SRCN");
mEffects.load(esm);
esm.getHNT (mSpeed, "SPED");
esm.getHNT (mStack, "STCK");
mSound = esm.getHNString ("SOUN");
}
void ProjectileState::save(ESMWriter &esm) const
{
BaseProjectileState::save(esm);
esm.writeHNString ("BOW_", mBowId);
esm.writeHNT ("VEL_", mVelocity);
}
void ProjectileState::load(ESMReader &esm)
{
BaseProjectileState::load(esm);
mBowId = esm.getHNString ("BOW_");
esm.getHNT (mVelocity, "VEL_");
}
}

@ -0,0 +1,90 @@
#ifndef OPENMW_ESM_PROJECTILESTATE_H
#define OPENMW_ESM_PROJECTILESTATE_H
#include <string>
#include <OgreVector3.h>
#include <OgreQuaternion.h>
#include "effectlist.hpp"
namespace ESM
{
// format 0, savegames only
struct Quaternion
{
float mValues[4];
Quaternion() {}
Quaternion (Ogre::Quaternion q)
{
mValues[0] = q.w;
mValues[1] = q.x;
mValues[2] = q.y;
mValues[3] = q.z;
}
operator Ogre::Quaternion () const
{
return Ogre::Quaternion(mValues[0], mValues[1], mValues[2], mValues[3]);
}
};
struct Vector3
{
float mValues[3];
Vector3() {}
Vector3 (Ogre::Vector3 v)
{
mValues[0] = v.x;
mValues[1] = v.y;
mValues[2] = v.z;
}
operator Ogre::Vector3 () const
{
return Ogre::Vector3(&mValues[0]);
}
};
struct BaseProjectileState
{
std::string mId;
Vector3 mPosition;
Quaternion mOrientation;
int mActorId;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
struct MagicBoltState : public BaseProjectileState
{
std::string mSpellId;
std::string mSourceName;
ESM::EffectList mEffects;
float mSpeed;
bool mStack;
std::string mSound;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
struct ProjectileState : public BaseProjectileState
{
std::string mBowId;
Vector3 mVelocity;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;
};
}
#endif
Loading…
Cancel
Save