2022-06-30 10:31:01 +00:00
|
|
|
#pragma once
|
|
|
|
|
2022-07-05 19:17:52 +00:00
|
|
|
#include <SimpleIni.h>
|
|
|
|
|
2022-07-14 18:36:46 +00:00
|
|
|
using namespace SKSE;
|
|
|
|
using namespace SKSE::log;
|
|
|
|
|
2022-06-30 10:31:01 +00:00
|
|
|
inline void ListRemoveItem(RE::BGSListForm* a_List, RE::TESForm* a_form)
|
|
|
|
{
|
|
|
|
using func_t = decltype(&ListRemoveItem);
|
|
|
|
REL::Relocation<func_t> 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_t> func{ REL::RelocationID(20469, 20912) };
|
|
|
|
return func(a_form);
|
|
|
|
}
|
|
|
|
|
2022-07-04 12:13:47 +00:00
|
|
|
inline std::uint32_t GetItemCount(RE::TESObjectREFR* a_container, RE::TESForm* a_form)
|
2022-07-02 17:04:55 +00:00
|
|
|
{
|
|
|
|
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;
|
2022-12-01 00:39:28 +00:00
|
|
|
return RE::BSContainer::ForEachResult::kStop;
|
2022-07-02 17:04:55 +00:00
|
|
|
}
|
2022-12-01 00:39:28 +00:00
|
|
|
return RE::BSContainer::ForEachResult::kContinue;
|
2022-07-02 17:04:55 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return iResult > 0 ? iResult : 0;
|
|
|
|
}
|
|
|
|
|
2022-07-02 13:57:34 +00:00
|
|
|
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) {
|
2022-07-02 17:04:55 +00:00
|
|
|
if (a_entry.obj && a_entry.obj->formID == a_formID) {
|
2022-07-02 13:57:34 +00:00
|
|
|
iResult += a_entry.count;
|
2022-12-01 00:39:28 +00:00
|
|
|
return RE::BSContainer::ForEachResult::kStop;
|
2022-07-02 13:57:34 +00:00
|
|
|
}
|
2022-12-01 00:39:28 +00:00
|
|
|
return RE::BSContainer::ForEachResult::kContinue;
|
2022-07-02 13:57:34 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return iResult > 0 ? iResult : 0;
|
|
|
|
}
|
|
|
|
|
2022-07-02 17:04:55 +00:00
|
|
|
inline bool RefListHasItem(RE::TESForm* a_refOrList, RE::FormID a_formID)
|
2022-06-30 10:31:01 +00:00
|
|
|
{
|
|
|
|
if (!a_refOrList || !a_formID) {
|
2022-07-14 18:36:46 +00:00
|
|
|
log::warn("Invalid arguments in RefListHasItem");
|
2022-06-30 10:31:01 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto refr = a_refOrList->As<RE::TESObjectREFR>();
|
|
|
|
|
|
|
|
if (refr) {
|
2022-07-02 13:57:34 +00:00
|
|
|
return GetItemCount(refr, a_formID) > 0;
|
2022-06-30 10:31:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const auto list = a_refOrList->As<RE::BGSListForm>();
|
|
|
|
|
|
|
|
if (list) {
|
2022-07-02 16:42:40 +00:00
|
|
|
bool bResult = false;
|
|
|
|
list->ForEachForm([&](RE::TESForm& a_form) {
|
2022-07-02 17:04:55 +00:00
|
|
|
if (&a_form && RefListHasItem(&a_form, a_formID)) {
|
2022-07-02 16:42:40 +00:00
|
|
|
bResult = true;
|
2022-12-01 00:39:28 +00:00
|
|
|
return RE::BSContainer::ForEachResult::kStop;
|
2022-06-30 10:31:01 +00:00
|
|
|
}
|
2022-12-01 00:39:28 +00:00
|
|
|
return RE::BSContainer::ForEachResult::kContinue;
|
2022-07-02 16:42:40 +00:00
|
|
|
});
|
|
|
|
return bResult;
|
2022-06-30 10:31:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-07-02 17:04:55 +00:00
|
|
|
inline bool FollowersHaveItem(RE::TESForm* a_form)
|
2022-06-30 10:31:01 +00:00
|
|
|
{
|
|
|
|
if (const auto processLists = RE::ProcessLists::GetSingleton(); processLists) {
|
|
|
|
for (auto& actorHandle : processLists->highActorHandles) {
|
|
|
|
if (auto actor = actorHandle.get(); actor && actor->IsPlayerTeammate()) {
|
|
|
|
|
2022-07-02 17:04:55 +00:00
|
|
|
if (GetItemCount(actor->As<RE::TESObjectREFR>(), a_form) > 0) {
|
2022-07-02 16:37:52 +00:00
|
|
|
return true;
|
|
|
|
}
|
2022-06-30 10:31:01 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-02 16:37:52 +00:00
|
|
|
return false;
|
2022-07-02 13:57:34 +00:00
|
|
|
}
|
2022-07-04 12:13:47 +00:00
|
|
|
|
|
|
|
inline bool IsInSameCell(RE::TESObjectREFR* ref)
|
|
|
|
{
|
|
|
|
return ref && (ref->GetParentCell() == RE::PlayerCharacter::GetSingleton()->GetParentCell());
|
|
|
|
}
|
2022-07-05 19:17:52 +00:00
|
|
|
|
|
|
|
inline void LoadINI(std::map<std::string, bool>* settings, const char* iniPath)
|
|
|
|
{
|
|
|
|
for (auto it = settings->begin(); it != settings->end(); it++) {
|
2022-07-14 18:36:46 +00:00
|
|
|
log::info("[DEFAULT] {} = {}", it->first, it->second);
|
2022-07-05 19:17:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!std::filesystem::exists(iniPath)) {
|
2022-07-14 18:36:46 +00:00
|
|
|
log::warn("{} does not exist, using default values.", iniPath);
|
2022-07-05 19:17:52 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
CSimpleIniA ini;
|
|
|
|
ini.SetUnicode(false);
|
|
|
|
ini.SetMultiKey(false);
|
|
|
|
ini.LoadFile(iniPath);
|
|
|
|
|
|
|
|
std::list<CSimpleIniA::Entry> 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)));
|
2022-07-14 18:36:46 +00:00
|
|
|
log::info("[INI] {} = {}", k.pItem, settings->at(k.pItem));
|
2022-07-05 19:17:52 +00:00
|
|
|
bExists = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!bExists) {
|
|
|
|
ini.SetBoolValue("", it->first.c_str(), it->second);
|
|
|
|
bUpdateINI = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bUpdateINI) {
|
2022-07-14 18:36:46 +00:00
|
|
|
log::info("New settings detected, adding to ArtifactTracker.ini");
|
2022-07-05 19:17:52 +00:00
|
|
|
ini.SaveFile(iniPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (const std::exception& e) {
|
2022-09-16 00:39:07 +00:00
|
|
|
log::error("{}", e.what());
|
2022-07-05 19:17:52 +00:00
|
|
|
}
|
2022-07-07 23:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void RunBenchmark(std::function<void()> 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<double>(end - start);
|
2022-07-14 18:36:46 +00:00
|
|
|
log::info("{}: Elapsed time: {} seconds", desc, elapsed.count());
|
2022-07-11 11:41:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline std::vector<RE::Actor*> GetPlayerFollowers()
|
|
|
|
{
|
|
|
|
std::vector<RE::Actor*> 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;
|
|
|
|
}
|