2012-06-29 18:54:23 +02:00
|
|
|
#include "cellstore.hpp"
|
2021-11-23 15:26:43 +01:00
|
|
|
#include "magiceffects.hpp"
|
2012-06-29 18:54:23 +02:00
|
|
|
|
2014-02-23 16:46:07 +01:00
|
|
|
#include <algorithm>
|
2012-06-29 18:54:23 +02:00
|
|
|
|
2018-08-14 23:05:43 +04:00
|
|
|
#include <components/debug/debuglog.hpp>
|
|
|
|
|
2022-01-22 15:58:41 +01:00
|
|
|
#include <components/esm3/cellid.hpp>
|
|
|
|
#include <components/esm3/cellref.hpp>
|
|
|
|
#include <components/esm3/cellstate.hpp>
|
|
|
|
#include <components/esm3/containerstate.hpp>
|
|
|
|
#include <components/esm3/creaturelevliststate.hpp>
|
|
|
|
#include <components/esm3/creaturestate.hpp>
|
|
|
|
#include <components/esm3/doorstate.hpp>
|
2022-06-01 22:53:18 +02:00
|
|
|
#include <components/esm3/esmreader.hpp>
|
2022-01-22 15:58:41 +01:00
|
|
|
#include <components/esm3/esmwriter.hpp>
|
2022-06-01 22:53:18 +02:00
|
|
|
#include <components/esm3/fogstate.hpp>
|
2022-09-10 19:48:48 +02:00
|
|
|
#include <components/esm3/loadacti.hpp>
|
|
|
|
#include <components/esm3/loadalch.hpp>
|
|
|
|
#include <components/esm3/loadappa.hpp>
|
|
|
|
#include <components/esm3/loadarmo.hpp>
|
|
|
|
#include <components/esm3/loadbody.hpp>
|
|
|
|
#include <components/esm3/loadbook.hpp>
|
|
|
|
#include <components/esm3/loadclot.hpp>
|
|
|
|
#include <components/esm3/loadcont.hpp>
|
|
|
|
#include <components/esm3/loadcrea.hpp>
|
|
|
|
#include <components/esm3/loaddoor.hpp>
|
|
|
|
#include <components/esm3/loadench.hpp>
|
|
|
|
#include <components/esm3/loadingr.hpp>
|
|
|
|
#include <components/esm3/loadlevlist.hpp>
|
|
|
|
#include <components/esm3/loadligh.hpp>
|
|
|
|
#include <components/esm3/loadlock.hpp>
|
|
|
|
#include <components/esm3/loadmisc.hpp>
|
|
|
|
#include <components/esm3/loadnpc.hpp>
|
|
|
|
#include <components/esm3/loadprob.hpp>
|
|
|
|
#include <components/esm3/loadrepa.hpp>
|
|
|
|
#include <components/esm3/loadstat.hpp>
|
|
|
|
#include <components/esm3/loadweap.hpp>
|
|
|
|
#include <components/esm3/npcstate.hpp>
|
|
|
|
#include <components/esm3/objectstate.hpp>
|
|
|
|
#include <components/esm3/readerscache.hpp>
|
|
|
|
#include <components/misc/tuplehelpers.hpp>
|
|
|
|
|
2012-07-24 12:30:59 +04:00
|
|
|
#include "../mwbase/environment.hpp"
|
2021-04-19 13:33:25 +02:00
|
|
|
#include "../mwbase/luamanager.hpp"
|
2018-09-23 22:03:43 +04:00
|
|
|
#include "../mwbase/mechanicsmanager.hpp"
|
2012-07-24 12:30:59 +04:00
|
|
|
#include "../mwbase/world.hpp"
|
|
|
|
|
2014-04-29 15:27:49 +02:00
|
|
|
#include "../mwmechanics/creaturestats.hpp"
|
2018-12-15 10:23:50 +04:00
|
|
|
#include "../mwmechanics/recharge.hpp"
|
2014-04-29 15:27:49 +02:00
|
|
|
|
2013-08-15 14:45:13 +02:00
|
|
|
#include "class.hpp"
|
|
|
|
#include "containerstore.hpp"
|
2012-10-01 19:17:04 +04:00
|
|
|
#include "esmstore.hpp"
|
2012-07-24 11:15:20 +04:00
|
|
|
#include "ptr.hpp"
|
2013-08-15 14:45:13 +02:00
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
2022-09-09 18:28:48 +02:00
|
|
|
|
2022-09-10 22:20:47 +02:00
|
|
|
template <typename Record>
|
|
|
|
struct RecordToState
|
|
|
|
{
|
|
|
|
using StateType = ESM::ObjectState;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <>
|
|
|
|
struct RecordToState<ESM::NPC>
|
|
|
|
{
|
|
|
|
using StateType = ESM::NpcState;
|
|
|
|
};
|
|
|
|
template <>
|
|
|
|
struct RecordToState<ESM::Creature>
|
|
|
|
{
|
|
|
|
using StateType = ESM::CreatureState;
|
|
|
|
};
|
|
|
|
template <>
|
|
|
|
struct RecordToState<ESM::Door>
|
|
|
|
{
|
|
|
|
using StateType = ESM::DoorState;
|
|
|
|
};
|
|
|
|
template <>
|
|
|
|
struct RecordToState<ESM::Container>
|
|
|
|
{
|
|
|
|
using StateType = ESM::ContainerState;
|
|
|
|
};
|
|
|
|
template <>
|
|
|
|
struct RecordToState<ESM::CreatureLevList>
|
|
|
|
{
|
|
|
|
using StateType = ESM::CreatureLevListState;
|
|
|
|
};
|
|
|
|
|
2013-08-15 14:45:13 +02:00
|
|
|
template <typename T>
|
2022-08-27 13:07:59 +02:00
|
|
|
MWWorld::Ptr searchInContainerList(MWWorld::CellRefList<T>& containerList, std::string_view id)
|
2013-08-15 14:45:13 +02:00
|
|
|
{
|
|
|
|
for (typename MWWorld::CellRefList<T>::List::iterator iter(containerList.mList.begin());
|
|
|
|
iter != containerList.mList.end(); ++iter)
|
|
|
|
{
|
2020-11-13 11:39:47 +04:00
|
|
|
MWWorld::Ptr container(&*iter, nullptr);
|
2013-08-15 14:45:13 +02:00
|
|
|
|
2019-05-06 23:05:31 +04:00
|
|
|
if (container.getRefData().getCustomData() == nullptr)
|
|
|
|
continue;
|
|
|
|
|
2014-05-22 20:37:22 +02:00
|
|
|
MWWorld::Ptr ptr = container.getClass().getContainerStore(container).search(id);
|
2013-08-15 14:45:13 +02:00
|
|
|
|
|
|
|
if (!ptr.isEmpty())
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return MWWorld::Ptr();
|
|
|
|
}
|
2014-01-23 12:51:42 +01:00
|
|
|
|
2014-04-29 15:27:49 +02:00
|
|
|
template <typename T>
|
|
|
|
MWWorld::Ptr searchViaActorId(MWWorld::CellRefList<T>& actorList, int actorId, MWWorld::CellStore* cell,
|
2015-12-04 19:46:02 +01:00
|
|
|
const std::map<MWWorld::LiveCellRefBase*, MWWorld::CellStore*>& toIgnore)
|
2014-04-29 15:27:49 +02:00
|
|
|
{
|
|
|
|
for (typename MWWorld::CellRefList<T>::List::iterator iter(actorList.mList.begin());
|
|
|
|
iter != actorList.mList.end(); ++iter)
|
|
|
|
{
|
|
|
|
MWWorld::Ptr actor(&*iter, cell);
|
|
|
|
|
2015-12-04 19:46:02 +01:00
|
|
|
if (toIgnore.find(&*iter) != toIgnore.end())
|
|
|
|
continue;
|
|
|
|
|
2014-05-15 06:08:55 +02:00
|
|
|
if (actor.getClass().getCreatureStats(actor).matchesActorId(actorId) && actor.getRefData().getCount() > 0)
|
2014-04-29 15:27:49 +02:00
|
|
|
return actor;
|
|
|
|
}
|
|
|
|
|
|
|
|
return MWWorld::Ptr();
|
|
|
|
}
|
|
|
|
|
2022-09-10 22:20:47 +02:00
|
|
|
template <typename T>
|
2014-01-23 12:51:42 +01:00
|
|
|
void writeReferenceCollection(ESM::ESMWriter& writer, const MWWorld::CellRefList<T>& collection)
|
|
|
|
{
|
|
|
|
if (!collection.mList.empty())
|
|
|
|
{
|
|
|
|
// references
|
|
|
|
for (typename MWWorld::CellRefList<T>::List::const_iterator iter(collection.mList.begin());
|
|
|
|
iter != collection.mList.end(); ++iter)
|
|
|
|
{
|
2015-01-11 12:20:22 +13:00
|
|
|
if (!iter->mData.hasChanged() && !iter->mRef.hasChanged() && iter->mRef.hasContentFile())
|
2014-05-25 14:13:07 +02:00
|
|
|
{
|
|
|
|
// Reference that came from a content file and has not been changed -> ignore
|
|
|
|
continue;
|
|
|
|
}
|
2015-01-11 12:20:22 +13:00
|
|
|
if (iter->mData.getCount() == 0 && !iter->mRef.hasContentFile())
|
2014-05-25 14:13:07 +02:00
|
|
|
{
|
|
|
|
// Deleted reference that did not come from a content file -> ignore
|
|
|
|
continue;
|
|
|
|
}
|
2022-09-10 22:58:15 +02:00
|
|
|
using StateType = typename RecordToState<T>::StateType;
|
2022-09-10 22:20:47 +02:00
|
|
|
StateType state;
|
2014-01-23 12:51:42 +01:00
|
|
|
iter->save(state);
|
|
|
|
|
2015-01-19 23:29:06 +01:00
|
|
|
// recordId currently unused
|
2014-01-27 13:27:42 +01:00
|
|
|
writer.writeHNT("OBJE", collection.mList.front().mBase->sRecordId);
|
2015-01-19 23:29:06 +01:00
|
|
|
|
2014-01-23 12:51:42 +01:00
|
|
|
state.save(writer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-01-27 13:27:42 +01:00
|
|
|
|
2021-03-04 22:52:03 +01:00
|
|
|
template <class RecordType, class T>
|
|
|
|
void fixRestockingImpl(const T* base, RecordType& state)
|
|
|
|
{
|
|
|
|
// Workaround for old saves not containing negative quantities
|
|
|
|
for (const auto& baseItem : base->mInventory.mList)
|
|
|
|
{
|
|
|
|
if (baseItem.mCount < 0)
|
|
|
|
{
|
|
|
|
for (auto& item : state.mInventory.mItems)
|
|
|
|
{
|
|
|
|
if (item.mCount > 0 && Misc::StringUtils::ciEqual(baseItem.mItem, item.mRef.mRefID))
|
|
|
|
item.mCount = -item.mCount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class RecordType, class T>
|
|
|
|
void fixRestocking(const T* base, RecordType& state)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
template <>
|
|
|
|
void fixRestocking<>(const ESM::Creature* base, ESM::CreatureState& state)
|
|
|
|
{
|
|
|
|
fixRestockingImpl(base, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <>
|
|
|
|
void fixRestocking<>(const ESM::NPC* base, ESM::NpcState& state)
|
|
|
|
{
|
|
|
|
fixRestockingImpl(base, state);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <>
|
|
|
|
void fixRestocking<>(const ESM::Container* base, ESM::ContainerState& state)
|
|
|
|
{
|
|
|
|
fixRestockingImpl(base, state);
|
|
|
|
}
|
|
|
|
|
2022-09-10 22:20:47 +02:00
|
|
|
template <typename T>
|
2019-08-03 13:37:00 +00:00
|
|
|
void readReferenceCollection(ESM::ESMReader& reader, MWWorld::CellRefList<T>& collection, const ESM::CellRef& cref,
|
|
|
|
const std::map<int, int>& contentFileMap, MWWorld::CellStore* cellstore)
|
2014-01-27 13:27:42 +01:00
|
|
|
{
|
|
|
|
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
|
|
|
|
|
2022-09-10 22:58:15 +02:00
|
|
|
using StateType = typename RecordToState<T>::StateType;
|
2022-09-10 22:20:47 +02:00
|
|
|
StateType state;
|
2015-01-19 23:29:06 +01:00
|
|
|
state.mRef = cref;
|
|
|
|
state.load(reader);
|
2014-01-27 13:27:42 +01:00
|
|
|
|
2014-04-26 03:15:50 +02:00
|
|
|
// If the reference came from a content file, make sure this content file is loaded
|
2015-01-11 12:20:22 +13:00
|
|
|
if (state.mRef.mRefNum.hasContentFile())
|
2014-04-26 03:15:50 +02:00
|
|
|
{
|
|
|
|
std::map<int, int>::const_iterator iter = contentFileMap.find(state.mRef.mRefNum.mContentFile);
|
2014-01-27 13:27:42 +01:00
|
|
|
|
2014-04-26 03:15:50 +02:00
|
|
|
if (iter == contentFileMap.end())
|
|
|
|
return; // content file has been removed -> skip
|
2014-01-27 13:27:42 +01:00
|
|
|
|
2014-04-26 03:15:50 +02:00
|
|
|
state.mRef.mRefNum.mContentFile = iter->second;
|
|
|
|
}
|
2014-01-27 13:27:42 +01:00
|
|
|
|
|
|
|
if (!MWWorld::LiveCellRef<T>::checkState(state))
|
|
|
|
return; // not valid anymore with current content files -> skip
|
|
|
|
|
|
|
|
const T* record = esmStore.get<T>().search(state.mRef.mRefID);
|
|
|
|
|
|
|
|
if (!record)
|
|
|
|
return;
|
|
|
|
|
2021-03-04 22:52:03 +01:00
|
|
|
if (state.mVersion < 15)
|
|
|
|
fixRestocking(record, state);
|
2021-08-27 20:07:50 +02:00
|
|
|
if (state.mVersion < 17)
|
|
|
|
{
|
2022-09-10 22:20:47 +02:00
|
|
|
if constexpr (std::is_same_v<T, ESM::Creature>)
|
2021-08-27 20:07:50 +02:00
|
|
|
MWWorld::convertMagicEffects(state.mCreatureStats, state.mInventory);
|
2022-09-10 22:20:47 +02:00
|
|
|
else if constexpr (std::is_same_v<T, ESM::NPC>)
|
2021-08-27 20:07:50 +02:00
|
|
|
MWWorld::convertMagicEffects(state.mCreatureStats, state.mInventory, &state.mNpcStats);
|
|
|
|
}
|
2022-02-14 18:38:37 +01:00
|
|
|
else if (state.mVersion < 20)
|
|
|
|
{
|
2022-09-10 22:58:15 +02:00
|
|
|
if constexpr (std::is_same_v<T, ESM::Creature> || std::is_same_v<T, ESM::NPC>)
|
2022-02-14 18:38:37 +01:00
|
|
|
MWWorld::convertStats(state.mCreatureStats);
|
|
|
|
}
|
2021-03-04 22:52:03 +01:00
|
|
|
|
2015-01-11 12:20:22 +13:00
|
|
|
if (state.mRef.mRefNum.hasContentFile())
|
2014-05-10 00:00:36 +02:00
|
|
|
{
|
|
|
|
for (typename MWWorld::CellRefList<T>::List::iterator iter(collection.mList.begin());
|
|
|
|
iter != collection.mList.end(); ++iter)
|
2022-01-23 20:55:17 +01:00
|
|
|
if (iter->mRef.getRefNum() == state.mRef.mRefNum && iter->mRef.getRefId() == state.mRef.mRefID)
|
2014-05-10 00:00:36 +02:00
|
|
|
{
|
|
|
|
// overwrite existing reference
|
2020-05-11 13:37:00 +00:00
|
|
|
float oldscale = iter->mRef.getScale();
|
2014-05-10 00:00:36 +02:00
|
|
|
iter->load(state);
|
2020-05-11 13:37:00 +00:00
|
|
|
const ESM::Position& oldpos = iter->mRef.getPosition();
|
|
|
|
const ESM::Position& newpos = iter->mData.getPosition();
|
|
|
|
const MWWorld::Ptr ptr(&*iter, cellstore);
|
|
|
|
if ((oldscale != iter->mRef.getScale() || oldpos.asVec3() != newpos.asVec3()
|
|
|
|
|| oldpos.rot[0] != newpos.rot[0] || oldpos.rot[1] != newpos.rot[1]
|
|
|
|
|| oldpos.rot[2] != newpos.rot[2])
|
|
|
|
&& !ptr.getClass().isActor())
|
2021-04-11 18:18:10 +02:00
|
|
|
MWBase::Environment::get().getWorld()->moveObject(ptr, newpos.asVec3());
|
2019-08-03 13:37:00 +00:00
|
|
|
if (!iter->mData.isEnabled())
|
|
|
|
{
|
|
|
|
iter->mData.enable();
|
|
|
|
MWBase::Environment::get().getWorld()->disable(MWWorld::Ptr(&*iter, cellstore));
|
|
|
|
}
|
2021-04-19 13:33:25 +02:00
|
|
|
else
|
|
|
|
MWBase::Environment::get().getLuaManager()->registerObject(MWWorld::Ptr(&*iter, cellstore));
|
2014-05-10 00:00:36 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-12-22 20:48:58 +01:00
|
|
|
|
2018-08-14 23:05:43 +04:00
|
|
|
Log(Debug::Warning) << "Warning: Dropping reference to " << state.mRef.mRefID
|
|
|
|
<< " (invalid content file link)";
|
2016-12-22 20:48:58 +01:00
|
|
|
return;
|
2014-05-10 00:00:36 +02:00
|
|
|
}
|
2014-01-27 13:27:42 +01:00
|
|
|
|
|
|
|
// new reference
|
|
|
|
MWWorld::LiveCellRef<T> ref(record);
|
|
|
|
ref.load(state);
|
|
|
|
collection.mList.push_back(ref);
|
2021-04-19 13:33:25 +02:00
|
|
|
|
|
|
|
MWWorld::LiveCellRefBase* base = &collection.mList.back();
|
|
|
|
MWBase::Environment::get().getLuaManager()->registerObject(MWWorld::Ptr(base, cellstore));
|
2014-01-27 13:27:42 +01:00
|
|
|
}
|
2022-09-10 10:03:13 +02:00
|
|
|
|
|
|
|
// this function allows us to link a CellRefList<T> to the associated recNameInt, and apply a function
|
|
|
|
template <typename RecordType, typename Callable>
|
2022-09-10 19:48:48 +02:00
|
|
|
static void recNameSwitcher(MWWorld::CellRefList<RecordType>& store, ESM::RecNameInts recnNameInt, Callable&& f)
|
2022-09-10 10:03:13 +02:00
|
|
|
{
|
|
|
|
if (RecordType::sRecordId == recnNameInt)
|
|
|
|
{
|
|
|
|
f(store);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper function for forEachInternal
|
|
|
|
template <class Visitor, class List>
|
|
|
|
bool forEachImp(Visitor& visitor, List& list, MWWorld::CellStore* cellStore)
|
|
|
|
{
|
|
|
|
for (typename List::List::iterator iter(list.mList.begin()); iter != list.mList.end(); ++iter)
|
|
|
|
{
|
|
|
|
if (!MWWorld::CellStore::isAccessible(iter->mData, iter->mRef))
|
|
|
|
continue;
|
|
|
|
if (!visitor(MWWorld::Ptr(&*iter, cellStore)))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2013-08-15 14:45:13 +02:00
|
|
|
}
|
2012-07-24 11:15:20 +04:00
|
|
|
|
2012-06-29 18:54:23 +02:00
|
|
|
namespace MWWorld
|
|
|
|
{
|
2022-09-08 20:52:00 +02:00
|
|
|
struct CellStoreImp
|
|
|
|
{
|
|
|
|
CellStoreTuple mRefLists;
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
static void assignStoreToIndex(CellStore& stores, CellRefList<T>& refList)
|
|
|
|
{
|
|
|
|
const std::size_t storeIndex = CellStore::getTypeIndex<T>();
|
|
|
|
if (stores.mCellRefLists.size() <= storeIndex)
|
|
|
|
stores.mCellRefLists.resize(storeIndex + 1);
|
|
|
|
|
2022-09-09 18:28:48 +02:00
|
|
|
assert(&refList == &std::get<CellRefList<T>>(stores.mCellStoreImp->mRefLists));
|
2022-09-08 20:52:00 +02:00
|
|
|
|
|
|
|
stores.mCellRefLists[storeIndex] = &refList;
|
|
|
|
}
|
2022-09-08 23:39:05 +02:00
|
|
|
|
2022-09-10 10:03:13 +02:00
|
|
|
// listing only objects owned by this cell. Internal use only, you probably want to use forEach() so that moved
|
|
|
|
// objects are accounted for.
|
|
|
|
template <class Visitor>
|
|
|
|
static bool forEachInternal(Visitor& visitor, MWWorld::CellStore& cellStore)
|
2022-09-08 23:39:05 +02:00
|
|
|
{
|
2022-09-10 10:03:13 +02:00
|
|
|
bool returnValue = true;
|
|
|
|
|
|
|
|
Misc::tupleForEach(cellStore.mCellStoreImp->mRefLists, [&visitor, &returnValue, &cellStore](auto& store) {
|
|
|
|
returnValue = returnValue && forEachImp(visitor, store, &cellStore);
|
|
|
|
});
|
|
|
|
|
|
|
|
return returnValue;
|
2022-09-08 23:39:05 +02:00
|
|
|
}
|
2022-09-08 20:52:00 +02:00
|
|
|
};
|
2021-01-12 12:39:19 +04:00
|
|
|
|
2013-02-09 13:00:57 +01:00
|
|
|
template <typename X>
|
2015-07-21 13:17:03 +03:00
|
|
|
void CellRefList<X>::load(ESM::CellRef& ref, bool deleted, const MWWorld::ESMStore& esmStore)
|
2013-02-09 13:00:57 +01:00
|
|
|
{
|
2014-01-06 13:53:20 +01:00
|
|
|
const MWWorld::Store<X>& store = esmStore.get<X>();
|
|
|
|
|
|
|
|
if (const X* ptr = store.search(ref.mRefID))
|
|
|
|
{
|
2014-01-12 16:58:06 +01:00
|
|
|
typename std::list<LiveRef>::iterator iter = std::find(mList.begin(), mList.end(), ref.mRefNum);
|
2014-01-06 13:53:20 +01:00
|
|
|
|
|
|
|
LiveRef liveCellRef(ref, ptr);
|
|
|
|
|
2015-07-21 13:17:03 +03:00
|
|
|
if (deleted)
|
2015-12-12 22:37:23 +01:00
|
|
|
liveCellRef.mData.setDeletedByContentFile(true);
|
2013-08-15 14:45:13 +02:00
|
|
|
|
2013-08-26 00:14:02 +02:00
|
|
|
if (iter != mList.end())
|
2014-01-06 13:53:20 +01:00
|
|
|
*iter = liveCellRef;
|
|
|
|
else
|
|
|
|
mList.push_back(liveCellRef);
|
2013-02-09 13:00:57 +01:00
|
|
|
}
|
2014-01-06 13:53:20 +01:00
|
|
|
else
|
|
|
|
{
|
2017-03-02 22:07:43 +01:00
|
|
|
Log(Debug::Warning) << "Warning: could not resolve cell reference '" << ref.mRefID << "'"
|
2018-08-14 23:05:43 +04:00
|
|
|
<< " (dropping reference)";
|
2013-02-09 13:00:57 +01:00
|
|
|
}
|
|
|
|
}
|
2013-08-15 14:45:13 +02:00
|
|
|
|
2013-02-09 13:00:57 +01:00
|
|
|
template <typename X>
|
|
|
|
bool operator==(const LiveCellRef<X>& ref, int pRefnum)
|
|
|
|
{
|
|
|
|
return (ref.mRef.mRefnum == pRefnum);
|
|
|
|
}
|
|
|
|
|
2016-10-18 23:51:45 +02:00
|
|
|
Ptr CellStore::getCurrentPtr(LiveCellRefBase* ref)
|
|
|
|
{
|
|
|
|
MovedRefTracker::iterator found = mMovedToAnotherCell.find(ref);
|
|
|
|
if (found != mMovedToAnotherCell.end())
|
|
|
|
return Ptr(ref, found->second);
|
|
|
|
return Ptr(ref, this);
|
|
|
|
}
|
|
|
|
|
2015-11-14 17:44:16 +01:00
|
|
|
void CellStore::moveFrom(const Ptr& object, CellStore* from)
|
|
|
|
{
|
2015-12-06 18:04:48 +01:00
|
|
|
if (mState != State_Loaded)
|
|
|
|
load();
|
|
|
|
|
2015-12-04 19:46:02 +01:00
|
|
|
mHasState = true;
|
2015-11-14 17:44:16 +01:00
|
|
|
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));
|
|
|
|
}
|
2015-12-06 18:04:48 +01:00
|
|
|
updateMergedRefs();
|
2015-11-14 17:44:16 +01:00
|
|
|
}
|
|
|
|
|
2015-12-04 19:46:02 +01:00
|
|
|
MWWorld::Ptr CellStore::moveTo(const Ptr& object, CellStore* cellToMoveTo)
|
2015-11-14 17:44:16 +01:00
|
|
|
{
|
2015-12-04 19:46:02 +01:00
|
|
|
if (cellToMoveTo == this)
|
2015-12-06 20:05:13 +01:00
|
|
|
throw std::runtime_error("moveTo: object is already in this cell");
|
2015-12-04 19:46:02 +01:00
|
|
|
|
|
|
|
// We assume that *this is in State_Loaded since we could hardly have reference to a live object otherwise.
|
|
|
|
if (mState != State_Loaded)
|
2015-12-06 20:05:13 +01:00
|
|
|
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
|
2020-05-11 13:37:00 +00:00
|
|
|
if (searchViaRefNum(object.getCellRef().getRefNum()).isEmpty())
|
2015-12-06 20:05:13 +01:00
|
|
|
throw std::runtime_error("moveTo: object is not in this cell");
|
2015-12-04 19:46:02 +01:00
|
|
|
|
2021-04-19 13:33:25 +02:00
|
|
|
MWBase::Environment::get().getLuaManager()->registerObject(MWWorld::Ptr(object.getBase(), cellToMoveTo));
|
2015-12-06 18:43:52 +01:00
|
|
|
|
2015-11-14 17:44:16 +01:00
|
|
|
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
|
2015-12-04 19:46:02 +01:00
|
|
|
if (cellToMoveTo != originalCell)
|
|
|
|
{
|
|
|
|
originalCell->moveTo(object, cellToMoveTo);
|
|
|
|
}
|
2015-11-14 17:44:16 +01:00
|
|
|
|
|
|
|
updateMergedRefs();
|
2015-12-04 19:46:02 +01:00
|
|
|
return MWWorld::Ptr(object.getBase(), cellToMoveTo);
|
2015-11-14 17:44:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
cellToMoveTo->moveFrom(object, this);
|
|
|
|
mMovedToAnotherCell.insert(std::make_pair(object.getBase(), cellToMoveTo));
|
|
|
|
|
|
|
|
updateMergedRefs();
|
2015-12-04 19:46:02 +01:00
|
|
|
return MWWorld::Ptr(object.getBase(), cellToMoveTo);
|
2015-11-14 17:44:16 +01:00
|
|
|
}
|
|
|
|
|
2015-12-06 18:13:04 +01:00
|
|
|
struct MergeVisitor
|
2015-11-14 17:44:16 +01:00
|
|
|
{
|
2015-12-06 18:13:04 +01:00
|
|
|
MergeVisitor(std::vector<LiveCellRefBase*>& mergeTo,
|
|
|
|
const std::map<LiveCellRefBase*, MWWorld::CellStore*>& movedHere,
|
2015-12-04 19:46:02 +01:00
|
|
|
const std::map<LiveCellRefBase*, MWWorld::CellStore*>& movedToAnotherCell)
|
|
|
|
: mMergeTo(mergeTo)
|
|
|
|
, mMovedHere(movedHere)
|
|
|
|
, mMovedToAnotherCell(movedToAnotherCell)
|
|
|
|
{
|
|
|
|
}
|
2015-11-14 17:44:16 +01:00
|
|
|
|
2015-12-04 19:46:02 +01:00
|
|
|
bool operator()(const MWWorld::Ptr& ptr)
|
|
|
|
{
|
|
|
|
if (mMovedToAnotherCell.find(ptr.getBase()) != mMovedToAnotherCell.end())
|
|
|
|
return true;
|
|
|
|
mMergeTo.push_back(ptr.getBase());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void merge()
|
|
|
|
{
|
2021-05-16 09:40:41 +02:00
|
|
|
for (const auto& [base, _] : mMovedHere)
|
|
|
|
mMergeTo.push_back(base);
|
2015-12-04 19:46:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::vector<LiveCellRefBase*>& mMergeTo;
|
|
|
|
|
|
|
|
const std::map<LiveCellRefBase*, MWWorld::CellStore*>& mMovedHere;
|
|
|
|
const std::map<LiveCellRefBase*, MWWorld::CellStore*>& mMovedToAnotherCell;
|
|
|
|
};
|
|
|
|
|
|
|
|
void CellStore::updateMergedRefs()
|
|
|
|
{
|
|
|
|
mMergedRefs.clear();
|
2018-12-15 10:23:50 +04:00
|
|
|
mRechargingItemsUpToDate = false;
|
2015-12-06 18:13:04 +01:00
|
|
|
MergeVisitor visitor(mMergedRefs, mMovedHere, mMovedToAnotherCell);
|
2022-09-10 10:03:13 +02:00
|
|
|
CellStoreImp::forEachInternal(visitor, *this);
|
2015-12-06 18:13:04 +01:00
|
|
|
visitor.merge();
|
2015-11-14 17:44:16 +01:00
|
|
|
}
|
|
|
|
|
2018-11-20 21:53:27 +04:00
|
|
|
bool CellStore::movedHere(const MWWorld::Ptr& ptr) const
|
|
|
|
{
|
|
|
|
if (ptr.isEmpty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (mMovedHere.find(ptr.getBase()) != mMovedHere.end())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-06-01 22:53:18 +02:00
|
|
|
CellStore::CellStore(const ESM::Cell* cell, const MWWorld::ESMStore& esmStore, ESM::ReadersCache& readers)
|
|
|
|
: mStore(esmStore)
|
|
|
|
, mReaders(readers)
|
|
|
|
, mCell(cell)
|
|
|
|
, mState(State_Unloaded)
|
|
|
|
, mHasState(false)
|
|
|
|
, mLastRespawn(0, 0)
|
2022-09-08 20:52:00 +02:00
|
|
|
, mCellStoreImp(std::make_unique<CellStoreImp>())
|
2022-09-09 18:28:48 +02:00
|
|
|
, mRechargingItemsUpToDate(false)
|
2012-06-29 18:54:23 +02:00
|
|
|
{
|
2022-09-08 20:52:00 +02:00
|
|
|
std::apply([this](auto&... x) { (CellStoreImp::assignStoreToIndex(*this, x), ...); }, mCellStoreImp->mRefLists);
|
2012-09-17 11:37:50 +04:00
|
|
|
mWaterLevel = cell->mWater;
|
2012-06-29 18:54:23 +02:00
|
|
|
}
|
|
|
|
|
2022-09-08 20:52:00 +02:00
|
|
|
CellStore::~CellStore() = default;
|
|
|
|
CellStore::CellStore(CellStore&&) = default;
|
|
|
|
|
2014-02-21 11:35:46 +01:00
|
|
|
const ESM::Cell* CellStore::getCell() const
|
|
|
|
{
|
|
|
|
return mCell;
|
|
|
|
}
|
|
|
|
|
2014-02-23 14:26:36 +01:00
|
|
|
CellStore::State CellStore::getState() const
|
|
|
|
{
|
|
|
|
return mState;
|
|
|
|
}
|
|
|
|
|
2016-02-07 00:07:02 +01:00
|
|
|
const std::vector<std::string>& CellStore::getPreloadedIds() const
|
|
|
|
{
|
|
|
|
return mIds;
|
|
|
|
}
|
|
|
|
|
2014-02-24 10:03:04 +01:00
|
|
|
bool CellStore::hasState() const
|
|
|
|
{
|
|
|
|
return mHasState;
|
|
|
|
}
|
|
|
|
|
2022-08-27 13:07:59 +02:00
|
|
|
bool CellStore::hasId(std::string_view id) const
|
2014-02-23 16:46:07 +01:00
|
|
|
{
|
|
|
|
if (mState == State_Unloaded)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (mState == State_Preloaded)
|
|
|
|
return std::binary_search(mIds.begin(), mIds.end(), id);
|
|
|
|
|
2015-12-17 22:37:18 +01:00
|
|
|
return searchConst(id).isEmpty();
|
2014-02-23 16:46:07 +01:00
|
|
|
}
|
|
|
|
|
2015-12-17 23:59:18 +01:00
|
|
|
template <typename PtrType>
|
2015-12-06 18:13:04 +01:00
|
|
|
struct SearchVisitor
|
2015-12-04 19:46:02 +01:00
|
|
|
{
|
2015-12-17 23:59:18 +01:00
|
|
|
PtrType mFound;
|
2022-08-27 13:07:59 +02:00
|
|
|
std::string_view mIdToFind;
|
2015-12-17 23:59:18 +01:00
|
|
|
bool operator()(const PtrType& ptr)
|
2015-12-04 19:46:02 +01:00
|
|
|
{
|
2022-08-27 13:07:59 +02:00
|
|
|
if (ptr.getCellRef().getRefId() == mIdToFind)
|
2015-12-04 19:46:02 +01:00
|
|
|
{
|
|
|
|
mFound = ptr;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-08-27 13:07:59 +02:00
|
|
|
Ptr CellStore::search(std::string_view id)
|
2014-02-23 16:46:07 +01:00
|
|
|
{
|
2015-12-17 23:59:18 +01:00
|
|
|
SearchVisitor<MWWorld::Ptr> searchVisitor;
|
2022-08-27 13:07:59 +02:00
|
|
|
searchVisitor.mIdToFind = id;
|
2015-12-06 18:13:04 +01:00
|
|
|
forEach(searchVisitor);
|
|
|
|
return searchVisitor.mFound;
|
2014-02-23 16:46:07 +01:00
|
|
|
}
|
|
|
|
|
2022-08-27 13:07:59 +02:00
|
|
|
ConstPtr CellStore::searchConst(std::string_view id) const
|
2015-12-17 22:37:18 +01:00
|
|
|
{
|
2015-12-17 23:59:18 +01:00
|
|
|
SearchVisitor<MWWorld::ConstPtr> searchVisitor;
|
2022-08-27 13:07:59 +02:00
|
|
|
searchVisitor.mIdToFind = id;
|
2015-12-17 23:59:18 +01:00
|
|
|
forEachConst(searchVisitor);
|
|
|
|
return searchVisitor.mFound;
|
2015-12-17 22:37:18 +01:00
|
|
|
}
|
|
|
|
|
2014-04-29 15:27:49 +02:00
|
|
|
Ptr CellStore::searchViaActorId(int id)
|
|
|
|
{
|
2022-09-08 20:52:00 +02:00
|
|
|
if (Ptr ptr = ::searchViaActorId(get<ESM::NPC>(), id, this, mMovedToAnotherCell))
|
2014-04-29 15:27:49 +02:00
|
|
|
return ptr;
|
|
|
|
|
2022-09-08 20:52:00 +02:00
|
|
|
if (Ptr ptr = ::searchViaActorId(get<ESM::Creature>(), id, this, mMovedToAnotherCell))
|
2014-04-29 15:27:49 +02:00
|
|
|
return ptr;
|
|
|
|
|
2021-05-16 09:40:41 +02:00
|
|
|
for (const auto& [base, _] : mMovedHere)
|
2015-12-04 19:46:02 +01:00
|
|
|
{
|
2021-05-16 09:40:41 +02:00
|
|
|
MWWorld::Ptr actor(base, this);
|
2015-12-04 19:46:02 +01:00
|
|
|
if (!actor.getClass().isActor())
|
|
|
|
continue;
|
|
|
|
if (actor.getClass().getCreatureStats(actor).matchesActorId(id) && actor.getRefData().getCount() > 0)
|
|
|
|
return actor;
|
|
|
|
}
|
|
|
|
|
2014-04-29 15:27:49 +02:00
|
|
|
return Ptr();
|
|
|
|
}
|
|
|
|
|
2020-05-10 14:57:06 +02:00
|
|
|
class RefNumSearchVisitor
|
|
|
|
{
|
|
|
|
const ESM::RefNum& mRefNum;
|
2022-09-22 21:26:05 +03:00
|
|
|
|
2020-05-10 14:57:06 +02:00
|
|
|
public:
|
|
|
|
RefNumSearchVisitor(const ESM::RefNum& refNum)
|
|
|
|
: mRefNum(refNum)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Ptr mFound;
|
|
|
|
|
|
|
|
bool operator()(const Ptr& ptr)
|
|
|
|
{
|
|
|
|
if (ptr.getCellRef().getRefNum() == mRefNum)
|
|
|
|
{
|
|
|
|
mFound = ptr;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Ptr CellStore::searchViaRefNum(const ESM::RefNum& refNum)
|
|
|
|
{
|
|
|
|
RefNumSearchVisitor searchVisitor(refNum);
|
|
|
|
forEach(searchVisitor);
|
|
|
|
return searchVisitor.mFound;
|
|
|
|
}
|
|
|
|
|
2014-02-23 17:34:18 +01:00
|
|
|
float CellStore::getWaterLevel() const
|
|
|
|
{
|
2015-12-03 15:16:50 +01:00
|
|
|
if (isExterior())
|
|
|
|
return -1;
|
2014-02-23 17:34:18 +01:00
|
|
|
return mWaterLevel;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CellStore::setWaterLevel(float level)
|
|
|
|
{
|
|
|
|
mWaterLevel = level;
|
2014-02-24 10:03:04 +01:00
|
|
|
mHasState = true;
|
2014-02-23 17:34:18 +01:00
|
|
|
}
|
|
|
|
|
2018-04-01 16:48:25 +03:00
|
|
|
std::size_t CellStore::count() const
|
2014-02-23 21:21:27 +01:00
|
|
|
{
|
2015-12-04 20:03:14 +01:00
|
|
|
return mMergedRefs.size();
|
2014-02-23 21:21:27 +01:00
|
|
|
}
|
|
|
|
|
2015-12-06 18:03:55 +01:00
|
|
|
void CellStore::load()
|
2012-06-29 18:54:23 +02:00
|
|
|
{
|
|
|
|
if (mState != State_Loaded)
|
|
|
|
{
|
|
|
|
if (mState == State_Preloaded)
|
|
|
|
mIds.clear();
|
|
|
|
|
2015-12-06 18:03:55 +01:00
|
|
|
loadRefs();
|
2012-06-29 18:54:23 +02:00
|
|
|
|
|
|
|
mState = State_Loaded;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-06 18:03:55 +01:00
|
|
|
void CellStore::preload()
|
2012-06-29 18:54:23 +02:00
|
|
|
{
|
|
|
|
if (mState == State_Unloaded)
|
|
|
|
{
|
2015-12-06 18:03:55 +01:00
|
|
|
listRefs();
|
2012-06-29 18:54:23 +02:00
|
|
|
|
|
|
|
mState = State_Preloaded;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-06 18:03:55 +01:00
|
|
|
void CellStore::listRefs()
|
2012-06-29 18:54:23 +02:00
|
|
|
{
|
2012-11-05 16:07:59 +04:00
|
|
|
assert(mCell);
|
2012-06-29 18:54:23 +02:00
|
|
|
|
2013-07-31 18:46:32 +02:00
|
|
|
if (mCell->mContextList.empty())
|
2012-06-29 18:54:23 +02:00
|
|
|
return; // this is a dynamically generated cell -> skipping.
|
|
|
|
|
2012-11-06 22:13:19 +01:00
|
|
|
// Load references from all plugins that do something with this cell.
|
2012-11-25 17:19:29 +01:00
|
|
|
for (size_t i = 0; i < mCell->mContextList.size(); i++)
|
2012-11-06 22:13:19 +01:00
|
|
|
{
|
2016-03-07 21:28:50 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
// Reopen the ESM reader and seek to the right position.
|
2022-06-01 22:53:18 +02:00
|
|
|
const std::size_t index = static_cast<std::size_t>(mCell->mContextList[i].index);
|
|
|
|
const ESM::ReadersCache::BusyItem reader = mReaders.get(index);
|
|
|
|
mCell->restore(*reader, i);
|
2012-06-29 18:54:23 +02:00
|
|
|
|
2016-03-07 21:28:50 +01:00
|
|
|
ESM::CellRef ref;
|
2012-06-29 18:54:23 +02:00
|
|
|
|
2016-03-07 21:28:50 +01:00
|
|
|
// Get each reference in turn
|
2021-07-07 08:18:38 +10:00
|
|
|
ESM::MovedCellRef cMRef;
|
|
|
|
cMRef.mRefNum.mIndex = 0;
|
2016-03-07 21:28:50 +01:00
|
|
|
bool deleted = false;
|
2021-07-12 17:30:39 +02:00
|
|
|
bool moved = false;
|
2022-06-01 22:53:18 +02:00
|
|
|
while (ESM::Cell::getNextRef(
|
|
|
|
*reader, ref, deleted, cMRef, moved, ESM::Cell::GetNextRefMode::LoadOnlyNotMoved))
|
2016-03-07 21:28:50 +01:00
|
|
|
{
|
2021-07-12 17:30:39 +02:00
|
|
|
if (deleted || moved)
|
2016-03-07 21:28:50 +01:00
|
|
|
continue;
|
2012-06-29 18:54:23 +02:00
|
|
|
|
2016-03-07 21:28:50 +01:00
|
|
|
// 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;
|
|
|
|
}
|
2014-06-14 19:13:40 +02:00
|
|
|
|
2021-05-15 02:29:50 +02:00
|
|
|
Misc::StringUtils::lowerCaseInPlace(ref.mRefID);
|
|
|
|
mIds.push_back(std::move(ref.mRefID));
|
2016-03-07 21:28:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (std::exception& e)
|
|
|
|
{
|
2018-08-14 23:05:43 +04:00
|
|
|
Log(Debug::Error) << "An error occurred listing references for cell " << getCell()->getDescription()
|
|
|
|
<< ": " << e.what();
|
2012-11-06 22:13:19 +01:00
|
|
|
}
|
2012-06-29 18:54:23 +02:00
|
|
|
}
|
|
|
|
|
2014-06-14 19:13:40 +02:00
|
|
|
// List moved references, from separately tracked list.
|
2021-05-16 09:40:41 +02:00
|
|
|
for (const auto& [ref, deleted] : mCell->mLeasedRefs)
|
2014-06-14 19:13:40 +02:00
|
|
|
{
|
2016-07-22 01:59:02 +02:00
|
|
|
if (!deleted)
|
|
|
|
mIds.push_back(Misc::StringUtils::lowerCase(ref.mRefID));
|
2014-06-14 19:13:40 +02:00
|
|
|
}
|
|
|
|
|
2012-06-29 18:54:23 +02:00
|
|
|
std::sort(mIds.begin(), mIds.end());
|
|
|
|
}
|
|
|
|
|
2015-12-06 18:03:55 +01:00
|
|
|
void CellStore::loadRefs()
|
2012-06-29 18:54:23 +02:00
|
|
|
{
|
2014-01-06 13:53:20 +01:00
|
|
|
assert(mCell);
|
2012-06-29 18:54:23 +02:00
|
|
|
|
2013-07-31 18:46:32 +02:00
|
|
|
if (mCell->mContextList.empty())
|
2012-06-29 18:54:23 +02:00
|
|
|
return; // this is a dynamically generated cell -> skipping.
|
|
|
|
|
2016-07-22 03:58:23 +02:00
|
|
|
std::map<ESM::RefNum, std::string> refNumToID; // used to detect refID modifications
|
|
|
|
|
2012-11-06 22:13:19 +01:00
|
|
|
// Load references from all plugins that do something with this cell.
|
2012-11-25 17:19:29 +01:00
|
|
|
for (size_t i = 0; i < mCell->mContextList.size(); i++)
|
2012-06-29 18:54:23 +02:00
|
|
|
{
|
2016-03-07 21:28:50 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
// Reopen the ESM reader and seek to the right position.
|
2022-06-01 22:53:18 +02:00
|
|
|
const std::size_t index = static_cast<std::size_t>(mCell->mContextList[i].index);
|
|
|
|
const ESM::ReadersCache::BusyItem reader = mReaders.get(index);
|
|
|
|
mCell->restore(*reader, i);
|
2012-06-29 18:54:23 +02:00
|
|
|
|
2016-03-07 21:28:50 +01:00
|
|
|
ESM::CellRef ref;
|
2021-01-22 15:48:37 +01:00
|
|
|
ref.mRefNum.unset();
|
2012-06-29 18:54:23 +02:00
|
|
|
|
2016-03-07 21:28:50 +01:00
|
|
|
// Get each reference in turn
|
2021-07-06 11:43:30 +10:00
|
|
|
ESM::MovedCellRef cMRef;
|
|
|
|
cMRef.mRefNum.mIndex = 0;
|
2016-03-07 21:28:50 +01:00
|
|
|
bool deleted = false;
|
2021-07-12 17:30:39 +02:00
|
|
|
bool moved = false;
|
2022-06-01 22:53:18 +02:00
|
|
|
while (ESM::Cell::getNextRef(
|
|
|
|
*reader, ref, deleted, cMRef, moved, ESM::Cell::GetNextRefMode::LoadOnlyNotMoved))
|
2016-03-07 21:28:50 +01:00
|
|
|
{
|
2021-07-12 17:30:39 +02:00
|
|
|
if (moved)
|
|
|
|
continue;
|
2021-07-06 11:43:30 +10:00
|
|
|
|
2016-03-07 21:28:50 +01:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2016-07-22 03:58:23 +02:00
|
|
|
loadRef(ref, deleted, refNumToID);
|
2013-08-15 14:45:13 +02:00
|
|
|
}
|
2016-03-07 21:28:50 +01:00
|
|
|
}
|
|
|
|
catch (std::exception& e)
|
|
|
|
{
|
2018-08-14 23:05:43 +04:00
|
|
|
Log(Debug::Error) << "An error occurred loading references for cell " << getCell()->getDescription()
|
|
|
|
<< ": " << e.what();
|
2012-06-29 18:54:23 +02:00
|
|
|
}
|
|
|
|
}
|
2013-01-19 23:33:18 +01:00
|
|
|
|
|
|
|
// Load moved references, from separately tracked list.
|
2021-05-16 09:40:41 +02:00
|
|
|
for (const auto& leasedRef : mCell->mLeasedRefs)
|
2013-01-19 23:33:18 +01:00
|
|
|
{
|
2021-05-16 09:40:41 +02:00
|
|
|
ESM::CellRef& ref = const_cast<ESM::CellRef&>(leasedRef.first);
|
|
|
|
bool deleted = leasedRef.second;
|
2013-08-15 14:45:13 +02:00
|
|
|
|
2016-07-22 03:58:23 +02:00
|
|
|
loadRef(ref, deleted, refNumToID);
|
2013-01-19 23:33:18 +01:00
|
|
|
}
|
2015-12-04 19:46:02 +01:00
|
|
|
|
|
|
|
updateMergedRefs();
|
2012-06-29 18:54:23 +02:00
|
|
|
}
|
2013-08-15 14:45:13 +02:00
|
|
|
|
2014-02-23 21:39:18 +01:00
|
|
|
bool CellStore::isExterior() const
|
|
|
|
{
|
|
|
|
return mCell->isExterior();
|
|
|
|
}
|
|
|
|
|
2022-08-19 14:51:52 +00:00
|
|
|
bool CellStore::isQuasiExterior() const
|
|
|
|
{
|
|
|
|
return (mCell->mData.mFlags & ESM::Cell::QuasiEx) != 0;
|
|
|
|
}
|
|
|
|
|
2022-08-27 13:07:59 +02:00
|
|
|
Ptr CellStore::searchInContainer(std::string_view id)
|
2013-08-15 14:45:13 +02:00
|
|
|
{
|
2014-02-24 10:03:04 +01:00
|
|
|
bool oldState = mHasState;
|
|
|
|
|
|
|
|
mHasState = true;
|
|
|
|
|
2022-09-08 20:52:00 +02:00
|
|
|
if (Ptr ptr = searchInContainerList(get<ESM::Container>(), id))
|
2014-02-23 18:17:41 +01:00
|
|
|
return ptr;
|
2013-08-15 14:45:13 +02:00
|
|
|
|
2022-09-08 20:52:00 +02:00
|
|
|
if (Ptr ptr = searchInContainerList(get<ESM::Creature>(), id))
|
2014-02-23 18:17:41 +01:00
|
|
|
return ptr;
|
2013-08-15 14:45:13 +02:00
|
|
|
|
2022-09-08 20:52:00 +02:00
|
|
|
if (Ptr ptr = searchInContainerList(get<ESM::NPC>(), id))
|
2014-02-23 18:17:41 +01:00
|
|
|
return ptr;
|
2013-08-15 14:45:13 +02:00
|
|
|
|
2014-02-24 10:03:04 +01:00
|
|
|
mHasState = oldState;
|
|
|
|
|
2013-08-15 14:45:13 +02:00
|
|
|
return Ptr();
|
|
|
|
}
|
2014-01-06 13:53:20 +01:00
|
|
|
|
2016-07-22 03:58:23 +02:00
|
|
|
void CellStore::loadRef(ESM::CellRef& ref, bool deleted, std::map<ESM::RefNum, std::string>& refNumToID)
|
2014-01-06 13:53:20 +01:00
|
|
|
{
|
2015-12-07 22:49:15 +01:00
|
|
|
Misc::StringUtils::lowerCaseInPlace(ref.mRefID);
|
2014-01-06 13:53:20 +01:00
|
|
|
|
2015-12-06 18:03:55 +01:00
|
|
|
const MWWorld::ESMStore& store = mStore;
|
|
|
|
|
2016-07-22 03:58:23 +02:00
|
|
|
std::map<ESM::RefNum, std::string>::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
|
2022-09-10 19:48:48 +02:00
|
|
|
ESM::RecNameInts foundType = static_cast<ESM::RecNameInts>(store.find(it->second));
|
|
|
|
if (foundType != 0)
|
|
|
|
{
|
|
|
|
Misc::tupleForEach(this->mCellStoreImp->mRefLists, [&ref, foundType](auto& x) {
|
|
|
|
recNameSwitcher(x, foundType, [&ref](auto& storeIn) { storeIn.remove(ref.mRefNum); });
|
|
|
|
});
|
|
|
|
}
|
2016-07-22 03:58:23 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-10 19:48:48 +02:00
|
|
|
ESM::RecNameInts foundType = static_cast<ESM::RecNameInts>(store.find(ref.mRefID));
|
2022-09-09 18:28:48 +02:00
|
|
|
bool handledType = false;
|
|
|
|
if (foundType != 0)
|
|
|
|
{
|
2022-09-10 19:48:48 +02:00
|
|
|
Misc::tupleForEach(
|
|
|
|
this->mCellStoreImp->mRefLists, [&ref, &deleted, &store, foundType, &handledType](auto& x) {
|
|
|
|
recNameSwitcher(x, foundType, [&ref, &deleted, &store, &handledType](auto& storeIn) {
|
2022-09-09 18:28:48 +02:00
|
|
|
handledType = true;
|
|
|
|
storeIn.load(ref, deleted, store);
|
2022-09-22 21:26:05 +03:00
|
|
|
});
|
2022-09-10 19:48:48 +02:00
|
|
|
});
|
2022-09-09 18:28:48 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Log(Debug::Error) << "Cell reference '" + ref.mRefID + "' not found!";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!handledType)
|
|
|
|
{
|
|
|
|
Log(Debug::Error) << "Error: Ignoring reference '" << ref.mRefID << "' of unhandled type";
|
|
|
|
return;
|
2014-01-06 13:53:20 +01:00
|
|
|
}
|
2016-07-22 03:58:23 +02:00
|
|
|
|
|
|
|
refNumToID[ref.mRefNum] = ref.mRefID;
|
2014-01-06 13:53:20 +01:00
|
|
|
}
|
2014-01-23 11:29:40 +01:00
|
|
|
|
|
|
|
void CellStore::loadState(const ESM::CellState& state)
|
|
|
|
{
|
2014-02-24 10:03:04 +01:00
|
|
|
mHasState = true;
|
|
|
|
|
2014-01-23 11:29:40 +01:00
|
|
|
if (mCell->mData.mFlags & ESM::Cell::Interior && mCell->mData.mFlags & ESM::Cell::HasWater)
|
|
|
|
mWaterLevel = state.mWaterLevel;
|
|
|
|
|
2014-05-17 09:05:41 +02:00
|
|
|
mLastRespawn = MWWorld::TimeStamp(state.mLastRespawn);
|
2014-01-23 11:29:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void CellStore::saveState(ESM::CellState& state) const
|
|
|
|
{
|
|
|
|
state.mId = mCell->getCellId();
|
|
|
|
|
2018-04-17 12:11:05 +03:00
|
|
|
if (mCell->mData.mFlags & ESM::Cell::Interior && mCell->mData.mFlags & ESM::Cell::HasWater)
|
|
|
|
state.mWaterLevel = mWaterLevel;
|
2014-01-23 11:29:40 +01:00
|
|
|
|
2014-05-11 02:07:28 +02:00
|
|
|
state.mHasFogOfWar = (mFogState.get() ? 1 : 0);
|
2014-05-17 09:05:41 +02:00
|
|
|
state.mLastRespawn = mLastRespawn.toEsm();
|
2014-05-11 02:07:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2022-05-29 13:24:48 +02:00
|
|
|
mFogState = std::make_unique<ESM::FogState>();
|
2014-05-11 02:07:28 +02:00
|
|
|
mFogState->load(reader);
|
2014-01-23 11:29:40 +01:00
|
|
|
}
|
2014-01-23 12:51:42 +01:00
|
|
|
|
|
|
|
void CellStore::writeReferences(ESM::ESMWriter& writer) const
|
|
|
|
{
|
2022-09-10 22:20:47 +02:00
|
|
|
Misc::tupleForEach(this->mCellStoreImp->mRefLists,
|
|
|
|
[&writer](auto& cellRefList) { writeReferenceCollection(writer, cellRefList); });
|
2015-12-04 19:46:02 +01:00
|
|
|
|
2021-05-16 09:40:41 +02:00
|
|
|
for (const auto& [base, store] : mMovedToAnotherCell)
|
2015-12-06 19:11:25 +01:00
|
|
|
{
|
|
|
|
ESM::RefNum refNum = base->mRef.getRefNum();
|
2021-05-16 09:40:41 +02:00
|
|
|
ESM::CellId movedTo = store->getCell()->getCellId();
|
2015-12-06 19:11:25 +01:00
|
|
|
|
|
|
|
refNum.save(writer, true, "MVRF");
|
|
|
|
movedTo.save(writer);
|
|
|
|
}
|
2014-01-23 12:51:42 +01:00
|
|
|
}
|
2014-01-27 13:27:42 +01:00
|
|
|
|
2015-12-06 19:53:06 +01:00
|
|
|
void CellStore::readReferences(
|
|
|
|
ESM::ESMReader& reader, const std::map<int, int>& contentFileMap, GetCellStoreCallback* callback)
|
2014-01-27 13:27:42 +01:00
|
|
|
{
|
2014-02-24 10:03:04 +01:00
|
|
|
mHasState = true;
|
|
|
|
|
2014-01-27 13:27:42 +01:00
|
|
|
while (reader.isNextSub("OBJE"))
|
|
|
|
{
|
2015-01-19 23:29:06 +01:00
|
|
|
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)
|
|
|
|
{
|
2018-08-14 23:05:43 +04:00
|
|
|
Log(Debug::Warning) << "Dropping reference to '" << cref.mRefID << "' (object no longer exists)";
|
2021-07-12 20:51:13 +02:00
|
|
|
// Skip until the next OBJE or MVRF
|
|
|
|
while (reader.hasMoreSubs() && !reader.peekNextSub("OBJE") && !reader.peekNextSub("MVRF"))
|
|
|
|
{
|
|
|
|
reader.getSubName();
|
|
|
|
reader.skipHSub();
|
|
|
|
}
|
2015-01-19 23:29:06 +01:00
|
|
|
continue;
|
|
|
|
}
|
2014-01-27 13:27:42 +01:00
|
|
|
|
2022-09-10 22:20:47 +02:00
|
|
|
if (type != 0)
|
2014-01-27 13:27:42 +01:00
|
|
|
{
|
2022-09-10 22:20:47 +02:00
|
|
|
bool foundCorrespondingStore = false;
|
|
|
|
Misc::tupleForEach(this->mCellStoreImp->mRefLists,
|
|
|
|
[&reader, this, &cref, &contentFileMap, &foundCorrespondingStore, type](auto&& x) {
|
|
|
|
recNameSwitcher(x, static_cast<ESM::RecNameInts>(type),
|
|
|
|
[&reader, this, &cref, &contentFileMap, &foundCorrespondingStore](auto& store) {
|
|
|
|
foundCorrespondingStore = true;
|
|
|
|
readReferenceCollection(reader, store, cref, contentFileMap, this);
|
2022-09-10 19:48:48 +02:00
|
|
|
});
|
2022-09-09 18:28:48 +02:00
|
|
|
});
|
2022-09-10 22:20:47 +02:00
|
|
|
|
|
|
|
if (!foundCorrespondingStore)
|
|
|
|
throw std::runtime_error("unknown type in cell reference section");
|
2014-01-27 13:27:42 +01:00
|
|
|
}
|
|
|
|
}
|
2015-12-06 19:11:25 +01:00
|
|
|
|
2016-02-03 19:12:46 +01:00
|
|
|
// 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();
|
|
|
|
|
2015-12-06 19:11:25 +01:00
|
|
|
while (reader.isNextSub("MVRF"))
|
|
|
|
{
|
|
|
|
reader.cacheSubName();
|
|
|
|
ESM::RefNum refnum;
|
|
|
|
ESM::CellId movedTo;
|
|
|
|
refnum.load(reader, true, "MVRF");
|
|
|
|
movedTo.load(reader);
|
|
|
|
|
2021-01-29 22:53:02 +01:00
|
|
|
if (refnum.hasContentFile())
|
|
|
|
{
|
|
|
|
auto iter = contentFileMap.find(refnum.mContentFile);
|
|
|
|
if (iter != contentFileMap.end())
|
|
|
|
refnum.mContentFile = iter->second;
|
|
|
|
}
|
|
|
|
|
2015-12-06 19:53:06 +01:00
|
|
|
// Search for the reference. It might no longer exist if its content file was removed.
|
2020-05-11 13:37:00 +00:00
|
|
|
Ptr movedRef = searchViaRefNum(refnum);
|
|
|
|
if (movedRef.isEmpty())
|
2015-12-06 19:53:06 +01:00
|
|
|
{
|
2018-08-14 23:05:43 +04:00
|
|
|
Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << refnum.mIndex
|
|
|
|
<< " (moved object no longer exists)";
|
2015-12-06 19:53:06 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
CellStore* otherCell = callback->getCellStore(movedTo);
|
|
|
|
|
2018-10-09 10:21:12 +04:00
|
|
|
if (otherCell == nullptr)
|
2015-12-06 19:53:06 +01:00
|
|
|
{
|
2020-05-11 13:37:00 +00:00
|
|
|
Log(Debug::Warning) << "Warning: Dropping moved ref tag for " << movedRef.getCellRef().getRefId()
|
2018-08-14 23:05:43 +04:00
|
|
|
<< " (target cell " << movedTo.mWorldspace
|
|
|
|
<< " no longer exists). Reference moved back to its original location.";
|
2015-12-06 19:53:06 +01:00
|
|
|
// Note by dropping tag the object will automatically re-appear in its original cell, though potentially
|
|
|
|
// at inapproriate coordinates. Restore original coordinates:
|
2020-05-11 13:37:00 +00:00
|
|
|
movedRef.getRefData().setPosition(movedRef.getCellRef().getPosition());
|
2015-12-06 19:53:06 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (otherCell == this)
|
|
|
|
{
|
2015-12-06 20:05:13 +01:00
|
|
|
// Should never happen unless someone's tampering with files.
|
2018-08-14 23:05:43 +04:00
|
|
|
Log(Debug::Warning) << "Found invalid moved ref, ignoring";
|
2015-12-06 19:53:06 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-05-11 13:37:00 +00:00
|
|
|
moveTo(movedRef, otherCell);
|
2015-12-06 19:11:25 +01:00
|
|
|
}
|
2014-01-27 13:27:42 +01:00
|
|
|
}
|
2014-02-23 21:39:18 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2014-04-03 21:41:34 +11:00
|
|
|
|
2022-04-08 22:04:32 +02:00
|
|
|
void CellStore::setFog(std::unique_ptr<ESM::FogState>&& fog)
|
2014-05-11 02:07:28 +02:00
|
|
|
{
|
2022-04-08 22:04:32 +02:00
|
|
|
mFogState = std::move(fog);
|
2014-05-11 02:07:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ESM::FogState* CellStore::getFog() const
|
|
|
|
{
|
|
|
|
return mFogState.get();
|
|
|
|
}
|
2014-05-17 09:05:41 +02:00
|
|
|
|
2016-02-27 13:13:24 +01:00
|
|
|
void clearCorpse(const MWWorld::Ptr& ptr)
|
|
|
|
{
|
|
|
|
const MWMechanics::CreatureStats& creatureStats = ptr.getClass().getCreatureStats(ptr);
|
2018-08-29 18:38:12 +03:00
|
|
|
static const float fCorpseClearDelay = MWBase::Environment::get()
|
|
|
|
.getWorld()
|
|
|
|
->getStore()
|
|
|
|
.get<ESM::GameSetting>()
|
|
|
|
.find("fCorpseClearDelay")
|
|
|
|
->mValue.getFloat();
|
2018-06-11 22:29:32 +04:00
|
|
|
if (creatureStats.isDead() && creatureStats.isDeathAnimationFinished() && !ptr.getClass().isPersistent(ptr)
|
|
|
|
&& creatureStats.getTimeOfDeath() + fCorpseClearDelay
|
|
|
|
<= MWBase::Environment::get().getWorld()->getTimeStamp())
|
|
|
|
{
|
2016-02-27 13:13:24 +01:00
|
|
|
MWBase::Environment::get().getWorld()->deleteObject(ptr);
|
2018-06-11 22:29:32 +04:00
|
|
|
}
|
2016-02-27 13:13:24 +01:00
|
|
|
}
|
|
|
|
|
2019-01-25 20:04:35 +04:00
|
|
|
void CellStore::rest(double hours)
|
2018-09-23 22:03:43 +04:00
|
|
|
{
|
|
|
|
if (mState == State_Loaded)
|
|
|
|
{
|
2022-09-08 20:52:00 +02:00
|
|
|
for (CellRefList<ESM::Creature>::List::iterator it(get<ESM::Creature>().mList.begin());
|
|
|
|
it != get<ESM::Creature>().mList.end(); ++it)
|
2018-09-23 22:03:43 +04:00
|
|
|
{
|
|
|
|
Ptr ptr = getCurrentPtr(&*it);
|
|
|
|
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
|
|
|
{
|
2019-01-25 20:04:35 +04:00
|
|
|
MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, hours, true);
|
2018-09-23 22:03:43 +04:00
|
|
|
}
|
|
|
|
}
|
2022-09-08 20:52:00 +02:00
|
|
|
for (CellRefList<ESM::NPC>::List::iterator it(get<ESM::NPC>().mList.begin());
|
|
|
|
it != get<ESM::NPC>().mList.end(); ++it)
|
2018-09-23 22:03:43 +04:00
|
|
|
{
|
|
|
|
Ptr ptr = getCurrentPtr(&*it);
|
|
|
|
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
|
|
|
{
|
2019-01-25 20:04:35 +04:00
|
|
|
MWBase::Environment::get().getMechanicsManager()->restoreDynamicStats(ptr, hours, true);
|
2018-09-23 22:03:43 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-15 10:23:50 +04:00
|
|
|
void CellStore::recharge(float duration)
|
|
|
|
{
|
|
|
|
if (duration <= 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (mState == State_Loaded)
|
|
|
|
{
|
2022-09-08 20:52:00 +02:00
|
|
|
for (CellRefList<ESM::Creature>::List::iterator it(get<ESM::Creature>().mList.begin());
|
|
|
|
it != get<ESM::Creature>().mList.end(); ++it)
|
2018-12-15 10:23:50 +04:00
|
|
|
{
|
|
|
|
Ptr ptr = getCurrentPtr(&*it);
|
|
|
|
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
|
|
|
{
|
|
|
|
ptr.getClass().getContainerStore(ptr).rechargeItems(duration);
|
|
|
|
}
|
|
|
|
}
|
2022-09-08 20:52:00 +02:00
|
|
|
for (CellRefList<ESM::NPC>::List::iterator it(get<ESM::NPC>().mList.begin());
|
|
|
|
it != get<ESM::NPC>().mList.end(); ++it)
|
2018-12-15 10:23:50 +04:00
|
|
|
{
|
|
|
|
Ptr ptr = getCurrentPtr(&*it);
|
|
|
|
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
|
|
|
{
|
|
|
|
ptr.getClass().getContainerStore(ptr).rechargeItems(duration);
|
|
|
|
}
|
|
|
|
}
|
2022-09-08 20:52:00 +02:00
|
|
|
for (CellRefList<ESM::Container>::List::iterator it(get<ESM::Container>().mList.begin());
|
|
|
|
it != get<ESM::Container>().mList.end(); ++it)
|
2018-12-15 10:23:50 +04:00
|
|
|
{
|
|
|
|
Ptr ptr = getCurrentPtr(&*it);
|
2020-10-13 17:46:32 +02:00
|
|
|
if (!ptr.isEmpty() && ptr.getRefData().getCustomData() != nullptr && ptr.getRefData().getCount() > 0
|
|
|
|
&& ptr.getClass().getContainerStore(ptr).isResolved())
|
2018-12-15 10:23:50 +04:00
|
|
|
{
|
|
|
|
ptr.getClass().getContainerStore(ptr).rechargeItems(duration);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rechargeItems(duration);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-17 09:05:41 +02:00
|
|
|
void CellStore::respawn()
|
|
|
|
{
|
|
|
|
if (mState == State_Loaded)
|
|
|
|
{
|
2018-08-29 18:38:12 +03:00
|
|
|
static const int iMonthsToRespawn = MWBase::Environment::get()
|
|
|
|
.getWorld()
|
|
|
|
->getStore()
|
|
|
|
.get<ESM::GameSetting>()
|
|
|
|
.find("iMonthsToRespawn")
|
|
|
|
->mValue.getInteger();
|
2014-05-17 09:05:41 +02:00
|
|
|
if (MWBase::Environment::get().getWorld()->getTimeStamp() - mLastRespawn > 24 * 30 * iMonthsToRespawn)
|
|
|
|
{
|
|
|
|
mLastRespawn = MWBase::Environment::get().getWorld()->getTimeStamp();
|
2022-09-08 20:52:00 +02:00
|
|
|
for (CellRefList<ESM::Container>::List::iterator it(get<ESM::Container>().mList.begin());
|
|
|
|
it != get<ESM::Container>().mList.end(); ++it)
|
2014-05-17 09:05:41 +02:00
|
|
|
{
|
2016-10-18 23:51:45 +02:00
|
|
|
Ptr ptr = getCurrentPtr(&*it);
|
2014-05-17 09:05:41 +02:00
|
|
|
ptr.getClass().respawn(ptr);
|
|
|
|
}
|
2016-02-27 12:53:07 +01:00
|
|
|
}
|
|
|
|
|
2022-09-08 20:52:00 +02:00
|
|
|
for (CellRefList<ESM::Creature>::List::iterator it(get<ESM::Creature>().mList.begin());
|
|
|
|
it != get<ESM::Creature>().mList.end(); ++it)
|
2016-02-27 12:53:07 +01:00
|
|
|
{
|
2016-10-18 23:51:45 +02:00
|
|
|
Ptr ptr = getCurrentPtr(&*it);
|
2016-02-27 13:13:24 +01:00
|
|
|
clearCorpse(ptr);
|
2016-02-27 12:53:07 +01:00
|
|
|
ptr.getClass().respawn(ptr);
|
|
|
|
}
|
2022-09-08 20:52:00 +02:00
|
|
|
for (CellRefList<ESM::NPC>::List::iterator it(get<ESM::NPC>().mList.begin());
|
|
|
|
it != get<ESM::NPC>().mList.end(); ++it)
|
2016-02-27 12:53:07 +01:00
|
|
|
{
|
2016-10-18 23:51:45 +02:00
|
|
|
Ptr ptr = getCurrentPtr(&*it);
|
2016-02-27 13:13:24 +01:00
|
|
|
clearCorpse(ptr);
|
2016-02-27 12:53:07 +01:00
|
|
|
ptr.getClass().respawn(ptr);
|
|
|
|
}
|
2022-09-08 20:52:00 +02:00
|
|
|
for (CellRefList<ESM::CreatureLevList>::List::iterator it(get<ESM::CreatureLevList>().mList.begin());
|
|
|
|
it != get<ESM::CreatureLevList>().mList.end(); ++it)
|
2016-02-27 12:53:07 +01:00
|
|
|
{
|
2016-10-18 23:51:45 +02:00
|
|
|
Ptr ptr = getCurrentPtr(&*it);
|
2022-09-08 20:52:00 +02:00
|
|
|
// no need to clearCorpse, handled as part of get<ESM::Creature>()
|
2016-02-27 12:53:07 +01:00
|
|
|
ptr.getClass().respawn(ptr);
|
2014-05-17 09:05:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-12-15 10:23:50 +04:00
|
|
|
|
|
|
|
void MWWorld::CellStore::rechargeItems(float duration)
|
|
|
|
{
|
|
|
|
if (!mRechargingItemsUpToDate)
|
|
|
|
{
|
|
|
|
updateRechargingItems();
|
|
|
|
mRechargingItemsUpToDate = true;
|
|
|
|
}
|
2021-05-16 09:40:41 +02:00
|
|
|
for (const auto& [item, charge] : mRechargingItems)
|
2018-12-15 10:23:50 +04:00
|
|
|
{
|
2021-05-16 09:40:41 +02:00
|
|
|
MWMechanics::rechargeItem(item, charge, duration);
|
2018-12-15 10:23:50 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MWWorld::CellStore::updateRechargingItems()
|
|
|
|
{
|
|
|
|
mRechargingItems.clear();
|
|
|
|
|
2021-05-16 09:40:41 +02:00
|
|
|
const auto update = [this](auto& list) {
|
|
|
|
for (auto& item : list)
|
2018-12-15 10:23:50 +04:00
|
|
|
{
|
2021-05-16 09:40:41 +02:00
|
|
|
Ptr ptr = getCurrentPtr(&item);
|
|
|
|
if (!ptr.isEmpty() && ptr.getRefData().getCount() > 0)
|
|
|
|
{
|
|
|
|
checkItem(ptr);
|
|
|
|
}
|
2018-12-15 10:23:50 +04:00
|
|
|
}
|
2021-05-16 09:40:41 +02:00
|
|
|
};
|
|
|
|
|
2022-09-08 20:52:00 +02:00
|
|
|
update(get<ESM::Weapon>().mList);
|
|
|
|
update(get<ESM::Armor>().mList);
|
|
|
|
update(get<ESM::Clothing>().mList);
|
|
|
|
update(get<ESM::Book>().mList);
|
2018-12-15 10:23:50 +04:00
|
|
|
}
|
|
|
|
|
2021-06-23 23:13:59 +02:00
|
|
|
void MWWorld::CellStore::checkItem(const Ptr& ptr)
|
2018-12-15 10:23:50 +04:00
|
|
|
{
|
2022-08-22 16:55:53 +02:00
|
|
|
std::string_view enchantmentId = ptr.getClass().getEnchantment(ptr);
|
|
|
|
if (enchantmentId.empty())
|
2018-12-15 10:23:50 +04:00
|
|
|
return;
|
|
|
|
|
|
|
|
const ESM::Enchantment* enchantment
|
|
|
|
= MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().search(enchantmentId);
|
|
|
|
if (!enchantment)
|
|
|
|
{
|
|
|
|
Log(Debug::Warning) << "Warning: Can't find enchantment '" << enchantmentId << "' on item "
|
|
|
|
<< ptr.getCellRef().getRefId();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (enchantment->mData.mType == ESM::Enchantment::WhenUsed
|
|
|
|
|| enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
|
|
|
|
mRechargingItems.emplace_back(ptr.getBase(), static_cast<float>(enchantment->mData.mCharge));
|
|
|
|
}
|
2021-12-06 13:46:56 +00:00
|
|
|
|
|
|
|
Ptr MWWorld::CellStore::getMovedActor(int actorId) const
|
|
|
|
{
|
|
|
|
for (const auto& [cellRef, cell] : mMovedToAnotherCell)
|
|
|
|
{
|
|
|
|
if (cellRef->mClass->isActor() && cellRef->mData.getCustomData())
|
|
|
|
{
|
|
|
|
Ptr actor(cellRef, cell);
|
|
|
|
if (actor.getClass().getCreatureStats(actor).getActorId() == actorId)
|
|
|
|
return actor;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
2012-06-29 18:54:23 +02:00
|
|
|
}
|