mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-05-04 07:47:59 +03:00
isolates groundcover content files (#3208)
Specifications developed in PR #3206 require that groundcover content files must not be allowed to corrupt normal content files. With this PR we simply isolate our existing loading logic by instantiating a separate `ESMStore` for `Groundcover`. In addition, we remove some outdated workarounds.
This commit is contained in:
parent
68e7a4083e
commit
a62b22cd31
10 changed files with 62 additions and 86 deletions
|
@ -5,6 +5,7 @@
|
||||||
#include <osg/BlendFunc>
|
#include <osg/BlendFunc>
|
||||||
#include <osg/Geometry>
|
#include <osg/Geometry>
|
||||||
#include <osg/VertexAttribDivisor>
|
#include <osg/VertexAttribDivisor>
|
||||||
|
#include <osg/Program>
|
||||||
|
|
||||||
#include <components/esm/esmreader.hpp>
|
#include <components/esm/esmreader.hpp>
|
||||||
#include <components/sceneutil/lightmanager.hpp>
|
#include <components/sceneutil/lightmanager.hpp>
|
||||||
|
@ -20,15 +21,12 @@
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
std::string getGroundcoverModel(int type, const std::string& id, const MWWorld::ESMStore& store)
|
std::string getGroundcoverModel(const std::string& id, const MWWorld::ESMStore& groundcoverStore, const MWWorld::ESMStore& store)
|
||||||
{
|
{
|
||||||
switch (type)
|
const ESM::Static* stat = groundcoverStore.get<ESM::Static>().searchStatic(id);
|
||||||
{
|
if (!stat)
|
||||||
case ESM::REC_STAT:
|
stat = store.get<ESM::Static>().searchStatic(id);
|
||||||
return store.get<ESM::Static>().searchStatic(id)->mModel;
|
return stat ? stat->mModel : std::string();
|
||||||
default:
|
|
||||||
return std::string();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class InstancingVisitor : public osg::NodeVisitor
|
class InstancingVisitor : public osg::NodeVisitor
|
||||||
|
@ -155,11 +153,12 @@ namespace MWRender
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Groundcover::Groundcover(Resource::SceneManager* sceneManager, float density, float viewDistance)
|
Groundcover::Groundcover(Resource::SceneManager* sceneManager, float density, float viewDistance, const MWWorld::ESMStore& store)
|
||||||
: GenericResourceManager<GroundcoverChunkId>(nullptr)
|
: GenericResourceManager<GroundcoverChunkId>(nullptr)
|
||||||
, mSceneManager(sceneManager)
|
, mSceneManager(sceneManager)
|
||||||
, mDensity(density)
|
, mDensity(density)
|
||||||
, mStateset(new osg::StateSet)
|
, mStateset(new osg::StateSet)
|
||||||
|
, mGroundcoverStore(store)
|
||||||
{
|
{
|
||||||
setViewDistance(viewDistance);
|
setViewDistance(viewDistance);
|
||||||
// MGE uses default alpha settings for groundcover, so we can not rely on alpha properties
|
// MGE uses default alpha settings for groundcover, so we can not rely on alpha properties
|
||||||
|
@ -176,9 +175,13 @@ namespace MWRender
|
||||||
mProgramTemplate->addBindAttribLocation("aRotation", 7);
|
mProgramTemplate->addBindAttribLocation("aRotation", 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Groundcover::~Groundcover()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void Groundcover::collectInstances(InstanceMap& instances, float size, const osg::Vec2f& center)
|
void Groundcover::collectInstances(InstanceMap& instances, float size, const osg::Vec2f& center)
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
|
const MWWorld::ESMStore& worldStore = MWBase::Environment::get().getWorld()->getStore();
|
||||||
osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f));
|
osg::Vec2f minBound = (center - osg::Vec2f(size/2.f, size/2.f));
|
||||||
osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f));
|
osg::Vec2f maxBound = (center + osg::Vec2f(size/2.f, size/2.f));
|
||||||
DensityCalculator calculator(mDensity);
|
DensityCalculator calculator(mDensity);
|
||||||
|
@ -188,7 +191,7 @@ namespace MWRender
|
||||||
{
|
{
|
||||||
for (int cellY = startCell.y(); cellY < startCell.y() + size; ++cellY)
|
for (int cellY = startCell.y(); cellY < startCell.y() + size; ++cellY)
|
||||||
{
|
{
|
||||||
const ESM::Cell* cell = store.get<ESM::Cell>().searchStatic(cellX, cellY);
|
const ESM::Cell* cell = mGroundcoverStore.get<ESM::Cell>().searchStatic(cellX, cellY);
|
||||||
if (!cell) continue;
|
if (!cell) continue;
|
||||||
|
|
||||||
calculator.reset();
|
calculator.reset();
|
||||||
|
@ -204,14 +207,11 @@ namespace MWRender
|
||||||
while(cell->getNextRef(esm[index], ref, deleted))
|
while(cell->getNextRef(esm[index], ref, deleted))
|
||||||
{
|
{
|
||||||
if (deleted) continue;
|
if (deleted) continue;
|
||||||
if (!ref.mRefNum.fromGroundcoverFile()) continue;
|
|
||||||
|
|
||||||
if (!calculator.isInstanceEnabled()) continue;
|
if (!calculator.isInstanceEnabled()) continue;
|
||||||
if (!isInChunkBorders(ref, minBound, maxBound)) continue;
|
if (!isInChunkBorders(ref, minBound, maxBound)) continue;
|
||||||
|
|
||||||
Misc::StringUtils::lowerCaseInPlace(ref.mRefID);
|
std::string model = getGroundcoverModel(ref.mRefID, mGroundcoverStore, worldStore);
|
||||||
int type = store.findStatic(ref.mRefID);
|
|
||||||
std::string model = getGroundcoverModel(type, ref.mRefID, store);
|
|
||||||
if (model.empty()) continue;
|
if (model.empty()) continue;
|
||||||
model = "meshes/" + model;
|
model = "meshes/" + model;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,15 @@
|
||||||
#include <components/terrain/quadtreeworld.hpp>
|
#include <components/terrain/quadtreeworld.hpp>
|
||||||
#include <components/resource/scenemanager.hpp>
|
#include <components/resource/scenemanager.hpp>
|
||||||
#include <components/esm/loadcell.hpp>
|
#include <components/esm/loadcell.hpp>
|
||||||
#include <osg/Program>
|
|
||||||
|
namespace MWWorld
|
||||||
|
{
|
||||||
|
class ESMStore;
|
||||||
|
}
|
||||||
|
namespace osg
|
||||||
|
{
|
||||||
|
class Program;
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWRender
|
namespace MWRender
|
||||||
{
|
{
|
||||||
|
@ -12,8 +20,8 @@ namespace MWRender
|
||||||
class Groundcover : public Resource::GenericResourceManager<GroundcoverChunkId>, public Terrain::QuadTreeWorld::ChunkManager
|
class Groundcover : public Resource::GenericResourceManager<GroundcoverChunkId>, public Terrain::QuadTreeWorld::ChunkManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Groundcover(Resource::SceneManager* sceneManager, float density, float viewDistance);
|
Groundcover(Resource::SceneManager* sceneManager, float density, float viewDistance, const MWWorld::ESMStore& groundcoverStore);
|
||||||
~Groundcover() = default;
|
~Groundcover();
|
||||||
|
|
||||||
osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) override;
|
osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) override;
|
||||||
|
|
||||||
|
@ -35,6 +43,8 @@ namespace MWRender
|
||||||
float mDensity;
|
float mDensity;
|
||||||
osg::ref_ptr<osg::StateSet> mStateset;
|
osg::ref_ptr<osg::StateSet> mStateset;
|
||||||
osg::ref_ptr<osg::Program> mProgramTemplate;
|
osg::ref_ptr<osg::Program> mProgramTemplate;
|
||||||
|
/// @note mGroundcoverStore is separated from World's store because groundcover files must not be allowed to corrupt normal content files.
|
||||||
|
const MWWorld::ESMStore& mGroundcoverStore;
|
||||||
|
|
||||||
typedef std::map<std::string, std::vector<GroundcoverEntry>> InstanceMap;
|
typedef std::map<std::string, std::vector<GroundcoverEntry>> InstanceMap;
|
||||||
osg::ref_ptr<osg::Node> createChunk(InstanceMap& instances, const osg::Vec2f& center);
|
osg::ref_ptr<osg::Node> createChunk(InstanceMap& instances, const osg::Vec2f& center);
|
||||||
|
|
|
@ -432,7 +432,6 @@ namespace MWRender
|
||||||
int type = store.findStatic(ref.mRefID);
|
int type = store.findStatic(ref.mRefID);
|
||||||
if (!typeFilter(type,size>=2)) continue;
|
if (!typeFilter(type,size>=2)) continue;
|
||||||
if (deleted) { refs.erase(ref.mRefNum); continue; }
|
if (deleted) { refs.erase(ref.mRefNum); continue; }
|
||||||
if (ref.mRefNum.fromGroundcoverFile()) continue;
|
|
||||||
refs[ref.mRefNum] = std::move(ref);
|
refs[ref.mRefNum] = std::move(ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -294,7 +294,7 @@ namespace MWRender
|
||||||
|
|
||||||
RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
|
RenderingManager::RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
|
||||||
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
|
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
|
||||||
const std::string& resourcePath, DetourNavigator::Navigator& navigator)
|
const std::string& resourcePath, DetourNavigator::Navigator& navigator, const MWWorld::ESMStore& groundcoverStore)
|
||||||
: mViewer(viewer)
|
: mViewer(viewer)
|
||||||
, mRootNode(rootNode)
|
, mRootNode(rootNode)
|
||||||
, mResourceSystem(resourceSystem)
|
, mResourceSystem(resourceSystem)
|
||||||
|
@ -450,7 +450,7 @@ namespace MWRender
|
||||||
float density = Settings::Manager::getFloat("density", "Groundcover");
|
float density = Settings::Manager::getFloat("density", "Groundcover");
|
||||||
density = std::clamp(density, 0.f, 1.f);
|
density = std::clamp(density, 0.f, 1.f);
|
||||||
|
|
||||||
mGroundcover.reset(new Groundcover(mResourceSystem->getSceneManager(), density, groundcoverDistance));
|
mGroundcover.reset(new Groundcover(mResourceSystem->getSceneManager(), density, groundcoverDistance, groundcoverStore));
|
||||||
static_cast<Terrain::QuadTreeWorld*>(mTerrain.get())->addChunkManager(mGroundcover.get());
|
static_cast<Terrain::QuadTreeWorld*>(mTerrain.get())->addChunkManager(mGroundcover.get());
|
||||||
mResourceSystem->addResourceManager(mGroundcover.get());
|
mResourceSystem->addResourceManager(mGroundcover.get());
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@ namespace MWRender
|
||||||
public:
|
public:
|
||||||
RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
|
RenderingManager(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
|
||||||
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
|
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
|
||||||
const std::string& resourcePath, DetourNavigator::Navigator& navigator);
|
const std::string& resourcePath, DetourNavigator::Navigator& navigator, const MWWorld::ESMStore& groundcoverStore);
|
||||||
~RenderingManager();
|
~RenderingManager();
|
||||||
|
|
||||||
osgUtil::IncrementalCompileOperation* getIncrementalCompileOperation();
|
osgUtil::IncrementalCompileOperation* getIncrementalCompileOperation();
|
||||||
|
|
|
@ -741,11 +741,7 @@ namespace MWWorld
|
||||||
case ESM::REC_NPC_: mNpcs.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_PROB: mProbes.load(ref, deleted, store); break;
|
||||||
case ESM::REC_REPA: mRepairs.load(ref, deleted, store); break;
|
case ESM::REC_REPA: mRepairs.load(ref, deleted, store); break;
|
||||||
case ESM::REC_STAT:
|
case ESM::REC_STAT: mStatics.load(ref, deleted, store); break;
|
||||||
{
|
|
||||||
if (ref.mRefNum.fromGroundcoverFile()) return;
|
|
||||||
mStatics.load(ref, deleted, store); break;
|
|
||||||
}
|
|
||||||
case ESM::REC_WEAP: mWeapons.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 ESM::REC_BODY: mBodyParts.load(ref, deleted, store); break;
|
||||||
|
|
||||||
|
|
|
@ -152,23 +152,16 @@ namespace MWWorld
|
||||||
mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0),
|
mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0),
|
||||||
mPlayerTraveling(false), mPlayerInJail(false), mSpellPreloadTimer(0.f)
|
mPlayerTraveling(false), mPlayerInJail(false), mSpellPreloadTimer(0.f)
|
||||||
{
|
{
|
||||||
mEsm.resize(contentFiles.size() + groundcoverFiles.size());
|
mEsm.resize(contentFiles.size());
|
||||||
Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
|
Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
|
||||||
listener->loadingOn();
|
listener->loadingOn();
|
||||||
|
|
||||||
GameContentLoader gameContentLoader(*listener);
|
loadContentFiles(fileCollections, contentFiles, mStore, mEsm, encoder, listener);
|
||||||
EsmLoader esmLoader(mStore, mEsm, encoder, *listener);
|
if (!groundcoverFiles.empty())
|
||||||
|
{
|
||||||
gameContentLoader.addLoader(".esm", &esmLoader);
|
std::vector<ESM::ESMReader> tempReaders (groundcoverFiles.size());
|
||||||
gameContentLoader.addLoader(".esp", &esmLoader);
|
loadContentFiles(fileCollections, groundcoverFiles, mGroundcoverStore, tempReaders, encoder, listener, false);
|
||||||
gameContentLoader.addLoader(".omwgame", &esmLoader);
|
}
|
||||||
gameContentLoader.addLoader(".omwaddon", &esmLoader);
|
|
||||||
gameContentLoader.addLoader(".project", &esmLoader);
|
|
||||||
|
|
||||||
OMWScriptsLoader omwScriptsLoader(*listener, mStore);
|
|
||||||
gameContentLoader.addLoader(".omwscripts", &omwScriptsLoader);
|
|
||||||
|
|
||||||
loadContentFiles(fileCollections, contentFiles, groundcoverFiles, gameContentLoader);
|
|
||||||
|
|
||||||
listener->loadingOff();
|
listener->loadingOff();
|
||||||
|
|
||||||
|
@ -176,10 +169,6 @@ namespace MWWorld
|
||||||
if (mEsm[0].getFormat() == 0)
|
if (mEsm[0].getFormat() == 0)
|
||||||
ensureNeededRecords();
|
ensureNeededRecords();
|
||||||
|
|
||||||
// TODO: We can and should validate before we call loadContentFiles().
|
|
||||||
// Currently we validate here to prevent merge conflicts with groundcover ESMStore fixes.
|
|
||||||
validateMasterFiles(mEsm);
|
|
||||||
|
|
||||||
mCurrentDate.reset(new DateTimeManager());
|
mCurrentDate.reset(new DateTimeManager());
|
||||||
|
|
||||||
fillGlobalVariables();
|
fillGlobalVariables();
|
||||||
|
@ -202,7 +191,7 @@ namespace MWWorld
|
||||||
mNavigator = DetourNavigator::makeNavigatorStub();
|
mNavigator = DetourNavigator::makeNavigatorStub();
|
||||||
}
|
}
|
||||||
|
|
||||||
mRendering.reset(new MWRender::RenderingManager(viewer, rootNode, resourceSystem, workQueue, resourcePath, *mNavigator));
|
mRendering.reset(new MWRender::RenderingManager(viewer, rootNode, resourceSystem, workQueue, resourcePath, *mNavigator, mGroundcoverStore));
|
||||||
mProjectileManager.reset(new ProjectileManager(mRendering->getLightRoot(), resourceSystem, mRendering.get(), mPhysics.get()));
|
mProjectileManager.reset(new ProjectileManager(mRendering->getLightRoot(), resourceSystem, mRendering.get(), mPhysics.get()));
|
||||||
mRendering->preloadCommonAssets();
|
mRendering->preloadCommonAssets();
|
||||||
|
|
||||||
|
@ -2959,9 +2948,22 @@ namespace MWWorld
|
||||||
return mScriptsEnabled;
|
return mScriptsEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::loadContentFiles(const Files::Collections& fileCollections,
|
void World::loadContentFiles(const Files::Collections& fileCollections, const std::vector<std::string>& content, ESMStore& store, std::vector<ESM::ESMReader>& readers, ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener, bool validate)
|
||||||
const std::vector<std::string>& content, const std::vector<std::string>& groundcover, ContentLoader& contentLoader)
|
|
||||||
{
|
{
|
||||||
|
GameContentLoader gameContentLoader(*listener);
|
||||||
|
EsmLoader esmLoader(store, readers, encoder, *listener);
|
||||||
|
if (validate)
|
||||||
|
validateMasterFiles(readers);
|
||||||
|
|
||||||
|
gameContentLoader.addLoader(".esm", &esmLoader);
|
||||||
|
gameContentLoader.addLoader(".esp", &esmLoader);
|
||||||
|
gameContentLoader.addLoader(".omwgame", &esmLoader);
|
||||||
|
gameContentLoader.addLoader(".omwaddon", &esmLoader);
|
||||||
|
gameContentLoader.addLoader(".project", &esmLoader);
|
||||||
|
|
||||||
|
OMWScriptsLoader omwScriptsLoader(*listener, store);
|
||||||
|
gameContentLoader.addLoader(".omwscripts", &omwScriptsLoader);
|
||||||
|
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
for (const std::string &file : content)
|
for (const std::string &file : content)
|
||||||
{
|
{
|
||||||
|
@ -2969,7 +2971,7 @@ namespace MWWorld
|
||||||
const Files::MultiDirCollection& col = fileCollections.getCollection(filename.extension().string());
|
const Files::MultiDirCollection& col = fileCollections.getCollection(filename.extension().string());
|
||||||
if (col.doesExist(file))
|
if (col.doesExist(file))
|
||||||
{
|
{
|
||||||
contentLoader.load(col.getPath(file), idx);
|
gameContentLoader.load(col.getPath(file), idx);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -2978,24 +2980,6 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESM::GroundcoverIndex = idx;
|
|
||||||
|
|
||||||
for (const std::string &file : groundcover)
|
|
||||||
{
|
|
||||||
boost::filesystem::path filename(file);
|
|
||||||
const Files::MultiDirCollection& col = fileCollections.getCollection(filename.extension().string());
|
|
||||||
if (col.doesExist(file))
|
|
||||||
{
|
|
||||||
contentLoader.load(col.getPath(file), idx);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::string message = "Failed loading " + file + ": the groundcover file does not exist";
|
|
||||||
throw std::runtime_error(message);
|
|
||||||
}
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool World::startSpellCast(const Ptr &actor)
|
bool World::startSpellCast(const Ptr &actor)
|
||||||
|
|
|
@ -80,6 +80,7 @@ namespace MWWorld
|
||||||
|
|
||||||
std::vector<ESM::ESMReader> mEsm;
|
std::vector<ESM::ESMReader> mEsm;
|
||||||
MWWorld::ESMStore mStore;
|
MWWorld::ESMStore mStore;
|
||||||
|
MWWorld::ESMStore mGroundcoverStore;
|
||||||
LocalScripts mLocalScripts;
|
LocalScripts mLocalScripts;
|
||||||
MWWorld::Globals mGlobalVariables;
|
MWWorld::Globals mGlobalVariables;
|
||||||
|
|
||||||
|
@ -163,14 +164,10 @@ namespace MWWorld
|
||||||
|
|
||||||
void updateSkyDate();
|
void updateSkyDate();
|
||||||
|
|
||||||
/**
|
// A helper method called automatically during World construction.
|
||||||
* @brief loadContentFiles - Loads content files (esm,esp,omwgame,omwaddon)
|
void loadContentFiles(const Files::Collections& fileCollections, const std::vector<std::string>& content,
|
||||||
* @param fileCollections- Container which holds content file names and their paths
|
ESMStore& store, std::vector<ESM::ESMReader>& readers, ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener, bool validateMasterFiles = true);
|
||||||
* @param content - Container which holds content file names
|
|
||||||
* @param contentLoader -
|
|
||||||
*/
|
|
||||||
void loadContentFiles(const Files::Collections& fileCollections,
|
|
||||||
const std::vector<std::string>& content, const std::vector<std::string>& groundcover, ContentLoader& contentLoader);
|
|
||||||
|
|
||||||
float feetToGameUnits(float feet);
|
float feetToGameUnits(float feet);
|
||||||
float getActivationDistancePlusTelekinesis();
|
float getActivationDistancePlusTelekinesis();
|
||||||
|
|
|
@ -5,11 +5,6 @@
|
||||||
#include "esmreader.hpp"
|
#include "esmreader.hpp"
|
||||||
#include "esmwriter.hpp"
|
#include "esmwriter.hpp"
|
||||||
|
|
||||||
namespace ESM
|
|
||||||
{
|
|
||||||
int GroundcoverIndex = std::numeric_limits<int>::max();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESM::RefNum::load (ESMReader& esm, bool wide, const std::string& tag)
|
void ESM::RefNum::load (ESMReader& esm, bool wide, const std::string& tag)
|
||||||
{
|
{
|
||||||
if (wide)
|
if (wide)
|
||||||
|
|
|
@ -12,7 +12,6 @@ namespace ESM
|
||||||
class ESMReader;
|
class ESMReader;
|
||||||
|
|
||||||
const int UnbreakableLock = std::numeric_limits<int>::max();
|
const int UnbreakableLock = std::numeric_limits<int>::max();
|
||||||
extern int GroundcoverIndex;
|
|
||||||
|
|
||||||
struct RefNum
|
struct RefNum
|
||||||
{
|
{
|
||||||
|
@ -27,10 +26,6 @@ namespace ESM
|
||||||
|
|
||||||
inline bool isSet() const { return mIndex != 0 || mContentFile != -1; }
|
inline bool isSet() const { return mIndex != 0 || mContentFile != -1; }
|
||||||
inline void unset() { *this = {0, -1}; }
|
inline void unset() { *this = {0, -1}; }
|
||||||
|
|
||||||
// Note: this method should not be used for objects with invalid RefNum
|
|
||||||
// (for example, for objects from disabled plugins in savegames).
|
|
||||||
inline bool fromGroundcoverFile() const { return mContentFile >= GroundcoverIndex; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Cell reference. This represents ONE object (of many) inside the
|
/* Cell reference. This represents ONE object (of many) inside the
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue