From b81454d226c2ea919b2a48eb6482f7b5553bd5a0 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 13 Jun 2015 14:37:47 +1000 Subject: [PATCH 1/2] Fix using wrong bit flag for NPC stats auto-calculation. Also set the corresponding mNpdtType which is used when determining which data structure to save. Should resolve Bug #2668. --- apps/esmtool/labels.cpp | 24 +++++++++---------- .../opencs/model/tools/referenceablecheck.cpp | 2 +- apps/opencs/model/world/refidadapterimp.cpp | 4 ++++ components/esm/loadnpc.hpp | 12 +++++----- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/apps/esmtool/labels.cpp b/apps/esmtool/labels.cpp index 88e188df0..883a9e728 100644 --- a/apps/esmtool/labels.cpp +++ b/apps/esmtool/labels.cpp @@ -119,7 +119,7 @@ std::string clothingTypeLabel(int idx) } std::string armorTypeLabel(int idx) -{ +{ if (idx >= 0 && idx <= 10) { static const char *armorTypeLabels[] = { @@ -645,7 +645,7 @@ std::string ruleFunction(int idx) else return "Invalid"; } - + // The "unused flag bits" should probably be defined alongside the // defined bits in the ESM component. The names of the flag bits are // very inconsistent. @@ -653,7 +653,7 @@ std::string ruleFunction(int idx) std::string bodyPartFlags(int flags) { std::string properties = ""; - if (flags == 0) properties += "[None] "; + if (flags == 0) properties += "[None] "; if (flags & ESM::BodyPart::BPF_Female) properties += "Female "; if (flags & ESM::BodyPart::BPF_NotPlayable) properties += "NotPlayable "; int unused = (0xFFFFFFFF ^ @@ -667,7 +667,7 @@ std::string bodyPartFlags(int flags) std::string cellFlags(int flags) { std::string properties = ""; - if (flags == 0) properties += "[None] "; + if (flags == 0) properties += "[None] "; if (flags & ESM::Cell::HasWater) properties += "HasWater "; if (flags & ESM::Cell::Interior) properties += "Interior "; if (flags & ESM::Cell::NoSleep) properties += "NoSleep "; @@ -830,12 +830,12 @@ std::string npcFlags(int flags) std::string properties = ""; if (flags == 0) properties += "[None] "; // Mythicmods and the ESM component differ. Mythicmods says - // 0x8=None and 0x10=AutoCalc, while our code defines 0x8 as - // AutoCalc. The former seems to be correct. All Bethesda - // records have bit 0x8 set. A suspiciously large portion of - // females have autocalc turned off. - if (flags & ESM::NPC::Autocalc) properties += "Unknown "; - if (flags & 0x00000010) properties += "Autocalc "; + // 0x8=None and 0x10=AutoCalc, while our code previously defined + // 0x8 as AutoCalc. The former seems to be correct. All Bethesda + // records have bit 0x8 set. Previously, suspiciously large portion + // of females had autocalc turned off. + if (flags & 0x00000008) properties += "Unknown "; + if (flags & ESM::NPC::Autocalc) properties += "Autocalc "; if (flags & ESM::NPC::Female) properties += "Female "; if (flags & ESM::NPC::Respawn) properties += "Respawn "; if (flags & ESM::NPC::Essential) properties += "Essential "; @@ -847,8 +847,8 @@ std::string npcFlags(int flags) // however the only unknown bit occurs on ALL records, and // relatively few NPCs have this bit set. int unused = (0xFFFFFFFF ^ - (ESM::NPC::Autocalc| - 0x00000010| + (0x00000008| + ESM::NPC::Autocalc| ESM::NPC::Female| ESM::NPC::Respawn| ESM::NPC::Essential| diff --git a/apps/opencs/model/tools/referenceablecheck.cpp b/apps/opencs/model/tools/referenceablecheck.cpp index 548fcd36f..6b323547f 100644 --- a/apps/opencs/model/tools/referenceablecheck.cpp +++ b/apps/opencs/model/tools/referenceablecheck.cpp @@ -648,7 +648,7 @@ void CSMTools::ReferenceableCheckStage::npcCheck ( if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated { - if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0008 = autocalculated flag + if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag { messages.push_back (std::make_pair (id, npc.mId + " mNpdtType or flags mismatch!")); //should not happend? return; diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index d31a9ceaa..cb71319c8 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -546,6 +546,10 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d record.get().mFlags |= iter->second; else record.get().mFlags &= ~iter->second; + + if (iter->second == ESM::NPC::Autocalc) + record.get().mNpdtType = (value.toInt() != 0) ? ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS + : ESM::NPC::NPC_DEFAULT; } else ActorRefIdAdapter::setData (column, data, index, value); diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index b535b91b0..281020c98 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -52,12 +52,12 @@ struct NPC enum Flags { - Female = 0x0001, - Essential = 0x0002, - Respawn = 0x0004, - Autocalc = 0x0008, - Skeleton = 0x0400, // Skeleton blood effect (white) - Metal = 0x0800 // Metal blood effect (golden?) + Female = 0x0001, + Essential = 0x0002, + Respawn = 0x0004, + Autocalc = 0x0010, + Skeleton = 0x0400, // Skeleton blood effect (white) + Metal = 0x0800 // Metal blood effect (golden?) }; enum NpcType From 5b6984d8d844c812c16776298be6eab05a2e0615 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 13 Jun 2015 22:24:22 +1000 Subject: [PATCH 2/2] Set modified flag in setData() operations, without which the changes weren't being saved. Should resolve Bug #2656. --- apps/opencs/model/world/refidadapterimp.cpp | 169 +++++++++++++++----- 1 file changed, 128 insertions(+), 41 deletions(-) diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index cb71319c8..4c369ef24 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -37,10 +37,18 @@ void CSMWorld::PotionRefIdAdapter::setData (const RefIdColumn *column, RefIdData Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Potion))); + ESM::Potion potion = record.get(); + if (column==mAutoCalc) - record.get().mData.mAutoCalc = value.toInt(); + potion.mData.mAutoCalc = value.toInt(); else + { InventoryRefIdAdapter::setData (column, data, index, value); + + return; + } + + record.setModified(potion); } @@ -71,12 +79,19 @@ void CSMWorld::ApparatusRefIdAdapter::setData (const RefIdColumn *column, RefIdD Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Apparatus))); + ESM::Apparatus apparatus = record.get(); + if (column==mType) - record.get().mData.mType = value.toInt(); + apparatus.mData.mType = value.toInt(); else if (column==mQuality) - record.get().mData.mQuality = value.toFloat(); + apparatus.mData.mQuality = value.toFloat(); else + { InventoryRefIdAdapter::setData (column, data, index, value); + + return; + } + record.setModified(apparatus); } @@ -114,14 +129,22 @@ void CSMWorld::ArmorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Armor))); + ESM::Armor armor = record.get(); + if (column==mType) - record.get().mData.mType = value.toInt(); + armor.mData.mType = value.toInt(); else if (column==mHealth) - record.get().mData.mHealth = value.toInt(); + armor.mData.mHealth = value.toInt(); else if (column==mArmor) - record.get().mData.mArmor = value.toInt(); + armor.mData.mArmor = value.toInt(); else + { EnchantableRefIdAdapter::setData (column, data, index, value); + + return; + } + + record.setModified(armor); } CSMWorld::BookRefIdAdapter::BookRefIdAdapter (const EnchantableColumns& columns, @@ -151,12 +174,20 @@ void CSMWorld::BookRefIdAdapter::setData (const RefIdColumn *column, RefIdData& Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Book))); + ESM::Book book = record.get(); + if (column==mScroll) - record.get().mData.mIsScroll = value.toInt(); + book.mData.mIsScroll = value.toInt(); else if (column==mSkill) - record.get().mData.mSkillID = value.toInt(); + book.mData.mSkillID = value.toInt(); else + { EnchantableRefIdAdapter::setData (column, data, index, value); + + return; + } + + record.setModified(book); } CSMWorld::ClothingRefIdAdapter::ClothingRefIdAdapter (const EnchantableColumns& columns, @@ -186,10 +217,18 @@ void CSMWorld::ClothingRefIdAdapter::setData (const RefIdColumn *column, RefIdDa Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Clothing))); + ESM::Clothing clothing = record.get(); + if (column==mType) - record.get().mData.mType = value.toInt(); + clothing.mData.mType = value.toInt(); else + { EnchantableRefIdAdapter::setData (column, data, index, value); + + return; + } + + record.setModified(clothing); } CSMWorld::ContainerRefIdAdapter::ContainerRefIdAdapter (const NameColumns& columns, @@ -226,24 +265,32 @@ void CSMWorld::ContainerRefIdAdapter::setData (const RefIdColumn *column, RefIdD Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Container))); + ESM::Container container = record.get(); + if (column==mWeight) - record.get().mWeight = value.toFloat(); + container.mWeight = value.toFloat(); else if (column==mOrganic) { if (value.toInt()) - record.get().mFlags |= ESM::Container::Organic; + container.mFlags |= ESM::Container::Organic; else - record.get().mFlags &= ~ESM::Container::Organic; + container.mFlags &= ~ESM::Container::Organic; } else if (column==mRespawn) { if (value.toInt()) - record.get().mFlags |= ESM::Container::Respawn; + container.mFlags |= ESM::Container::Respawn; else - record.get().mFlags &= ~ESM::Container::Respawn; + container.mFlags &= ~ESM::Container::Respawn; } else + { NameRefIdAdapter::setData (column, data, index, value); + + return; + } + + record.setModified(container); } CSMWorld::CreatureColumns::CreatureColumns (const ActorColumns& actorColumns) @@ -303,20 +350,22 @@ void CSMWorld::CreatureRefIdAdapter::setData (const RefIdColumn *column, RefIdDa Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Creature))); + ESM::Creature creature = record.get(); + if (column==mColumns.mType) - record.get().mData.mType = value.toInt(); + creature.mData.mType = value.toInt(); else if (column==mColumns.mSoul) - record.get().mData.mSoul = value.toInt(); + creature.mData.mSoul = value.toInt(); else if (column==mColumns.mScale) - record.get().mScale = value.toFloat(); + creature.mScale = value.toFloat(); else if (column==mColumns.mOriginal) - record.get().mOriginal = value.toString().toUtf8().constData(); + creature.mOriginal = value.toString().toUtf8().constData(); else if (column==mColumns.mCombat) - record.get().mData.mCombat = value.toInt(); + creature.mData.mCombat = value.toInt(); else if (column==mColumns.mMagic) - record.get().mData.mMagic = value.toInt(); + creature.mData.mMagic = value.toInt(); else if (column==mColumns.mStealth) - record.get().mData.mStealth = value.toInt(); + creature.mData.mStealth = value.toInt(); else { std::map::const_iterator iter = @@ -325,13 +374,19 @@ void CSMWorld::CreatureRefIdAdapter::setData (const RefIdColumn *column, RefIdDa if (iter!=mColumns.mFlags.end()) { if (value.toInt()!=0) - record.get().mFlags |= iter->second; + creature.mFlags |= iter->second; else - record.get().mFlags &= ~iter->second; + creature.mFlags &= ~iter->second; } else + { ActorRefIdAdapter::setData (column, data, index, value); + + return; + } } + + record.setModified(creature); } CSMWorld::DoorRefIdAdapter::DoorRefIdAdapter (const NameColumns& columns, @@ -361,12 +416,20 @@ void CSMWorld::DoorRefIdAdapter::setData (const RefIdColumn *column, RefIdData& Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Door))); + ESM::Door door = record.get(); + if (column==mOpenSound) - record.get().mOpenSound = value.toString().toUtf8().constData(); + door.mOpenSound = value.toString().toUtf8().constData(); else if (column==mCloseSound) - record.get().mCloseSound = value.toString().toUtf8().constData(); + door.mCloseSound = value.toString().toUtf8().constData(); else + { NameRefIdAdapter::setData (column, data, index, value); + + return; + } + + record.setModified(door); } CSMWorld::LightColumns::LightColumns (const InventoryColumns& columns) @@ -409,14 +472,16 @@ void CSMWorld::LightRefIdAdapter::setData (const RefIdColumn *column, RefIdData& Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Light))); + ESM::Light light = record.get(); + if (column==mColumns.mTime) - record.get().mData.mTime = value.toInt(); + light.mData.mTime = value.toInt(); else if (column==mColumns.mRadius) - record.get().mData.mRadius = value.toInt(); + light.mData.mRadius = value.toInt(); else if (column==mColumns.mColor) - record.get().mData.mColor = value.toInt(); + light.mData.mColor = value.toInt(); else if (column==mColumns.mSound) - record.get().mSound = value.toString().toUtf8().constData(); + light.mSound = value.toString().toUtf8().constData(); else { std::map::const_iterator iter = @@ -425,13 +490,19 @@ void CSMWorld::LightRefIdAdapter::setData (const RefIdColumn *column, RefIdData& if (iter!=mColumns.mFlags.end()) { if (value.toInt()!=0) - record.get().mData.mFlags |= iter->second; + light.mData.mFlags |= iter->second; else - record.get().mData.mFlags &= ~iter->second; + light.mData.mFlags &= ~iter->second; } else + { InventoryRefIdAdapter::setData (column, data, index, value); + + return; + } } + + record.setModified (light); } CSMWorld::MiscRefIdAdapter::MiscRefIdAdapter (const InventoryColumns& columns, const RefIdColumn *key) @@ -456,10 +527,18 @@ void CSMWorld::MiscRefIdAdapter::setData (const RefIdColumn *column, RefIdData& Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Miscellaneous))); + ESM::Miscellaneous misc = record.get(); + if (column==mKey) - record.get().mData.mIsKey = value.toInt(); + misc.mData.mIsKey = value.toInt(); else + { InventoryRefIdAdapter::setData (column, data, index, value); + + return; + } + + record.setModified(misc); } CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns) @@ -525,16 +604,18 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d Record& record = static_cast&> ( data.getRecord (RefIdData::LocalIndex (index, UniversalId::Type_Npc))); + ESM::NPC npc = record.get(); + if (column==mColumns.mRace) - record.get().mRace = value.toString().toUtf8().constData(); + npc.mRace = value.toString().toUtf8().constData(); else if (column==mColumns.mClass) - record.get().mClass = value.toString().toUtf8().constData(); + npc.mClass = value.toString().toUtf8().constData(); else if (column==mColumns.mFaction) - record.get().mFaction = value.toString().toUtf8().constData(); + npc.mFaction = value.toString().toUtf8().constData(); else if (column==mColumns.mHair) - record.get().mHair = value.toString().toUtf8().constData(); + npc.mHair = value.toString().toUtf8().constData(); else if (column==mColumns.mHead) - record.get().mHead = value.toString().toUtf8().constData(); + npc.mHead = value.toString().toUtf8().constData(); else { std::map::const_iterator iter = @@ -543,17 +624,23 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d if (iter!=mColumns.mFlags.end()) { if (value.toInt()!=0) - record.get().mFlags |= iter->second; + npc.mFlags |= iter->second; else - record.get().mFlags &= ~iter->second; + npc.mFlags &= ~iter->second; if (iter->second == ESM::NPC::Autocalc) - record.get().mNpdtType = (value.toInt() != 0) ? ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS - : ESM::NPC::NPC_DEFAULT; + npc.mNpdtType = (value.toInt() != 0) ? ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS + : ESM::NPC::NPC_DEFAULT; } else + { ActorRefIdAdapter::setData (column, data, index, value); + + return; + } } + + record.setModified (npc); } CSMWorld::NpcAttributesRefIdAdapter::NpcAttributesRefIdAdapter ()