Skip to content

Commit

Permalink
Fix issues with temporary FF IDs used in PCLevelMult map
Browse files Browse the repository at this point in the history
  • Loading branch information
powerof3 committed Nov 10, 2022
1 parent e7da60e commit 8b20723
Show file tree
Hide file tree
Showing 12 changed files with 216 additions and 198 deletions.
17 changes: 10 additions & 7 deletions SPID/include/Distribute.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,29 +46,31 @@ namespace Distribute
void list_npc_count(std::string_view a_recordType, Forms::Distributables<Form>& a_distributables, const size_t a_totalNPCCount)
{
if (a_distributables) {
logger::info(" {}", a_recordType);
logger::info("\t{}", a_recordType);

for (auto& formData : a_distributables.forms) {
if (const auto& form = formData.form) {
if (const auto& form = formData.form) {
std::string name{};
if constexpr (std::is_same_v<Form, RE::BGSKeyword>) {
name = form->GetFormEditorID();
} else {
name = Cache::EditorID::GetEditorID(form->GetFormID());
}
if (auto file = form->GetFile(0)) {
logger::info(" {} [0x{:X}~{}] added to {}/{} NPCs", name, form->GetLocalFormID(), file->GetFilename(), formData.npcCount, a_totalNPCCount);
logger::info("\t\t{} [0x{:X}~{}] added to {}/{} NPCs", name, form->GetLocalFormID(), file->GetFilename(), formData.npcCount, a_totalNPCCount);
} else {
logger::info(" {} [0x{:X}] added to {}/{} NPCs", name, form->GetFormID(), formData.npcCount, a_totalNPCCount);
logger::info("\t\t{} [0x{:X}] added to {}/{} NPCs", name, form->GetFormID(), formData.npcCount, a_totalNPCCount);
}
}
}
}
}

namespace DeathItem
namespace Event
{
class Manager : public RE::BSTEventSink<RE::TESDeathEvent>
class Manager :
public RE::BSTEventSink<RE::TESDeathEvent>,
public RE::BSTEventSink<RE::TESFormDeleteEvent>
{
public:
static Manager* GetSingleton()
Expand All @@ -81,6 +83,7 @@ namespace Distribute

protected:
EventResult ProcessEvent(const RE::TESDeathEvent* a_event, RE::BSTEventSource<RE::TESDeathEvent>*) override;
EventResult ProcessEvent(const RE::TESFormDeleteEvent* a_event, RE::BSTEventSource<RE::TESFormDeleteEvent>*) override;

private:
Manager() = default;
Expand All @@ -99,7 +102,7 @@ namespace Distribute
void Install();
}

void Distribute(RE::TESNPC* a_actorbase, bool a_onlyPlayerLevelEntries, bool a_noPlayerLevelDistribution);
void Distribute(RE::TESNPC* a_actorbase, const PCLevelMult::Input& a_input);

void ApplyToNPCs();
}
2 changes: 0 additions & 2 deletions SPID/include/DistributePCLevelMult.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,4 @@ namespace Distribute
{
void Install();
}

void ApplyToPCLevelMultNPCs(RE::TESDataHandler* a_dataHandler);
}
14 changes: 7 additions & 7 deletions SPID/include/LookupConfigs.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,23 +118,23 @@ namespace INI
//skill min max
if (!skills.empty()) {
if (skills.size() > 2) {
auto type = string::to_num<std::uint32_t>(skills.at(0));
auto minLevel = string::to_num<std::uint8_t>(skills.at(1));
auto maxLevel = string::to_num<std::uint8_t>(skills.at(2));
auto type = string::to_num<std::uint32_t>(skills[0]);
auto minLevel = string::to_num<std::uint8_t>(skills[1]);
auto maxLevel = string::to_num<std::uint8_t>(skills[2]);

skillLevelPairs.push_back({ type, { minLevel, maxLevel } });
} else {
auto type = string::to_num<std::uint32_t>(skills.at(0));
auto minLevel = string::to_num<std::uint8_t>(skills.at(1));
auto type = string::to_num<std::uint32_t>(skills[0]);
auto minLevel = string::to_num<std::uint8_t>(skills[1]);

skillLevelPairs.push_back({ type, { minLevel, UINT8_MAX } });
}
}
} else {
auto split_level = string::split(levels, "/");
if (split_level.size() > 1) {
auto minLevel = string::to_num<std::uint16_t>(split_level.at(0));
auto maxLevel = string::to_num<std::uint16_t>(split_level.at(1));
auto minLevel = string::to_num<std::uint16_t>(split_level[0]);
auto maxLevel = string::to_num<std::uint16_t>(split_level[1]);

actorLevelPair = { minLevel, maxLevel };
} else {
Expand Down
18 changes: 9 additions & 9 deletions SPID/include/LookupForms.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ namespace Lookup
}
if (modName && !formID) {
if (const RE::TESFile* filterMod = a_dataHandler->LookupModByName(*modName); filterMod) {
logger::info(" [{}] Filter ({}) INFO - mod found", a_path, filterMod->fileName);
logger::info("\t\t\t[{}] Filter ({}) INFO - mod found", a_path, filterMod->fileName);
a_formVec.push_back(filterMod);
} else {
logger::error(" [{}] Filter ({}) SKIP - mod cannot be found", a_path, *modName);
logger::error("\t\t\t[{}] Filter ({}) SKIP - mod cannot be found", a_path, *modName);
}
} else if (formID) {
auto filterForm = modName ?
Expand All @@ -85,10 +85,10 @@ namespace Lookup
if (Cache::FormType::GetWhitelisted(formType)) {
a_formVec.push_back(filterForm);
} else {
logger::error(" [{}] Filter [0x{:X}] ({}) SKIP - invalid formtype ({})", a_path, *formID, modName.value_or(""), formType);
logger::error("\t\t\t[{}] Filter [0x{:X}] ({}) SKIP - invalid formtype ({})", a_path, *formID, modName.value_or(""), formType);
}
} else {
logger::error(" [{}] Filter [0x{:X}] ({}) SKIP - form doesn't exist", a_path, *formID, modName.value_or(""));
logger::error("\t\t\t[{}] Filter [0x{:X}] ({}) SKIP - form doesn't exist", a_path, *formID, modName.value_or(""));
}
}
} else if (std::holds_alternative<std::string>(formOrEditorID)) {
Expand Down Expand Up @@ -134,7 +134,7 @@ namespace Lookup
return;
}

logger::info(" Starting {} lookup", a_type);
logger::info("\tStarting {} lookup", a_type);

for (auto& [formOrEditorID, strings, filterIDs, level, traits, idxOrCount, chance, path] : a_INIDataVec) {
Form* form = nullptr;
Expand Down Expand Up @@ -187,23 +187,23 @@ namespace Lookup
if (result != keywordArray.end()) {
if (const auto keyword = *result; keyword) {
if (!keyword->IsDynamicForm()) {
logger::info(" [{}] {} [0x{:X}] INFO - using existing keyword", path, keywordName, keyword->GetFormID());
logger::info("\t\t[{}] {} [0x{:X}] INFO - using existing keyword", path, keywordName, keyword->GetFormID());
}
form = keyword;
} else {
logger::critical(" [{}] {} FAIL - couldn't get existing keyword", path, keywordName);
logger::critical("\t\t[{}] {} FAIL - couldn't get existing keyword", path, keywordName);
continue;
}
} else {
const auto factory = RE::IFormFactory::GetConcreteFormFactoryByType<RE::BGSKeyword>();
if (auto keyword = factory ? factory->Create() : nullptr; keyword) {
keyword->formEditorID = keywordName;
keywordArray.push_back(keyword);
logger::info(" [{}] {} [0x{:X}] INFO - creating keyword", path, keywordName, keyword->GetFormID());
logger::info("\t\t[{}] {} [0x{:X}] INFO - creating keyword", path, keywordName, keyword->GetFormID());

form = keyword;
} else {
logger::critical(" [{}] {} FAIL - couldn't create keyword", path, keywordName);
logger::critical("\t\t[{}] {} FAIL - couldn't create keyword", path, keywordName);
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion SPID/include/PCLevelMultManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ namespace PCLevelMult
{
struct Input
{
Input(const RE::TESNPC* a_npc, bool a_onlyPlayerLevelEntries, bool a_noPlayerLevelDistribution);
Input(const RE::TESNPC* a_base, bool a_onlyPlayerLevelEntries, bool a_noPlayerLevelDistribution);
Input(const RE::Actor* a_character, const RE::TESNPC* a_base, bool a_onlyPlayerLevelEntries, bool a_noPlayerLevelDistribution);

std::uint64_t playerID;
RE::FormID npcFormID;
Expand Down Expand Up @@ -35,6 +36,8 @@ namespace PCLevelMult
void ForEachDistributedEntry(const Input& a_input, std::function<void(RE::TESForm&, IdxOrCount a_idx, bool)> a_fn) const;
void DumpDistributedEntries();

void DeleteNPC(RE::FormID a_characterID);

bool HasHitLevelCap(const Input& a_input);

std::uint64_t GetCurrentPlayerID();
Expand Down
88 changes: 44 additions & 44 deletions SPID/src/Distribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,17 @@

namespace Distribute
{
void Distribute(RE::TESNPC* a_actorbase, bool a_onlyPlayerLevelEntries, bool a_noPlayerLevelDistribution)
void Distribute(RE::TESNPC* a_actorbase, const PCLevelMult::Input& a_input)
{
const PCLevelMult::Input input{
a_actorbase,
a_onlyPlayerLevelEntries,
a_noPlayerLevelDistribution
};

if (a_onlyPlayerLevelEntries && PCLevelMult::Manager::GetSingleton()->HasHitLevelCap(input)) {
if (a_input.onlyPlayerLevelEntries && PCLevelMult::Manager::GetSingleton()->HasHitLevelCap(a_input)) {
return;
}

for_each_form<RE::BGSKeyword>(*a_actorbase, Forms::keywords, input, [&](auto* a_keyword, [[maybe_unused]] IdxOrCount a_count) {
for_each_form<RE::BGSKeyword>(*a_actorbase, Forms::keywords, a_input, [&](auto* a_keyword, [[maybe_unused]] IdxOrCount a_count) {
return a_actorbase->AddKeyword(a_keyword);
});

for_each_form<RE::TESFaction>(*a_actorbase, Forms::factions, input, [&](auto* a_faction, [[maybe_unused]] IdxOrCount a_count) {
for_each_form<RE::TESFaction>(*a_actorbase, Forms::factions, a_input, [&](auto* a_faction, [[maybe_unused]] IdxOrCount a_count) {
if (!a_actorbase->IsInFaction(a_faction)) {
const RE::FACTION_RANK faction{ a_faction, 1 };
a_actorbase->factions.push_back(faction);
Expand All @@ -28,38 +22,38 @@ namespace Distribute
return false;
});

for_each_form<RE::BGSPerk>(*a_actorbase, Forms::perks, input, [&](auto* a_perk, [[maybe_unused]] IdxOrCount a_count) {
for_each_form<RE::BGSPerk>(*a_actorbase, Forms::perks, a_input, [&](auto* a_perk, [[maybe_unused]] IdxOrCount a_count) {
return a_actorbase->AddPerk(a_perk, 1);
});

for_each_form<RE::SpellItem>(*a_actorbase, Forms::spells, input, [&](auto* a_spell, [[maybe_unused]] IdxOrCount a_count) {
for_each_form<RE::SpellItem>(*a_actorbase, Forms::spells, a_input, [&](auto* a_spell, [[maybe_unused]] IdxOrCount a_count) {
const auto actorEffects = a_actorbase->GetSpellList();
return actorEffects && actorEffects->AddSpell(a_spell);
});

for_each_form<RE::TESShout>(*a_actorbase, Forms::shouts, input, [&](auto* a_shout, [[maybe_unused]] IdxOrCount a_count) {
for_each_form<RE::TESShout>(*a_actorbase, Forms::shouts, a_input, [&](auto* a_shout, [[maybe_unused]] IdxOrCount a_count) {
const auto actorEffects = a_actorbase->GetSpellList();
return actorEffects && actorEffects->AddShout(a_shout);
});

for_each_form<RE::TESLevSpell>(*a_actorbase, Forms::levSpells, input, [&](auto* a_levSpell, [[maybe_unused]] IdxOrCount a_count) {
for_each_form<RE::TESLevSpell>(*a_actorbase, Forms::levSpells, a_input, [&](auto* a_levSpell, [[maybe_unused]] IdxOrCount a_count) {
const auto actorEffects = a_actorbase->GetSpellList();
return actorEffects && actorEffects->AddLevSpell(a_levSpell);
});

for_each_form<RE::TESBoundObject>(*a_actorbase, Forms::items, input, [&](auto* a_item, IdxOrCount a_count) {
for_each_form<RE::TESBoundObject>(*a_actorbase, Forms::items, a_input, [&](auto* a_item, IdxOrCount a_count) {
return a_actorbase->AddObjectToContainer(a_item, a_count, a_actorbase);
});

for_each_form<RE::BGSOutfit>(*a_actorbase, Forms::outfits, input, [&](auto* a_outfit, [[maybe_unused]] IdxOrCount a_count) {
for_each_form<RE::BGSOutfit>(*a_actorbase, Forms::outfits, a_input, [&](auto* a_outfit, [[maybe_unused]] IdxOrCount a_count) {
if (a_actorbase->defaultOutfit != a_outfit) {
a_actorbase->defaultOutfit = a_outfit;
return true;
}
return false;
});

for_each_form<RE::TESForm>(*a_actorbase, Forms::packages, input, [&](auto* a_packageOrList, [[maybe_unused]] IdxOrCount a_idx) {
for_each_form<RE::TESForm>(*a_actorbase, Forms::packages, a_input, [&](auto* a_packageOrList, [[maybe_unused]] IdxOrCount a_idx) {
auto packageIdx = a_idx;

if (a_packageOrList->Is(RE::FormType::Package)) {
Expand Down Expand Up @@ -116,15 +110,15 @@ namespace Distribute
return false;
});

for_each_form<RE::BGSOutfit>(*a_actorbase, Forms::sleepOutfits, input, [&](auto* a_outfit, [[maybe_unused]] IdxOrCount a_count) {
for_each_form<RE::BGSOutfit>(*a_actorbase, Forms::sleepOutfits, a_input, [&](auto* a_outfit, [[maybe_unused]] IdxOrCount a_count) {
if (a_actorbase->sleepOutfit != a_outfit) {
a_actorbase->sleepOutfit = a_outfit;
return true;
}
return false;
});

for_each_form<RE::TESObjectARMO>(*a_actorbase, Forms::skins, input, [&](auto* a_skin, [[maybe_unused]] IdxOrCount a_count) {
for_each_form<RE::TESObjectARMO>(*a_actorbase, Forms::skins, a_input, [&](auto* a_skin, [[maybe_unused]] IdxOrCount a_count) {
if (a_actorbase->skin != a_skin) {
a_actorbase->skin = a_skin;
return true;
Expand All @@ -143,7 +137,7 @@ namespace Distribute
std::size_t totalNPCs = 0;
for (const auto& actorbase : dataHandler->GetFormArray<RE::TESNPC>()) {
if (actorbase && !actorbase->IsPlayer() && (!uses_template(actorbase) || actorbase->IsUnique())) {
Distribute(actorbase, false, true);
Distribute(actorbase, PCLevelMult::Input{ actorbase, false, true });
totalNPCs++;
}
}
Expand Down Expand Up @@ -174,7 +168,7 @@ namespace Distribute
}
}

namespace Distribute::DeathItem
namespace Distribute::Event
{
struct detail //AddObjectToContainer doesn't work with leveled items :s
{
Expand All @@ -188,13 +182,13 @@ namespace Distribute::DeathItem

void Manager::Register()
{
if (!Forms::deathItems) {
return;
}

if (auto scripts = RE::ScriptEventSourceHolder::GetSingleton()) {
scripts->AddEventSink(GetSingleton());
logger::info(" Registered {}"sv, typeid(Manager).name());
if (const auto scripts = RE::ScriptEventSourceHolder::GetSingleton()) {
scripts->AddEventSink<RE::TESFormDeleteEvent>(GetSingleton());
logger::info("\tRegistered for {}", typeid(RE::TESFormDeleteEvent).name());
if (Forms::deathItems) {
scripts->AddEventSink<RE::TESDeathEvent>(GetSingleton());
logger::info("\tRegistered for {}", typeid(RE::TESDeathEvent).name());
}
}
}

Expand All @@ -206,14 +200,14 @@ namespace Distribute::DeathItem

if (a_event && a_event->dead && is_NPC(a_event->actorDying)) {
const auto actor = a_event->actorDying->As<RE::Actor>();
const auto actorBase = actor ? actor->GetActorBase() : nullptr;
if (actor && actorBase) {
PCLevelMult::Input input{
actorBase,
const auto actorbase = actor ? actor->GetActorBase() : nullptr;
if (actor && actorbase) {
const PCLevelMult::Input input{
actorbase,
false,
false,
};
for_each_form<RE::TESBoundObject>(*actorBase, Forms::deathItems, input, [&](auto* a_deathItem, IdxOrCount a_count) {
for_each_form<RE::TESBoundObject>(*actorbase, Forms::deathItems, input, [&](auto* a_deathItem, IdxOrCount a_count) {
detail::add_item(actor, a_deathItem, a_count, true, 0, RE::BSScript::Internal::VirtualMachine::GetSingleton());
return true;
});
Expand All @@ -222,33 +216,39 @@ namespace Distribute::DeathItem

return EventResult::kContinue;
}

EventResult Manager::ProcessEvent(const RE::TESFormDeleteEvent* a_event, RE::BSTEventSource<RE::TESFormDeleteEvent>*)
{
if (a_event && a_event->formID != 0) {
PCLevelMult::Manager::GetSingleton()->DeleteNPC(a_event->formID);
}
return EventResult::kContinue;
}
}

namespace Distribute::LeveledActor
{
struct CopyFromTemplateForms
struct SetObjectReference
{
static void thunk(RE::TESActorBaseData* a_this, RE::TESActorBase** a_templateForms)
static void thunk(RE::Character* a_this, RE::TESNPC* a_npc)
{
func(a_this, a_templateForms);
func(a_this, a_npc);

if (!a_this->baseTemplateForm || !a_templateForms) {
if (!a_npc || !a_npc->IsDynamicForm()) {
return;
}

if (const auto actorbase = stl::adjust_pointer<RE::TESNPC>(a_this, -0x30); actorbase) {
Distribute(actorbase, false, false);
}
Distribute(a_npc, PCLevelMult::Input{ a_this, a_npc, false, false });
}
static inline REL::Relocation<decltype(thunk)> func;

static inline size_t index{ 1 };
static inline size_t size{ 0x4 };
static inline size_t index{ 0 };
static inline size_t size{ 0x84 };
};

void Install()
{
stl::write_vfunc<RE::TESNPC, CopyFromTemplateForms>();
logger::info(" Hooked leveled actor init");
stl::write_vfunc<RE::Character, SetObjectReference>();
logger::info("\tHooked leveled actor init");
}
}
Loading

0 comments on commit 8b20723

Please sign in to comment.