Skip to content

Commit

Permalink
Implemented Linked Distribution parsing.
Browse files Browse the repository at this point in the history
  • Loading branch information
adya committed Mar 24, 2024
1 parent 827b34b commit 1926498
Show file tree
Hide file tree
Showing 7 changed files with 236 additions and 84 deletions.
2 changes: 1 addition & 1 deletion SPID/include/Distribute.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace Distribute
auto result = a_formData.filters.PassedFilters(a_npcData);

if (result != Filter::Result::kPass) {
if (result == Filter::Result::kFailRNG && hasLevelFilters) {
if (hasLevelFilters && result == Filter::Result::kFailRNG) {
pcLevelMultManager->InsertRejectedEntry(a_input, distributedFormID, index);
}
return false;
Expand Down
8 changes: 4 additions & 4 deletions SPID/include/FormData.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ namespace Forms
}

template <class Form = RE::TESForm>
std::variant<Form*, const RE::TESFile*> get_form_or_mod(RE::TESDataHandler* dataHandler, const FormOrEditorID& formOrEditorID, const std::string& path, bool whitelistedOnly = false)
std::variant<Form*, const RE::TESFile*> get_form_or_mod(RE::TESDataHandler* const dataHandler, const FormOrEditorID& formOrEditorID, const std::string& path, bool whitelistedOnly = false)
{
Form* form = nullptr;
const RE::TESFile* mod = nullptr;
Expand Down Expand Up @@ -235,7 +235,7 @@ namespace Forms
return form;
}

inline const RE::TESFile* get_file(RE::TESDataHandler* dataHandler, const FormOrEditorID& formOrEditorID, const std::string& path)
inline const RE::TESFile* get_file(RE::TESDataHandler* const dataHandler, const FormOrEditorID& formOrEditorID, const std::string& path)
{
auto formOrMod = get_form_or_mod(dataHandler, formOrEditorID, path);

Expand All @@ -247,7 +247,7 @@ namespace Forms
}

template <class Form = RE::TESForm>
Form* get_form(RE::TESDataHandler* dataHandler, const FormOrEditorID& formOrEditorID, const std::string& path, bool whitelistedOnly = false)
Form* get_form(RE::TESDataHandler* const dataHandler, const FormOrEditorID& formOrEditorID, const std::string& path, bool whitelistedOnly = false)
{
auto formOrMod = get_form_or_mod<Form>(dataHandler, formOrEditorID, path, whitelistedOnly);

Expand All @@ -258,7 +258,7 @@ namespace Forms
return nullptr;
}

inline bool formID_to_form(RE::TESDataHandler* a_dataHandler, RawFormVec& a_rawFormVec, FormVec& a_formVec, const std::string& a_path, bool a_all = false, bool whitelistedOnly = true)
inline bool formID_to_form(RE::TESDataHandler* const a_dataHandler, RawFormVec& a_rawFormVec, FormVec& a_formVec, const std::string& a_path, bool a_all = false, bool whitelistedOnly = true)
{
if (a_rawFormVec.empty()) {
return true;
Expand Down
94 changes: 72 additions & 22 deletions SPID/include/LinkedDistribution.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ namespace LinkedDistribution
{
namespace INI
{

struct RawLinkedItem
{
FormOrEditorID rawForm{};
Expand All @@ -25,20 +24,36 @@ namespace LinkedDistribution

inline LinkedItemsVec linkedItems{};

namespace Parser
{
bool TryParse(const std::string& a_key, const std::string& a_value, const std::string& a_path);
}
bool TryParse(const std::string& a_key, const std::string& a_value, const std::string& a_path);
}

using namespace Forms;

template<class T>
using LinkedForms = std::unordered_map<RE::TESForm*, DataVec<T>>;
class Manager;

class Manager : public ISingleton<Manager>
template <class Form>
struct LinkedForms
{
friend Manager; // allow Manager to later modify forms directly.

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

LinkedForms(RECORD::TYPE type) :
type(type)
{}

RECORD::TYPE GetType() const { return type; }
const Map& GetForms() const { return forms; }

private:
RECORD::TYPE type;
Map forms{};

void Link(Form* form, const FormVec& linkedForms, const RandomCount& count, const Chance& chance, const std::string& path);
};

class Manager : public ISingleton<Manager>
{
public:
/// <summary>
/// Does a forms lookup similar to what Filters do.
Expand All @@ -47,8 +62,7 @@ namespace LinkedDistribution
/// </summary>
/// <param name="dataHandler">A DataHandler that will perform the actual lookup.</param>
/// <param name="rawLinkedDistribution">A raw linked item entries that should be processed.</param>
void LookupLinkedItems(RE::TESDataHandler* const dataHandler, INI::LinkedItemsVec& rawLinkedItems);

void LookupLinkedItems(RE::TESDataHandler* const dataHandler, INI::LinkedItemsVec& rawLinkedItems = INI::linkedItems);

/// <summary>
/// Calculates DistributionSet for each linked form and calls a callback for each of them.
Expand All @@ -58,18 +72,15 @@ namespace LinkedDistribution
/// <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);

private:
/// <summary>
/// Iterates over each type of LinkedForms and calls a callback with each of them.
/// </summary>
template <typename Func, typename... Args>
void ForEachLinkedForms(Func&& func, const Args&&... args);

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

LinkedForms<RE::SpellItem> spells{ RECORD::kSpell };
LinkedForms<RE::BGSPerk> perks{ RECORD::kPerk };
Expand All @@ -79,9 +90,48 @@ namespace LinkedDistribution
LinkedForms<RE::TESForm> packages{ RECORD::kPackage };
LinkedForms<RE::BGSOutfit> outfits{ RECORD::kOutfit };
LinkedForms<RE::BGSKeyword> keywords{ RECORD::kKeyword };
LinkedForms<RE::TESBoundObject> deathItems{ RECORD::kDeathItem };
LinkedForms<RE::TESFaction> factions{ RECORD::kFaction };
LinkedForms<RE::BGSOutfit> sleepOutfits{ RECORD::kSleepOutfit };
LinkedForms<RE::TESObjectARMO> skins{ RECORD::kSkin };
};

#pragma region Implementation
template <class Form>
DataVec<Form>& Manager::LinkedFormsForForm(RE::TESForm* 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;
}
}

template <typename Func, typename... Args>
void Manager::ForEachLinkedForms(Func&& func, const Args&&... args)
{
func(keywords, std::forward<Args>(args)...);
func(spells, std::forward<Args>(args)...);
func(levSpells, std::forward<Args>(args)...);
func(perks, std::forward<Args>(args)...);
func(shouts, std::forward<Args>(args)...);
func(items, std::forward<Args>(args)...);
func(outfits, std::forward<Args>(args)...);
func(factions, std::forward<Args>(args)...);
func(packages, std::forward<Args>(args)...);
func(skins, std::forward<Args>(args)...);
}

template <class Form>
void LinkedForms<Form>::Link(Form* form, const FormVec& linkedForms, const RandomCount& count, const Chance& chance, const std::string& path)
{
for (const auto& linkedForm : linkedForms) {
if (std::holds_alternative<RE::TESForm*>(linkedForm)) {
auto& distributableForms = forms[std::get<RE::TESForm*>(linkedForm)];
// Note that we don't use Data.index here, as these linked items doesn't have any leveled filters
// and as such do not to track their index.
distributableForms.emplace_back(0, form, count, FilterData({}, {}, {}, {}, chance), path);
}
}
}
#pragma endregion
}
3 changes: 2 additions & 1 deletion SPID/src/Distribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ namespace Distribute
void DistributeLinkedEntries(NPCData& npcData, const PCLevelMult::Input& input, const std::set<RE::TESForm*>& forms)
{
LinkedDistribution::Manager::GetSingleton()->ForEachLinkedDistributionSet(forms, [&](Forms::DistributionSet& set) {
detail::distribute(npcData, input, set, nullptr);
detail::distribute(npcData, input, set, nullptr); // TODO: Accumulate forms here?
});
}

Expand All @@ -198,6 +198,7 @@ namespace Distribute
return;
}

// TODO: Figure out how to distribute only death items perhaps?
Forms::DistributionSet entries{
Forms::spells.GetForms(a_input.onlyPlayerLevelEntries),
Forms::perks.GetForms(a_input.onlyPlayerLevelEntries),
Expand Down
Loading

0 comments on commit 1926498

Please sign in to comment.