#include "cellstore.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwmechanics/creaturestats.hpp" #include "ptr.hpp" #include "esmstore.hpp" #include "class.hpp" #include "containerstore.hpp" namespace { template MWWorld::Ptr searchInContainerList (MWWorld::CellRefList& containerList, const std::string& id) { for (typename MWWorld::CellRefList::List::iterator iter (containerList.mList.begin()); iter!=containerList.mList.end(); ++iter) { MWWorld::Ptr container (&*iter, 0); MWWorld::Ptr ptr = container.getClass().getContainerStore (container).search (id); if (!ptr.isEmpty()) return ptr; } return MWWorld::Ptr(); } template MWWorld::Ptr searchViaActorId (MWWorld::CellRefList& actorList, int actorId, MWWorld::CellStore *cell, const std::map& toIgnore) { for (typename MWWorld::CellRefList::List::iterator iter (actorList.mList.begin()); iter!=actorList.mList.end(); ++iter) { MWWorld::Ptr actor (&*iter, cell); if (toIgnore.find(&*iter) != toIgnore.end()) continue; if (actor.getClass().getCreatureStats (actor).matchesActorId (actorId) && actor.getRefData().getCount() > 0) return actor; } return MWWorld::Ptr(); } template void writeReferenceCollection (ESM::ESMWriter& writer, const MWWorld::CellRefList& collection) { if (!collection.mList.empty()) { // references for (typename MWWorld::CellRefList::List::const_iterator iter (collection.mList.begin()); iter!=collection.mList.end(); ++iter) { if (!iter->mData.hasChanged() && !iter->mRef.hasChanged() && iter->mRef.hasContentFile()) { // Reference that came from a content file and has not been changed -> ignore continue; } if (iter->mData.getCount()==0 && !iter->mRef.hasContentFile()) { // Deleted reference that did not come from a content file -> ignore continue; } RecordType state; iter->save (state); // recordId currently unused writer.writeHNT ("OBJE", collection.mList.front().mBase->sRecordId); state.save (writer); } } } template void readReferenceCollection (ESM::ESMReader& reader, MWWorld::CellRefList& collection, const ESM::CellRef& cref, const std::map& contentFileMap) { const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); RecordType state; state.mRef = cref; state.load(reader); // If the reference came from a content file, make sure this content file is loaded if (state.mRef.mRefNum.hasContentFile()) { std::map::const_iterator iter = contentFileMap.find (state.mRef.mRefNum.mContentFile); if (iter==contentFileMap.end()) return; // content file has been removed -> skip state.mRef.mRefNum.mContentFile = iter->second; } if (!MWWorld::LiveCellRef::checkState (state)) return; // not valid anymore with current content files -> skip const T *record = esmStore.get().search (state.mRef.mRefID); if (!record) return; if (state.mRef.mRefNum.hasContentFile()) { for (typename MWWorld::CellRefList::List::iterator iter (collection.mList.begin()); iter!=collection.mList.end(); ++iter) if (iter->mRef.getRefNum()==state.mRef.mRefNum) { // overwrite existing reference iter->load (state); return; } std::cerr << "Warning: Dropping reference to " << state.mRef.mRefID << " (invalid content file link)" << std::endl; return; } // new reference MWWorld::LiveCellRef ref (record); ref.load (state); collection.mList.push_back (ref); } struct SearchByRefNumVisitor { MWWorld::LiveCellRefBase* mFound; ESM::RefNum mRefNumToFind; SearchByRefNumVisitor(const ESM::RefNum& toFind) : mFound(NULL) , mRefNumToFind(toFind) { } bool operator()(const MWWorld::Ptr& ptr) { if (ptr.getCellRef().getRefNum() == mRefNumToFind) { mFound = ptr.getBase(); return false; } return true; } }; } namespace MWWorld { template void CellRefList::load(ESM::CellRef &ref, bool deleted, const MWWorld::ESMStore &esmStore) { const MWWorld::Store &store = esmStore.get(); if (const X *ptr = store.search (ref.mRefID)) { typename std::list::iterator iter = std::find(mList.begin(), mList.end(), ref.mRefNum); LiveRef liveCellRef (ref, ptr); if (deleted) liveCellRef.mData.setDeletedByContentFile(true); if (iter != mList.end()) *iter = liveCellRef; else mList.push_back (liveCellRef); } else { std::cerr << "Warning: could not resolve cell reference '" << ref.mRefID << "'" << " (dropping reference)" << std::endl; } } template bool operator==(const LiveCellRef& ref, int pRefnum) { return (ref.mRef.mRefnum == pRefnum); } Ptr CellStore::getCurrentPtr(LiveCellRefBase *ref) { MovedRefTracker::iterator found = mMovedToAnotherCell.find(ref); if (found != mMovedToAnotherCell.end()) return Ptr(ref, found->second); return Ptr(ref, this); } void CellStore::moveFrom(const Ptr &object, CellStore *from) { if (mState != State_Loaded) load(); mHasState = true; MovedRefTracker::iterator found = mMovedToAnotherCell.find(object.getBase()); if (found != mMovedToAnotherCell.end()) { // A cell we had previously moved an object to is returning it to us. assert (found->second == from); mMovedToAnotherCell.erase(found); } else { mMovedHere.insert(std::make_pair(object.getBase(), from)); } updateMergedRefs(); } MWWorld::Ptr CellStore::moveTo(const Ptr &object, CellStore *cellToMoveTo) { if (cellToMoveTo == this) throw std::runtime_error("moveTo: object is already in this cell"); // We assume that *this is in State_Loaded since we could hardly have reference to a live object otherwise. if (mState != State_Loaded) throw std::runtime_error("moveTo: can't move object from a non-loaded cell (how did you get this object anyway?)"); // Ensure that the object actually exists in the cell SearchByRefNumVisitor searchVisitor(object.getCellRef().getRefNum()); forEach(searchVisitor); if (!searchVisitor.mFound) throw std::runtime_error("moveTo: object is not in this cell"); // Objects with no refnum can't be handled correctly in the merging process that happens // on a save/load, so do a simple copy & delete for these objects. if (!object.getCellRef().getRefNum().hasContentFile()) { MWWorld::Ptr copied = object.getClass().copyToCell(object, *cellToMoveTo, object.getRefData().getCount()); object.getRefData().setCount(0); object.getRefData().setBaseNode(NULL); return copied; } MovedRefTracker::iterator found = mMovedHere.find(object.getBase()); if (found != mMovedHere.end()) { // Special case - object didn't originate in this cell // Move it back to its original cell first CellStore* originalCell = found->second; assert (originalCell != this); originalCell->moveFrom(object, this); mMovedHere.erase(found); // Now that object is back to its rightful owner, we can move it if (cellToMoveTo != originalCell) { originalCell->moveTo(object, cellToMoveTo); } updateMergedRefs(); return MWWorld::Ptr(object.getBase(), cellToMoveTo); } cellToMoveTo->moveFrom(object, this); mMovedToAnotherCell.insert(std::make_pair(object.getBase(), cellToMoveTo)); updateMergedRefs(); return MWWorld::Ptr(object.getBase(), cellToMoveTo); } struct MergeVisitor { MergeVisitor(std::vector& mergeTo, const std::map& movedHere, const std::map& movedToAnotherCell) : mMergeTo(mergeTo) , mMovedHere(movedHere) , mMovedToAnotherCell(movedToAnotherCell) { } bool operator() (const MWWorld::Ptr& ptr) { if (mMovedToAnotherCell.find(ptr.getBase()) != mMovedToAnotherCell.end()) return true; mMergeTo.push_back(ptr.getBase()); return true; } void merge() { for (std::map::const_iterator it = mMovedHere.begin(); it != mMovedHere.end(); ++it) mMergeTo.push_back(it->first); } private: std::vector& mMergeTo; const std::map& mMovedHere; const std::map& mMovedToAnotherCell; }; void CellStore::updateMergedRefs() { mMergedRefs.clear(); MergeVisitor visitor(mMergedRefs, mMovedHere, mMovedToAnotherCell); forEachInternal(visitor); visitor.merge(); } CellStore::CellStore (const ESM::Cell *cell, const MWWorld::ESMStore& esmStore, std::vector& readerList) : mStore(esmStore), mReader(readerList), mCell (cell), mState (State_Unloaded), mHasState (false), mLastRespawn(0,0) { mWaterLevel = cell->mWater; } const ESM::Cell *CellStore::getCell() const { return mCell; } CellStore::State CellStore::getState() const { return mState; } const std::vector &CellStore::getPreloadedIds() const { return mIds; } bool CellStore::hasState() const { return mHasState; } bool CellStore::hasId (const std::string& id) const { if (mState==State_Unloaded) return false; if (mState==State_Preloaded) return std::binary_search (mIds.begin(), mIds.end(), id); return searchConst (id).isEmpty(); } template struct SearchVisitor { PtrType mFound; std::string mIdToFind; bool operator()(const PtrType& ptr) { if (ptr.getCellRef().getRefId() == mIdToFind) { mFound = ptr; return false; } return true; } }; Ptr CellStore::search (const std::string& id) { SearchVisitor searchVisitor; searchVisitor.mIdToFind = id; forEach(searchVisitor); return searchVisitor.mFound; } ConstPtr CellStore::searchConst (const std::string& id) const { SearchVisitor searchVisitor; searchVisitor.mIdToFind = id; forEachConst(searchVisitor); return searchVisitor.mFound; } Ptr CellStore::searchViaActorId (int id) { if (Ptr ptr = ::searchViaActorId (mNpcs, id, this, mMovedToAnotherCell)) return ptr; if (Ptr ptr = ::searchViaActorId (mCreatures, id, this, mMovedToAnotherCell)) return ptr; for (MovedRefTracker::const_iterator it = mMovedHere.begin(); it != mMovedHere.end(); ++it) { MWWorld::Ptr actor (it->first, this); if (!actor.getClass().isActor()) continue; if (actor.getClass().getCreatureStats (actor).matchesActorId (id) && actor.getRefData().getCount() > 0) return actor; } return Ptr(); } float CellStore::getWaterLevel() const { if (isExterior()) return -1; return mWaterLevel; } void CellStore::setWaterLevel (float level) { mWaterLevel = level; mHasState = true; } int CellStore::count() const { return mMergedRefs.size(); } void CellStore::load () { if (mState!=State_Loaded) { if (mState==State_Preloaded) mIds.clear(); loadRefs (); mState = State_Loaded; } } void CellStore::preload () { if (mState==State_Unloaded) { listRefs (); mState = State_Preloaded; } } void CellStore::listRefs() { std::vector& esm = mReader; assert (mCell); if (mCell->mContextList.empty()) return; // this is a dynamically generated cell -> skipping. // Load references from all plugins that do something with this cell. for (size_t i = 0; i < mCell->mContextList.size(); i++) { try { // Reopen the ESM reader and seek to the right position. int index = mCell->mContextList.at(i).index; mCell->restore (esm[index], i); ESM::CellRef ref; // Get each reference in turn bool deleted = false; while (mCell->getNextRef (esm[index], ref, deleted)) { if (deleted) continue; // Don't list reference if it was moved to a different cell. ESM::MovedCellRefTracker::const_iterator iter = std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefNum); if (iter != mCell->mMovedRefs.end()) { continue; } mIds.push_back (Misc::StringUtils::lowerCase (ref.mRefID)); } } catch (std::exception& e) { std::cerr << "An error occurred listing references for cell " << getCell()->getDescription() << ": " << e.what() << std::endl; } } // List moved references, from separately tracked list. for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); ++it) { const ESM::CellRef &ref = it->first; bool deleted = it->second; if (!deleted) mIds.push_back(Misc::StringUtils::lowerCase(ref.mRefID)); } std::sort (mIds.begin(), mIds.end()); } void CellStore::loadRefs() { std::vector& esm = mReader; assert (mCell); if (mCell->mContextList.empty()) return; // this is a dynamically generated cell -> skipping. std::map refNumToID; // used to detect refID modifications // Load references from all plugins that do something with this cell. for (size_t i = 0; i < mCell->mContextList.size(); i++) { try { // Reopen the ESM reader and seek to the right position. int index = mCell->mContextList.at(i).index; mCell->restore (esm[index], i); ESM::CellRef ref; ref.mRefNum.mContentFile = ESM::RefNum::RefNum_NoContentFile; // Get each reference in turn bool deleted = false; while(mCell->getNextRef(esm[index], ref, deleted)) { // Don't load reference if it was moved to a different cell. ESM::MovedCellRefTracker::const_iterator iter = std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefNum); if (iter != mCell->mMovedRefs.end()) { continue; } loadRef (ref, deleted, refNumToID); } } catch (std::exception& e) { std::cerr << "An error occurred loading references for cell " << getCell()->getDescription() << ": " << e.what() << std::endl; } } // Load moved references, from separately tracked list. for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); ++it) { ESM::CellRef &ref = const_cast(it->first); bool deleted = it->second; loadRef (ref, deleted, refNumToID); } updateMergedRefs(); } bool CellStore::isExterior() const { return mCell->isExterior(); } Ptr CellStore::searchInContainer (const std::string& id) { bool oldState = mHasState; mHasState = true; if (Ptr ptr = searchInContainerList (mContainers, id)) return ptr; if (Ptr ptr = searchInContainerList (mCreatures, id)) return ptr; if (Ptr ptr = searchInContainerList (mNpcs, id)) return ptr; mHasState = oldState; return Ptr(); } void CellStore::loadRef (ESM::CellRef& ref, bool deleted, std::map& refNumToID) { Misc::StringUtils::lowerCaseInPlace (ref.mRefID); const MWWorld::ESMStore& store = mStore; std::map::iterator it = refNumToID.find(ref.mRefNum); if (it != refNumToID.end()) { if (it->second != ref.mRefID) { // refID was modified, make sure we don't end up with duplicated refs switch (store.find(it->second)) { case ESM::REC_ACTI: mActivators.remove(ref.mRefNum); break; case ESM::REC_ALCH: mPotions.remove(ref.mRefNum); break; case ESM::REC_APPA: mAppas.remove(ref.mRefNum); break; case ESM::REC_ARMO: mArmors.remove(ref.mRefNum); break; case ESM::REC_BOOK: mBooks.remove(ref.mRefNum); break; case ESM::REC_CLOT: mClothes.remove(ref.mRefNum); break; case ESM::REC_CONT: mContainers.remove(ref.mRefNum); break; case ESM::REC_CREA: mCreatures.remove(ref.mRefNum); break; case ESM::REC_DOOR: mDoors.remove(ref.mRefNum); break; case ESM::REC_INGR: mIngreds.remove(ref.mRefNum); break; case ESM::REC_LEVC: mCreatureLists.remove(ref.mRefNum); break; case ESM::REC_LEVI: mItemLists.remove(ref.mRefNum); break; case ESM::REC_LIGH: mLights.remove(ref.mRefNum); break; case ESM::REC_LOCK: mLockpicks.remove(ref.mRefNum); break; case ESM::REC_MISC: mMiscItems.remove(ref.mRefNum); break; case ESM::REC_NPC_: mNpcs.remove(ref.mRefNum); break; case ESM::REC_PROB: mProbes.remove(ref.mRefNum); break; case ESM::REC_REPA: mRepairs.remove(ref.mRefNum); break; case ESM::REC_STAT: mStatics.remove(ref.mRefNum); break; case ESM::REC_WEAP: mWeapons.remove(ref.mRefNum); break; case ESM::REC_BODY: mBodyParts.remove(ref.mRefNum); break; default: break; } } } switch (store.find (ref.mRefID)) { case ESM::REC_ACTI: mActivators.load(ref, deleted, store); break; case ESM::REC_ALCH: mPotions.load(ref, deleted,store); break; case ESM::REC_APPA: mAppas.load(ref, deleted, store); break; case ESM::REC_ARMO: mArmors.load(ref, deleted, store); break; case ESM::REC_BOOK: mBooks.load(ref, deleted, store); break; case ESM::REC_CLOT: mClothes.load(ref, deleted, store); break; case ESM::REC_CONT: mContainers.load(ref, deleted, store); break; case ESM::REC_CREA: mCreatures.load(ref, deleted, store); break; case ESM::REC_DOOR: mDoors.load(ref, deleted, store); break; case ESM::REC_INGR: mIngreds.load(ref, deleted, store); break; case ESM::REC_LEVC: mCreatureLists.load(ref, deleted, store); break; case ESM::REC_LEVI: mItemLists.load(ref, deleted, store); break; case ESM::REC_LIGH: mLights.load(ref, deleted, store); break; case ESM::REC_LOCK: mLockpicks.load(ref, deleted, store); break; case ESM::REC_MISC: mMiscItems.load(ref, deleted, store); break; case ESM::REC_NPC_: mNpcs.load(ref, deleted, store); break; case ESM::REC_PROB: mProbes.load(ref, deleted, store); break; case ESM::REC_REPA: mRepairs.load(ref, deleted, store); break; case ESM::REC_STAT: mStatics.load(ref, deleted, store); break; case ESM::REC_WEAP: mWeapons.load(ref, deleted, store); break; case ESM::REC_BODY: mBodyParts.load(ref, deleted, store); break; case 0: std::cerr << "Cell reference '" + ref.mRefID + "' not found!\n"; return; default: std::cerr << "Error: Ignoring reference '" << ref.mRefID << "' of unhandled type\n"; return; } refNumToID[ref.mRefNum] = ref.mRefID; } void CellStore::loadState (const ESM::CellState& state) { mHasState = true; if (mCell->mData.mFlags & ESM::Cell::Interior && mCell->mData.mFlags & ESM::Cell::HasWater) mWaterLevel = state.mWaterLevel; mWaterLevel = state.mWaterLevel; mLastRespawn = MWWorld::TimeStamp(state.mLastRespawn); } void CellStore::saveState (ESM::CellState& state) const { state.mId = mCell->getCellId(); if (mCell->mData.mFlags & ESM::Cell::Interior && mCell->mData.mFlags & ESM::Cell::HasWater) state.mWaterLevel = mWaterLevel; state.mWaterLevel = mWaterLevel; state.mHasFogOfWar = (mFogState.get() ? 1 : 0); state.mLastRespawn = mLastRespawn.toEsm(); } void CellStore::writeFog(ESM::ESMWriter &writer) const { if (mFogState.get()) { mFogState->save(writer, mCell->mData.mFlags & ESM::Cell::Interior); } } void CellStore::readFog(ESM::ESMReader &reader) { mFogState.reset(new ESM::FogState()); mFogState->load(reader); } void CellStore::writeReferences (ESM::ESMWriter& writer) const { writeReferenceCollection (writer, mActivators); writeReferenceCollection (writer, mPotions); writeReferenceCollection (writer, mAppas); writeReferenceCollection (writer, mArmors); writeReferenceCollection (writer, mBooks); writeReferenceCollection (writer, mClothes); writeReferenceCollection (writer, mContainers); writeReferenceCollection (writer, mCreatures); writeReferenceCollection (writer, mDoors); writeReferenceCollection (writer, mIngreds); writeReferenceCollection (writer, mCreatureLists); writeReferenceCollection (writer, mItemLists); writeReferenceCollection (writer, mLights); writeReferenceCollection (writer, mLockpicks); writeReferenceCollection (writer, mMiscItems); writeReferenceCollection (writer, mNpcs); writeReferenceCollection (writer, mProbes); writeReferenceCollection (writer, mRepairs); writeReferenceCollection (writer, mStatics); writeReferenceCollection (writer, mWeapons); writeReferenceCollection (writer, mBodyParts); for (MovedRefTracker::const_iterator it = mMovedToAnotherCell.begin(); it != mMovedToAnotherCell.end(); ++it) { LiveCellRefBase* base = it->first; ESM::RefNum refNum = base->mRef.getRefNum(); ESM::CellId movedTo = it->second->getCell()->getCellId(); refNum.save(writer, true, "MVRF"); movedTo.save(writer); } } void CellStore::readReferences (ESM::ESMReader& reader, const std::map& contentFileMap, GetCellStoreCallback* callback) { mHasState = true; while (reader.isNextSub ("OBJE")) { unsigned int unused; reader.getHT (unused); // load the RefID first so we know what type of object it is ESM::CellRef cref; cref.loadId(reader, true); int type = MWBase::Environment::get().getWorld()->getStore().find(cref.mRefID); if (type == 0) { std::cerr << "Dropping reference to '" << cref.mRefID << "' (object no longer exists)" << std::endl; reader.skipHSubUntil("OBJE"); continue; } switch (type) { case ESM::REC_ACTI: readReferenceCollection (reader, mActivators, cref, contentFileMap); break; case ESM::REC_ALCH: readReferenceCollection (reader, mPotions, cref, contentFileMap); break; case ESM::REC_APPA: readReferenceCollection (reader, mAppas, cref, contentFileMap); break; case ESM::REC_ARMO: readReferenceCollection (reader, mArmors, cref, contentFileMap); break; case ESM::REC_BOOK: readReferenceCollection (reader, mBooks, cref, contentFileMap); break; case ESM::REC_CLOT: readReferenceCollection (reader, mClothes, cref, contentFileMap); break; case ESM::REC_CONT: readReferenceCollection (reader, mContainers, cref, contentFileMap); break; case ESM::REC_CREA: readReferenceCollection (reader, mCreatures, cref, contentFileMap); break; case ESM::REC_DOOR: readReferenceCollection (reader, mDoors, cref, contentFileMap); break; case ESM::REC_INGR: readReferenceCollection (reader, mIngreds, cref, contentFileMap); break; case ESM::REC_LEVC: readReferenceCollection (reader, mCreatureLists, cref, contentFileMap); break; case ESM::REC_LEVI: readReferenceCollection (reader, mItemLists, cref, contentFileMap); break; case ESM::REC_LIGH: readReferenceCollection (reader, mLights, cref, contentFileMap); break; case ESM::REC_LOCK: readReferenceCollection (reader, mLockpicks, cref, contentFileMap); break; case ESM::REC_MISC: readReferenceCollection (reader, mMiscItems, cref, contentFileMap); break; case ESM::REC_NPC_: readReferenceCollection (reader, mNpcs, cref, contentFileMap); break; case ESM::REC_PROB: readReferenceCollection (reader, mProbes, cref, contentFileMap); break; case ESM::REC_REPA: readReferenceCollection (reader, mRepairs, cref, contentFileMap); break; case ESM::REC_STAT: readReferenceCollection (reader, mStatics, cref, contentFileMap); break; case ESM::REC_WEAP: readReferenceCollection (reader, mWeapons, cref, contentFileMap); break; case ESM::REC_BODY: readReferenceCollection (reader, mBodyParts, cref, contentFileMap); break; default: throw std::runtime_error ("unknown type in cell reference section"); } } // Do another update here to make sure objects referred to by MVRF tags can be found // This update is only needed for old saves that used the old copy&delete way of moving objects updateMergedRefs(); while (reader.isNextSub("MVRF")) { reader.cacheSubName(); ESM::RefNum refnum; ESM::CellId movedTo; refnum.load(reader, true, "MVRF"); movedTo.load(reader); // Search for the reference. It might no longer exist if its content file was removed. SearchByRefNumVisitor visitor(refnum); forEachInternal(visitor); if (!visitor.mFound) { std::cerr << "Warning: Dropping moved ref tag for " << refnum.mIndex << " (moved object no longer exists)" << std::endl; continue; } MWWorld::LiveCellRefBase* movedRef = visitor.mFound; CellStore* otherCell = callback->getCellStore(movedTo); if (otherCell == NULL) { std::cerr << "Warning: Dropping moved ref tag for " << movedRef->mRef.getRefId() << " (target cell " << movedTo.mWorldspace << " no longer exists). Reference moved back to its original location." << std::endl; // Note by dropping tag the object will automatically re-appear in its original cell, though potentially at inapproriate coordinates. // Restore original coordinates: movedRef->mData.setPosition(movedRef->mRef.getPosition()); continue; } if (otherCell == this) { // Should never happen unless someone's tampering with files. std::cerr << "Found invalid moved ref, ignoring" << std::endl; continue; } moveTo(MWWorld::Ptr(movedRef, this), otherCell); } } bool operator== (const CellStore& left, const CellStore& right) { return left.getCell()->getCellId()==right.getCell()->getCellId(); } bool operator!= (const CellStore& left, const CellStore& right) { return !(left==right); } void CellStore::setFog(ESM::FogState *fog) { mFogState.reset(fog); } ESM::FogState* CellStore::getFog() const { return mFogState.get(); } void clearCorpse(const MWWorld::Ptr& ptr) { const MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr); static const float fCorpseClearDelay = MWBase::Environment::get().getWorld()->getStore().get().find("fCorpseClearDelay")->getFloat(); if (creatureStats.isDead() && !ptr.getClass().isPersistent(ptr) && creatureStats.getTimeOfDeath() + fCorpseClearDelay <= MWBase::Environment::get().getWorld()->getTimeStamp()) MWBase::Environment::get().getWorld()->deleteObject(ptr); } void CellStore::respawn() { if (mState == State_Loaded) { static const int iMonthsToRespawn = MWBase::Environment::get().getWorld()->getStore().get().find("iMonthsToRespawn")->getInt(); if (MWBase::Environment::get().getWorld()->getTimeStamp() - mLastRespawn > 24*30*iMonthsToRespawn) { mLastRespawn = MWBase::Environment::get().getWorld()->getTimeStamp(); for (CellRefList::List::iterator it (mContainers.mList.begin()); it!=mContainers.mList.end(); ++it) { Ptr ptr = getCurrentPtr(&*it); ptr.getClass().respawn(ptr); } } for (CellRefList::List::iterator it (mCreatures.mList.begin()); it!=mCreatures.mList.end(); ++it) { Ptr ptr = getCurrentPtr(&*it); clearCorpse(ptr); ptr.getClass().respawn(ptr); } for (CellRefList::List::iterator it (mNpcs.mList.begin()); it!=mNpcs.mList.end(); ++it) { Ptr ptr = getCurrentPtr(&*it); clearCorpse(ptr); ptr.getClass().respawn(ptr); } for (CellRefList::List::iterator it (mCreatureLists.mList.begin()); it!=mCreatureLists.mList.end(); ++it) { Ptr ptr = getCurrentPtr(&*it); // no need to clearCorpse, handled as part of mCreatures ptr.getClass().respawn(ptr); } } } }