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 --version print version information and quit
--data arg (=data) set data directories (later directories --data arg (=data) set data directories (later directories
have higher priority) have higher priority)
--data-local arg set local data directory (highest --data-local arg set local data directory (highest
priority) priority)
--fallback-archive arg (=fallback-archive) --fallback-archive arg (=fallback-archive)
set fallback BSA archives (later set fallback BSA archives (later
archives have higher priority) archives have higher priority)
--resources arg (=resources) set resources directory --resources arg (=resources) set resources directory
--start arg (=Beshara) set initial cell --start arg set initial cell
--content arg content file(s): esm/esp, or --content arg content file(s): esm/esp, or
omwgame/omwaddon omwgame/omwaddon
--anim-verbose [=arg(=1)] (=0) output animation indices files
--no-sound [=arg(=1)] (=0) disable all sounds --no-sound [=arg(=1)] (=0) disable all sounds
--script-verbose [=arg(=1)] (=0) verbose script output --script-verbose [=arg(=1)] (=0) verbose script output
--script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue
scripts) at startup 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 functionality
--script-run arg select a file containing a list of --script-run arg select a file containing a list of
console commands that is executed on console commands that is executed on
startup startup
--script-warn [=arg(=1)] (=1) handling of warnings when compiling --script-warn [=arg(=1)] (=1) handling of warnings when compiling
scripts scripts
0 - ignore warning 0 - ignore warning
1 - show warning but consider script as 1 - show warning but consider script as
correctly compiled anyway correctly compiled anyway
2 - treat warnings as errors 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 --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) 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) folding)
--encoding arg (=win1252) Character encoding used in OpenMW game --encoding arg (=win1252) Character encoding used in OpenMW game
messages: messages:
win1250 - Central and Eastern European win1250 - Central and Eastern European
such as Polish, Czech, Slovak, such as Polish, Czech, Slovak,
Hungarian, Slovene, Bosnian, Croatian, Hungarian, Slovene, Bosnian, Croatian,
Serbian (Latin script), Romanian and Serbian (Latin script), Romanian and
Albanian languages Albanian languages
win1251 - Cyrillic alphabet such as win1251 - Cyrillic alphabet such as
Russian, Bulgarian, Serbian Cyrillic Russian, Bulgarian, Serbian Cyrillic
and other languages and other languages
win1252 - Western European (Latin) win1252 - Western European (Latin)
alphabet, used by default alphabet, used by default
--fallback arg fallback values --fallback arg fallback values
--no-grab Don't grab mouse cursor --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 --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: '" << ref.mFaction << "'" << std::endl;
std::cout << " Faction rank: '" << ref.mFactionRank << "'" << std::endl; std::cout << " Faction rank: '" << ref.mFactionRank << "'" << std::endl;
std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n"; 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 << " Gold value: '" << ref.mGoldValue << "'\n";
std::cout << " Blocked: '" << static_cast<int>(ref.mReferenceBlocked) << "'" << std::endl; std::cout << " Blocked: '" << static_cast<int>(ref.mReferenceBlocked) << "'" << std::endl;
std::cout << " Deleted: " << deleted << std::endl; std::cout << " Deleted: " << deleted << std::endl;

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

@ -27,6 +27,8 @@ namespace ESSImport
cStats.mAttributes[i].mMod = acdt.mAttributes[i][0]; cStats.mAttributes[i].mMod = acdt.mAttributes[i][0];
cStats.mAttributes[i].mCurrent = 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) 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.mCurrent = actorData.mSkills[i][1];
npcStats.mSkills[i].mRegular.mBase = actorData.mSkills[i][0]; 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 "converter.hpp"
#include <stdexcept>
#include <OgreImage.h> #include <OgreImage.h>
#include <components/esm/creaturestate.hpp> #include <components/esm/creaturestate.hpp>
#include <components/esm/containerstate.hpp>
#include "convertcrec.hpp"
#include "convertcntc.hpp"
namespace namespace
{ {
@ -15,6 +21,24 @@ namespace
screenshot.save(out); 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 namespace ESSImport
@ -114,8 +138,9 @@ namespace ESSImport
ref.load (esm); ref.load (esm);
if (esm.isNextSub("DELE")) if (esm.isNextSub("DELE"))
{ {
// TODO
// strangely this can be e.g. 52 instead of just 1, // 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(); esm.skipHSub();
} }
cellrefs.push_back(ref); cellrefs.push_back(ref);
@ -153,83 +178,128 @@ namespace ESSImport
newcell.mRefs = cellrefs; 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; const CellRef& cellref = *refIt;
esm.startRecord(ESM::REC_CSTA); ESM::CellRef out;
ESM::CellState csta; out.blank();
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();
if (cellref.mIndexedRefId.size() < 8) if (!isIndexedRefId(cellref.mIndexedRefId))
{ {
std::cerr << "CellRef with no index?" << std::endl; // non-indexed RefNum, i.e. no CREC/NPCC/CNTC record associated with it
continue; // 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; 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; int refIndex;
stream >> refIndex; stream >> refIndex;
out.mRefID = cellref.mIndexedRefId.substr(0,cellref.mIndexedRefId.size()-8); 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)); 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.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); convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats);
objstate.mEnabled = cellref.mEnabled; convertNpcData(cellref.mActorData, objstate.mNpcStats);
objstate.mPosition = cellref.mPos; 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 = out;
objstate.mRef.mRefNum = cellref.mRefNum; objstate.mRef.mRefID = idLower;
// FIXME: change save format to not require object type, instead look up it up using the RefId convertCNTC(cntcIt->second, objstate);
esm.writeHNT ("OBJE", ESM::REC_CREA); convertCellRef(cellref, objstate);
esm.writeHNT ("OBJE", ESM::REC_CONT);
objstate.save(esm); objstate.save(esm);
continue; 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)); std::make_pair(refIndex, out.mRefID));
if (npccIt != mContext->mNpcChanges.end()) if (crecIt != mContext->mCreatureChanges.end())
{ {
ESM::NpcState objstate; ESM::CreatureState objstate;
objstate.blank(); 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 = out;
objstate.mRef.mRefNum = cellref.mRefNum; objstate.mRef.mRefID = idLower;
esm.writeHNT ("OBJE", ESM::REC_NPC_); 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); objstate.save(esm);
continue; 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) for (std::vector<ESM::CustomMarker>::const_iterator it = mMarkers.begin(); it != mMarkers.end(); ++it)
{ {
esm.startRecord(ESM::REC_MARK); esm.startRecord(ESM::REC_MARK);

@ -9,9 +9,12 @@
#include <components/esm/loadclas.hpp> #include <components/esm/loadclas.hpp>
#include <components/esm/loadglob.hpp> #include <components/esm/loadglob.hpp>
#include <components/esm/cellstate.hpp> #include <components/esm/cellstate.hpp>
#include <components/esm/loadfact.hpp>
#include <components/esm/dialoguestate.hpp>
#include <components/esm/custommarkerstate.hpp> #include <components/esm/custommarkerstate.hpp>
#include "importcrec.hpp" #include "importcrec.hpp"
#include "importcntc.hpp"
#include "importercontext.hpp" #include "importercontext.hpp"
#include "importcellref.hpp" #include "importcellref.hpp"
@ -94,7 +97,8 @@ public:
mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel; mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel;
mContext->mPlayerBase = npc; mContext->mPlayerBase = npc;
std::map<const int, float> empty; 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) 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; mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty;
} }
@ -165,7 +169,10 @@ public:
convertNPCC(npcc, mContext->mPlayer.mObject); convertNPCC(npcc, mContext->mPlayer.mObject);
} }
else 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; faction.mReputation = it->mReputation;
mContext->mPlayer.mObject.mNpcStats.mFactions[it->mFactionName.toString()] = faction; 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 class ConvertCREC : public Converter
{ {
public: public:
@ -217,7 +244,6 @@ public:
std::string id = esm.getHNString("NAME"); std::string id = esm.getHNString("NAME");
CREC crec; CREC crec;
crec.load(esm); crec.load(esm);
mContext->mCreatureChanges.insert(std::make_pair(std::make_pair(crec.mIndex,id), crec)); mContext->mCreatureChanges.insert(std::make_pair(std::make_pair(crec.mIndex,id), crec));
} }
}; };
@ -242,9 +268,12 @@ private:
std::vector<unsigned int> mFogOfWar; 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; std::vector<ESM::CustomMarker> mMarkers;
void writeCell(const Cell& cell, ESM::ESMWriter &esm);
}; };
class ConvertKLST : public Converter class ConvertKLST : public Converter
@ -274,6 +303,48 @@ private:
std::map<std::string, int> mKillCounter; 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 #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 "convertnpcc.hpp"
#include "convertinventory.hpp"
namespace ESSImport namespace ESSImport
{ {
void convertNPCC(const NPCC &npcc, ESM::NpcState &npcState) void convertNPCC(const NPCC &npcc, ESM::NpcState &npcState)
{ {
npcState.mNpcStats.mDisposition = npcc.mNPDT.mDisposition;
npcState.mNpcStats.mReputation = npcc.mNPDT.mReputation; npcState.mNpcStats.mReputation = npcc.mNPDT.mReputation;
convertInventory(npcc.mInventory, npcState.mInventory);
} }
} }

@ -22,7 +22,7 @@ namespace ESSImport
ESM::CellRef bla; ESM::CellRef bla;
bla.ESM::CellRef::loadData(esm); 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"); esm.getHNOT(mACDT, "ACDT");
ACSC acsc; ACSC acsc;
@ -76,6 +76,7 @@ namespace ESSImport
esm.skipHSub(); // 4 byte, 0 esm.skipHSub(); // 4 byte, 0
} }
// FIXME: not all actors have this, add flag
if (esm.isNextSub("CHRD")) // npc only if (esm.isNextSub("CHRD")) // npc only
esm.getHExact(mSkills, 27*2*sizeof(int)); esm.getHExact(mSkills, 27*2*sizeof(int));

@ -11,16 +11,31 @@ namespace ESM
namespace ESSImport namespace ESSImport
{ {
enum ACDTFlags
{
TalkedToPlayer = 0x4
};
/// Actor data, shared by (at least) REFR and CellRef /// Actor data, shared by (at least) REFR and CellRef
#pragma pack(push)
#pragma pack(1)
struct ACDT 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]; float mDynamic[3][2];
unsigned char mUnknown2[16]; unsigned char mUnknown3[16];
float mAttributes[8][2]; float mAttributes[8][2];
unsigned char mUnknown3[120]; unsigned char mUnknown4[112];
unsigned int mGoldPool;
unsigned char mUnknown5[4];
}; };
#pragma pack(pop)
struct ActorData struct ActorData
{ {

@ -18,8 +18,13 @@ namespace ESSImport
mIndexedRefId = esm.getHNString("NAME"); mIndexedRefId = esm.getHNString("NAME");
if (esm.isNextSub("LVCR")) 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); mActorData.load(esm);
mEnabled = true; 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("GLOB", "FLTV")); // gamehour
blacklist.insert(std::make_pair("REFR", "DATA")); // player position blacklist.insert(std::make_pair("REFR", "DATA")); // player position
blacklist.insert(std::make_pair("CELL", "NAM8")); // fog of war 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; File file1;
read(mEssFile, file1); read(mEssFile, file1);
@ -181,6 +186,8 @@ namespace ESSImport
Ogre::LogManager logman; Ogre::LogManager logman;
Ogre::Root root; Ogre::Root root;
// TODO: set up encoding on ESMReader based on openmw.cfg / --encoding switch
ESM::ESMReader esm; ESM::ESMReader esm;
esm.open(mEssFile); esm.open(mEssFile);
@ -193,6 +200,7 @@ namespace ESSImport
const unsigned int recPCDT = ESM::FourCC<'P','C','D','T'>::value; 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 recFMAP = ESM::FourCC<'F','M','A','P'>::value;
const unsigned int recKLST = ESM::FourCC<'K','L','S','T'>::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; std::map<unsigned int, boost::shared_ptr<Converter> > converters;
converters[ESM::REC_GLOB] = boost::shared_ptr<Converter>(new ConvertGlobal()); 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_NPC_] = boost::shared_ptr<Converter>(new ConvertNPC());
converters[ESM::REC_NPCC] = boost::shared_ptr<Converter>(new ConvertNPCC()); converters[ESM::REC_NPCC] = boost::shared_ptr<Converter>(new ConvertNPCC());
converters[ESM::REC_CREC] = boost::shared_ptr<Converter>(new ConvertCREC()); converters[ESM::REC_CREC] = boost::shared_ptr<Converter>(new ConvertCREC());
converters[recREFR] = boost::shared_ptr<Converter>(new ConvertREFR()); converters[recREFR ] = boost::shared_ptr<Converter>(new ConvertREFR());
converters[recPCDT] = boost::shared_ptr<Converter>(new ConvertPCDT()); converters[recPCDT ] = boost::shared_ptr<Converter>(new ConvertPCDT());
converters[recFMAP] = boost::shared_ptr<Converter>(new ConvertFMAP()); converters[recFMAP ] = boost::shared_ptr<Converter>(new ConvertFMAP());
converters[recKLST] = boost::shared_ptr<Converter>(new ConvertKLST()); 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_CELL] = boost::shared_ptr<Converter>(new ConvertCell());
converters[ESM::REC_ALCH] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Potion>()); converters[ESM::REC_ALCH] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Potion>());
converters[ESM::REC_CLAS] = boost::shared_ptr<Converter>(new ConvertClass()); 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_WEAP] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::Weapon>());
converters[ESM::REC_LEVC] = boost::shared_ptr<Converter>(new DefaultConverter<ESM::CreatureLevList>()); 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_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; std::set<unsigned int> unknownRecords;
@ -321,6 +332,10 @@ namespace ESSImport
} }
context.mPlayer.save(writer); context.mPlayer.save(writer);
writer.endRecord(ESM::REC_PLAY); 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/loadnpc.hpp>
#include <components/esm/player.hpp> #include <components/esm/player.hpp>
#include <components/esm/dialoguestate.hpp>
#include "importnpcc.hpp" #include "importnpcc.hpp"
#include "importcrec.hpp" #include "importcrec.hpp"
#include "importcntc.hpp"
#include "importplayer.hpp" #include "importplayer.hpp"
@ -24,12 +26,15 @@ namespace ESSImport
ESM::NPC mPlayerBase; ESM::NPC mPlayerBase;
std::string mCustomPlayerClassName; std::string mCustomPlayerClassName;
ESM::DialogueState mDialogueState;
int mDay, mMonth, mYear; int mDay, mMonth, mYear;
float mHour; float mHour;
// key <refIndex, refId> // key <refIndex, refId>
std::map<std::pair<int, std::string>, CREC> mCreatureChanges; 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>, NPCC> mNpcChanges;
std::map<std::pair<int, std::string>, CNTC> mContainerChanges;
Context() Context()
{ {

@ -1,7 +1,11 @@
#include "importinventory.hpp" #include "importinventory.hpp"
#include <stdexcept>
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
#include <components/esm/loadcont.hpp>
namespace ESSImport namespace ESSImport
{ {
@ -9,37 +13,66 @@ namespace ESSImport
{ {
while (esm.isNextSub("NPCO")) while (esm.isNextSub("NPCO"))
{ {
InventoryItem item; ESM::ContItem contItem;
item.mId = esm.getHString(); esm.getHT(contItem);
if (esm.isNextSub("XIDX")) InventoryItem item;
esm.skipHSub(); item.mId = contItem.mItem.toString();
item.mCount = contItem.mCount;
item.mRelativeEquipmentSlot = -1;
std::string script = esm.getHNOString("SCRI"); // seems that a stack of items can have a set of subrecords for each item? rings0000.ess
// script variables? // doesn't make any sense to me, if the values were different then the items shouldn't stack in the first place?
// unsure if before or after ESM::CellRef // I guess we should double check the stacking logic in OpenMW
if (!script.empty()) for (int i=0;i<std::abs(item.mCount);++i)
{ {
if (esm.isNextSub("SLCS")) if (esm.isNextSub("XIDX")) // index in the stack?
esm.skipHSub(); 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 std::string script = esm.getHNOString("SCRI");
item.ESM::CellRef::loadData(esm); // 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); mItems.push_back(item);
} }
// equipped items
while (esm.isNextSub("WIDX")) while (esm.isNextSub("WIDX"))
{ {
// equipping? // note: same item can be equipped 2 items (e.g. 2 rings)
esm.skipHSub(); // 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 struct InventoryItem : public ESM::CellRef
{ {
std::string mId; std::string mId;
int mCondition; int mCount;
int mRelativeEquipmentSlot;
}; };
std::vector<InventoryItem> mItems; std::vector<InventoryItem> mItems;

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

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

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

@ -38,6 +38,8 @@ struct PCDT
std::vector<std::string> mKnownDialogueTopics; std::vector<std::string> mKnownDialogueTopics;
#pragma pack(push)
#pragma pack(1)
struct FNAM struct FNAM
{ {
unsigned char mRank; unsigned char mRank;
@ -47,7 +49,18 @@ struct PCDT
unsigned char mUnknown2[3]; unsigned char mUnknown2[3];
ESM::NAME32 mFactionName; 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; std::vector<FNAM> mFactions;
PNAM mPNAM;
void load(ESM::ESMReader& esm); void load(ESM::ESMReader& esm);
}; };

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

@ -74,6 +74,8 @@ namespace MWBase
/// Changes faction1's opinion of faction2 by \a diff. /// 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 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 /// @return faction1's opinion of faction2
virtual int getFactionReaction (const std::string& faction1, const std::string& faction2) const = 0; 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) void Creature::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const const
{ {
if (!state.mHasCustomState)
return;
const ESM::CreatureState& state2 = dynamic_cast<const ESM::CreatureState&> (state); const ESM::CreatureState& state2 = dynamic_cast<const ESM::CreatureState&> (state);
ensureCustomData(ptr); ensureCustomData(ptr);
@ -844,7 +847,6 @@ namespace MWClass
customData.mContainerStore->readState (state2.mInventory); customData.mContainerStore->readState (state2.mInventory);
customData.mCreatureStats.readState (state2.mCreatureStats); customData.mCreatureStats.readState (state2.mCreatureStats);
} }
void Creature::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) void Creature::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
@ -852,6 +854,12 @@ namespace MWClass
{ {
ESM::CreatureState& state2 = dynamic_cast<ESM::CreatureState&> (state); ESM::CreatureState& state2 = dynamic_cast<ESM::CreatureState&> (state);
if (!ptr.getRefData().getCustomData())
{
state.mHasCustomState = false;
return;
}
ensureCustomData (ptr); ensureCustomData (ptr);
CreatureCustomData& customData = dynamic_cast<CreatureCustomData&> (*ptr.getRefData().getCustomData()); CreatureCustomData& customData = dynamic_cast<CreatureCustomData&> (*ptr.getRefData().getCustomData());

@ -2,7 +2,7 @@
#include "light.hpp" #include "light.hpp"
#include <components/esm/loadligh.hpp> #include <components/esm/loadligh.hpp>
#include <components/esm/lightstate.hpp> #include <components/esm/objectstate.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -26,27 +26,6 @@
#include "../mwrender/actors.hpp" #include "../mwrender/actors.hpp"
#include "../mwrender/renderinginterface.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 namespace MWClass
{ {
std::string Light::getId (const MWWorld::Ptr& ptr) const 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 void Light::setRemainingUsageTime (const MWWorld::Ptr& ptr, float duration) const
{ {
ensureCustomData(ptr); ptr.getCellRef().setChargeFloat(duration);
float &timeRemaining = dynamic_cast<LightCustomData&> (*ptr.getRefData().getCustomData()).mTime;
timeRemaining = duration;
} }
float Light::getRemainingUsageTime (const MWWorld::Ptr& ptr) const float Light::getRemainingUsageTime (const MWWorld::Ptr& ptr) const
{ {
ensureCustomData(ptr); MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>();
if (ptr.getCellRef().getCharge() == -1)
return dynamic_cast<LightCustomData&> (*ptr.getRefData().getCustomData()).mTime; return ref->mBase->mData.mTime;
else
return ptr.getCellRef().getChargeFloat();
} }
MWWorld::Ptr MWWorld::Ptr
@ -241,12 +219,6 @@ namespace MWClass
return MWWorld::Ptr(&cell.get<ESM::Light>().insert(*ref), &cell); 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 bool Light::canSell (const MWWorld::Ptr& item, int npcServices) const
{ {
return npcServices & ESM::NPC::Lights; return npcServices & ESM::NPC::Lights;
@ -282,26 +254,6 @@ namespace MWClass
return std::make_pair(1,""); 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 std::string Light::getSound(const MWWorld::Ptr& ptr) const
{ {
return ptr.get<ESM::Light>()->mBase->mSound; return ptr.get<ESM::Light>()->mBase->mSound;

@ -10,8 +10,6 @@ namespace MWClass
virtual MWWorld::Ptr virtual MWWorld::Ptr
copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const;
void ensureCustomData (const MWWorld::Ptr& ptr) const;
public: public:
/// Return ID of \a ptr /// 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; 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; 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) void Npc::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const const
{ {
if (!state.mHasCustomState)
return;
const ESM::NpcState& state2 = dynamic_cast<const ESM::NpcState&> (state); const ESM::NpcState& state2 = dynamic_cast<const ESM::NpcState&> (state);
ensureCustomData(ptr); ensureCustomData(ptr);
@ -1302,6 +1305,12 @@ namespace MWClass
{ {
ESM::NpcState& state2 = dynamic_cast<ESM::NpcState&> (state); ESM::NpcState& state2 = dynamic_cast<ESM::NpcState&> (state);
if (!ptr.getRefData().getCustomData())
{
state.mHasCustomState = false;
return;
}
ensureCustomData (ptr); ensureCustomData (ptr);
NpcCustomData& customData = dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData()); NpcCustomData& customData = dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData());

@ -640,7 +640,7 @@ namespace MWDialogue
if (iter->second) if (iter->second)
state.mKnownTopics.push_back (iter->first); state.mKnownTopics.push_back (iter->first);
state.mModFactionReaction = mModFactionReaction; state.mChangedFactionReaction = mChangedFactionReaction;
writer.startRecord (ESM::REC_DIAS); writer.startRecord (ESM::REC_DIAS);
state.save (writer); state.save (writer);
@ -661,7 +661,7 @@ namespace MWDialogue
if (store.get<ESM::Dialogue>().search (*iter)) if (store.get<ESM::Dialogue>().search (*iter))
mKnownTopics.insert (std::make_pair (*iter, true)); 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(fact1);
MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact2); MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact2);
std::map<std::string, int>& map = mModFactionReaction[fact1]; int newValue = getFactionReaction(faction1, faction2) + diff;
if (map.find(fact2) == map.end())
map[fact2] = 0; std::map<std::string, int>& map = mChangedFactionReaction[fact1];
map[fact2] += diff; 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 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 fact1 = Misc::StringUtils::lowerCase(faction1);
std::string fact2 = Misc::StringUtils::lowerCase(faction2); std::string fact2 = Misc::StringUtils::lowerCase(faction2);
ModFactionReactionMap::const_iterator map = mModFactionReaction.find(fact1); ModFactionReactionMap::const_iterator map = mChangedFactionReaction.find(fact1);
int diff = 0; if (map != mChangedFactionReaction.end() && map->second.find(fact2) != map->second.end())
if (map != mModFactionReaction.end() && map->second.find(fact2) != map->second.end()) return map->second.at(fact2);
diff = map->second.at(fact2);
const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get<ESM::Faction>().find(fact1); 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) for (; it != faction->mReactions.end(); ++it)
{ {
if (Misc::StringUtils::ciEqual(it->first, fact2)) 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 void DialogueManager::clearInfoActor(const MWWorld::Ptr &actor) const

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

@ -22,27 +22,27 @@ namespace MWMechanics
class NpcStats : public CreatureStats 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; int mDisposition;
SkillValue mSkill[ESM::Skill::Length]; SkillValue mSkill[ESM::Skill::Length];
SkillValue mWerewolfSkill[ESM::Skill::Length]; SkillValue mWerewolfSkill[ESM::Skill::Length];
int mBounty;
std::set<std::string> mExpelled;
std::map<std::string, int> mFactionReputation;
int mReputation; int mReputation;
int mCrimeId; int mCrimeId;
int mWerewolfKills;
int mProfit; 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 int mLevelProgress; // 0-10
std::vector<int> mSkillIncreases; // number of skill increases for each attribute std::vector<int> mSkillIncreases; // number of skill increases for each attribute
std::set<std::string> mUsedIds; std::set<std::string> mUsedIds;
// ---------------------------------------------------------------------------
/// Countdown to getting damage while underwater /// Countdown to getting damage while underwater
float mTimeToStartDrowning; 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> template <class R>
class OpClearInfoActor : public Interpreter::Opcode0 class OpClearInfoActor : public Interpreter::Opcode0
{ {
@ -268,6 +287,7 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFaction, new OpSameFaction<ImplicitRef>); interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFaction, new OpSameFaction<ImplicitRef>);
interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFactionExplicit, new OpSameFaction<ExplicitRef>); interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFactionExplicit, new OpSameFaction<ExplicitRef>);
interpreter.installSegment5 (Compiler::Dialogue::opcodeModFactionReaction, new OpModFactionReaction); 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::opcodeGetFactionReaction, new OpGetFactionReaction);
interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActor, new OpClearInfoActor<ImplicitRef>); interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActor, new OpClearInfoActor<ImplicitRef>);
interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActorExplicit, new OpClearInfoActor<ExplicitRef>); interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActorExplicit, new OpClearInfoActor<ExplicitRef>);

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

@ -81,15 +81,29 @@ namespace MWWorld
int CellRef::getCharge() const int CellRef::getCharge() const
{ {
return mCellRef.mCharge; return mCellRef.mChargeInt;
} }
void CellRef::setCharge(int charge) void CellRef::setCharge(int charge)
{ {
if (charge != mCellRef.mCharge) if (charge != mCellRef.mChargeInt)
{ {
mChanged = true; 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 weapon or armor, this is the remaining item health.
// For tools (lockpicks, probes, repair hammer) it is the remaining uses. // For tools (lockpicks, probes, repair hammer) it is the remaining uses.
// If this returns int(-1) it means full health.
int getCharge() const; int getCharge() const;
float getChargeFloat() const; // Implemented as union with int charge
void setCharge(int charge); void setCharge(int charge);
void setChargeFloat(float charge);
// The NPC that owns this object (and will get angry if you steal it) // The NPC that owns this object (and will get angry if you steal it)
std::string getOwner() const; std::string getOwner() const;

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

@ -85,28 +85,29 @@ void MWWorld::ContainerStore::storeState (const LiveCellRef<T>& ref, ESM::Object
ref.save (state); ref.save (state);
} }
/// \todo make this method const once const-correct ContainerStoreIterators are available
template<typename T> template<typename T>
void MWWorld::ContainerStore::storeStates (const CellRefList<T>& collection, void MWWorld::ContainerStore::storeStates (CellRefList<T>& collection,
std::vector<std::pair<ESM::ObjectState, std::pair<unsigned int, int> > >& states, bool equipable) const 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) iter!=collection.mList.end(); ++iter)
{ {
if (iter->mData.getCount() == 0) if (iter->mData.getCount() == 0)
continue; continue;
ESM::ObjectState state; ESM::ObjectState state;
storeState (*iter, state); storeState (*iter, state);
int slot = equipable ? getSlot (*iter) : -1; int slot = equipable ? getRelativeSlot (MWWorld::ContainerStoreIterator(this, iter)) : -1;
states.push_back (std::make_pair (state, std::make_pair (T::sRecordId, slot))); 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; 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"; const std::string MWWorld::ContainerStore::sGoldId = "gold_001";
@ -641,7 +642,7 @@ MWWorld::Ptr MWWorld::ContainerStore::search (const std::string& id)
return Ptr(); return Ptr();
} }
void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const void MWWorld::ContainerStore::writeState (ESM::InventoryState& state)
{ {
state.mItems.clear(); state.mItems.clear();
@ -656,55 +657,45 @@ void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const
storeStates (probes, state.mItems, true); storeStates (probes, state.mItems, true);
storeStates (repairs, state.mItems); storeStates (repairs, state.mItems);
storeStates (weapons, state.mItems, true); storeStates (weapons, state.mItems, true);
storeStates (lights, state.mItems, true);
state.mLights.clear();
state.mLevelledItemMap = mLevelledItemMap; 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) void MWWorld::ContainerStore::readState (const ESM::InventoryState& state)
{ {
clear(); 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) 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_ALCH: getState (potions, iter->first); break;
case ESM::REC_APPA: getState (appas, 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_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_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_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_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: default:
std::cerr << "invalid item type in inventory state, refid " << state.mRef.mRefID << std::endl;
std::cerr << "invalid item type in inventory state" << 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; mLevelledItemMap = state.mLevelledItemMap;
} }

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

@ -49,19 +49,40 @@ void MWWorld::InventoryStore::initSlots (TSlots& slots_)
slots_.push_back (end()); 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) for (int i = 0; i<static_cast<int> (mSlots.size()); ++i)
if (mSlots[i].getType()!=-1 && mSlots[i]->getBase()==&ref) if (mSlots[i].getType()!=-1 && mSlots[i] == iter)
return i; {
// 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; 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) if (relativeSlot < 0 || iter == end())
mSlots[slot] = iter; 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() MWWorld::InventoryStore::InventoryStore()
@ -703,7 +724,7 @@ bool MWWorld::InventoryStore::isEquipped(const MWWorld::Ptr &item)
return false; return false;
} }
void MWWorld::InventoryStore::writeState(ESM::InventoryState &state) const void MWWorld::InventoryStore::writeState(ESM::InventoryState &state)
{ {
MWWorld::ContainerStore::writeState(state); MWWorld::ContainerStore::writeState(state);

@ -113,10 +113,10 @@ namespace MWWorld
void fireEquipmentChangedEvent(); 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). ///< 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. ///< Set slot for \a iter. Ignored if \a iter is an end iterator or if slot==-1.
public: public:
@ -209,7 +209,7 @@ namespace MWWorld
virtual void clear(); virtual void clear();
///< Empty container. ///< Empty container.
virtual void writeState (ESM::InventoryState& state) const; virtual void writeState (ESM::InventoryState& state);
virtual void readState (const ESM::InventoryState& state); virtual void readState (const ESM::InventoryState& state);
}; };

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

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

@ -57,11 +57,11 @@ add_component_dir (to_utf8
add_component_dir (esm add_component_dir (esm
attr defs esmcommon esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell 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 loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc
loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter 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 npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile
aisequence magiceffects util custommarkerstate aisequence magiceffects util custommarkerstate
) )

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

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

@ -5,6 +5,12 @@
#include "esmwriter.hpp" #include "esmwriter.hpp"
void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) 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 // 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. // 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"); esm.getHNT (mRefNum.mIndex, "FRMR");
mRefID = esm.getHNString ("NAME"); mRefID = esm.getHNString ("NAME");
loadData(esm);
} }
void ESM::CellRef::loadData(ESMReader &esm) void ESM::CellRef::loadData(ESMReader &esm)
@ -42,12 +46,12 @@ void ESM::CellRef::loadData(ESMReader &esm)
esm.getHNOT (mFactionRank, "INDX"); esm.getHNOT (mFactionRank, "INDX");
mGoldValue = 1; mGoldValue = 1;
mCharge = -1; mChargeInt = -1;
mEnchantmentCharge = -1; mEnchantmentCharge = -1;
esm.getHNOT (mEnchantmentCharge, "XCHG"); esm.getHNOT (mEnchantmentCharge, "XCHG");
esm.getHNOT (mCharge, "INTV"); esm.getHNOT (mChargeInt, "INTV");
esm.getHNOT (mGoldValue, "NAM9"); esm.getHNOT (mGoldValue, "NAM9");
@ -102,8 +106,8 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons
if (mEnchantmentCharge != -1) if (mEnchantmentCharge != -1)
esm.writeHNT("XCHG", mEnchantmentCharge); esm.writeHNT("XCHG", mEnchantmentCharge);
if (mCharge != -1) if (mChargeInt != -1)
esm.writeHNT("INTV", mCharge); esm.writeHNT("INTV", mChargeInt);
if (mGoldValue != 1) { if (mGoldValue != 1) {
esm.writeHNT("NAM9", mGoldValue); esm.writeHNT("NAM9", mGoldValue);
@ -142,8 +146,8 @@ void ESM::CellRef::blank()
mSoul.clear(); mSoul.clear();
mFaction.clear(); mFaction.clear();
mFactionRank = -2; mFactionRank = -2;
mCharge = 0; mChargeInt = -1;
mEnchantmentCharge = 0; mEnchantmentCharge = -1;
mGoldValue = 0; mGoldValue = 0;
mDestCell.clear(); mDestCell.clear();
mLockLevel = 0; mLockLevel = 0;

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

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

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

@ -13,13 +13,20 @@ void ESM::DialogueState::load (ESMReader &esm)
{ {
std::string faction = esm.getHString(); std::string faction = esm.getHString();
while (esm.isNextSub ("REAC")) while (esm.isNextSub("REA2"))
{ {
std::string faction2 = esm.getHString(); std::string faction2 = esm.getHString();
int reaction; int reaction;
esm.getHNT(reaction, "INTV"); 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); esm.writeHNString ("TOPI", *iter);
} }
for (std::map<std::string, std::map<std::string, int> >::const_iterator iter = mModFactionReaction.begin(); for (std::map<std::string, std::map<std::string, int> >::const_iterator iter = mChangedFactionReaction.begin();
iter != mModFactionReaction.end(); ++iter) iter != mChangedFactionReaction.end(); ++iter)
{ {
esm.writeHNString ("FACT", iter->first); esm.writeHNString ("FACT", iter->first);
for (std::map<std::string, int>::const_iterator reactIter = iter->second.begin(); for (std::map<std::string, int>::const_iterator reactIter = iter->second.begin();
reactIter != iter->second.end(); ++reactIter) reactIter != iter->second.end(); ++reactIter)
{ {
esm.writeHNString ("REAC", reactIter->first); esm.writeHNString ("REA2", reactIter->first);
esm.writeHNT ("INTV", reactIter->second); esm.writeHNT ("INTV", reactIter->second);
} }
} }

@ -14,9 +14,11 @@ namespace ESM
struct DialogueState struct DialogueState
{ {
// must be lower case topic IDs
std::vector<std::string> mKnownTopics; 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 load (ESMReader &esm);
void save (ESMWriter &esm) const; void save (ESMWriter &esm) const;

@ -134,7 +134,11 @@ void ESMReader::getHExact(void*p, int size)
{ {
getSubHeader(); getSubHeader();
if (size != static_cast<int> (mCtx.leftSub)) 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); getExact(p, size);
} }
@ -210,6 +214,17 @@ void ESMReader::skipHSubSize(int size)
fail("skipHSubSize() mismatch"); 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() void ESMReader::getSubHeader()
{ {
if (mCtx.leftRec < 4) if (mCtx.leftRec < 4)

@ -137,7 +137,11 @@ public:
{ {
getSubHeader(); getSubHeader();
if (mCtx.leftSub != sizeof(X)) 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); getT(x);
} }
@ -195,6 +199,9 @@ public:
// Skip sub record and check its size // Skip sub record and check its size
void skipHSubSize(int 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 header. This updates leftRec beyond the current
sub-record as well. leftSub contains size of current sub-record. sub-record as well. leftSub contains size of current sub-record.
*/ */

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

@ -4,7 +4,6 @@
#include <map> #include <map>
#include "objectstate.hpp" #include "objectstate.hpp"
#include "lightstate.hpp"
namespace ESM namespace ESM
{ {
@ -16,11 +15,8 @@ namespace ESM
/// \brief State for inventories and containers /// \brief State for inventories and containers
struct InventoryState struct InventoryState
{ {
// anything but lights (type, slot) /// <ObjectState, relative equipment slot>
std::vector<std::pair<ObjectState, std::pair<unsigned int, int> > > mItems; std::vector<std::pair<ObjectState, int> > mItems;
// lights (slot)
std::vector<std::pair<LightState, int> > mLights;
std::map<std::string, int> mLevelledItemMap; 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); 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 void ESM::NpcState::save (ESMWriter &esm, bool inInventory) const
{ {
ObjectState::save (esm, inInventory); 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() void ESM::NpcState::blank()
@ -28,4 +34,5 @@ void ESM::NpcState::blank()
ObjectState::blank(); ObjectState::blank();
mNpcStats.blank(); mNpcStats.blank();
mCreatureStats.blank(); mCreatureStats.blank();
mHasCustomState = true;
} }

@ -6,7 +6,7 @@
void ESM::ObjectState::load (ESMReader &esm) void ESM::ObjectState::load (ESMReader &esm)
{ {
mRef.load (esm, true); mRef.loadData(esm);
mHasLocals = 0; mHasLocals = 0;
esm.getHNOT (mHasLocals, "HLOC"); esm.getHNOT (mHasLocals, "HLOC");
@ -23,6 +23,14 @@ void ESM::ObjectState::load (ESMReader &esm)
esm.getHNOT (mPosition, "POS_", 24); esm.getHNOT (mPosition, "POS_", 24);
esm.getHNOT (mLocalRotation, "LROT", 12); 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 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 ("POS_", mPosition, 24);
esm.writeHNT ("LROT", mLocalRotation, 12); esm.writeHNT ("LROT", mLocalRotation, 12);
} }
if (!mHasCustomState)
esm.writeHNT ("HCUS", false);
} }
void ESM::ObjectState::blank() void ESM::ObjectState::blank()
@ -60,6 +71,7 @@ void ESM::ObjectState::blank()
mPosition.rot[i] = 0; mPosition.rot[i] = 0;
mLocalRotation[i] = 0; mLocalRotation[i] = 0;
} }
mHasCustomState = true;
} }
ESM::ObjectState::~ObjectState() {} ESM::ObjectState::~ObjectState() {}

@ -26,7 +26,15 @@ namespace ESM
ESM::Position mPosition; ESM::Position mPosition;
float mLocalRotation[3]; 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 load (ESMReader &esm);
virtual void save (ESMWriter &esm, bool inInventory = false) const; virtual void save (ESMWriter &esm, bool inInventory = false) const;
/// Initialize to default state /// Initialize to default state

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

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

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

Loading…
Cancel
Save