From d7f30bcf3c7b4921e4c324edfda94e29b0e91ab1 Mon Sep 17 00:00:00 2001 From: Arkadii Hlushchevskyi Date: Sun, 24 Nov 2024 20:50:53 +0200 Subject: [PATCH] Few improvements for debugging - Added more information on deserialization error. - Extracted equipping outfits to a separate method for easier replacement. --- SPID/include/OutfitManager.h | 8 ++++++++ SPID/src/OutfitManager.cpp | 39 +++++++++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/SPID/include/OutfitManager.h b/SPID/include/OutfitManager.h index 213055e..eb5a8a4 100644 --- a/SPID/include/OutfitManager.h +++ b/SPID/include/OutfitManager.h @@ -50,6 +50,14 @@ namespace Outfits static void Save(SKSE::SerializationInterface*); static void Revert(SKSE::SerializationInterface*); + /// + /// This method performs the actual change of the outfit. + /// + /// Actor for whom outfit should be changed + /// The outfit to be set + /// True if the outfit was successfully set, false otherwise + bool SetDefaultOutfit(RE::Actor*, RE::BGSOutfit*); + struct OutfitReplacement { /// The one that NPC had before SPID distribution. diff --git a/SPID/src/OutfitManager.cpp b/SPID/src/OutfitManager.cpp index 09f32e2..3a2f85b 100644 --- a/SPID/src/OutfitManager.cpp +++ b/SPID/src/OutfitManager.cpp @@ -50,7 +50,7 @@ namespace Outfits constexpr std::uint32_t recordType = 'OTFT'; template - bool Load(SKSE::SerializationInterface* interface, T*& output) + bool Load(SKSE::SerializationInterface* interface, T*& output, RE::FormID& formID) { RE::FormID id = 0; @@ -62,10 +62,14 @@ namespace Outfits return false; } + formID = id; // save the originally read formID + if (!interface->ResolveFormID(id, id)) { return false; } + formID = id; // save the resolved formID + if (const auto form = RE::TESForm::LookupByID(id); form) { output = form; return true; @@ -76,18 +80,20 @@ namespace Outfits bool Load(SKSE::SerializationInterface* interface, RE::Actor*& loadedActor, RE::BGSOutfit*& loadedOriginalOutfit, RE::BGSOutfit*& loadedDistributedOutfit) { - if (!Load(interface, loadedActor)) { - logger::warn("Failed to load Outfit Replacement record: Corrupted actor."); + RE::FormID id = 0; + + if (!Load(interface, loadedActor, id)) { + logger::warn("Failed to load Outfit Replacement record: Corrupted actor [{:08X}].", id); return false; } - if (!Load(interface, loadedOriginalOutfit)) { - logger::warn("Failed to load Outfit Replacement record: Corrupted original outfit."); + if (!Load(interface, loadedOriginalOutfit, id)) { + logger::warn("Failed to load Outfit Replacement record: Corrupted original outfit [{:08X}].", id); return false; } - if (!Load(interface, loadedDistributedOutfit)) { - logger::warn("Failed to load Outfit Replacement record: Corrupted distributed outfit."); + if (!Load(interface, loadedDistributedOutfit, id)) { + logger::warn("Failed to load Outfit Replacement record: Corrupted distributed outfit [{:08X}].", id); return false; } @@ -172,7 +178,7 @@ namespace Outfits return ReplacementResult::Skipped; } - actor->SetDefaultOutfit(outfit, false); // Having true here causes infinite loading. It seems that equipping works either way, so we are good :) + SetDefaultOutfit(actor, outfit); if (auto previous = replacements.find(actor->formID); previous != replacements.end()) { previous->second.distributed = outfit; @@ -231,7 +237,7 @@ namespace Outfits logger::info("\tReverting Outfit Replacement for {}", *actor); logger::info("\t\t{:R}", replacement); #endif - if (actor->SetDefaultOutfit(replacement.original, false)) { // Having true here causes infinite loading. It seems that it works either way. + if (manager->SetDefaultOutfit(actor, replacement.original)) { ++revertedCount; } } @@ -243,6 +249,21 @@ namespace Outfits #endif } + bool Manager::SetDefaultOutfit(RE::Actor* actor, RE::BGSOutfit* outfit) + { + actor->SetDefaultOutfit(outfit, false); // Having true here causes infinite loading. It seems that equipping works either way, so we are good :) + + // TODO: Implement the equipment solution from po3 to avoid crashes :) + // With that approach nothing will be saved in the save file again, as such we'll only need to make sure that whatever was NPC's default outfit will get equipped once replacement no longer available. + /* + if (const auto npc = actor->GetActorBase(); npc) { + npc->defaultOutfit = outfit; + } + force_equip_outfit(actor, actor->GetActorBase()); + */ + return true; + } + void Manager::Save(SKSE::SerializationInterface* interface) { #ifndef NDEBUG