diff --git a/esm/defs.hpp b/esm/defs.hpp index a9af4cd30..52940f812 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 44d8e3498..8a72026ac 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