From 5d2394a78b15feba0073adab6049f216b2231d22 Mon Sep 17 00:00:00 2001
From: Nicolay Korslund <korslund@gmail.com>
Date: Thu, 4 Mar 2010 14:12:23 +0100
Subject: [PATCH] Finished cell loading code in esm/loadcell.hpp

---
 esm/defs.hpp     |  11 +++
 esm/loadcell.hpp | 187 +++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 184 insertions(+), 14 deletions(-)

diff --git a/esm/defs.hpp b/esm/defs.hpp
index a9af4cd30f..52940f812e 100644
--- a/esm/defs.hpp
+++ b/esm/defs.hpp
@@ -5,6 +5,9 @@
 
 namespace ESM {
 
+// Pixel color value. Standard four-byte rr,gg,bb,aa format.
+typedef int32_t Color;
+
 enum VarType
   {
     VT_Unknown,
@@ -43,6 +46,14 @@ struct SpellList
 */
 #pragma pack(push)
 #pragma pack(1)
+
+// Position and rotation
+struct Position
+{
+  float pos[3];
+  float rot[3];
+};
+
 struct ENAMstruct
 {
   // Magical effect, hard-coded ID
diff --git a/esm/loadcell.hpp b/esm/loadcell.hpp
index 44d8e34987..8a72026ac0 100644
--- a/esm/loadcell.hpp
+++ b/esm/loadcell.hpp
@@ -2,9 +2,77 @@
 #define _ESM_CELL_H
 
 #include "esm_reader.hpp"
+#include "defs.hpp"
 
 namespace ESM {
 
+/* Cell reference. This represents ONE object (of many) inside the
+   cell. The cell references are not loaded as part of the normal
+   loading process, but are rather loaded later on demand when we are
+   setting up a specific cell.
+ */
+struct CellRef
+{
+  int refnum;           // Reference number
+  std::string refID;    // ID of object being referenced
+
+  float scale;          // Scale applied to mesh
+
+  // The NPC that owns this object (and will get angry if you steal
+  // it)
+  std::string owner;
+
+  // I have no idea, looks like a link to a global variable?
+  std::string glob;
+
+  // ID of creature trapped in this soul gem (?)
+  std::string soul;
+
+  // ?? CNAM has a faction name, might be for objects/beds etc
+  // belonging to a faction.
+  std::string faction;
+
+  // INDX might be PC faction rank required to use the item? Sometimes
+  // is -1, which I assume means "any rank".
+  int factIndex;
+
+  // Depends on context - possibly weapon health, number of uses left
+  // or weapon magic charge?
+  float charge;
+
+  // I have no idea, these are present some times, often along with
+  // owner (ANAM) and sometimes otherwise. Is NAM9 is always 1?  INTV
+  // is usually one, but big for lights. Perhaps something to do with
+  // remaining light "charge". I haven't tried reading it as a float
+  // in those cases.
+  int intv, nam9;
+
+  // For doors - true if this door teleports to somewhere else, false
+  // if it should open through animation.
+  bool teleport;
+
+  // Teleport location for the door, if this is a teleporting door.
+  Position doorDest;
+
+  // Destination cell for doors (optional)
+  std::string destCell;
+
+  // Lock level for doors and containers
+  int lockLevel;
+  std::string key, trap; // Key and trap ID names, if any
+
+  // No idea - occurs ONCE in Morrowind.esm, for an activator
+  char unam;
+
+  // Occurs in Tribunal.esm, eg. in the cell "Mournhold, Plaza
+  // Brindisi Dorom", where it has the value 100. Also only for
+  // activators.
+  int fltv;
+
+  // Position and rotation of this object within the cell
+  Position pos;
+};
+
 /* Cells hold data about objects, creatures, statics (rocks, walls,
    buildings) and landscape (for exterior cells). Cells frequently
    also has other associated LAND and PGRD records. Combined, all this
@@ -12,12 +80,7 @@ namespace ESM {
    the strategy we use is to remember the file position of each cell
    (using ESMReader::getContext()) and jumping back into place
    whenever we need to load a given cell.
-
-   TODO: We should handle the actual cell content loading in this file
-   too, although the solution should be as open as possible and let
-   the user handle all data storage.
  */
-
 struct Cell
 {
   enum Flags
@@ -35,34 +98,130 @@ struct Cell
     int gridX, gridY;
   };
 
+  struct AMBIstruct
+  {
+    Color ambient, sunlight, fog;
+    float fogDensity;
+  };
+
   // Interior cells are indexed by this (it's the 'id'), for exterior
   // cells it is optional.
   std::string name,
 
-  // Optional region name for exterior cells.
+  // Optional region name for exterior and quasi-exterior cells.
     region;
 
-  // File position
-  ESM_Context context;
-
+  ESM_Context context; // File position
   DATAstruct data;
+  AMBIstruct ambi;
+  int water; // Water level
+  int mapColor;
 
   void load(ESMReader &esm)
   {
-    // This is implicit?
-    //name = esm.getHNString("NAME");
-
     // Ignore this for now, it might mean we should delete the entire
     // cell?
     if(esm.isNextSub("DELE")) esm.skipHSub();
 
     esm.getHNT(data, "DATA", 12);
-    region = esm.getHNOString("RGNN");
 
-    // Save position and move on
+    // Water level
+    water = 0;
+
+    if(data.flags & Interior)
+      {
+        // Interior cells
+
+        if(esm.isNextSub("INTV") || esm.isNextSub("WHGT"))
+          esm.getHT(water);
+
+        // Quasi-exterior cells have a region (which determines the
+        // weather), pure interior cells have ambient lighting
+        // instead.
+        if(data.flags & QuasiEx)
+          region = esm.getHNOString("RGNN");
+        else
+          esm.getHNT(ambi, "AMBI", 16);
+      }
+    else
+      {
+        // Exterior cells
+        region = esm.getHNOString("RGNN");
+        esm.getHNOT(mapColor, "NAM5");
+      }
+
+    // Save position of the cell references and move on
     context = esm.getContext();
     esm.skipRecord();
   }
+
+  // Restore the given reader to the stored position. Will try to open
+  // the file matching the stored file name. If you want to read from
+  // somewhere other than the file system, you need to pre-open the
+  // ESMReader, and the filename must match the stored filename
+  // exactly.
+  void restore(ESMReader &esm)
+  { esm.restoreContext(context); }
+
+  // Get the next reference in this cell, if any. Returns false when
+  // there are no more references in the cell.
+  bool getNextRef(ESMReader &esm, CellRef &ref)
+  {
+    if(!esm.hasMoreSubs()) return false;
+
+    // Number of references in the cell? Maximum once in each cell,
+    // but not always at the beginning, and not always right. In other
+    // words, completely useless.
+    {
+      int i;
+      esm.getHNOT(i, "NAM0");
+    }
+
+    esm.getHNT(ref.refnum, "FRMR");
+    ref.refID = esm.getHNString("NAME");
+
+    // getHNOT will not change the existing value (1.0) if the
+    // subrecord is missing
+    ref.scale = 1.0;
+    esm.getHNOT(ref.scale, "XSCL");
+
+    ref.owner = esm.getHNOString("ANAM");
+    ref.glob = esm.getHNOString("BNAM");
+    ref.soul = esm.getHNOString("XSOL");
+
+    ref.faction = esm.getHNOString("CNAM");
+    esm.getHNOT(ref.factIndex, "INDX");
+
+    ref.charge = 0.0;
+    esm.getHNOT(ref.charge, "XCHG");
+
+    ref.intv = 0;
+    ref.nam9 = 0;
+    esm.getHNOT(ref.intv, "INTV");
+    esm.getHNOT(ref.nam9, "NAM9");
+
+    // Present for doors that teleport you to another cell.
+    if(esm.isNextSub("DODT"))
+      {
+        ref.teleport = true;
+        esm.getHT(ref.doorDest);
+        ref.destCell = esm.getHNOString("DNAM");
+      }
+    else ref.teleport = false;
+
+    esm.getHNOT(ref.lockLevel, "FLTV"); // int, despite the name
+    ref.key = esm.getHNOString("KNAM");
+    ref.trap = esm.getHNOString("TNAM");
+
+    ref.unam = 0;
+    ref.fltv = 0;
+    esm.getHNOT(ref.unam, "UNAM");
+    esm.getHNOT(ref.fltv, "FLTV");
+
+    esm.getHNT(ref.pos, "DATA", 24);
+
+    return true;
+  }
 };
 }
 #endif