#pragma once #include using namespace SKSE; using namespace SKSE::log; inline void ListRemoveItem(RE::BGSListForm* a_List, RE::TESForm* a_form) { using func_t = decltype(&ListRemoveItem); REL::Relocation func{ REL::RelocationID(20471, 20914) }; return func(a_List, a_form); } inline void ListRevert(RE::BGSListForm* a_form) { using func_t = decltype(&ListRevert); REL::Relocation func{ REL::RelocationID(20469, 20912) }; return func(a_form); } inline std::uint32_t GetItemCount(RE::TESObjectREFR* a_container, RE::TESForm* a_form) { std::int32_t iResult = 0; auto invChanges = a_container->GetInventoryChanges(); if (invChanges && invChanges->entryList) { for (auto& entry : *invChanges->entryList) { if (entry && entry->object == a_form) { if (entry->IsLeveled()) { return entry->countDelta > 0 ? entry->countDelta : 0; } else { iResult = entry->countDelta; break; } } } } auto container = a_container->GetContainer(); if (container) { container->ForEachContainerObject([&](RE::ContainerObject& a_entry) { if (a_entry.obj == a_form) { iResult += a_entry.count; return false; } return true; }); } return iResult > 0 ? iResult : 0; } inline std::int32_t GetItemCount(RE::TESObjectREFR* a_container, RE::FormID a_formID) { std::int32_t iResult = 0; auto invChanges = a_container->GetInventoryChanges(); if (invChanges && invChanges->entryList) { for (auto& entry : *invChanges->entryList) { if (entry && entry->object && entry->object->formID == a_formID) { if (entry->IsLeveled()) { return entry->countDelta > 0 ? entry->countDelta : 0; } else { iResult = entry->countDelta; break; } } } } auto container = a_container->GetContainer(); if (container) { container->ForEachContainerObject([&](RE::ContainerObject& a_entry) { if (a_entry.obj && a_entry.obj->formID == a_formID) { iResult += a_entry.count; return false; } return true; }); } return iResult > 0 ? iResult : 0; } inline bool RefListHasItem(RE::TESForm* a_refOrList, RE::FormID a_formID) { if (!a_refOrList || !a_formID) { log::warn("Invalid arguments in RefListHasItem"); return false; } const auto refr = a_refOrList->As(); if (refr) { return GetItemCount(refr, a_formID) > 0; } const auto list = a_refOrList->As(); if (list) { bool bResult = false; list->ForEachForm([&](RE::TESForm& a_form) { if (&a_form && RefListHasItem(&a_form, a_formID)) { bResult = true; return false; } return true; }); return bResult; } return false; } inline bool FollowersHaveItem(RE::TESForm* a_form) { if (const auto processLists = RE::ProcessLists::GetSingleton(); processLists) { for (auto& actorHandle : processLists->highActorHandles) { if (auto actor = actorHandle.get(); actor && actor->IsPlayerTeammate()) { if (GetItemCount(actor->As(), a_form) > 0) { return true; } } } } return false; } inline bool IsInSameCell(RE::TESObjectREFR* ref) { return ref && (ref->GetParentCell() == RE::PlayerCharacter::GetSingleton()->GetParentCell()); } inline void LoadINI(std::map* settings, const char* iniPath) { for (auto it = settings->begin(); it != settings->end(); it++) { log::info("[DEFAULT] {} = {}", it->first, it->second); } if (!std::filesystem::exists(iniPath)) { log::warn("{} does not exist, using default values.", iniPath); return; } try { CSimpleIniA ini; ini.SetUnicode(false); ini.SetMultiKey(false); ini.LoadFile(iniPath); std::list keysList; ini.GetAllKeys("", keysList); bool bUpdateINI = false; for (auto it = settings->begin(); it != settings->end(); it++) { bool bExists = false; for (const auto& k : keysList) { if (it->first == k.pItem) { settings->insert_or_assign(k.pItem, ini.GetBoolValue("", k.pItem, settings->at(k.pItem))); log::info("[INI] {} = {}", k.pItem, settings->at(k.pItem)); bExists = true; break; } } if (!bExists) { ini.SetBoolValue("", it->first.c_str(), it->second); bUpdateINI = true; } } if (bUpdateINI) { log::info("New settings detected, adding to ArtifactTracker.ini"); ini.SaveFile(iniPath); } } catch (const std::exception& e) { log::error(e.what()); } } inline void RunBenchmark(std::function benchmark, std::string desc) { const auto start = std::chrono::high_resolution_clock::now(); benchmark(); const auto end = std::chrono::high_resolution_clock::now(); const auto elapsed = std::chrono::duration(end - start); log::info("{}: Elapsed time: {} seconds", desc, elapsed.count()); } inline std::vector GetPlayerFollowers() { std::vector result; if (const auto processLists = RE::ProcessLists::GetSingleton(); processLists) { for (auto& actorHandle : processLists->highActorHandles) { if (auto actor = actorHandle.get(); actor && actor->IsPlayerTeammate()) { result.push_back(actor.get()); } } } return result; }