Merge remote-tracking branch 'scrawl/essimporter'

openmw-35
Marc Zinnschlag 10 years ago
commit cd62dbc5b7

@ -41,51 +41,58 @@ Command line options
--version print version information and quit
--data arg (=data) set data directories (later directories
have higher priority)
--data-local arg set local data directory (highest
--data-local arg set local data directory (highest
priority)
--fallback-archive arg (=fallback-archive)
set fallback BSA archives (later
set fallback BSA archives (later
archives have higher priority)
--resources arg (=resources) set resources directory
--start arg (=Beshara) set initial cell
--content arg content file(s): esm/esp, or
--start arg set initial cell
--content arg content file(s): esm/esp, or
omwgame/omwaddon
--anim-verbose [=arg(=1)] (=0) output animation indices files
--no-sound [=arg(=1)] (=0) disable all sounds
--script-verbose [=arg(=1)] (=0) verbose script output
--script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue
scripts) at startup
--script-console [=arg(=1)] (=0) enable console-only script
--script-all-dialogue [=arg(=1)] (=0) compile all dialogue scripts at startup
--script-console [=arg(=1)] (=0) enable console-only script
functionality
--script-run arg select a file containing a list of
console commands that is executed on
--script-run arg select a file containing a list of
console commands that is executed on
startup
--script-warn [=arg(=1)] (=1) handling of warnings when compiling
--script-warn [=arg(=1)] (=1) handling of warnings when compiling
scripts
0 - ignore warning
1 - show warning but consider script as
correctly compiled anyway
2 - treat warnings as errors
--script-blacklist arg ignore the specified script (if the use
of the blacklist is enabled)
--script-blacklist-use [=arg(=1)] (=1)
enable script blacklisting
--load-savegame arg load a save game file on game startup
--skip-menu [=arg(=1)] (=0) skip main menu on game startup
--new-game [=arg(=1)] (=0) run new game sequence (ignored if
--new-game [=arg(=1)] (=0) run new game sequence (ignored if
skip-menu=0)
--fs-strict [=arg(=1)] (=0) strict file system handling (no case
--fs-strict [=arg(=1)] (=0) strict file system handling (no case
folding)
--encoding arg (=win1252) Character encoding used in OpenMW game
--encoding arg (=win1252) Character encoding used in OpenMW game
messages:
win1250 - Central and Eastern European
such as Polish, Czech, Slovak,
Hungarian, Slovene, Bosnian, Croatian,
Serbian (Latin script), Romanian and
win1250 - Central and Eastern European
such as Polish, Czech, Slovak,
Hungarian, Slovene, Bosnian, Croatian,
Serbian (Latin script), Romanian and
Albanian languages
win1251 - Cyrillic alphabet such as
Russian, Bulgarian, Serbian Cyrillic
win1251 - Cyrillic alphabet such as
Russian, Bulgarian, Serbian Cyrillic
and other languages
win1252 - Western European (Latin)
win1252 - Western European (Latin)
alphabet, used by default
--fallback arg fallback values
--no-grab Don't grab mouse cursor
--export-fonts [=arg(=1)] (=0) Export Morrowind .fnt fonts to PNG
image and XML file in current directory
--activate-dist arg (=-1) activation distance override

@ -261,7 +261,7 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
std::cout << " Faction: '" << ref.mFaction << "'" << std::endl;
std::cout << " Faction rank: '" << ref.mFactionRank << "'" << std::endl;
std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n";
std::cout << " Uses/health: '" << ref.mCharge << "'\n";
std::cout << " Uses/health: '" << ref.mChargeInt << "'\n";
std::cout << " Gold value: '" << ref.mGoldValue << "'\n";
std::cout << " Blocked: '" << static_cast<int>(ref.mReferenceBlocked) << "'" << std::endl;
std::cout << " Deleted: " << deleted << std::endl;

@ -8,10 +8,14 @@ set(ESSIMPORTER_FILES
importacdt.cpp
importinventory.cpp
importklst.cpp
importcntc.cpp
importercontext.cpp
converter.cpp
convertacdt.cpp
convertnpcc.cpp
convertinventory.cpp
convertcrec.cpp
convertcntc.cpp
)
add_executable(openmw-essimporter

@ -27,6 +27,8 @@ namespace ESSImport
cStats.mAttributes[i].mMod = acdt.mAttributes[i][0];
cStats.mAttributes[i].mCurrent = acdt.mAttributes[i][0];
}
cStats.mGoldPool = acdt.mGoldPool;
cStats.mTalkedTo = acdt.mFlags & TalkedToPlayer;
}
void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats)
@ -37,6 +39,8 @@ namespace ESSImport
npcStats.mSkills[i].mRegular.mCurrent = actorData.mSkills[i][1];
npcStats.mSkills[i].mRegular.mBase = actorData.mSkills[i][0];
}
npcStats.mTimeToStartDrowning = actorData.mACDT.mBreathMeter;
}
}

@ -0,0 +1,13 @@
#include "convertcntc.hpp"
#include "convertinventory.hpp"
namespace ESSImport
{
void convertCNTC(const CNTC &cntc, ESM::ContainerState &state)
{
convertInventory(cntc.mInventory, state.mInventory);
}
}

@ -0,0 +1,15 @@
#ifndef OPENMW_ESSIMPORT_CONVERTCNTC_H
#define OPENMW_ESSIMPORT_CONVERTCNTC_H
#include "importcntc.hpp"
#include <components/esm/containerstate.hpp>
namespace ESSImport
{
void convertCNTC(const CNTC& cntc, ESM::ContainerState& state);
}
#endif

@ -0,0 +1,13 @@
#include "convertcrec.hpp"
#include "convertinventory.hpp"
namespace ESSImport
{
void convertCREC(const CREC &crec, ESM::CreatureState &state)
{
convertInventory(crec.mInventory, state.mInventory);
}
}

@ -0,0 +1,15 @@
#ifndef OPENMW_ESSIMPORT_CONVERTCREC_H
#define OPENMW_ESSIMPORT_CONVERTCREC_H
#include "importcrec.hpp"
#include <components/esm/creaturestate.hpp>
namespace ESSImport
{
void convertCREC(const CREC& crec, ESM::CreatureState& state);
}
#endif

@ -1,8 +1,14 @@
#include "converter.hpp"
#include <stdexcept>
#include <OgreImage.h>
#include <components/esm/creaturestate.hpp>
#include <components/esm/containerstate.hpp>
#include "convertcrec.hpp"
#include "convertcntc.hpp"
namespace
{
@ -15,6 +21,24 @@ namespace
screenshot.save(out);
}
void convertCellRef(const ESSImport::CellRef& cellref, ESM::ObjectState& objstate)
{
objstate.mEnabled = cellref.mEnabled;
objstate.mPosition = cellref.mPos;
objstate.mRef.mRefNum = cellref.mRefNum;
}
bool isIndexedRefId(const std::string& indexedRefId)
{
if (indexedRefId.size() <= 8)
return false;
std::string index = indexedRefId.substr(indexedRefId.size()-8);
if(index.find_first_not_of("0123456789ABCDEF") == std::string::npos )
return true;
return false;
}
}
namespace ESSImport
@ -114,8 +138,9 @@ namespace ESSImport
ref.load (esm);
if (esm.isNextSub("DELE"))
{
// TODO
// strangely this can be e.g. 52 instead of just 1,
std::cout << "deleted ref " << ref.mIndexedRefId << std::endl;
//std::cout << "deleted ref " << ref.mIndexedRefId << std::endl;
esm.skipHSub();
}
cellrefs.push_back(ref);
@ -153,83 +178,128 @@ namespace ESSImport
newcell.mRefs = cellrefs;
// FIXME: map by ID for exterior cells
mCells[id] = newcell;
if (cell.isExterior())
mExtCells[std::make_pair(cell.mData.mX, cell.mData.mY)] = newcell;
else
mIntCells[id] = newcell;
}
void ConvertCell::write(ESM::ESMWriter &esm)
void ConvertCell::writeCell(const Cell &cell, ESM::ESMWriter& esm)
{
for (std::map<std::string, Cell>::const_iterator it = mCells.begin(); it != mCells.end(); ++it)
ESM::Cell esmcell = cell.mCell;
esm.startRecord(ESM::REC_CSTA);
ESM::CellState csta;
csta.mHasFogOfWar = 0;
csta.mId = esmcell.getCellId();
csta.mId.save(esm);
// TODO csta.mLastRespawn;
// shouldn't be needed if we respawn on global schedule like in original MW
csta.mWaterLevel = esmcell.mWater;
csta.save(esm);
for (std::vector<CellRef>::const_iterator refIt = cell.mRefs.begin(); refIt != cell.mRefs.end(); ++refIt)
{
const ESM::Cell& cell = it->second.mCell;
esm.startRecord(ESM::REC_CSTA);
ESM::CellState csta;
csta.mHasFogOfWar = 0;
csta.mId = cell.getCellId();
csta.mId.save(esm);
// TODO csta.mLastRespawn;
// shouldn't be needed if we respawn on global schedule like in original MW
csta.mWaterLevel = cell.mWater;
csta.save(esm);
for (std::vector<CellRef>::const_iterator refIt = it->second.mRefs.begin(); refIt != it->second.mRefs.end(); ++refIt)
{
const CellRef& cellref = *refIt;
ESM::CellRef out;
out.blank();
const CellRef& cellref = *refIt;
ESM::CellRef out;
out.blank();
if (cellref.mIndexedRefId.size() < 8)
{
std::cerr << "CellRef with no index?" << std::endl;
continue;
}
if (!isIndexedRefId(cellref.mIndexedRefId))
{
// non-indexed RefNum, i.e. no CREC/NPCC/CNTC record associated with it
// this could be any type of object really (even creatures/npcs too)
out.mRefID = cellref.mIndexedRefId;
std::string idLower = Misc::StringUtils::lowerCase(out.mRefID);
ESM::ObjectState objstate;
objstate.blank();
objstate.mRef = out;
objstate.mRef.mRefID = idLower;
objstate.mHasCustomState = false;
convertCellRef(cellref, objstate);
esm.writeHNT ("OBJE", 0);
objstate.save(esm);
continue;
}
else
{
std::stringstream stream;
stream << cellref.mIndexedRefId.substr(cellref.mIndexedRefId.size()-8,8);
stream << std::hex << cellref.mIndexedRefId.substr(cellref.mIndexedRefId.size()-8,8);
int refIndex;
stream >> refIndex;
out.mRefID = cellref.mIndexedRefId.substr(0,cellref.mIndexedRefId.size()-8);
std::string idLower = Misc::StringUtils::lowerCase(out.mRefID);
std::map<std::pair<int, std::string>, CREC>::const_iterator crecIt = mContext->mCreatureChanges.find(
std::map<std::pair<int, std::string>, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find(
std::make_pair(refIndex, out.mRefID));
if (crecIt != mContext->mCreatureChanges.end())
if (npccIt != mContext->mNpcChanges.end())
{
ESM::CreatureState objstate;
ESM::NpcState objstate;
objstate.blank();
objstate.mRef = out;
objstate.mRef.mRefID = idLower;
// probably need more micromanagement here so we don't overwrite values
// from the ESM with default values
convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats);
objstate.mEnabled = cellref.mEnabled;
objstate.mPosition = cellref.mPos;
convertNpcData(cellref.mActorData, objstate.mNpcStats);
convertNPCC(npccIt->second, objstate);
convertCellRef(cellref, objstate);
esm.writeHNT ("OBJE", ESM::REC_NPC_);
objstate.save(esm);
continue;
}
std::map<std::pair<int, std::string>, CNTC>::const_iterator cntcIt = mContext->mContainerChanges.find(
std::make_pair(refIndex, out.mRefID));
if (cntcIt != mContext->mContainerChanges.end())
{
ESM::ContainerState objstate;
objstate.blank();
objstate.mRef = out;
objstate.mRef.mRefNum = cellref.mRefNum;
// FIXME: change save format to not require object type, instead look up it up using the RefId
esm.writeHNT ("OBJE", ESM::REC_CREA);
objstate.mRef.mRefID = idLower;
convertCNTC(cntcIt->second, objstate);
convertCellRef(cellref, objstate);
esm.writeHNT ("OBJE", ESM::REC_CONT);
objstate.save(esm);
continue;
}
std::map<std::pair<int, std::string>, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find(
std::map<std::pair<int, std::string>, CREC>::const_iterator crecIt = mContext->mCreatureChanges.find(
std::make_pair(refIndex, out.mRefID));
if (npccIt != mContext->mNpcChanges.end())
if (crecIt != mContext->mCreatureChanges.end())
{
ESM::NpcState objstate;
ESM::CreatureState objstate;
objstate.blank();
convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats);
convertNpcData(cellref.mActorData, objstate.mNpcStats);
objstate.mEnabled = cellref.mEnabled;
objstate.mPosition = cellref.mPos;
objstate.mRef = out;
objstate.mRef.mRefNum = cellref.mRefNum;
esm.writeHNT ("OBJE", ESM::REC_NPC_);
objstate.mRef.mRefID = idLower;
convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats);
// probably need more micromanagement here so we don't overwrite values
// from the ESM with default values
convertCREC(crecIt->second, objstate);
convertCellRef(cellref, objstate);
esm.writeHNT ("OBJE", ESM::REC_CREA);
objstate.save(esm);
continue;
}
std::cerr << "Can't find type for " << refIndex << " " << out.mRefID << std::endl;
std::stringstream error;
error << "Can't find type for " << cellref.mIndexedRefId << std::endl;
throw std::runtime_error(error.str());
}
esm.endRecord(ESM::REC_CSTA);
}
esm.endRecord(ESM::REC_CSTA);
}
void ConvertCell::write(ESM::ESMWriter &esm)
{
for (std::map<std::string, Cell>::const_iterator it = mIntCells.begin(); it != mIntCells.end(); ++it)
writeCell(it->second, esm);
for (std::map<std::pair<int, int>, Cell>::const_iterator it = mExtCells.begin(); it != mExtCells.end(); ++it)
writeCell(it->second, esm);
for (std::vector<ESM::CustomMarker>::const_iterator it = mMarkers.begin(); it != mMarkers.end(); ++it)
{
esm.startRecord(ESM::REC_MARK);

@ -9,9 +9,12 @@
#include <components/esm/loadclas.hpp>
#include <components/esm/loadglob.hpp>
#include <components/esm/cellstate.hpp>
#include <components/esm/loadfact.hpp>
#include <components/esm/dialoguestate.hpp>
#include <components/esm/custommarkerstate.hpp>
#include "importcrec.hpp"
#include "importcntc.hpp"
#include "importercontext.hpp"
#include "importcellref.hpp"
@ -94,7 +97,8 @@ public:
mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel;
mContext->mPlayerBase = npc;
std::map<const int, float> empty;
// FIXME: not working?
// FIXME: player start spells, racial spells and birthsign spells aren't listed here,
// need to fix openmw to account for this
for (std::vector<std::string>::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it)
mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty;
}
@ -165,7 +169,10 @@ public:
convertNPCC(npcc, mContext->mPlayer.mObject);
}
else
mContext->mNpcChanges.insert(std::make_pair(std::make_pair(npcc.mIndex,id), npcc));
{
int index = npcc.mNPDT.mIndex;
mContext->mNpcChanges.insert(std::make_pair(std::make_pair(index,id), npcc)).second;
}
}
};
@ -205,10 +212,30 @@ public:
faction.mReputation = it->mReputation;
mContext->mPlayer.mObject.mNpcStats.mFactions[it->mFactionName.toString()] = faction;
}
for (int i=0; i<8; ++i)
mContext->mPlayer.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i];
mContext->mPlayer.mObject.mNpcStats.mLevelProgress = pcdt.mPNAM.mLevelProgress;
for (std::vector<std::string>::const_iterator it = pcdt.mKnownDialogueTopics.begin();
it != pcdt.mKnownDialogueTopics.end(); ++it)
{
mContext->mDialogueState.mKnownTopics.push_back(Misc::StringUtils::lowerCase(*it));
}
}
};
class ConvertCNTC : public Converter
{
virtual void read(ESM::ESMReader &esm)
{
std::string id = esm.getHNString("NAME");
CNTC cntc;
cntc.load(esm);
mContext->mContainerChanges.insert(std::make_pair(std::make_pair(cntc.mIndex,id), cntc));
}
};
class ConvertCREC : public Converter
{
public:
@ -217,7 +244,6 @@ public:
std::string id = esm.getHNString("NAME");
CREC crec;
crec.load(esm);
mContext->mCreatureChanges.insert(std::make_pair(std::make_pair(crec.mIndex,id), crec));
}
};
@ -242,9 +268,12 @@ private:
std::vector<unsigned int> mFogOfWar;
};
std::map<std::string, Cell> mCells;
std::map<std::string, Cell> mIntCells;
std::map<std::pair<int, int>, Cell> mExtCells;
std::vector<ESM::CustomMarker> mMarkers;
void writeCell(const Cell& cell, ESM::ESMWriter &esm);
};
class ConvertKLST : public Converter
@ -274,6 +303,48 @@ private:
std::map<std::string, int> mKillCounter;
};
class ConvertFACT : public Converter
{
public:
virtual void read(ESM::ESMReader& esm)
{
std::string id = esm.getHNString("NAME");
ESM::Faction faction;
faction.load(esm);
Misc::StringUtils::toLower(id);
for (std::map<std::string, int>::const_iterator it = faction.mReactions.begin(); it != faction.mReactions.end(); ++it)
{
std::string faction2 = Misc::StringUtils::lowerCase(it->first);
mContext->mDialogueState.mChangedFactionReaction[id].insert(std::make_pair(faction2, it->second));
}
}
};
/// Stolen items
class ConvertSTLN : public Converter
{
public:
virtual void read(ESM::ESMReader &esm)
{
std::string itemid = esm.getHNString("NAME");
while (esm.isNextSub("ONAM"))
{
std::string ownerid = esm.getHString();
mStolenItems.insert(std::make_pair(itemid, ownerid));
}
while (esm.isNextSub("FNAM"))
{
std::string factionid = esm.getHString();
mFactionStolenItems.insert(std::make_pair(itemid, factionid));
}
}
private:
std::multimap<std::string, std::string> mStolenItems;
std::multimap<std::string, std::string> mFactionStolenItems;
};
}
#endif

@ -0,0 +1,23 @@
#include "convertinventory.hpp"
#include <components/misc/stringops.hpp>
namespace ESSImport
{
void convertInventory(const Inventory &inventory, ESM::InventoryState &state)
{
for (std::vector<Inventory::InventoryItem>::const_iterator it = inventory.mItems.begin();
it != inventory.mItems.end(); ++it)
{
ESM::ObjectState objstate;
objstate.blank();
objstate.mRef = *it;
objstate.mRef.mRefID = Misc::StringUtils::lowerCase(it->mId);
objstate.mCount = std::abs(it->mCount); // restocking items have negative count in the savefile
// openmw handles them differently, so no need to set any flags
state.mItems.push_back(std::make_pair(objstate, it->mRelativeEquipmentSlot));
}
}
}

@ -0,0 +1,15 @@
#ifndef OPENMW_ESSIMPORT_CONVERTINVENTORY_H
#define OPENMW_ESSIMPORT_CONVERTINVENTORY_H
#include "importinventory.hpp"
#include <components/esm/inventorystate.hpp>
namespace ESSImport
{
void convertInventory (const Inventory& inventory, ESM::InventoryState& state);
}
#endif

@ -1,10 +1,15 @@
#include "convertnpcc.hpp"
#include "convertinventory.hpp"
namespace ESSImport
{
void convertNPCC(const NPCC &npcc, ESM::NpcState &npcState)
{
npcState.mNpcStats.mDisposition = npcc.mNPDT.mDisposition;
npcState.mNpcStats.mReputation = npcc.mNPDT.mReputation;
convertInventory(npcc.mInventory, npcState.mInventory);
}
}

@ -22,7 +22,7 @@ namespace ESSImport
ESM::CellRef bla;
bla.ESM::CellRef::loadData(esm);
// FIXME: actually should be required for all actors?, but ActorData is currently in base CellRef
// FIXME: not all actors have this, add flag
esm.getHNOT(mACDT, "ACDT");
ACSC acsc;
@ -76,6 +76,7 @@ namespace ESSImport
esm.skipHSub(); // 4 byte, 0
}
// FIXME: not all actors have this, add flag
if (esm.isNextSub("CHRD")) // npc only
esm.getHExact(mSkills, 27*2*sizeof(int));

@ -11,16 +11,31 @@ namespace ESM
namespace ESSImport
{
enum ACDTFlags
{
TalkedToPlayer = 0x4
};
/// Actor data, shared by (at least) REFR and CellRef
#pragma pack(push)
#pragma pack(1)
struct ACDT
{
unsigned char mUnknown1[40];
// Note, not stored at *all*:
// - Level changes are lost on reload, except for the player (there it's in the NPC record).
unsigned char mUnknown[12];
unsigned char mFlags; // ACDTFlags
unsigned char mUnknown1[3];
float mBreathMeter; // Seconds left before drowning
unsigned char mUnknown2[20];
float mDynamic[3][2];
unsigned char mUnknown2[16];
unsigned char mUnknown3[16];
float mAttributes[8][2];
unsigned char mUnknown3[120];
unsigned char mUnknown4[112];
unsigned int mGoldPool;
unsigned char mUnknown5[4];
};
#pragma pack(pop)
struct ActorData
{

@ -18,8 +18,13 @@ namespace ESSImport
mIndexedRefId = esm.getHNString("NAME");
if (esm.isNextSub("LVCR"))
esm.skipHSub();
{
// occurs on leveled creature spawner references
// probably some identifier for the the creature that has been spawned?
unsigned char lvcr;
esm.getHT(lvcr);
//std::cout << "LVCR: " << (int)lvcr << std::endl;
}
mActorData.load(esm);
mEnabled = true;

@ -0,0 +1,16 @@
#include "importcntc.hpp"
#include <components/esm/esmreader.hpp>
namespace ESSImport
{
void CNTC::load(ESM::ESMReader &esm)
{
mIndex = 0;
esm.getHNT(mIndex, "INDX");
mInventory.load(esm);
}
}

@ -0,0 +1,25 @@
#ifndef OPENMW_ESSIMPORT_IMPORTCNTC_H
#define OPENMW_ESSIMPORT_IMPORTCNTC_H
#include "importinventory.hpp"
namespace ESM
{
class ESMReader;
}
namespace ESSImport
{
/// Changed container contents
struct CNTC
{
int mIndex;
Inventory mInventory;
void load(ESM::ESMReader& esm);
};
}
#endif

@ -104,6 +104,11 @@ namespace ESSImport
blacklist.insert(std::make_pair("GLOB", "FLTV")); // gamehour
blacklist.insert(std::make_pair("REFR", "DATA")); // player position
blacklist.insert(std::make_pair("CELL", "NAM8")); // fog of war
blacklist.insert(std::make_pair("GAME", "GMDT")); // weather data, current time always changes
// this changes way too often, name suggests some renderer internal data?
blacklist.insert(std::make_pair("CELL", "ND3D"));
blacklist.insert(std::make_pair("REFR", "ND3D"));
File file1;
read(mEssFile, file1);
@ -181,6 +186,8 @@ namespace ESSImport
Ogre::LogManager logman;
Ogre::Root root;
// TODO: set up encoding on ESMReader based on openmw.cfg / --encoding switch
ESM::ESMReader esm;
esm.open(mEssFile);
@ -193,6 +200,7 @@ namespace ESSImport
const unsigned int recPCDT = ESM::FourCC<'P','C','D','T'>::value;
const unsigned int recFMAP = ESM::FourCC<'F','M','A','P'>::value;
const unsigned int recKLST = ESM::FourCC<'K','L','S','T'>::value;
const unsigned int recSTLN = ESM::FourCC<'S','T','L','N'>::value;
std::map<unsigned int, boost::shared_ptr<Converter> > converters;
converters[ESM::REC_GLOB] = boost::shared_ptr<Converter>(new ConvertGlobal());
@ -200,10 +208,11 @@ namespace ESSImport
converters[ESM::REC_NPC_] = boost::shared_ptr<Converter>(new ConvertNPC());
converters[ESM::REC_NPCC] = boost::shared_ptr<Converter>(new ConvertNPCC());
converters[ESM::REC_CREC] = boost::shared_ptr<Converter>(new ConvertCREC());
converters[recREFR] = boost::shared_ptr<Converter>(new ConvertREFR());
converters[recPCDT] = boost::shared_ptr<Converter>(new ConvertPCDT());
converters[recFMAP] = boost::shared_ptr<Converter>(new ConvertFMAP());
converters[recKLST] = boost::shared_ptr<Converter>(new ConvertKLST());
converters[recREFR ] = boost::shared_ptr<Converter>(new ConvertREFR());
converters[recPCDT ] = boost::shared_ptr<Converter>(new ConvertPCDT());
converters[recFMAP ] = boost::shared_ptr<Converter>(new ConvertFMAP());
converters[recKLST ] = boost::shared_ptr<Converter>(new ConvertKLST());
converters[recSTLN ] = boost::shared_ptr<Converter>(new ConvertSTLN());
converters[ESM::REC_CELL] = boost::shared_ptr<Converter>(new ConvertCell());
converters[ESM::REC_ALCH] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Potion>());
converters[ESM::REC_CLAS] = boost::shared_ptr<Converter>(new ConvertClass());
@ -215,6 +224,8 @@ namespace ESSImport
converters[ESM::REC_WEAP] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Weapon>());
converters[ESM::REC_LEVC] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::CreatureLevList>());
converters[ESM::REC_LEVI] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::ItemLevList>());
converters[ESM::REC_CNTC] = boost::shared_ptr<Converter>(new ConvertCNTC());
converters[ESM::REC_FACT] = boost::shared_ptr<Converter>(new ConvertFACT());
std::set<unsigned int> unknownRecords;
@ -321,6 +332,10 @@ namespace ESSImport
}
context.mPlayer.save(writer);
writer.endRecord(ESM::REC_PLAY);
writer.startRecord (ESM::REC_DIAS);
context.mDialogueState.save(writer);
writer.endRecord(ESM::REC_DIAS);
}

@ -5,9 +5,11 @@
#include <components/esm/loadnpc.hpp>
#include <components/esm/player.hpp>
#include <components/esm/dialoguestate.hpp>
#include "importnpcc.hpp"
#include "importcrec.hpp"
#include "importcntc.hpp"
#include "importplayer.hpp"
@ -24,12 +26,15 @@ namespace ESSImport
ESM::NPC mPlayerBase;
std::string mCustomPlayerClassName;
ESM::DialogueState mDialogueState;
int mDay, mMonth, mYear;
float mHour;
// key <refIndex, refId>
std::map<std::pair<int, std::string>, CREC> mCreatureChanges;
std::map<std::pair<int, std::string>, NPCC> mNpcChanges;
std::map<std::pair<int, std::string>, CNTC> mContainerChanges;
Context()
{

@ -1,7 +1,11 @@
#include "importinventory.hpp"
#include <stdexcept>
#include <components/esm/esmreader.hpp>
#include <components/esm/loadcont.hpp>
namespace ESSImport
{
@ -9,37 +13,66 @@ namespace ESSImport
{
while (esm.isNextSub("NPCO"))
{
InventoryItem item;
item.mId = esm.getHString();
ESM::ContItem contItem;
esm.getHT(contItem);
if (esm.isNextSub("XIDX"))
esm.skipHSub();
InventoryItem item;
item.mId = contItem.mItem.toString();
item.mCount = contItem.mCount;
item.mRelativeEquipmentSlot = -1;
std::string script = esm.getHNOString("SCRI");
// script variables?
// unsure if before or after ESM::CellRef
if (!script.empty())
// seems that a stack of items can have a set of subrecords for each item? rings0000.ess
// doesn't make any sense to me, if the values were different then the items shouldn't stack in the first place?
// I guess we should double check the stacking logic in OpenMW
for (int i=0;i<std::abs(item.mCount);++i)
{
if (esm.isNextSub("SLCS"))
if (esm.isNextSub("XIDX")) // index in the stack?
esm.skipHSub();
if (esm.isNextSub("SLSD")) // Short Data?
esm.skipHSub();
if (esm.isNextSub("SLFD")) // Float Data?
esm.skipHSub();
}
// for XSOL and XCHG seen so far, but probably others too
item.ESM::CellRef::loadData(esm);
std::string script = esm.getHNOString("SCRI");
// script variables?
// unsure if before or after ESM::CellRef
if (!script.empty())
{
if (esm.isNextSub("SLCS"))
esm.skipHSub();
if (esm.isNextSub("SLSD")) // Short Data?
esm.skipHSub();
if (esm.isNextSub("SLFD")) // Float Data?
esm.skipHSub();
}
// for XSOL and XCHG seen so far, but probably others too
item.ESM::CellRef::loadData(esm);
int charge=-1;
esm.getHNOT(charge, "XHLT");
item.mChargeInt = charge;
}
item.mCondition = -1;
esm.getHNOT(item.mCondition, "XHLT");
mItems.push_back(item);
}
// equipped items
while (esm.isNextSub("WIDX"))
{
// equipping?
esm.skipHSub();
// note: same item can be equipped 2 items (e.g. 2 rings)
// and will be *stacked* in the NPCO list, unlike openmw!
// this is currently not handled properly.
esm.getSubHeader();
int itemIndex; // index of the item in the NPCO list
esm.getT(itemIndex);
if (itemIndex < 0 || itemIndex >= int(mItems.size()))
esm.fail("equipment item index out of range");
// appears to be a relative index for only the *possible* slots this item can be equipped in,
// i.e. 0 most of the time
int slotIndex;
esm.getT(slotIndex);
mItems[itemIndex].mRelativeEquipmentSlot = slotIndex;
}
}

@ -19,7 +19,8 @@ namespace ESSImport
struct InventoryItem : public ESM::CellRef
{
std::string mId;
int mCondition;
int mCount;
int mRelativeEquipmentSlot;
};
std::vector<InventoryItem> mItems;

@ -7,9 +7,6 @@ namespace ESSImport
void NPCC::load(ESM::ESMReader &esm)
{
mIndex = 0;
esm.getHNOT(mIndex, "INDX");
esm.getHNT(mNPDT, "NPDT");
if (esm.isNextSub("AI_E"))

@ -19,15 +19,15 @@ namespace ESSImport
{
struct NPDT
{
unsigned char unknown[2];
unsigned char mDisposition;
unsigned char unknown;
unsigned char mReputation;
unsigned char unknown2[5];
unsigned char unknown2;
int mIndex;
} mNPDT;
Inventory mInventory;
int mIndex;
void load(ESM::ESMReader &esm);
};

@ -20,12 +20,11 @@ namespace ESSImport
{
while (esm.isNextSub("DNAM"))
{
// TODO: deal with encoding?
mKnownDialogueTopics.push_back(esm.getHString());
}
if (esm.isNextSub("PNAM"))
esm.skipHSub();
esm.getHNT(mPNAM, "PNAM");
if (esm.isNextSub("SNAM"))
esm.skipHSub();
if (esm.isNextSub("NAM9"))

@ -38,6 +38,8 @@ struct PCDT
std::vector<std::string> mKnownDialogueTopics;
#pragma pack(push)
#pragma pack(1)
struct FNAM
{
unsigned char mRank;
@ -47,7 +49,18 @@ struct PCDT
unsigned char mUnknown2[3];
ESM::NAME32 mFactionName;
};
struct PNAM
{
unsigned char mUnknown1[4];
unsigned char mLevelProgress;
unsigned char mUnknown2[111];
unsigned char mSkillIncreases[8]; // number of skill increases for each attribute
unsigned char mUnknown3[88];
};
#pragma pack(pop)
std::vector<FNAM> mFactions;
PNAM mPNAM;
void load(ESM::ESMReader& esm);
};

@ -1053,13 +1053,13 @@ namespace CSMWorld
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return record.get().mCharge;
return record.get().mChargeInt;
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mCharge = data.toInt();
record2.mChargeInt = data.toInt();
record.setModified (record2);
}

@ -74,6 +74,8 @@ namespace MWBase
/// Changes faction1's opinion of faction2 by \a diff.
virtual void modFactionReaction (const std::string& faction1, const std::string& faction2, int diff) = 0;
virtual void setFactionReaction (const std::string& faction1, const std::string& faction2, int absolute) = 0;
/// @return faction1's opinion of faction2
virtual int getFactionReaction (const std::string& faction1, const std::string& faction2) const = 0;

@ -816,6 +816,9 @@ namespace MWClass
void Creature::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
if (!state.mHasCustomState)
return;
const ESM::CreatureState& state2 = dynamic_cast<const ESM::CreatureState&> (state);
ensureCustomData(ptr);
@ -844,7 +847,6 @@ namespace MWClass
customData.mContainerStore->readState (state2.mInventory);
customData.mCreatureStats.readState (state2.mCreatureStats);
}
void Creature::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
@ -852,6 +854,12 @@ namespace MWClass
{
ESM::CreatureState& state2 = dynamic_cast<ESM::CreatureState&> (state);
if (!ptr.getRefData().getCustomData())
{
state.mHasCustomState = false;
return;
}
ensureCustomData (ptr);
CreatureCustomData& customData = dynamic_cast<CreatureCustomData&> (*ptr.getRefData().getCustomData());

@ -2,7 +2,7 @@
#include "light.hpp"
#include <components/esm/loadligh.hpp>
#include <components/esm/lightstate.hpp>
#include <components/esm/objectstate.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -26,27 +26,6 @@
#include "../mwrender/actors.hpp"
#include "../mwrender/renderinginterface.hpp"
namespace
{
struct LightCustomData : public MWWorld::CustomData
{
float mTime;
///< Time remaining
LightCustomData(MWWorld::Ptr ptr)
{
MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();
mTime = ref->mBase->mData.mTime;
}
///< Constructs this CustomData from the base values for Ptr.
virtual MWWorld::CustomData *clone() const
{
return new LightCustomData (*this);
}
};
}
namespace MWClass
{
std::string Light::getId (const MWWorld::Ptr& ptr) const
@ -219,17 +198,16 @@ namespace MWClass
void Light::setRemainingUsageTime (const MWWorld::Ptr& ptr, float duration) const
{
ensureCustomData(ptr);
float &timeRemaining = dynamic_cast<LightCustomData&> (*ptr.getRefData().getCustomData()).mTime;
timeRemaining = duration;
ptr.getCellRef().setChargeFloat(duration);
}
float Light::getRemainingUsageTime (const MWWorld::Ptr& ptr) const
{
ensureCustomData(ptr);
return dynamic_cast<LightCustomData&> (*ptr.getRefData().getCustomData()).mTime;
MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();
if (ptr.getCellRef().getCharge() == -1)
return ref->mBase->mData.mTime;
else
return ptr.getCellRef().getChargeFloat();
}
MWWorld::Ptr
@ -241,12 +219,6 @@ namespace MWClass
return MWWorld::Ptr(&cell.get<ESM::Light>().insert(*ref), &cell);
}
void Light::ensureCustomData (const MWWorld::Ptr& ptr) const
{
if (!ptr.getRefData().getCustomData())
ptr.getRefData().setCustomData(new LightCustomData(ptr));
}
bool Light::canSell (const MWWorld::Ptr& item, int npcServices) const
{
return npcServices & ESM::NPC::Lights;
@ -282,26 +254,6 @@ namespace MWClass
return std::make_pair(1,"");
}
void Light::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
const ESM::LightState& state2 = dynamic_cast<const ESM::LightState&> (state);
ensureCustomData (ptr);
dynamic_cast<LightCustomData&> (*ptr.getRefData().getCustomData()).mTime = state2.mTime;
}
void Light::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const
{
ESM::LightState& state2 = dynamic_cast<ESM::LightState&> (state);
ensureCustomData (ptr);
state2.mTime = dynamic_cast<LightCustomData&> (*ptr.getRefData().getCustomData()).mTime;
}
std::string Light::getSound(const MWWorld::Ptr& ptr) const
{
return ptr.get<ESM::Light>()->mBase->mSound;

@ -10,8 +10,6 @@ namespace MWClass
virtual MWWorld::Ptr
copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const;
void ensureCustomData (const MWWorld::Ptr& ptr) const;
public:
/// Return ID of \a ptr
@ -75,14 +73,6 @@ namespace MWClass
std::pair<int, std::string> canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const;
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;
///< Read additional state from \a state into \a ptr.
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
virtual std::string getSound(const MWWorld::Ptr& ptr) const;
};
}

@ -1275,6 +1275,9 @@ namespace MWClass
void Npc::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
if (!state.mHasCustomState)
return;
const ESM::NpcState& state2 = dynamic_cast<const ESM::NpcState&> (state);
ensureCustomData(ptr);
@ -1302,6 +1305,12 @@ namespace MWClass
{
ESM::NpcState& state2 = dynamic_cast<ESM::NpcState&> (state);
if (!ptr.getRefData().getCustomData())
{
state.mHasCustomState = false;
return;
}
ensureCustomData (ptr);
NpcCustomData& customData = dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData());

@ -640,7 +640,7 @@ namespace MWDialogue
if (iter->second)
state.mKnownTopics.push_back (iter->first);
state.mModFactionReaction = mModFactionReaction;
state.mChangedFactionReaction = mChangedFactionReaction;
writer.startRecord (ESM::REC_DIAS);
state.save (writer);
@ -661,7 +661,7 @@ namespace MWDialogue
if (store.get<ESM::Dialogue>().search (*iter))
mKnownTopics.insert (std::make_pair (*iter, true));
mModFactionReaction = state.mModFactionReaction;
mChangedFactionReaction = state.mChangedFactionReaction;
}
}
@ -674,10 +674,23 @@ namespace MWDialogue
MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact1);
MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact2);
std::map<std::string, int>& map = mModFactionReaction[fact1];
if (map.find(fact2) == map.end())
map[fact2] = 0;
map[fact2] += diff;
int newValue = getFactionReaction(faction1, faction2) + diff;
std::map<std::string, int>& map = mChangedFactionReaction[fact1];
map[fact2] = newValue;
}
void DialogueManager::setFactionReaction(const std::string &faction1, const std::string &faction2, int absolute)
{
std::string fact1 = Misc::StringUtils::lowerCase(faction1);
std::string fact2 = Misc::StringUtils::lowerCase(faction2);
// Make sure the factions exist
MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact1);
MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact2);
std::map<std::string, int>& map = mChangedFactionReaction[fact1];
map[fact2] = absolute;
}
int DialogueManager::getFactionReaction(const std::string &faction1, const std::string &faction2) const
@ -685,10 +698,9 @@ namespace MWDialogue
std::string fact1 = Misc::StringUtils::lowerCase(faction1);
std::string fact2 = Misc::StringUtils::lowerCase(faction2);
ModFactionReactionMap::const_iterator map = mModFactionReaction.find(fact1);
int diff = 0;
if (map != mModFactionReaction.end() && map->second.find(fact2) != map->second.end())
diff = map->second.at(fact2);
ModFactionReactionMap::const_iterator map = mChangedFactionReaction.find(fact1);
if (map != mChangedFactionReaction.end() && map->second.find(fact2) != map->second.end())
return map->second.at(fact2);
const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact1);
@ -696,9 +708,9 @@ namespace MWDialogue
for (; it != faction->mReactions.end(); ++it)
{
if (Misc::StringUtils::ciEqual(it->first, fact2))
return it->second + diff;
return it->second;
}
return diff;
return 0;
}
void DialogueManager::clearInfoActor(const MWWorld::Ptr &actor) const

@ -27,7 +27,7 @@ namespace MWDialogue
// Modified faction reactions. <Faction1, <Faction2, Difference> >
typedef std::map<std::string, std::map<std::string, int> > ModFactionReactionMap;
ModFactionReactionMap mModFactionReaction;
ModFactionReactionMap mChangedFactionReaction;
std::list<std::string> mActorKnownTopics;
@ -97,6 +97,8 @@ namespace MWDialogue
/// Changes faction1's opinion of faction2 by \a diff.
virtual void modFactionReaction (const std::string& faction1, const std::string& faction2, int diff);
virtual void setFactionReaction (const std::string& faction1, const std::string& faction2, int absolute);
/// @return faction1's opinion of faction2
virtual int getFactionReaction (const std::string& faction1, const std::string& faction2) const;

@ -22,27 +22,27 @@ namespace MWMechanics
class NpcStats : public CreatureStats
{
/// NPCs other than the player can only have one faction. But for the sake of consistency
/// we use the same data structure for the PC and the NPCs.
/// \note the faction key must be in lowercase
std::map<std::string, int> mFactionRank;
int mDisposition;
SkillValue mSkill[ESM::Skill::Length];
SkillValue mWerewolfSkill[ESM::Skill::Length];
int mBounty;
std::set<std::string> mExpelled;
std::map<std::string, int> mFactionReputation;
int mReputation;
int mCrimeId;
int mWerewolfKills;
int mProfit;
// ----- used by the player only, maybe should be moved at some point -------
int mBounty;
int mWerewolfKills;
/// NPCs other than the player can only have one faction. But for the sake of consistency
/// we use the same data structure for the PC and the NPCs.
/// \note the faction key must be in lowercase
std::map<std::string, int> mFactionRank;
std::set<std::string> mExpelled;
std::map<std::string, int> mFactionReputation;
int mLevelProgress; // 0-10
std::vector<int> mSkillIncreases; // number of skill increases for each attribute
std::set<std::string> mUsedIds;
// ---------------------------------------------------------------------------
/// Countdown to getting damage while underwater
float mTimeToStartDrowning;

@ -236,6 +236,25 @@ namespace MWScript
}
};
class OpSetFactionReaction : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
std::string faction1 = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
std::string faction2 = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
int newValue = runtime[0].mInteger;
runtime.pop();
MWBase::Environment::get().getDialogueManager()->setFactionReaction(faction1, faction2, newValue);
}
};
template <class R>
class OpClearInfoActor : public Interpreter::Opcode0
{
@ -268,6 +287,7 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFaction, new OpSameFaction<ImplicitRef>);
interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFactionExplicit, new OpSameFaction<ExplicitRef>);
interpreter.installSegment5 (Compiler::Dialogue::opcodeModFactionReaction, new OpModFactionReaction);
interpreter.installSegment5 (Compiler::Dialogue::opcodeSetFactionReaction, new OpSetFactionReaction);
interpreter.installSegment5 (Compiler::Dialogue::opcodeGetFactionReaction, new OpGetFactionReaction);
interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActor, new OpClearInfoActor<ImplicitRef>);
interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActorExplicit, new OpClearInfoActor<ExplicitRef>);

@ -442,5 +442,6 @@ op 0x20002fb: AddToLevCreature
op 0x20002fc: RemoveFromLevCreature
op 0x20002fd: AddToLevItem
op 0x20002fe: RemoveFromLevItem
op 0x20002ff: SetFactionReaction
opcodes 0x20002ff-0x3ffffff unused
opcodes 0x2000300-0x3ffffff unused

@ -81,15 +81,29 @@ namespace MWWorld
int CellRef::getCharge() const
{
return mCellRef.mCharge;
return mCellRef.mChargeInt;
}
void CellRef::setCharge(int charge)
{
if (charge != mCellRef.mCharge)
if (charge != mCellRef.mChargeInt)
{
mChanged = true;
mCellRef.mCharge = charge;
mCellRef.mChargeInt = charge;
}
}
float CellRef::getChargeFloat() const
{
return mCellRef.mChargeFloat;
}
void CellRef::setChargeFloat(float charge)
{
if (charge != mCellRef.mChargeFloat)
{
mChanged = true;
mCellRef.mChargeFloat = charge;
}
}

@ -60,8 +60,11 @@ namespace MWWorld
// For weapon or armor, this is the remaining item health.
// For tools (lockpicks, probes, repair hammer) it is the remaining uses.
// If this returns int(-1) it means full health.
int getCharge() const;
float getChargeFloat() const; // Implemented as union with int charge
void setCharge(int charge);
void setChargeFloat(float charge);
// The NPC that owns this object (and will get angry if you steal it)
std::string getOwner() const;

@ -7,7 +7,6 @@
#include <components/esm/cellid.hpp>
#include <components/esm/esmwriter.hpp>
#include <components/esm/objectstate.hpp>
#include <components/esm/lightstate.hpp>
#include <components/esm/containerstate.hpp>
#include <components/esm/npcstate.hpp>
#include <components/esm/creaturestate.hpp>
@ -86,7 +85,9 @@ namespace
RecordType state;
iter->save (state);
// recordId currently unused
writer.writeHNT ("OBJE", collection.mList.front().mBase->sRecordId);
state.save (writer);
}
}
@ -94,12 +95,13 @@ namespace
template<typename RecordType, typename T>
void readReferenceCollection (ESM::ESMReader& reader,
MWWorld::CellRefList<T>& collection, const std::map<int, int>& contentFileMap)
MWWorld::CellRefList<T>& collection, const ESM::CellRef& cref, const std::map<int, int>& contentFileMap)
{
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
RecordType state;
state.load (reader);
state.mRef = cref;
state.load(reader);
// If the reference came from a content file, make sure this content file is loaded
if (state.mRef.mRefNum.hasContentFile())
@ -464,7 +466,7 @@ namespace MWWorld
// List moved references, from separately tracked list.
for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); ++it)
{
ESM::CellRef &ref = const_cast<ESM::CellRef&>(*it);
const ESM::CellRef &ref = *it;
mIds.push_back(Misc::StringUtils::lowerCase(ref.mRefID));
}
@ -624,7 +626,7 @@ namespace MWWorld
writeReferenceCollection<ESM::ObjectState> (writer, mIngreds);
writeReferenceCollection<ESM::CreatureLevListState> (writer, mCreatureLists);
writeReferenceCollection<ESM::ObjectState> (writer, mItemLists);
writeReferenceCollection<ESM::LightState> (writer, mLights);
writeReferenceCollection<ESM::ObjectState> (writer, mLights);
writeReferenceCollection<ESM::ObjectState> (writer, mLockpicks);
writeReferenceCollection<ESM::ObjectState> (writer, mMiscItems);
writeReferenceCollection<ESM::NpcState> (writer, mNpcs);
@ -641,109 +643,121 @@ namespace MWWorld
while (reader.isNextSub ("OBJE"))
{
unsigned int id = 0;
reader.getHT (id);
unsigned int unused;
reader.getHT (unused);
// load the RefID first so we know what type of object it is
ESM::CellRef cref;
cref.loadId(reader, true);
int type = MWBase::Environment::get().getWorld()->getStore().find(cref.mRefID);
if (type == 0)
{
std::cerr << "Dropping reference to '" << cref.mRefID << "' (object no longer exists)" << std::endl;
reader.skipHSubUntil("OBJE");
continue;
}
switch (id)
switch (type)
{
case ESM::REC_ACTI:
readReferenceCollection<ESM::ObjectState> (reader, mActivators, contentFileMap);
readReferenceCollection<ESM::ObjectState> (reader, mActivators, cref, contentFileMap);
break;
case ESM::REC_ALCH:
readReferenceCollection<ESM::ObjectState> (reader, mPotions, contentFileMap);
readReferenceCollection<ESM::ObjectState> (reader, mPotions, cref, contentFileMap);
break;
case ESM::REC_APPA:
readReferenceCollection<ESM::ObjectState> (reader, mAppas, contentFileMap);
readReferenceCollection<ESM::ObjectState> (reader, mAppas, cref, contentFileMap);
break;
case ESM::REC_ARMO:
readReferenceCollection<ESM::ObjectState> (reader, mArmors, contentFileMap);
readReferenceCollection<ESM::ObjectState> (reader, mArmors, cref, contentFileMap);
break;
case ESM::REC_BOOK:
readReferenceCollection<ESM::ObjectState> (reader, mBooks, contentFileMap);
readReferenceCollection<ESM::ObjectState> (reader, mBooks, cref, contentFileMap);
break;
case ESM::REC_CLOT:
readReferenceCollection<ESM::ObjectState> (reader, mClothes, contentFileMap);
readReferenceCollection<ESM::ObjectState> (reader, mClothes, cref, contentFileMap);
break;
case ESM::REC_CONT:
readReferenceCollection<ESM::ContainerState> (reader, mContainers, contentFileMap);
readReferenceCollection<ESM::ContainerState> (reader, mContainers, cref, contentFileMap);
break;
case ESM::REC_CREA:
readReferenceCollection<ESM::CreatureState> (reader, mCreatures, contentFileMap);
readReferenceCollection<ESM::CreatureState> (reader, mCreatures, cref, contentFileMap);
break;
case ESM::REC_DOOR:
readReferenceCollection<ESM::DoorState> (reader, mDoors, contentFileMap);
readReferenceCollection<ESM::DoorState> (reader, mDoors, cref, contentFileMap);
break;
case ESM::REC_INGR:
readReferenceCollection<ESM::ObjectState> (reader, mIngreds, contentFileMap);
readReferenceCollection<ESM::ObjectState> (reader, mIngreds, cref, contentFileMap);
break;
case ESM::REC_LEVC:
readReferenceCollection<ESM::CreatureLevListState> (reader, mCreatureLists, contentFileMap);
readReferenceCollection<ESM::CreatureLevListState> (reader, mCreatureLists, cref, contentFileMap);
break;
case ESM::REC_LEVI:
readReferenceCollection<ESM::ObjectState> (reader, mItemLists, contentFileMap);
readReferenceCollection<ESM::ObjectState> (reader, mItemLists, cref, contentFileMap);
break;
case ESM::REC_LIGH:
readReferenceCollection<ESM::LightState> (reader, mLights, contentFileMap);
readReferenceCollection<ESM::ObjectState> (reader, mLights, cref, contentFileMap);
break;
case ESM::REC_LOCK:
readReferenceCollection<ESM::ObjectState> (reader, mLockpicks, contentFileMap);
readReferenceCollection<ESM::ObjectState> (reader, mLockpicks, cref, contentFileMap);
break;
case ESM::REC_MISC:
readReferenceCollection<ESM::ObjectState> (reader, mMiscItems, contentFileMap);
readReferenceCollection<ESM::ObjectState> (reader, mMiscItems, cref, contentFileMap);
break;
case ESM::REC_NPC_:
readReferenceCollection<ESM::NpcState> (reader, mNpcs, contentFileMap);
readReferenceCollection<ESM::NpcState> (reader, mNpcs, cref, contentFileMap);
break;
case ESM::REC_PROB:
readReferenceCollection<ESM::ObjectState> (reader, mProbes, contentFileMap);
readReferenceCollection<ESM::ObjectState> (reader, mProbes, cref, contentFileMap);
break;
case ESM::REC_REPA:
readReferenceCollection<ESM::ObjectState> (reader, mRepairs, contentFileMap);
readReferenceCollection<ESM::ObjectState> (reader, mRepairs, cref, contentFileMap);
break;
case ESM::REC_STAT:
readReferenceCollection<ESM::ObjectState> (reader, mStatics, contentFileMap);
readReferenceCollection<ESM::ObjectState> (reader, mStatics, cref, contentFileMap);
break;
case ESM::REC_WEAP:
readReferenceCollection<ESM::ObjectState> (reader, mWeapons, contentFileMap);
readReferenceCollection<ESM::ObjectState> (reader, mWeapons, cref, contentFileMap);
break;
default:

@ -85,28 +85,29 @@ void MWWorld::ContainerStore::storeState (const LiveCellRef<T>& ref, ESM::Object
ref.save (state);
}
/// \todo make this method const once const-correct ContainerStoreIterators are available
template<typename T>
void MWWorld::ContainerStore::storeStates (const CellRefList<T>& collection,
std::vector<std::pair<ESM::ObjectState, std::pair<unsigned int, int> > >& states, bool equipable) const
void MWWorld::ContainerStore::storeStates (CellRefList<T>& collection,
std::vector<std::pair<ESM::ObjectState, int> >& states, bool equipable)
{
for (typename CellRefList<T>::List::const_iterator iter (collection.mList.begin());
for (typename CellRefList<T>::List::iterator iter (collection.mList.begin());
iter!=collection.mList.end(); ++iter)
{
if (iter->mData.getCount() == 0)
continue;
ESM::ObjectState state;
storeState (*iter, state);
int slot = equipable ? getSlot (*iter) : -1;
states.push_back (std::make_pair (state, std::make_pair (T::sRecordId, slot)));
int slot = equipable ? getRelativeSlot (MWWorld::ContainerStoreIterator(this, iter)) : -1;
states.push_back (std::make_pair (state, slot));
}
}
int MWWorld::ContainerStore::getSlot (const MWWorld::LiveCellRefBase& ref) const
int MWWorld::ContainerStore::getRelativeSlot (const MWWorld::ContainerStoreIterator& iter) const
{
return -1;
}
void MWWorld::ContainerStore::setSlot (const MWWorld::ContainerStoreIterator& iter, int slot) {}
void MWWorld::ContainerStore::setRelativeSlot (const MWWorld::ContainerStoreIterator& iter, int slot) {}
const std::string MWWorld::ContainerStore::sGoldId = "gold_001";
@ -641,7 +642,7 @@ MWWorld::Ptr MWWorld::ContainerStore::search (const std::string& id)
return Ptr();
}
void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const
void MWWorld::ContainerStore::writeState (ESM::InventoryState& state)
{
state.mItems.clear();
@ -656,55 +657,45 @@ void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const
storeStates (probes, state.mItems, true);
storeStates (repairs, state.mItems);
storeStates (weapons, state.mItems, true);
state.mLights.clear();
storeStates (lights, state.mItems, true);
state.mLevelledItemMap = mLevelledItemMap;
for (MWWorld::CellRefList<ESM::Light>::List::const_iterator iter (lights.mList.begin());
iter!=lights.mList.end(); ++iter)
{
ESM::LightState objectState;
storeState (*iter, objectState);
state.mLights.push_back (std::make_pair (objectState, getSlot (*iter)));
}
}
void MWWorld::ContainerStore::readState (const ESM::InventoryState& state)
{
clear();
for (std::vector<std::pair<ESM::ObjectState, std::pair<unsigned int, int> > >::const_iterator
for (std::vector<std::pair<ESM::ObjectState, int> >::const_iterator
iter (state.mItems.begin()); iter!=state.mItems.end(); ++iter)
{
int slot = iter->second.second;
int slot = iter->second;
const ESM::ObjectState& state = iter->first;
switch (iter->second.first)
int type = MWBase::Environment::get().getWorld()->getStore().find(state.mRef.mRefID);
switch (type)
{
case ESM::REC_ALCH: getState (potions, iter->first); break;
case ESM::REC_APPA: getState (appas, iter->first); break;
case ESM::REC_ARMO: setSlot (getState (armors, iter->first), slot); break;
case ESM::REC_ARMO: setRelativeSlot (getState (armors, iter->first), slot); break;
case ESM::REC_BOOK: getState (books, iter->first); break;
case ESM::REC_CLOT: setSlot (getState (clothes, iter->first), slot); break;
case ESM::REC_CLOT: setRelativeSlot (getState (clothes, iter->first), slot); break;
case ESM::REC_INGR: getState (ingreds, iter->first); break;
case ESM::REC_LOCK: setSlot (getState (lockpicks, iter->first), slot); break;
case ESM::REC_LOCK: setRelativeSlot (getState (lockpicks, iter->first), slot); break;
case ESM::REC_MISC: getState (miscItems, iter->first); break;
case ESM::REC_PROB: setSlot (getState (probes, iter->first), slot); break;
case ESM::REC_PROB: setRelativeSlot (getState (probes, iter->first), slot); break;
case ESM::REC_REPA: getState (repairs, iter->first); break;
case ESM::REC_WEAP: setSlot (getState (weapons, iter->first), slot); break;
case ESM::REC_WEAP: setRelativeSlot (getState (weapons, iter->first), slot); break;
case ESM::REC_LIGH: setRelativeSlot (getState (lights, iter->first), slot); break;
default:
std::cerr << "invalid item type in inventory state" << std::endl;
std::cerr << "invalid item type in inventory state, refid " << state.mRef.mRefID << std::endl;
break;
}
}
for (std::vector<std::pair<ESM::LightState, int> >::const_iterator iter (state.mLights.begin());
iter!=state.mLights.end(); ++iter)
{
int slot = iter->second;
setSlot (getState (lights, iter->first), slot);
}
mLevelledItemMap = state.mLevelledItemMap;
}

@ -84,15 +84,16 @@ namespace MWWorld
template<typename T>
void storeState (const LiveCellRef<T>& ref, ESM::ObjectState& state) const;
/// \todo make this method const once const-correct ContainerStoreIterators are available
template<typename T>
void storeStates (const CellRefList<T>& collection,
std::vector<std::pair<ESM::ObjectState, std::pair<unsigned int, int> > >& states,
bool equipable = false) const;
void storeStates (CellRefList<T>& collection,
std::vector<std::pair<ESM::ObjectState, int> >& states,
bool equipable = false);
virtual int getSlot (const MWWorld::LiveCellRefBase& ref) const;
virtual int getRelativeSlot (const MWWorld::ContainerStoreIterator& iter) const;
///< Return inventory slot that \a ref is in or -1 (if \a ref is not in a slot).
virtual void setSlot (const MWWorld::ContainerStoreIterator& iter, int slot);
virtual void setRelativeSlot (const MWWorld::ContainerStoreIterator& iter, int slot);
///< Set slot for \a iter. Ignored if \a iter is an end iterator or if slot==-1.
public:
@ -171,7 +172,8 @@ namespace MWWorld
Ptr search (const std::string& id);
virtual void writeState (ESM::InventoryState& state) const;
/// \todo make this method const once const-correct ContainerStoreIterators are available
virtual void writeState (ESM::InventoryState& state);
virtual void readState (const ESM::InventoryState& state);

@ -49,19 +49,40 @@ void MWWorld::InventoryStore::initSlots (TSlots& slots_)
slots_.push_back (end());
}
int MWWorld::InventoryStore::getSlot (const MWWorld::LiveCellRefBase& ref) const
int MWWorld::InventoryStore::getRelativeSlot (const MWWorld::ContainerStoreIterator& iter)
{
for (int i = 0; i<static_cast<int> (mSlots.size()); ++i)
if (mSlots[i].getType()!=-1 && mSlots[i]->getBase()==&ref)
return i;
if (mSlots[i].getType()!=-1 && mSlots[i] == iter)
{
// linear complexity, but allowedSlots is most of the time just 1 anyway
std::vector<int> allowedSlots = iter->getClass().getEquipmentSlots(*iter).first;
std::vector<int>::iterator found = std::find(allowedSlots.begin(),allowedSlots.end(),i);
if (found == allowedSlots.end())
return -1;
else
return std::distance(allowedSlots.begin(), found);
}
return -1;
}
void MWWorld::InventoryStore::setSlot (const MWWorld::ContainerStoreIterator& iter, int slot)
void MWWorld::InventoryStore::setRelativeSlot (const MWWorld::ContainerStoreIterator& iter, int relativeSlot)
{
if (iter!=end() && slot>=0 && slot<Slots)
mSlots[slot] = iter;
if (relativeSlot < 0 || iter == end())
return;
std::pair<std::vector<int>, bool> allowedSlots = iter->getClass().getEquipmentSlots(*iter);
relativeSlot = std::min(int(allowedSlots.first.size()-1), relativeSlot);
// unstack if required
if (!allowedSlots.second && iter->getRefData().getCount() > 1)
{
MWWorld::ContainerStoreIterator newIter = addNewStack(*iter, 1);
iter->getRefData().setCount(iter->getRefData().getCount()-1);
mSlots[allowedSlots.first[relativeSlot]] = newIter;
}
else
mSlots[allowedSlots.first[relativeSlot]] = iter;
}
MWWorld::InventoryStore::InventoryStore()
@ -703,7 +724,7 @@ bool MWWorld::InventoryStore::isEquipped(const MWWorld::Ptr &item)
return false;
}
void MWWorld::InventoryStore::writeState(ESM::InventoryState &state) const
void MWWorld::InventoryStore::writeState(ESM::InventoryState &state)
{
MWWorld::ContainerStore::writeState(state);

@ -113,10 +113,10 @@ namespace MWWorld
void fireEquipmentChangedEvent();
virtual int getSlot (const MWWorld::LiveCellRefBase& ref) const;
virtual int getRelativeSlot (const MWWorld::ContainerStoreIterator& iter);
///< Return inventory slot that \a ref is in or -1 (if \a ref is not in a slot).
virtual void setSlot (const MWWorld::ContainerStoreIterator& iter, int slot);
virtual void setRelativeSlot (const MWWorld::ContainerStoreIterator& iter, int relativeSlot);
///< Set slot for \a iter. Ignored if \a iter is an end iterator or if slot==-1.
public:
@ -209,7 +209,7 @@ namespace MWWorld
virtual void clear();
///< Empty container.
virtual void writeState (ESM::InventoryState& state) const;
virtual void writeState (ESM::InventoryState& state);
virtual void readState (const ESM::InventoryState& state);
};

@ -28,7 +28,7 @@ namespace MWWorld
cellRef.mRefID = name;
cellRef.mScale = 1;
cellRef.mFactionRank = 0;
cellRef.mCharge = -1;
cellRef.mChargeInt = -1;
cellRef.mGoldValue = 1;
cellRef.mEnchantmentCharge = -1;
cellRef.mTeleport = false;

@ -2031,7 +2031,7 @@ namespace MWWorld
bool World::isOnGround(const MWWorld::Ptr &ptr) const
{
RefData &refdata = ptr.getRefData();
const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle());
OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle());
if(!physactor)
return false;
@ -2049,7 +2049,7 @@ namespace MWWorld
mPhysEngine);
if(tracer.mFraction < 1.0f) // collision, must be close to something below
{
const_cast<OEngine::Physic::PhysicActor *> (physactor)->setOnGround(true);
physactor->setOnGround(true);
return true;
}
else

@ -57,11 +57,11 @@ add_component_dir (to_utf8
add_component_dir (esm
attr defs esmcommon esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell
loadclas loadclot loadcont loadcrea loadcrec loaddial loaddoor loadench loadfact loadglob loadgmst
loadclas loadclot loadcont loadcrea loaddial loaddoor loadench loadfact loadglob loadgmst
loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc
loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap inventorystate containerstate npcstate creaturestate dialoguestate statstate
npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile
aisequence magiceffects util custommarkerstate
)

@ -192,6 +192,7 @@ namespace Compiler
extensions.registerFunction("samefaction", 'l', "", opcodeSameFaction,
opcodeSameFactionExplicit);
extensions.registerInstruction("modfactionreaction", "ccl", opcodeModFactionReaction);
extensions.registerInstruction("setfactionreaction", "ccl", opcodeSetFactionReaction);
extensions.registerFunction("getfactionreaction", 'l', "ccX", opcodeGetFactionReaction);
extensions.registerInstruction("clearinfoactor", "", opcodeClearInfoActor, opcodeClearInfoActorExplicit);
}

@ -167,6 +167,7 @@ namespace Compiler
const int opcodeSameFaction = 0x20001b5;
const int opcodeSameFactionExplicit = 0x20001b6;
const int opcodeModFactionReaction = 0x2000242;
const int opcodeSetFactionReaction = 0x20002ff;
const int opcodeGetFactionReaction = 0x2000243;
const int opcodeClearInfoActor = 0x2000245;
const int opcodeClearInfoActorExplicit = 0x2000246;

@ -5,6 +5,12 @@
#include "esmwriter.hpp"
void ESM::CellRef::load (ESMReader& esm, bool wideRefNum)
{
loadId(esm, wideRefNum);
loadData(esm);
}
void ESM::CellRef::loadId(ESMReader &esm, bool wideRefNum)
{
// According to Hrnchamd, this does not belong to the actual ref. Instead, it is a marker indicating that
// the following refs are part of a "temp refs" section. A temp ref is not being tracked by the moved references system.
@ -19,8 +25,6 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum)
esm.getHNT (mRefNum.mIndex, "FRMR");
mRefID = esm.getHNString ("NAME");
loadData(esm);
}
void ESM::CellRef::loadData(ESMReader &esm)
@ -42,12 +46,12 @@ void ESM::CellRef::loadData(ESMReader &esm)
esm.getHNOT (mFactionRank, "INDX");
mGoldValue = 1;
mCharge = -1;
mChargeInt = -1;
mEnchantmentCharge = -1;
esm.getHNOT (mEnchantmentCharge, "XCHG");
esm.getHNOT (mCharge, "INTV");
esm.getHNOT (mChargeInt, "INTV");
esm.getHNOT (mGoldValue, "NAM9");
@ -102,8 +106,8 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons
if (mEnchantmentCharge != -1)
esm.writeHNT("XCHG", mEnchantmentCharge);
if (mCharge != -1)
esm.writeHNT("INTV", mCharge);
if (mChargeInt != -1)
esm.writeHNT("INTV", mChargeInt);
if (mGoldValue != 1) {
esm.writeHNT("NAM9", mGoldValue);
@ -142,8 +146,8 @@ void ESM::CellRef::blank()
mSoul.clear();
mFaction.clear();
mFactionRank = -2;
mCharge = 0;
mEnchantmentCharge = 0;
mChargeInt = -1;
mEnchantmentCharge = -1;
mGoldValue = 0;
mDestCell.clear();
mLockLevel = 0;

@ -59,7 +59,11 @@ namespace ESM
// For weapon or armor, this is the remaining item health.
// For tools (lockpicks, probes, repair hammer) it is the remaining uses.
int mCharge;
union
{
int mChargeInt;
float mChargeFloat;
};
// Remaining enchantment charge. This could be -1 if the charge was not touched yet (i.e. full).
float mEnchantmentCharge;
@ -89,8 +93,11 @@ namespace ESM
// Position and rotation of this object within the cell
Position mPos;
/// Calls loadId and loadData
void load (ESMReader& esm, bool wideRefNum = false);
void loadId (ESMReader& esm, bool wideRefNum = false);
/// Implicitly called by load
void loadData (ESMReader& esm);

@ -5,18 +5,24 @@ void ESM::CreatureState::load (ESMReader &esm)
{
ObjectState::load (esm);
mInventory.load (esm);
if (mHasCustomState)
{
mInventory.load (esm);
mCreatureStats.load (esm);
mCreatureStats.load (esm);
}
}
void ESM::CreatureState::save (ESMWriter &esm, bool inInventory) const
{
ObjectState::save (esm, inInventory);
mInventory.save (esm);
if (mHasCustomState)
{
mInventory.save (esm);
mCreatureStats.save (esm);
mCreatureStats.save (esm);
}
}
void ESM::CreatureState::blank()

@ -230,7 +230,7 @@ void ESM::CreatureStats::blank()
mTradeTime.mHour = 0;
mTradeTime.mDay = 0;
mGoldPool = 0;
mActorId = 0;
mActorId = -1;
mHasAiSettings = false;
mDead = false;
mDied = false;

@ -13,13 +13,20 @@ void ESM::DialogueState::load (ESMReader &esm)
{
std::string faction = esm.getHString();
while (esm.isNextSub ("REAC"))
while (esm.isNextSub("REA2"))
{
std::string faction2 = esm.getHString();
int reaction;
esm.getHNT(reaction, "INTV");
mChangedFactionReaction[faction][faction2] = reaction;
}
mModFactionReaction[faction][faction2] = reaction;
// no longer used
while (esm.isNextSub ("REAC"))
{
esm.skipHSub();
esm.getSubHeader();
esm.skipHSub();
}
}
}
@ -32,15 +39,15 @@ void ESM::DialogueState::save (ESMWriter &esm) const
esm.writeHNString ("TOPI", *iter);
}
for (std::map<std::string, std::map<std::string, int> >::const_iterator iter = mModFactionReaction.begin();
iter != mModFactionReaction.end(); ++iter)
for (std::map<std::string, std::map<std::string, int> >::const_iterator iter = mChangedFactionReaction.begin();
iter != mChangedFactionReaction.end(); ++iter)
{
esm.writeHNString ("FACT", iter->first);
for (std::map<std::string, int>::const_iterator reactIter = iter->second.begin();
reactIter != iter->second.end(); ++reactIter)
{
esm.writeHNString ("REAC", reactIter->first);
esm.writeHNString ("REA2", reactIter->first);
esm.writeHNT ("INTV", reactIter->second);
}
}

@ -14,9 +14,11 @@ namespace ESM
struct DialogueState
{
// must be lower case topic IDs
std::vector<std::string> mKnownTopics;
std::map<std::string, std::map<std::string, int> > mModFactionReaction;
// must be lower case faction IDs
std::map<std::string, std::map<std::string, int> > mChangedFactionReaction;
void load (ESMReader &esm);
void save (ESMWriter &esm) const;

@ -134,7 +134,11 @@ void ESMReader::getHExact(void*p, int size)
{
getSubHeader();
if (size != static_cast<int> (mCtx.leftSub))
fail("getHExact() size mismatch");
{
std::stringstream error;
error << "getHExact(): size mismatch (requested " << size << ", got " << mCtx.leftSub << ")";
fail(error.str());
}
getExact(p, size);
}
@ -210,6 +214,17 @@ void ESMReader::skipHSubSize(int size)
fail("skipHSubSize() mismatch");
}
void ESMReader::skipHSubUntil(const char *name)
{
while (hasMoreSubs() && !isNextSub(name))
{
mCtx.subCached = false;
skipHSub();
}
if (hasMoreSubs())
mCtx.subCached = true;
}
void ESMReader::getSubHeader()
{
if (mCtx.leftRec < 4)

@ -137,7 +137,11 @@ public:
{
getSubHeader();
if (mCtx.leftSub != sizeof(X))
fail("getHT(): subrecord size mismatch");
{
std::stringstream error;
error << "getHT(): subrecord size mismatch (requested " << sizeof(X) << ", got " << mCtx.leftSub << ")";
fail(error.str());
}
getT(x);
}
@ -195,6 +199,9 @@ public:
// Skip sub record and check its size
void skipHSubSize(int size);
// Skip all subrecords until the given subrecord or no more subrecords remaining
void skipHSubUntil(const char* name);
/* Sub-record header. This updates leftRec beyond the current
sub-record as well. leftSub contains size of current sub-record.
*/

@ -11,12 +11,14 @@ namespace
slot = -1;
esm.getHNOT (slot, "SLOT");
state.mRef.loadId(esm, true);
state.load (esm);
}
void write (ESM::ESMWriter &esm, const ESM::ObjectState& state, unsigned int type, int slot)
void write (ESM::ESMWriter &esm, const ESM::ObjectState& state, int slot)
{
esm.writeHNT ("IOBJ", type);
int unused = 0;
esm.writeHNT ("IOBJ", unused);
if (slot!=-1)
esm.writeHNT ("SLOT", slot);
@ -29,27 +31,15 @@ void ESM::InventoryState::load (ESMReader &esm)
{
while (esm.isNextSub ("IOBJ"))
{
unsigned int id = 0;
esm.getHT (id);
if (id==ESM::REC_LIGH)
{
LightState state;
int slot;
read (esm, state, slot);
if (state.mCount == 0)
continue;
mLights.push_back (std::make_pair (state, slot));
}
else
{
ObjectState state;
int slot;
read (esm, state, slot);
if (state.mCount == 0)
continue;
mItems.push_back (std::make_pair (state, std::make_pair (id, slot)));
}
int unused; // no longer used
esm.getHT(unused);
ObjectState state;
int slot;
read (esm, state, slot);
if (state.mCount == 0)
continue;
mItems.push_back (std::make_pair (state, slot));
}
while (esm.isNextSub("LEVM"))
@ -78,12 +68,8 @@ void ESM::InventoryState::load (ESMReader &esm)
void ESM::InventoryState::save (ESMWriter &esm) const
{
for (std::vector<std::pair<ObjectState, std::pair<unsigned int, int> > >::const_iterator iter (mItems.begin()); iter!=mItems.end(); ++iter)
write (esm, iter->first, iter->second.first, iter->second.second);
for (std::vector<std::pair<LightState, int> >::const_iterator iter (mLights.begin());
iter!=mLights.end(); ++iter)
write (esm, iter->first, ESM::REC_LIGH, iter->second);
for (std::vector<std::pair<ObjectState, int> >::const_iterator iter (mItems.begin()); iter!=mItems.end(); ++iter)
write (esm, iter->first, iter->second);
for (std::map<std::string, int>::const_iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it)
{

@ -4,7 +4,6 @@
#include <map>
#include "objectstate.hpp"
#include "lightstate.hpp"
namespace ESM
{
@ -16,11 +15,8 @@ namespace ESM
/// \brief State for inventories and containers
struct InventoryState
{
// anything but lights (type, slot)
std::vector<std::pair<ObjectState, std::pair<unsigned int, int> > > mItems;
// lights (slot)
std::vector<std::pair<LightState, int> > mLights;
/// <ObjectState, relative equipment slot>
std::vector<std::pair<ObjectState, int> > mItems;
std::map<std::string, int> mLevelledItemMap;

@ -1,21 +0,0 @@
#include "lightstate.hpp"
#include "esmreader.hpp"
#include "esmwriter.hpp"
void ESM::LightState::load (ESMReader &esm)
{
ObjectState::load (esm);
mTime = 0;
esm.getHNOT (mTime, "LTIM");
}
void ESM::LightState::save (ESMWriter &esm, bool inInventory) const
{
ObjectState::save (esm, inInventory);
if (mTime)
esm.writeHNT ("LTIM", mTime);
}

@ -1,19 +0,0 @@
#ifndef OPENMW_ESM_LIGHTSTATE_H
#define OPENMW_ESM_LIGHTSTATE_H
#include "objectstate.hpp"
namespace ESM
{
// format 0, saved games only
struct LightState : public ObjectState
{
float mTime;
virtual void load (ESMReader &esm);
virtual void save (ESMWriter &esm, bool inInventory = false) const;
};
}
#endif

@ -1,49 +0,0 @@
#ifndef OPENMW_ESM_CREC_H
#define OPENMW_ESM_CREC_H
#include <string>
// TODO create implementation files and remove this one
#include "esmreader.hpp"
namespace ESM {
class ESMReader;
class ESMWriter;
/* These two are only used in save games. They are not decoded yet.
*/
/// Changes a creature
struct LoadCREC
{
static unsigned int sRecordId;
std::string mId;
void load(ESMReader &esm)
{
esm.skipRecord();
}
void save(ESMWriter &esm) const
{
}
};
/// Changes an item list / container
struct LoadCNTC
{
std::string mId;
void load(ESMReader &esm)
{
esm.skipRecord();
}
void save(ESMWriter &esm) const
{
}
};
}
#endif

@ -5,22 +5,28 @@ void ESM::NpcState::load (ESMReader &esm)
{
ObjectState::load (esm);
mInventory.load (esm);
if (mHasCustomState)
{
mInventory.load (esm);
mNpcStats.load (esm);
mNpcStats.load (esm);
mCreatureStats.load (esm);
mCreatureStats.load (esm);
}
}
void ESM::NpcState::save (ESMWriter &esm, bool inInventory) const
{
ObjectState::save (esm, inInventory);
mInventory.save (esm);
if (mHasCustomState)
{
mInventory.save (esm);
mNpcStats.save (esm);
mNpcStats.save (esm);
mCreatureStats.save (esm);
mCreatureStats.save (esm);
}
}
void ESM::NpcState::blank()
@ -28,4 +34,5 @@ void ESM::NpcState::blank()
ObjectState::blank();
mNpcStats.blank();
mCreatureStats.blank();
mHasCustomState = true;
}

@ -6,7 +6,7 @@
void ESM::ObjectState::load (ESMReader &esm)
{
mRef.load (esm, true);
mRef.loadData(esm);
mHasLocals = 0;
esm.getHNOT (mHasLocals, "HLOC");
@ -23,6 +23,14 @@ void ESM::ObjectState::load (ESMReader &esm)
esm.getHNOT (mPosition, "POS_", 24);
esm.getHNOT (mLocalRotation, "LROT", 12);
// obsolete
int unused;
esm.getHNOT(unused, "LTIM");
// FIXME: assuming "false" as default would make more sense, but also break compatibility with older save files
mHasCustomState = true;
esm.getHNOT (mHasCustomState, "HCUS");
}
void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const
@ -46,6 +54,9 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const
esm.writeHNT ("POS_", mPosition, 24);
esm.writeHNT ("LROT", mLocalRotation, 12);
}
if (!mHasCustomState)
esm.writeHNT ("HCUS", false);
}
void ESM::ObjectState::blank()
@ -60,6 +71,7 @@ void ESM::ObjectState::blank()
mPosition.rot[i] = 0;
mLocalRotation[i] = 0;
}
mHasCustomState = true;
}
ESM::ObjectState::~ObjectState() {}

@ -26,7 +26,15 @@ namespace ESM
ESM::Position mPosition;
float mLocalRotation[3];
// Is there any class-specific state following the ObjectState
bool mHasCustomState;
ObjectState() : mHasCustomState(true)
{}
/// @note Does not load the CellRef ID, it should already be loaded before calling this method
virtual void load (ESMReader &esm);
virtual void save (ESMWriter &esm, bool inInventory = false) const;
/// Initialize to default state

@ -6,6 +6,7 @@
void ESM::Player::load (ESMReader &esm)
{
mObject.mRef.loadId(esm, true);
mObject.load (esm);
mCellId.load (esm);

@ -14,7 +14,6 @@
#include "loadclot.hpp"
#include "loadcont.hpp"
#include "loadcrea.hpp"
#include "loadcrec.hpp"
#include "loadinfo.hpp"
#include "loaddial.hpp"
#include "loaddoor.hpp"

@ -402,8 +402,8 @@ namespace ESMTerrain
int endX = startX + 1;
int endY = startY + 1;
assert(endX < ESM::Land::LAND_SIZE);
assert(endY < ESM::Land::LAND_SIZE);
endX = std::min(endX, ESM::Land::LAND_SIZE-1);
endY = std::min(endY, ESM::Land::LAND_SIZE-1);
// now get points in terrain space (effectively rounding them to boundaries)
float startXTS = startX * invFactor;

Loading…
Cancel
Save