From c973b7d8d422314ad9b68127116d60d9386d71fc Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Tue, 24 Jan 2023 23:26:58 -0800 Subject: [PATCH 01/28] Bump commonlibvr --- extern/CommonLibVR | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/CommonLibVR b/extern/CommonLibVR index a3cc6f6..d636864 160000 --- a/extern/CommonLibVR +++ b/extern/CommonLibVR @@ -1 +1 @@ -Subproject commit a3cc6f69457006518f901b2f956861885f67e207 +Subproject commit d63686414905a950435b43a2d4d6ca57f2d753e0 From 59bb6fcba98a5cead9289a6271ac9184db0654fd Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Sat, 22 Apr 2023 00:22:08 -0700 Subject: [PATCH 02/28] Bump commonlibvr --- extern/CommonLibVR | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/CommonLibVR b/extern/CommonLibVR index a03c9b6..08bccb4 160000 --- a/extern/CommonLibVR +++ b/extern/CommonLibVR @@ -1 +1 @@ -Subproject commit a03c9b6caad05dc3d67016b112f1c108f87ba6fe +Subproject commit 08bccb4afec774e02b7741426500348c479ef186 From 9394b8292a486174885e5b8d411b291c0c151fd5 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Fri, 15 Dec 2023 21:37:56 -0800 Subject: [PATCH 03/28] Bump commonlibvr for esl support --- extern/CommonLibVR | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/CommonLibVR b/extern/CommonLibVR index d9e5470..f80b914 160000 --- a/extern/CommonLibVR +++ b/extern/CommonLibVR @@ -1 +1 @@ -Subproject commit d9e54705e5bd208f1c76c4f616e886478cfb811b +Subproject commit f80b9144d6bac29797f90053b8dc8475318b9bfb From 0abc726b540147b67efadaf81c62f4dc34539d84 Mon Sep 17 00:00:00 2001 From: Alan Tse Date: Mon, 29 Jan 2024 23:45:29 -0800 Subject: [PATCH 04/28] bump commonlibvr to latest --- extern/CommonLibVR | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extern/CommonLibVR b/extern/CommonLibVR index 21cdda7..cf9a3b8 160000 --- a/extern/CommonLibVR +++ b/extern/CommonLibVR @@ -1 +1 @@ -Subproject commit 21cdda7fff58b5f2caa0897a160a077feb87ed22 +Subproject commit cf9a3b803bef6f6d07e5abc28af41b9c0e14f333 From 7ea720d3bef0d8d794b0f83d6b35624551ade085 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Wed, 14 Feb 2024 04:47:35 +0530 Subject: [PATCH 05/28] Fix item distribution --- SPID/include/Cache.h | 33 +++++++++++++++------------------ SPID/include/Distribute.h | 2 +- SPID/include/FormData.h | 4 ++-- SPID/src/Cache.cpp | 7 ++----- 4 files changed, 20 insertions(+), 26 deletions(-) diff --git a/SPID/include/Cache.h b/SPID/include/Cache.h index 6664b69..4b2a3a5 100644 --- a/SPID/include/Cache.h +++ b/SPID/include/Cache.h @@ -1,23 +1,20 @@ #pragma once -namespace Cache +namespace FormType { - namespace FormType - { - inline constexpr std::array whitelist{ - RE::FormType::Faction, - RE::FormType::Class, - RE::FormType::CombatStyle, - RE::FormType::Race, - RE::FormType::Outfit, - RE::FormType::NPC, - RE::FormType::VoiceType, - RE::FormType::FormList, - RE::FormType::Spell, - RE::FormType::Armor, - RE::FormType::Location - }; + inline constexpr std::array whitelist{ + RE::FormType::Faction, + RE::FormType::Class, + RE::FormType::CombatStyle, + RE::FormType::Race, + RE::FormType::Outfit, + RE::FormType::NPC, + RE::FormType::VoiceType, + RE::FormType::FormList, + RE::FormType::Spell, + RE::FormType::Armor, + RE::FormType::Location + }; - bool GetWhitelisted(RE::FormType a_type); - } + bool GetWhitelisted(RE::FormType a_type); } diff --git a/SPID/include/Distribute.h b/SPID/include/Distribute.h index 2c5bbc2..ff12aaa 100644 --- a/SPID/include/Distribute.h +++ b/SPID/include/Distribute.h @@ -150,7 +150,7 @@ namespace Distribute if (formData.form->Is(RE::FormType::LeveledItem)) { hasLeveledItems = true; } - collectedForms.emplace(formData.form, formData.idxOrCount); + collectedForms[formData.form] += formData.idxOrCount; ++formData.npcCount; } } diff --git a/SPID/include/FormData.h b/SPID/include/FormData.h index 1c9f520..b70a09c 100644 --- a/SPID/include/FormData.h +++ b/SPID/include/FormData.h @@ -52,7 +52,7 @@ namespace Forms a_dataHandler->LookupForm(*formID, *modName) : RE::TESForm::LookupByID(*formID)) { const auto formType = filterForm->GetFormType(); - if (Cache::FormType::GetWhitelisted(formType)) { + if (FormType::GetWhitelisted(formType)) { a_formVec.emplace_back(filterForm); } else { buffered_logger::error("\t\t[{}] Filter [0x{:X}] ({}) SKIP - invalid formtype ({})", a_path, *formID, modName.value_or(""), formType); @@ -66,7 +66,7 @@ namespace Forms if (!a_editorID.empty()) { if (auto filterForm = RE::TESForm::LookupByEditorID(a_editorID); filterForm) { const auto formType = filterForm->GetFormType(); - if (Cache::FormType::GetWhitelisted(formType)) { + if (FormType::GetWhitelisted(formType)) { a_formVec.emplace_back(filterForm); } else { buffered_logger::error("\t\t[{}] Filter ({}) SKIP - invalid formtype ({})", a_path, a_editorID, formType); diff --git a/SPID/src/Cache.cpp b/SPID/src/Cache.cpp index 596eee1..fef8cab 100644 --- a/SPID/src/Cache.cpp +++ b/SPID/src/Cache.cpp @@ -1,9 +1,6 @@ #include "Cache.h" -namespace Cache +bool FormType::GetWhitelisted(const RE::FormType a_type) { - bool FormType::GetWhitelisted(const RE::FormType a_type) - { - return std::ranges::find(whitelist, a_type) != whitelist.end(); - } + return std::ranges::find(whitelist, a_type) != whitelist.end(); } From ac9d01cb0e8c810a5126f9d1ba807ea895819f10 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Wed, 14 Feb 2024 04:48:26 +0530 Subject: [PATCH 06/28] Memory improvements? --- SPID/include/LookupNPC.h | 3 ++- SPID/include/PCH.h | 4 ++-- SPID/src/DistributeManager.cpp | 2 +- SPID/src/LookupNPC.cpp | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/SPID/include/LookupNPC.h b/SPID/include/LookupNPC.h index 275ff11..795efd0 100644 --- a/SPID/include/LookupNPC.h +++ b/SPID/include/LookupNPC.h @@ -33,6 +33,7 @@ namespace NPC { ID() = default; explicit ID(const RE::TESActorBase* a_base); + ~ID() = default; [[nodiscard]] bool contains(const std::string& a_str) const; @@ -49,9 +50,9 @@ namespace NPC RE::TESNPC* npc; RE::Actor* actor; - std::string name; RE::TESRace* race; std::vector IDs; + std::string name; StringSet keywords{}; std::uint16_t level; RE::SEX sex; diff --git a/SPID/include/PCH.h b/SPID/include/PCH.h index 6faa8d7..ba76683 100644 --- a/SPID/include/PCH.h +++ b/SPID/include/PCH.h @@ -44,9 +44,9 @@ struct overload : Ts... }; template -using Map = ankerl::unordered_dense::segmented_map; +using Map = ankerl::unordered_dense::map; template -using Set = ankerl::unordered_dense::segmented_set; +using Set = ankerl::unordered_dense::set; struct string_hash { diff --git a/SPID/src/DistributeManager.cpp b/SPID/src/DistributeManager.cpp index be7e6a3..fb07519 100644 --- a/SPID/src/DistributeManager.cpp +++ b/SPID/src/DistributeManager.cpp @@ -123,7 +123,7 @@ namespace Distribute Event::Manager::Register(); PCLevelMult::Manager::Register(); - //DoInitialDistribution(); + DoInitialDistribution(); // Clear logger's buffer to free some memory :) buffered_logger::clear(); diff --git a/SPID/src/LookupNPC.cpp b/SPID/src/LookupNPC.cpp index 75d7024..18e4558 100644 --- a/SPID/src/LookupNPC.cpp +++ b/SPID/src/LookupNPC.cpp @@ -30,8 +30,8 @@ namespace NPC Data::Data(RE::Actor* a_actor, RE::TESNPC* a_npc) : npc(a_npc), actor(a_actor), - name(a_actor->GetName()), race(a_npc->GetRace()), + name(a_actor->GetName()), level(a_npc->GetLevel()), sex(a_npc->GetSex()), unique(a_npc->IsUnique()), From 9af8475358b7711bef444299c3248d327dddcd1b Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Wed, 14 Feb 2024 04:48:41 +0530 Subject: [PATCH 07/28] Fix outfit distribution when dead --- SPID/src/DistributeManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SPID/src/DistributeManager.cpp b/SPID/src/DistributeManager.cpp index fb07519..f65c36e 100644 --- a/SPID/src/DistributeManager.cpp +++ b/SPID/src/DistributeManager.cpp @@ -17,7 +17,7 @@ namespace Distribute void detail::force_equip_outfit(RE::Actor* a_actor, const RE::TESNPC* a_npc) { - if (!a_actor->HasOutfitItems(a_npc->defaultOutfit)) { + if (!a_actor->HasOutfitItems(a_npc->defaultOutfit) && !a_actor->IsDead()) { if (const auto invChanges = a_actor->GetInventoryChanges()) { invChanges->InitOutfitItems(a_npc->defaultOutfit, a_npc->GetLevel()); } From 40e1de3593cef47556dc6d99c08ba6ded29e2a04 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Wed, 14 Feb 2024 04:50:50 +0530 Subject: [PATCH 08/28] Update workflows --- .github/workflows/main.yml | 2 +- .github/workflows/maintenance.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ad5beb5..0ea0808 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -30,4 +30,4 @@ jobs: PUBLISH_MOD_CHANGELOG_FILE: "FOMOD/changelog.txt" PUBLISH_MOD_DESCRIPTION_FILE: "FOMOD/description.txt" PUBLISH_ARCHIVE_TYPE: '7z' - VCPKG_COMMIT_ID: '53bef8994c541b6561884a8395ea35715ece75db' + VCPKG_COMMIT_ID: '9854d1d92200d81dde189e53b64c9ba6a305dc9f' diff --git a/.github/workflows/maintenance.yml b/.github/workflows/maintenance.yml index 90bfc7a..85d1a32 100644 --- a/.github/workflows/maintenance.yml +++ b/.github/workflows/maintenance.yml @@ -7,11 +7,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.10' - name: Run clang-format run: find -type f \( -name *.h -o -name *.cpp \) | xargs clang-format-14 -style=file -i From 406ae0320b89cbc454d0515c8e3ee4266aa583e5 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Wed, 14 Feb 2024 04:51:44 +0530 Subject: [PATCH 09/28] Version bump --- SPID/CMakeLists.txt | 2 +- SPID/vcpkg.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SPID/CMakeLists.txt b/SPID/CMakeLists.txt index 985fa02..41435a7 100644 --- a/SPID/CMakeLists.txt +++ b/SPID/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.20) set(NAME "po3_SpellPerkItemDistributor" CACHE STRING "") -set(VERSION 6.8.2 CACHE STRING "") +set(VERSION 6.8.5 CACHE STRING "") set(AE_VERSION 1) set(VR_VERSION 1) diff --git a/SPID/vcpkg.json b/SPID/vcpkg.json index 3407870..409dd44 100644 --- a/SPID/vcpkg.json +++ b/SPID/vcpkg.json @@ -1,6 +1,6 @@ { "name": "spid", - "version-string": "6.8.2", + "version-string": "6.8.5", "description": "Spell Perk Item Distributor", "homepage": "https://github.com/powerof3/Spell-Perk-Item-Distributor", "license": "MIT", From 451cc22cc75de4aa20116daef6e25e4da7638861 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Thu, 15 Feb 2024 02:48:42 +0530 Subject: [PATCH 10/28] Fix distribution on load --- SPID/include/DependencyResolver.h | 1 + SPID/src/DistributeManager.cpp | 14 ++++++++++++-- SPID/src/KeywordDependencies.cpp | 11 +++++++---- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/SPID/include/DependencyResolver.h b/SPID/include/DependencyResolver.h index e1a6da2..0d8e1f6 100644 --- a/SPID/include/DependencyResolver.h +++ b/SPID/include/DependencyResolver.h @@ -155,6 +155,7 @@ class DependencyResolver for (const auto& pair : nodes) { delete pair.second; } + nodes.clear(); } /// Attempts to create a dependency rule between `parent` and `dependency` objects. diff --git a/SPID/src/DistributeManager.cpp b/SPID/src/DistributeManager.cpp index f65c36e..851178e 100644 --- a/SPID/src/DistributeManager.cpp +++ b/SPID/src/DistributeManager.cpp @@ -33,9 +33,16 @@ namespace Distribute static bool thunk(RE::Character* a_this) { if (const auto npc = a_this->GetActorBase()) { - if (detail::should_process_NPC(npc)) { + const auto process = detail::should_process_NPC(npc); + const auto processOnLoad = detail::should_process_NPC(npc, processedOnLoad); + if (process || processOnLoad) { auto npcData = NPCData(a_this, npc); - Distribute(npcData, false); + if (process) { + Distribute(npcData, false, true); + } + if (processOnLoad) { + DistributeItemOutfits(npcData, { a_this, npc, false }); + } } } @@ -113,6 +120,9 @@ namespace Distribute if (processedOutfit = factory->Create(); processedOutfit) { processedOutfit->formEditorID = "SPID_ProcessedOutfit"; } + if (processedOnLoad = factory->Create(); processedOnLoad) { + processedOnLoad->formEditorID = "SPID_ProcessedOnLoad"; + } } if (Forms::GetTotalLeveledEntries() > 0) { diff --git a/SPID/src/KeywordDependencies.cpp b/SPID/src/KeywordDependencies.cpp index 718f874..fc9cc31 100644 --- a/SPID/src/KeywordDependencies.cpp +++ b/SPID/src/KeywordDependencies.cpp @@ -67,7 +67,8 @@ void Dependencies::ResolveKeywords() return; } - const auto startTime = std::chrono::steady_clock::now(); + Timer timer; + timer.start(); // Pre-build a map of all available keywords by names. StringMap allKeywords{}; @@ -140,7 +141,8 @@ void Dependencies::ResolveKeywords() } const auto result = resolver.resolve(); - const auto endTime = std::chrono::steady_clock::now(); + + timer.end(); keywordForms.clear(); logger::info("\tSorted keywords :"); @@ -154,6 +156,7 @@ void Dependencies::ResolveKeywords() } } - const auto duration = std::chrono::duration_cast(endTime - startTime).count(); - logger::info("\tKeyword resolution took {}μs / {}ms", duration, duration / 1000.0f); + logger::info("\tKeyword resolution took {}μs / {}ms", timer.duration_μs(), timer.duration_ms()); + + allKeywords.clear(); } From bc9ba8c69df44db98894ff0b5b00e8eec4c84794 Mon Sep 17 00:00:00 2001 From: powerof3 Date: Wed, 14 Feb 2024 21:19:39 +0000 Subject: [PATCH 11/28] maintenance --- SPID/src/KeywordDependencies.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SPID/src/KeywordDependencies.cpp b/SPID/src/KeywordDependencies.cpp index fc9cc31..dedc37d 100644 --- a/SPID/src/KeywordDependencies.cpp +++ b/SPID/src/KeywordDependencies.cpp @@ -141,7 +141,7 @@ void Dependencies::ResolveKeywords() } const auto result = resolver.resolve(); - + timer.end(); keywordForms.clear(); From ee8e8cb3bce6a97bdb31e29a53af301be791f259 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Thu, 15 Feb 2024 15:17:54 +0530 Subject: [PATCH 12/28] Fix race and leveled filters --- SPID/include/LookupNPC.h | 6 ------ SPID/src/LookupFilters.cpp | 8 +++++--- SPID/src/LookupNPC.cpp | 27 +++------------------------ extern/CommonLibSSE | 2 +- 4 files changed, 9 insertions(+), 34 deletions(-) diff --git a/SPID/include/LookupNPC.h b/SPID/include/LookupNPC.h index 795efd0..7f3be01 100644 --- a/SPID/include/LookupNPC.h +++ b/SPID/include/LookupNPC.h @@ -19,9 +19,6 @@ namespace NPC [[nodiscard]] bool HasFormFilter(const FormVec& a_forms, bool all = false) const; [[nodiscard]] std::uint16_t GetLevel() const; - [[nodiscard]] RE::SEX GetSex() const; - [[nodiscard]] bool IsUnique() const; - [[nodiscard]] bool IsSummonable() const; [[nodiscard]] bool IsChild() const; [[nodiscard]] bool IsLeveled() const; [[nodiscard]] bool IsTeammate() const; @@ -55,9 +52,6 @@ namespace NPC std::string name; StringSet keywords{}; std::uint16_t level; - RE::SEX sex; - bool unique; - bool summonable; bool child; bool teammate; bool leveled; diff --git a/SPID/src/LookupFilters.cpp b/SPID/src/LookupFilters.cpp index 78328e2..aa4a0ab 100644 --- a/SPID/src/LookupFilters.cpp +++ b/SPID/src/LookupFilters.cpp @@ -145,14 +145,16 @@ namespace Filter Result Data::passed_trait_filters(const NPCData& a_npcData) const { + auto npc = a_npcData.GetNPC(); + // Traits - if (traits.sex && a_npcData.GetSex() != *traits.sex) { + if (traits.sex && npc->GetSex() != *traits.sex) { return Result::kFail; } - if (traits.unique && a_npcData.IsUnique() != *traits.unique) { + if (traits.unique && npc->IsUnique() != *traits.unique) { return Result::kFail; } - if (traits.summonable && a_npcData.IsSummonable() != *traits.summonable) { + if (traits.summonable && npc->IsSummonable() != *traits.summonable) { return Result::kFail; } if (traits.child && a_npcData.IsChild() != *traits.child) { diff --git a/SPID/src/LookupNPC.cpp b/SPID/src/LookupNPC.cpp index 18e4558..8fc9d3a 100644 --- a/SPID/src/LookupNPC.cpp +++ b/SPID/src/LookupNPC.cpp @@ -30,13 +30,11 @@ namespace NPC Data::Data(RE::Actor* a_actor, RE::TESNPC* a_npc) : npc(a_npc), actor(a_actor), - race(a_npc->GetRace()), + race(a_actor->GetRace()), name(a_actor->GetName()), level(a_npc->GetLevel()), - sex(a_npc->GetSex()), - unique(a_npc->IsUnique()), - summonable(a_npc->IsSummonable()), - child(a_actor->IsChild() || race && race->formEditorID.contains("RaceChild")) + child(a_actor->IsChild() || race && race->formEditorID.contains("RaceChild")), + leveled(a_actor->IsLeveled()) { npc->ForEachKeyword([&](const RE::BGSKeyword* a_keyword) { keywords.emplace(a_keyword->GetFormEditorID()); @@ -48,11 +46,7 @@ namespace NPC IDs.emplace_back(originalBase); } if (const auto templateBase = extraLvlCreature->templateBase) { - leveled = true; IDs.emplace_back(templateBase); - if (const auto templateRace = templateBase->As()->GetRace()) { - race = templateRace; - } } } else { IDs.emplace_back(npc); @@ -193,21 +187,6 @@ namespace NPC return level; } - RE::SEX Data::GetSex() const - { - return sex; - } - - bool Data::IsUnique() const - { - return unique; - } - - bool Data::IsSummonable() const - { - return summonable; - } - bool Data::IsChild() const { return child; diff --git a/extern/CommonLibSSE b/extern/CommonLibSSE index 78f140e..37c738c 160000 --- a/extern/CommonLibSSE +++ b/extern/CommonLibSSE @@ -1 +1 @@ -Subproject commit 78f140e3878f241b40d0004ae3bd9f30f7a2fa74 +Subproject commit 37c738cfd4485628e5fa503c9833e6113e4d6abc From 3a5a880919c8c1b358f101b23bdc0d2ce88c0e71 Mon Sep 17 00:00:00 2001 From: powerof3 Date: Thu, 15 Feb 2024 09:48:10 +0000 Subject: [PATCH 13/28] maintenance --- SPID/src/LookupFilters.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SPID/src/LookupFilters.cpp b/SPID/src/LookupFilters.cpp index aa4a0ab..ba60729 100644 --- a/SPID/src/LookupFilters.cpp +++ b/SPID/src/LookupFilters.cpp @@ -146,7 +146,7 @@ namespace Filter Result Data::passed_trait_filters(const NPCData& a_npcData) const { auto npc = a_npcData.GetNPC(); - + // Traits if (traits.sex && npc->GetSex() != *traits.sex) { return Result::kFail; From 42dba26fc4c300750b39bfcab9debb43d7086ab1 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Mon, 4 Mar 2024 23:53:21 +0530 Subject: [PATCH 14/28] Prevent outfits from stacking and other fixes --- SPID/include/Distribute.h | 1 - SPID/include/DistributeManager.h | 2 ++ SPID/include/FormData.h | 4 +-- SPID/src/Distribute.cpp | 61 ++++++++++++-------------------- SPID/src/DistributeManager.cpp | 58 ++++++++++++++++++++++-------- 5 files changed, 70 insertions(+), 56 deletions(-) diff --git a/SPID/include/Distribute.h b/SPID/include/Distribute.h index ff12aaa..768442a 100644 --- a/SPID/include/Distribute.h +++ b/SPID/include/Distribute.h @@ -67,7 +67,6 @@ namespace Distribute } } - void equip_worn_outfit(RE::Actor* actor, const RE::BGSOutfit* a_outfit); void add_item(RE::Actor* a_actor, RE::TESBoundObject* a_item, std::uint32_t a_itemCount); void init_leveled_items(RE::Actor* a_actor); bool can_equip_outfit(const RE::TESNPC* a_npc, RE::BGSOutfit* a_outfit); diff --git a/SPID/include/DistributeManager.h b/SPID/include/DistributeManager.h index 2ba7bab..d887ac9 100644 --- a/SPID/include/DistributeManager.h +++ b/SPID/include/DistributeManager.h @@ -9,6 +9,8 @@ namespace Distribute namespace detail { bool should_process_NPC(RE::TESNPC* a_npc, RE::BGSKeyword* a_keyword = processed); + + void equip_worn_outfit(RE::Actor* actor, const RE::BGSOutfit* a_outfit); void force_equip_outfit(RE::Actor* a_actor, const RE::TESNPC* a_npc); } diff --git a/SPID/include/FormData.h b/SPID/include/FormData.h index b70a09c..5111551 100644 --- a/SPID/include/FormData.h +++ b/SPID/include/FormData.h @@ -149,15 +149,15 @@ namespace Forms void ForEachDistributable(Func&& a_func, Args&&... args) { a_func(keywords, std::forward(args)...); + a_func(factions, std::forward(args)...); + a_func(perks, std::forward(args)...); a_func(spells, std::forward(args)...); a_func(levSpells, std::forward(args)...); - a_func(perks, std::forward(args)...); a_func(shouts, std::forward(args)...); a_func(items, std::forward(args)...); a_func(deathItems, std::forward(args)...); a_func(outfits, std::forward(args)...); a_func(sleepOutfits, std::forward(args)...); - a_func(factions, std::forward(args)...); a_func(packages, std::forward(args)...); a_func(skins, std::forward(args)...); } diff --git a/SPID/src/Distribute.cpp b/SPID/src/Distribute.cpp index 2e40cee..4dba983 100644 --- a/SPID/src/Distribute.cpp +++ b/SPID/src/Distribute.cpp @@ -6,30 +6,6 @@ namespace Distribute { namespace detail { - void equip_worn_outfit(RE::Actor* actor, const RE::BGSOutfit* a_outfit) - { - if (!actor || !a_outfit) { - return; - } - - if (const auto invChanges = actor->GetInventoryChanges()) { - if (const auto entryLists = invChanges->entryList) { - const auto formID = a_outfit->GetFormID(); - - for (const auto& entryList : *entryLists) { - if (entryList && entryList->object && entryList->extraLists) { - for (const auto& xList : *entryList->extraLists) { - const auto outfitItem = xList ? xList->GetByType() : nullptr; - if (outfitItem && outfitItem->id == formID) { - RE::ActorEquipManager::GetSingleton()->EquipObject(actor, entryList->object, xList, 1, nullptr, true); - } - } - } - } - } - } - } - void add_item(RE::Actor* a_actor, RE::TESBoundObject* a_item, std::uint32_t a_itemCount) { using func_t = void (*)(RE::Actor*, RE::TESBoundObject*, std::uint32_t, bool, std::uint32_t, RE::BSScript::Internal::VirtualMachine*); @@ -73,6 +49,10 @@ namespace Distribute } }); + for_each_form(a_npcData, Forms::perks, a_input, [&](const std::vector& a_perks) { + npc->AddPerks(a_perks, 1); + }); + for_each_form(a_npcData, Forms::spells, a_input, [&](const std::vector& a_spells) { npc->GetSpellList()->AddSpells(a_spells); }); @@ -81,10 +61,6 @@ namespace Distribute npc->GetSpellList()->AddLevSpells(a_levSpells); }); - for_each_form(a_npcData, Forms::perks, a_input, [&](const std::vector& a_perks) { - npc->AddPerks(a_perks, 1); - }); - for_each_form(a_npcData, Forms::shouts, a_input, [&](const std::vector& a_shouts) { npc->GetSpellList()->AddShouts(a_shouts); }); @@ -172,21 +148,30 @@ namespace Distribute const auto npc = a_npcData.GetNPC(); const auto actor = a_npcData.GetActor(); + if (!actor->IsDead()) { + actor->ResetInventory(false); + } + + for_each_form(a_npcData, Forms::outfits, a_input, [&](auto* a_outfit) { + if (detail::can_equip_outfit(npc, a_outfit)) { + actor->RemoveOutfitItems(npc->defaultOutfit); + npc->defaultOutfit = a_outfit; + npc->AddKeyword(processedOutfit); + return true; + } + return false; + }); + for_each_form(a_npcData, Forms::items, a_input, [&](std::map& a_objects, const bool a_hasLvlItem) { if (npc->AddObjectsToContainer(a_objects, npc)) { if (a_hasLvlItem) { detail::init_leveled_items(actor); } - return true; - } - return false; - }); - - for_each_form(a_npcData, Forms::outfits, a_input, [&](auto* a_outfit) { - if (detail::can_equip_outfit(npc, a_outfit)) { - actor->RemoveOutfitItems(npc->defaultOutfit); - npc->defaultOutfit = a_outfit; - npc->AddKeyword(processedOutfit); + for (auto& [item, count] : a_objects) { + if (item->Is(RE::FormType::Weapon, RE::FormType::Armor)) { + RE::ActorEquipManager::GetSingleton()->EquipObject(actor, item); + } + } return true; } return false; diff --git a/SPID/src/DistributeManager.cpp b/SPID/src/DistributeManager.cpp index 851178e..3415966 100644 --- a/SPID/src/DistributeManager.cpp +++ b/SPID/src/DistributeManager.cpp @@ -6,7 +6,7 @@ namespace Distribute { bool detail::should_process_NPC(RE::TESNPC* a_npc, RE::BGSKeyword* a_keyword) { - if (a_npc->IsDeleted() || a_npc->HasKeyword(a_keyword)) { + if (a_npc->IsPlayer() || a_npc->IsDeleted() || a_npc->HasKeyword(a_keyword)) { return false; } @@ -15,13 +15,42 @@ namespace Distribute return true; } + void detail::equip_worn_outfit(RE::Actor* actor, const RE::BGSOutfit* a_outfit) + { + if (!actor || !a_outfit) { + return; + } + + if (const auto invChanges = actor->GetInventoryChanges()) { + if (const auto entryLists = invChanges->entryList) { + const auto formID = a_outfit->GetFormID(); + + for (const auto& entryList : *entryLists) { + if (entryList && entryList->object && entryList->extraLists) { + for (const auto& xList : *entryList->extraLists) { + const auto outfitItem = xList ? xList->GetByType() : nullptr; + if (outfitItem && outfitItem->id == formID) { + RE::ActorEquipManager::GetSingleton()->EquipObject(actor, entryList->object, xList, 1, nullptr, true); + } + } + } + } + } + } + } + void detail::force_equip_outfit(RE::Actor* a_actor, const RE::TESNPC* a_npc) { + if (!a_npc->defaultOutfit) { + return; + } + if (!a_actor->HasOutfitItems(a_npc->defaultOutfit) && !a_actor->IsDead()) { if (const auto invChanges = a_actor->GetInventoryChanges()) { invChanges->InitOutfitItems(a_npc->defaultOutfit, a_npc->GetLevel()); } } + equip_worn_outfit(a_actor, a_npc->defaultOutfit); } @@ -62,9 +91,7 @@ namespace Distribute const auto root = func(a_this, a_arg1); if (const auto npc = a_this->GetActorBase()) { - if (npc->HasKeyword(processedOutfit)) { - detail::force_equip_outfit(a_this, npc); - } + detail::force_equip_outfit(a_this, npc); } return root; @@ -89,9 +116,8 @@ namespace Distribute auto npcData = NPCData(a_this, npc); Distribute(npcData, false); } - if (npc->HasKeyword(processedOutfit)) { - detail::force_equip_outfit(a_this, npc); - } + + detail::force_equip_outfit(a_this, npc); } } static inline REL::Relocation func; @@ -146,15 +172,17 @@ namespace Distribute if (const auto processLists = RE::ProcessLists::GetSingleton()) { timer.start(); - processLists->ForAllActors([&](RE::Actor* a_actor) { - if (const auto npc = a_actor->GetActorBase(); npc && detail::should_process_NPC(npc)) { - auto npcData = NPCData(a_actor, npc); - Distribute(npcData, false, true); - ++actorCount; + + for (auto& actorHandle : processLists->lowActorHandles) { + if (const auto& actor = actorHandle.get()) { + if (const auto npc = actor->GetActorBase(); npc && detail::should_process_NPC(npc)) { + auto npcData = NPCData(actor.get(), npc); + Distribute(npcData, false, true); + ++actorCount; + } } - return RE::BSContainer::ForEachResult::kContinue; - }); - timer.end(); + } + timer.end(); } LogResults(actorCount); From 9dfa1b4ef8e1d19c61badc6ebbafe06d37086427 Mon Sep 17 00:00:00 2001 From: powerof3 Date: Mon, 4 Mar 2024 18:23:42 +0000 Subject: [PATCH 15/28] maintenance --- SPID/include/DistributeManager.h | 2 +- SPID/src/Distribute.cpp | 6 +++--- SPID/src/DistributeManager.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/SPID/include/DistributeManager.h b/SPID/include/DistributeManager.h index d887ac9..ea4d54c 100644 --- a/SPID/include/DistributeManager.h +++ b/SPID/include/DistributeManager.h @@ -9,7 +9,7 @@ namespace Distribute namespace detail { bool should_process_NPC(RE::TESNPC* a_npc, RE::BGSKeyword* a_keyword = processed); - + void equip_worn_outfit(RE::Actor* actor, const RE::BGSOutfit* a_outfit); void force_equip_outfit(RE::Actor* a_actor, const RE::TESNPC* a_npc); } diff --git a/SPID/src/Distribute.cpp b/SPID/src/Distribute.cpp index 4dba983..1545184 100644 --- a/SPID/src/Distribute.cpp +++ b/SPID/src/Distribute.cpp @@ -151,9 +151,9 @@ namespace Distribute if (!actor->IsDead()) { actor->ResetInventory(false); } - + for_each_form(a_npcData, Forms::outfits, a_input, [&](auto* a_outfit) { - if (detail::can_equip_outfit(npc, a_outfit)) { + if (detail::can_equip_outfit(npc, a_outfit)) { actor->RemoveOutfitItems(npc->defaultOutfit); npc->defaultOutfit = a_outfit; npc->AddKeyword(processedOutfit); @@ -161,7 +161,7 @@ namespace Distribute } return false; }); - + for_each_form(a_npcData, Forms::items, a_input, [&](std::map& a_objects, const bool a_hasLvlItem) { if (npc->AddObjectsToContainer(a_objects, npc)) { if (a_hasLvlItem) { diff --git a/SPID/src/DistributeManager.cpp b/SPID/src/DistributeManager.cpp index 3415966..d22084f 100644 --- a/SPID/src/DistributeManager.cpp +++ b/SPID/src/DistributeManager.cpp @@ -172,7 +172,7 @@ namespace Distribute if (const auto processLists = RE::ProcessLists::GetSingleton()) { timer.start(); - + for (auto& actorHandle : processLists->lowActorHandles) { if (const auto& actor = actorHandle.get()) { if (const auto npc = actor->GetActorBase(); npc && detail::should_process_NPC(npc)) { @@ -182,7 +182,7 @@ namespace Distribute } } } - timer.end(); + timer.end(); } LogResults(actorCount); From 1e62a0687e4964c91398185735abbab5014df36c Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Tue, 5 Mar 2024 15:44:04 +0530 Subject: [PATCH 16/28] outfit fixes --- SPID/src/Distribute.cpp | 9 ++------- SPID/src/DistributeManager.cpp | 16 +++++++++------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/SPID/src/Distribute.cpp b/SPID/src/Distribute.cpp index 1545184..f591ce1 100644 --- a/SPID/src/Distribute.cpp +++ b/SPID/src/Distribute.cpp @@ -147,15 +147,10 @@ namespace Distribute const auto npc = a_npcData.GetNPC(); const auto actor = a_npcData.GetActor(); - - if (!actor->IsDead()) { - actor->ResetInventory(false); - } - + for_each_form(a_npcData, Forms::outfits, a_input, [&](auto* a_outfit) { if (detail::can_equip_outfit(npc, a_outfit)) { - actor->RemoveOutfitItems(npc->defaultOutfit); - npc->defaultOutfit = a_outfit; + npc->defaultOutfit = a_outfit; npc->AddKeyword(processedOutfit); return true; } diff --git a/SPID/src/DistributeManager.cpp b/SPID/src/DistributeManager.cpp index d22084f..e0fc90d 100644 --- a/SPID/src/DistributeManager.cpp +++ b/SPID/src/DistributeManager.cpp @@ -30,7 +30,7 @@ namespace Distribute for (const auto& xList : *entryList->extraLists) { const auto outfitItem = xList ? xList->GetByType() : nullptr; if (outfitItem && outfitItem->id == formID) { - RE::ActorEquipManager::GetSingleton()->EquipObject(actor, entryList->object, xList, 1, nullptr, true); + RE::ActorEquipManager::GetSingleton()->EquipObject(actor, entryList->object, xList, 1, nullptr, true, !actor->IsDead()); } } } @@ -70,6 +70,7 @@ namespace Distribute Distribute(npcData, false, true); } if (processOnLoad) { + a_this->RemoveOutfitItems(nullptr); DistributeItemOutfits(npcData, { a_this, npc, false }); } } @@ -111,13 +112,14 @@ namespace Distribute func(a_this, a_buf); if (const auto npc = a_this->GetActorBase()) { - // some npcs are completely reset upon loading - if (a_this->Is3DLoaded() && detail::should_process_NPC(npc)) { - auto npcData = NPCData(a_this, npc); - Distribute(npcData, false); + // some leveled npcs are completely reset upon loading + if (a_this->Is3DLoaded()) { + if (detail::should_process_NPC(npc)) { + auto npcData = NPCData(a_this, npc); + Distribute(npcData, false); + } + detail::force_equip_outfit(a_this, npc); } - - detail::force_equip_outfit(a_this, npc); } } static inline REL::Relocation func; From 25945d0311b4e4f064b31025f86415513cdebbd4 Mon Sep 17 00:00:00 2001 From: powerof3 Date: Tue, 5 Mar 2024 10:14:23 +0000 Subject: [PATCH 17/28] maintenance --- SPID/src/Distribute.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SPID/src/Distribute.cpp b/SPID/src/Distribute.cpp index f591ce1..3685091 100644 --- a/SPID/src/Distribute.cpp +++ b/SPID/src/Distribute.cpp @@ -147,10 +147,10 @@ namespace Distribute const auto npc = a_npcData.GetNPC(); const auto actor = a_npcData.GetActor(); - + for_each_form(a_npcData, Forms::outfits, a_input, [&](auto* a_outfit) { if (detail::can_equip_outfit(npc, a_outfit)) { - npc->defaultOutfit = a_outfit; + npc->defaultOutfit = a_outfit; npc->AddKeyword(processedOutfit); return true; } From 3bdd8715f58efff65f999f6a05323463e03d3a38 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Sun, 10 Mar 2024 13:05:25 +0530 Subject: [PATCH 18/28] Track removed outfit items when dead --- SPID/include/Distribute.h | 2 +- SPID/include/DistributeManager.h | 7 +- SPID/src/Distribute.cpp | 21 +++--- SPID/src/DistributeManager.cpp | 118 ++++++++++++++++--------------- 4 files changed, 80 insertions(+), 68 deletions(-) diff --git a/SPID/include/Distribute.h b/SPID/include/Distribute.h index 768442a..c016cb5 100644 --- a/SPID/include/Distribute.h +++ b/SPID/include/Distribute.h @@ -264,7 +264,7 @@ namespace Distribute } void Distribute(NPCData& a_npcData, const PCLevelMult::Input& a_input); - void DistributeItemOutfits(NPCData& a_npcData, const PCLevelMult::Input& a_input); + void DistributeItemOutfits(NPCData& a_npcData, const PCLevelMult::Input& a_input, bool a_applyNow = false); void Distribute(NPCData& a_npcData, bool a_onlyLeveledEntries, bool a_noItemOutfits = false); } diff --git a/SPID/include/DistributeManager.h b/SPID/include/DistributeManager.h index ea4d54c..595e1f0 100644 --- a/SPID/include/DistributeManager.h +++ b/SPID/include/DistributeManager.h @@ -4,14 +4,15 @@ namespace Distribute { inline RE::BGSKeyword* processed{ nullptr }; inline RE::BGSKeyword* processedOnLoad{ nullptr }; - inline RE::BGSKeyword* processedOutfit{ nullptr }; namespace detail { bool should_process_NPC(RE::TESNPC* a_npc, RE::BGSKeyword* a_keyword = processed); - void equip_worn_outfit(RE::Actor* actor, const RE::BGSOutfit* a_outfit); - void force_equip_outfit(RE::Actor* a_actor, const RE::TESNPC* a_npc); + std::set get_equipped_item_slots(RE::Actor* a_actor); + void force_equip_outfit(RE::Actor* a_actor, const RE::TESNPC* a_npc, const std::set& a_slots); + + void distribute_on_load(RE::Actor* a_actor, RE::TESNPC* a_npc); } namespace Actor diff --git a/SPID/src/Distribute.cpp b/SPID/src/Distribute.cpp index 3685091..d653dcc 100644 --- a/SPID/src/Distribute.cpp +++ b/SPID/src/Distribute.cpp @@ -22,11 +22,7 @@ namespace Distribute bool can_equip_outfit(const RE::TESNPC* a_npc, RE::BGSOutfit* a_outfit) { - if (a_npc->HasKeyword(processedOutfit) || a_npc->defaultOutfit == a_outfit) { - return false; - } - - return true; + return a_npc->defaultOutfit != a_outfit; } } @@ -139,7 +135,7 @@ namespace Distribute }); } - void DistributeItemOutfits(NPCData& a_npcData, const PCLevelMult::Input& a_input) + void DistributeItemOutfits(NPCData& a_npcData, const PCLevelMult::Input& a_input, bool a_applyNow) { if (a_input.onlyPlayerLevelEntries && PCLevelMult::Manager::GetSingleton()->HasHitLevelCap(a_input)) { return; @@ -148,22 +144,31 @@ namespace Distribute const auto npc = a_npcData.GetNPC(); const auto actor = a_npcData.GetActor(); + std::set slots; + if (a_applyNow) { + slots = detail::get_equipped_item_slots(actor); + actor->RemoveOutfitItems(nullptr); + } + for_each_form(a_npcData, Forms::outfits, a_input, [&](auto* a_outfit) { if (detail::can_equip_outfit(npc, a_outfit)) { npc->defaultOutfit = a_outfit; - npc->AddKeyword(processedOutfit); return true; } return false; }); + if (a_applyNow) { + detail::force_equip_outfit(actor, npc, slots); + } + for_each_form(a_npcData, Forms::items, a_input, [&](std::map& a_objects, const bool a_hasLvlItem) { if (npc->AddObjectsToContainer(a_objects, npc)) { if (a_hasLvlItem) { detail::init_leveled_items(actor); } for (auto& [item, count] : a_objects) { - if (item->Is(RE::FormType::Weapon, RE::FormType::Armor)) { + if (item->Is(RE::FormType::Weapon, RE::FormType::Armor, RE::FormType::LeveledItem)) { RE::ActorEquipManager::GetSingleton()->EquipObject(actor, item); } } diff --git a/SPID/src/DistributeManager.cpp b/SPID/src/DistributeManager.cpp index e0fc90d..155288a 100644 --- a/SPID/src/DistributeManager.cpp +++ b/SPID/src/DistributeManager.cpp @@ -15,43 +15,88 @@ namespace Distribute return true; } - void detail::equip_worn_outfit(RE::Actor* actor, const RE::BGSOutfit* a_outfit) + std::set detail::get_equipped_item_slots(RE::Actor* a_actor) { - if (!actor || !a_outfit) { + if (!a_actor) { + return {}; + } + + std::set slots; + + if (const auto invChanges = a_actor->GetInventoryChanges()) { + if (const auto entryLists = invChanges->entryList) { + for (const auto& entryList : *entryLists) { + if (entryList && entryList->object && entryList->object->IsArmor() && entryList->IsWorn()) { + slots.insert(entryList->object->As()->GetSlotMask()); + } + } + } + } + + return slots; + } + + void detail::force_equip_outfit(RE::Actor* a_actor, const RE::TESNPC* a_npc, const std::set& a_slots) + { + if (!a_npc->defaultOutfit) { return; } - if (const auto invChanges = actor->GetInventoryChanges()) { + if (const auto invChanges = a_actor->GetInventoryChanges()) { + invChanges->InitOutfitItems(a_npc->defaultOutfit, a_npc->GetLevel()); + } + + std::vector armorToRemove; + + if (const auto invChanges = a_actor->GetInventoryChanges()) { if (const auto entryLists = invChanges->entryList) { - const auto formID = a_outfit->GetFormID(); + const auto formID = a_npc->defaultOutfit->GetFormID(); + + bool startsDead = a_actor->IsDead() || (a_actor->formFlags & RE::Actor::RecordFlags::kStartsDead) != 0; for (const auto& entryList : *entryLists) { if (entryList && entryList->object && entryList->extraLists) { for (const auto& xList : *entryList->extraLists) { const auto outfitItem = xList ? xList->GetByType() : nullptr; if (outfitItem && outfitItem->id == formID) { - RE::ActorEquipManager::GetSingleton()->EquipObject(actor, entryList->object, xList, 1, nullptr, true, !actor->IsDead()); + if (auto armor = entryList->object->As(); armor && startsDead) { + if (a_slots.empty() || std::ranges::none_of(a_slots, [&](auto slot) { return armor->bipedModelData.bipedObjectSlots.any(slot); })) { + armorToRemove.push_back(armor); + continue; + } + } + RE::ActorEquipManager::GetSingleton()->EquipObject(a_actor, entryList->object, xList, 1, nullptr, true, true, false); } } } } } } - } - void detail::force_equip_outfit(RE::Actor* a_actor, const RE::TESNPC* a_npc) - { - if (!a_npc->defaultOutfit) { - return; + if (!armorToRemove.empty()) { + SKSE::GetTaskInterface()->AddTask([a_actor, armorToRemove]() { + for (auto& armor : armorToRemove) { + if (armor) { + a_actor->RemoveItem(armor, 1, RE::ITEM_REMOVE_REASON::kRemove, nullptr, nullptr); + } + } + }); } + } - if (!a_actor->HasOutfitItems(a_npc->defaultOutfit) && !a_actor->IsDead()) { - if (const auto invChanges = a_actor->GetInventoryChanges()) { - invChanges->InitOutfitItems(a_npc->defaultOutfit, a_npc->GetLevel()); + void detail::distribute_on_load(RE::Actor* a_actor, RE::TESNPC* a_npc) + { + const auto process = should_process_NPC(a_npc); + const auto processOnLoad = detail::should_process_NPC(a_npc, processedOnLoad); + if (process || processOnLoad) { + auto npcData = NPCData(a_actor, a_npc); + if (process) { + Distribute(npcData, false, true); + } + if (processOnLoad) { + DistributeItemOutfits(npcData, { a_actor, a_npc, false }, true); } } - - equip_worn_outfit(a_actor, a_npc->defaultOutfit); } namespace Actor @@ -62,18 +107,7 @@ namespace Distribute static bool thunk(RE::Character* a_this) { if (const auto npc = a_this->GetActorBase()) { - const auto process = detail::should_process_NPC(npc); - const auto processOnLoad = detail::should_process_NPC(npc, processedOnLoad); - if (process || processOnLoad) { - auto npcData = NPCData(a_this, npc); - if (process) { - Distribute(npcData, false, true); - } - if (processOnLoad) { - a_this->RemoveOutfitItems(nullptr); - DistributeItemOutfits(npcData, { a_this, npc, false }); - } - } + detail::distribute_on_load(a_this, npc); } return func(a_this); @@ -84,26 +118,6 @@ namespace Distribute static inline constexpr std::size_t size{ 0x6D }; }; - // Force outfit equip - struct Load3D - { - static RE::NiAVObject* thunk(RE::Character* a_this, bool a_arg1) - { - const auto root = func(a_this, a_arg1); - - if (const auto npc = a_this->GetActorBase()) { - detail::force_equip_outfit(a_this, npc); - } - - return root; - } - - static inline REL::Relocation func; - - static inline constexpr std::size_t index{ 0 }; - static inline constexpr std::size_t size{ 0x6A }; - }; - // Post distribution struct InitLoadGame { @@ -114,11 +128,7 @@ namespace Distribute if (const auto npc = a_this->GetActorBase()) { // some leveled npcs are completely reset upon loading if (a_this->Is3DLoaded()) { - if (detail::should_process_NPC(npc)) { - auto npcData = NPCData(a_this, npc); - Distribute(npcData, false); - } - detail::force_equip_outfit(a_this, npc); + detail::distribute_on_load(a_this, npc); } } } @@ -131,7 +141,6 @@ namespace Distribute void Install() { stl::write_vfunc(); - stl::write_vfunc(); stl::write_vfunc(); logger::info("Installed actor load hooks"); @@ -145,9 +154,6 @@ namespace Distribute if (processed = factory->Create(); processed) { processed->formEditorID = "SPID_Processed"; } - if (processedOutfit = factory->Create(); processedOutfit) { - processedOutfit->formEditorID = "SPID_ProcessedOutfit"; - } if (processedOnLoad = factory->Create(); processedOnLoad) { processedOnLoad->formEditorID = "SPID_ProcessedOnLoad"; } From ecd0a41b3defd4657cb1ea33e83d2da9ffc77288 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Sun, 10 Mar 2024 13:54:03 +0530 Subject: [PATCH 19/28] Equip leveled items --- SPID/include/Distribute.h | 2 -- SPID/src/Distribute.cpp | 46 +++++++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/SPID/include/Distribute.h b/SPID/include/Distribute.h index c016cb5..f230cce 100644 --- a/SPID/include/Distribute.h +++ b/SPID/include/Distribute.h @@ -68,8 +68,6 @@ namespace Distribute } void add_item(RE::Actor* a_actor, RE::TESBoundObject* a_item, std::uint32_t a_itemCount); - void init_leveled_items(RE::Actor* a_actor); - bool can_equip_outfit(const RE::TESNPC* a_npc, RE::BGSOutfit* a_outfit); } // old method (distributing one by one) diff --git a/SPID/src/Distribute.cpp b/SPID/src/Distribute.cpp index d653dcc..01e29f9 100644 --- a/SPID/src/Distribute.cpp +++ b/SPID/src/Distribute.cpp @@ -8,22 +8,10 @@ namespace Distribute { void add_item(RE::Actor* a_actor, RE::TESBoundObject* a_item, std::uint32_t a_itemCount) { - using func_t = void (*)(RE::Actor*, RE::TESBoundObject*, std::uint32_t, bool, std::uint32_t, RE::BSScript::Internal::VirtualMachine*); + using func_t = void (*)(RE::Actor*, RE::TESBoundObject*, std::uint32_t, bool, std::uint32_t, RE::BSScript::Internal::VirtualMachine*); REL::Relocation func{ RELOCATION_ID(55945, 56489) }; return func(a_actor, a_item, a_itemCount, true, 0, RE::BSScript::Internal::VirtualMachine::GetSingleton()); } - - void init_leveled_items(RE::Actor* a_actor) - { - if (const auto invChanges = a_actor->GetInventoryChanges(true)) { - invChanges->InitLeveledItems(); - } - } - - bool can_equip_outfit(const RE::TESNPC* a_npc, RE::BGSOutfit* a_outfit) - { - return a_npc->defaultOutfit != a_outfit; - } } void Distribute(NPCData& a_npcData, const PCLevelMult::Input& a_input) @@ -151,7 +139,7 @@ namespace Distribute } for_each_form(a_npcData, Forms::outfits, a_input, [&](auto* a_outfit) { - if (detail::can_equip_outfit(npc, a_outfit)) { + if (npc->defaultOutfit != a_outfit) { npc->defaultOutfit = a_outfit; return true; } @@ -162,20 +150,40 @@ namespace Distribute detail::force_equip_outfit(actor, npc, slots); } + auto inv_before = actor->GetInventory([](auto& item) { + return item.Is(RE::FormType::Weapon, RE::FormType::Armor); + }); + + bool recalcInventory = false; for_each_form(a_npcData, Forms::items, a_input, [&](std::map& a_objects, const bool a_hasLvlItem) { if (npc->AddObjectsToContainer(a_objects, npc)) { if (a_hasLvlItem) { - detail::init_leveled_items(actor); - } - for (auto& [item, count] : a_objects) { - if (item->Is(RE::FormType::Weapon, RE::FormType::Armor, RE::FormType::LeveledItem)) { - RE::ActorEquipManager::GetSingleton()->EquipObject(actor, item); + recalcInventory = true; + if (const auto invChanges = actor->GetInventoryChanges(true)) { + invChanges->InitLeveledItems(); + } + } else { + for (auto& [item, count] : a_objects) { + if (item->Is(RE::FormType::Weapon, RE::FormType::Armor)) { + RE::ActorEquipManager::GetSingleton()->EquipObject(actor, item); + } } } return true; } return false; }); + + if (recalcInventory) { + auto inv_after = actor->GetInventory([](auto& item) { + return item.Is(RE::FormType::Weapon, RE::FormType::Armor); + }); + for (auto& [item, data] : inv_after) { + if (!inv_before.contains(item) && item->Is(RE::FormType::Weapon, RE::FormType::Armor)) { + RE::ActorEquipManager::GetSingleton()->EquipObject(actor, item); + } + } + } } void Distribute(NPCData& a_npcData, bool a_onlyLeveledEntries, bool a_noItemOutfits) From 8ce120e472574819d3af07f0ec45e930dc1d2a09 Mon Sep 17 00:00:00 2001 From: powerof3 Date: Sun, 10 Mar 2024 08:24:21 +0000 Subject: [PATCH 20/28] maintenance --- SPID/src/Distribute.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SPID/src/Distribute.cpp b/SPID/src/Distribute.cpp index 01e29f9..601738c 100644 --- a/SPID/src/Distribute.cpp +++ b/SPID/src/Distribute.cpp @@ -8,7 +8,7 @@ namespace Distribute { void add_item(RE::Actor* a_actor, RE::TESBoundObject* a_item, std::uint32_t a_itemCount) { - using func_t = void (*)(RE::Actor*, RE::TESBoundObject*, std::uint32_t, bool, std::uint32_t, RE::BSScript::Internal::VirtualMachine*); + using func_t = void (*)(RE::Actor*, RE::TESBoundObject*, std::uint32_t, bool, std::uint32_t, RE::BSScript::Internal::VirtualMachine*); REL::Relocation func{ RELOCATION_ID(55945, 56489) }; return func(a_actor, a_item, a_itemCount, true, 0, RE::BSScript::Internal::VirtualMachine::GetSingleton()); } From 066b0d528a08d7df1a35f0707c70ec8984ef263d Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Sun, 10 Mar 2024 14:34:56 +0530 Subject: [PATCH 21/28] Don't equip items with zero count --- SPID/src/Distribute.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/SPID/src/Distribute.cpp b/SPID/src/Distribute.cpp index 601738c..bbabfb3 100644 --- a/SPID/src/Distribute.cpp +++ b/SPID/src/Distribute.cpp @@ -179,7 +179,8 @@ namespace Distribute return item.Is(RE::FormType::Weapon, RE::FormType::Armor); }); for (auto& [item, data] : inv_after) { - if (!inv_before.contains(item) && item->Is(RE::FormType::Weapon, RE::FormType::Armor)) { + auto& [count, extra] = data; + if (!inv_before.contains(item) && count > 0 && !extra->IsWorn()) { RE::ActorEquipManager::GetSingleton()->EquipObject(actor, item); } } From 5a77986af9c7c2409d145a3f5f840e607e5626cb Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Tue, 12 Mar 2024 07:24:50 +0530 Subject: [PATCH 22/28] force equip outfit on 3d load AND game load --- SPID/include/Distribute.h | 4 ++-- SPID/src/Distribute.cpp | 34 ++++++++++++---------------------- SPID/src/DistributeManager.cpp | 12 +++++++++++- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/SPID/include/Distribute.h b/SPID/include/Distribute.h index f230cce..ad26751 100644 --- a/SPID/include/Distribute.h +++ b/SPID/include/Distribute.h @@ -262,7 +262,7 @@ namespace Distribute } void Distribute(NPCData& a_npcData, const PCLevelMult::Input& a_input); - void DistributeItemOutfits(NPCData& a_npcData, const PCLevelMult::Input& a_input, bool a_applyNow = false); + void DistributeItems(NPCData& a_npcData, const PCLevelMult::Input& a_input); - void Distribute(NPCData& a_npcData, bool a_onlyLeveledEntries, bool a_noItemOutfits = false); + void Distribute(NPCData& a_npcData, bool a_onlyLeveledEntries, bool a_noItems = false); } diff --git a/SPID/src/Distribute.cpp b/SPID/src/Distribute.cpp index bbabfb3..8216039 100644 --- a/SPID/src/Distribute.cpp +++ b/SPID/src/Distribute.cpp @@ -114,6 +114,14 @@ namespace Distribute return false; }); + for_each_form(a_npcData, Forms::outfits, a_input, [&](auto* a_outfit) { + if (npc->defaultOutfit != a_outfit) { + npc->defaultOutfit = a_outfit; + return true; + } + return false; + }); + for_each_form(a_npcData, Forms::sleepOutfits, a_input, [&](auto* a_outfit) { if (npc->sleepOutfit != a_outfit) { npc->sleepOutfit = a_outfit; @@ -123,7 +131,7 @@ namespace Distribute }); } - void DistributeItemOutfits(NPCData& a_npcData, const PCLevelMult::Input& a_input, bool a_applyNow) + void DistributeItems(NPCData& a_npcData, const PCLevelMult::Input& a_input) { if (a_input.onlyPlayerLevelEntries && PCLevelMult::Manager::GetSingleton()->HasHitLevelCap(a_input)) { return; @@ -132,24 +140,6 @@ namespace Distribute const auto npc = a_npcData.GetNPC(); const auto actor = a_npcData.GetActor(); - std::set slots; - if (a_applyNow) { - slots = detail::get_equipped_item_slots(actor); - actor->RemoveOutfitItems(nullptr); - } - - for_each_form(a_npcData, Forms::outfits, a_input, [&](auto* a_outfit) { - if (npc->defaultOutfit != a_outfit) { - npc->defaultOutfit = a_outfit; - return true; - } - return false; - }); - - if (a_applyNow) { - detail::force_equip_outfit(actor, npc, slots); - } - auto inv_before = actor->GetInventory([](auto& item) { return item.Is(RE::FormType::Weapon, RE::FormType::Armor); }); @@ -187,13 +177,13 @@ namespace Distribute } } - void Distribute(NPCData& a_npcData, bool a_onlyLeveledEntries, bool a_noItemOutfits) + void Distribute(NPCData& a_npcData, bool a_onlyLeveledEntries, bool a_noItems) { const auto input = PCLevelMult::Input{ a_npcData.GetActor(), a_npcData.GetNPC(), a_onlyLeveledEntries }; Distribute(a_npcData, input); - if (!a_noItemOutfits) { - DistributeItemOutfits(a_npcData, input); + if (!a_noItems) { + DistributeItems(a_npcData, input); } } } diff --git a/SPID/src/DistributeManager.cpp b/SPID/src/DistributeManager.cpp index 155288a..e772b14 100644 --- a/SPID/src/DistributeManager.cpp +++ b/SPID/src/DistributeManager.cpp @@ -94,7 +94,7 @@ namespace Distribute Distribute(npcData, false, true); } if (processOnLoad) { - DistributeItemOutfits(npcData, { a_actor, a_npc, false }, true); + DistributeItems(npcData, { a_actor, a_npc, false }); } } } @@ -107,7 +107,12 @@ namespace Distribute static bool thunk(RE::Character* a_this) { if (const auto npc = a_this->GetActorBase()) { + auto slots = detail::get_equipped_item_slots(a_this); + a_this->RemoveOutfitItems(nullptr); + detail::distribute_on_load(a_this, npc); + + detail::force_equip_outfit(a_this, npc, slots); } return func(a_this); @@ -126,10 +131,15 @@ namespace Distribute func(a_this, a_buf); if (const auto npc = a_this->GetActorBase()) { + auto slots = detail::get_equipped_item_slots(a_this); + a_this->RemoveOutfitItems(nullptr); + // some leveled npcs are completely reset upon loading if (a_this->Is3DLoaded()) { detail::distribute_on_load(a_this, npc); } + + detail::force_equip_outfit(a_this, npc, slots); } } static inline REL::Relocation func; From e017e6ae70f0745691ea20fd1b197135b58f1013 Mon Sep 17 00:00:00 2001 From: powerof3 Date: Tue, 12 Mar 2024 01:55:17 +0000 Subject: [PATCH 23/28] maintenance --- SPID/src/DistributeManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SPID/src/DistributeManager.cpp b/SPID/src/DistributeManager.cpp index e772b14..ce4d670 100644 --- a/SPID/src/DistributeManager.cpp +++ b/SPID/src/DistributeManager.cpp @@ -133,7 +133,7 @@ namespace Distribute if (const auto npc = a_this->GetActorBase()) { auto slots = detail::get_equipped_item_slots(a_this); a_this->RemoveOutfitItems(nullptr); - + // some leveled npcs are completely reset upon loading if (a_this->Is3DLoaded()) { detail::distribute_on_load(a_this, npc); From 7c063005734031e10e8a704fb4791cf2da4c0543 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Wed, 13 Mar 2024 02:21:36 +0530 Subject: [PATCH 24/28] Dead actors can't equip stuff --- SPID/src/Distribute.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/SPID/src/Distribute.cpp b/SPID/src/Distribute.cpp index 8216039..c0ce8fc 100644 --- a/SPID/src/Distribute.cpp +++ b/SPID/src/Distribute.cpp @@ -8,7 +8,7 @@ namespace Distribute { void add_item(RE::Actor* a_actor, RE::TESBoundObject* a_item, std::uint32_t a_itemCount) { - using func_t = void (*)(RE::Actor*, RE::TESBoundObject*, std::uint32_t, bool, std::uint32_t, RE::BSScript::Internal::VirtualMachine*); + using func_t = void (*)(RE::Actor*, RE::TESBoundObject*, std::uint32_t, bool, std::uint32_t, RE::BSScript::Internal::VirtualMachine*); REL::Relocation func{ RELOCATION_ID(55945, 56489) }; return func(a_actor, a_item, a_itemCount, true, 0, RE::BSScript::Internal::VirtualMachine::GetSingleton()); } @@ -145,6 +145,8 @@ namespace Distribute }); bool recalcInventory = false; + bool startsDead = actor->IsDead() || (actor->formFlags & RE::Actor::RecordFlags::kStartsDead) != 0; + for_each_form(a_npcData, Forms::items, a_input, [&](std::map& a_objects, const bool a_hasLvlItem) { if (npc->AddObjectsToContainer(a_objects, npc)) { if (a_hasLvlItem) { @@ -152,7 +154,7 @@ namespace Distribute if (const auto invChanges = actor->GetInventoryChanges(true)) { invChanges->InitLeveledItems(); } - } else { + } else if (!startsDead) { for (auto& [item, count] : a_objects) { if (item->Is(RE::FormType::Weapon, RE::FormType::Armor)) { RE::ActorEquipManager::GetSingleton()->EquipObject(actor, item); @@ -164,7 +166,7 @@ namespace Distribute return false; }); - if (recalcInventory) { + if (recalcInventory && !startsDead) { auto inv_after = actor->GetInventory([](auto& item) { return item.Is(RE::FormType::Weapon, RE::FormType::Armor); }); From eb5156eb32a2a9b1de0f82d2edfe3c0ad490f7a9 Mon Sep 17 00:00:00 2001 From: powerof3 Date: Tue, 12 Mar 2024 20:52:00 +0000 Subject: [PATCH 25/28] maintenance --- SPID/src/Distribute.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SPID/src/Distribute.cpp b/SPID/src/Distribute.cpp index c0ce8fc..fe65b33 100644 --- a/SPID/src/Distribute.cpp +++ b/SPID/src/Distribute.cpp @@ -8,7 +8,7 @@ namespace Distribute { void add_item(RE::Actor* a_actor, RE::TESBoundObject* a_item, std::uint32_t a_itemCount) { - using func_t = void (*)(RE::Actor*, RE::TESBoundObject*, std::uint32_t, bool, std::uint32_t, RE::BSScript::Internal::VirtualMachine*); + using func_t = void (*)(RE::Actor*, RE::TESBoundObject*, std::uint32_t, bool, std::uint32_t, RE::BSScript::Internal::VirtualMachine*); REL::Relocation func{ RELOCATION_ID(55945, 56489) }; return func(a_actor, a_item, a_itemCount, true, 0, RE::BSScript::Internal::VirtualMachine::GetSingleton()); } From c94d4221aa652eb9a961412930fc76fd9feb3d52 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Wed, 13 Mar 2024 19:08:28 +0530 Subject: [PATCH 26/28] revert item equipping --- SPID/include/Distribute.h | 4 +- SPID/include/DistributeManager.h | 2 +- SPID/src/Distribute.cpp | 63 ++++---------------------------- SPID/src/DistributeManager.cpp | 56 ++++++++++++---------------- 4 files changed, 33 insertions(+), 92 deletions(-) diff --git a/SPID/include/Distribute.h b/SPID/include/Distribute.h index ad26751..a9b0751 100644 --- a/SPID/include/Distribute.h +++ b/SPID/include/Distribute.h @@ -262,7 +262,5 @@ namespace Distribute } void Distribute(NPCData& a_npcData, const PCLevelMult::Input& a_input); - void DistributeItems(NPCData& a_npcData, const PCLevelMult::Input& a_input); - - void Distribute(NPCData& a_npcData, bool a_onlyLeveledEntries, bool a_noItems = false); + void Distribute(NPCData& a_npcData, bool a_onlyLeveledEntries); } diff --git a/SPID/include/DistributeManager.h b/SPID/include/DistributeManager.h index 595e1f0..83cd48f 100644 --- a/SPID/include/DistributeManager.h +++ b/SPID/include/DistributeManager.h @@ -3,7 +3,7 @@ namespace Distribute { inline RE::BGSKeyword* processed{ nullptr }; - inline RE::BGSKeyword* processedOnLoad{ nullptr }; + inline RE::BGSKeyword* processedOutfit{ nullptr }; namespace detail { diff --git a/SPID/src/Distribute.cpp b/SPID/src/Distribute.cpp index c0ce8fc..9a217b6 100644 --- a/SPID/src/Distribute.cpp +++ b/SPID/src/Distribute.cpp @@ -106,16 +106,9 @@ namespace Distribute return false; }); - for_each_form(a_npcData, Forms::skins, a_input, [&](auto* a_skin) { - if (npc->skin != a_skin) { - npc->skin = a_skin; - return true; - } - return false; - }); - for_each_form(a_npcData, Forms::outfits, a_input, [&](auto* a_outfit) { - if (npc->defaultOutfit != a_outfit) { + if (npc->defaultOutfit != a_outfit && !npc->HasKeyword(processedOutfit)) { + npc->AddKeyword(processedOutfit); npc->defaultOutfit = a_outfit; return true; } @@ -129,63 +122,23 @@ namespace Distribute } return false; }); - } - - void DistributeItems(NPCData& a_npcData, const PCLevelMult::Input& a_input) - { - if (a_input.onlyPlayerLevelEntries && PCLevelMult::Manager::GetSingleton()->HasHitLevelCap(a_input)) { - return; - } - - const auto npc = a_npcData.GetNPC(); - const auto actor = a_npcData.GetActor(); - auto inv_before = actor->GetInventory([](auto& item) { - return item.Is(RE::FormType::Weapon, RE::FormType::Armor); + for_each_form(a_npcData, Forms::items, a_input, [&](std::map& a_objects, const bool a_hasLvlItem) { + return npc->AddObjectsToContainer(a_objects, npc); }); - bool recalcInventory = false; - bool startsDead = actor->IsDead() || (actor->formFlags & RE::Actor::RecordFlags::kStartsDead) != 0; - - for_each_form(a_npcData, Forms::items, a_input, [&](std::map& a_objects, const bool a_hasLvlItem) { - if (npc->AddObjectsToContainer(a_objects, npc)) { - if (a_hasLvlItem) { - recalcInventory = true; - if (const auto invChanges = actor->GetInventoryChanges(true)) { - invChanges->InitLeveledItems(); - } - } else if (!startsDead) { - for (auto& [item, count] : a_objects) { - if (item->Is(RE::FormType::Weapon, RE::FormType::Armor)) { - RE::ActorEquipManager::GetSingleton()->EquipObject(actor, item); - } - } - } + for_each_form(a_npcData, Forms::skins, a_input, [&](auto* a_skin) { + if (npc->skin != a_skin) { + npc->skin = a_skin; return true; } return false; }); - - if (recalcInventory && !startsDead) { - auto inv_after = actor->GetInventory([](auto& item) { - return item.Is(RE::FormType::Weapon, RE::FormType::Armor); - }); - for (auto& [item, data] : inv_after) { - auto& [count, extra] = data; - if (!inv_before.contains(item) && count > 0 && !extra->IsWorn()) { - RE::ActorEquipManager::GetSingleton()->EquipObject(actor, item); - } - } - } } - void Distribute(NPCData& a_npcData, bool a_onlyLeveledEntries, bool a_noItems) + void Distribute(NPCData& a_npcData, bool a_onlyLeveledEntries) { const auto input = PCLevelMult::Input{ a_npcData.GetActor(), a_npcData.GetNPC(), a_onlyLeveledEntries }; - Distribute(a_npcData, input); - if (!a_noItems) { - DistributeItems(a_npcData, input); - } } } diff --git a/SPID/src/DistributeManager.cpp b/SPID/src/DistributeManager.cpp index ce4d670..d6eb3d0 100644 --- a/SPID/src/DistributeManager.cpp +++ b/SPID/src/DistributeManager.cpp @@ -46,7 +46,8 @@ namespace Distribute invChanges->InitOutfitItems(a_npc->defaultOutfit, a_npc->GetLevel()); } - std::vector armorToRemove; + std::vector armorToRemove; + std::vector armorToEquip; if (const auto invChanges = a_actor->GetInventoryChanges()) { if (const auto entryLists = invChanges->entryList) { @@ -65,7 +66,7 @@ namespace Distribute continue; } } - RE::ActorEquipManager::GetSingleton()->EquipObject(a_actor, entryList->object, xList, 1, nullptr, true, true, false); + armorToEquip.push_back(entryList->object); } } } @@ -73,30 +74,29 @@ namespace Distribute } } - if (!armorToRemove.empty()) { - SKSE::GetTaskInterface()->AddTask([a_actor, armorToRemove]() { - for (auto& armor : armorToRemove) { - if (armor) { - a_actor->RemoveItem(armor, 1, RE::ITEM_REMOVE_REASON::kRemove, nullptr, nullptr); - } + SKSE::GetTaskInterface()->AddTask([a_actor, armorToRemove, armorToEquip]() { + for (auto& armor : armorToRemove) { + if (armor) { + a_actor->RemoveItem(armor, 1, RE::ITEM_REMOVE_REASON::kRemove, nullptr, nullptr); } - }); - } + } + for (auto& armor : armorToEquip) { + RE::ActorEquipManager::GetSingleton()->EquipObject(a_actor, armor, nullptr, 1, nullptr, true, true, false, false); + } + }); } void detail::distribute_on_load(RE::Actor* a_actor, RE::TESNPC* a_npc) { - const auto process = should_process_NPC(a_npc); - const auto processOnLoad = detail::should_process_NPC(a_npc, processedOnLoad); - if (process || processOnLoad) { + auto slots = detail::get_equipped_item_slots(a_actor); + a_actor->RemoveOutfitItems(nullptr); + + if (should_process_NPC(a_npc)) { auto npcData = NPCData(a_actor, a_npc); - if (process) { - Distribute(npcData, false, true); - } - if (processOnLoad) { - DistributeItems(npcData, { a_actor, a_npc, false }); - } + Distribute(npcData, false); } + + detail::force_equip_outfit(a_actor, a_npc, slots); } namespace Actor @@ -107,12 +107,7 @@ namespace Distribute static bool thunk(RE::Character* a_this) { if (const auto npc = a_this->GetActorBase()) { - auto slots = detail::get_equipped_item_slots(a_this); - a_this->RemoveOutfitItems(nullptr); - detail::distribute_on_load(a_this, npc); - - detail::force_equip_outfit(a_this, npc, slots); } return func(a_this); @@ -131,15 +126,10 @@ namespace Distribute func(a_this, a_buf); if (const auto npc = a_this->GetActorBase()) { - auto slots = detail::get_equipped_item_slots(a_this); - a_this->RemoveOutfitItems(nullptr); - // some leveled npcs are completely reset upon loading if (a_this->Is3DLoaded()) { detail::distribute_on_load(a_this, npc); } - - detail::force_equip_outfit(a_this, npc, slots); } } static inline REL::Relocation func; @@ -164,8 +154,8 @@ namespace Distribute if (processed = factory->Create(); processed) { processed->formEditorID = "SPID_Processed"; } - if (processedOnLoad = factory->Create(); processedOnLoad) { - processedOnLoad->formEditorID = "SPID_ProcessedOnLoad"; + if (processedOutfit = factory->Create(); processedOutfit) { + processedOutfit->formEditorID = "SPID_ProcessedOutfit"; } } @@ -195,7 +185,7 @@ namespace Distribute if (const auto& actor = actorHandle.get()) { if (const auto npc = actor->GetActorBase(); npc && detail::should_process_NPC(npc)) { auto npcData = NPCData(actor.get(), npc); - Distribute(npcData, false, true); + Distribute(npcData, false); ++actorCount; } } @@ -215,7 +205,7 @@ namespace Distribute logger::info("{:*^50}", "RESULTS"); ForEachDistributable([&](Distributables
& a_distributable) { - if (a_distributable && a_distributable.GetType() != RECORD::kItem && a_distributable.GetType() != RECORD::kOutfit && a_distributable.GetType() != RECORD::kDeathItem) { + if (a_distributable && a_distributable.GetType() != RECORD::kDeathItem) { logger::info("{}", RECORD::add[a_distributable.GetType()]); auto& forms = a_distributable.GetForms(); From 1d96c85017fbbeb84258129f6fa9e3ca0cc1c954 Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Thu, 14 Mar 2024 11:05:32 +0530 Subject: [PATCH 27/28] scrap outfit auto equip --- SPID/include/DistributeManager.h | 4 -- SPID/src/DistributeManager.cpp | 76 -------------------------------- 2 files changed, 80 deletions(-) diff --git a/SPID/include/DistributeManager.h b/SPID/include/DistributeManager.h index 83cd48f..29ae9ba 100644 --- a/SPID/include/DistributeManager.h +++ b/SPID/include/DistributeManager.h @@ -8,10 +8,6 @@ namespace Distribute namespace detail { bool should_process_NPC(RE::TESNPC* a_npc, RE::BGSKeyword* a_keyword = processed); - - std::set get_equipped_item_slots(RE::Actor* a_actor); - void force_equip_outfit(RE::Actor* a_actor, const RE::TESNPC* a_npc, const std::set& a_slots); - void distribute_on_load(RE::Actor* a_actor, RE::TESNPC* a_npc); } diff --git a/SPID/src/DistributeManager.cpp b/SPID/src/DistributeManager.cpp index d6eb3d0..33d2d13 100644 --- a/SPID/src/DistributeManager.cpp +++ b/SPID/src/DistributeManager.cpp @@ -15,88 +15,12 @@ namespace Distribute return true; } - std::set detail::get_equipped_item_slots(RE::Actor* a_actor) - { - if (!a_actor) { - return {}; - } - - std::set slots; - - if (const auto invChanges = a_actor->GetInventoryChanges()) { - if (const auto entryLists = invChanges->entryList) { - for (const auto& entryList : *entryLists) { - if (entryList && entryList->object && entryList->object->IsArmor() && entryList->IsWorn()) { - slots.insert(entryList->object->As()->GetSlotMask()); - } - } - } - } - - return slots; - } - - void detail::force_equip_outfit(RE::Actor* a_actor, const RE::TESNPC* a_npc, const std::set& a_slots) - { - if (!a_npc->defaultOutfit) { - return; - } - - if (const auto invChanges = a_actor->GetInventoryChanges()) { - invChanges->InitOutfitItems(a_npc->defaultOutfit, a_npc->GetLevel()); - } - - std::vector armorToRemove; - std::vector armorToEquip; - - if (const auto invChanges = a_actor->GetInventoryChanges()) { - if (const auto entryLists = invChanges->entryList) { - const auto formID = a_npc->defaultOutfit->GetFormID(); - - bool startsDead = a_actor->IsDead() || (a_actor->formFlags & RE::Actor::RecordFlags::kStartsDead) != 0; - - for (const auto& entryList : *entryLists) { - if (entryList && entryList->object && entryList->extraLists) { - for (const auto& xList : *entryList->extraLists) { - const auto outfitItem = xList ? xList->GetByType() : nullptr; - if (outfitItem && outfitItem->id == formID) { - if (auto armor = entryList->object->As(); armor && startsDead) { - if (a_slots.empty() || std::ranges::none_of(a_slots, [&](auto slot) { return armor->bipedModelData.bipedObjectSlots.any(slot); })) { - armorToRemove.push_back(armor); - continue; - } - } - armorToEquip.push_back(entryList->object); - } - } - } - } - } - } - - SKSE::GetTaskInterface()->AddTask([a_actor, armorToRemove, armorToEquip]() { - for (auto& armor : armorToRemove) { - if (armor) { - a_actor->RemoveItem(armor, 1, RE::ITEM_REMOVE_REASON::kRemove, nullptr, nullptr); - } - } - for (auto& armor : armorToEquip) { - RE::ActorEquipManager::GetSingleton()->EquipObject(a_actor, armor, nullptr, 1, nullptr, true, true, false, false); - } - }); - } - void detail::distribute_on_load(RE::Actor* a_actor, RE::TESNPC* a_npc) { - auto slots = detail::get_equipped_item_slots(a_actor); - a_actor->RemoveOutfitItems(nullptr); - if (should_process_NPC(a_npc)) { auto npcData = NPCData(a_actor, a_npc); Distribute(npcData, false); } - - detail::force_equip_outfit(a_actor, a_npc, slots); } namespace Actor From f7fffe26c3c90554650baba160189162a1126aec Mon Sep 17 00:00:00 2001 From: powerof3 <32599957+powerof3@users.noreply.github.com> Date: Thu, 14 Mar 2024 11:33:39 +0530 Subject: [PATCH 28/28] manually calc leveled items --- SPID/include/Distribute.h | 24 +++++++++++++++--------- SPID/src/Distribute.cpp | 2 +- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/SPID/include/Distribute.h b/SPID/include/Distribute.h index a9b0751..f88545f 100644 --- a/SPID/include/Distribute.h +++ b/SPID/include/Distribute.h @@ -128,10 +128,10 @@ namespace Distribute // items template void for_each_form( - const NPCData& a_npcData, - Forms::Distributables& a_distributables, - const PCLevelMult::Input& a_input, - std::function&, bool)> a_callback) + const NPCData& a_npcData, + Forms::Distributables& a_distributables, + const PCLevelMult::Input& a_input, + std::function&)> a_callback) { auto& vec = a_distributables.GetForms(a_input.onlyPlayerLevelEntries); @@ -140,20 +140,26 @@ namespace Distribute } std::map collectedForms{}; - bool hasLeveledItems = false; for (auto& formData : vec) { if (detail::passed_filters(a_npcData, a_input, formData)) { - if (formData.form->Is(RE::FormType::LeveledItem)) { - hasLeveledItems = true; + if (auto leveledItem = formData.form->As()) { + auto level = a_npcData.GetLevel(); + RE::BSScrapArray calcedObjects{}; + + leveledItem->CalculateCurrentFormList(level, formData.idxOrCount, calcedObjects, 0, true); + for (auto& calcObj : calcedObjects) { + collectedForms[static_cast(calcObj.form)] += calcObj.count; + } + } else { + collectedForms[formData.form] += formData.idxOrCount; } - collectedForms[formData.form] += formData.idxOrCount; ++formData.npcCount; } } if (!collectedForms.empty()) { - a_callback(collectedForms, hasLeveledItems); + a_callback(collectedForms); } } diff --git a/SPID/src/Distribute.cpp b/SPID/src/Distribute.cpp index c55a951..2f3d4de 100644 --- a/SPID/src/Distribute.cpp +++ b/SPID/src/Distribute.cpp @@ -123,7 +123,7 @@ namespace Distribute return false; }); - for_each_form(a_npcData, Forms::items, a_input, [&](std::map& a_objects, const bool a_hasLvlItem) { + for_each_form(a_npcData, Forms::items, a_input, [&](std::map& a_objects) { return npc->AddObjectsToContainer(a_objects, npc); });