From 235683e44943bc68528527f9da14953749126b21 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 19 Jan 2015 10:34:49 +0100 Subject: [PATCH 01/21] Remove LightState from openmw save format to streamline inventory loading --- apps/openmw/mwclass/light.cpp | 10 ++---- apps/openmw/mwworld/cellstore.cpp | 5 ++- apps/openmw/mwworld/containerstore.cpp | 36 ++++++++------------- apps/openmw/mwworld/containerstore.hpp | 2 +- components/CMakeLists.txt | 2 +- components/esm/inventorystate.cpp | 43 +++++++++----------------- components/esm/inventorystate.hpp | 7 +---- components/esm/lightstate.cpp | 21 ------------- components/esm/lightstate.hpp | 19 ------------ components/esm/objectstate.cpp | 8 +++++ components/esm/objectstate.hpp | 2 ++ 11 files changed, 45 insertions(+), 110 deletions(-) delete mode 100644 components/esm/lightstate.cpp delete mode 100644 components/esm/lightstate.hpp diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 669b8187c..c50ad52d8 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -2,7 +2,7 @@ #include "light.hpp" #include -#include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -285,21 +285,17 @@ namespace MWClass void Light::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const { - const ESM::LightState& state2 = dynamic_cast (state); - ensureCustomData (ptr); - dynamic_cast (*ptr.getRefData().getCustomData()).mTime = state2.mTime; + dynamic_cast (*ptr.getRefData().getCustomData()).mTime = state.mTime; } void Light::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) const { - ESM::LightState& state2 = dynamic_cast (state); - ensureCustomData (ptr); - state2.mTime = dynamic_cast (*ptr.getRefData().getCustomData()).mTime; + state.mTime = dynamic_cast (*ptr.getRefData().getCustomData()).mTime; } std::string Light::getSound(const MWWorld::Ptr& ptr) const diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 1c0e7e385..4cf99fdb7 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -624,7 +623,7 @@ namespace MWWorld writeReferenceCollection (writer, mIngreds); writeReferenceCollection (writer, mCreatureLists); writeReferenceCollection (writer, mItemLists); - writeReferenceCollection (writer, mLights); + writeReferenceCollection (writer, mLights); writeReferenceCollection (writer, mLockpicks); writeReferenceCollection (writer, mMiscItems); writeReferenceCollection (writer, mNpcs); @@ -708,7 +707,7 @@ namespace MWWorld case ESM::REC_LIGH: - readReferenceCollection (reader, mLights, contentFileMap); + readReferenceCollection (reader, mLights, contentFileMap); break; case ESM::REC_LOCK: diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index e9c968a07..bd14720f7 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -87,7 +87,7 @@ void MWWorld::ContainerStore::storeState (const LiveCellRef& ref, ESM::Object template void MWWorld::ContainerStore::storeStates (const CellRefList& collection, - std::vector > >& states, bool equipable) const + std::vector >& states, bool equipable) const { for (typename CellRefList::List::const_iterator iter (collection.mList.begin()); iter!=collection.mList.end(); ++iter) @@ -97,7 +97,7 @@ void MWWorld::ContainerStore::storeStates (const CellRefList& collection, ESM::ObjectState state; storeState (*iter, state); int slot = equipable ? getSlot (*iter) : -1; - states.push_back (std::make_pair (state, std::make_pair (T::sRecordId, slot))); + states.push_back (std::make_pair (state, slot)); } } @@ -656,30 +656,25 @@ void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const storeStates (probes, state.mItems, true); storeStates (repairs, state.mItems); storeStates (weapons, state.mItems, true); - - state.mLights.clear(); + storeStates (lights, state.mItems, true); state.mLevelledItemMap = mLevelledItemMap; - - for (MWWorld::CellRefList::List::const_iterator iter (lights.mList.begin()); - iter!=lights.mList.end(); ++iter) - { - ESM::LightState objectState; - storeState (*iter, objectState); - state.mLights.push_back (std::make_pair (objectState, getSlot (*iter))); - } } void MWWorld::ContainerStore::readState (const ESM::InventoryState& state) { clear(); - for (std::vector > >::const_iterator + for (std::vector >::const_iterator iter (state.mItems.begin()); iter!=state.mItems.end(); ++iter) { - int slot = iter->second.second; + int slot = iter->second; + + const ESM::ObjectState& state = iter->first; - switch (iter->second.first) + int type = MWBase::Environment::get().getWorld()->getStore().find(state.mRef.mRefID); + + switch (type) { case ESM::REC_ALCH: getState (potions, iter->first); break; case ESM::REC_APPA: getState (appas, iter->first); break; @@ -692,19 +687,14 @@ void MWWorld::ContainerStore::readState (const ESM::InventoryState& state) case ESM::REC_PROB: setSlot (getState (probes, iter->first), slot); break; case ESM::REC_REPA: getState (repairs, iter->first); break; case ESM::REC_WEAP: setSlot (getState (weapons, iter->first), slot); break; + case ESM::REC_LIGH: setSlot (getState (lights, iter->first), slot); break; default: - - std::cerr << "invalid item type in inventory state" << std::endl; + std::cerr << "invalid item type in inventory state, refid " << state.mRef.mRefID << std::endl; + break; } } - for (std::vector >::const_iterator iter (state.mLights.begin()); - iter!=state.mLights.end(); ++iter) - { - int slot = iter->second; - setSlot (getState (lights, iter->first), slot); - } mLevelledItemMap = state.mLevelledItemMap; } diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 2849463c9..68ee41a1d 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -86,7 +86,7 @@ namespace MWWorld template void storeStates (const CellRefList& collection, - std::vector > >& states, + std::vector >& states, bool equipable = false) const; virtual int getSlot (const MWWorld::LiveCellRefBase& ref) const; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 0578a44b0..93228f87a 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -61,7 +61,7 @@ add_component_dir (esm loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter - savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate + savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap inventorystate containerstate npcstate creaturestate dialoguestate statstate npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile aisequence magiceffects util custommarkerstate ) diff --git a/components/esm/inventorystate.cpp b/components/esm/inventorystate.cpp index a2d49b144..fdaba3322 100644 --- a/components/esm/inventorystate.cpp +++ b/components/esm/inventorystate.cpp @@ -14,9 +14,10 @@ namespace state.load (esm); } - void write (ESM::ESMWriter &esm, const ESM::ObjectState& state, unsigned int type, int slot) + void write (ESM::ESMWriter &esm, const ESM::ObjectState& state, int slot) { - esm.writeHNT ("IOBJ", type); + int unused = 0; + esm.writeHNT ("IOBJ", unused); if (slot!=-1) esm.writeHNT ("SLOT", slot); @@ -29,27 +30,15 @@ void ESM::InventoryState::load (ESMReader &esm) { while (esm.isNextSub ("IOBJ")) { - unsigned int id = 0; - esm.getHT (id); - - if (id==ESM::REC_LIGH) - { - LightState state; - int slot; - read (esm, state, slot); - if (state.mCount == 0) - continue; - mLights.push_back (std::make_pair (state, slot)); - } - else - { - ObjectState state; - int slot; - read (esm, state, slot); - if (state.mCount == 0) - continue; - mItems.push_back (std::make_pair (state, std::make_pair (id, slot))); - } + int unused; // no longer used + esm.getHT(unused); + + ObjectState state; + int slot; + read (esm, state, slot); + if (state.mCount == 0) + continue; + mItems.push_back (std::make_pair (state, slot)); } while (esm.isNextSub("LEVM")) @@ -78,12 +67,8 @@ void ESM::InventoryState::load (ESMReader &esm) void ESM::InventoryState::save (ESMWriter &esm) const { - for (std::vector > >::const_iterator iter (mItems.begin()); iter!=mItems.end(); ++iter) - write (esm, iter->first, iter->second.first, iter->second.second); - - for (std::vector >::const_iterator iter (mLights.begin()); - iter!=mLights.end(); ++iter) - write (esm, iter->first, ESM::REC_LIGH, iter->second); + for (std::vector >::const_iterator iter (mItems.begin()); iter!=mItems.end(); ++iter) + write (esm, iter->first, iter->second); for (std::map::const_iterator it = mLevelledItemMap.begin(); it != mLevelledItemMap.end(); ++it) { diff --git a/components/esm/inventorystate.hpp b/components/esm/inventorystate.hpp index bd0b46a22..3d4407e7b 100644 --- a/components/esm/inventorystate.hpp +++ b/components/esm/inventorystate.hpp @@ -4,7 +4,6 @@ #include #include "objectstate.hpp" -#include "lightstate.hpp" namespace ESM { @@ -16,11 +15,7 @@ namespace ESM /// \brief State for inventories and containers struct InventoryState { - // anything but lights (type, slot) - std::vector > > mItems; - - // lights (slot) - std::vector > mLights; + std::vector > mItems; std::map mLevelledItemMap; diff --git a/components/esm/lightstate.cpp b/components/esm/lightstate.cpp deleted file mode 100644 index 1ef040823..000000000 --- a/components/esm/lightstate.cpp +++ /dev/null @@ -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); -} \ No newline at end of file diff --git a/components/esm/lightstate.hpp b/components/esm/lightstate.hpp deleted file mode 100644 index a22735e07..000000000 --- a/components/esm/lightstate.hpp +++ /dev/null @@ -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 diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index f7755f8cb..98cf3eba6 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -23,6 +23,10 @@ void ESM::ObjectState::load (ESMReader &esm) esm.getHNOT (mPosition, "POS_", 24); esm.getHNOT (mLocalRotation, "LROT", 12); + + // used for lights only + mTime = 0; + esm.getHNOT (mTime, "LTIM"); } void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const @@ -46,6 +50,9 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const esm.writeHNT ("POS_", mPosition, 24); esm.writeHNT ("LROT", mLocalRotation, 12); } + + if (mTime) + esm.writeHNT ("LTIM", mTime); } void ESM::ObjectState::blank() @@ -60,6 +67,7 @@ void ESM::ObjectState::blank() mPosition.rot[i] = 0; mLocalRotation[i] = 0; } + mTime = 0; } ESM::ObjectState::~ObjectState() {} diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index 5b05e0949..6b536bd01 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -26,6 +26,8 @@ namespace ESM ESM::Position mPosition; float mLocalRotation[3]; + float mTime; // Used for lights only. Overhead should not be so awful, besides CellRef isn't OO either + virtual void load (ESMReader &esm); virtual void save (ESMWriter &esm, bool inInventory = false) const; From f7e32a24c9f8550c018495860a2f9e0b4dc36194 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 19 Jan 2015 12:03:30 +0100 Subject: [PATCH 02/21] Fix terrain assertion --- components/esmterrain/storage.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/esmterrain/storage.cpp b/components/esmterrain/storage.cpp index 3c76cc3b4..a66193f97 100644 --- a/components/esmterrain/storage.cpp +++ b/components/esmterrain/storage.cpp @@ -402,8 +402,8 @@ namespace ESMTerrain int endX = startX + 1; int endY = startY + 1; - assert(endX < ESM::Land::LAND_SIZE); - assert(endY < ESM::Land::LAND_SIZE); + endX = std::min(endX, ESM::Land::LAND_SIZE-1); + endY = std::min(endY, ESM::Land::LAND_SIZE-1); // now get points in terrain space (effectively rounding them to boundaries) float startXTS = startX * invFactor; From a7b82e5107caed0fceb407abfa364071d0338053 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 19 Jan 2015 12:22:51 +0100 Subject: [PATCH 03/21] ESSImport: inventory loading works, equipment slots need more work --- apps/essimporter/CMakeLists.txt | 2 ++ apps/essimporter/convertcrec.cpp | 13 +++++++++++++ apps/essimporter/convertcrec.hpp | 15 +++++++++++++++ apps/essimporter/converter.cpp | 4 ++++ apps/essimporter/convertinventory.cpp | 23 +++++++++++++++++++++++ apps/essimporter/convertinventory.hpp | 15 +++++++++++++++ apps/essimporter/convertnpcc.cpp | 4 ++++ apps/essimporter/importinventory.cpp | 22 +++++++++++++++++++--- apps/essimporter/importinventory.hpp | 1 + components/esm/cellref.cpp | 4 ++-- 10 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 apps/essimporter/convertcrec.cpp create mode 100644 apps/essimporter/convertcrec.hpp create mode 100644 apps/essimporter/convertinventory.cpp create mode 100644 apps/essimporter/convertinventory.hpp diff --git a/apps/essimporter/CMakeLists.txt b/apps/essimporter/CMakeLists.txt index cf3218454..1af55de9b 100644 --- a/apps/essimporter/CMakeLists.txt +++ b/apps/essimporter/CMakeLists.txt @@ -12,6 +12,8 @@ set(ESSIMPORTER_FILES converter.cpp convertacdt.cpp convertnpcc.cpp + convertinventory.cpp + convertcrec.cpp ) add_executable(openmw-essimporter diff --git a/apps/essimporter/convertcrec.cpp b/apps/essimporter/convertcrec.cpp new file mode 100644 index 000000000..34e1c0070 --- /dev/null +++ b/apps/essimporter/convertcrec.cpp @@ -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); + } + +} diff --git a/apps/essimporter/convertcrec.hpp b/apps/essimporter/convertcrec.hpp new file mode 100644 index 000000000..7d317f03e --- /dev/null +++ b/apps/essimporter/convertcrec.hpp @@ -0,0 +1,15 @@ +#ifndef OPENMW_ESSIMPORT_CONVERTCREC_H +#define OPENMW_ESSIMPORT_CONVERTCREC_H + +#include "importcrec.hpp" + +#include + +namespace ESSImport +{ + + void convertCREC(const CREC& crec, ESM::CreatureState& state); + +} + +#endif diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index ce3cea675..22d6d8282 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -4,6 +4,8 @@ #include +#include "convertcrec.hpp" + namespace { @@ -197,6 +199,7 @@ namespace ESSImport ESM::CreatureState objstate; objstate.blank(); convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); + convertCREC(crecIt->second, objstate); objstate.mEnabled = cellref.mEnabled; objstate.mPosition = cellref.mPos; objstate.mRef = out; @@ -215,6 +218,7 @@ namespace ESSImport objstate.blank(); convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); convertNpcData(cellref.mActorData, objstate.mNpcStats); + convertNPCC(npccIt->second, objstate); objstate.mEnabled = cellref.mEnabled; objstate.mPosition = cellref.mPos; objstate.mRef = out; diff --git a/apps/essimporter/convertinventory.cpp b/apps/essimporter/convertinventory.cpp new file mode 100644 index 000000000..42535d946 --- /dev/null +++ b/apps/essimporter/convertinventory.cpp @@ -0,0 +1,23 @@ +#include "convertinventory.hpp" + +#include + +namespace ESSImport +{ + + void convertInventory(const Inventory &inventory, ESM::InventoryState &state) + { + for (std::vector::const_iterator it = inventory.mItems.begin(); + it != inventory.mItems.end(); ++it) + { + ESM::ObjectState objstate; + objstate.blank(); + 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 + objstate.mRef.mCharge = it->mCondition; + state.mItems.push_back(std::make_pair(objstate, -1)); + } + } + +} diff --git a/apps/essimporter/convertinventory.hpp b/apps/essimporter/convertinventory.hpp new file mode 100644 index 000000000..8abe85a44 --- /dev/null +++ b/apps/essimporter/convertinventory.hpp @@ -0,0 +1,15 @@ +#ifndef OPENMW_ESSIMPORT_CONVERTINVENTORY_H +#define OPENMW_ESSIMPORT_CONVERTINVENTORY_H + +#include "importinventory.hpp" + +#include + +namespace ESSImport +{ + + void convertInventory (const Inventory& inventory, ESM::InventoryState& state); + +} + +#endif diff --git a/apps/essimporter/convertnpcc.cpp b/apps/essimporter/convertnpcc.cpp index fdf96c15a..2155f46fb 100644 --- a/apps/essimporter/convertnpcc.cpp +++ b/apps/essimporter/convertnpcc.cpp @@ -1,10 +1,14 @@ #include "convertnpcc.hpp" +#include "convertinventory.hpp" + namespace ESSImport { void convertNPCC(const NPCC &npcc, ESM::NpcState &npcState) { npcState.mNpcStats.mReputation = npcc.mNPDT.mReputation; + + convertInventory(npcc.mInventory, npcState.mInventory); } } diff --git a/apps/essimporter/importinventory.cpp b/apps/essimporter/importinventory.cpp index d132fd76d..b3bd4a86b 100644 --- a/apps/essimporter/importinventory.cpp +++ b/apps/essimporter/importinventory.cpp @@ -2,6 +2,8 @@ #include +#include + namespace ESSImport { @@ -9,8 +11,12 @@ namespace ESSImport { while (esm.isNextSub("NPCO")) { + ESM::ContItem contItem; + esm.getHT(contItem); + InventoryItem item; - item.mId = esm.getHString(); + item.mId = contItem.mItem.toString(); + item.mCount = contItem.mCount; if (esm.isNextSub("XIDX")) esm.skipHSub(); @@ -32,14 +38,24 @@ namespace ESSImport item.ESM::CellRef::loadData(esm); item.mCondition = -1; + // FIXME: for Lights, this is actually a float esm.getHNOT(item.mCondition, "XHLT"); mItems.push_back(item); } + // equipped items while (esm.isNextSub("WIDX")) { - // equipping? - esm.skipHSub(); + // note: same item can be equipped 2 items (e.g. 2 rings) + // and will be *stacked* in the NPCO list, unlike openmw! + esm.getSubHeader(); + int itemIndex; // index of the item in the NPCO list + esm.getT(itemIndex); + + // appears to be a relative index for only the *possible* slots this item can be equipped in, + // i.e. 0 most of the time, unlike openmw slot enum index + int slotIndex; + esm.getT(slotIndex); } } diff --git a/apps/essimporter/importinventory.hpp b/apps/essimporter/importinventory.hpp index e31cab76a..359e43fa6 100644 --- a/apps/essimporter/importinventory.hpp +++ b/apps/essimporter/importinventory.hpp @@ -19,6 +19,7 @@ namespace ESSImport struct InventoryItem : public ESM::CellRef { std::string mId; + int mCount; int mCondition; }; std::vector mItems; diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 43b1f4d29..0c2dfd5db 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -142,8 +142,8 @@ void ESM::CellRef::blank() mSoul.clear(); mFaction.clear(); mFactionRank = -2; - mCharge = 0; - mEnchantmentCharge = 0; + mCharge = -1; + mEnchantmentCharge = -1; mGoldValue = 0; mDestCell.clear(); mLockLevel = 0; From 8e1eeccbe190f3ec04fc60283313683e7ea7dac2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 19 Jan 2015 13:16:12 +0100 Subject: [PATCH 04/21] ESSImport: container state --- apps/essimporter/CMakeLists.txt | 2 + apps/essimporter/convertcntc.cpp | 13 +++ apps/essimporter/convertcntc.hpp | 15 +++ apps/essimporter/converter.cpp | 163 ++++++++++++++++----------- apps/essimporter/converter.hpp | 20 +++- apps/essimporter/importcntc.cpp | 16 +++ apps/essimporter/importcntc.hpp | 25 ++++ apps/essimporter/importer.cpp | 1 + apps/essimporter/importercontext.hpp | 2 + components/CMakeLists.txt | 2 +- components/esm/loadcrec.hpp | 49 -------- components/esm/records.hpp | 1 - 12 files changed, 189 insertions(+), 120 deletions(-) create mode 100644 apps/essimporter/convertcntc.cpp create mode 100644 apps/essimporter/convertcntc.hpp create mode 100644 apps/essimporter/importcntc.cpp create mode 100644 apps/essimporter/importcntc.hpp delete mode 100644 components/esm/loadcrec.hpp diff --git a/apps/essimporter/CMakeLists.txt b/apps/essimporter/CMakeLists.txt index 1af55de9b..8d1af714d 100644 --- a/apps/essimporter/CMakeLists.txt +++ b/apps/essimporter/CMakeLists.txt @@ -8,12 +8,14 @@ set(ESSIMPORTER_FILES importacdt.cpp importinventory.cpp importklst.cpp + importcntc.cpp importercontext.cpp converter.cpp convertacdt.cpp convertnpcc.cpp convertinventory.cpp convertcrec.cpp + convertcntc.cpp ) add_executable(openmw-essimporter diff --git a/apps/essimporter/convertcntc.cpp b/apps/essimporter/convertcntc.cpp new file mode 100644 index 000000000..426ef4496 --- /dev/null +++ b/apps/essimporter/convertcntc.cpp @@ -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); + } + +} diff --git a/apps/essimporter/convertcntc.hpp b/apps/essimporter/convertcntc.hpp new file mode 100644 index 000000000..c299d87a1 --- /dev/null +++ b/apps/essimporter/convertcntc.hpp @@ -0,0 +1,15 @@ +#ifndef OPENMW_ESSIMPORT_CONVERTCNTC_H +#define OPENMW_ESSIMPORT_CONVERTCNTC_H + +#include "importcntc.hpp" + +#include + +namespace ESSImport +{ + + void convertCNTC(const CNTC& cntc, ESM::ContainerState& state); + +} + +#endif diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 22d6d8282..293c48624 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -3,8 +3,10 @@ #include #include +#include #include "convertcrec.hpp" +#include "convertcntc.hpp" namespace { @@ -17,6 +19,13 @@ namespace screenshot.save(out); } + + void convertCellRef(const ESSImport::CellRef& cellref, ESM::ObjectState& objstate) + { + objstate.mEnabled = cellref.mEnabled; + objstate.mPosition = cellref.mPos; + objstate.mRef.mRefNum = cellref.mRefNum; + } } namespace ESSImport @@ -116,8 +125,9 @@ namespace ESSImport ref.load (esm); if (esm.isNextSub("DELE")) { + // TODO // strangely this can be e.g. 52 instead of just 1, - std::cout << "deleted ref " << ref.mIndexedRefId << std::endl; + //std::cout << "deleted ref " << ref.mIndexedRefId << std::endl; esm.skipHSub(); } cellrefs.push_back(ref); @@ -155,85 +165,104 @@ namespace ESSImport newcell.mRefs = cellrefs; - // FIXME: map by ID for exterior cells - mCells[id] = newcell; + + if (cell.isExterior()) + mExtCells[std::make_pair(cell.mData.mX, cell.mData.mY)] = newcell; + else + mIntCells[id] = newcell; } - void ConvertCell::write(ESM::ESMWriter &esm) + void ConvertCell::writeCell(const Cell &cell, ESM::ESMWriter& esm) { - for (std::map::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::const_iterator refIt = cell.mRefs.begin(); refIt != cell.mRefs.end(); ++refIt) { - const ESM::Cell& cell = it->second.mCell; - esm.startRecord(ESM::REC_CSTA); - ESM::CellState csta; - csta.mHasFogOfWar = 0; - csta.mId = cell.getCellId(); - csta.mId.save(esm); - // TODO csta.mLastRespawn; - // shouldn't be needed if we respawn on global schedule like in original MW - csta.mWaterLevel = cell.mWater; - csta.save(esm); - - for (std::vector::const_iterator refIt = it->second.mRefs.begin(); refIt != it->second.mRefs.end(); ++refIt) - { - const CellRef& cellref = *refIt; - ESM::CellRef out; - out.blank(); + const CellRef& cellref = *refIt; + ESM::CellRef out; + out.blank(); - if (cellref.mIndexedRefId.size() < 8) - { - std::cerr << "CellRef with no index?" << std::endl; - continue; - } - std::stringstream stream; - stream << cellref.mIndexedRefId.substr(cellref.mIndexedRefId.size()-8,8); - int refIndex; - stream >> refIndex; + if (cellref.mIndexedRefId.size() < 8) + { + std::cerr << "CellRef with no index?" << std::endl; + continue; + } + std::stringstream stream; + stream << cellref.mIndexedRefId.substr(cellref.mIndexedRefId.size()-8,8); + int refIndex; + stream >> refIndex; - out.mRefID = cellref.mIndexedRefId.substr(0,cellref.mIndexedRefId.size()-8); + out.mRefID = cellref.mIndexedRefId.substr(0,cellref.mIndexedRefId.size()-8); - std::map, CREC>::const_iterator crecIt = mContext->mCreatureChanges.find( - std::make_pair(refIndex, out.mRefID)); - if (crecIt != mContext->mCreatureChanges.end()) - { - ESM::CreatureState objstate; - objstate.blank(); - convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); - convertCREC(crecIt->second, objstate); - objstate.mEnabled = cellref.mEnabled; - objstate.mPosition = cellref.mPos; - objstate.mRef = out; - objstate.mRef.mRefNum = cellref.mRefNum; - // FIXME: change save format to not require object type, instead look up it up using the RefId - esm.writeHNT ("OBJE", ESM::REC_CREA); - objstate.save(esm); - continue; - } + std::map, CREC>::const_iterator crecIt = mContext->mCreatureChanges.find( + std::make_pair(refIndex, out.mRefID)); + if (crecIt != mContext->mCreatureChanges.end()) + { + ESM::CreatureState objstate; + objstate.blank(); + objstate.mRef = out; + convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); + convertCREC(crecIt->second, objstate); + convertCellRef(cellref, objstate); + // FIXME: change save format to not require object type, instead look up it up using the RefId + esm.writeHNT ("OBJE", ESM::REC_CREA); + objstate.save(esm); + continue; + } - std::map, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find( - std::make_pair(refIndex, out.mRefID)); - if (npccIt != mContext->mNpcChanges.end()) - { - ESM::NpcState objstate; - objstate.blank(); - convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); - convertNpcData(cellref.mActorData, objstate.mNpcStats); - convertNPCC(npccIt->second, objstate); - objstate.mEnabled = cellref.mEnabled; - objstate.mPosition = cellref.mPos; - objstate.mRef = out; - objstate.mRef.mRefNum = cellref.mRefNum; - esm.writeHNT ("OBJE", ESM::REC_NPC_); - objstate.save(esm); - continue; - } + std::map, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find( + std::make_pair(refIndex, out.mRefID)); + if (npccIt != mContext->mNpcChanges.end()) + { + ESM::NpcState objstate; + objstate.blank(); + objstate.mRef = out; + convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); + convertNpcData(cellref.mActorData, objstate.mNpcStats); + convertNPCC(npccIt->second, objstate); + convertCellRef(cellref, objstate); + esm.writeHNT ("OBJE", ESM::REC_NPC_); + objstate.save(esm); + continue; + } - std::cerr << "Can't find type for " << refIndex << " " << out.mRefID << std::endl; + std::map, 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; + convertCNTC(cntcIt->second, objstate); + convertCellRef(cellref, objstate); + esm.writeHNT ("OBJE", ESM::REC_CONT); + objstate.save(esm); + continue; } - esm.endRecord(ESM::REC_CSTA); + std::cerr << "Can't find type for " << refIndex << " " << out.mRefID << std::endl; } + esm.endRecord(ESM::REC_CSTA); + } + + void ConvertCell::write(ESM::ESMWriter &esm) + { + for (std::map::const_iterator it = mIntCells.begin(); it != mIntCells.end(); ++it) + writeCell(it->second, esm); + + for (std::map, Cell>::const_iterator it = mExtCells.begin(); it != mExtCells.end(); ++it) + writeCell(it->second, esm); + for (std::vector::const_iterator it = mMarkers.begin(); it != mMarkers.end(); ++it) { esm.startRecord(ESM::REC_MARK); diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index aee60f2e7..e758db964 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -12,6 +12,7 @@ #include #include "importcrec.hpp" +#include "importcntc.hpp" #include "importercontext.hpp" #include "importcellref.hpp" @@ -94,7 +95,8 @@ public: mContext->mPlayer.mObject.mCreatureStats.mLevel = npc.mNpdt52.mLevel; mContext->mPlayerBase = npc; std::map 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::const_iterator it = npc.mSpells.mList.begin(); it != npc.mSpells.mList.end(); ++it) mContext->mPlayer.mObject.mCreatureStats.mSpells.mSpells[*it] = empty; } @@ -209,6 +211,17 @@ public: } }; +class ConvertCNTC : public Converter +{ + virtual void read(ESM::ESMReader &esm) + { + std::string id = esm.getHNString("NAME"); + CNTC cntc; + cntc.load(esm); + mContext->mContainerChanges.insert(std::make_pair(std::make_pair(cntc.mIndex,id), cntc)); + } +}; + class ConvertCREC : public Converter { public: @@ -242,9 +255,12 @@ private: std::vector mFogOfWar; }; - std::map mCells; + std::map mIntCells; + std::map, Cell> mExtCells; std::vector mMarkers; + + void writeCell(const Cell& cell, ESM::ESMWriter &esm); }; class ConvertKLST : public Converter diff --git a/apps/essimporter/importcntc.cpp b/apps/essimporter/importcntc.cpp new file mode 100644 index 000000000..a492aef5a --- /dev/null +++ b/apps/essimporter/importcntc.cpp @@ -0,0 +1,16 @@ +#include "importcntc.hpp" + +#include + +namespace ESSImport +{ + + void CNTC::load(ESM::ESMReader &esm) + { + mIndex = 0; + esm.getHNT(mIndex, "INDX"); + + mInventory.load(esm); + } + +} diff --git a/apps/essimporter/importcntc.hpp b/apps/essimporter/importcntc.hpp new file mode 100644 index 000000000..1bc7d94bd --- /dev/null +++ b/apps/essimporter/importcntc.hpp @@ -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 diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 4cf41a861..9075db404 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -215,6 +215,7 @@ namespace ESSImport converters[ESM::REC_WEAP] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_LEVC] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_LEVI] = boost::shared_ptr(new DefaultConverter()); + converters[ESM::REC_CNTC] = boost::shared_ptr(new ConvertCNTC()); std::set unknownRecords; diff --git a/apps/essimporter/importercontext.hpp b/apps/essimporter/importercontext.hpp index 9183e6179..a921b81ef 100644 --- a/apps/essimporter/importercontext.hpp +++ b/apps/essimporter/importercontext.hpp @@ -8,6 +8,7 @@ #include "importnpcc.hpp" #include "importcrec.hpp" +#include "importcntc.hpp" #include "importplayer.hpp" @@ -30,6 +31,7 @@ namespace ESSImport // key std::map, CREC> mCreatureChanges; std::map, NPCC> mNpcChanges; + std::map, CNTC> mContainerChanges; Context() { diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 93228f87a..a0500127c 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -57,7 +57,7 @@ add_component_dir (to_utf8 add_component_dir (esm attr defs esmcommon esmreader esmwriter loadacti loadalch loadappa loadarmo loadbody loadbook loadbsgn loadcell - loadclas loadclot loadcont loadcrea loadcrec loaddial loaddoor loadench loadfact loadglob loadgmst + loadclas loadclot loadcont loadcrea loaddial loaddoor loadench loadfact loadglob loadgmst loadinfo loadingr loadland loadlevlist loadligh loadlock loadprob loadrepa loadltex loadmgef loadmisc loadnpcc loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter diff --git a/components/esm/loadcrec.hpp b/components/esm/loadcrec.hpp deleted file mode 100644 index 280739aca..000000000 --- a/components/esm/loadcrec.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef OPENMW_ESM_CREC_H -#define OPENMW_ESM_CREC_H - -#include - -// 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 diff --git a/components/esm/records.hpp b/components/esm/records.hpp index 7a0452eb3..c01c89d57 100644 --- a/components/esm/records.hpp +++ b/components/esm/records.hpp @@ -14,7 +14,6 @@ #include "loadclot.hpp" #include "loadcont.hpp" #include "loadcrea.hpp" -#include "loadcrec.hpp" #include "loadinfo.hpp" #include "loaddial.hpp" #include "loaddoor.hpp" From 9014dc48ee166ff9d3e5750971787056a318356e Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 19 Jan 2015 23:29:06 +0100 Subject: [PATCH 05/21] Don't require the object type id for reading references from savegames This is redundant, since we can look it up from the RefID. --- apps/openmw/mwclass/creature.cpp | 10 ++++- apps/openmw/mwclass/npc.cpp | 9 +++++ apps/openmw/mwworld/cellstore.cpp | 65 +++++++++++++++++++------------ components/esm/cellref.cpp | 8 +++- components/esm/cellref.hpp | 3 ++ components/esm/creaturestate.cpp | 14 +++++-- components/esm/esmreader.cpp | 11 ++++++ components/esm/esmreader.hpp | 3 ++ components/esm/inventorystate.cpp | 1 + components/esm/npcstate.cpp | 19 ++++++--- components/esm/objectstate.cpp | 10 ++++- components/esm/objectstate.hpp | 8 ++++ components/esm/player.cpp | 1 + 13 files changed, 123 insertions(+), 39 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index a3614af96..2e53fe802 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -816,6 +816,9 @@ namespace MWClass void Creature::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const { + if (!state.mHasCustomState) + return; + const ESM::CreatureState& state2 = dynamic_cast (state); ensureCustomData(ptr); @@ -844,7 +847,6 @@ namespace MWClass customData.mContainerStore->readState (state2.mInventory); customData.mCreatureStats.readState (state2.mCreatureStats); - } void Creature::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) @@ -852,6 +854,12 @@ namespace MWClass { ESM::CreatureState& state2 = dynamic_cast (state); + if (!ptr.getRefData().getCustomData()) + { + state.mHasCustomState = false; + return; + } + ensureCustomData (ptr); CreatureCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index ac4182734..8f62cc1e8 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -1275,6 +1275,9 @@ namespace MWClass void Npc::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const { + if (!state.mHasCustomState) + return; + const ESM::NpcState& state2 = dynamic_cast (state); ensureCustomData(ptr); @@ -1302,6 +1305,12 @@ namespace MWClass { ESM::NpcState& state2 = dynamic_cast (state); + if (!ptr.getRefData().getCustomData()) + { + state.mHasCustomState = false; + return; + } + ensureCustomData (ptr); NpcCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 4cf99fdb7..605c8f9d2 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -85,7 +85,9 @@ namespace RecordType state; iter->save (state); + // recordId currently unused writer.writeHNT ("OBJE", collection.mList.front().mBase->sRecordId); + state.save (writer); } } @@ -93,12 +95,13 @@ namespace template void readReferenceCollection (ESM::ESMReader& reader, - MWWorld::CellRefList& collection, const std::map& contentFileMap) + MWWorld::CellRefList& collection, const ESM::CellRef& cref, const std::map& contentFileMap) { const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); RecordType state; - state.load (reader); + state.mRef = cref; + state.load(reader); // If the reference came from a content file, make sure this content file is loaded if (state.mRef.mRefNum.hasContentFile()) @@ -640,109 +643,121 @@ namespace MWWorld while (reader.isNextSub ("OBJE")) { - unsigned int id = 0; - reader.getHT (id); + unsigned int unused; + reader.getHT (unused); + + // load the RefID first so we know what type of object it is + ESM::CellRef cref; + cref.loadId(reader, true); + + int type = MWBase::Environment::get().getWorld()->getStore().find(cref.mRefID); + if (type == 0) + { + std::cerr << "Dropping reference to '" << cref.mRefID << "' (object no longer exists)" << std::endl; + reader.skipHSubUntil("OBJE"); + continue; + } - switch (id) + switch (type) { case ESM::REC_ACTI: - readReferenceCollection (reader, mActivators, contentFileMap); + readReferenceCollection (reader, mActivators, cref, contentFileMap); break; case ESM::REC_ALCH: - readReferenceCollection (reader, mPotions, contentFileMap); + readReferenceCollection (reader, mPotions, cref, contentFileMap); break; case ESM::REC_APPA: - readReferenceCollection (reader, mAppas, contentFileMap); + readReferenceCollection (reader, mAppas, cref, contentFileMap); break; case ESM::REC_ARMO: - readReferenceCollection (reader, mArmors, contentFileMap); + readReferenceCollection (reader, mArmors, cref, contentFileMap); break; case ESM::REC_BOOK: - readReferenceCollection (reader, mBooks, contentFileMap); + readReferenceCollection (reader, mBooks, cref, contentFileMap); break; case ESM::REC_CLOT: - readReferenceCollection (reader, mClothes, contentFileMap); + readReferenceCollection (reader, mClothes, cref, contentFileMap); break; case ESM::REC_CONT: - readReferenceCollection (reader, mContainers, contentFileMap); + readReferenceCollection (reader, mContainers, cref, contentFileMap); break; case ESM::REC_CREA: - readReferenceCollection (reader, mCreatures, contentFileMap); + readReferenceCollection (reader, mCreatures, cref, contentFileMap); break; case ESM::REC_DOOR: - readReferenceCollection (reader, mDoors, contentFileMap); + readReferenceCollection (reader, mDoors, cref, contentFileMap); break; case ESM::REC_INGR: - readReferenceCollection (reader, mIngreds, contentFileMap); + readReferenceCollection (reader, mIngreds, cref, contentFileMap); break; case ESM::REC_LEVC: - readReferenceCollection (reader, mCreatureLists, contentFileMap); + readReferenceCollection (reader, mCreatureLists, cref, contentFileMap); break; case ESM::REC_LEVI: - readReferenceCollection (reader, mItemLists, contentFileMap); + readReferenceCollection (reader, mItemLists, cref, contentFileMap); break; case ESM::REC_LIGH: - readReferenceCollection (reader, mLights, contentFileMap); + readReferenceCollection (reader, mLights, cref, contentFileMap); break; case ESM::REC_LOCK: - readReferenceCollection (reader, mLockpicks, contentFileMap); + readReferenceCollection (reader, mLockpicks, cref, contentFileMap); break; case ESM::REC_MISC: - readReferenceCollection (reader, mMiscItems, contentFileMap); + readReferenceCollection (reader, mMiscItems, cref, contentFileMap); break; case ESM::REC_NPC_: - readReferenceCollection (reader, mNpcs, contentFileMap); + readReferenceCollection (reader, mNpcs, cref, contentFileMap); break; case ESM::REC_PROB: - readReferenceCollection (reader, mProbes, contentFileMap); + readReferenceCollection (reader, mProbes, cref, contentFileMap); break; case ESM::REC_REPA: - readReferenceCollection (reader, mRepairs, contentFileMap); + readReferenceCollection (reader, mRepairs, cref, contentFileMap); break; case ESM::REC_STAT: - readReferenceCollection (reader, mStatics, contentFileMap); + readReferenceCollection (reader, mStatics, cref, contentFileMap); break; case ESM::REC_WEAP: - readReferenceCollection (reader, mWeapons, contentFileMap); + readReferenceCollection (reader, mWeapons, cref, contentFileMap); break; default: diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 0c2dfd5db..cd9fe8570 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -5,6 +5,12 @@ #include "esmwriter.hpp" void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) +{ + loadId(esm, wideRefNum); + loadData(esm); +} + +void ESM::CellRef::loadId(ESMReader &esm, bool wideRefNum) { // According to Hrnchamd, this does not belong to the actual ref. Instead, it is a marker indicating that // the following refs are part of a "temp refs" section. A temp ref is not being tracked by the moved references system. @@ -19,8 +25,6 @@ void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) esm.getHNT (mRefNum.mIndex, "FRMR"); mRefID = esm.getHNString ("NAME"); - - loadData(esm); } void ESM::CellRef::loadData(ESMReader &esm) diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index b7b00a654..7aa0e4d44 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -89,8 +89,11 @@ namespace ESM // Position and rotation of this object within the cell Position mPos; + /// Calls loadId and loadData void load (ESMReader& esm, bool wideRefNum = false); + void loadId (ESMReader& esm, bool wideRefNum = false); + /// Implicitly called by load void loadData (ESMReader& esm); diff --git a/components/esm/creaturestate.cpp b/components/esm/creaturestate.cpp index c2838f78d..c15becd98 100644 --- a/components/esm/creaturestate.cpp +++ b/components/esm/creaturestate.cpp @@ -5,18 +5,24 @@ void ESM::CreatureState::load (ESMReader &esm) { ObjectState::load (esm); - mInventory.load (esm); + if (mHasCustomState) + { + mInventory.load (esm); - mCreatureStats.load (esm); + mCreatureStats.load (esm); + } } void ESM::CreatureState::save (ESMWriter &esm, bool inInventory) const { ObjectState::save (esm, inInventory); - mInventory.save (esm); + if (mHasCustomState) + { + mInventory.save (esm); - mCreatureStats.save (esm); + mCreatureStats.save (esm); + } } void ESM::CreatureState::blank() diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 7cf0de1a9..de110dd18 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -210,6 +210,17 @@ void ESMReader::skipHSubSize(int size) fail("skipHSubSize() mismatch"); } +void ESMReader::skipHSubUntil(const char *name) +{ + while (hasMoreSubs() && !isNextSub(name)) + { + mCtx.subCached = false; + skipHSub(); + } + if (hasMoreSubs()) + mCtx.subCached = true; +} + void ESMReader::getSubHeader() { if (mCtx.leftRec < 4) diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 642ec4168..bb615af11 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -195,6 +195,9 @@ public: // Skip sub record and check its size void skipHSubSize(int size); + // Skip all subrecords until the given subrecord or no more subrecords remaining + void skipHSubUntil(const char* name); + /* Sub-record header. This updates leftRec beyond the current sub-record as well. leftSub contains size of current sub-record. */ diff --git a/components/esm/inventorystate.cpp b/components/esm/inventorystate.cpp index fdaba3322..b946f9501 100644 --- a/components/esm/inventorystate.cpp +++ b/components/esm/inventorystate.cpp @@ -11,6 +11,7 @@ namespace slot = -1; esm.getHNOT (slot, "SLOT"); + state.mRef.loadId(esm, true); state.load (esm); } diff --git a/components/esm/npcstate.cpp b/components/esm/npcstate.cpp index 6134193c2..724d67326 100644 --- a/components/esm/npcstate.cpp +++ b/components/esm/npcstate.cpp @@ -5,22 +5,28 @@ void ESM::NpcState::load (ESMReader &esm) { ObjectState::load (esm); - mInventory.load (esm); + if (mHasCustomState) + { + mInventory.load (esm); - mNpcStats.load (esm); + mNpcStats.load (esm); - mCreatureStats.load (esm); + mCreatureStats.load (esm); + } } void ESM::NpcState::save (ESMWriter &esm, bool inInventory) const { ObjectState::save (esm, inInventory); - mInventory.save (esm); + if (mHasCustomState) + { + mInventory.save (esm); - mNpcStats.save (esm); + mNpcStats.save (esm); - mCreatureStats.save (esm); + mCreatureStats.save (esm); + } } void ESM::NpcState::blank() @@ -28,4 +34,5 @@ void ESM::NpcState::blank() ObjectState::blank(); mNpcStats.blank(); mCreatureStats.blank(); + mHasCustomState = true; } diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index 98cf3eba6..66fd49663 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -6,7 +6,7 @@ void ESM::ObjectState::load (ESMReader &esm) { - mRef.load (esm, true); + mRef.loadData(esm); mHasLocals = 0; esm.getHNOT (mHasLocals, "HLOC"); @@ -27,6 +27,10 @@ void ESM::ObjectState::load (ESMReader &esm) // used for lights only mTime = 0; esm.getHNOT (mTime, "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 @@ -53,6 +57,9 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const if (mTime) esm.writeHNT ("LTIM", mTime); + + if (!mHasCustomState) + esm.writeHNT ("HCUS", false); } void ESM::ObjectState::blank() @@ -68,6 +75,7 @@ void ESM::ObjectState::blank() mLocalRotation[i] = 0; } mTime = 0; + mHasCustomState = true; } ESM::ObjectState::~ObjectState() {} diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index 6b536bd01..b03689653 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -28,7 +28,15 @@ namespace ESM float mTime; // Used for lights only. Overhead should not be so awful, besides CellRef isn't OO either + // Is there any class-specific state following the ObjectState + bool mHasCustomState; + + ObjectState() : mHasCustomState(true) + {} + + /// @note Does not load the CellRef ID, it should already be loaded before calling this method virtual void load (ESMReader &esm); + virtual void save (ESMWriter &esm, bool inInventory = false) const; /// Initialize to default state diff --git a/components/esm/player.cpp b/components/esm/player.cpp index 52b44c945..70c4b79e2 100644 --- a/components/esm/player.cpp +++ b/components/esm/player.cpp @@ -6,6 +6,7 @@ void ESM::Player::load (ESMReader &esm) { + mObject.mRef.loadId(esm, true); mObject.load (esm); mCellId.load (esm); From 40c29abe20445d8cdd61b3dc63ce8e63891757c4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 19 Jan 2015 23:51:43 +0100 Subject: [PATCH 06/21] ESSImport: convert other references (non-creature/npc/container) --- apps/essimporter/converter.cpp | 129 ++++++++++++++++++----------- apps/essimporter/converter.hpp | 9 +- apps/essimporter/importcellref.cpp | 9 +- apps/essimporter/importnpcc.cpp | 3 - apps/essimporter/importnpcc.hpp | 2 - 5 files changed, 95 insertions(+), 57 deletions(-) diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 293c48624..83ea077ea 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -26,6 +26,17 @@ namespace 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 @@ -191,65 +202,87 @@ namespace ESSImport ESM::CellRef out; out.blank(); - if (cellref.mIndexedRefId.size() < 8) + if (!isIndexedRefId(cellref.mIndexedRefId)) { - std::cerr << "CellRef with no index?" << std::endl; - continue; - } - std::stringstream stream; - stream << cellref.mIndexedRefId.substr(cellref.mIndexedRefId.size()-8,8); - int refIndex; - stream >> refIndex; + // non-indexed RefNum, i.e. no CREC/NPCC/CNTC record associated with it + // this could be any type of object really (even creatures/npcs too) + out.mRefID = cellref.mIndexedRefId; + std::string idLower = Misc::StringUtils::lowerCase(out.mRefID); - out.mRefID = cellref.mIndexedRefId.substr(0,cellref.mIndexedRefId.size()-8); - - std::map, CREC>::const_iterator crecIt = mContext->mCreatureChanges.find( - std::make_pair(refIndex, out.mRefID)); - if (crecIt != mContext->mCreatureChanges.end()) - { - ESM::CreatureState objstate; + ESM::ObjectState objstate; objstate.blank(); objstate.mRef = out; - convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); - convertCREC(crecIt->second, objstate); + objstate.mRef.mRefID = idLower; + objstate.mHasCustomState = false; convertCellRef(cellref, objstate); - // FIXME: change save format to not require object type, instead look up it up using the RefId - esm.writeHNT ("OBJE", ESM::REC_CREA); + esm.writeHNT ("OBJE", 0); objstate.save(esm); continue; } - - std::map, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find( - std::make_pair(refIndex, out.mRefID)); - if (npccIt != mContext->mNpcChanges.end()) + else { - ESM::NpcState objstate; - objstate.blank(); - objstate.mRef = out; - convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); - convertNpcData(cellref.mActorData, objstate.mNpcStats); - convertNPCC(npccIt->second, objstate); - convertCellRef(cellref, objstate); - esm.writeHNT ("OBJE", ESM::REC_NPC_); - objstate.save(esm); - continue; - } + std::stringstream stream; + stream << std::hex << cellref.mIndexedRefId.substr(cellref.mIndexedRefId.size()-8,8); + int refIndex; + stream >> refIndex; - std::map, 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; - convertCNTC(cntcIt->second, objstate); - convertCellRef(cellref, objstate); - esm.writeHNT ("OBJE", ESM::REC_CONT); - objstate.save(esm); - continue; - } + out.mRefID = cellref.mIndexedRefId.substr(0,cellref.mIndexedRefId.size()-8); + std::string idLower = Misc::StringUtils::lowerCase(out.mRefID); + + std::map, NPCC>::const_iterator npccIt = mContext->mNpcChanges.find( + std::make_pair(refIndex, out.mRefID)); + if (npccIt != mContext->mNpcChanges.end()) + { + ESM::NpcState objstate; + objstate.blank(); + objstate.mRef = out; + objstate.mRef.mRefID = idLower; + // probably need more micromanagement here so we don't overwrite values + // from the ESM with default values + convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); + convertNpcData(cellref.mActorData, objstate.mNpcStats); + convertNPCC(npccIt->second, objstate); + convertCellRef(cellref, objstate); + esm.writeHNT ("OBJE", ESM::REC_NPC_); + objstate.save(esm); + continue; + } + + std::map, 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.mRefID = idLower; + convertCNTC(cntcIt->second, objstate); + convertCellRef(cellref, objstate); + esm.writeHNT ("OBJE", ESM::REC_CONT); + objstate.save(esm); + continue; + } - std::cerr << "Can't find type for " << refIndex << " " << out.mRefID << std::endl; + std::map, CREC>::const_iterator crecIt = mContext->mCreatureChanges.find( + std::make_pair(refIndex, out.mRefID)); + if (crecIt != mContext->mCreatureChanges.end()) + { + ESM::CreatureState objstate; + objstate.blank(); + objstate.mRef = out; + objstate.mRef.mRefID = idLower; + convertACDT(cellref.mActorData.mACDT, objstate.mCreatureStats); + // probably need more micromanagement here so we don't overwrite values + // from the ESM with default values + convertCREC(crecIt->second, objstate); + convertCellRef(cellref, objstate); + esm.writeHNT ("OBJE", ESM::REC_CREA); + objstate.save(esm); + continue; + } + + std::cerr << "Can't find type for " << cellref.mIndexedRefId << std::endl; + } } esm.endRecord(ESM::REC_CSTA); diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index e758db964..187386b9d 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -167,8 +167,14 @@ public: convertNPCC(npcc, mContext->mPlayer.mObject); } else - mContext->mNpcChanges.insert(std::make_pair(std::make_pair(npcc.mIndex,id), npcc)); + { + int index = mIndexCounter[id]++; + mContext->mNpcChanges.insert(std::make_pair(std::make_pair(index,id), npcc)).second; + } } + +private: + std::map mIndexCounter; }; class ConvertREFR : public Converter @@ -230,7 +236,6 @@ public: std::string id = esm.getHNString("NAME"); CREC crec; crec.load(esm); - mContext->mCreatureChanges.insert(std::make_pair(std::make_pair(crec.mIndex,id), crec)); } }; diff --git a/apps/essimporter/importcellref.cpp b/apps/essimporter/importcellref.cpp index 8c2b8f36d..fd58d5bc1 100644 --- a/apps/essimporter/importcellref.cpp +++ b/apps/essimporter/importcellref.cpp @@ -18,8 +18,13 @@ namespace ESSImport mIndexedRefId = esm.getHNString("NAME"); if (esm.isNextSub("LVCR")) - esm.skipHSub(); - + { + // occurs on leveled creature spawner references + // probably some identifier for the the creature that has been spawned? + unsigned char lvcr; + esm.getHT(lvcr); + //std::cout << "LVCR: " << (int)lvcr << std::endl; + } mActorData.load(esm); mEnabled = true; diff --git a/apps/essimporter/importnpcc.cpp b/apps/essimporter/importnpcc.cpp index 8400a9e4a..c389ede7f 100644 --- a/apps/essimporter/importnpcc.cpp +++ b/apps/essimporter/importnpcc.cpp @@ -7,9 +7,6 @@ namespace ESSImport void NPCC::load(ESM::ESMReader &esm) { - mIndex = 0; - esm.getHNOT(mIndex, "INDX"); - esm.getHNT(mNPDT, "NPDT"); if (esm.isNextSub("AI_E")) diff --git a/apps/essimporter/importnpcc.hpp b/apps/essimporter/importnpcc.hpp index f3c4e24c7..2f474aba0 100644 --- a/apps/essimporter/importnpcc.hpp +++ b/apps/essimporter/importnpcc.hpp @@ -26,8 +26,6 @@ namespace ESSImport Inventory mInventory; - int mIndex; - void load(ESM::ESMReader &esm); }; From 5104a5a0235061454c8aae984e88dce6b928be8c Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 19 Jan 2015 23:55:17 +0100 Subject: [PATCH 07/21] Add missing setFactionReaction instruction, use absolute storage instead of difference Seems to be closer to how MW is storing it (it has the complete FACT record in the savegame, actually). This (somewhat) breaks OMW savegame compatibility in that old changes are discarded, but I don't think the faction reactions are quest relevant anywhere. --- apps/openmw/mwbase/dialoguemanager.hpp | 2 ++ apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 36 ++++++++++++------- apps/openmw/mwdialogue/dialoguemanagerimp.hpp | 4 ++- apps/openmw/mwscript/dialogueextensions.cpp | 20 +++++++++++ apps/openmw/mwscript/docs/vmformat.txt | 3 +- components/compiler/extensions0.cpp | 1 + components/compiler/opcodes.hpp | 1 + components/esm/dialoguestate.cpp | 17 ++++++--- components/esm/dialoguestate.hpp | 2 +- 9 files changed, 66 insertions(+), 20 deletions(-) diff --git a/apps/openmw/mwbase/dialoguemanager.hpp b/apps/openmw/mwbase/dialoguemanager.hpp index d0e64b23c..d35b5950f 100644 --- a/apps/openmw/mwbase/dialoguemanager.hpp +++ b/apps/openmw/mwbase/dialoguemanager.hpp @@ -74,6 +74,8 @@ namespace MWBase /// Changes faction1's opinion of faction2 by \a diff. virtual void modFactionReaction (const std::string& faction1, const std::string& faction2, int diff) = 0; + virtual void setFactionReaction (const std::string& faction1, const std::string& faction2, int absolute) = 0; + /// @return faction1's opinion of faction2 virtual int getFactionReaction (const std::string& faction1, const std::string& faction2) const = 0; diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index dd0fa21fa..0ee7dfe94 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -640,7 +640,7 @@ namespace MWDialogue if (iter->second) state.mKnownTopics.push_back (iter->first); - state.mModFactionReaction = mModFactionReaction; + state.mChangedFactionReaction = mChangedFactionReaction; writer.startRecord (ESM::REC_DIAS); state.save (writer); @@ -661,7 +661,7 @@ namespace MWDialogue if (store.get().search (*iter)) mKnownTopics.insert (std::make_pair (*iter, true)); - mModFactionReaction = state.mModFactionReaction; + mChangedFactionReaction = state.mChangedFactionReaction; } } @@ -674,10 +674,23 @@ namespace MWDialogue MWBase::Environment::get().getWorld()->getStore().get().find(fact1); MWBase::Environment::get().getWorld()->getStore().get().find(fact2); - std::map& map = mModFactionReaction[fact1]; - if (map.find(fact2) == map.end()) - map[fact2] = 0; - map[fact2] += diff; + int newValue = getFactionReaction(faction1, faction2) + diff; + + std::map& map = mChangedFactionReaction[fact1]; + map[fact2] = newValue; + } + + void DialogueManager::setFactionReaction(const std::string &faction1, const std::string &faction2, int absolute) + { + std::string fact1 = Misc::StringUtils::lowerCase(faction1); + std::string fact2 = Misc::StringUtils::lowerCase(faction2); + + // Make sure the factions exist + MWBase::Environment::get().getWorld()->getStore().get().find(fact1); + MWBase::Environment::get().getWorld()->getStore().get().find(fact2); + + std::map& map = mChangedFactionReaction[fact1]; + map[fact2] = absolute; } int DialogueManager::getFactionReaction(const std::string &faction1, const std::string &faction2) const @@ -685,10 +698,9 @@ namespace MWDialogue std::string fact1 = Misc::StringUtils::lowerCase(faction1); std::string fact2 = Misc::StringUtils::lowerCase(faction2); - ModFactionReactionMap::const_iterator map = mModFactionReaction.find(fact1); - int diff = 0; - if (map != mModFactionReaction.end() && map->second.find(fact2) != map->second.end()) - diff = map->second.at(fact2); + ModFactionReactionMap::const_iterator map = mChangedFactionReaction.find(fact1); + if (map != mChangedFactionReaction.end() && map->second.find(fact2) != map->second.end()) + return map->second.at(fact2); const ESM::Faction* faction = MWBase::Environment::get().getWorld()->getStore().get().find(fact1); @@ -696,9 +708,9 @@ namespace MWDialogue for (; it != faction->mReactions.end(); ++it) { if (Misc::StringUtils::ciEqual(it->first, fact2)) - return it->second + diff; + return it->second; } - return diff; + return 0; } void DialogueManager::clearInfoActor(const MWWorld::Ptr &actor) const diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp index 9c3d0d54b..c06299736 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.hpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.hpp @@ -27,7 +27,7 @@ namespace MWDialogue // Modified faction reactions. > typedef std::map > ModFactionReactionMap; - ModFactionReactionMap mModFactionReaction; + ModFactionReactionMap mChangedFactionReaction; std::list mActorKnownTopics; @@ -97,6 +97,8 @@ namespace MWDialogue /// Changes faction1's opinion of faction2 by \a diff. virtual void modFactionReaction (const std::string& faction1, const std::string& faction2, int diff); + virtual void setFactionReaction (const std::string& faction1, const std::string& faction2, int absolute); + /// @return faction1's opinion of faction2 virtual int getFactionReaction (const std::string& faction1, const std::string& faction2) const; diff --git a/apps/openmw/mwscript/dialogueextensions.cpp b/apps/openmw/mwscript/dialogueextensions.cpp index 563a9dde3..1d82b8418 100644 --- a/apps/openmw/mwscript/dialogueextensions.cpp +++ b/apps/openmw/mwscript/dialogueextensions.cpp @@ -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 OpClearInfoActor : public Interpreter::Opcode0 { @@ -268,6 +287,7 @@ namespace MWScript interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFaction, new OpSameFaction); interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFactionExplicit, new OpSameFaction); interpreter.installSegment5 (Compiler::Dialogue::opcodeModFactionReaction, new OpModFactionReaction); + interpreter.installSegment5 (Compiler::Dialogue::opcodeSetFactionReaction, new OpSetFactionReaction); interpreter.installSegment5 (Compiler::Dialogue::opcodeGetFactionReaction, new OpGetFactionReaction); interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActor, new OpClearInfoActor); interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActorExplicit, new OpClearInfoActor); diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 172e1b528..b139d6138 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -442,5 +442,6 @@ op 0x20002fb: AddToLevCreature op 0x20002fc: RemoveFromLevCreature op 0x20002fd: AddToLevItem op 0x20002fe: RemoveFromLevItem +op 0x20002ff: SetFactionReaction -opcodes 0x20002ff-0x3ffffff unused +opcodes 0x2000300-0x3ffffff unused diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 234c5b12d..a5cc0da62 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -192,6 +192,7 @@ namespace Compiler extensions.registerFunction("samefaction", 'l', "", opcodeSameFaction, opcodeSameFactionExplicit); extensions.registerInstruction("modfactionreaction", "ccl", opcodeModFactionReaction); + extensions.registerInstruction("setfactionreaction", "ccl", opcodeSetFactionReaction); extensions.registerFunction("getfactionreaction", 'l', "ccX", opcodeGetFactionReaction); extensions.registerInstruction("clearinfoactor", "", opcodeClearInfoActor, opcodeClearInfoActorExplicit); } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index bbafd6b13..04666e976 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -167,6 +167,7 @@ namespace Compiler const int opcodeSameFaction = 0x20001b5; const int opcodeSameFactionExplicit = 0x20001b6; const int opcodeModFactionReaction = 0x2000242; + const int opcodeSetFactionReaction = 0x20002ff; const int opcodeGetFactionReaction = 0x2000243; const int opcodeClearInfoActor = 0x2000245; const int opcodeClearInfoActorExplicit = 0x2000246; diff --git a/components/esm/dialoguestate.cpp b/components/esm/dialoguestate.cpp index 14301ac19..f546462a3 100644 --- a/components/esm/dialoguestate.cpp +++ b/components/esm/dialoguestate.cpp @@ -13,13 +13,20 @@ void ESM::DialogueState::load (ESMReader &esm) { std::string faction = esm.getHString(); - while (esm.isNextSub ("REAC")) + while (esm.isNextSub("REA2")) { std::string faction2 = esm.getHString(); int reaction; esm.getHNT(reaction, "INTV"); + mChangedFactionReaction[faction][faction2] = reaction; + } - mModFactionReaction[faction][faction2] = reaction; + // no longer used + while (esm.isNextSub ("REAC")) + { + esm.skipHSub(); + esm.getSubHeader(); + esm.skipHSub(); } } } @@ -32,15 +39,15 @@ void ESM::DialogueState::save (ESMWriter &esm) const esm.writeHNString ("TOPI", *iter); } - for (std::map >::const_iterator iter = mModFactionReaction.begin(); - iter != mModFactionReaction.end(); ++iter) + for (std::map >::const_iterator iter = mChangedFactionReaction.begin(); + iter != mChangedFactionReaction.end(); ++iter) { esm.writeHNString ("FACT", iter->first); for (std::map::const_iterator reactIter = iter->second.begin(); reactIter != iter->second.end(); ++reactIter) { - esm.writeHNString ("REAC", reactIter->first); + esm.writeHNString ("REA2", reactIter->first); esm.writeHNT ("INTV", reactIter->second); } } diff --git a/components/esm/dialoguestate.hpp b/components/esm/dialoguestate.hpp index 5e5f602a3..1adade5a0 100644 --- a/components/esm/dialoguestate.hpp +++ b/components/esm/dialoguestate.hpp @@ -16,7 +16,7 @@ namespace ESM { std::vector mKnownTopics; - std::map > mModFactionReaction; + std::map > mChangedFactionReaction; void load (ESMReader &esm); void save (ESMWriter &esm) const; From 6d5bb57e00ea23f573731f7ed15f31f928a76576 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 01:21:44 +0100 Subject: [PATCH 08/21] ESSImport: fix loading item stacks in containers --- apps/essimporter/importinventory.cpp | 43 ++++++++++++++++------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/apps/essimporter/importinventory.cpp b/apps/essimporter/importinventory.cpp index b3bd4a86b..b4d193540 100644 --- a/apps/essimporter/importinventory.cpp +++ b/apps/essimporter/importinventory.cpp @@ -18,28 +18,35 @@ namespace ESSImport item.mId = contItem.mItem.toString(); item.mCount = contItem.mCount; - if (esm.isNextSub("XIDX")) - esm.skipHSub(); - - std::string script = esm.getHNOString("SCRI"); - // script variables? - // unsure if before or after ESM::CellRef - if (!script.empty()) + // seems that a stack of items can have a set of subrecords for each item? rings0000.ess + // doesn't make any sense to me, if the values were different then the items shouldn't stack in the first place? + // I guess we should double check the stacking logic in OpenMW + for (int i=0;i Date: Tue, 20 Jan 2015 01:28:34 +0100 Subject: [PATCH 09/21] ESSImport: convert faction reactions and known dialogue topics --- apps/essimporter/converter.hpp | 26 ++++++++++++++++++++++++++ apps/essimporter/importer.cpp | 7 +++++++ apps/essimporter/importercontext.hpp | 3 +++ apps/essimporter/importplayer.cpp | 1 - components/esm/dialoguestate.hpp | 2 ++ 5 files changed, 38 insertions(+), 1 deletion(-) diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 187386b9d..62729cd03 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include "importcrec.hpp" @@ -214,6 +216,12 @@ public: mContext->mPlayer.mObject.mNpcStats.mFactions[it->mFactionName.toString()] = faction; } + for (std::vector::const_iterator it = pcdt.mKnownDialogueTopics.begin(); + it != pcdt.mKnownDialogueTopics.end(); ++it) + { + mContext->mDialogueState.mKnownTopics.push_back(Misc::StringUtils::lowerCase(*it)); + } + } }; @@ -295,6 +303,24 @@ private: std::map 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::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)); + } + } +}; + } #endif diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 9075db404..e73aaafae 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -181,6 +181,8 @@ namespace ESSImport Ogre::LogManager logman; Ogre::Root root; + // TODO: set up encoding on ESMReader based on openmw.cfg / --encoding switch + ESM::ESMReader esm; esm.open(mEssFile); @@ -216,6 +218,7 @@ namespace ESSImport converters[ESM::REC_LEVC] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_LEVI] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_CNTC] = boost::shared_ptr(new ConvertCNTC()); + converters[ESM::REC_FACT] = boost::shared_ptr(new ConvertFACT()); std::set unknownRecords; @@ -322,6 +325,10 @@ namespace ESSImport } context.mPlayer.save(writer); writer.endRecord(ESM::REC_PLAY); + + writer.startRecord (ESM::REC_DIAS); + context.mDialogueState.save(writer); + writer.endRecord(ESM::REC_DIAS); } diff --git a/apps/essimporter/importercontext.hpp b/apps/essimporter/importercontext.hpp index a921b81ef..02585799a 100644 --- a/apps/essimporter/importercontext.hpp +++ b/apps/essimporter/importercontext.hpp @@ -5,6 +5,7 @@ #include #include +#include #include "importnpcc.hpp" #include "importcrec.hpp" @@ -25,6 +26,8 @@ namespace ESSImport ESM::NPC mPlayerBase; std::string mCustomPlayerClassName; + ESM::DialogueState mDialogueState; + int mDay, mMonth, mYear; float mHour; diff --git a/apps/essimporter/importplayer.cpp b/apps/essimporter/importplayer.cpp index 8a57ae588..f94d3aacc 100644 --- a/apps/essimporter/importplayer.cpp +++ b/apps/essimporter/importplayer.cpp @@ -20,7 +20,6 @@ namespace ESSImport { while (esm.isNextSub("DNAM")) { - // TODO: deal with encoding? mKnownDialogueTopics.push_back(esm.getHString()); } diff --git a/components/esm/dialoguestate.hpp b/components/esm/dialoguestate.hpp index 1adade5a0..d7cdb941c 100644 --- a/components/esm/dialoguestate.hpp +++ b/components/esm/dialoguestate.hpp @@ -14,8 +14,10 @@ namespace ESM struct DialogueState { + // must be lower case topic IDs std::vector mKnownTopics; + // must be lower case faction IDs std::map > mChangedFactionReaction; void load (ESMReader &esm); From 25ff45d819290feaa25a344ffdd491509a25083c Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 15:44:47 +0100 Subject: [PATCH 10/21] Update command line options in Readme --- README.md | 51 +++++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index bb59b7e02..7c24a4c81 100644 --- a/README.md +++ b/README.md @@ -41,51 +41,58 @@ Command line options --version print version information and quit --data arg (=data) set data directories (later directories have higher priority) - --data-local arg set local data directory (highest + --data-local arg set local data directory (highest priority) --fallback-archive arg (=fallback-archive) - set fallback BSA archives (later + set fallback BSA archives (later archives have higher priority) --resources arg (=resources) set resources directory - --start arg (=Beshara) set initial cell - --content arg content file(s): esm/esp, or + --start arg set initial cell + --content arg content file(s): esm/esp, or omwgame/omwaddon - --anim-verbose [=arg(=1)] (=0) output animation indices files --no-sound [=arg(=1)] (=0) disable all sounds --script-verbose [=arg(=1)] (=0) verbose script output --script-all [=arg(=1)] (=0) compile all scripts (excluding dialogue scripts) at startup - --script-console [=arg(=1)] (=0) enable console-only script + --script-all-dialogue [=arg(=1)] (=0) compile all dialogue scripts at startup + --script-console [=arg(=1)] (=0) enable console-only script functionality - --script-run arg select a file containing a list of - console commands that is executed on + --script-run arg select a file containing a list of + console commands that is executed on startup - --script-warn [=arg(=1)] (=1) handling of warnings when compiling + --script-warn [=arg(=1)] (=1) handling of warnings when compiling scripts 0 - ignore warning 1 - show warning but consider script as correctly compiled anyway 2 - treat warnings as errors + --script-blacklist arg ignore the specified script (if the use + of the blacklist is enabled) + --script-blacklist-use [=arg(=1)] (=1) + enable script blacklisting + --load-savegame arg load a save game file on game startup --skip-menu [=arg(=1)] (=0) skip main menu on game startup - --new-game [=arg(=1)] (=0) run new game sequence (ignored if + --new-game [=arg(=1)] (=0) run new game sequence (ignored if skip-menu=0) - --fs-strict [=arg(=1)] (=0) strict file system handling (no case + --fs-strict [=arg(=1)] (=0) strict file system handling (no case folding) - --encoding arg (=win1252) Character encoding used in OpenMW game + --encoding arg (=win1252) Character encoding used in OpenMW game messages: - - win1250 - Central and Eastern European - such as Polish, Czech, Slovak, - Hungarian, Slovene, Bosnian, Croatian, - Serbian (Latin script), Romanian and + + win1250 - Central and Eastern European + such as Polish, Czech, Slovak, + Hungarian, Slovene, Bosnian, Croatian, + Serbian (Latin script), Romanian and Albanian languages - - win1251 - Cyrillic alphabet such as - Russian, Bulgarian, Serbian Cyrillic + + win1251 - Cyrillic alphabet such as + Russian, Bulgarian, Serbian Cyrillic and other languages - - win1252 - Western European (Latin) + + win1252 - Western European (Latin) alphabet, used by default --fallback arg fallback values --no-grab Don't grab mouse cursor + --export-fonts [=arg(=1)] (=0) Export Morrowind .fnt fonts to PNG + image and XML file in current directory --activate-dist arg (=-1) activation distance override From 1375a4e4bb36ac144ba9511451feb1669783b1a3 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 16:06:15 +0100 Subject: [PATCH 11/21] ESSImport: read stolen items (not converted yet) --- apps/essimporter/converter.hpp | 24 ++++++++++++++++++++++++ apps/essimporter/importer.cpp | 10 ++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 62729cd03..716ddc9c7 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -321,6 +321,30 @@ public: } }; +/// 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 mStolenItems; + std::multimap mFactionStolenItems; +}; + } #endif diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index e73aaafae..699725d67 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -195,6 +195,7 @@ namespace ESSImport const unsigned int recPCDT = ESM::FourCC<'P','C','D','T'>::value; const unsigned int recFMAP = ESM::FourCC<'F','M','A','P'>::value; const unsigned int recKLST = ESM::FourCC<'K','L','S','T'>::value; + const unsigned int recSTLN = ESM::FourCC<'S','T','L','N'>::value; std::map > converters; converters[ESM::REC_GLOB] = boost::shared_ptr(new ConvertGlobal()); @@ -202,10 +203,11 @@ namespace ESSImport converters[ESM::REC_NPC_] = boost::shared_ptr(new ConvertNPC()); converters[ESM::REC_NPCC] = boost::shared_ptr(new ConvertNPCC()); converters[ESM::REC_CREC] = boost::shared_ptr(new ConvertCREC()); - converters[recREFR] = boost::shared_ptr(new ConvertREFR()); - converters[recPCDT] = boost::shared_ptr(new ConvertPCDT()); - converters[recFMAP] = boost::shared_ptr(new ConvertFMAP()); - converters[recKLST] = boost::shared_ptr(new ConvertKLST()); + converters[recREFR ] = boost::shared_ptr(new ConvertREFR()); + converters[recPCDT ] = boost::shared_ptr(new ConvertPCDT()); + converters[recFMAP ] = boost::shared_ptr(new ConvertFMAP()); + converters[recKLST ] = boost::shared_ptr(new ConvertKLST()); + converters[recSTLN ] = boost::shared_ptr(new ConvertSTLN()); converters[ESM::REC_CELL] = boost::shared_ptr(new ConvertCell()); converters[ESM::REC_ALCH] = boost::shared_ptr(new DefaultConverter()); converters[ESM::REC_CLAS] = boost::shared_ptr(new ConvertClass()); From 0fc9221eb36058ccb31e4fb57f09cc1a1a3bc2fb Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 18:22:02 +0100 Subject: [PATCH 12/21] ESSImport: convert NPC disposition --- apps/essimporter/convertnpcc.cpp | 1 + apps/essimporter/importnpcc.hpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/essimporter/convertnpcc.cpp b/apps/essimporter/convertnpcc.cpp index 2155f46fb..48d3d9232 100644 --- a/apps/essimporter/convertnpcc.cpp +++ b/apps/essimporter/convertnpcc.cpp @@ -7,6 +7,7 @@ namespace ESSImport void convertNPCC(const NPCC &npcc, ESM::NpcState &npcState) { + npcState.mNpcStats.mDisposition = npcc.mNPDT.mDisposition; npcState.mNpcStats.mReputation = npcc.mNPDT.mReputation; convertInventory(npcc.mInventory, npcState.mInventory); diff --git a/apps/essimporter/importnpcc.hpp b/apps/essimporter/importnpcc.hpp index 2f474aba0..ee8eccda2 100644 --- a/apps/essimporter/importnpcc.hpp +++ b/apps/essimporter/importnpcc.hpp @@ -19,7 +19,8 @@ namespace ESSImport { struct NPDT { - unsigned char unknown[2]; + unsigned char mDisposition; + unsigned char unknown; unsigned char mReputation; unsigned char unknown2[5]; } mNPDT; From d473629dcd6aa6e04d53accc2e7a69314b8fa79c Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 19:12:07 +0100 Subject: [PATCH 13/21] Improve ESMReader error messages --- components/esm/esmreader.cpp | 6 +++++- components/esm/esmreader.hpp | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index de110dd18..3c43d067f 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -134,7 +134,11 @@ void ESMReader::getHExact(void*p, int size) { getSubHeader(); if (size != static_cast (mCtx.leftSub)) - fail("getHExact() size mismatch"); + { + std::stringstream error; + error << "getHExact(): size mismatch (requested " << size << ", got " << mCtx.leftSub << ")"; + fail(error.str()); + } getExact(p, size); } diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index bb615af11..e7721bb8f 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -137,7 +137,11 @@ public: { getSubHeader(); if (mCtx.leftSub != sizeof(X)) - fail("getHT(): subrecord size mismatch"); + { + std::stringstream error; + error << "getHT(): subrecord size mismatch (requested " << sizeof(X) << ", got " << mCtx.leftSub << ")"; + fail(error.str()); + } getT(x); } From eede2c8e55fe9579dd8e746385be2a794ce969bb Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 19:30:39 +0100 Subject: [PATCH 14/21] ESSImport: convert breath meter and skill increases --- apps/essimporter/convertacdt.cpp | 2 ++ apps/essimporter/converter.hpp | 2 ++ apps/essimporter/importacdt.hpp | 16 +++++++++++++--- apps/essimporter/importer.cpp | 5 +++++ apps/essimporter/importplayer.cpp | 4 ++-- apps/essimporter/importplayer.hpp | 11 +++++++++++ apps/openmw/mwmechanics/npcstats.hpp | 22 +++++++++++----------- 7 files changed, 46 insertions(+), 16 deletions(-) diff --git a/apps/essimporter/convertacdt.cpp b/apps/essimporter/convertacdt.cpp index 81ab61084..af66ec272 100644 --- a/apps/essimporter/convertacdt.cpp +++ b/apps/essimporter/convertacdt.cpp @@ -37,6 +37,8 @@ namespace ESSImport npcStats.mSkills[i].mRegular.mCurrent = actorData.mSkills[i][1]; npcStats.mSkills[i].mRegular.mBase = actorData.mSkills[i][0]; } + + npcStats.mTimeToStartDrowning = actorData.mACDT.mBreathMeter; } } diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 716ddc9c7..23b0434b1 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -215,6 +215,8 @@ public: faction.mReputation = it->mReputation; mContext->mPlayer.mObject.mNpcStats.mFactions[it->mFactionName.toString()] = faction; } + for (int i=0; i<8; ++i) + mContext->mPlayer.mObject.mNpcStats.mSkillIncrease[i] = pcdt.mPNAM.mSkillIncreases[i]; for (std::vector::const_iterator it = pcdt.mKnownDialogueTopics.begin(); it != pcdt.mKnownDialogueTopics.end(); ++it) diff --git a/apps/essimporter/importacdt.hpp b/apps/essimporter/importacdt.hpp index 6efae9dc5..cf72b5cf7 100644 --- a/apps/essimporter/importacdt.hpp +++ b/apps/essimporter/importacdt.hpp @@ -13,14 +13,24 @@ namespace ESSImport /// Actor data, shared by (at least) REFR and CellRef +#pragma pack(push) +#pragma pack(1) struct ACDT { - unsigned char mUnknown1[40]; + // Note, not stored at *all*: + // - Level changes are lost on reload, except for the player (there it's in the NPC record). + unsigned char mUnknown1[16]; + float mBreathMeter; // Seconds left before drowning + unsigned char mUnknown2[20]; float mDynamic[3][2]; - unsigned char mUnknown2[16]; + unsigned char mUnknown3[16]; float mAttributes[8][2]; - unsigned char mUnknown3[120]; + unsigned char mUnknown4[109]; + // This seems to increase when purchasing training, though I don't see it anywhere ingame. + int mGold; + unsigned char mUnknown5[7]; }; +#pragma pack(pop) struct ActorData { diff --git a/apps/essimporter/importer.cpp b/apps/essimporter/importer.cpp index 699725d67..c2092ea4d 100644 --- a/apps/essimporter/importer.cpp +++ b/apps/essimporter/importer.cpp @@ -104,6 +104,11 @@ namespace ESSImport blacklist.insert(std::make_pair("GLOB", "FLTV")); // gamehour blacklist.insert(std::make_pair("REFR", "DATA")); // player position blacklist.insert(std::make_pair("CELL", "NAM8")); // fog of war + blacklist.insert(std::make_pair("GAME", "GMDT")); // weather data, current time always changes + + // this changes way too often, name suggests some renderer internal data? + blacklist.insert(std::make_pair("CELL", "ND3D")); + blacklist.insert(std::make_pair("REFR", "ND3D")); File file1; read(mEssFile, file1); diff --git a/apps/essimporter/importplayer.cpp b/apps/essimporter/importplayer.cpp index f94d3aacc..285605068 100644 --- a/apps/essimporter/importplayer.cpp +++ b/apps/essimporter/importplayer.cpp @@ -23,8 +23,8 @@ namespace ESSImport mKnownDialogueTopics.push_back(esm.getHString()); } - if (esm.isNextSub("PNAM")) - esm.skipHSub(); + esm.getHNT(mPNAM, "PNAM"); + if (esm.isNextSub("SNAM")) esm.skipHSub(); if (esm.isNextSub("NAM9")) diff --git a/apps/essimporter/importplayer.hpp b/apps/essimporter/importplayer.hpp index af8625fa6..2bd55ef73 100644 --- a/apps/essimporter/importplayer.hpp +++ b/apps/essimporter/importplayer.hpp @@ -38,6 +38,8 @@ struct PCDT std::vector mKnownDialogueTopics; +#pragma pack(push) +#pragma pack(1) struct FNAM { unsigned char mRank; @@ -47,7 +49,16 @@ struct PCDT unsigned char mUnknown2[3]; ESM::NAME32 mFactionName; }; + struct PNAM + { + unsigned char mUnknown1[116]; + unsigned char mSkillIncreases[8]; // number of skill increases for each attribute + unsigned char mUnknown2[88]; + }; +#pragma pack(pop) + std::vector mFactions; + PNAM mPNAM; void load(ESM::ESMReader& esm); }; diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index 4594492e1..9e543bb79 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -22,27 +22,27 @@ namespace MWMechanics class NpcStats : public CreatureStats { - /// NPCs other than the player can only have one faction. But for the sake of consistency - /// we use the same data structure for the PC and the NPCs. - /// \note the faction key must be in lowercase - std::map mFactionRank; - int mDisposition; SkillValue mSkill[ESM::Skill::Length]; SkillValue mWerewolfSkill[ESM::Skill::Length]; - int mBounty; - std::set mExpelled; - std::map mFactionReputation; int mReputation; int mCrimeId; - int mWerewolfKills; + int mProfit; + // ----- used by the player only, maybe should be moved at some point ------- + int mBounty; + int mWerewolfKills; + /// NPCs other than the player can only have one faction. But for the sake of consistency + /// we use the same data structure for the PC and the NPCs. + /// \note the faction key must be in lowercase + std::map mFactionRank; + std::set mExpelled; + std::map mFactionReputation; int mLevelProgress; // 0-10 - std::vector mSkillIncreases; // number of skill increases for each attribute - std::set mUsedIds; + // --------------------------------------------------------------------------- /// Countdown to getting damage while underwater float mTimeToStartDrowning; From 5b705196bc11acb9e471ad96c6eb928bbe5bad3e Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 19:47:54 +0100 Subject: [PATCH 15/21] ESSImport: convert level progress --- apps/essimporter/converter.hpp | 1 + apps/essimporter/importplayer.hpp | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 23b0434b1..920e6eead 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -217,6 +217,7 @@ public: } 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::const_iterator it = pcdt.mKnownDialogueTopics.begin(); it != pcdt.mKnownDialogueTopics.end(); ++it) diff --git a/apps/essimporter/importplayer.hpp b/apps/essimporter/importplayer.hpp index 2bd55ef73..64ceddfd7 100644 --- a/apps/essimporter/importplayer.hpp +++ b/apps/essimporter/importplayer.hpp @@ -51,9 +51,11 @@ struct PCDT }; struct PNAM { - unsigned char mUnknown1[116]; + unsigned char mUnknown1[4]; + unsigned char mLevelProgress; + unsigned char mUnknown2[111]; unsigned char mSkillIncreases[8]; // number of skill increases for each attribute - unsigned char mUnknown2[88]; + unsigned char mUnknown3[88]; }; #pragma pack(pop) From e38d75634505ea77173d492ee90fff5e0a347128 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 20:18:30 +0100 Subject: [PATCH 16/21] ESSImport: fix NPCC indices --- apps/essimporter/converter.cpp | 6 +++++- apps/essimporter/converter.hpp | 5 +---- apps/essimporter/importnpcc.hpp | 3 ++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/essimporter/converter.cpp b/apps/essimporter/converter.cpp index 83ea077ea..099987762 100644 --- a/apps/essimporter/converter.cpp +++ b/apps/essimporter/converter.cpp @@ -1,5 +1,7 @@ #include "converter.hpp" +#include + #include #include @@ -281,7 +283,9 @@ namespace ESSImport continue; } - std::cerr << "Can't find type for " << cellref.mIndexedRefId << std::endl; + std::stringstream error; + error << "Can't find type for " << cellref.mIndexedRefId << std::endl; + throw std::runtime_error(error.str()); } } diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index 920e6eead..b68f9a218 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -170,13 +170,10 @@ public: } else { - int index = mIndexCounter[id]++; + int index = npcc.mNPDT.mIndex; mContext->mNpcChanges.insert(std::make_pair(std::make_pair(index,id), npcc)).second; } } - -private: - std::map mIndexCounter; }; class ConvertREFR : public Converter diff --git a/apps/essimporter/importnpcc.hpp b/apps/essimporter/importnpcc.hpp index ee8eccda2..c69fa3e03 100644 --- a/apps/essimporter/importnpcc.hpp +++ b/apps/essimporter/importnpcc.hpp @@ -22,7 +22,8 @@ namespace ESSImport unsigned char mDisposition; unsigned char unknown; unsigned char mReputation; - unsigned char unknown2[5]; + unsigned char unknown2; + int mIndex; } mNPDT; Inventory mInventory; From 142a138b75e3391963ce109b216c7eb625a441b7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 20:19:08 +0100 Subject: [PATCH 17/21] ESSImport: convert TalkedTo flag and gold pool --- apps/essimporter/convertacdt.cpp | 2 ++ apps/essimporter/importacdt.cpp | 3 ++- apps/essimporter/importacdt.hpp | 15 ++++++++++----- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/apps/essimporter/convertacdt.cpp b/apps/essimporter/convertacdt.cpp index af66ec272..496eab9e9 100644 --- a/apps/essimporter/convertacdt.cpp +++ b/apps/essimporter/convertacdt.cpp @@ -27,6 +27,8 @@ namespace ESSImport cStats.mAttributes[i].mMod = acdt.mAttributes[i][0]; cStats.mAttributes[i].mCurrent = acdt.mAttributes[i][0]; } + cStats.mGoldPool = acdt.mGoldPool; + cStats.mTalkedTo = acdt.mFlags & TalkedToPlayer; } void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats) diff --git a/apps/essimporter/importacdt.cpp b/apps/essimporter/importacdt.cpp index d961720a7..84c800897 100644 --- a/apps/essimporter/importacdt.cpp +++ b/apps/essimporter/importacdt.cpp @@ -22,7 +22,7 @@ namespace ESSImport ESM::CellRef bla; bla.ESM::CellRef::loadData(esm); - // FIXME: actually should be required for all actors?, but ActorData is currently in base CellRef + // FIXME: not all actors have this, add flag esm.getHNOT(mACDT, "ACDT"); ACSC acsc; @@ -76,6 +76,7 @@ namespace ESSImport esm.skipHSub(); // 4 byte, 0 } + // FIXME: not all actors have this, add flag if (esm.isNextSub("CHRD")) // npc only esm.getHExact(mSkills, 27*2*sizeof(int)); diff --git a/apps/essimporter/importacdt.hpp b/apps/essimporter/importacdt.hpp index cf72b5cf7..79f67d2c2 100644 --- a/apps/essimporter/importacdt.hpp +++ b/apps/essimporter/importacdt.hpp @@ -11,6 +11,10 @@ namespace ESM namespace ESSImport { + enum ACDTFlags + { + TalkedToPlayer = 0x4 + }; /// Actor data, shared by (at least) REFR and CellRef #pragma pack(push) @@ -19,16 +23,17 @@ namespace ESSImport { // Note, not stored at *all*: // - Level changes are lost on reload, except for the player (there it's in the NPC record). - unsigned char mUnknown1[16]; + unsigned char mUnknown[12]; + unsigned char mFlags; // ACDTFlags + unsigned char mUnknown1[3]; float mBreathMeter; // Seconds left before drowning unsigned char mUnknown2[20]; float mDynamic[3][2]; unsigned char mUnknown3[16]; float mAttributes[8][2]; - unsigned char mUnknown4[109]; - // This seems to increase when purchasing training, though I don't see it anywhere ingame. - int mGold; - unsigned char mUnknown5[7]; + unsigned char mUnknown4[112]; + unsigned int mGoldPool; + unsigned char mUnknown5[4]; }; #pragma pack(pop) From 89d9649b50b2b6e102b90517a5b136cc74415ba0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 23:55:26 +0100 Subject: [PATCH 18/21] Change save format to store relative equipment index Store the index for the allowedSlots vector instead of the absolute slot index. This will more gracefully handle edge cases like the available slots for an item having changed when loading the game, or the "allows stacking" property having changed. However the main reason this was done is to ease work on the essimporter. --- apps/openmw/mwworld/cellstore.cpp | 2 +- apps/openmw/mwworld/containerstore.cpp | 27 ++++++++++---------- apps/openmw/mwworld/containerstore.hpp | 12 +++++---- apps/openmw/mwworld/inventorystore.cpp | 35 ++++++++++++++++++++------ apps/openmw/mwworld/inventorystore.hpp | 6 ++--- apps/openmw/mwworld/worldimp.cpp | 4 +-- components/esm/inventorystate.hpp | 1 + 7 files changed, 56 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 605c8f9d2..545bbd4b3 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -466,7 +466,7 @@ namespace MWWorld // List moved references, from separately tracked list. for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); ++it) { - ESM::CellRef &ref = const_cast(*it); + const ESM::CellRef &ref = *it; mIds.push_back(Misc::StringUtils::lowerCase(ref.mRefID)); } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index bd14720f7..5911b45f7 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -85,28 +85,29 @@ void MWWorld::ContainerStore::storeState (const LiveCellRef& ref, ESM::Object ref.save (state); } +/// \todo make this method const once const-correct ContainerStoreIterators are available template -void MWWorld::ContainerStore::storeStates (const CellRefList& collection, - std::vector >& states, bool equipable) const +void MWWorld::ContainerStore::storeStates (CellRefList& collection, + std::vector >& states, bool equipable) { - for (typename CellRefList::List::const_iterator iter (collection.mList.begin()); + for (typename CellRefList::List::iterator iter (collection.mList.begin()); iter!=collection.mList.end(); ++iter) { if (iter->mData.getCount() == 0) continue; ESM::ObjectState state; storeState (*iter, state); - int slot = equipable ? getSlot (*iter) : -1; + int slot = equipable ? getRelativeSlot (MWWorld::ContainerStoreIterator(this, iter)) : -1; states.push_back (std::make_pair (state, slot)); } } -int MWWorld::ContainerStore::getSlot (const MWWorld::LiveCellRefBase& ref) const +int MWWorld::ContainerStore::getRelativeSlot (const MWWorld::ContainerStoreIterator& iter) const { return -1; } -void MWWorld::ContainerStore::setSlot (const MWWorld::ContainerStoreIterator& iter, int slot) {} +void MWWorld::ContainerStore::setRelativeSlot (const MWWorld::ContainerStoreIterator& iter, int slot) {} const std::string MWWorld::ContainerStore::sGoldId = "gold_001"; @@ -641,7 +642,7 @@ MWWorld::Ptr MWWorld::ContainerStore::search (const std::string& id) return Ptr(); } -void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) const +void MWWorld::ContainerStore::writeState (ESM::InventoryState& state) { state.mItems.clear(); @@ -678,16 +679,16 @@ void MWWorld::ContainerStore::readState (const ESM::InventoryState& state) { case ESM::REC_ALCH: getState (potions, iter->first); break; case ESM::REC_APPA: getState (appas, iter->first); break; - case ESM::REC_ARMO: setSlot (getState (armors, iter->first), slot); break; + case ESM::REC_ARMO: setRelativeSlot (getState (armors, iter->first), slot); break; case ESM::REC_BOOK: getState (books, iter->first); break; - case ESM::REC_CLOT: setSlot (getState (clothes, iter->first), slot); break; + case ESM::REC_CLOT: setRelativeSlot (getState (clothes, iter->first), slot); break; case ESM::REC_INGR: getState (ingreds, iter->first); break; - case ESM::REC_LOCK: setSlot (getState (lockpicks, iter->first), slot); break; + case ESM::REC_LOCK: setRelativeSlot (getState (lockpicks, iter->first), slot); break; case ESM::REC_MISC: getState (miscItems, iter->first); break; - case ESM::REC_PROB: setSlot (getState (probes, iter->first), slot); break; + case ESM::REC_PROB: setRelativeSlot (getState (probes, iter->first), slot); break; case ESM::REC_REPA: getState (repairs, iter->first); break; - case ESM::REC_WEAP: setSlot (getState (weapons, iter->first), slot); break; - case ESM::REC_LIGH: setSlot (getState (lights, iter->first), slot); break; + case ESM::REC_WEAP: setRelativeSlot (getState (weapons, iter->first), slot); break; + case ESM::REC_LIGH: setRelativeSlot (getState (lights, iter->first), slot); break; default: std::cerr << "invalid item type in inventory state, refid " << state.mRef.mRefID << std::endl; diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 68ee41a1d..4a95606f7 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -84,15 +84,16 @@ namespace MWWorld template void storeState (const LiveCellRef& ref, ESM::ObjectState& state) const; + /// \todo make this method const once const-correct ContainerStoreIterators are available template - void storeStates (const CellRefList& collection, + void storeStates (CellRefList& collection, std::vector >& 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). - virtual void setSlot (const MWWorld::ContainerStoreIterator& iter, int slot); + virtual void setRelativeSlot (const MWWorld::ContainerStoreIterator& iter, int slot); ///< Set slot for \a iter. Ignored if \a iter is an end iterator or if slot==-1. public: @@ -171,7 +172,8 @@ namespace MWWorld Ptr search (const std::string& id); - virtual void writeState (ESM::InventoryState& state) const; + /// \todo make this method const once const-correct ContainerStoreIterators are available + virtual void writeState (ESM::InventoryState& state); virtual void readState (const ESM::InventoryState& state); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 445b42d8d..8f272ca49 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -49,19 +49,40 @@ void MWWorld::InventoryStore::initSlots (TSlots& slots_) slots_.push_back (end()); } -int MWWorld::InventoryStore::getSlot (const MWWorld::LiveCellRefBase& ref) const +int MWWorld::InventoryStore::getRelativeSlot (const MWWorld::ContainerStoreIterator& iter) { for (int i = 0; i (mSlots.size()); ++i) - if (mSlots[i].getType()!=-1 && mSlots[i]->getBase()==&ref) - return i; + if (mSlots[i].getType()!=-1 && mSlots[i] == iter) + { + // linear complexity, but allowedSlots is most of the time just 1 anyway + std::vector allowedSlots = iter->getClass().getEquipmentSlots(*iter).first; + std::vector::iterator found = std::find(allowedSlots.begin(),allowedSlots.end(),i); + if (found == allowedSlots.end()) + return -1; + else + return std::distance(allowedSlots.begin(), found); + } return -1; } -void MWWorld::InventoryStore::setSlot (const MWWorld::ContainerStoreIterator& iter, int slot) +void MWWorld::InventoryStore::setRelativeSlot (const MWWorld::ContainerStoreIterator& iter, int relativeSlot) { - if (iter!=end() && slot>=0 && slot, bool> allowedSlots = iter->getClass().getEquipmentSlots(*iter); + relativeSlot = std::min(int(allowedSlots.first.size()-1), relativeSlot); + + // unstack if required + if (!allowedSlots.second && iter->getRefData().getCount() > 1) + { + MWWorld::ContainerStoreIterator newIter = addNewStack(*iter, 1); + iter->getRefData().setCount(iter->getRefData().getCount()-1); + mSlots[allowedSlots.first[relativeSlot]] = newIter; + } + else + mSlots[allowedSlots.first[relativeSlot]] = iter; } MWWorld::InventoryStore::InventoryStore() @@ -703,7 +724,7 @@ bool MWWorld::InventoryStore::isEquipped(const MWWorld::Ptr &item) return false; } -void MWWorld::InventoryStore::writeState(ESM::InventoryState &state) const +void MWWorld::InventoryStore::writeState(ESM::InventoryState &state) { MWWorld::ContainerStore::writeState(state); diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 9fd18c54b..ae2ced2b8 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -113,10 +113,10 @@ namespace MWWorld void fireEquipmentChangedEvent(); - virtual int getSlot (const MWWorld::LiveCellRefBase& ref) const; + virtual int getRelativeSlot (const MWWorld::ContainerStoreIterator& iter); ///< Return inventory slot that \a ref is in or -1 (if \a ref is not in a slot). - virtual void setSlot (const MWWorld::ContainerStoreIterator& iter, int slot); + virtual void setRelativeSlot (const MWWorld::ContainerStoreIterator& iter, int relativeSlot); ///< Set slot for \a iter. Ignored if \a iter is an end iterator or if slot==-1. public: @@ -209,7 +209,7 @@ namespace MWWorld virtual void clear(); ///< Empty container. - virtual void writeState (ESM::InventoryState& state) const; + virtual void writeState (ESM::InventoryState& state); virtual void readState (const ESM::InventoryState& state); }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 104605e65..f7d5d9616 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2031,7 +2031,7 @@ namespace MWWorld bool World::isOnGround(const MWWorld::Ptr &ptr) const { RefData &refdata = ptr.getRefData(); - const OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); + OEngine::Physic::PhysicActor *physactor = mPhysEngine->getCharacter(refdata.getHandle()); if(!physactor) return false; @@ -2049,7 +2049,7 @@ namespace MWWorld mPhysEngine); if(tracer.mFraction < 1.0f) // collision, must be close to something below { - const_cast (physactor)->setOnGround(true); + physactor->setOnGround(true); return true; } else diff --git a/components/esm/inventorystate.hpp b/components/esm/inventorystate.hpp index 3d4407e7b..13af28e30 100644 --- a/components/esm/inventorystate.hpp +++ b/components/esm/inventorystate.hpp @@ -15,6 +15,7 @@ namespace ESM /// \brief State for inventories and containers struct InventoryState { + /// std::vector > mItems; std::map mLevelledItemMap; From 03df659e7d60c1047dd51b8d293cdd26f1cb9e2d Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 20 Jan 2015 23:58:24 +0100 Subject: [PATCH 19/21] ESSImport: convert inventory equipment slots --- apps/essimporter/convertinventory.cpp | 2 +- apps/essimporter/importinventory.cpp | 12 +++++++++++- apps/essimporter/importinventory.hpp | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/apps/essimporter/convertinventory.cpp b/apps/essimporter/convertinventory.cpp index 42535d946..6a31be6a3 100644 --- a/apps/essimporter/convertinventory.cpp +++ b/apps/essimporter/convertinventory.cpp @@ -16,7 +16,7 @@ namespace ESSImport 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 objstate.mRef.mCharge = it->mCondition; - state.mItems.push_back(std::make_pair(objstate, -1)); + state.mItems.push_back(std::make_pair(objstate, it->mRelativeEquipmentSlot)); } } diff --git a/apps/essimporter/importinventory.cpp b/apps/essimporter/importinventory.cpp index b4d193540..2f5c3c054 100644 --- a/apps/essimporter/importinventory.cpp +++ b/apps/essimporter/importinventory.cpp @@ -1,5 +1,7 @@ #include "importinventory.hpp" +#include + #include #include @@ -17,6 +19,7 @@ namespace ESSImport InventoryItem item; item.mId = contItem.mItem.toString(); item.mCount = contItem.mCount; + item.mRelativeEquipmentSlot = -1; // seems that a stack of items can have a set of subrecords for each item? rings0000.ess // doesn't make any sense to me, if the values were different then the items shouldn't stack in the first place? @@ -55,14 +58,21 @@ namespace ESSImport { // note: same item can be equipped 2 items (e.g. 2 rings) // and will be *stacked* in the NPCO list, unlike openmw! + // this is currently not handled properly. + esm.getSubHeader(); int itemIndex; // index of the item in the NPCO list esm.getT(itemIndex); + if (itemIndex < 0 || itemIndex >= int(mItems.size())) + esm.fail("equipment item index out of range"); + // appears to be a relative index for only the *possible* slots this item can be equipped in, - // i.e. 0 most of the time, unlike openmw slot enum index + // i.e. 0 most of the time int slotIndex; esm.getT(slotIndex); + + mItems[itemIndex].mRelativeEquipmentSlot = slotIndex; } } diff --git a/apps/essimporter/importinventory.hpp b/apps/essimporter/importinventory.hpp index 359e43fa6..8a1594465 100644 --- a/apps/essimporter/importinventory.hpp +++ b/apps/essimporter/importinventory.hpp @@ -21,6 +21,7 @@ namespace ESSImport std::string mId; int mCount; int mCondition; + int mRelativeEquipmentSlot; }; std::vector mItems; From db64ff66453245ecf0b273354bee3ec1af7a0810 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 21 Jan 2015 01:33:48 +0100 Subject: [PATCH 20/21] Fix actorId initialization --- components/esm/creaturestats.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index a1ef7eb0e..cac5cc0a6 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -230,7 +230,7 @@ void ESM::CreatureStats::blank() mTradeTime.mHour = 0; mTradeTime.mDay = 0; mGoldPool = 0; - mActorId = 0; + mActorId = -1; mHasAiSettings = false; mDead = false; mDied = false; From 5e0428243b38d18c55b80e7675ea74a0563f5e44 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 21 Jan 2015 01:59:45 +0100 Subject: [PATCH 21/21] Light charge handling fix --- apps/esmtool/esmtool.cpp | 2 +- apps/essimporter/convertinventory.cpp | 2 +- apps/essimporter/importinventory.cpp | 6 +-- apps/essimporter/importinventory.hpp | 1 - apps/opencs/model/world/columnimp.hpp | 4 +- apps/openmw/mwclass/light.cpp | 56 +++------------------------ apps/openmw/mwclass/light.hpp | 10 ----- apps/openmw/mwworld/cellref.cpp | 20 ++++++++-- apps/openmw/mwworld/cellref.hpp | 3 ++ apps/openmw/mwworld/manualref.hpp | 2 +- components/esm/cellref.cpp | 10 ++--- components/esm/cellref.hpp | 6 ++- components/esm/objectstate.cpp | 10 ++--- components/esm/objectstate.hpp | 2 - 14 files changed, 47 insertions(+), 87 deletions(-) diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index a18736bf2..b23ba72f9 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -261,7 +261,7 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info) std::cout << " Faction: '" << ref.mFaction << "'" << std::endl; std::cout << " Faction rank: '" << ref.mFactionRank << "'" << std::endl; std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n"; - std::cout << " Uses/health: '" << ref.mCharge << "'\n"; + std::cout << " Uses/health: '" << ref.mChargeInt << "'\n"; std::cout << " Gold value: '" << ref.mGoldValue << "'\n"; std::cout << " Blocked: '" << static_cast(ref.mReferenceBlocked) << "'" << std::endl; std::cout << " Deleted: " << deleted << std::endl; diff --git a/apps/essimporter/convertinventory.cpp b/apps/essimporter/convertinventory.cpp index 6a31be6a3..31272bf5a 100644 --- a/apps/essimporter/convertinventory.cpp +++ b/apps/essimporter/convertinventory.cpp @@ -12,10 +12,10 @@ namespace ESSImport { 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 - objstate.mRef.mCharge = it->mCondition; state.mItems.push_back(std::make_pair(objstate, it->mRelativeEquipmentSlot)); } } diff --git a/apps/essimporter/importinventory.cpp b/apps/essimporter/importinventory.cpp index 2f5c3c054..a20584ff5 100644 --- a/apps/essimporter/importinventory.cpp +++ b/apps/essimporter/importinventory.cpp @@ -45,9 +45,9 @@ namespace ESSImport // for XSOL and XCHG seen so far, but probably others too item.ESM::CellRef::loadData(esm); - item.mCondition = -1; - // FIXME: for Lights, this is actually a float - esm.getHNOT(item.mCondition, "XHLT"); + int charge=-1; + esm.getHNOT(charge, "XHLT"); + item.mChargeInt = charge; } mItems.push_back(item); diff --git a/apps/essimporter/importinventory.hpp b/apps/essimporter/importinventory.hpp index 8a1594465..99fed9b20 100644 --- a/apps/essimporter/importinventory.hpp +++ b/apps/essimporter/importinventory.hpp @@ -20,7 +20,6 @@ namespace ESSImport { std::string mId; int mCount; - int mCondition; int mRelativeEquipmentSlot; }; std::vector mItems; diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index eba73cf0b..da14bb495 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -1053,13 +1053,13 @@ namespace CSMWorld virtual QVariant get (const Record& record) const { - return record.get().mCharge; + return record.get().mChargeInt; } virtual void set (Record& record, const QVariant& data) { ESXRecordT record2 = record.get(); - record2.mCharge = data.toInt(); + record2.mChargeInt = data.toInt(); record.setModified (record2); } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index c50ad52d8..35a21b63e 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -26,27 +26,6 @@ #include "../mwrender/actors.hpp" #include "../mwrender/renderinginterface.hpp" -namespace -{ - struct LightCustomData : public MWWorld::CustomData - { - float mTime; - ///< Time remaining - - LightCustomData(MWWorld::Ptr ptr) - { - MWWorld::LiveCellRef *ref = ptr.get(); - mTime = ref->mBase->mData.mTime; - } - ///< Constructs this CustomData from the base values for Ptr. - - virtual MWWorld::CustomData *clone() const - { - return new LightCustomData (*this); - } - }; -} - namespace MWClass { std::string Light::getId (const MWWorld::Ptr& ptr) const @@ -219,17 +198,16 @@ namespace MWClass void Light::setRemainingUsageTime (const MWWorld::Ptr& ptr, float duration) const { - ensureCustomData(ptr); - - float &timeRemaining = dynamic_cast (*ptr.getRefData().getCustomData()).mTime; - timeRemaining = duration; + ptr.getCellRef().setChargeFloat(duration); } float Light::getRemainingUsageTime (const MWWorld::Ptr& ptr) const { - ensureCustomData(ptr); - - return dynamic_cast (*ptr.getRefData().getCustomData()).mTime; + MWWorld::LiveCellRef *ref = ptr.get(); + if (ptr.getCellRef().getCharge() == -1) + return ref->mBase->mData.mTime; + else + return ptr.getCellRef().getChargeFloat(); } MWWorld::Ptr @@ -241,12 +219,6 @@ namespace MWClass return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } - void Light::ensureCustomData (const MWWorld::Ptr& ptr) const - { - if (!ptr.getRefData().getCustomData()) - ptr.getRefData().setCustomData(new LightCustomData(ptr)); - } - bool Light::canSell (const MWWorld::Ptr& item, int npcServices) const { return npcServices & ESM::NPC::Lights; @@ -282,22 +254,6 @@ namespace MWClass return std::make_pair(1,""); } - void Light::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) - const - { - ensureCustomData (ptr); - - dynamic_cast (*ptr.getRefData().getCustomData()).mTime = state.mTime; - } - - void Light::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) - const - { - ensureCustomData (ptr); - - state.mTime = dynamic_cast (*ptr.getRefData().getCustomData()).mTime; - } - std::string Light::getSound(const MWWorld::Ptr& ptr) const { return ptr.get()->mBase->mSound; diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index bbca30113..8658375b7 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -10,8 +10,6 @@ namespace MWClass virtual MWWorld::Ptr copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; - void ensureCustomData (const MWWorld::Ptr& ptr) const; - public: /// Return ID of \a ptr @@ -75,14 +73,6 @@ namespace MWClass std::pair 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; }; } diff --git a/apps/openmw/mwworld/cellref.cpp b/apps/openmw/mwworld/cellref.cpp index aa6627de5..744e0e27a 100644 --- a/apps/openmw/mwworld/cellref.cpp +++ b/apps/openmw/mwworld/cellref.cpp @@ -81,15 +81,29 @@ namespace MWWorld int CellRef::getCharge() const { - return mCellRef.mCharge; + return mCellRef.mChargeInt; } void CellRef::setCharge(int charge) { - if (charge != mCellRef.mCharge) + if (charge != mCellRef.mChargeInt) { mChanged = true; - mCellRef.mCharge = charge; + mCellRef.mChargeInt = charge; + } + } + + float CellRef::getChargeFloat() const + { + return mCellRef.mChargeFloat; + } + + void CellRef::setChargeFloat(float charge) + { + if (charge != mCellRef.mChargeFloat) + { + mChanged = true; + mCellRef.mChargeFloat = charge; } } diff --git a/apps/openmw/mwworld/cellref.hpp b/apps/openmw/mwworld/cellref.hpp index a7ffbe08b..054fd7fc6 100644 --- a/apps/openmw/mwworld/cellref.hpp +++ b/apps/openmw/mwworld/cellref.hpp @@ -60,8 +60,11 @@ namespace MWWorld // For weapon or armor, this is the remaining item health. // For tools (lockpicks, probes, repair hammer) it is the remaining uses. + // If this returns int(-1) it means full health. int getCharge() const; + float getChargeFloat() const; // Implemented as union with int charge void setCharge(int charge); + void setChargeFloat(float charge); // The NPC that owns this object (and will get angry if you steal it) std::string getOwner() const; diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index 4eb93543b..f32b07471 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -28,7 +28,7 @@ namespace MWWorld cellRef.mRefID = name; cellRef.mScale = 1; cellRef.mFactionRank = 0; - cellRef.mCharge = -1; + cellRef.mChargeInt = -1; cellRef.mGoldValue = 1; cellRef.mEnchantmentCharge = -1; cellRef.mTeleport = false; diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index cd9fe8570..dfc3052ee 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -46,12 +46,12 @@ void ESM::CellRef::loadData(ESMReader &esm) esm.getHNOT (mFactionRank, "INDX"); mGoldValue = 1; - mCharge = -1; + mChargeInt = -1; mEnchantmentCharge = -1; esm.getHNOT (mEnchantmentCharge, "XCHG"); - esm.getHNOT (mCharge, "INTV"); + esm.getHNOT (mChargeInt, "INTV"); esm.getHNOT (mGoldValue, "NAM9"); @@ -106,8 +106,8 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) cons if (mEnchantmentCharge != -1) esm.writeHNT("XCHG", mEnchantmentCharge); - if (mCharge != -1) - esm.writeHNT("INTV", mCharge); + if (mChargeInt != -1) + esm.writeHNT("INTV", mChargeInt); if (mGoldValue != 1) { esm.writeHNT("NAM9", mGoldValue); @@ -146,7 +146,7 @@ void ESM::CellRef::blank() mSoul.clear(); mFaction.clear(); mFactionRank = -2; - mCharge = -1; + mChargeInt = -1; mEnchantmentCharge = -1; mGoldValue = 0; mDestCell.clear(); diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 7aa0e4d44..56932aa4d 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -59,7 +59,11 @@ namespace ESM // For weapon or armor, this is the remaining item health. // For tools (lockpicks, probes, repair hammer) it is the remaining uses. - int mCharge; + union + { + int mChargeInt; + float mChargeFloat; + }; // Remaining enchantment charge. This could be -1 if the charge was not touched yet (i.e. full). float mEnchantmentCharge; diff --git a/components/esm/objectstate.cpp b/components/esm/objectstate.cpp index 66fd49663..9ef1ccf80 100644 --- a/components/esm/objectstate.cpp +++ b/components/esm/objectstate.cpp @@ -24,9 +24,9 @@ void ESM::ObjectState::load (ESMReader &esm) esm.getHNOT (mLocalRotation, "LROT", 12); - // used for lights only - mTime = 0; - esm.getHNOT (mTime, "LTIM"); + // 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; @@ -55,9 +55,6 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const esm.writeHNT ("LROT", mLocalRotation, 12); } - if (mTime) - esm.writeHNT ("LTIM", mTime); - if (!mHasCustomState) esm.writeHNT ("HCUS", false); } @@ -74,7 +71,6 @@ void ESM::ObjectState::blank() mPosition.rot[i] = 0; mLocalRotation[i] = 0; } - mTime = 0; mHasCustomState = true; } diff --git a/components/esm/objectstate.hpp b/components/esm/objectstate.hpp index b03689653..d1077733a 100644 --- a/components/esm/objectstate.hpp +++ b/components/esm/objectstate.hpp @@ -26,8 +26,6 @@ namespace ESM ESM::Position mPosition; float mLocalRotation[3]; - float mTime; // Used for lights only. Overhead should not be so awful, besides CellRef isn't OO either - // Is there any class-specific state following the ObjectState bool mHasCustomState;