From 1bf640da70ad91e482d557840b7e9fe868b27960 Mon Sep 17 00:00:00 2001 From: Arkadii Hlushchevskyi Date: Thu, 14 Mar 2024 01:57:14 +0200 Subject: [PATCH 1/5] Added support for random count value. Count can now be specified in form of range, e.g. 5/20 to produce anywhere between 5 and 20 items. --- SPID/include/Defs.h | 15 ++++++++++++++- SPID/include/Distribute.h | 21 ++++++++++++++------- SPID/include/FormData.h | 11 ++++++----- SPID/include/LookupConfigs.h | 3 ++- SPID/src/Distribute.cpp | 9 ++++++--- SPID/src/DistributeManager.cpp | 10 ++++++++-- SPID/src/LookupConfigs.cpp | 25 +++++++++++++++++++------ 7 files changed, 69 insertions(+), 25 deletions(-) diff --git a/SPID/include/Defs.h b/SPID/include/Defs.h index 7691604..29db1ac 100644 --- a/SPID/include/Defs.h +++ b/SPID/include/Defs.h @@ -54,6 +54,16 @@ struct Range return value >= min && value <= max; } + [[nodiscard]] bool IsExact() const + { + return min == max; + } + + [[nodiscard]] T GetRandom() const + { + return IsExact() ? min : RNG().generate(min, max); + } + // members T min{ std::numeric_limits::min() }; T max{ std::numeric_limits::max() }; @@ -83,7 +93,10 @@ struct Traits std::optional teammate{}; }; -using IdxOrCount = std::int32_t; +using Index = std::int32_t; +using Count = std::int32_t; +using RandomCount = Range; +using IndexOrCount = std::variant; using Chance = std::uint32_t; /// A standardized way of converting any object to string. diff --git a/SPID/include/Distribute.h b/SPID/include/Distribute.h index 787931b..9499025 100644 --- a/SPID/include/Distribute.h +++ b/SPID/include/Distribute.h @@ -80,14 +80,21 @@ namespace Distribute const NPCData& a_npcData, Forms::Distributables
& a_distributables, const PCLevelMult::Input& a_input, - std::function a_callback) + std::function a_callback) { auto& vec = a_distributables.GetForms(a_input.onlyPlayerLevelEntries); for (auto& formData : vec) { if (!a_npcData.HasMutuallyExclusiveForm(formData.form) && detail::passed_filters(a_npcData, a_input, formData)) { - a_callback(formData.form, formData.idxOrCount); - ++formData.npcCount; + if constexpr (std::is_same_v) { + if (a_callback(formData.form, formData.count.GetRandom())) { + ++formData.npcCount; + } + } else { + if (a_callback(formData.form, formData.packageIndex)) { + ++formData.npcCount; + } + } } } } @@ -134,7 +141,7 @@ namespace Distribute const NPCData& a_npcData, Forms::Distributables& a_distributables, const PCLevelMult::Input& a_input, - std::function&, bool)> a_callback) + std::function&, bool)> a_callback) { auto& vec = a_distributables.GetForms(a_input.onlyPlayerLevelEntries); @@ -142,15 +149,15 @@ namespace Distribute return; } - std::map collectedForms{}; - bool hasLeveledItems = false; + std::map collectedForms{}; + bool hasLeveledItems = false; for (auto& formData : vec) { if (!a_npcData.HasMutuallyExclusiveForm(formData.form) && detail::passed_filters(a_npcData, a_input, formData)) { if (formData.form->Is(RE::FormType::LeveledItem)) { hasLeveledItems = true; } - collectedForms.emplace(formData.form, formData.idxOrCount); + collectedForms.emplace(formData.form, formData.count.GetRandom()); ++formData.npcCount; } } diff --git a/SPID/include/FormData.h b/SPID/include/FormData.h index 093acb9..8c112aa 100644 --- a/SPID/include/FormData.h +++ b/SPID/include/FormData.h @@ -307,9 +307,10 @@ namespace Forms { std::uint32_t index{ 0 }; - Form* form{ nullptr }; - IdxOrCount idxOrCount{ 1 }; - FilterData filters{}; + Form* form{ nullptr }; + Index packageIndex{ 0 }; + RandomCount count{ 1, 1 }; + FilterData filters{}; std::string path{}; std::uint32_t npcCount{ 0 }; @@ -442,7 +443,7 @@ void Forms::Distributables::LookupForms(RE::TESDataHandler* a_dataHandler, forms.reserve(a_INIDataVec.size()); std::uint32_t index = 0; - for (auto& [formOrEditorID, strings, filterIDs, level, traits, idxOrCount, chance, path] : a_INIDataVec) { + for (auto& [formOrEditorID, strings, filterIDs, level, traits, packageIndex, itemsCount, chance, path] : a_INIDataVec) { try { if (auto form = detail::get_form(a_dataHandler, formOrEditorID, path); form) { FormFilters filterForms{}; @@ -456,7 +457,7 @@ void Forms::Distributables::LookupForms(RE::TESDataHandler* a_dataHandler, } if (validEntry) { - forms.emplace_back(index, form, idxOrCount, FilterData(strings, filterForms, level, traits, chance), path); + forms.emplace_back(index, form, packageIndex, itemsCount, FilterData(strings, filterForms, level, traits, chance), path); index++; } } diff --git a/SPID/include/LookupConfigs.h b/SPID/include/LookupConfigs.h index 063c2ed..a650760 100644 --- a/SPID/include/LookupConfigs.h +++ b/SPID/include/LookupConfigs.h @@ -46,7 +46,8 @@ namespace INI Filters rawFormFilters{}; LevelFilters levelFilters{}; Traits traits{}; - IdxOrCount idxOrCount{ 1 }; + Index index{ 0 }; + RandomCount count{ 1, 1 }; Chance chance{ 100 }; std::string path{}; }; diff --git a/SPID/src/Distribute.cpp b/SPID/src/Distribute.cpp index 2e40cee..9e1d400 100644 --- a/SPID/src/Distribute.cpp +++ b/SPID/src/Distribute.cpp @@ -89,8 +89,11 @@ namespace Distribute npc->GetSpellList()->AddShouts(a_shouts); }); - for_each_form(a_npcData, Forms::packages, a_input, [&](auto* a_packageOrList, [[maybe_unused]] IdxOrCount a_idx) { - auto packageIdx = a_idx; + for_each_form(a_npcData, Forms::packages, a_input, [&](auto* a_packageOrList, [[maybe_unused]] IndexOrCount a_idx) { + if (!std::holds_alternative(a_idx)) { + return false; + } + auto packageIdx = std::get(a_idx); if (a_packageOrList->Is(RE::FormType::Package)) { auto package = a_packageOrList->As(); @@ -172,7 +175,7 @@ namespace Distribute const auto npc = a_npcData.GetNPC(); const auto actor = a_npcData.GetActor(); - 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, const bool a_hasLvlItem) { if (npc->AddObjectsToContainer(a_objects, npc)) { if (a_hasLvlItem) { detail::init_leveled_items(actor); diff --git a/SPID/src/DistributeManager.cpp b/SPID/src/DistributeManager.cpp index be7e6a3..1381c3b 100644 --- a/SPID/src/DistributeManager.cpp +++ b/SPID/src/DistributeManager.cpp @@ -222,8 +222,14 @@ namespace Distribute::Event const auto npcData = NPCData(actor, npc); const auto input = PCLevelMult::Input{ actor, npc, false }; - for_each_form(npcData, Forms::deathItems, input, [&](auto* a_deathItem, IdxOrCount a_count) { - detail::add_item(actor, a_deathItem, a_count); + for_each_form(npcData, Forms::deathItems, input, [&](auto* deathItem, IndexOrCount idxOrCount) { + if (!std::holds_alternative(idxOrCount)) { + return false; + } + + auto count = std::get(idxOrCount); + + detail::add_item(actor, deathItem, count.GetRandom()); return true; }); } diff --git a/SPID/src/LookupConfigs.cpp b/SPID/src/LookupConfigs.cpp index 07df2e0..71c14a2 100644 --- a/SPID/src/LookupConfigs.cpp +++ b/SPID/src/LookupConfigs.cpp @@ -246,15 +246,28 @@ namespace INI } //ITEMCOUNT/INDEX - if (a_key == "Package") { // reuse item count for package stack index - data.idxOrCount = 0; - } + if (kIdxOrCount < size) { - if (const auto& str = sections[kIdxOrCount]; distribution::is_valid_entry(str)) { - data.idxOrCount = string::to_num(str); + if (a_key == "Package") { // reuse item count for package stack index + if (const auto& str = sections[kIdxOrCount]; distribution::is_valid_entry(str)) { + data.index = string::to_num(str); + } + } else { + if (const auto& str = sections[kIdxOrCount]; distribution::is_valid_entry(str)) { + if (auto countPair = string::split(str, "/"); countPair.size() > 1) { + auto minCount = string::to_num(countPair[0]); + auto maxCount = string::to_num(countPair[1]); + + data.count = RandomCount(minCount, maxCount); + } else { + auto count = string::to_num(str); + + data.count = RandomCount(count, count); // create the exact match range. + } + } } } - + //CHANCE if (kChance < size) { if (const auto& str = sections[kChance]; distribution::is_valid_entry(str)) { From 1b99281bc723c7a55c914fd7ce6baacb4fe31f57 Mon Sep 17 00:00:00 2001 From: adya Date: Thu, 14 Mar 2024 00:10:39 +0000 Subject: [PATCH 2/5] maintenance --- SPID/include/Defs.h | 2 +- SPID/include/Distribute.h | 14 +++++++------- SPID/src/LookupConfigs.cpp | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/SPID/include/Defs.h b/SPID/include/Defs.h index 29db1ac..57c23f5 100644 --- a/SPID/include/Defs.h +++ b/SPID/include/Defs.h @@ -54,7 +54,7 @@ struct Range return value >= min && value <= max; } - [[nodiscard]] bool IsExact() const + [[nodiscard]] bool IsExact() const { return min == max; } diff --git a/SPID/include/Distribute.h b/SPID/include/Distribute.h index 9499025..059dbab 100644 --- a/SPID/include/Distribute.h +++ b/SPID/include/Distribute.h @@ -77,9 +77,9 @@ namespace Distribute // for now, only packages/death items use this template void for_each_form( - const NPCData& a_npcData, - Forms::Distributables& a_distributables, - const PCLevelMult::Input& a_input, + 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); @@ -138,9 +138,9 @@ namespace Distribute // items template void for_each_form( - const NPCData& a_npcData, - Forms::Distributables& a_distributables, - const PCLevelMult::Input& a_input, + const NPCData& a_npcData, + Forms::Distributables& a_distributables, + const PCLevelMult::Input& a_input, std::function&, bool)> a_callback) { auto& vec = a_distributables.GetForms(a_input.onlyPlayerLevelEntries); @@ -150,7 +150,7 @@ namespace Distribute } std::map collectedForms{}; - bool hasLeveledItems = false; + bool hasLeveledItems = false; for (auto& formData : vec) { if (!a_npcData.HasMutuallyExclusiveForm(formData.form) && detail::passed_filters(a_npcData, a_input, formData)) { diff --git a/SPID/src/LookupConfigs.cpp b/SPID/src/LookupConfigs.cpp index 71c14a2..ad8f5aa 100644 --- a/SPID/src/LookupConfigs.cpp +++ b/SPID/src/LookupConfigs.cpp @@ -246,7 +246,7 @@ namespace INI } //ITEMCOUNT/INDEX - + if (kIdxOrCount < size) { if (a_key == "Package") { // reuse item count for package stack index if (const auto& str = sections[kIdxOrCount]; distribution::is_valid_entry(str)) { @@ -267,7 +267,7 @@ namespace INI } } } - + //CHANCE if (kChance < size) { if (const auto& str = sections[kChance]; distribution::is_valid_entry(str)) { From f944a828a999d6aac2b618f121e36d6e21238a20 Mon Sep 17 00:00:00 2001 From: Arkadii Hlushchevskyi Date: Thu, 14 Mar 2024 02:26:00 +0200 Subject: [PATCH 3/5] Merged IndexOrCount into a single variant. --- SPID/include/Distribute.h | 13 +++---------- SPID/include/FormData.h | 23 +++++++++++++++++------ SPID/include/LookupConfigs.h | 3 +-- SPID/src/LookupConfigs.cpp | 13 ++++++++----- 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/SPID/include/Distribute.h b/SPID/include/Distribute.h index 059dbab..25a2ba9 100644 --- a/SPID/include/Distribute.h +++ b/SPID/include/Distribute.h @@ -86,15 +86,8 @@ namespace Distribute for (auto& formData : vec) { if (!a_npcData.HasMutuallyExclusiveForm(formData.form) && detail::passed_filters(a_npcData, a_input, formData)) { - if constexpr (std::is_same_v) { - if (a_callback(formData.form, formData.count.GetRandom())) { - ++formData.npcCount; - } - } else { - if (a_callback(formData.form, formData.packageIndex)) { - ++formData.npcCount; - } - } + a_callback(formData.form, formData.idxOrCount); + ++formData.npcCount; } } } @@ -157,7 +150,7 @@ namespace Distribute if (formData.form->Is(RE::FormType::LeveledItem)) { hasLeveledItems = true; } - collectedForms.emplace(formData.form, formData.count.GetRandom()); + collectedForms.emplace(formData.form, formData.GetCount().GetRandom()); ++formData.npcCount; } } diff --git a/SPID/include/FormData.h b/SPID/include/FormData.h index 8c112aa..eb3ef08 100644 --- a/SPID/include/FormData.h +++ b/SPID/include/FormData.h @@ -307,15 +307,20 @@ namespace Forms { std::uint32_t index{ 0 }; - Form* form{ nullptr }; - Index packageIndex{ 0 }; - RandomCount count{ 1, 1 }; - FilterData filters{}; + Form* form{ nullptr }; + IndexOrCount idxOrCount{ RandomCount(1, 1) }; + FilterData filters{}; std::string path{}; std::uint32_t npcCount{ 0 }; bool operator==(const Data& a_rhs) const; + + /// + /// Unsafely gets a RandomCount from idxOrCount. + /// Onlly call this method when you're sure that idxOrCount is a RandomCount. + /// + const RandomCount& GetCount() const; }; template @@ -392,6 +397,12 @@ bool Forms::Data::operator==(const Data& a_rhs) const return form->GetFormID() == a_rhs.form->GetFormID(); } +template +const RandomCount& Forms::Data::GetCount() const +{ + return std::get(idxOrCount); +} + template Forms::Distributables::operator bool() { @@ -443,7 +454,7 @@ void Forms::Distributables::LookupForms(RE::TESDataHandler* a_dataHandler, forms.reserve(a_INIDataVec.size()); std::uint32_t index = 0; - for (auto& [formOrEditorID, strings, filterIDs, level, traits, packageIndex, itemsCount, chance, path] : a_INIDataVec) { + for (auto& [formOrEditorID, strings, filterIDs, level, traits, idxOrCount, chance, path] : a_INIDataVec) { try { if (auto form = detail::get_form(a_dataHandler, formOrEditorID, path); form) { FormFilters filterForms{}; @@ -457,7 +468,7 @@ void Forms::Distributables::LookupForms(RE::TESDataHandler* a_dataHandler, } if (validEntry) { - forms.emplace_back(index, form, packageIndex, itemsCount, FilterData(strings, filterForms, level, traits, chance), path); + forms.emplace_back(index, form, idxOrCount, FilterData(strings, filterForms, level, traits, chance), path); index++; } } diff --git a/SPID/include/LookupConfigs.h b/SPID/include/LookupConfigs.h index a650760..07e9181 100644 --- a/SPID/include/LookupConfigs.h +++ b/SPID/include/LookupConfigs.h @@ -46,8 +46,7 @@ namespace INI Filters rawFormFilters{}; LevelFilters levelFilters{}; Traits traits{}; - Index index{ 0 }; - RandomCount count{ 1, 1 }; + IndexOrCount idxOrCount{ RandomCount(1, 1) }; Chance chance{ 100 }; std::string path{}; }; diff --git a/SPID/src/LookupConfigs.cpp b/SPID/src/LookupConfigs.cpp index ad8f5aa..260b89d 100644 --- a/SPID/src/LookupConfigs.cpp +++ b/SPID/src/LookupConfigs.cpp @@ -246,23 +246,26 @@ namespace INI } //ITEMCOUNT/INDEX + if (a_key == "Package") { // reuse item count for package stack index + data.idxOrCount = 0; + } if (kIdxOrCount < size) { - if (a_key == "Package") { // reuse item count for package stack index + if (a_key == "Package") { // If it's a package, then we only expect a single number. if (const auto& str = sections[kIdxOrCount]; distribution::is_valid_entry(str)) { - data.index = string::to_num(str); + data.idxOrCount = string::to_num(str); } } else { if (const auto& str = sections[kIdxOrCount]; distribution::is_valid_entry(str)) { - if (auto countPair = string::split(str, "/"); countPair.size() > 1) { + if (auto countPair = string::split(str, "-"); countPair.size() > 1) { auto minCount = string::to_num(countPair[0]); auto maxCount = string::to_num(countPair[1]); - data.count = RandomCount(minCount, maxCount); + data.idxOrCount = RandomCount(minCount, maxCount); } else { auto count = string::to_num(str); - data.count = RandomCount(count, count); // create the exact match range. + data.idxOrCount = RandomCount(count, count); // create the exact match range. } } } From e5eaac484af9ebbceefa64dcb3e7d84eae27f28a Mon Sep 17 00:00:00 2001 From: adya Date: Thu, 14 Mar 2024 00:26:19 +0000 Subject: [PATCH 4/5] maintenance --- SPID/src/LookupConfigs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SPID/src/LookupConfigs.cpp b/SPID/src/LookupConfigs.cpp index 260b89d..f3c3a9f 100644 --- a/SPID/src/LookupConfigs.cpp +++ b/SPID/src/LookupConfigs.cpp @@ -251,7 +251,7 @@ namespace INI } if (kIdxOrCount < size) { - if (a_key == "Package") { // If it's a package, then we only expect a single number. + if (a_key == "Package") { // If it's a package, then we only expect a single number. if (const auto& str = sections[kIdxOrCount]; distribution::is_valid_entry(str)) { data.idxOrCount = string::to_num(str); } From f80555f1b5fd00f655679b815d111487fe6ebc6e Mon Sep 17 00:00:00 2001 From: Arkadii Hlushchevskyi Date: Thu, 14 Mar 2024 02:31:18 +0200 Subject: [PATCH 5/5] =?UTF-8?q?Removed=20unnecessary=20safety=20checks=20t?= =?UTF-8?q?o=20keep=20maximum=20performance=20=F0=9F=98=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SPID/include/Distribute.h | 2 +- SPID/include/FormData.h | 12 ------------ SPID/src/Distribute.cpp | 3 --- SPID/src/DistributeManager.cpp | 4 ---- 4 files changed, 1 insertion(+), 20 deletions(-) diff --git a/SPID/include/Distribute.h b/SPID/include/Distribute.h index 25a2ba9..f0fd2f2 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.GetCount().GetRandom()); + collectedForms.emplace(formData.form, std::get(formData.idxOrCount).GetRandom()); ++formData.npcCount; } } diff --git a/SPID/include/FormData.h b/SPID/include/FormData.h index eb3ef08..9c165b0 100644 --- a/SPID/include/FormData.h +++ b/SPID/include/FormData.h @@ -315,12 +315,6 @@ namespace Forms std::uint32_t npcCount{ 0 }; bool operator==(const Data& a_rhs) const; - - /// - /// Unsafely gets a RandomCount from idxOrCount. - /// Onlly call this method when you're sure that idxOrCount is a RandomCount. - /// - const RandomCount& GetCount() const; }; template @@ -397,12 +391,6 @@ bool Forms::Data::operator==(const Data& a_rhs) const return form->GetFormID() == a_rhs.form->GetFormID(); } -template -const RandomCount& Forms::Data::GetCount() const -{ - return std::get(idxOrCount); -} - template Forms::Distributables::operator bool() { diff --git a/SPID/src/Distribute.cpp b/SPID/src/Distribute.cpp index 9e1d400..0f2a559 100644 --- a/SPID/src/Distribute.cpp +++ b/SPID/src/Distribute.cpp @@ -90,9 +90,6 @@ namespace Distribute }); for_each_form(a_npcData, Forms::packages, a_input, [&](auto* a_packageOrList, [[maybe_unused]] IndexOrCount a_idx) { - if (!std::holds_alternative(a_idx)) { - return false; - } auto packageIdx = std::get(a_idx); if (a_packageOrList->Is(RE::FormType::Package)) { diff --git a/SPID/src/DistributeManager.cpp b/SPID/src/DistributeManager.cpp index 1381c3b..63b8414 100644 --- a/SPID/src/DistributeManager.cpp +++ b/SPID/src/DistributeManager.cpp @@ -223,10 +223,6 @@ namespace Distribute::Event const auto input = PCLevelMult::Input{ actor, npc, false }; for_each_form(npcData, Forms::deathItems, input, [&](auto* deathItem, IndexOrCount idxOrCount) { - if (!std::holds_alternative(idxOrCount)) { - return false; - } - auto count = std::get(idxOrCount); detail::add_item(actor, deathItem, count.GetRandom());