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

pull/102/head
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
{
const std::string model = getModel(ptr);

@ -64,6 +64,8 @@ namespace MWClass
static void registerSelf();
virtual void respawn (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;
}
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::fMaxWalkSpeedCreature;
const ESM::GameSetting *Creature::fEncumberedMoveEffect;

@ -143,6 +143,8 @@ namespace MWClass
///< Write additional state from \a ptr into \a state.
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
int mSpawnActorId;
bool mSpawn; // Should a new creature be spawned?
virtual MWWorld::CustomData *clone() const;
};
@ -31,6 +32,14 @@ namespace MWClass
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()
{
boost::shared_ptr<Class> instance (new CreatureLevList);
@ -43,9 +52,8 @@ namespace MWClass
ensureCustomData(ptr);
CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData());
if (customData.mSpawnActorId != -1)
return; // TODO: handle respawning
if (!customData.mSpawn)
return;
MWWorld::LiveCellRef<ESM::CreatureLevList> *ref =
ptr.get<ESM::CreatureLevList>();
@ -54,11 +62,21 @@ namespace MWClass
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();
MWWorld::ManualRef ref(store, id);
ref.getPtr().getCellRef().mPos = 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.mSpawn = false;
}
}
@ -68,6 +86,7 @@ namespace MWClass
{
std::auto_ptr<CreatureLevListCustomData> data (new CreatureLevListCustomData);
data->mSpawnActorId = -1;
data->mSpawn = true;
ptr.getRefData().setCustomData(data.release());
}
@ -81,6 +100,7 @@ namespace MWClass
ensureCustomData(ptr);
CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData());
customData.mSpawnActorId = state2.mSpawnActorId;
customData.mSpawn = state2.mSpawn;
}
void CreatureLevList::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
@ -91,5 +111,6 @@ namespace MWClass
ensureCustomData(ptr);
CreatureLevListCustomData& customData = dynamic_cast<CreatureLevListCustomData&> (*ptr.getRefData().getCustomData());
state2.mSpawnActorId = customData.mSpawnActorId;
state2.mSpawn = customData.mSpawn;
}
}

@ -27,6 +27,8 @@ namespace MWClass
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< 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);
}
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::fMaxWalkSpeed;
const ESM::GameSetting *Npc::fEncumberedMoveEffect;

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

@ -169,7 +169,7 @@ namespace MWWorld
}
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;
}
@ -555,6 +555,7 @@ namespace MWWorld
mWaterLevel = state.mWaterLevel;
mWaterLevel = state.mWaterLevel;
mLastRespawn = MWWorld::TimeStamp(state.mLastRespawn);
}
void CellStore::saveState (ESM::CellState& state) const
@ -566,6 +567,7 @@ namespace MWWorld
state.mWaterLevel = mWaterLevel;
state.mHasFogOfWar = (mFogState.get() ? 1 : 0);
state.mLastRespawn = mLastRespawn.toEsm();
}
void CellStore::writeFog(ESM::ESMWriter &writer) const
@ -754,4 +756,36 @@ namespace MWWorld
{
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 "timestamp.hpp"
namespace ESM
{
struct CellState;
@ -48,6 +50,8 @@ namespace MWWorld
std::vector<std::string> mIds;
float mWaterLevel;
MWWorld::TimeStamp mLastRespawn;
CellRefList<ESM::Activator> mActivators;
CellRefList<ESM::Potion> mPotions;
CellRefList<ESM::Apparatus> mAppas;
@ -161,6 +165,9 @@ namespace MWWorld
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>
CellRefList<T>& get() {
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;
/// 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 respawn (const MWWorld::Ptr& ptr) const {}
};
}

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

@ -11,6 +11,10 @@ void ESM::CellState::load (ESMReader &esm)
mHasFogOfWar = false;
esm.getHNOT (mHasFogOfWar, "HFOW");
mLastRespawn.mDay = 0;
mLastRespawn.mHour = 0;
esm.getHNOT (mLastRespawn, "RESP");
}
void ESM::CellState::save (ESMWriter &esm) const
@ -18,5 +22,7 @@ void ESM::CellState::save (ESMWriter &esm) const
if (!mId.mPaged)
esm.writeHNT ("WLVL", mWaterLevel);
esm.writeHNT("HFOW", mHasFogOfWar);
esm.writeHNT ("HFOW", mHasFogOfWar);
esm.writeHNT ("RESP", mLastRespawn);
}

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

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

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

Loading…
Cancel
Save