Skip to content

Commit

Permalink
WIP: Refactored distribution code to reuse in linked distribution.
Browse files Browse the repository at this point in the history
Key change is abstracted out distribution of a given set of entries and to not rely on global Distributables.
  • Loading branch information
adya committed Mar 18, 2024
1 parent 7a91d0b commit 8bb55e5
Show file tree
Hide file tree
Showing 4 changed files with 231 additions and 175 deletions.
131 changes: 49 additions & 82 deletions SPID/include/Distribute.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,79 +73,86 @@ namespace Distribute
bool can_equip_outfit(const RE::TESNPC* a_npc, RE::BGSOutfit* a_outfit);
}

#pragma region Packages, Death Items
// old method (distributing one by one)
// for now, only packages/death items use this
template <class Form>
void for_each_form(
const NPCData& a_npcData,
Forms::Distributables<Form>& a_distributables,
Forms::DataVec<Form>& forms,
const PCLevelMult::Input& a_input,
std::function<bool(Form*, IndexOrCount)> a_callback)
std::function<bool(Form*, IndexOrCount)> a_callback,
std::set<RE::TESForm*>* accumulatedForms = nullptr)
{
auto& vec = a_distributables.GetForms(a_input.onlyPlayerLevelEntries);

for (auto& formData : vec) {
for (auto& formData : forms) {
if (!a_npcData.HasMutuallyExclusiveForm(formData.form) && detail::passed_filters(a_npcData, a_input, formData)) {
if (accumulatedForms) {
accumulatedForms->insert(formData.form);
}
a_callback(formData.form, formData.idxOrCount);
++formData.npcCount;
}
}
}
#pragma endregion

// outfits/sleep outfits
// skins
#pragma region Outfits, Sleep Outfits, Skins
template <class Form>
void for_each_form(
const NPCData& a_npcData,
Forms::Distributables<Form>& a_distributables,
const PCLevelMult::Input& a_input,
std::function<bool(Form*)> a_callback)
const NPCData& a_npcData,
Forms::DataVec<Form>& forms,
const PCLevelMult::Input& a_input,
std::function<bool(Form*)> a_callback,
std::set<RE::TESForm*>* accumulatedForms = nullptr)
{
auto& vec = a_distributables.GetForms(a_input.onlyPlayerLevelEntries);

for (auto& formData : vec) { // Vector is reversed in FinishLookupForms
for (auto& formData : forms) { // Vector is reversed in FinishLookupForms
if (!a_npcData.HasMutuallyExclusiveForm(formData.form) && detail::passed_filters(a_npcData, a_input, formData) && a_callback(formData.form)) {
if (accumulatedForms) {
accumulatedForms->insert(formData.form);
}
++formData.npcCount;
break;
}
}
}
#pragma endregion

// TODO: Is this unused?
// outfits/sleep outfits
template <class Form>
void for_each_form(
const NPCData& a_npcData,
Forms::Distributables<Form>& a_distributables,
std::function<bool(Form*)> a_callback)
std::function<bool(Form*)> a_callback,
std::set<RE::TESForm*>* accumulatedForms = nullptr)
{
auto& vec = a_distributables.GetForms(false);

for (auto& formData : vec) { // Vector is reversed in FinishLookupForms
if (!a_npcData.HasMutuallyExclusiveForm(formData.form) && detail::passed_filters(a_npcData, formData) && a_callback(formData.form)) {
if (accumulatedForms) {
accumulatedForms->insert(formData.form);
}
++formData.npcCount;
break;
}
}
}

// items
#pragma region Items
// countable items
template <class Form>
void for_each_form(
const NPCData& a_npcData,
Forms::Distributables<Form>& a_distributables,
Forms::DataVec<Form>& forms,
const PCLevelMult::Input& a_input,
std::function<bool(std::map<Form*, Count>&, bool)> a_callback)
std::function<bool(std::map<Form*, Count>&, bool)> a_callback,
std::set<RE::TESForm*>* accumulatedForms = nullptr)
{
auto& vec = a_distributables.GetForms(a_input.onlyPlayerLevelEntries);

if (vec.empty()) {
return;
}

std::map<Form*, Count> collectedForms{};
bool hasLeveledItems = false;

for (auto& formData : vec) {
for (auto& formData : forms) {
if (!a_npcData.HasMutuallyExclusiveForm(formData.form) && detail::passed_filters(a_npcData, a_input, formData)) {
if (formData.form->Is(RE::FormType::LeveledItem)) {
hasLeveledItems = true;
Expand All @@ -156,36 +163,36 @@ namespace Distribute
}

if (!collectedForms.empty()) {
if (accumulatedForms) {
std::ranges::copy(collectedForms | std::views::keys, std::inserter(*accumulatedForms, accumulatedForms->end()));
}
a_callback(collectedForms, hasLeveledItems);
}
}
#pragma endregion

#pragma region Spells, Perks, Shouts, Keywords
// spells, perks, shouts, keywords
// forms that can be added to
template <class Form>
void for_each_form(
NPCData& a_npcData,
Forms::Distributables<Form>& a_distributables,
Forms::DataVec<Form>& forms,
const PCLevelMult::Input& a_input,
std::function<void(const std::vector<Form*>&)> a_callback)
std::function<void(const std::vector<Form*>&)> a_callback,
std::set<RE::TESForm*>* accumulatedForms = nullptr)
{
auto& vec = a_distributables.GetForms(a_input.onlyPlayerLevelEntries);

if (vec.empty()) {
return;
}

const auto npc = a_npcData.GetNPC();

std::vector<Form*> collectedForms{};
Set<RE::FormID> collectedFormIDs{};
Set<RE::FormID> collectedLeveledFormIDs{};

collectedForms.reserve(vec.size());
collectedFormIDs.reserve(vec.size());
collectedLeveledFormIDs.reserve(vec.size());
collectedForms.reserve(forms.size());
collectedFormIDs.reserve(forms.size());
collectedLeveledFormIDs.reserve(forms.size());

for (auto& formData : vec) {
for (auto& formData : forms) {
auto form = formData.form;
auto formID = form->GetFormID();
if (collectedFormIDs.contains(formID)) {
Expand All @@ -212,60 +219,20 @@ namespace Distribute
}

if (!collectedForms.empty()) {
if (accumulatedForms) {
accumulatedForms->insert(collectedForms.begin(), collectedForms.end());
}
a_callback(collectedForms);
if (!collectedLeveledFormIDs.empty()) {
PCLevelMult::Manager::GetSingleton()->InsertDistributedEntry(a_input, Form::FORMTYPE, collectedLeveledFormIDs);
}
}
}

template <class Form>
void for_each_form(
NPCData& a_npcData,
Forms::Distributables<Form>& a_distributables,
std::function<void(const std::vector<Form*>&)> a_callback)
{
const auto& vec = a_distributables.GetForms(false);

if (vec.empty()) {
return;
}

const auto npc = a_npcData.GetNPC();

std::vector<Form*> collectedForms{};
Set<RE::FormID> collectedFormIDs{};

collectedForms.reserve(vec.size());
collectedFormIDs.reserve(vec.size());

for (auto& formData : vec) {
auto form = formData.form;
auto formID = form->GetFormID();
if (collectedFormIDs.contains(formID)) {
continue;
}
if constexpr (std::is_same_v<RE::BGSKeyword, Form>) {
if (!a_npcData.HasMutuallyExclusiveForm(form) && detail::passed_filters(a_npcData, formData) && a_npcData.InsertKeyword(form->GetFormEditorID())) {
collectedForms.emplace_back(form);
collectedFormIDs.emplace(formID);
++formData.npcCount;
}
} else {
if (!a_npcData.HasMutuallyExclusiveForm(form) && detail::passed_filters(a_npcData, formData) && !detail::has_form(npc, form) && collectedFormIDs.emplace(formID).second) {
collectedForms.emplace_back(form);
++formData.npcCount;
}
}
}

if (!collectedForms.empty()) {
a_callback(collectedForms);
}
}
#pragma endregion

void Distribute(NPCData& a_npcData, const PCLevelMult::Input& a_input);
void DistributeItemOutfits(NPCData& a_npcData, const PCLevelMult::Input& a_input);

void Distribute(NPCData& a_npcData, bool a_onlyLeveledEntries, bool a_noItemOutfits = false);

}
28 changes: 28 additions & 0 deletions SPID/include/FormData.h
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,34 @@ namespace Forms
template <class Form>
using DataVec = std::vector<Data<Form>>;


/// <summary>
/// A set of distributable forms that should be processed.
///
/// DistributionSet is used to conveniently pack all distributable forms into one structure.
/// </summary>
struct DistributionSet
{
DataVec<RE::SpellItem> spells{};
DataVec<RE::BGSPerk> perks{};
DataVec<RE::TESBoundObject> items{};
DataVec<RE::TESShout> shouts{};
DataVec<RE::TESLevSpell> levSpells{};
DataVec<RE::TESForm> packages{};
DataVec<RE::BGSOutfit> outfits{};
DataVec<RE::BGSKeyword> keywords{};
DataVec<RE::TESBoundObject> deathItems{};
DataVec<RE::TESFaction> factions{};
DataVec<RE::BGSOutfit> sleepOutfits{};
DataVec<RE::TESObjectARMO> skins{};
};

/// <summary>
/// A container that holds distributable entries for a single form type.
///
/// Note that this container tracks separately leveled (those using level in their filters) entries.
/// </summary>
/// <typeparam name="Form">Type of the forms to store.</typeparam>
template <class Form>
struct Distributables
{
Expand Down
Loading

0 comments on commit 8bb55e5

Please sign in to comment.