1 #ifndef OPENMW_MWWORLD_ESMSTORE_H 2 #define OPENMW_MWWORLD_ESMSTORE_H 3 4 #include <memory> 5 #include <sstream> 6 #include <stdexcept> 7 #include <unordered_map> 8 9 #include <components/esm/records.hpp> 10 #include "store.hpp" 11 12 namespace Loading 13 { 14 class Listener; 15 } 16 17 namespace MWMechanics 18 { 19 class SpellList; 20 } 21 22 namespace MWWorld 23 { 24 class ESMStore 25 { 26 Store<ESM::Activator> mActivators; 27 Store<ESM::Potion> mPotions; 28 Store<ESM::Apparatus> mAppas; 29 Store<ESM::Armor> mArmors; 30 Store<ESM::BodyPart> mBodyParts; 31 Store<ESM::Book> mBooks; 32 Store<ESM::BirthSign> mBirthSigns; 33 Store<ESM::Class> mClasses; 34 Store<ESM::Clothing> mClothes; 35 Store<ESM::Container> mContainers; 36 Store<ESM::Creature> mCreatures; 37 Store<ESM::Dialogue> mDialogs; 38 Store<ESM::Door> mDoors; 39 Store<ESM::Enchantment> mEnchants; 40 Store<ESM::Faction> mFactions; 41 Store<ESM::Global> mGlobals; 42 Store<ESM::Ingredient> mIngreds; 43 Store<ESM::CreatureLevList> mCreatureLists; 44 Store<ESM::ItemLevList> mItemLists; 45 Store<ESM::Light> mLights; 46 Store<ESM::Lockpick> mLockpicks; 47 Store<ESM::Miscellaneous> mMiscItems; 48 Store<ESM::NPC> mNpcs; 49 Store<ESM::Probe> mProbes; 50 Store<ESM::Race> mRaces; 51 Store<ESM::Region> mRegions; 52 Store<ESM::Repair> mRepairs; 53 Store<ESM::SoundGenerator> mSoundGens; 54 Store<ESM::Sound> mSounds; 55 Store<ESM::Spell> mSpells; 56 Store<ESM::StartScript> mStartScripts; 57 Store<ESM::Static> mStatics; 58 Store<ESM::Weapon> mWeapons; 59 60 Store<ESM::GameSetting> mGameSettings; 61 Store<ESM::Script> mScripts; 62 63 // Lists that need special rules 64 Store<ESM::Cell> mCells; 65 Store<ESM::Land> mLands; 66 Store<ESM::LandTexture> mLandTextures; 67 Store<ESM::Pathgrid> mPathgrids; 68 69 Store<ESM::MagicEffect> mMagicEffects; 70 Store<ESM::Skill> mSkills; 71 72 // Special entry which is hardcoded and not loaded from an ESM 73 Store<ESM::Attribute> mAttributes; 74 75 // Lookup of all IDs. Makes looking up references faster. Just 76 // maps the id name to the record type. 77 std::map<std::string, int> mIds; 78 std::map<std::string, int> mStaticIds; 79 80 std::unordered_map<std::string, int> mRefCount; 81 82 std::map<int, StoreBase *> mStores; 83 84 unsigned int mDynamicCount; 85 86 mutable std::map<std::string, std::weak_ptr<MWMechanics::SpellList> > mSpellListCache; 87 88 /// Validate entries in store after setup 89 void validate(); 90 91 void countRecords(); 92 public: 93 /// \todo replace with SharedIterator<StoreBase> 94 typedef std::map<int, StoreBase *>::const_iterator iterator; 95 begin() const96 iterator begin() const { 97 return mStores.begin(); 98 } 99 end() const100 iterator end() const { 101 return mStores.end(); 102 } 103 104 /// Look up the given ID in 'all'. Returns 0 if not found. 105 /// \note id must be in lower case. find(const std::string & id) const106 int find(const std::string &id) const 107 { 108 std::map<std::string, int>::const_iterator it = mIds.find(id); 109 if (it == mIds.end()) { 110 return 0; 111 } 112 return it->second; 113 } findStatic(const std::string & id) const114 int findStatic(const std::string &id) const 115 { 116 std::map<std::string, int>::const_iterator it = mStaticIds.find(id); 117 if (it == mStaticIds.end()) { 118 return 0; 119 } 120 return it->second; 121 } 122 ESMStore()123 ESMStore() 124 : mDynamicCount(0) 125 { 126 mStores[ESM::REC_ACTI] = &mActivators; 127 mStores[ESM::REC_ALCH] = &mPotions; 128 mStores[ESM::REC_APPA] = &mAppas; 129 mStores[ESM::REC_ARMO] = &mArmors; 130 mStores[ESM::REC_BODY] = &mBodyParts; 131 mStores[ESM::REC_BOOK] = &mBooks; 132 mStores[ESM::REC_BSGN] = &mBirthSigns; 133 mStores[ESM::REC_CELL] = &mCells; 134 mStores[ESM::REC_CLAS] = &mClasses; 135 mStores[ESM::REC_CLOT] = &mClothes; 136 mStores[ESM::REC_CONT] = &mContainers; 137 mStores[ESM::REC_CREA] = &mCreatures; 138 mStores[ESM::REC_DIAL] = &mDialogs; 139 mStores[ESM::REC_DOOR] = &mDoors; 140 mStores[ESM::REC_ENCH] = &mEnchants; 141 mStores[ESM::REC_FACT] = &mFactions; 142 mStores[ESM::REC_GLOB] = &mGlobals; 143 mStores[ESM::REC_GMST] = &mGameSettings; 144 mStores[ESM::REC_INGR] = &mIngreds; 145 mStores[ESM::REC_LAND] = &mLands; 146 mStores[ESM::REC_LEVC] = &mCreatureLists; 147 mStores[ESM::REC_LEVI] = &mItemLists; 148 mStores[ESM::REC_LIGH] = &mLights; 149 mStores[ESM::REC_LOCK] = &mLockpicks; 150 mStores[ESM::REC_LTEX] = &mLandTextures; 151 mStores[ESM::REC_MISC] = &mMiscItems; 152 mStores[ESM::REC_NPC_] = &mNpcs; 153 mStores[ESM::REC_PGRD] = &mPathgrids; 154 mStores[ESM::REC_PROB] = &mProbes; 155 mStores[ESM::REC_RACE] = &mRaces; 156 mStores[ESM::REC_REGN] = &mRegions; 157 mStores[ESM::REC_REPA] = &mRepairs; 158 mStores[ESM::REC_SCPT] = &mScripts; 159 mStores[ESM::REC_SNDG] = &mSoundGens; 160 mStores[ESM::REC_SOUN] = &mSounds; 161 mStores[ESM::REC_SPEL] = &mSpells; 162 mStores[ESM::REC_SSCR] = &mStartScripts; 163 mStores[ESM::REC_STAT] = &mStatics; 164 mStores[ESM::REC_WEAP] = &mWeapons; 165 166 mPathgrids.setCells(mCells); 167 } 168 clearDynamic()169 void clearDynamic () 170 { 171 for (std::map<int, StoreBase *>::iterator it = mStores.begin(); it != mStores.end(); ++it) 172 it->second->clearDynamic(); 173 174 movePlayerRecord(); 175 } 176 movePlayerRecord()177 void movePlayerRecord () 178 { 179 auto player = mNpcs.find("player"); 180 mNpcs.insert(*player); 181 } 182 183 /// Validate entries in store after loading a save 184 void validateDynamic(); 185 186 void load(ESM::ESMReader &esm, Loading::Listener* listener); 187 188 template <class T> get() const189 const Store<T> &get() const { 190 throw std::runtime_error("Storage for this type not exist"); 191 } 192 193 /// Insert a custom record (i.e. with a generated ID that will not clash will pre-existing records) 194 template <class T> insert(const T & x)195 const T *insert(const T &x) 196 { 197 const std::string id = "$dynamic" + std::to_string(mDynamicCount++); 198 199 Store<T> &store = const_cast<Store<T> &>(get<T>()); 200 if (store.search(id) != nullptr) 201 { 202 const std::string msg = "Try to override existing record '" + id + "'"; 203 throw std::runtime_error(msg); 204 } 205 T record = x; 206 207 record.mId = id; 208 209 T *ptr = store.insert(record); 210 for (iterator it = mStores.begin(); it != mStores.end(); ++it) { 211 if (it->second == &store) { 212 mIds[ptr->mId] = it->first; 213 } 214 } 215 return ptr; 216 } 217 218 /// Insert a record with set ID, and allow it to override a pre-existing static record. 219 template <class T> overrideRecord(const T & x)220 const T *overrideRecord(const T &x) { 221 Store<T> &store = const_cast<Store<T> &>(get<T>()); 222 223 T *ptr = store.insert(x); 224 for (iterator it = mStores.begin(); it != mStores.end(); ++it) { 225 if (it->second == &store) { 226 mIds[ptr->mId] = it->first; 227 } 228 } 229 return ptr; 230 } 231 232 template <class T> insertStatic(const T & x)233 const T *insertStatic(const T &x) 234 { 235 const std::string id = "$dynamic" + std::to_string(mDynamicCount++); 236 237 Store<T> &store = const_cast<Store<T> &>(get<T>()); 238 if (store.search(id) != nullptr) 239 { 240 const std::string msg = "Try to override existing record '" + id + "'"; 241 throw std::runtime_error(msg); 242 } 243 T record = x; 244 245 T *ptr = store.insertStatic(record); 246 for (iterator it = mStores.begin(); it != mStores.end(); ++it) { 247 if (it->second == &store) { 248 mIds[ptr->mId] = it->first; 249 } 250 } 251 return ptr; 252 } 253 254 // This method must be called once, after loading all master/plugin files. This can only be done 255 // from the outside, so it must be public. 256 void setUp(bool validateRecords = false); 257 258 int countSavedGameRecords() const; 259 260 void write (ESM::ESMWriter& writer, Loading::Listener& progress) const; 261 262 bool readRecord (ESM::ESMReader& reader, uint32_t type); 263 ///< \return Known type? 264 265 // To be called when we are done with dynamic record loading 266 void checkPlayer(); 267 268 /// @return The number of instances defined in the base files. Excludes changes from the save file. 269 int getRefCount(const std::string& id) const; 270 271 /// Actors with the same ID share spells, abilities, etc. 272 /// @return The shared spell list to use for this actor and whether or not it has already been initialized. 273 std::pair<std::shared_ptr<MWMechanics::SpellList>, bool> getSpellList(const std::string& id) const; 274 }; 275 276 template <> insert(const ESM::Cell & cell)277 inline const ESM::Cell *ESMStore::insert<ESM::Cell>(const ESM::Cell &cell) { 278 return mCells.insert(cell); 279 } 280 281 template <> insert(const ESM::NPC & npc)282 inline const ESM::NPC *ESMStore::insert<ESM::NPC>(const ESM::NPC &npc) 283 { 284 const std::string id = "$dynamic" + std::to_string(mDynamicCount++); 285 286 if (Misc::StringUtils::ciEqual(npc.mId, "player")) 287 { 288 return mNpcs.insert(npc); 289 } 290 else if (mNpcs.search(id) != nullptr) 291 { 292 const std::string msg = "Try to override existing record '" + id + "'"; 293 throw std::runtime_error(msg); 294 } 295 ESM::NPC record = npc; 296 297 record.mId = id; 298 299 ESM::NPC *ptr = mNpcs.insert(record); 300 mIds[ptr->mId] = ESM::REC_NPC_; 301 return ptr; 302 } 303 304 template <> get() const305 inline const Store<ESM::Activator> &ESMStore::get<ESM::Activator>() const { 306 return mActivators; 307 } 308 309 template <> get() const310 inline const Store<ESM::Potion> &ESMStore::get<ESM::Potion>() const { 311 return mPotions; 312 } 313 314 template <> get() const315 inline const Store<ESM::Apparatus> &ESMStore::get<ESM::Apparatus>() const { 316 return mAppas; 317 } 318 319 template <> get() const320 inline const Store<ESM::Armor> &ESMStore::get<ESM::Armor>() const { 321 return mArmors; 322 } 323 324 template <> get() const325 inline const Store<ESM::BodyPart> &ESMStore::get<ESM::BodyPart>() const { 326 return mBodyParts; 327 } 328 329 template <> get() const330 inline const Store<ESM::Book> &ESMStore::get<ESM::Book>() const { 331 return mBooks; 332 } 333 334 template <> get() const335 inline const Store<ESM::BirthSign> &ESMStore::get<ESM::BirthSign>() const { 336 return mBirthSigns; 337 } 338 339 template <> get() const340 inline const Store<ESM::Class> &ESMStore::get<ESM::Class>() const { 341 return mClasses; 342 } 343 344 template <> get() const345 inline const Store<ESM::Clothing> &ESMStore::get<ESM::Clothing>() const { 346 return mClothes; 347 } 348 349 template <> get() const350 inline const Store<ESM::Container> &ESMStore::get<ESM::Container>() const { 351 return mContainers; 352 } 353 354 template <> get() const355 inline const Store<ESM::Creature> &ESMStore::get<ESM::Creature>() const { 356 return mCreatures; 357 } 358 359 template <> get() const360 inline const Store<ESM::Dialogue> &ESMStore::get<ESM::Dialogue>() const { 361 return mDialogs; 362 } 363 364 template <> get() const365 inline const Store<ESM::Door> &ESMStore::get<ESM::Door>() const { 366 return mDoors; 367 } 368 369 template <> get() const370 inline const Store<ESM::Enchantment> &ESMStore::get<ESM::Enchantment>() const { 371 return mEnchants; 372 } 373 374 template <> get() const375 inline const Store<ESM::Faction> &ESMStore::get<ESM::Faction>() const { 376 return mFactions; 377 } 378 379 template <> get() const380 inline const Store<ESM::Global> &ESMStore::get<ESM::Global>() const { 381 return mGlobals; 382 } 383 384 template <> get() const385 inline const Store<ESM::Ingredient> &ESMStore::get<ESM::Ingredient>() const { 386 return mIngreds; 387 } 388 389 template <> get() const390 inline const Store<ESM::CreatureLevList> &ESMStore::get<ESM::CreatureLevList>() const { 391 return mCreatureLists; 392 } 393 394 template <> get() const395 inline const Store<ESM::ItemLevList> &ESMStore::get<ESM::ItemLevList>() const { 396 return mItemLists; 397 } 398 399 template <> get() const400 inline const Store<ESM::Light> &ESMStore::get<ESM::Light>() const { 401 return mLights; 402 } 403 404 template <> get() const405 inline const Store<ESM::Lockpick> &ESMStore::get<ESM::Lockpick>() const { 406 return mLockpicks; 407 } 408 409 template <> get() const410 inline const Store<ESM::Miscellaneous> &ESMStore::get<ESM::Miscellaneous>() const { 411 return mMiscItems; 412 } 413 414 template <> get() const415 inline const Store<ESM::NPC> &ESMStore::get<ESM::NPC>() const { 416 return mNpcs; 417 } 418 419 template <> get() const420 inline const Store<ESM::Probe> &ESMStore::get<ESM::Probe>() const { 421 return mProbes; 422 } 423 424 template <> get() const425 inline const Store<ESM::Race> &ESMStore::get<ESM::Race>() const { 426 return mRaces; 427 } 428 429 template <> get() const430 inline const Store<ESM::Region> &ESMStore::get<ESM::Region>() const { 431 return mRegions; 432 } 433 434 template <> get() const435 inline const Store<ESM::Repair> &ESMStore::get<ESM::Repair>() const { 436 return mRepairs; 437 } 438 439 template <> get() const440 inline const Store<ESM::SoundGenerator> &ESMStore::get<ESM::SoundGenerator>() const { 441 return mSoundGens; 442 } 443 444 template <> get() const445 inline const Store<ESM::Sound> &ESMStore::get<ESM::Sound>() const { 446 return mSounds; 447 } 448 449 template <> get() const450 inline const Store<ESM::Spell> &ESMStore::get<ESM::Spell>() const { 451 return mSpells; 452 } 453 454 template <> get() const455 inline const Store<ESM::StartScript> &ESMStore::get<ESM::StartScript>() const { 456 return mStartScripts; 457 } 458 459 template <> get() const460 inline const Store<ESM::Static> &ESMStore::get<ESM::Static>() const { 461 return mStatics; 462 } 463 464 template <> get() const465 inline const Store<ESM::Weapon> &ESMStore::get<ESM::Weapon>() const { 466 return mWeapons; 467 } 468 469 template <> get() const470 inline const Store<ESM::GameSetting> &ESMStore::get<ESM::GameSetting>() const { 471 return mGameSettings; 472 } 473 474 template <> get() const475 inline const Store<ESM::Script> &ESMStore::get<ESM::Script>() const { 476 return mScripts; 477 } 478 479 template <> get() const480 inline const Store<ESM::Cell> &ESMStore::get<ESM::Cell>() const { 481 return mCells; 482 } 483 484 template <> get() const485 inline const Store<ESM::Land> &ESMStore::get<ESM::Land>() const { 486 return mLands; 487 } 488 489 template <> get() const490 inline const Store<ESM::LandTexture> &ESMStore::get<ESM::LandTexture>() const { 491 return mLandTextures; 492 } 493 494 template <> get() const495 inline const Store<ESM::Pathgrid> &ESMStore::get<ESM::Pathgrid>() const { 496 return mPathgrids; 497 } 498 499 template <> get() const500 inline const Store<ESM::MagicEffect> &ESMStore::get<ESM::MagicEffect>() const { 501 return mMagicEffects; 502 } 503 504 template <> get() const505 inline const Store<ESM::Skill> &ESMStore::get<ESM::Skill>() const { 506 return mSkills; 507 } 508 509 template <> get() const510 inline const Store<ESM::Attribute> &ESMStore::get<ESM::Attribute>() const { 511 return mAttributes; 512 } 513 } 514 515 #endif 516