mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 19:19:56 +00:00
Feature #32: Implement respawn for containers, creatures and NPCs
This commit is contained in:
parent
92c5bb56e0
commit
ae66d28c87
16 changed files with 146 additions and 5 deletions
|
@ -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…
Reference in a new issue