diff --git a/SPID/include/Distribute.h b/SPID/include/Distribute.h index 78e7d68..a556b18 100644 --- a/SPID/include/Distribute.h +++ b/SPID/include/Distribute.h @@ -46,10 +46,10 @@ namespace Distribute void list_npc_count(std::string_view a_recordType, Forms::Distributables
& 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) { name = form->GetFormEditorID(); @@ -57,18 +57,20 @@ namespace Distribute 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 + class Manager : + public RE::BSTEventSink, + public RE::BSTEventSink { public: static Manager* GetSingleton() @@ -81,6 +83,7 @@ namespace Distribute protected: EventResult ProcessEvent(const RE::TESDeathEvent* a_event, RE::BSTEventSource*) override; + EventResult ProcessEvent(const RE::TESFormDeleteEvent* a_event, RE::BSTEventSource*) override; private: Manager() = default; @@ -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(); } diff --git a/SPID/include/DistributePCLevelMult.h b/SPID/include/DistributePCLevelMult.h index f3776ce..74f9087 100644 --- a/SPID/include/DistributePCLevelMult.h +++ b/SPID/include/DistributePCLevelMult.h @@ -6,6 +6,4 @@ namespace Distribute { void Install(); } - - void ApplyToPCLevelMultNPCs(RE::TESDataHandler* a_dataHandler); } diff --git a/SPID/include/LookupConfigs.h b/SPID/include/LookupConfigs.h index 849a2e3..9360164 100644 --- a/SPID/include/LookupConfigs.h +++ b/SPID/include/LookupConfigs.h @@ -118,14 +118,14 @@ namespace INI //skill min max if (!skills.empty()) { if (skills.size() > 2) { - auto type = string::to_num(skills.at(0)); - auto minLevel = string::to_num(skills.at(1)); - auto maxLevel = string::to_num(skills.at(2)); + auto type = string::to_num(skills[0]); + auto minLevel = string::to_num(skills[1]); + auto maxLevel = string::to_num(skills[2]); skillLevelPairs.push_back({ type, { minLevel, maxLevel } }); } else { - auto type = string::to_num(skills.at(0)); - auto minLevel = string::to_num(skills.at(1)); + auto type = string::to_num(skills[0]); + auto minLevel = string::to_num(skills[1]); skillLevelPairs.push_back({ type, { minLevel, UINT8_MAX } }); } @@ -133,8 +133,8 @@ namespace INI } else { auto split_level = string::split(levels, "/"); if (split_level.size() > 1) { - auto minLevel = string::to_num(split_level.at(0)); - auto maxLevel = string::to_num(split_level.at(1)); + auto minLevel = string::to_num(split_level[0]); + auto maxLevel = string::to_num(split_level[1]); actorLevelPair = { minLevel, maxLevel }; } else { diff --git a/SPID/include/LookupForms.h b/SPID/include/LookupForms.h index bb109e4..031c812 100644 --- a/SPID/include/LookupForms.h +++ b/SPID/include/LookupForms.h @@ -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 ? @@ -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(formOrEditorID)) { @@ -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; @@ -187,11 +187,11 @@ 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 { @@ -199,11 +199,11 @@ namespace Lookup 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); } } } diff --git a/SPID/include/PCLevelMultManager.h b/SPID/include/PCLevelMultManager.h index 9933915..b24fbcf 100644 --- a/SPID/include/PCLevelMultManager.h +++ b/SPID/include/PCLevelMultManager.h @@ -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; @@ -35,6 +36,8 @@ namespace PCLevelMult void ForEachDistributedEntry(const Input& a_input, std::function a_fn) const; void DumpDistributedEntries(); + void DeleteNPC(RE::FormID a_characterID); + bool HasHitLevelCap(const Input& a_input); std::uint64_t GetCurrentPlayerID(); diff --git a/SPID/src/Distribute.cpp b/SPID/src/Distribute.cpp index 39cd712..99c74d2 100644 --- a/SPID/src/Distribute.cpp +++ b/SPID/src/Distribute.cpp @@ -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(*a_actorbase, Forms::keywords, input, [&](auto* a_keyword, [[maybe_unused]] IdxOrCount a_count) { + for_each_form(*a_actorbase, Forms::keywords, a_input, [&](auto* a_keyword, [[maybe_unused]] IdxOrCount a_count) { return a_actorbase->AddKeyword(a_keyword); }); - for_each_form(*a_actorbase, Forms::factions, input, [&](auto* a_faction, [[maybe_unused]] IdxOrCount a_count) { + for_each_form(*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); @@ -28,30 +22,30 @@ namespace Distribute return false; }); - for_each_form(*a_actorbase, Forms::perks, input, [&](auto* a_perk, [[maybe_unused]] IdxOrCount a_count) { + for_each_form(*a_actorbase, Forms::perks, a_input, [&](auto* a_perk, [[maybe_unused]] IdxOrCount a_count) { return a_actorbase->AddPerk(a_perk, 1); }); - for_each_form(*a_actorbase, Forms::spells, input, [&](auto* a_spell, [[maybe_unused]] IdxOrCount a_count) { + for_each_form(*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(*a_actorbase, Forms::shouts, input, [&](auto* a_shout, [[maybe_unused]] IdxOrCount a_count) { + for_each_form(*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(*a_actorbase, Forms::levSpells, input, [&](auto* a_levSpell, [[maybe_unused]] IdxOrCount a_count) { + for_each_form(*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(*a_actorbase, Forms::items, input, [&](auto* a_item, IdxOrCount a_count) { + for_each_form(*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(*a_actorbase, Forms::outfits, input, [&](auto* a_outfit, [[maybe_unused]] IdxOrCount a_count) { + for_each_form(*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; @@ -59,7 +53,7 @@ namespace Distribute return false; }); - for_each_form(*a_actorbase, Forms::packages, input, [&](auto* a_packageOrList, [[maybe_unused]] IdxOrCount a_idx) { + for_each_form(*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)) { @@ -116,7 +110,7 @@ namespace Distribute return false; }); - for_each_form(*a_actorbase, Forms::sleepOutfits, input, [&](auto* a_outfit, [[maybe_unused]] IdxOrCount a_count) { + for_each_form(*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; @@ -124,7 +118,7 @@ namespace Distribute return false; }); - for_each_form(*a_actorbase, Forms::skins, input, [&](auto* a_skin, [[maybe_unused]] IdxOrCount a_count) { + for_each_form(*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; @@ -143,7 +137,7 @@ namespace Distribute std::size_t totalNPCs = 0; for (const auto& actorbase : dataHandler->GetFormArray()) { if (actorbase && !actorbase->IsPlayer() && (!uses_template(actorbase) || actorbase->IsUnique())) { - Distribute(actorbase, false, true); + Distribute(actorbase, PCLevelMult::Input{ actorbase, false, true }); totalNPCs++; } } @@ -174,7 +168,7 @@ namespace Distribute } } -namespace Distribute::DeathItem +namespace Distribute::Event { struct detail //AddObjectToContainer doesn't work with leveled items :s { @@ -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(GetSingleton()); + logger::info("\tRegistered for {}", typeid(RE::TESFormDeleteEvent).name()); + if (Forms::deathItems) { + scripts->AddEventSink(GetSingleton()); + logger::info("\tRegistered for {}", typeid(RE::TESDeathEvent).name()); + } } } @@ -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(); - 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(*actorBase, Forms::deathItems, input, [&](auto* a_deathItem, IdxOrCount a_count) { + for_each_form(*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; }); @@ -222,33 +216,39 @@ namespace Distribute::DeathItem return EventResult::kContinue; } + + EventResult Manager::ProcessEvent(const RE::TESFormDeleteEvent* a_event, RE::BSTEventSource*) + { + 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(a_this, -0x30); actorbase) { - Distribute(actorbase, false, false); - } + Distribute(a_npc, PCLevelMult::Input{ a_this, a_npc, false, false }); } static inline REL::Relocation 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(); - logger::info(" Hooked leveled actor init"); + stl::write_vfunc(); + logger::info("\tHooked leveled actor init"); } } diff --git a/SPID/src/DistributePCLevelMult.cpp b/SPID/src/DistributePCLevelMult.cpp index 6a0dcd2..a12da84 100644 --- a/SPID/src/DistributePCLevelMult.cpp +++ b/SPID/src/DistributePCLevelMult.cpp @@ -6,126 +6,125 @@ namespace Distribute { namespace PlayerLeveledActor { - struct UpdateAutoCalcNPCs + struct HandleUpdatePlayerLevel { - static void thunk(RE::TESDataHandler* a_dataHandler) + static void thunk(RE::Actor* a_actor) { - func(a_dataHandler); + if (const auto npc = a_actor->GetActorBase(); npc && npc->HasPCLevelMult()) { + const auto input = npc->IsDynamicForm() ? PCLevelMult::Input{ a_actor, npc, true, false } : // use character formID for permanent storage + PCLevelMult::Input{ npc, true, false }; + Distribute(npc, input); + } - ApplyToPCLevelMultNPCs(a_dataHandler); + func(a_actor); } static inline REL::Relocation func; }; struct LoadGame { - static void thunk(RE::TESNPC* a_this, std::uintptr_t a_buf) + static void thunk(RE::Character* a_this, std::uintptr_t a_buf) { - func(a_this, a_buf); - - if (!a_this->HasPCLevelMult()) { - return; - } + if (const auto actorbase = a_this->GetActorBase(); actorbase && actorbase->HasPCLevelMult()) { + const auto input = actorbase->IsDynamicForm() ? PCLevelMult::Input{ a_this, actorbase, true, false } : // use character formID for permanent storage + PCLevelMult::Input{ actorbase, true, false }; - const PCLevelMult::Input input{ - a_this, - true, - false - }; - - const auto pcLevelMultManager = PCLevelMult::Manager::GetSingleton(); - - if (!pcLevelMultManager->FindDistributedEntry(input)) { //start distribution for first time - Distribute(a_this, true, false); - } else { - pcLevelMultManager->ForEachDistributedEntry(input, [&](RE::TESForm& a_form, [[maybe_unused]] IdxOrCount a_count, bool a_isBelowLevel) { //handle redistribution and removal - switch (a_form.GetFormType()) { - case RE::FormType::Keyword: - { - auto keyword = a_form.As(); - if (a_isBelowLevel) { - a_this->RemoveKeyword(keyword); - } else { - a_this->AddKeyword(keyword); - } - } - break; - case RE::FormType::Faction: - { - auto faction = a_form.As(); - auto it = std::ranges::find_if(a_this->factions, [&](const auto& factionRank) { - return factionRank.faction == faction; - }); - if (it != a_this->factions.end()) { + if (const auto pcLevelMultManager = PCLevelMult::Manager::GetSingleton(); !pcLevelMultManager->FindDistributedEntry(input)) { + //start distribution for first time + Distribute(actorbase, input); + } else { + //handle redistribution and removal + pcLevelMultManager->ForEachDistributedEntry(input, [&](RE::TESForm& a_form, [[maybe_unused]] IdxOrCount a_count, bool a_isBelowLevel) { + switch (a_form.GetFormType()) { + case RE::FormType::Keyword: + { + auto keyword = a_form.As(); if (a_isBelowLevel) { - (*it).rank = -1; + actorbase->RemoveKeyword(keyword); } else { - (*it).rank = 1; + actorbase->AddKeyword(keyword); } } - } - break; - case RE::FormType::Perk: - { - auto perk = a_form.As(); - if (a_isBelowLevel) { - a_this->RemovePerk(perk); - } else { - a_this->AddPerk(perk, 1); - } - } - break; - case RE::FormType::Spell: - { - auto spell = a_form.As(); - if (auto actorEffects = a_this->GetSpellList()) { - if (a_isBelowLevel) { - actorEffects->RemoveSpell(spell); - } else if (!actorEffects->GetIndex(spell)) { - actorEffects->AddSpell(spell); + break; + case RE::FormType::Faction: + { + auto faction = a_form.As(); + auto it = std::ranges::find_if(actorbase->factions, [&](const auto& factionRank) { + return factionRank.faction == faction; + }); + if (it != actorbase->factions.end()) { + if (a_isBelowLevel) { + (*it).rank = -1; + } else { + (*it).rank = 1; + } } } - } - break; - case RE::FormType::LeveledSpell: - { - auto spell = a_form.As(); - if (auto actorEffects = a_this->GetSpellList()) { + break; + case RE::FormType::Perk: + { + auto perk = a_form.As(); if (a_isBelowLevel) { - actorEffects->RemoveLevSpell(spell); + actorbase->RemovePerk(perk); } else { - actorEffects->AddLevSpell(spell); + actorbase->AddPerk(perk, 1); } } - } - break; - case RE::FormType::Shout: - { - auto shout = a_form.As(); - if (auto actorEffects = a_this->GetSpellList()) { - if (a_isBelowLevel) { - actorEffects->RemoveShout(shout); - } else { - actorEffects->AddShout(shout); + break; + case RE::FormType::Spell: + { + auto spell = a_form.As(); + if (auto actorEffects = actorbase->GetSpellList()) { + if (a_isBelowLevel) { + actorEffects->RemoveSpell(spell); + } else if (!actorEffects->GetIndex(spell)) { + actorEffects->AddSpell(spell); + } } } - } - break; - default: - { - if (a_form.IsInventoryObject()) { - auto boundObject = static_cast(&a_form); - if (a_isBelowLevel) { - a_this->RemoveObjectFromContainer(boundObject, a_count); - } else if (a_this->CountObjectsInContainer(boundObject) < a_count) { - a_this->AddObjectToContainer(boundObject, a_count, a_this); + break; + case RE::FormType::LeveledSpell: + { + auto spell = a_form.As(); + if (auto actorEffects = actorbase->GetSpellList()) { + if (a_isBelowLevel) { + actorEffects->RemoveLevSpell(spell); + } else { + actorEffects->AddLevSpell(spell); + } + } + } + break; + case RE::FormType::Shout: + { + auto shout = a_form.As(); + if (auto actorEffects = actorbase->GetSpellList()) { + if (a_isBelowLevel) { + actorEffects->RemoveShout(shout); + } else { + actorEffects->AddShout(shout); + } + } + } + break; + default: + { + if (a_form.IsInventoryObject()) { + auto boundObject = static_cast(&a_form); + if (a_isBelowLevel) { + actorbase->RemoveObjectFromContainer(boundObject, a_count); + } else if (actorbase->CountObjectsInContainer(boundObject) < a_count) { + actorbase->AddObjectToContainer(boundObject, a_count, a_this); + } } } + break; } - break; - } - }); + }); + } } + + func(a_this, a_buf); } static inline REL::Relocation func; @@ -135,20 +134,13 @@ namespace Distribute void Install() { - REL::Relocation target{ RELOCATION_ID(40560, 41567), OFFSET(0xB2, 0xC2) }; // SetLevel - stl::write_thunk_call(target.address()); + // ProcessLists::HandleUpdate + // inlined into SetLevel in AE + REL::Relocation target{ RELOCATION_ID(40575, 41567), OFFSET(0x86, 0x137) }; + stl::write_thunk_call(target.address()); - stl::write_vfunc(); + stl::write_vfunc(); logger::info(" Hooked npc load save"); } } - - void ApplyToPCLevelMultNPCs(RE::TESDataHandler* a_dataHandler) - { - for (const auto& actorbase : a_dataHandler->GetFormArray()) { - if (actorbase && actorbase->HasPCLevelMult()) { - Distribute(actorbase, true, false); - } - } - } } diff --git a/SPID/src/KeywordDependencies.cpp b/SPID/src/KeywordDependencies.cpp index 19ab022..f06f0e5 100644 --- a/SPID/src/KeywordDependencies.cpp +++ b/SPID/src/KeywordDependencies.cpp @@ -174,9 +174,9 @@ void Dependencies::ResolveKeywords() Forms::keywords.forms.emplace_back(keywordData); } - logger::info(" Keywords have been sorted: "); + logger::info("\tKeywords have been sorted: "); for (const auto& keywordData : Forms::keywords.forms) { - logger::info(" {} [0x{:X}]", keywordData.form->GetFormEditorID(), keywordData.form->GetFormID()); + logger::info("\t\t{} [0x{:X}]", keywordData.form->GetFormEditorID(), keywordData.form->GetFormID()); } } diff --git a/SPID/src/LookupConfigs.cpp b/SPID/src/LookupConfigs.cpp index b5fd061..0010b0b 100644 --- a/SPID/src/LookupConfigs.cpp +++ b/SPID/src/LookupConfigs.cpp @@ -11,7 +11,7 @@ bool INI::Read() return false; } - logger::info(" {} matching inis found", files.size()); + logger::info("\t{} matching inis found", files.size()); //initialize map for (size_t i = 0; i < RECORD::kTotal; i++) { @@ -19,14 +19,14 @@ bool INI::Read() } for (auto& path : files) { - logger::info(" INI : {}", path); + logger::info("\tINI : {}", path); CSimpleIniA ini; ini.SetUnicode(); ini.SetMultiKey(); if (const auto rc = ini.LoadFile(path.c_str()); rc < 0) { - logger::error(" couldn't read INI"); + logger::error("\t\tcouldn't read INI"); continue; } @@ -41,12 +41,12 @@ bool INI::Read() oldFormatMap.emplace(key, std::make_pair(entry, *sanitized_str)); } } catch (...) { - logger::warn(" Failed to parse entry [{} = {}]", key.pItem, entry); + logger::warn("\t\tFailed to parse entry [{} = {}]", key.pItem, entry); } } if (!oldFormatMap.empty()) { - logger::info(" sanitizing {} entries", oldFormatMap.size()); + logger::info("\t\tsanitizing {} entries", oldFormatMap.size()); for (auto& [key, entry] : oldFormatMap) { auto& [original, sanitized] = entry; diff --git a/SPID/src/LookupForms.cpp b/SPID/src/LookupForms.cpp index 9a1427c..6bad18d 100644 --- a/SPID/src/LookupForms.cpp +++ b/SPID/src/LookupForms.cpp @@ -38,7 +38,7 @@ bool Lookup::GetForms() const auto list_lookup_result = [&](const RECORD::TYPE a_recordType, Distributables& a_map) { const auto& recordName = RECORD::add[a_recordType]; - logger::info(" Adding {}/{} {}(s)", a_map.forms.size(), INI::configs[recordName].size(), recordName); + logger::info("\tAdding {}/{} {}(s)", a_map.forms.size(), INI::configs[recordName].size(), recordName); }; list_lookup_result(RECORD::kKeyword, keywords); diff --git a/SPID/src/PCLevelMultManager.cpp b/SPID/src/PCLevelMultManager.cpp index 07b12d2..1ec52dd 100644 --- a/SPID/src/PCLevelMultManager.cpp +++ b/SPID/src/PCLevelMultManager.cpp @@ -1,13 +1,23 @@ #include "PCLevelMultManager.h" +#include "Distribute.h" #include "DistributePCLevelMult.h" namespace PCLevelMult { - Input::Input(const RE::TESNPC* a_npc, bool a_onlyPlayerLevelEntries, bool a_noPlayerLevelDistribution) : + Input::Input(const RE::TESNPC* a_base, bool a_onlyPlayerLevelEntries, bool a_noPlayerLevelDistribution) : playerID(Manager::GetSingleton()->GetCurrentPlayerID()), - npcFormID(a_npc->GetFormID()), - npcLevel(a_npc->GetLevel()), - npcLevelCap(a_npc->actorData.calcLevelMax), + npcFormID(a_base->GetFormID()), + npcLevel(a_base->GetLevel()), + npcLevelCap(a_base->actorData.calcLevelMax), + onlyPlayerLevelEntries(a_onlyPlayerLevelEntries), + noPlayerLevelDistribution(a_noPlayerLevelDistribution) + {} + + Input::Input(const RE::Actor* a_character, const RE::TESNPC* a_base, bool a_onlyPlayerLevelEntries, bool a_noPlayerLevelDistribution) : + playerID(Manager::GetSingleton()->GetCurrentPlayerID()), + npcFormID(a_character->GetFormID()), + npcLevel(a_base->GetLevel()), + npcLevelCap(a_base->actorData.calcLevelMax), onlyPlayerLevelEntries(a_onlyPlayerLevelEntries), noPlayerLevelDistribution(a_noPlayerLevelDistribution) {} @@ -16,7 +26,7 @@ namespace PCLevelMult { if (auto UI = RE::UI::GetSingleton()) { UI->AddEventSink(this); - logger::info(" Registered {}"sv, typeid(Manager).name()); + logger::info("\tRegistered {}", typeid(Manager).name()); } } @@ -33,7 +43,11 @@ namespace PCLevelMult currentPlayerID = newPlayerID; if (const auto dataHandler = RE::TESDataHandler::GetSingleton()) { - Distribute::ApplyToPCLevelMultNPCs(dataHandler); + for (const auto& npc : dataHandler->GetFormArray()) { + if (npc && npc->HasPCLevelMult()) { + Distribute::Distribute(npc, Input{ npc, true, false }); + } + } } } else if (oldPlayerID != newPlayerID) { remap_player_ids(oldPlayerID, newPlayerID); @@ -85,13 +99,13 @@ namespace PCLevelMult for (auto& [playerID, npcFormIDs] : cache) { logger::info("PlayerID : {:X}", playerID); for (auto& [npcFormID, levelMap] : npcFormIDs) { - logger::info(" NPC : {} [{:X}]", Cache::EditorID::GetEditorID(npcFormID), npcFormID); + logger::info("\tNPC : {} [{:X}]", Cache::EditorID::GetEditorID(npcFormID), npcFormID); for (auto& [level, distFormMap] : levelMap.entries) { - logger::info(" Level : {}", level); + logger::info("\t\tLevel : {}", level); for (auto& [distFormID, idxSet] : distFormMap.rejectedEntries) { - logger::info(" Dist FormID : {} [{:X}]", Cache::EditorID::GetEditorID(distFormID), distFormID); + logger::info("\t\t\tDist FormID : {} [{:X}]", Cache::EditorID::GetEditorID(distFormID), distFormID); for (auto& idx : idxSet) { - logger::info(" IDX : {}", idx); + logger::info("\t\t\t\tIDX : {}", idx); } } } @@ -145,17 +159,25 @@ namespace PCLevelMult for (auto& [playerID, npcFormIDs] : cache) { logger::info("PlayerID : {:X}", playerID); for (auto& [npcFormID, levelMap] : npcFormIDs) { - logger::info(" NPC : {} [{:X}]", Cache::EditorID::GetEditorID(npcFormID), npcFormID); + logger::info("\tNPC : {} [{:X}]", Cache::EditorID::GetEditorID(npcFormID), npcFormID); for (auto& [level, distFormMap] : levelMap.entries) { - logger::info(" Level : {}", level); + logger::info("\t\tLevel : {}", level); for (auto& [distFormID, idxSet] : distFormMap.distributedEntries) { - logger::info(" Dist FormID : {} [{:X}] : {}", Cache::EditorID::GetEditorID(distFormID), distFormID, idxSet); + logger::info("\t\t\tDist FormID : {} [{:X}] : {}", Cache::EditorID::GetEditorID(distFormID), distFormID, idxSet); } } } } } + void Manager::DeleteNPC(RE::FormID a_characterID) + { + auto& currentCache = cache[GetSingleton()->GetCurrentPlayerID()]; + if (const auto it = currentCache.find(a_characterID); it != currentCache.end()) { + currentCache.erase(it); + } + } + bool Manager::HasHitLevelCap(const Input& a_input) { bool hitCap = (a_input.npcLevel == a_input.npcLevelCap); @@ -196,7 +218,7 @@ namespace PCLevelMult currentPlayerID = string::to_num(save[1], true); } else { logger::info("Loaded non-standard save : {}", a_saveName); - currentPlayerID = 0; // non standard save name, use game playerID instead + currentPlayerID = 0; // non standard save name, use game playerID instead } } diff --git a/SPID/src/main.cpp b/SPID/src/main.cpp index 0b26e06..34f2be7 100644 --- a/SPID/src/main.cpp +++ b/SPID/src/main.cpp @@ -14,7 +14,7 @@ bool DoDistribute() { if (shouldDistribute = Lookup::GetForms(); shouldDistribute) { Distribute::ApplyToNPCs(); - Distribute::DeathItem::Manager::Register(); + Distribute::Event::Manager::Register(); return true; }