Skip to content

Commit

Permalink
Implemented Local scope for linked forms.
Browse files Browse the repository at this point in the history
  • Loading branch information
adya committed Mar 31, 2024
1 parent b2e692c commit f54db3a
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 48 deletions.
36 changes: 22 additions & 14 deletions SPID/include/Distribute.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ namespace Distribute
void add_item(RE::Actor* a_actor, RE::TESBoundObject* a_item, std::uint32_t a_itemCount);
}

using namespace Forms;

#pragma region Packages, Death Items
// old method (distributing one by one)
// for now, only packages/death items use this
Expand All @@ -79,12 +81,12 @@ namespace Distribute
Forms::DataVec<Form>& forms,
const PCLevelMult::Input& a_input,
std::function<bool(Form*, IndexOrCount)> a_callback,
std::set<RE::TESForm*>* accumulatedForms = nullptr)
DistributedForms* accumulatedForms = nullptr)
{
for (auto& formData : forms) {
if (!a_npcData.HasMutuallyExclusiveForm(formData.form) && detail::passed_filters(a_npcData, a_input, formData)) {
if (accumulatedForms) {
accumulatedForms->insert(formData.form);
accumulatedForms->insert({ formData.form, formData.path });
}
a_callback(formData.form, formData.idxOrCount);
++formData.npcCount;
Expand All @@ -100,12 +102,12 @@ namespace Distribute
Forms::DataVec<Form>& forms,
const PCLevelMult::Input& a_input,
std::function<bool(Form*)> a_callback,
std::set<RE::TESForm*>* accumulatedForms = nullptr)
DistributedForms* accumulatedForms = nullptr)
{
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);
accumulatedForms->insert({ formData.form, formData.path });
}
++formData.npcCount;
break;
Expand All @@ -121,14 +123,14 @@ namespace Distribute
const NPCData& a_npcData,
Forms::Distributables<Form>& a_distributables,
std::function<bool(Form*)> a_callback,
std::set<RE::TESForm*>* accumulatedForms = nullptr)
DistributedForms* 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);
accumulatedForms->insert({ formData.form, formData.path });
}
++formData.npcCount;
break;
Expand All @@ -144,7 +146,7 @@ namespace Distribute
Forms::DataVec<Form>& forms,
const PCLevelMult::Input& a_input,
std::function<bool(std::map<Form*, Count>&)> a_callback,
std::set<RE::TESForm*>* accumulatedForms = nullptr)
DistributedForms* accumulatedForms = nullptr)
{
std::map<Form*, Count> collectedForms{};

Expand All @@ -159,18 +161,21 @@ namespace Distribute
leveledItem->CalculateCurrentFormList(level, count, calcedObjects, 0, true);
for (auto& calcObj : calcedObjects) {
collectedForms[static_cast<RE::TESBoundObject*>(calcObj.form)] += calcObj.count;
if (accumulatedForms) {
accumulatedForms->insert({ calcObj.form, formData.path });
}
}
} else {
collectedForms[formData.form] += count;
if (accumulatedForms) {
accumulatedForms->insert({ formData.form, formData.path });
}
}
++formData.npcCount;
}
}

if (!collectedForms.empty()) {
if (accumulatedForms) {
std::ranges::copy(collectedForms | std::views::keys, std::inserter(*accumulatedForms, accumulatedForms->end()));
}
a_callback(collectedForms);
}
}
Expand All @@ -185,7 +190,7 @@ namespace Distribute
Forms::DataVec<Form>& forms,
const PCLevelMult::Input& a_input,
std::function<void(const std::vector<Form*>&)> a_callback,
std::set<RE::TESForm*>* accumulatedForms = nullptr)
DistributedForms* accumulatedForms = nullptr)
{
const auto npc = a_npcData.GetNPC();

Expand All @@ -210,6 +215,9 @@ namespace Distribute
if (formData.filters.HasLevelFilters()) {
collectedLeveledFormIDs.emplace(formID);
}
if (accumulatedForms) {
accumulatedForms->insert({ form, formData.path });
}
++formData.npcCount;
}
} else {
Expand All @@ -218,15 +226,15 @@ namespace Distribute
if (formData.filters.HasLevelFilters()) {
collectedLeveledFormIDs.emplace(formID);
}
if (accumulatedForms) {
accumulatedForms->insert({ form, formData.path });
}
++formData.npcCount;
}
}
}

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);
Expand Down
7 changes: 4 additions & 3 deletions SPID/include/FormData.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,7 @@ namespace Forms
}

form = as_form(anyForm);
if (!form || anyForm->GetFormType() != Form::FORMTYPE) {
// Ideally, we'd want to throw separate exception for unsupported form type,
// so that attempting to distribute, for example, CELL would properly report such error.
if (!form) {
throw MismatchingFormTypeException(Form::FORMTYPE, anyForm->GetFormType(), FormModPair{ *formID, modName }, path);
}

Expand Down Expand Up @@ -364,6 +362,9 @@ namespace Forms
template <class Form>
using DataVec = std::vector<Data<Form>>;

using DistributedForm = std::pair<RE::TESForm*, const Path>;
using DistributedForms = std::set<DistributedForm>;

/// <summary>
/// A set of distributable forms that should be processed.
///
Expand Down
35 changes: 19 additions & 16 deletions SPID/include/LinkedDistribution.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ namespace LinkedDistribution
friend Manager; // allow Manager to later modify forms directly.
friend Form* detail::LookupLinkedForm(RE::TESDataHandler* const, INI::RawLinkedForm&);

using FormsMap = std::unordered_map<RE::TESForm*, DataVec<Form>>;
using FormsMap = std::unordered_map<Path, std::unordered_map<RE::TESForm*, DataVec<Form>>>;

LinkedForms(RECORD::TYPE type) :
type(type)
Expand Down Expand Up @@ -108,7 +108,7 @@ namespace LinkedDistribution
/// <param name="linkedForms">A set of forms for which distribution sets should be calculated.
/// This is typically distributed forms accumulated during first distribution pass.</param>
/// <param name="callback">A callback to be called with each DistributionSet. This is supposed to do the actual distribution.</param>
void ForEachLinkedDistributionSet(const std::set<RE::TESForm*>& linkedForms, std::function<void(DistributionSet&)> callback);
void ForEachLinkedDistributionSet(const DistributedForms& linkedForms, std::function<void(DistributionSet&)> callback);

/// <summary>
/// Calculates DistributionSet with only DeathItems for each linked form and calls a callback for each of them.
Expand All @@ -117,11 +117,11 @@ namespace LinkedDistribution
/// <param name="linkedForms">A set of forms for which distribution sets should be calculated.
/// This is typically distributed forms accumulated during first distribution pass.</param>
/// <param name="callback">A callback to be called with each DistributionSet. This is supposed to do the actual distribution.</param>
void ForEachLinkedDeathDistributionSet(const std::set<RE::TESForm*>& linkedForms, std::function<void(DistributionSet&)> callback);
void ForEachLinkedDeathDistributionSet(const DistributedForms& linkedForms, std::function<void(DistributionSet&)> callback);

private:
template <class Form>
DataVec<Form>& LinkedFormsForForm(RE::TESForm* form, LinkedForms<Form>& linkedForms) const;
DataVec<Form>& LinkedFormsForForm(const DistributedForm&, LinkedForms<Form>&) const;

LinkedForms<RE::SpellItem> spells{ RECORD::kSpell };
LinkedForms<RE::BGSPerk> perks{ RECORD::kPerk };
Expand Down Expand Up @@ -193,14 +193,15 @@ namespace LinkedDistribution
}

template <class Form>
DataVec<Form>& Manager::LinkedFormsForForm(RE::TESForm* form, LinkedForms<Form>& linkedForms) const
DataVec<Form>& Manager::LinkedFormsForForm(const DistributedForm& form, LinkedForms<Form>& linkedForms) const
{
if (auto it = linkedForms.forms.find(form); it != linkedForms.forms.end()) {
return it->second;
} else {
static DataVec<Form> empty{};
return empty;
if (const auto formsIt = linkedForms.forms.find(form.second); formsIt != linkedForms.forms.end()) {
if (const auto linkedFormsIt = formsIt->second.find(form.first); linkedFormsIt != formsIt->second.end()) {
return linkedFormsIt->second;
}
}
static DataVec<Form> empty{};
return empty;
}

template <typename Func, typename... Args>
Expand All @@ -224,11 +225,12 @@ namespace LinkedDistribution
void LinkedForms<Form>::LookupForms(RE::TESDataHandler* const dataHandler, INI::LinkedFormsVec& rawLinkedForms)
{
for (auto& rawForm : rawLinkedForms) {
auto form = detail::LookupLinkedForm<Form>(dataHandler, rawForm);
auto& [formID, scope, parentFormIDs, count, chance, path] = rawForm;
FormVec parentForms{};
if (Forms::detail::formID_to_form(dataHandler, parentFormIDs.MATCH, parentForms, path, false, false)) {
Link(form, scope, parentForms, count, chance, path);
if (auto form = detail::LookupLinkedForm<Form>(dataHandler, rawForm); form) {
auto& [formID, scope, parentFormIDs, count, chance, path] = rawForm;
FormVec parentForms{};
if (Forms::detail::formID_to_form(dataHandler, parentFormIDs.MATCH, parentForms, path, false, false)) {
Link(form, scope, parentForms, count, chance, path);
}
}
}
}
Expand All @@ -239,7 +241,8 @@ namespace LinkedDistribution
// TODO: Handle scope
for (const auto& linkedForm : linkedForms) {
if (std::holds_alternative<RE::TESForm*>(linkedForm)) {
auto& distributableForms = forms[std::get<RE::TESForm*>(linkedForm)];
auto& distributableFormsAtPath = forms[path];
auto& distributableForms = distributableFormsAtPath[std::get<RE::TESForm*>(linkedForm)];
// Note that we don't use Data.index here, as these linked forms don't have any leveled filters
// and as such do not to track their index.
distributableForms.emplace_back(0, form, idxOrCount, FilterData({}, {}, {}, {}, chance), path);
Expand Down
6 changes: 3 additions & 3 deletions SPID/src/Distribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace Distribute
/// <param name="input">Leveling information about NPC that is being processed.</param>
/// <param name="forms">A set of forms that should be distributed to NPC.</param>
/// <param name="accumulatedForms">An optional pointer to a set that will accumulate all distributed forms.</param>
void distribute(NPCData& npcData, const PCLevelMult::Input& input, Forms::DistributionSet& forms, std::set<RE::TESForm*>* accumulatedForms)
void distribute(NPCData& npcData, const PCLevelMult::Input& input, Forms::DistributionSet& forms, DistributedForms* accumulatedForms)
{
const auto npc = npcData.GetNPC();

Expand Down Expand Up @@ -191,7 +191,7 @@ namespace Distribute
Forms::skins.GetForms(input.onlyPlayerLevelEntries)
};

std::set<RE::TESForm*> distributedForms{};
DistributedForms distributedForms{};

detail::distribute(npcData, input, entries, &distributedForms);
// TODO: We can now log per-NPC distributed forms.
Expand All @@ -212,7 +212,7 @@ namespace Distribute

void DistributeDeathItems(NPCData& npcData, const PCLevelMult::Input& input)
{
std::set<RE::TESForm*> distributedForms{};
DistributedForms distributedForms{};

Forms::DistributionSet entries{
Forms::DistributionSet::empty<RE::SpellItem>(),
Expand Down
28 changes: 16 additions & 12 deletions SPID/src/LinkedDistribution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,15 +182,19 @@ namespace LinkedDistribution
return;
}

std::unordered_map<RE::TESForm*, std::vector<RE::TESForm*>> map{};
std::unordered_map<RE::TESForm*, std::vector<DistributedForm>> map{};

// Iterate through the original map
for (const auto& pair : linkedForms.GetForms()) {
const auto key = pair.first;
const DataVec<Form>& values = pair.second;

for (const auto& value : values) {
map[value.form].push_back(key);
const auto& path = pair.first;
const auto& formsMap = pair.second;

for (const auto& pair : formsMap) {
const auto key = pair.first;
const auto& values = pair.second;
for (const auto& value : values) {
map[value.form].emplace_back(key, path);
}
}
}

Expand All @@ -203,17 +207,17 @@ namespace LinkedDistribution
const auto lastItemIndex = linkedForms.size() - 1;
for (int i = 0; i < lastItemIndex; ++i) {
const auto& linkedItem = linkedForms[i];
logger::info("\t├─── {}", describe(linkedItem));
logger::info("\t├─── {} @{}", describe(linkedItem.first), linkedItem.second);
}
const auto& lastLinkedItem = linkedForms[lastItemIndex];
logger::info("\t└─── {}", describe(lastLinkedItem));
logger::info("\t└─── {} @{}", describe(lastLinkedItem.first), lastLinkedItem.second);
}
});
}

void Manager::ForEachLinkedDistributionSet(const std::set<RE::TESForm*>& targetForms, std::function<void(DistributionSet&)> performDistribution)
void Manager::ForEachLinkedDistributionSet(const DistributedForms& targetForms, std::function<void(DistributionSet&)> performDistribution)
{
for (const auto form : targetForms) {
for (const auto& form : targetForms) {
auto& linkedSpells = LinkedFormsForForm(form, spells);
auto& linkedPerks = LinkedFormsForForm(form, perks);
auto& linkedItems = LinkedFormsForForm(form, items);
Expand Down Expand Up @@ -249,9 +253,9 @@ namespace LinkedDistribution
}
}

void Manager::ForEachLinkedDeathDistributionSet(const std::set<RE::TESForm*>& targetForms, std::function<void(DistributionSet&)> performDistribution)
void Manager::ForEachLinkedDeathDistributionSet(const DistributedForms& targetForms, std::function<void(DistributionSet&)> performDistribution)
{
for (const auto form : targetForms) {
for (const auto& form : targetForms) {
auto& linkedDeathItems = LinkedFormsForForm(form, deathItems);

DistributionSet linkedEntries{
Expand Down

0 comments on commit f54db3a

Please sign in to comment.