From 6c01ce267294abfd90ed74bcd8973ed4e459e0f9 Mon Sep 17 00:00:00 2001 From: Petr Mikheev Date: Tue, 31 Oct 2023 11:47:42 +0100 Subject: [PATCH] Use correct template flags for FONV and FO4 NPCs --- apps/openmw/mwclass/esm4npc.cpp | 30 +++++++++++++++++----- components/esm4/loadnpc.cpp | 4 ++- components/esm4/loadnpc.hpp | 45 +++++++++++++++++++-------------- 3 files changed, 53 insertions(+), 26 deletions(-) diff --git a/apps/openmw/mwclass/esm4npc.cpp b/apps/openmw/mwclass/esm4npc.cpp index 78cbd89b50..13e7866537 100644 --- a/apps/openmw/mwclass/esm4npc.cpp +++ b/apps/openmw/mwclass/esm4npc.cpp @@ -34,11 +34,26 @@ namespace MWClass static const ESM4::Npc* chooseTemplate(const std::vector& recs, uint16_t flag) { - // In case of FO3 the function may return nullptr that will lead to "ESM4 NPC traits not found" - // exception and the NPC will not be added to the scene. But in any way it shouldn't cause a crash. for (const auto* rec : recs) - if (rec->mIsTES4 || rec->mIsFONV || !(rec->mBaseConfig.tes5.templateFlags & flag)) + { + if (rec->mIsTES4) return rec; + else if (rec->mIsFONV) + { + // TODO: FO3 should use this branch as well. But it is not clear how to distinguish FO3 from + // TES5. Currently FO3 uses wrong template flags that can lead to "ESM4 NPC traits not found" + // exception the NPC will not be added to the scene. But in any way it shouldn't cause a crash. + if (!(rec->mBaseConfig.fo3.templateFlags & flag)) + return rec; + } + else if (rec->mIsFO4) + { + if (!(rec->mBaseConfig.fo4.templateFlags & flag)) + return rec; + } + else if (!(rec->mBaseConfig.tes5.templateFlags & flag)) + return rec; + } return nullptr; } @@ -75,8 +90,8 @@ namespace MWClass const MWWorld::ESMStore* store = MWBase::Environment::get().getESMStore(); auto npcRecs = withBaseTemplates(ptr.get()->mBase); - data->mTraits = chooseTemplate(npcRecs, ESM4::Npc::TES5_UseTraits); - data->mBaseData = chooseTemplate(npcRecs, ESM4::Npc::TES5_UseBaseData); + data->mTraits = chooseTemplate(npcRecs, ESM4::Npc::Template_UseTraits); + data->mBaseData = chooseTemplate(npcRecs, ESM4::Npc::Template_UseBaseData); if (!data->mTraits) throw std::runtime_error("ESM4 NPC traits not found"); @@ -88,10 +103,13 @@ namespace MWClass data->mIsFemale = data->mTraits->mBaseConfig.tes4.flags & ESM4::Npc::TES4_Female; else if (data->mTraits->mIsFONV) data->mIsFemale = data->mTraits->mBaseConfig.fo3.flags & ESM4::Npc::FO3_Female; + else if (data->mTraits->mIsFO4) + data->mIsFemale + = data->mTraits->mBaseConfig.fo4.flags & ESM4::Npc::TES5_Female; // FO4 flags are same as TES5 else data->mIsFemale = data->mTraits->mBaseConfig.tes5.flags & ESM4::Npc::TES5_Female; - if (auto inv = chooseTemplate(npcRecs, ESM4::Npc::TES5_UseInventory)) + if (auto inv = chooseTemplate(npcRecs, ESM4::Npc::Template_UseInventory)) { for (const ESM4::InventoryItem& item : inv->mInventory) { diff --git a/components/esm4/loadnpc.cpp b/components/esm4/loadnpc.cpp index 251af13630..885263d67b 100644 --- a/components/esm4/loadnpc.cpp +++ b/components/esm4/loadnpc.cpp @@ -116,9 +116,11 @@ void ESM4::Npc::load(ESM4::Reader& reader) { switch (subHdr.dataSize) { + case 20: // FO4 + mIsFO4 = true; + [[fallthrough]]; case 16: // TES4 case 24: // FO3/FNV, TES5 - case 20: // FO4 reader.get(&mBaseConfig, subHdr.dataSize); break; default: diff --git a/components/esm4/loadnpc.hpp b/components/esm4/loadnpc.hpp index 6f4b3c7e24..04e56b7bd9 100644 --- a/components/esm4/loadnpc.hpp +++ b/components/esm4/loadnpc.hpp @@ -78,6 +78,7 @@ namespace ESM4 FO3_NoRotateHead = 0x40000000 }; + // In FO4 flags seem to be the same. enum ACBS_TES5 { TES5_Female = 0x00000001, @@ -101,27 +102,32 @@ namespace ESM4 TES5_Invulnerable = 0x80000000 }; + // All FO3+ games. enum Template_Flags { - TES5_UseTraits = 0x0001, // Destructible Object; Traits tab, including race, gender, height, weight, - // voice type, death item; Sounds tab; Animation tab; Character Gen tabs - TES5_UseStats = 0x0002, // Stats tab, including level, autocalc, skills, health/magicka/stamina, - // speed, bleedout, class - TES5_UseFactions = 0x0004, // both factions and assigned crime faction - TES5_UseSpellList = 0x0008, // both spells and perks - TES5_UseAIData = 0x0010, // AI Data tab, including aggression/confidence/morality, combat style and - // gift filter - TES5_UseAIPackage = 0x0020, // only the basic Packages listed on the AI Packages tab; - // rest of tab controlled by Def Pack List - TES5_UseBaseData = 0x0080, // including name and short name, and flags for Essential, Protected, - // Respawn, Summonable, Simple Actor, and Doesn't affect stealth meter - TES5_UseInventory = 0x0100, // Inventory tab, including all outfits and geared-up item - // -- but not death item - TES5_UseScript = 0x0200, - TES5_UseDefined = 0x0400, // Def Pack List (the dropdown-selected package lists on the AI Packages tab) - TES5_UseAtkData = 0x0800, // Attack Data tab, including override from behavior graph race, - // events, and data) - TES5_UseKeywords = 0x1000 + Template_UseTraits = 0x0001, // Destructible Object; Traits tab, including race, gender, height, weight, + // voice type, death item; Sounds tab; Animation tab; Character Gen tabs + Template_UseStats = 0x0002, // Stats tab, including level, autocalc, skills, health/magicka/stamina, + // speed, bleedout, class + Template_UseFactions = 0x0004, // both factions and assigned crime faction + Template_UseSpellList = 0x0008, // both spells and perks + Template_UseAIData = 0x0010, // AI Data tab, including aggression/confidence/morality, combat style and + // gift filter + Template_UseAIPackage = 0x0020, // only the basic Packages listed on the AI Packages tab; + // rest of tab controlled by Def Pack List + Template_UseModel = 0x0040, // FO3, FONV; probably not used in TES5+ + Template_UseBaseData = 0x0080, // including name and short name, and flags for Essential, Protected, + // Respawn, Summonable, Simple Actor, and Doesn't affect stealth meter + Template_UseInventory = 0x0100, // Inventory tab, including all outfits and geared-up item, + // but not death item + Template_UseScript = 0x0200, + + // The following flags were added in TES5+: + + Template_UseDefined = 0x0400, // Def Pack List (the dropdown-selected package lists on the AI Packages tab) + Template_UseAtkData = 0x0800, // Attack Data tab, including override from behavior graph race, + // events, and data) + Template_UseKeywords = 0x1000 }; #pragma pack(push, 1) @@ -172,6 +178,7 @@ namespace ESM4 bool mIsTES4; bool mIsFONV; + bool mIsFO4 = false; std::string mEditorId; std::string mFullName;