1 #ifndef OPENMW_ESM_CELL_H
2 #define OPENMW_ESM_CELL_H
3 
4 #include <string>
5 #include <vector>
6 #include <list>
7 
8 #include "esmcommon.hpp"
9 #include "defs.hpp"
10 #include "cellref.hpp"
11 #include "cellid.hpp"
12 
13 namespace MWWorld
14 {
15     class ESMStore;
16 }
17 
18 namespace ESM
19 {
20 class ESMReader;
21 class ESMWriter;
22 
23 /* Moved cell reference tracking object. This mainly stores the target cell
24         of the reference, so we can easily know where it has been moved when another
25         plugin tries to move it independently.
26     Unfortunately, we need to implement this here.
27     */
28 class MovedCellRef
29 {
30 public:
31     RefNum mRefNum;
32 
33     // Coordinates of target exterior cell
34     int mTarget[2];
35 
36     // The content file format does not support moving objects to an interior cell.
37     // The save game format does support moving to interior cells, but uses a different mechanism
38     // (see the MovedRefTracker implementation in MWWorld::CellStore for more details).
39 };
40 
41 /// Overloaded compare operator used to search inside a list of cell refs.
42 bool operator==(const MovedCellRef& ref, const RefNum& refNum);
43 bool operator==(const CellRef& ref, const RefNum& refNum);
44 
45 typedef std::list<MovedCellRef> MovedCellRefTracker;
46 typedef std::list<std::pair<CellRef, bool> > CellRefTracker;
47 
48 struct CellRefTrackerPredicate
49 {
50     RefNum mRefNum;
51 
CellRefTrackerPredicateESM::CellRefTrackerPredicate52     CellRefTrackerPredicate(const RefNum& refNum) : mRefNum(refNum) {}
operator ()ESM::CellRefTrackerPredicate53     bool operator() (const std::pair<CellRef, bool>& refdelPair) { return refdelPair.first == mRefNum; }
54 };
55 
56 /* Cells hold data about objects, creatures, statics (rocks, walls,
57    buildings) and landscape (for exterior cells). Cells frequently
58    also has other associated LAND and PGRD records. Combined, all this
59    data can be huge, and we cannot load it all at startup. Instead,
60    the strategy we use is to remember the file position of each cell
61    (using ESMReader::getContext()) and jumping back into place
62    whenever we need to load a given cell.
63  */
64 struct Cell
65 {
66     static unsigned int sRecordId;
67     /// Return a string descriptor for this record type. Currently used for debugging / error logs only.
getRecordTypeESM::Cell68     static std::string getRecordType() { return "Cell"; }
69 
70   enum Flags
71     {
72       Interior  = 0x01, // Interior cell
73       HasWater  = 0x02, // Does this cell have a water surface
74       NoSleep   = 0x04, // Is it allowed to sleep here (without a bed)
75       QuasiEx   = 0x80  // Behave like exterior (Tribunal+), with
76                         // skybox and weather
77     };
78 
79   struct DATAstruct
80   {
81       int mFlags {0};
82       int mX {0}, mY {0};
83   };
84 
85   struct AMBIstruct
86   {
87       Color mAmbient {0}, mSunlight {0}, mFog {0};
88       float mFogDensity {0.f};
89   };
90 
CellESM::Cell91   Cell() : mName(""),
92            mRegion(""),
93            mHasAmbi(true),
94            mWater(0),
95            mWaterInt(false),
96            mMapColor(0),
97            mRefNumCounter(0)
98   {}
99 
100   // Interior cells are indexed by this (it's the 'id'), for exterior
101   // cells it is optional.
102   std::string mName;
103 
104   // Optional region name for exterior and quasi-exterior cells.
105   std::string mRegion;
106 
107   std::vector<ESM_Context> mContextList; // File position; multiple positions for multiple plugin support
108   DATAstruct mData;
109   CellId mCellId;
110 
111   AMBIstruct mAmbi;
112   bool mHasAmbi;
113 
114   float mWater; // Water level
115   bool mWaterInt;
116   int mMapColor;
117   // Counter for RefNums. This is only used during content file editing and has no impact on gameplay.
118   // It prevents overwriting previous refNums, even if they were deleted.
119   // as that would collide with refs when a content file is upgraded.
120   int mRefNumCounter;
121 
122   // References "leased" from another cell (i.e. a different cell
123   //  introduced this ref, and it has been moved here by a plugin)
124   CellRefTracker mLeasedRefs;
125   MovedCellRefTracker mMovedRefs;
126 
127   void postLoad(ESMReader &esm);
128 
129   // This method is left in for compatibility with esmtool. Parsing moved references currently requires
130   //  passing ESMStore, bit it does not know about this parameter, so we do it this way.
131   void load(ESMReader &esm, bool &isDeleted, bool saveContext = true); // Load everything (except references)
132   void loadNameAndData(ESMReader &esm, bool &isDeleted); // Load NAME and DATAstruct
133   void loadCell(ESMReader &esm, bool saveContext = true); // Load everything, except NAME, DATAstruct and references
134 
135   void save(ESMWriter &esm, bool isDeleted = false) const;
136 
isExteriorESM::Cell137   bool isExterior() const
138   {
139       return !(mData.mFlags & Interior);
140   }
141 
getGridXESM::Cell142   int getGridX() const
143   {
144       return mData.mX;
145   }
146 
getGridYESM::Cell147   int getGridY() const
148   {
149       return mData.mY;
150   }
151 
hasWaterESM::Cell152   bool hasWater() const
153   {
154       return ((mData.mFlags&HasWater) != 0) || isExterior();
155   }
156 
hasAmbientESM::Cell157   bool hasAmbient() const
158   {
159       return mHasAmbi;
160   }
161 
setHasAmbientESM::Cell162   void setHasAmbient(bool hasAmbi)
163   {
164       mHasAmbi = hasAmbi;
165   }
166 
167   // Restore the given reader to the stored position. Will try to open
168   // the file matching the stored file name. If you want to read from
169   // somewhere other than the file system, you need to pre-open the
170   // ESMReader, and the filename must match the stored filename
171   // exactly.
172   void restore(ESMReader &esm, int iCtx) const;
173 
174   std::string getDescription() const;
175   ///< Return a short string describing the cell (mostly used for debugging/logging purpose)
176 
177   /* Get the next reference in this cell, if any. Returns false when
178      there are no more references in the cell.
179 
180      All fields of the CellRef struct are overwritten. You can safely
181      reuse one memory location without blanking it between calls.
182   */
183   /// \param ignoreMoves ignore MVRF record and read reference like a regular CellRef.
184   static bool getNextRef(ESMReader &esm,
185                          CellRef &ref,
186                          bool &isDeleted,
187                          bool ignoreMoves = false,
188                          MovedCellRef *mref = nullptr);
189 
190   /* This fetches an MVRF record, which is used to track moved references.
191    * Since they are comparably rare, we use a separate method for this.
192    */
193   static bool getNextMVRF(ESMReader &esm, MovedCellRef &mref);
194 
195     void blank();
196     ///< Set record to default state (does not touch the ID/index).
197 
198     const CellId& getCellId() const;
199 };
200 }
201 #endif
202