Skip to content

Commit

Permalink
Unified Death Distribution.
Browse files Browse the repository at this point in the history
On Death distribution will support everything that original distribution does.
  • Loading branch information
adya committed Apr 9, 2024
1 parent 94e67b4 commit 06ed7c9
Show file tree
Hide file tree
Showing 18 changed files with 973 additions and 528 deletions.
1 change: 1 addition & 0 deletions SPID/cmake/headerlist.cmake
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
set(headers ${headers}
include/Cache.h
include/DeathDistribution.h
include/Defs.h
include/DependencyResolver.h
include/Distribute.h
Expand Down
1 change: 1 addition & 0 deletions SPID/cmake/sourcelist.cmake
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
set(sources ${sources}
src/Cache.cpp
src/DeathDistribution.cpp
src/Distribute.cpp
src/DistributeManager.cpp
src/DistributePCLevelMult.cpp
Expand Down
76 changes: 76 additions & 0 deletions SPID/include/DeathDistribution.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#pragma once
#include "FormData.h"

namespace DeathDistribution
{
namespace INI
{
/// <summary>
/// Checks whether given entry is an on death distribuatble form and attempts to parse it.
/// </summary>
/// <returns>true if given entry was an on death distribuatble form. Note that returned value doesn't represent whether parsing was successful.</returns>
bool TryParse(const std::string& key, const std::string& value, const Path&);
}

using namespace Forms;

class Manager :
public ISingleton<Manager>,
public RE::BSTEventSink<RE::TESDeathEvent>
{
public:
static void Register();

/// <summary>
/// Does a forms lookup similar to what Filters do.
///
/// As a result this method configures Manager with discovered valid On Death Distributable Forms.
/// </summary>
/// <param name="dataHandler">A DataHandler that will perform the actual lookup.</param>
void LookupForms(RE::TESDataHandler* const dataHandler);

void LogFormsLookup();

bool IsEmpty();

private:
Distributables<RE::SpellItem> spells{ RECORD::kSpell };
Distributables<RE::BGSPerk> perks{ RECORD::kPerk };
Distributables<RE::TESBoundObject> items{ RECORD::kItem };
Distributables<RE::TESShout> shouts{ RECORD::kShout };
Distributables<RE::TESLevSpell> levSpells{ RECORD::kLevSpell };
Distributables<RE::TESForm> packages{ RECORD::kPackage };
Distributables<RE::BGSOutfit> outfits{ RECORD::kOutfit };
Distributables<RE::BGSKeyword> keywords{ RECORD::kKeyword };
Distributables<RE::TESFaction> factions{ RECORD::kFaction };
Distributables<RE::BGSOutfit> sleepOutfits{ RECORD::kSleepOutfit };
Distributables<RE::TESObjectARMO> skins{ RECORD::kSkin };

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

protected:
RE::BSEventNotifyControl ProcessEvent(const RE::TESDeathEvent*, RE::BSTEventSource<RE::TESDeathEvent>*) override;
};

#pragma region Implementation
template <typename Func, typename... Args>
void Manager::ForEachDistributable(Func&& func, Args&&... args)
{
func(keywords, std::forward<Args>(args)...);
func(factions, 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(packages, std::forward<Args>(args)...);
func(outfits, std::forward<Args>(args)...);
func(sleepOutfits, std::forward<Args>(args)...);
func(skins, std::forward<Args>(args)...);
func(items, std::forward<Args>(args)...);
}
#pragma endregion
}
6 changes: 3 additions & 3 deletions SPID/include/Defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ struct SkillLevel

struct LevelFilters
{
Range<std::uint16_t> actorLevel;
std::vector<SkillLevel> skillLevels; // skill levels
std::vector<SkillLevel> skillWeights; // skill weights (from Class)
Range<std::uint16_t> actorLevel{};
std::vector<SkillLevel> skillLevels{}; // skill levels
std::vector<SkillLevel> skillWeights{}; // skill weights (from Class)
};

struct Traits
Expand Down
20 changes: 14 additions & 6 deletions SPID/include/Distribute.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ namespace Distribute
return a_formData.filters.PassedFilters(a_npcData) == Filter::Result::kPass;
}

/// <summary>
/// Check that NPC doesn't already have the form that is about to be distributed.
/// </summary>
template <class Form>
bool has_form(RE::TESNPC* a_npc, Form* a_form)
{
Expand All @@ -66,15 +69,13 @@ namespace Distribute
return false;
}
}

void add_item(RE::Actor* a_actor, RE::TESBoundObject* a_item, std::uint32_t a_itemCount);
}

using namespace Forms;

#pragma region Packages, Death Items
#pragma region Packages
// old method (distributing one by one)
// for now, only packages/death items use this
// for now, only packages use this
template <class Form>
void for_each_form(
const NPCData& a_npcData,
Expand Down Expand Up @@ -221,8 +222,15 @@ namespace Distribute
}
#pragma endregion

/// <summary>
/// Performs distribution of all configured forms to NPC described with npcData and input.
/// </summary>
/// <param name="npcData">General information about NPC that is being processed.</param>
/// <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="allowOverwrites">If true, overwritable forms (like Outfits) will be to overwrite last distributed form on 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, bool allowOverwrites, DistributedForms* accumulatedForms = nullptr);
void Distribute(NPCData& npcData, const PCLevelMult::Input& input);
void Distribute(NPCData& npcData, bool onlyLeveledEntries);

void DistributeDeathItems(NPCData& npcData, const PCLevelMult::Input& input);
}
2 changes: 0 additions & 2 deletions SPID/include/DistributeManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,12 @@ namespace Distribute
{
class Manager :
public ISingleton<Manager>,
public RE::BSTEventSink<RE::TESDeathEvent>,
public RE::BSTEventSink<RE::TESFormDeleteEvent>
{
public:
static void Register();

protected:
RE::BSEventNotifyControl ProcessEvent(const RE::TESDeathEvent* a_event, RE::BSTEventSource<RE::TESDeathEvent>*) override;
RE::BSEventNotifyControl ProcessEvent(const RE::TESFormDeleteEvent* a_event, RE::BSTEventSource<RE::TESFormDeleteEvent>*) override;
};
}
Expand Down
43 changes: 20 additions & 23 deletions SPID/include/FormData.h
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,6 @@ namespace Forms
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;
Expand Down Expand Up @@ -435,7 +434,7 @@ namespace Forms
DataVec<Form>& GetForms(bool a_onlyLevelEntries);
DataVec<Form>& GetForms();

void LookupForms(RE::TESDataHandler*, std::string_view a_type, INI::DataVec&);
void LookupForms(RE::TESDataHandler*, std::string_view a_type, Configs::INI::DataVec&);
void EmplaceForm(bool isValid, Form*, const IndexOrCount&, const FilterData&, const Path&);

// Init formsWithLevels and formsNoLevels
Expand All @@ -450,7 +449,7 @@ namespace Forms
/// This counter is used for logging purposes.
std::size_t lookupCount{ 0 };

void LookupForm(RE::TESDataHandler*, INI::Data&);
void LookupForm(RE::TESDataHandler*, Configs::INI::Data&);
};

inline Distributables<RE::SpellItem> spells{ RECORD::kSpell };
Expand All @@ -461,7 +460,6 @@ namespace Forms
inline Distributables<RE::TESForm> packages{ RECORD::kPackage };
inline Distributables<RE::BGSOutfit> outfits{ RECORD::kOutfit };
inline Distributables<RE::BGSKeyword> keywords{ RECORD::kKeyword };
inline Distributables<RE::TESBoundObject> deathItems{ RECORD::kDeathItem };
inline Distributables<RE::TESFaction> factions{ RECORD::kFaction };
inline Distributables<RE::BGSOutfit> sleepOutfits{ RECORD::kSleepOutfit };
inline Distributables<RE::TESObjectARMO> skins{ RECORD::kSkin };
Expand All @@ -470,20 +468,19 @@ namespace Forms
std::size_t GetTotalLeveledEntries();

template <typename Func, typename... Args>
void ForEachDistributable(Func&& a_func, Args&&... args)
void ForEachDistributable(Func&& func, Args&&... args)
{
a_func(keywords, std::forward<Args>(args)...);
a_func(factions, std::forward<Args>(args)...);
a_func(perks, std::forward<Args>(args)...);
a_func(spells, std::forward<Args>(args)...);
a_func(levSpells, std::forward<Args>(args)...);
a_func(shouts, std::forward<Args>(args)...);
a_func(items, std::forward<Args>(args)...);
a_func(deathItems, std::forward<Args>(args)...);
a_func(outfits, std::forward<Args>(args)...);
a_func(sleepOutfits, std::forward<Args>(args)...);
a_func(packages, std::forward<Args>(args)...);
a_func(skins, std::forward<Args>(args)...);
func(keywords, std::forward<Args>(args)...);
func(factions, 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(packages, std::forward<Args>(args)...);
func(outfits, std::forward<Args>(args)...);
func(sleepOutfits, std::forward<Args>(args)...);
func(skins, std::forward<Args>(args)...);
func(items, std::forward<Args>(args)...);
}

/// <summary>
Expand All @@ -495,7 +492,7 @@ namespace Forms
/// <param name="rawForm">A raw form entry that needs to be looked up.</param>
/// <param name="callback">A callback to be called with validated data after successful lookup.</param>
template <class Form = RE::TESForm*>
void LookupGenericForm(RE::TESDataHandler* const dataHandler, INI::Data& rawForm, std::function<void(bool isValid, Form*, const IndexOrCount&, const FilterData&, const Path& path)> callback);
void LookupGenericForm(RE::TESDataHandler* const dataHandler, Configs::INI::Data& rawForm, std::function<void(bool isValid, Form*, const IndexOrCount&, const FilterData&, const Path& path)> callback);
}

template <class Form>
Expand Down Expand Up @@ -553,15 +550,15 @@ Forms::DataVec<Form>& Forms::Distributables<Form>::GetForms(bool a_onlyLevelEntr
}

template <class Form>
void Forms::Distributables<Form>::LookupForm(RE::TESDataHandler* dataHandler, INI::Data& rawForm)
void Forms::Distributables<Form>::LookupForm(RE::TESDataHandler* dataHandler, Configs::INI::Data& rawForm)
{
Forms::LookupGenericForm<Form>(dataHandler, rawForm, [&](bool isValid, Form* form, const auto& idxOrCount, const auto& filters, const auto& path) {
EmplaceForm(isValid, form, idxOrCount, filters, path);
});
}

template <class Form>
void Forms::Distributables<Form>::LookupForms(RE::TESDataHandler* dataHandler, std::string_view a_type, INI::DataVec& a_INIDataVec)
void Forms::Distributables<Form>::LookupForms(RE::TESDataHandler* dataHandler, std::string_view a_type, Configs::INI::DataVec& a_INIDataVec)
{
if (a_INIDataVec.empty()) {
return;
Expand Down Expand Up @@ -609,7 +606,7 @@ void Forms::Distributables<Form>::FinishLookupForms()
}

template <class Form>
void Forms::LookupGenericForm(RE::TESDataHandler* const dataHandler, INI::Data& rawForm, std::function<void(bool isValid, Form*, const IndexOrCount&, const FilterData&, const Path& path)> callback)
void Forms::LookupGenericForm(RE::TESDataHandler* const dataHandler, Configs::INI::Data& rawForm, std::function<void(bool isValid, Form*, const IndexOrCount&, const FilterData&, const Path& path)> callback)
{
auto& [formOrEditorID, strings, filterIDs, level, traits, idxOrCount, chance, path] = rawForm;

Expand Down Expand Up @@ -652,9 +649,9 @@ void Forms::LookupGenericForm(RE::TESDataHandler* const dataHandler, INI::Data&
buffered_logger::error("\t\t[{}] ({}) FAIL - mismatching form type (expected: {}, actual: {})", e.path, editorID, e.expectedFormType, e.actualFormType);
} },
e.formOrEditorID);
} catch (const Lookup::InvalidFormTypeException& e) {
} catch (const Lookup::InvalidFormTypeException&) {
// Whitelisting is disabled, so this should not occur
} catch (const Lookup::UnknownPluginException& e) {
} catch (const Lookup::UnknownPluginException&) {
// Likewise, we don't expect plugin names in distributable forms.
}
}
Expand Down
16 changes: 13 additions & 3 deletions SPID/include/LinkedDistribution.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ namespace LinkedDistribution
/// <summary>
/// Checks whether given entry is a linked form and attempts to parse it.
/// </summary>
/// <returns>true if given entry was a linked form. Note that returned value doesn't represent whether or parsing was successful.</returns>
/// <returns>true if given entry was a linked form. Note that returned value doesn't represent whether parsing was successful.</returns>
bool TryParse(const std::string& key, const std::string& value, const Path&);
}

Expand Down Expand Up @@ -129,7 +129,6 @@ namespace LinkedDistribution
LinkedForms<RE::SpellItem> spells{ RECORD::kSpell };
LinkedForms<RE::BGSPerk> perks{ RECORD::kPerk };
LinkedForms<RE::TESBoundObject> items{ RECORD::kItem };
LinkedForms<RE::TESBoundObject> deathItems{ RECORD::kDeathItem };
LinkedForms<RE::TESShout> shouts{ RECORD::kShout };
LinkedForms<RE::TESLevSpell> levSpells{ RECORD::kLevSpell };
LinkedForms<RE::TESForm> packages{ RECORD::kPackage };
Expand All @@ -139,6 +138,18 @@ namespace LinkedDistribution
LinkedForms<RE::TESFaction> factions{ RECORD::kFaction };
LinkedForms<RE::TESObjectARMO> skins{ RECORD::kSkin };

LinkedForms<RE::SpellItem> deathSpells{ RECORD::kSpell };
LinkedForms<RE::BGSPerk> deathPerks{ RECORD::kPerk };
LinkedForms<RE::TESBoundObject> deathItems{ RECORD::kItem };
LinkedForms<RE::TESShout> deathShouts{ RECORD::kShout };
LinkedForms<RE::TESLevSpell> deathLevSpells{ RECORD::kLevSpell };
LinkedForms<RE::TESForm> deathPackages{ RECORD::kPackage };
LinkedForms<RE::BGSOutfit> deathOutfits{ RECORD::kOutfit };
LinkedForms<RE::BGSOutfit> deathSleepOutfits{ RECORD::kSleepOutfit };
LinkedForms<RE::BGSKeyword> deathKeywords{ RECORD::kKeyword };
LinkedForms<RE::TESFaction> deathFactions{ RECORD::kFaction };
LinkedForms<RE::TESObjectARMO> deathSkins{ RECORD::kSkin };

/// <summary>
/// Iterates over each type of LinkedForms and calls a callback with each of them.
/// </summary>
Expand Down Expand Up @@ -217,7 +228,6 @@ namespace LinkedDistribution
func(perks, std::forward<Args>(args)...);
func(shouts, std::forward<Args>(args)...);
func(items, std::forward<Args>(args)...);
func(deathItems, std::forward<Args>(args)...);
func(outfits, std::forward<Args>(args)...);
func(sleepOutfits, std::forward<Args>(args)...);
func(factions, std::forward<Args>(args)...);
Expand Down
55 changes: 29 additions & 26 deletions SPID/include/LookupConfigs.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,36 +57,39 @@ namespace RECORD
}
}

namespace INI
namespace Configs
{
enum TYPE : std::uint32_t
namespace INI
{
kFormIDPair = 0,
kFormID = kFormIDPair,
kStrings,
kESP = kStrings,
kFilterIDs,
kLevel,
kTraits,
kIdxOrCount,
kChance
};
enum TYPE : std::uint32_t
{
kFormIDPair = 0,
kFormID = kFormIDPair,
kStrings,
kESP = kStrings,
kFilterIDs,
kLevel,
kTraits,
kIdxOrCount,
kChance
};

struct Data
{
FormOrEditorID rawForm{};
StringFilters stringFilters{};
Filters<FormOrEditorID> rawFormFilters{};
LevelFilters levelFilters{};
Traits traits{};
IndexOrCount idxOrCount{ RandomCount(1, 1) };
PercentChance chance{ 100 };
std::string path{};
};
struct Data
{
FormOrEditorID rawForm{};
StringFilters stringFilters{};
Filters<FormOrEditorID> rawFormFilters{};
LevelFilters levelFilters{};
Traits traits{};
IndexOrCount idxOrCount{ RandomCount(1, 1) };
PercentChance chance{ 100 };
std::string path{};
};

using DataVec = std::vector<Data>;
using DataVec = std::vector<Data>;

inline Map<RECORD::TYPE, DataVec> configs{};
inline Map<RECORD::TYPE, DataVec> configs{};

std::pair<bool, bool> GetConfigs();
std::pair<bool, bool> GetConfigs();
}
}
Loading

0 comments on commit 06ed7c9

Please sign in to comment.