Feature #32: Implement respawn for containers, creatures and NPCs

deque
scrawl 11 years ago
parent 92c5bb56e0
commit ae66d28c87

@ -60,6 +60,16 @@ namespace MWClass
} }
} }
void Container::respawn(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Container> *ref =
ptr.get<ESM::Container>();
if (ref->mBase->mFlags & ESM::Container::Respawn)
{
ptr.getRefData().setCustomData(NULL);
}
}
void Container::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const void Container::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{ {
const std::string model = getModel(ptr); const std::string model = getModel(ptr);

@ -64,6 +64,8 @@ namespace MWClass
static void registerSelf(); static void registerSelf();
virtual void respawn (const MWWorld::Ptr& ptr) const;
virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual std::string getModel(const MWWorld::Ptr &ptr) const;
}; };
} }

@ -822,6 +822,26 @@ namespace MWClass
return ptr.get<ESM::Creature>()->mBase->mData.mGold; return ptr.get<ESM::Creature>()->mBase->mData.mGold;
} }
void Creature::respawn(const MWWorld::Ptr &ptr) const
{
if (ptr.get<ESM::Creature>()->mBase->mFlags & ESM::Creature::Respawn)
{
// Note we do not respawn moved references in the cell they were moved to. Instead they are respawned in the original cell.
// This also means we cannot respawn dynamically placed references with no content file connection.
if (ptr.getCellRef().mRefNum.mContentFile != -1)
{
if (ptr.getRefData().getCount() == 0)
ptr.getRefData().setCount(1);
// Reset to original position
ESM::Position& pos = ptr.getRefData().getPosition();
pos = ptr.getCellRef().mPos;
ptr.getRefData().setCustomData(NULL);
}
}
}
const ESM::GameSetting* Creature::fMinWalkSpeedCreature; const ESM::GameSetting* Creature::fMinWalkSpeedCreature;
const ESM::GameSetting* Creature::fMaxWalkSpeedCreature; const ESM::GameSetting* Creature::fMaxWalkSpeedCreature;
const ESM::GameSetting *Creature::fEncumberedMoveEffect; const ESM::GameSetting *Creature::fEncumberedMoveEffect;

@ -143,6 +143,8 @@ namespace MWClass
///< Write additional state from \a ptr into \a state. ///< Write additional state from \a ptr into \a state.
virtual int getBaseGold(const MWWorld::Ptr& ptr) const; virtual int getBaseGold(const MWWorld::Ptr& ptr) const;
virtual void respawn (const MWWorld::Ptr& ptr) const;
}; };
} }

@ -14,6 +14,7 @@ namespace
{ {
// actorId of the creature we spawned // actorId of the creature we spawned
int mSpawnActorId; int mSpawnActorId;
bool mSpawn; // Should a new creature be spawned?
virtual MWWorld::CustomData *clone() const; virtual MWWorld::CustomData *clone() const;
}; };
@ -31,6 +32,14 @@ namespace MWClass
return ""; return "";
} }
void CreatureLevList::respawn(const MWWorld::Ptr &ptr) const
{
ensureCustomData(ptr);
CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData());
customData.mSpawn = true;
}
void CreatureLevList::registerSelf() void CreatureLevList::registerSelf()
{ {
boost::shared_ptr<Class> instance (new CreatureLevList); boost::shared_ptr<Class> instance (new CreatureLevList);
@ -43,9 +52,8 @@ namespace MWClass
ensureCustomData(ptr); ensureCustomData(ptr);
CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData()); CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData());
if (customData.mSpawnActorId != -1) if (!customData.mSpawn)
return; // TODO: handle respawning return;
MWWorld::LiveCellRef<ESM::CreatureLevList> *ref = MWWorld::LiveCellRef<ESM::CreatureLevList> *ref =
ptr.get<ESM::CreatureLevList>(); ptr.get<ESM::CreatureLevList>();
@ -54,11 +62,21 @@ namespace MWClass
if (!id.empty()) if (!id.empty())
{ {
// Delete the previous creature
if (customData.mSpawnActorId != -1)
{
MWWorld::Ptr creature = MWBase::Environment::get().getWorld()->searchPtrViaActorId(customData.mSpawnActorId);
if (!creature.isEmpty())
MWBase::Environment::get().getWorld()->deleteObject(creature);
customData.mSpawnActorId = -1;
}
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
MWWorld::ManualRef ref(store, id); MWWorld::ManualRef ref(store, id);
ref.getPtr().getCellRef().mPos = ptr.getCellRef().mPos; ref.getPtr().getCellRef().mPos = ptr.getCellRef().mPos;
MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().mPos); MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().mPos);
customData.mSpawnActorId = placed.getClass().getCreatureStats(placed).getActorId(); customData.mSpawnActorId = placed.getClass().getCreatureStats(placed).getActorId();
customData.mSpawn = false;
} }
} }
@ -68,6 +86,7 @@ namespace MWClass
{ {
std::auto_ptr<CreatureLevListCustomData> data (new CreatureLevListCustomData); std::auto_ptr<CreatureLevListCustomData> data (new CreatureLevListCustomData);
data->mSpawnActorId = -1; data->mSpawnActorId = -1;
data->mSpawn = true;
ptr.getRefData().setCustomData(data.release()); ptr.getRefData().setCustomData(data.release());
} }
@ -81,6 +100,7 @@ namespace MWClass
ensureCustomData(ptr); ensureCustomData(ptr);
CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData()); CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData());
customData.mSpawnActorId = state2.mSpawnActorId; customData.mSpawnActorId = state2.mSpawnActorId;
customData.mSpawn = state2.mSpawn;
} }
void CreatureLevList::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) void CreatureLevList::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
@ -91,5 +111,6 @@ namespace MWClass
ensureCustomData(ptr); ensureCustomData(ptr);
CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData()); CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData());
state2.mSpawnActorId = customData.mSpawnActorId; state2.mSpawnActorId = customData.mSpawnActorId;
state2.mSpawn = customData.mSpawn;
} }
} }

@ -27,6 +27,8 @@ namespace MWClass
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const; const;
///< Write additional state from \a ptr into \a state. ///< Write additional state from \a ptr into \a state.
virtual void respawn (const MWWorld::Ptr& ptr) const;
}; };
} }

@ -1310,6 +1310,26 @@ namespace MWClass
return Misc::StringUtils::ciEqual(ptr.get<ESM::NPC>()->mBase->mClass, className); return Misc::StringUtils::ciEqual(ptr.get<ESM::NPC>()->mBase->mClass, className);
} }
void Npc::respawn(const MWWorld::Ptr &ptr) const
{
if (ptr.get<ESM::NPC>()->mBase->mFlags & ESM::NPC::Respawn)
{
// Note we do not respawn moved references in the cell they were moved to. Instead they are respawned in the original cell.
// This also means we cannot respawn dynamically placed references with no content file connection.
if (ptr.getCellRef().mRefNum.mContentFile != -1)
{
if (ptr.getRefData().getCount() == 0)
ptr.getRefData().setCount(1);
// Reset to original position
ESM::Position& pos = ptr.getRefData().getPosition();
pos = ptr.getCellRef().mPos;
ptr.getRefData().setCustomData(NULL);
}
}
}
const ESM::GameSetting *Npc::fMinWalkSpeed; const ESM::GameSetting *Npc::fMinWalkSpeed;
const ESM::GameSetting *Npc::fMaxWalkSpeed; const ESM::GameSetting *Npc::fMaxWalkSpeed;
const ESM::GameSetting *Npc::fEncumberedMoveEffect; const ESM::GameSetting *Npc::fEncumberedMoveEffect;

@ -178,6 +178,8 @@ namespace MWClass
virtual bool canWalk (const MWWorld::Ptr &ptr) const { virtual bool canWalk (const MWWorld::Ptr &ptr) const {
return true; return true;
} }
virtual void respawn (const MWWorld::Ptr& ptr) const;
}; };
} }

@ -169,7 +169,7 @@ namespace MWWorld
} }
CellStore::CellStore (const ESM::Cell *cell) CellStore::CellStore (const ESM::Cell *cell)
: mCell (cell), mState (State_Unloaded), mHasState (false) : mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0)
{ {
mWaterLevel = cell->mWater; mWaterLevel = cell->mWater;
} }
@ -555,6 +555,7 @@ namespace MWWorld
mWaterLevel = state.mWaterLevel; mWaterLevel = state.mWaterLevel;
mWaterLevel = state.mWaterLevel; mWaterLevel = state.mWaterLevel;
mLastRespawn = MWWorld::TimeStamp(state.mLastRespawn);
} }
void CellStore::saveState (ESM::CellState& state) const void CellStore::saveState (ESM::CellState& state) const
@ -566,6 +567,7 @@ namespace MWWorld
state.mWaterLevel = mWaterLevel; state.mWaterLevel = mWaterLevel;
state.mHasFogOfWar = (mFogState.get() ? 1 : 0); state.mHasFogOfWar = (mFogState.get() ? 1 : 0);
state.mLastRespawn = mLastRespawn.toEsm();
} }
void CellStore::writeFog(ESM::ESMWriter &writer) const void CellStore::writeFog(ESM::ESMWriter &writer) const
@ -754,4 +756,36 @@ namespace MWWorld
{ {
return mFogState.get(); return mFogState.get();
} }
void CellStore::respawn()
{
if (mState == State_Loaded)
{
static int iMonthsToRespawn = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("iMonthsToRespawn")->getInt();
if (MWBase::Environment::get().getWorld()->getTimeStamp() - mLastRespawn > 24*30*iMonthsToRespawn)
{
mLastRespawn = MWBase::Environment::get().getWorld()->getTimeStamp();
for (CellRefList<ESM::Container>::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it)
{
Ptr ptr (&*it, this);
ptr.getClass().respawn(ptr);
}
for (CellRefList<ESM::Creature>::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it)
{
Ptr ptr (&*it, this);
ptr.getClass().respawn(ptr);
}
for (CellRefList<ESM::NPC>::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it)
{
Ptr ptr (&*it, this);
ptr.getClass().respawn(ptr);
}
for (CellRefList<ESM::CreatureLevList>::List::iterator it (mCreatureLists.mList.begin()); it!=mCreatureLists.mList.end(); ++it)
{
Ptr ptr (&*it, this);
ptr.getClass().respawn(ptr);
}
}
}
}
} }

@ -13,6 +13,8 @@
#include "../mwmechanics/pathgrid.hpp" // TODO: maybe belongs in mwworld #include "../mwmechanics/pathgrid.hpp" // TODO: maybe belongs in mwworld
#include "timestamp.hpp"
namespace ESM namespace ESM
{ {
struct CellState; struct CellState;
@ -48,6 +50,8 @@ namespace MWWorld
std::vector<std::string> mIds; std::vector<std::string> mIds;
float mWaterLevel; float mWaterLevel;
MWWorld::TimeStamp mLastRespawn;
CellRefList<ESM::Activator> mActivators; CellRefList<ESM::Activator> mActivators;
CellRefList<ESM::Potion> mPotions; CellRefList<ESM::Potion> mPotions;
CellRefList<ESM::Apparatus> mAppas; CellRefList<ESM::Apparatus> mAppas;
@ -161,6 +165,9 @@ namespace MWWorld
void readReferences (ESM::ESMReader& reader, const std::map<int, int>& contentFileMap); void readReferences (ESM::ESMReader& reader, const std::map<int, int>& contentFileMap);
void respawn ();
///< Check mLastRespawn and respawn references if necessary. This is a no-op if the cell is not loaded.
template <class T> template <class T>
CellRefList<T>& get() { CellRefList<T>& get() {
throw std::runtime_error ("Storage for this type not exist in cells"); throw std::runtime_error ("Storage for this type not exist in cells");

@ -341,6 +341,8 @@ namespace MWWorld
virtual int getDoorState (const MWWorld::Ptr &ptr) const; virtual int getDoorState (const MWWorld::Ptr &ptr) const;
/// This does not actually cause the door to move. Use World::activateDoor instead. /// This does not actually cause the door to move. Use World::activateDoor instead.
virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const; virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const;
virtual void respawn (const MWWorld::Ptr& ptr) const {}
}; };
} }

@ -165,6 +165,8 @@ namespace MWWorld
} }
} }
cell->respawn();
// ... then references. This is important for adjustPosition to work correctly. // ... then references. This is important for adjustPosition to work correctly.
/// \todo rescale depending on the state of a new GMST /// \todo rescale depending on the state of a new GMST
insertCell (*cell, true, loadingListener); insertCell (*cell, true, loadingListener);

@ -11,6 +11,10 @@ void ESM::CellState::load (ESMReader &esm)
mHasFogOfWar = false; mHasFogOfWar = false;
esm.getHNOT (mHasFogOfWar, "HFOW"); esm.getHNOT (mHasFogOfWar, "HFOW");
mLastRespawn.mDay = 0;
mLastRespawn.mHour = 0;
esm.getHNOT (mLastRespawn, "RESP");
} }
void ESM::CellState::save (ESMWriter &esm) const void ESM::CellState::save (ESMWriter &esm) const
@ -19,4 +23,6 @@ void ESM::CellState::save (ESMWriter &esm) const
esm.writeHNT ("WLVL", mWaterLevel); esm.writeHNT ("WLVL", mWaterLevel);
esm.writeHNT ("HFOW", mHasFogOfWar); esm.writeHNT ("HFOW", mHasFogOfWar);
esm.writeHNT ("RESP", mLastRespawn);
} }

@ -3,6 +3,8 @@
#include "cellid.hpp" #include "cellid.hpp"
#include "defs.hpp"
namespace ESM namespace ESM
{ {
class ESMReader; class ESMReader;
@ -19,6 +21,8 @@ namespace ESM
int mHasFogOfWar; // Do we have fog of war state (0 or 1)? (see fogstate.hpp) int mHasFogOfWar; // Do we have fog of war state (0 or 1)? (see fogstate.hpp)
ESM::TimeStamp mLastRespawn;
void load (ESMReader &esm); void load (ESMReader &esm);
void save (ESMWriter &esm) const; void save (ESMWriter &esm) const;
}; };

@ -12,6 +12,9 @@ namespace ESM
mSpawnActorId = -1; mSpawnActorId = -1;
esm.getHNOT (mSpawnActorId, "SPAW"); esm.getHNOT (mSpawnActorId, "SPAW");
mSpawn = false;
esm.getHNOT (mSpawn, "RESP");
} }
void CreatureLevListState::save(ESMWriter &esm, bool inInventory) const void CreatureLevListState::save(ESMWriter &esm, bool inInventory) const
@ -20,6 +23,9 @@ namespace ESM
if (mSpawnActorId != -1) if (mSpawnActorId != -1)
esm.writeHNT ("SPAW", mSpawnActorId); esm.writeHNT ("SPAW", mSpawnActorId);
if (mSpawn)
esm.writeHNT ("RESP", mSpawn);
} }
} }

@ -10,6 +10,7 @@ namespace ESM
struct CreatureLevListState : public ObjectState struct CreatureLevListState : public ObjectState
{ {
int mSpawnActorId; int mSpawnActorId;
bool mSpawn;
virtual void load (ESMReader &esm); virtual void load (ESMReader &esm);
virtual void save (ESMWriter &esm, bool inInventory = false) const; virtual void save (ESMWriter &esm, bool inInventory = false) const;

Loading…
Cancel
Save