1 #ifndef GAME_MWWORLD_CELLSTORE_H 2 #define GAME_MWWORLD_CELLSTORE_H 3 4 #include <algorithm> 5 #include <stdexcept> 6 #include <string> 7 #include <typeinfo> 8 #include <map> 9 #include <memory> 10 11 #include "livecellref.hpp" 12 #include "cellreflist.hpp" 13 14 #include <components/esm/loadacti.hpp> 15 #include <components/esm/loadalch.hpp> 16 #include <components/esm/loadappa.hpp> 17 #include <components/esm/loadarmo.hpp> 18 #include <components/esm/loadbook.hpp> 19 #include <components/esm/loadclot.hpp> 20 #include <components/esm/loadcont.hpp> 21 #include <components/esm/loadcrea.hpp> 22 #include <components/esm/loaddoor.hpp> 23 #include <components/esm/loadingr.hpp> 24 #include <components/esm/loadlevlist.hpp> 25 #include <components/esm/loadligh.hpp> 26 #include <components/esm/loadlock.hpp> 27 #include <components/esm/loadprob.hpp> 28 #include <components/esm/loadrepa.hpp> 29 #include <components/esm/loadstat.hpp> 30 #include <components/esm/loadweap.hpp> 31 #include <components/esm/loadnpc.hpp> 32 #include <components/esm/loadmisc.hpp> 33 #include <components/esm/loadbody.hpp> 34 35 #include "timestamp.hpp" 36 #include "ptr.hpp" 37 38 namespace ESM 39 { 40 struct Cell; 41 struct CellState; 42 struct FogState; 43 struct CellId; 44 struct RefNum; 45 } 46 47 namespace MWWorld 48 { 49 class ESMStore; 50 51 /// \brief Mutable state of a cell 52 class CellStore 53 { 54 public: 55 56 enum State 57 { 58 State_Unloaded, State_Preloaded, State_Loaded 59 }; 60 61 private: 62 63 const MWWorld::ESMStore& mStore; 64 std::vector<ESM::ESMReader>& mReader; 65 66 // Even though fog actually belongs to the player and not cells, 67 // it makes sense to store it here since we need it once for each cell. 68 // Note this is nullptr until the cell is explored to save some memory 69 std::shared_ptr<ESM::FogState> mFogState; 70 71 const ESM::Cell *mCell; 72 State mState; 73 bool mHasState; 74 std::vector<std::string> mIds; 75 float mWaterLevel; 76 77 MWWorld::TimeStamp mLastRespawn; 78 79 // List of refs owned by this cell 80 CellRefList<ESM::Activator> mActivators; 81 CellRefList<ESM::Potion> mPotions; 82 CellRefList<ESM::Apparatus> mAppas; 83 CellRefList<ESM::Armor> mArmors; 84 CellRefList<ESM::Book> mBooks; 85 CellRefList<ESM::Clothing> mClothes; 86 CellRefList<ESM::Container> mContainers; 87 CellRefList<ESM::Creature> mCreatures; 88 CellRefList<ESM::Door> mDoors; 89 CellRefList<ESM::Ingredient> mIngreds; 90 CellRefList<ESM::CreatureLevList> mCreatureLists; 91 CellRefList<ESM::ItemLevList> mItemLists; 92 CellRefList<ESM::Light> mLights; 93 CellRefList<ESM::Lockpick> mLockpicks; 94 CellRefList<ESM::Miscellaneous> mMiscItems; 95 CellRefList<ESM::NPC> mNpcs; 96 CellRefList<ESM::Probe> mProbes; 97 CellRefList<ESM::Repair> mRepairs; 98 CellRefList<ESM::Static> mStatics; 99 CellRefList<ESM::Weapon> mWeapons; 100 CellRefList<ESM::BodyPart> mBodyParts; 101 102 typedef std::map<LiveCellRefBase*, MWWorld::CellStore*> MovedRefTracker; 103 // References owned by a different cell that have been moved here. 104 // <reference, cell the reference originally came from> 105 MovedRefTracker mMovedHere; 106 // References owned by this cell that have been moved to another cell. 107 // <reference, cell the reference was moved to> 108 MovedRefTracker mMovedToAnotherCell; 109 110 // Merged list of ref's currently in this cell - i.e. with added refs from mMovedHere, removed refs from mMovedToAnotherCell 111 std::vector<LiveCellRefBase*> mMergedRefs; 112 113 // Get the Ptr for the given ref which originated from this cell (possibly moved to another cell at this point). 114 Ptr getCurrentPtr(MWWorld::LiveCellRefBase* ref); 115 116 /// Moves object from the given cell to this cell. 117 void moveFrom(const MWWorld::Ptr& object, MWWorld::CellStore* from); 118 119 /// Repopulate mMergedRefs. 120 void updateMergedRefs(); 121 122 // (item, max charge) 123 typedef std::vector<std::pair<LiveCellRefBase*, float> > TRechargingItems; 124 TRechargingItems mRechargingItems; 125 126 bool mRechargingItemsUpToDate; 127 128 void updateRechargingItems(); 129 void rechargeItems(float duration); 130 void checkItem(Ptr ptr); 131 132 // helper function for forEachInternal 133 template<class Visitor, class List> forEachImp(Visitor & visitor,List & list)134 bool forEachImp (Visitor& visitor, List& list) 135 { 136 for (typename List::List::iterator iter (list.mList.begin()); iter!=list.mList.end(); 137 ++iter) 138 { 139 if (!isAccessible(iter->mData, iter->mRef)) 140 continue; 141 if (!visitor (MWWorld::Ptr(&*iter, this))) 142 return false; 143 } 144 return true; 145 } 146 147 // listing only objects owned by this cell. Internal use only, you probably want to use forEach() so that moved objects are accounted for. 148 template<class Visitor> forEachInternal(Visitor & visitor)149 bool forEachInternal (Visitor& visitor) 150 { 151 return 152 forEachImp (visitor, mActivators) && 153 forEachImp (visitor, mPotions) && 154 forEachImp (visitor, mAppas) && 155 forEachImp (visitor, mArmors) && 156 forEachImp (visitor, mBooks) && 157 forEachImp (visitor, mClothes) && 158 forEachImp (visitor, mContainers) && 159 forEachImp (visitor, mDoors) && 160 forEachImp (visitor, mIngreds) && 161 forEachImp (visitor, mItemLists) && 162 forEachImp (visitor, mLights) && 163 forEachImp (visitor, mLockpicks) && 164 forEachImp (visitor, mMiscItems) && 165 forEachImp (visitor, mProbes) && 166 forEachImp (visitor, mRepairs) && 167 forEachImp (visitor, mStatics) && 168 forEachImp (visitor, mWeapons) && 169 forEachImp (visitor, mBodyParts) && 170 forEachImp (visitor, mCreatures) && 171 forEachImp (visitor, mNpcs) && 172 forEachImp (visitor, mCreatureLists); 173 } 174 175 /// @note If you get a linker error here, this means the given type can not be stored in a cell. The supported types are 176 /// defined at the bottom of this file. 177 template <class T> 178 CellRefList<T>& get(); 179 180 public: 181 182 /// Should this reference be accessible to the outside world (i.e. to scripts / game logic)? 183 /// Determined based on the deletion flags. By default, objects deleted by content files are never accessible; 184 /// objects deleted by setCount(0) are still accessible *if* they came from a content file (needed for vanilla 185 /// scripting compatibility, and the fact that objects may be "un-deleted" in the original game). isAccessible(const MWWorld::RefData & refdata,const MWWorld::CellRef & cref)186 static bool isAccessible(const MWWorld::RefData& refdata, const MWWorld::CellRef& cref) 187 { 188 return !refdata.isDeletedByContentFile() && (cref.hasContentFile() || refdata.getCount() > 0); 189 } 190 191 /// Moves object from this cell to the given cell. 192 /// @note automatically updates given cell by calling cellToMoveTo->moveFrom(...) 193 /// @note throws exception if cellToMoveTo == this 194 /// @return updated MWWorld::Ptr with the new CellStore pointer set. 195 MWWorld::Ptr moveTo(const MWWorld::Ptr& object, MWWorld::CellStore* cellToMoveTo); 196 197 void rest(double hours); 198 void recharge(float duration); 199 200 /// Make a copy of the given object and insert it into this cell. 201 /// @note If you get a linker error here, this means the given type can not be inserted into a cell. 202 /// The supported types are defined at the bottom of this file. 203 template <typename T> insert(const LiveCellRef<T> * ref)204 LiveCellRefBase* insert(const LiveCellRef<T>* ref) 205 { 206 mHasState = true; 207 CellRefList<T>& list = get<T>(); 208 LiveCellRefBase* ret = &list.insert(*ref); 209 updateMergedRefs(); 210 return ret; 211 } 212 213 /// @param readerList The readers to use for loading of the cell on-demand. 214 CellStore (const ESM::Cell *cell_, 215 const MWWorld::ESMStore& store, 216 std::vector<ESM::ESMReader>& readerList); 217 218 const ESM::Cell *getCell() const; 219 220 State getState() const; 221 222 const std::vector<std::string>& getPreloadedIds() const; 223 ///< Get Ids of objects in this cell, only valid in State_Preloaded 224 225 bool hasState() const; 226 ///< Does this cell have state that needs to be stored in a saved game file? 227 228 bool hasId (const std::string& id) const; 229 ///< May return true for deleted IDs when in preload state. Will return false, if cell is 230 /// unloaded. 231 /// @note Will not account for moved references which may exist in Loaded state. Use search() instead if the cell is loaded. 232 233 Ptr search (const std::string& id); 234 ///< Will return an empty Ptr if cell is not loaded. Does not check references in 235 /// containers. 236 /// @note Triggers CellStore hasState flag. 237 238 ConstPtr searchConst (const std::string& id) const; 239 ///< Will return an empty Ptr if cell is not loaded. Does not check references in 240 /// containers. 241 /// @note Does not trigger CellStore hasState flag. 242 243 Ptr searchViaActorId (int id); 244 ///< Will return an empty Ptr if cell is not loaded. 245 246 Ptr searchViaRefNum (const ESM::RefNum& refNum); 247 ///< Will return an empty Ptr if cell is not loaded. Does not check references in 248 /// containers. 249 /// @note Triggers CellStore hasState flag. 250 251 float getWaterLevel() const; 252 253 bool movedHere(const MWWorld::Ptr& ptr) const; 254 255 void setWaterLevel (float level); 256 257 void setFog (ESM::FogState* fog); 258 ///< \note Takes ownership of the pointer 259 260 ESM::FogState* getFog () const; 261 262 std::size_t count() const; 263 ///< Return total number of references, including deleted ones. 264 265 void load (); 266 ///< Load references from content file. 267 268 void preload (); 269 ///< Build ID list from content file. 270 271 /// Call visitor (MWWorld::Ptr) for each reference. visitor must return a bool. Returning 272 /// false will abort the iteration. 273 /// \note Prefer using forEachConst when possible. 274 /// \note Do not modify this cell (i.e. remove/add objects) during the forEach, doing this may result in unintended behaviour. 275 /// \attention This function also lists deleted (count 0) objects! 276 /// \return Iteration completed? 277 template<class Visitor> forEach(Visitor && visitor)278 bool forEach (Visitor&& visitor) 279 { 280 if (mState != State_Loaded) 281 return false; 282 283 if (mMergedRefs.empty()) 284 return true; 285 286 mHasState = true; 287 288 for (unsigned int i=0; i<mMergedRefs.size(); ++i) 289 { 290 if (!isAccessible(mMergedRefs[i]->mData, mMergedRefs[i]->mRef)) 291 continue; 292 293 if (!visitor(MWWorld::Ptr(mMergedRefs[i], this))) 294 return false; 295 } 296 return true; 297 } 298 299 /// Call visitor (MWWorld::ConstPtr) for each reference. visitor must return a bool. Returning 300 /// false will abort the iteration. 301 /// \note Do not modify this cell (i.e. remove/add objects) during the forEach, doing this may result in unintended behaviour. 302 /// \attention This function also lists deleted (count 0) objects! 303 /// \return Iteration completed? 304 template<class Visitor> forEachConst(Visitor && visitor) const305 bool forEachConst (Visitor&& visitor) const 306 { 307 if (mState != State_Loaded) 308 return false; 309 310 for (unsigned int i=0; i<mMergedRefs.size(); ++i) 311 { 312 if (!isAccessible(mMergedRefs[i]->mData, mMergedRefs[i]->mRef)) 313 continue; 314 315 if (!visitor(MWWorld::ConstPtr(mMergedRefs[i], this))) 316 return false; 317 } 318 return true; 319 } 320 321 322 /// Call visitor (ref) for each reference of given type. visitor must return a bool. Returning 323 /// false will abort the iteration. 324 /// \note Do not modify this cell (i.e. remove/add objects) during the forEach, doing this may result in unintended behaviour. 325 /// \attention This function also lists deleted (count 0) objects! 326 /// \return Iteration completed? 327 template <class T, class Visitor> forEachType(Visitor & visitor)328 bool forEachType(Visitor& visitor) 329 { 330 if (mState != State_Loaded) 331 return false; 332 333 if (mMergedRefs.empty()) 334 return true; 335 336 mHasState = true; 337 338 CellRefList<T>& list = get<T>(); 339 340 for (typename CellRefList<T>::List::iterator it (list.mList.begin()); it!=list.mList.end(); ++it) 341 { 342 LiveCellRefBase* base = &*it; 343 if (mMovedToAnotherCell.find(base) != mMovedToAnotherCell.end()) 344 continue; 345 if (!isAccessible(base->mData, base->mRef)) 346 continue; 347 if (!visitor(MWWorld::Ptr(base, this))) 348 return false; 349 } 350 351 for (MovedRefTracker::const_iterator it = mMovedHere.begin(); it != mMovedHere.end(); ++it) 352 { 353 LiveCellRefBase* base = it->first; 354 if (dynamic_cast<LiveCellRef<T>*>(base)) 355 if (!visitor(MWWorld::Ptr(base, this))) 356 return false; 357 } 358 return true; 359 } 360 361 // NOTE: does not account for moved references 362 // Should be phased out when we have const version of forEach getReadOnlyDoors() const363 inline const CellRefList<ESM::Door>& getReadOnlyDoors() const 364 { 365 return mDoors; 366 } getReadOnlyStatics() const367 inline const CellRefList<ESM::Static>& getReadOnlyStatics() const 368 { 369 return mStatics; 370 } 371 372 bool isExterior() const; 373 374 Ptr searchInContainer (const std::string& id); 375 376 void loadState (const ESM::CellState& state); 377 378 void saveState (ESM::CellState& state) const; 379 380 void writeFog (ESM::ESMWriter& writer) const; 381 382 void readFog (ESM::ESMReader& reader); 383 384 void writeReferences (ESM::ESMWriter& writer) const; 385 386 struct GetCellStoreCallback 387 { 388 public: 389 ///@note must return nullptr if the cell is not found 390 virtual CellStore* getCellStore(const ESM::CellId& cellId) = 0; 391 virtual ~GetCellStoreCallback() = default; 392 }; 393 394 /// @param callback to use for retrieving of additional CellStore objects by ID (required for resolving moved references) 395 void readReferences (ESM::ESMReader& reader, const std::map<int, int>& contentFileMap, GetCellStoreCallback* callback); 396 397 void respawn (); 398 ///< Check mLastRespawn and respawn references if necessary. This is a no-op if the cell is not loaded. 399 400 private: 401 402 /// Run through references and store IDs 403 void listRefs(); 404 405 void loadRefs(); 406 407 void loadRef (ESM::CellRef& ref, bool deleted, std::map<ESM::RefNum, std::string>& refNumToID); 408 ///< Make case-adjustments to \a ref and insert it into the respective container. 409 /// 410 /// Invalid \a ref objects are silently dropped. 411 }; 412 413 template<> get()414 inline CellRefList<ESM::Activator>& CellStore::get<ESM::Activator>() 415 { 416 mHasState = true; 417 return mActivators; 418 } 419 420 template<> get()421 inline CellRefList<ESM::Potion>& CellStore::get<ESM::Potion>() 422 { 423 mHasState = true; 424 return mPotions; 425 } 426 427 template<> get()428 inline CellRefList<ESM::Apparatus>& CellStore::get<ESM::Apparatus>() 429 { 430 mHasState = true; 431 return mAppas; 432 } 433 434 template<> get()435 inline CellRefList<ESM::Armor>& CellStore::get<ESM::Armor>() 436 { 437 mHasState = true; 438 return mArmors; 439 } 440 441 template<> get()442 inline CellRefList<ESM::Book>& CellStore::get<ESM::Book>() 443 { 444 mHasState = true; 445 return mBooks; 446 } 447 448 template<> get()449 inline CellRefList<ESM::Clothing>& CellStore::get<ESM::Clothing>() 450 { 451 mHasState = true; 452 return mClothes; 453 } 454 455 template<> get()456 inline CellRefList<ESM::Container>& CellStore::get<ESM::Container>() 457 { 458 mHasState = true; 459 return mContainers; 460 } 461 462 template<> get()463 inline CellRefList<ESM::Creature>& CellStore::get<ESM::Creature>() 464 { 465 mHasState = true; 466 return mCreatures; 467 } 468 469 template<> get()470 inline CellRefList<ESM::Door>& CellStore::get<ESM::Door>() 471 { 472 mHasState = true; 473 return mDoors; 474 } 475 476 template<> get()477 inline CellRefList<ESM::Ingredient>& CellStore::get<ESM::Ingredient>() 478 { 479 mHasState = true; 480 return mIngreds; 481 } 482 483 template<> get()484 inline CellRefList<ESM::CreatureLevList>& CellStore::get<ESM::CreatureLevList>() 485 { 486 mHasState = true; 487 return mCreatureLists; 488 } 489 490 template<> get()491 inline CellRefList<ESM::ItemLevList>& CellStore::get<ESM::ItemLevList>() 492 { 493 mHasState = true; 494 return mItemLists; 495 } 496 497 template<> get()498 inline CellRefList<ESM::Light>& CellStore::get<ESM::Light>() 499 { 500 mHasState = true; 501 return mLights; 502 } 503 504 template<> get()505 inline CellRefList<ESM::Lockpick>& CellStore::get<ESM::Lockpick>() 506 { 507 mHasState = true; 508 return mLockpicks; 509 } 510 511 template<> get()512 inline CellRefList<ESM::Miscellaneous>& CellStore::get<ESM::Miscellaneous>() 513 { 514 mHasState = true; 515 return mMiscItems; 516 } 517 518 template<> get()519 inline CellRefList<ESM::NPC>& CellStore::get<ESM::NPC>() 520 { 521 mHasState = true; 522 return mNpcs; 523 } 524 525 template<> get()526 inline CellRefList<ESM::Probe>& CellStore::get<ESM::Probe>() 527 { 528 mHasState = true; 529 return mProbes; 530 } 531 532 template<> get()533 inline CellRefList<ESM::Repair>& CellStore::get<ESM::Repair>() 534 { 535 mHasState = true; 536 return mRepairs; 537 } 538 539 template<> get()540 inline CellRefList<ESM::Static>& CellStore::get<ESM::Static>() 541 { 542 mHasState = true; 543 return mStatics; 544 } 545 546 template<> get()547 inline CellRefList<ESM::Weapon>& CellStore::get<ESM::Weapon>() 548 { 549 mHasState = true; 550 return mWeapons; 551 } 552 553 template<> get()554 inline CellRefList<ESM::BodyPart>& CellStore::get<ESM::BodyPart>() 555 { 556 mHasState = true; 557 return mBodyParts; 558 } 559 560 bool operator== (const CellStore& left, const CellStore& right); 561 bool operator!= (const CellStore& left, const CellStore& right); 562 } 563 564 #endif 565