#ifndef OPENMW_MWWORLD_ESMSTORE_H #define OPENMW_MWWORLD_ESMSTORE_H #include #include #include #include #include #include #include #include #include #include "store.hpp" namespace Loading { class Listener; } namespace MWMechanics { class SpellList; } namespace ESM4 { class Reader; struct Static; struct Cell; struct Light; } namespace ESM { class ReadersCache; struct Activator; struct Potion; struct Apparatus; struct Armor; struct BodyPart; struct Book; struct BirthSign; struct Class; struct Clothing; struct Container; struct Creature; struct Dialogue; struct Door; struct Enchantment; struct Faction; struct Global; struct Ingredient; struct CreatureLevList; struct ItemLevList; struct Light; struct Lockpick; struct Miscellaneous; struct NPC; struct Probe; struct Race; struct Region; struct Repair; struct SoundGenerator; struct Sound; struct Spell; struct StartScript; struct Static; struct Weapon; struct GameSetting; class Script; struct Cell; struct Land; struct LandTexture; struct Pathgrid; struct MagicEffect; struct Skill; struct Attribute; } namespace MWWorld { struct ESMStoreImp; class ESMStore { friend struct ESMStoreImp; // This allows StoreImp to extend esmstore without beeing included everywhere public: using StoreTuple = std::tuple, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, Store, // Lists that need special rules Store, Store, Store, Store, Store, Store, // Special entry which is hardcoded and not loaded from an ESM Store, Store, Store, Store>; private: template static constexpr std::size_t getTypeIndex() { static_assert(Misc::TupleHasType, StoreTuple>::value); return Misc::TupleTypeIndex, StoreTuple>::value; } std::unique_ptr mStoreImp; std::unordered_map mRefCount; std::vector mStores; std::vector mDynamicStores; unsigned int mDynamicCount; mutable std::unordered_map> mSpellListCache; template Store& getWritable() { return static_cast&>(*mStores[getTypeIndex()]); } /// Validate entries in store after setup void validate(); void countAllCellRefsAndMarkKeys(ESM::ReadersCache& readers); template void removeMissingObjects(Store& store); void setIdType(const ESM::RefId& id, ESM::RecNameInts type); using LuaContent = std::variant; // path to an omwscripts file std::vector mLuaContent; public: void addOMWScripts(std::filesystem::path filePath) { mLuaContent.push_back(std::move(filePath)); } ESM::LuaScriptsCfg getLuaScriptsCfg() const; /// \todo replace with SharedIterator typedef std::vector::const_iterator iterator; iterator begin() const { return mDynamicStores.begin(); } iterator end() const { return mDynamicStores.end(); } /// Look up the given ID in 'all'. Returns 0 if not found. int find(const ESM::RefId& id) const; int findStatic(const ESM::RefId& id) const; ESMStore(); ~ESMStore(); void clearDynamic(); void movePlayerRecord(); /// Validate entries in store after loading a save void validateDynamic(); void load(ESM::ESMReader& esm, Loading::Listener* listener, ESM::Dialogue*& dialogue); void loadESM4(ESM4::Reader& esm); template const Store& get() const { return static_cast&>(*mStores[getTypeIndex()]); } /// Insert a custom record (i.e. with a generated ID that will not clash will pre-existing records) template const T* insert(const T& x) { const ESM::RefId id = ESM::RefId::stringRefId("$dynamic" + std::to_string(mDynamicCount++)); Store& store = getWritable(); if (store.search(id) != nullptr) throw std::runtime_error("Try to override existing record: " + id.toDebugString()); T record = x; record.mId = id; T* ptr = store.insert(record); if constexpr (std::is_convertible_v*, DynamicStore*>) { setIdType(ptr->mId, T::sRecordId); } return ptr; } /// Insert a record with set ID, and allow it to override a pre-existing static record. template const T* overrideRecord(const T& x) { Store& store = getWritable(); T* ptr = store.insert(x); if constexpr (std::is_convertible_v*, DynamicStore*>) { setIdType(ptr->mId, T::sRecordId); } return ptr; } template const T* insertStatic(const T& x) { Store& store = getWritable(); if (store.search(x.mId) != nullptr) { const std::string msg = "Try to override existing record '" + x.mId.getRefIdString() + "'"; throw std::runtime_error(msg); } T* ptr = store.insertStatic(x); if constexpr (std::is_convertible_v*, DynamicStore*>) { setIdType(ptr->mId, T::sRecordId); } return ptr; } // This method must be called once, after loading all master/plugin files. This can only be done // from the outside, so it must be public. void setUp(); void validateRecords(ESM::ReadersCache& readers); int countSavedGameRecords() const; void write(ESM::ESMWriter& writer, Loading::Listener& progress) const; bool readRecord(ESM::ESMReader& reader, uint32_t type); ///< \return Known type? // To be called when we are done with dynamic record loading void checkPlayer(); /// @return The number of instances defined in the base files. Excludes changes from the save file. int getRefCount(const ESM::RefId& id) const; /// Actors with the same ID share spells, abilities, etc. /// @return The shared spell list to use for this actor and whether or not it has already been initialized. std::pair, bool> getSpellList(const ESM::RefId& id) const; }; template <> const ESM::Cell* ESMStore::insert(const ESM::Cell& cell); template <> const ESM::NPC* ESMStore::insert(const ESM::NPC& npc); template > struct HasRecordId : std::false_type { }; template struct HasRecordId> : std::true_type { }; } #endif