diff --git a/Artifact Tracker.esp b/Artifact Tracker.esp index b2cb0cb..9284361 100644 Binary files a/Artifact Tracker.esp and b/Artifact Tracker.esp differ diff --git a/SKSE/Plugins/ArtifactTrackerFunctions.dll b/SKSE/Plugins/ArtifactTrackerFunctions.dll index dde0cb7..ac40201 100644 Binary files a/SKSE/Plugins/ArtifactTrackerFunctions.dll and b/SKSE/Plugins/ArtifactTrackerFunctions.dll differ diff --git a/Scripts/ETR_Functions.pex b/Scripts/ETR_Functions.pex index 99cf804..b4f5e98 100644 Binary files a/Scripts/ETR_Functions.pex and b/Scripts/ETR_Functions.pex differ diff --git a/Scripts/ETR_TrackFoundItems.pex b/Scripts/ETR_TrackFoundItems.pex deleted file mode 100644 index 185b203..0000000 Binary files a/Scripts/ETR_TrackFoundItems.pex and /dev/null differ diff --git a/Scripts/ETR_TrackNewItems.pex b/Scripts/ETR_TrackNewItems.pex deleted file mode 100644 index 16f38d8..0000000 Binary files a/Scripts/ETR_TrackNewItems.pex and /dev/null differ diff --git a/Scripts/ETR_TrackStoredItems.pex b/Scripts/ETR_TrackStoredItems.pex index 993939b..5f9775e 100644 Binary files a/Scripts/ETR_TrackStoredItems.pex and b/Scripts/ETR_TrackStoredItems.pex differ diff --git a/Source/ArtifactTrackerDLL/CMakeLists.txt b/Source/ArtifactTrackerDLL/CMakeLists.txt index e77bcc9..3d87346 100644 --- a/Source/ArtifactTrackerDLL/CMakeLists.txt +++ b/Source/ArtifactTrackerDLL/CMakeLists.txt @@ -23,8 +23,9 @@ configure_file( set(sources src/Main.cpp src/Papyrus.cpp + src/ArtifactTracker.cpp + src/EventListener.cpp src/BookCheck.cpp - src/MiscCheck.cpp ${CMAKE_CURRENT_BINARY_DIR}/version.rc) diff --git a/Source/ArtifactTrackerDLL/src/ArtifactTracker.cpp b/Source/ArtifactTrackerDLL/src/ArtifactTracker.cpp new file mode 100644 index 0000000..985007d --- /dev/null +++ b/Source/ArtifactTrackerDLL/src/ArtifactTracker.cpp @@ -0,0 +1,321 @@ +#include "ArtifactTracker.h" +#include "BookCheck.h" +#include "EventListener.h" +#include "Util.h" + +namespace ArtifactTracker +{ + RE::TESGlobal* notifyNewArtifact; + RE::TESBoundObject* cellContainer; + RE::BGSListForm* listNew; + RE::BGSListForm* listStored; + RE::BGSListForm* listFound; + RE::BGSListForm* persistentStorage; + RE::BGSKeyword* homeKeyword; + bool bAtHome; + std::unordered_map validMisc; + std::unordered_map validBooks; + + void Init() + { + EventListener::Install(); + + const auto dataHandler = RE::TESDataHandler::GetSingleton(); + + notifyNewArtifact = dataHandler->LookupForm(0x809, "Artifact Tracker.esp"); + cellContainer = dataHandler->LookupForm(0x800, "Artifact Tracker.esp")->As(); + + listNew = dataHandler->LookupForm(0x803, "Artifact Tracker.esp"); + listStored = dataHandler->LookupForm(0x805, "Artifact Tracker.esp"); + listFound = dataHandler->LookupForm(0x806, "Artifact Tracker.esp"); + persistentStorage = dataHandler->LookupForm(0x807, "Artifact Tracker.esp"); + + homeKeyword = dataHandler->LookupForm(0xFC1A3, "Skyrim.esm"); + + if (!cellContainer) { + SKSE::log::warn("cellContainer is empty"); + } + + if (!persistentStorage) { + SKSE::log::warn("persistentStorage is empty"); + } + + // Preloading item lists + const RE::BGSKeyword* recipeKeyword = dataHandler->LookupForm(0xF5CB0, "Skyrim.esm"); // VendorItemRecipe + + for (const auto& form : dataHandler->GetFormArray()) { + if (form && !form->TeachesSpell() && (!form->HasKeyword(recipeKeyword) || BookCheck::IsBook(form))) { + validBooks[form->formID] = form; + } + } + + RE::BGSListForm* excludeKeywords = dataHandler->LookupForm(0x801, "Artifact Tracker.esp"); // ETR_ExcludeMiscKeywords + + for (const auto& form : dataHandler->GetFormArray()) { + if (form->GetPlayable() && !form->IsGold() && !form->IsLockpick() && !form->HasKeywordInList(excludeKeywords, false)) { + validMisc[form->formID] = form; + } + } + } + + bool IsArtifact(RE::TESForm* a_form) + { + switch (a_form->GetFormType()) { + case RE::FormType::Armor: + return a_form->GetPlayable(); + case RE::FormType::Weapon: + return a_form->GetPlayable() && a_form->formID != 0x000001F4; + case RE::FormType::Book: + return validBooks.contains(a_form->formID); + case RE::FormType::Misc: + return validMisc.contains(a_form->formID); + default: + return false; + } + } + + bool SetHomeLocation(RE::BGSLocation* a_location = NULL) + { + bAtHome = a_location && a_location->HasKeyword(homeKeyword); + return bAtHome; + } + + RE::TESObjectREFR* GetCellStorage( + RE::TESObjectREFR* a_ref, + RE::BGSListForm* a_refList, + RE::TESBoundObject* a_objectToCreate, + bool a_autoCreate) + { + RE::TESObjectREFR* result = NULL; + + if (!a_ref || !a_refList || !a_objectToCreate) { + SKSE::log::warn("Invalid arguments in GetCellStorage"); + return result; + } + + RE::TESObjectCELL* cell = a_ref->GetParentCell(); + + a_refList->ForEachForm([&result, &cell, &a_objectToCreate](RE::TESForm& a_exform) { + const auto ref = a_exform.As(); + + if (ref && ref->GetParentCell() == cell && ref->GetBaseObject()->formID == a_objectToCreate->formID) { + result = ref; + return false; + } + + return true; + }); + + if (!result && a_autoCreate) { + result = a_ref->PlaceObjectAtMe(a_objectToCreate, true).get(); + result->Disable(); + a_refList->AddForm(result); + } + + if (!result) { + SKSE::log::warn("Failed to find or create cell storage in GetCellStorage"); + } + + return result; + } + + void SyncCellStorage(RE::TESObjectREFR* a_cellStorage, RE::BGSListForm* a_excludeContainers) + { + if (!a_cellStorage || !a_excludeContainers) { + SKSE::log::warn("Invalid arguments in SyncCellStorage"); + return; + } + + std::unordered_map cellItems; + + const auto cell = a_cellStorage->GetParentCell(); + const auto inv = a_cellStorage->GetInventory(); + + for (const RE::NiPointer a_ref : cell->references) { + const auto baseObj = a_ref->GetBaseObject(); + + if (baseObj->formType == RE::FormType::Container || (baseObj->formType == RE::FormType::NPC && !a_ref->IsDisabled() && baseObj->As()->GetRace()->formID == 0x0010760A)) { + if (a_excludeContainers->HasForm(a_ref->formID) || baseObj->formID == 0xDC9E7) { // skip persistent and PlayerBookShelfContainer + continue; + } + + const auto contInv = a_ref->GetInventory([&](RE::TESBoundObject& a_object) -> bool { + return !cellItems.contains(a_object.formID); + }); + + for (const auto& [item, data] : contInv) { + if (data.first > 0) { + cellItems[item->formID] = true; + if (inv.find(item) == inv.end()) { + if (IsArtifact(item)) { + a_cellStorage->AddObjectToContainer(item, nullptr, 1, nullptr); + } + } + } + } + + continue; + } + + if (a_ref->IsMarkedForDeletion()) { + SKSE::log::info("found marked for deletion"); + } + + if (a_ref->IsDisabled() || a_ref->IsMarkedForDeletion()) { + continue; + } + + if (cellItems.contains(baseObj->formID)) { + continue; + } + + cellItems[baseObj->formID] = true; + + if (!IsArtifact(baseObj)) { + continue; + } + + if (inv.find(baseObj) == inv.end()) { + a_cellStorage->AddObjectToContainer(baseObj, nullptr, 1, nullptr); + } + } + + for (const auto& [item, data] : inv) { + const auto& [count, entry] = data; + if (count > 0 && !cellItems.contains(item->formID)) { + a_cellStorage->RemoveItem(item, count, RE::ITEM_REMOVE_REASON::kRemove, nullptr, nullptr); + } + } + + cellItems.clear(); + } + + void OnItemPickup(RE::TESBoundObject* form) + { + RE::TESObjectREFR* cellStorage = GetCellStorage(RE::PlayerCharacter::GetSingleton(), persistentStorage, cellContainer, true); + + SyncCellStorage(cellStorage, persistentStorage); + + if (!RefHasItem(cellStorage, form) && !GetItemCountInList(persistentStorage, form)) { + RemoveListItem(listStored, form); + listFound->AddForm(form); + } + } + + void OnContainerChanged(const RE::TESContainerChangedEvent* a_event) + { + if (a_event->newContainer == 0x14) { + return; + RE::TESBoundObject* form = RE::TESForm::LookupByID(a_event->baseObj); + + if (!form || !IsArtifact(form)) { + return; + } + + if (listFound->HasForm(form) || listStored->HasForm(form)) { + return; + } + + if (listNew->HasForm(form)) { + + RemoveListItem(listNew, form); + + listFound->AddForm(form); + + if (notifyNewArtifact->value) { + const auto itemName = form->GetName(); + char* notificationText = new char[strlen("New artifact acquired: ") + strlen(itemName) + 1]; + strcpy(notificationText, "New artifact acquired: "); + strcat(notificationText, itemName); + RE::DebugNotification(notificationText); + delete[] notificationText; + } + + } + + } else if (a_event->oldContainer == 0x14) { + + RE::TESBoundObject* form = RE::TESForm::LookupByID(a_event->baseObj); + + SKSE::log::info("{}", form->GetName()); + if (!form || !IsArtifact(form)) { + return; + } + + if (!a_event->newContainer) { + if (bAtHome && a_event->reference) { // dropped at home + RE::TESObjectREFR* cellStorage = GetCellStorage(RE::PlayerCharacter::GetSingleton(), persistentStorage, cellContainer, true); + if (!RefHasItem(cellStorage, form)) { + cellStorage->AddObjectToContainer(form, nullptr, 1, nullptr); + } + if (listFound->HasForm(form)) { + RemoveListItem(listFound, form); + } + listStored->AddForm(form); + RE::DebugNotification("added to stored"); + return; + } + + if (listStored->HasForm(form)) { + return; + } + + if (!RefHasItem(RE::PlayerCharacter::GetSingleton(), form) && !FollowersHaveItem(form)) { + if (listFound->HasForm(form)) { + RemoveListItem(listFound, form); + } + listNew->AddForm(form); + RE::DebugNotification("added to new"); + } + + return; + } + + bool bPersistent = false; + for (const auto& ref : persistentStorage->forms) { + if (ref && ref->formID == a_event->newContainer) { + bPersistent = true; + break; + } + } + + if (!bPersistent && !bAtHome) { + if (!RefHasItem(RE::PlayerCharacter::GetSingleton(), form) && !FollowersHaveItem(form->As())) { + if (listFound->HasForm(form)) { + RemoveListItem(listFound, form); + } + listNew->AddForm(form); + RE::DebugNotification("added to new"); + } + return; + } + + if (listStored->HasForm(form)) { + return; + } + + if (!bPersistent && bAtHome) { + RE::TESObjectREFR* targetContainer = RE::TESForm::LookupByID(a_event->newContainer); + + if (!targetContainer || targetContainer->GetParentCell() != RE::PlayerCharacter::GetSingleton()->GetParentCell()) { + return; + } + + if (targetContainer->GetBaseObject()->Is(RE::FormType::NPC) && targetContainer->GetBaseObject()->As()->GetRace()->formID != 0x0010760A) { + return; + } + + RE::TESObjectREFR* cellStorage = GetCellStorage(RE::PlayerCharacter::GetSingleton(), persistentStorage, cellContainer, true); + cellStorage->AddObjectToContainer(form->As(), nullptr, 1, nullptr); + RE::DebugNotification("updated cellStorage"); + } + + if (listFound->HasForm(form)) { + RemoveListItem(listFound, form); + } + + listStored->AddForm(form); + RE::DebugNotification("added to stored"); + } + } +} diff --git a/Source/ArtifactTrackerDLL/src/ArtifactTracker.h b/Source/ArtifactTrackerDLL/src/ArtifactTracker.h new file mode 100644 index 0000000..35c2322 --- /dev/null +++ b/Source/ArtifactTrackerDLL/src/ArtifactTracker.h @@ -0,0 +1,29 @@ +#pragma once + +namespace ArtifactTracker +{ + extern RE::TESGlobal* notifyNewArtifact; + extern RE::TESBoundObject* cellContainer; + extern RE::BGSListForm* listNew; + extern RE::BGSListForm* listStored; + extern RE::BGSListForm* listFound; + extern RE::BGSListForm* persistentStorage; + extern RE::BGSKeyword* homeKeyword; + extern bool bAtHome; + extern std::unordered_map validMisc; + extern std::unordered_map validBooks; + + void Init(); + + bool IsArtifact(RE::TESForm* a_item); + + bool SetHomeLocation(RE::BGSLocation* a_location); + + RE::TESObjectREFR* GetCellStorage(RE::TESObjectREFR* a_ref, RE::BGSListForm* a_refList, RE::TESBoundObject* a_objectToCreate, bool a_autoCreate = true); + + void SyncCellStorage(RE::TESObjectREFR* a_cellStorage, RE::BGSListForm* a_excludeContainers); + + void OnItemPickup(RE::TESBoundObject* form); + + void OnContainerChanged(const RE::TESContainerChangedEvent* a_event); +} diff --git a/Source/ArtifactTrackerDLL/src/BookCheck.cpp b/Source/ArtifactTrackerDLL/src/BookCheck.cpp index 73038a1..a0d78c6 100644 --- a/Source/ArtifactTrackerDLL/src/BookCheck.cpp +++ b/Source/ArtifactTrackerDLL/src/BookCheck.cpp @@ -3,33 +3,6 @@ // Mostly borrowed from Fix Note icon for SkyUI by 0xC0000005 namespace BookCheck { - std::unordered_map validBooks; - - void PreloadBookList() - { - const auto dataHandler = RE::TESDataHandler::GetSingleton(); - - if (!dataHandler) { - return; - } - - const RE::BGSKeyword* recipeKeyword = dataHandler->LookupForm(0xF5CB0, "Skyrim.esm"); // VendorItemRecipe - - for (const auto& form : dataHandler->GetFormArray()) { - if (!form || form->TeachesSpell()) { - continue; - } - if (form->HasKeyword(recipeKeyword) || IsBook(form)) { - validBooks[form->formID] = form; - } - } - } - - std::unordered_map GetBookList() - { - return validBooks; - } - const char* ExtractFileName(const char* const path, size_t& fileNameLength) { if (!path) { diff --git a/Source/ArtifactTrackerDLL/src/BookCheck.h b/Source/ArtifactTrackerDLL/src/BookCheck.h index 475df3c..4249a60 100644 --- a/Source/ArtifactTrackerDLL/src/BookCheck.h +++ b/Source/ArtifactTrackerDLL/src/BookCheck.h @@ -3,10 +3,6 @@ // Mostly borrowed from Fix Note icon for SkyUI by 0xC0000005 namespace BookCheck { - void PreloadBookList(); - - std::unordered_map GetBookList(); - const char* ExtractFileName(const char* const path, size_t& fileNameLength); // Performs a case-sensitive search for 'needle' in the specified string, diff --git a/Source/ArtifactTrackerDLL/src/EventListener.cpp b/Source/ArtifactTrackerDLL/src/EventListener.cpp new file mode 100644 index 0000000..961a59a --- /dev/null +++ b/Source/ArtifactTrackerDLL/src/EventListener.cpp @@ -0,0 +1,38 @@ +#include "EventListener.h" +#include "ArtifactTracker.h" + + +auto EventListener::GetSingleton() -> EventListener* +{ + static EventListener singleton{}; + return std::addressof(singleton); +} + +void EventListener::Install() +{ + const auto eventSource = RE::ScriptEventSourceHolder::GetSingleton(); + eventSource->AddEventSink(EventListener::GetSingleton()); + eventSource->AddEventSink(EventListener::GetSingleton()); +} + +auto EventListener::ProcessEvent( + const RE::TESActorLocationChangeEvent* a_event, + RE::BSTEventSource* a_eventSource) + -> RE::BSEventNotifyControl +{ + if (a_event->actor && a_event->actor->IsPlayerRef()) { + ArtifactTracker::SetHomeLocation(a_event->newLoc); + } + + return RE::BSEventNotifyControl::kContinue; +} + +auto EventListener::ProcessEvent( + const RE::TESContainerChangedEvent* a_event, + RE::BSTEventSource* a_eventSource) + -> RE::BSEventNotifyControl +{ + ArtifactTracker::OnContainerChanged(a_event); + + return RE::BSEventNotifyControl::kContinue; +} \ No newline at end of file diff --git a/Source/ArtifactTrackerDLL/src/EventListener.h b/Source/ArtifactTrackerDLL/src/EventListener.h new file mode 100644 index 0000000..fce8778 --- /dev/null +++ b/Source/ArtifactTrackerDLL/src/EventListener.h @@ -0,0 +1,29 @@ +#pragma once + +class EventListener : + public RE::BSTEventSink, + public RE::BSTEventSink +{ +public: + ~EventListener() = default; + EventListener(const EventListener&) = delete; + EventListener& operator=(const EventListener&) = delete; + EventListener& operator=(EventListener&&) = delete; + + static auto GetSingleton() -> EventListener*; + + static void Install(); + + auto ProcessEvent( + const RE::TESActorLocationChangeEvent* a_event, + RE::BSTEventSource* a_eventSource) + -> RE::BSEventNotifyControl override; + + auto ProcessEvent( + const RE::TESContainerChangedEvent* a_event, + RE::BSTEventSource* a_eventSource) + -> RE::BSEventNotifyControl override; + +private: + EventListener() = default; +}; \ No newline at end of file diff --git a/Source/ArtifactTrackerDLL/src/Functions/ArtifactTracker.h b/Source/ArtifactTrackerDLL/src/Functions/ArtifactTracker.h deleted file mode 100644 index 9cd09ac..0000000 --- a/Source/ArtifactTrackerDLL/src/Functions/ArtifactTracker.h +++ /dev/null @@ -1,288 +0,0 @@ -#pragma once - -namespace Papyrus::ArtifactTracker -{ - inline bool is_artifact(RE::TESForm* a_form) - { - switch (a_form->GetFormType()) { - case RE::FormType::Armor: - return a_form->GetPlayable(); - case RE::FormType::Weapon: - return a_form->GetPlayable() && a_form->formID != 0x000001F4; - case RE::FormType::Book: - return BookCheck::GetBookList().contains(a_form->formID); - case RE::FormType::Misc: - return MiscCheck::GetMiscList().contains(a_form->formID); - default: - return false; - } - } - - inline std::int32_t AddAllFormsToList(RE::StaticFunctionTag*, - RE::BGSListForm* a_targetList, - short a_formType, - RE::BGSListForm* a_storedList, - RE::BGSListForm* a_foundList) - { - const auto formType = static_cast(a_formType); - - switch (formType) { - - case RE::FormType::Book: - for (auto const& item : BookCheck::GetBookList()) { - if (!a_storedList->HasForm(item.second) && !a_foundList->HasForm(item.second)) { - a_targetList->AddForm(item.second); - } - } - break; - - case RE::FormType::Misc: - for (auto const& item : MiscCheck::GetMiscList()) { - if (!a_storedList->HasForm(item.second) && !a_foundList->HasForm(item.second)) { - a_targetList->AddForm(item.second); - } - } - break; - - case RE::FormType::None: - break; - - default: - const auto dataHandler = RE::TESDataHandler::GetSingleton(); - - if (!dataHandler) { - return a_targetList->forms.size(); - } - - for (const auto& form : dataHandler->GetFormArray(formType)) { - if (!form || !form->GetPlayable()) { - continue; - } - if (a_storedList->HasForm(form) || a_foundList->HasForm(form)) { - continue; - } - a_targetList->AddForm(form); - } - } - - return a_targetList->forms.size(); - } - - inline std::int32_t AddArtifactsToList(VM* a_vm, StackID a_stackID, RE::StaticFunctionTag*, - RE::TESForm* a_refOrList, - RE::BGSListForm* a_targetList, - RE::BGSListForm* a_excludeList = NULL) - { - if (!a_refOrList) { - a_vm->TraceStack("a_refOrList in AddArtifactsToList is None", a_stackID); - return 0; - } - if (!a_targetList) { - a_vm->TraceStack("a_targetList in AddArtifactsToList is None", a_stackID); - return 0; - } - - if (a_refOrList->Is(RE::FormType::FormList)) { - a_refOrList->As()->ForEachForm([&](RE::TESForm& a_exform) { - const auto refrItem = a_exform.As(); - if (refrItem) { - AddArtifactsToList(a_vm, a_stackID, nullptr, refrItem, a_targetList, a_excludeList); - } - return true; - }); - return a_targetList->forms.size(); - } - - const auto containerRef = a_refOrList->As(); - - if (!containerRef) { - a_vm->TraceStack("containerRef in AddArtifactsToList is not a reference", a_stackID); - return 0; - } - - const auto inv = containerRef->GetInventory([&](RE::TESBoundObject& a_exform) { - return is_artifact(&a_exform) && (!a_excludeList || !a_excludeList->HasForm(&a_exform)); - }); - - for (const auto& item : inv) { - if (item.second.first > 0) { - a_targetList->AddForm(item.first); - } - } - - return a_targetList->forms.size(); - } - - inline RE::TESObjectREFR* GetCellStorage(RE::StaticFunctionTag*, - RE::TESObjectREFR* a_ref, - RE::BGSListForm* a_refList, - RE::TESBoundObject* a_objectToCreate, - bool a_autoCreate = true) - { - RE::TESObjectREFR* result = NULL; - - if (!a_ref || !a_refList || !a_objectToCreate) { - return result; - } - - RE::TESObjectCELL* cell = a_ref->GetParentCell(); - - a_refList->ForEachForm([&result, &cell, &a_objectToCreate](RE::TESForm& a_exform) { - const auto ref = a_exform.As(); - - if (ref && ref->GetParentCell() == cell && ref->GetBaseObject()->formID == a_objectToCreate->formID) { - result = ref; - return false; - } - - return true; - }); - - if (!result && a_autoCreate) { - result = a_ref->PlaceObjectAtMe(a_objectToCreate, true).get(); - result->Disable(); - a_refList->AddForm(result); - } - - return result; - } - - inline void SyncCellStorage(VM* a_vm, StackID a_stackID, RE::StaticFunctionTag*, - RE::TESObjectREFR* a_cellStorage, - RE::BGSListForm* a_excludeContainers) - { - if (!a_cellStorage) { - return; - } - - std::unordered_map cellItems; - - const auto cell = a_cellStorage->GetParentCell(); - const auto inv = a_cellStorage->GetInventory(); - - for (const RE::NiPointer a_ref : cell->references) { - const auto baseObj = a_ref->GetBaseObject(); - - if (baseObj->formType == RE::FormType::Container || (baseObj->formType == RE::FormType::NPC && !a_ref->IsDisabled() && baseObj->As()->GetRace()->formID == 0x0010760A)) { - if (a_excludeContainers->HasForm(a_ref->formID) || baseObj->formID == 0xDC9E7) { // skip persistent and PlayerBookShelfContainer - continue; - } - - const auto contInv = a_ref->GetInventory([&](RE::TESBoundObject& a_object) -> bool { - return !cellItems.contains(a_object.formID); - }); - - for (const auto& [item, data] : contInv) { - if (data.first > 0) { - cellItems[item->formID] = true; - if (inv.find(item) == inv.end()) { - if (is_artifact(item)) { - a_cellStorage->AddObjectToContainer(item, nullptr, 1, nullptr); - } - } - } - } - - continue; - } - - if (a_ref->IsDisabled() || a_ref->IsMarkedForDeletion()) { - continue; - } - - if (cellItems.contains(baseObj->formID)) { - continue; - } - - cellItems[baseObj->formID] = true; - - if (!is_artifact(baseObj)) { - continue; - } - - if (inv.find(baseObj) == inv.end()) { - a_cellStorage->AddObjectToContainer(baseObj, nullptr, 1, nullptr); - } - } - - for (const auto& [item, data] : inv) { - const auto& [count, entry] = data; - if (count > 0 && !cellItems.contains(item->formID)) { - a_cellStorage->RemoveItem(item, count, RE::ITEM_REMOVE_REASON::kRemove, nullptr, nullptr); - } - } - - cellItems.clear(); - } - - // From po3's Papyrus Extender - inline std::vector GetPlayerFollowers(RE::StaticFunctionTag*) - { - 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; - } - - inline std::int32_t AddArtifactsFromFollowersToList(RE::StaticFunctionTag*, - RE::BGSListForm* a_targetList, - RE::BGSListForm* a_excludeList = NULL) - { - for (const auto& actor : GetPlayerFollowers(nullptr)) { - const auto inv = actor->GetInventory([&](RE::TESBoundObject& a_exform) { - return is_artifact(&a_exform) && (!a_excludeList || !a_excludeList->HasForm(&a_exform)); - }); - - for (const auto& item : inv) { - if (item.second.first > 0) { - a_targetList->AddForm(item.first); - } - } - } - - return a_targetList->forms.size(); - } - - inline bool FollowersHaveItem(RE::StaticFunctionTag*, - RE::TESBoundObject* a_form) - { - for (const auto& actor : GetPlayerFollowers(nullptr)) { - const auto inv = actor->GetInventory([&](RE::TESBoundObject& a_object) -> bool { - return a_form->formID == a_object.formID; - }); - const auto it = inv.find(a_form); - const auto iCount = it != inv.end() ? it->second.first : 0; - - if (iCount > 0) { - return true; - } - } - - return false; - } - - inline void Bind(VM& a_vm) - { - BIND(AddAllFormsToList); - logger::info("Registered AddAllFormsToList"sv); - BIND(AddArtifactsToList); - logger::info("Registered AddArtifactsToList"sv); - BIND(AddArtifactsFromFollowersToList); - logger::info("Registered AddArtifactsFromFollowersToList"sv); - BIND(GetCellStorage); - logger::info("Registered GetCellStorage"sv); - BIND(SyncCellStorage); - logger::info("Registered SyncCellStorage"sv); - BIND(FollowersHaveItem); - logger::info("Registered FollowersHaveItem"sv); - BIND(GetPlayerFollowers); - logger::info("Registered GetPlayerFollowers"sv); - } -} diff --git a/Source/ArtifactTrackerDLL/src/Functions/ObjectReference.h b/Source/ArtifactTrackerDLL/src/Functions/ObjectReference.h deleted file mode 100644 index 271f880..0000000 --- a/Source/ArtifactTrackerDLL/src/Functions/ObjectReference.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -namespace Papyrus::ObjectReference -{ - inline std::uint32_t GetItemCountInList(RE::StaticFunctionTag*, - RE::BGSListForm* a_containerList, - RE::TESBoundObject* a_form) - { - if (!a_containerList || !a_form) { - return 0; - } - - std::uint32_t iResult = 0; - - a_containerList->ForEachForm([&](RE::TESForm& a_container) { - const auto refrItem = a_container.As(); - if (refrItem) { - const auto inv = refrItem->GetInventory([&](RE::TESBoundObject& a_object) -> bool { - return a_form->formID == a_object.formID; - }); - const auto it = inv.find(a_form); - iResult += it != inv.end() ? it->second.first : 0; - } - return true; - }); - - return iResult; - } - - inline void Bind(VM& a_vm) - { - BIND(GetItemCountInList); - logger::info("Registered GetItemCountInList"sv); - } -} diff --git a/Source/ArtifactTrackerDLL/src/Main.cpp b/Source/ArtifactTrackerDLL/src/Main.cpp index a1467eb..87b3795 100644 --- a/Source/ArtifactTrackerDLL/src/Main.cpp +++ b/Source/ArtifactTrackerDLL/src/Main.cpp @@ -1,7 +1,7 @@ #include "Papyrus.h" -#include "BookCheck.h" -#include "MiscCheck.h" +#include "ArtifactTracker.h" +using namespace ArtifactTracker; using namespace RE::BSScript; using namespace SKSE; using namespace SKSE::log; @@ -9,7 +9,7 @@ using namespace SKSE::stl; namespace { - void InitializeLogging() { + void InitializeLogging() { auto path = logger::log_directory(); if (!path) { stl::report_and_fail("Failed to find standard logging directory"sv); @@ -27,14 +27,14 @@ namespace { spdlog::set_pattern("[%l] %v"s); } - void InitializeMessaging() { + void InitializeMessaging() + { GetMessagingInterface()->RegisterListener([](MessagingInterface::Message* message) { if (message->type == MessagingInterface::kDataLoaded) { - BookCheck::PreloadBookList(); - MiscCheck::PreloadMiscList(); + ArtifactTracker::Init(); } }); - } + } } SKSEPluginLoad(const LoadInterface* skse) { @@ -45,7 +45,8 @@ SKSEPluginLoad(const LoadInterface* skse) { log::info("{} {} is loading...", plugin->GetName(), version); Init(skse); - InitializeMessaging(); + InitializeMessaging(); + SKSE::GetPapyrusInterface()->Register(Papyrus::Bind); log::info("{} has finished loading.", plugin->GetName()); diff --git a/Source/ArtifactTrackerDLL/src/MiscCheck.cpp b/Source/ArtifactTrackerDLL/src/MiscCheck.cpp deleted file mode 100644 index 3b8f484..0000000 --- a/Source/ArtifactTrackerDLL/src/MiscCheck.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "MiscCheck.h" - -namespace MiscCheck -{ - std::unordered_map validMisc; - - void PreloadMiscList() - { - const auto dataHandler = RE::TESDataHandler::GetSingleton(); - - if (!dataHandler) { - return; - } - - RE::BGSListForm* excludeKeywords = dataHandler->LookupForm(0x801, "Artifact Tracker.esp"); // ETR_ExcludeMiscKeywords - - for (const auto& form : dataHandler->GetFormArray()) { - if (form->GetPlayable() && !form->IsGold() && !form->IsLockpick() && !form->HasKeywordInList(excludeKeywords, false)) { - validMisc[form->formID] = form; - } - } - } - - std::unordered_map GetMiscList() - { - return validMisc; - } -} diff --git a/Source/ArtifactTrackerDLL/src/MiscCheck.h b/Source/ArtifactTrackerDLL/src/MiscCheck.h deleted file mode 100644 index c0db170..0000000 --- a/Source/ArtifactTrackerDLL/src/MiscCheck.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -namespace MiscCheck -{ - void PreloadMiscList(); - - std::unordered_map GetMiscList(); -} diff --git a/Source/ArtifactTrackerDLL/src/Papyrus.cpp b/Source/ArtifactTrackerDLL/src/Papyrus.cpp index 6fcf0ef..918b1d7 100644 --- a/Source/ArtifactTrackerDLL/src/Papyrus.cpp +++ b/Source/ArtifactTrackerDLL/src/Papyrus.cpp @@ -1,9 +1,5 @@ #include "Papyrus.h" -#include "BookCheck.h" -#include "MiscCheck.h" - -#include "Functions/ObjectReference.h" -#include "Functions/ArtifactTracker.h" +#include "PapyrusFunctions.h" namespace Papyrus { @@ -16,8 +12,7 @@ namespace Papyrus logger::info("{:*^30}", "FUNCTIONS"sv); - ObjectReference::Bind(*a_vm); - ArtifactTracker::Bind(*a_vm); + PapyrusFunctions::Bind(*a_vm); return true; } diff --git a/Source/ArtifactTrackerDLL/src/PapyrusFunctions.h b/Source/ArtifactTrackerDLL/src/PapyrusFunctions.h new file mode 100644 index 0000000..4159bb2 --- /dev/null +++ b/Source/ArtifactTrackerDLL/src/PapyrusFunctions.h @@ -0,0 +1,164 @@ +#pragma once + +#include "ArtifactTracker.h" + +namespace Papyrus::PapyrusFunctions +{ + inline std::int32_t AddAllFormsToList(RE::StaticFunctionTag*, + RE::BGSListForm* a_targetList, + short a_formType, + RE::BGSListForm* a_storedList, + RE::BGSListForm* a_foundList) + { + const auto formType = static_cast(a_formType); + + switch (formType) { + + case RE::FormType::Book: + for (auto const& item : ArtifactTracker::validBooks) { + if (!a_storedList->HasForm(item.second) && !a_foundList->HasForm(item.second)) { + a_targetList->AddForm(item.second); + } + } + break; + + case RE::FormType::Misc: + for (auto const& item : ArtifactTracker::validMisc) { + if (!a_storedList->HasForm(item.second) && !a_foundList->HasForm(item.second)) { + a_targetList->AddForm(item.second); + } + } + break; + + case RE::FormType::None: + break; + + default: + const auto dataHandler = RE::TESDataHandler::GetSingleton(); + + if (!dataHandler) { + return a_targetList->forms.size(); + } + + for (const auto& form : dataHandler->GetFormArray(formType)) { + if (!form || !form->GetPlayable()) { + continue; + } + if (a_storedList->HasForm(form) || a_foundList->HasForm(form)) { + continue; + } + a_targetList->AddForm(form); + } + } + + return a_targetList->forms.size(); + } + + inline std::int32_t AddArtifactsToList(VM* a_vm, StackID a_stackID, RE::StaticFunctionTag*, + RE::TESForm* a_refOrList, + RE::BGSListForm* a_targetList, + RE::BGSListForm* a_excludeList = NULL) + { + if (!a_refOrList) { + a_vm->TraceStack("a_refOrList in AddArtifactsToList is None", a_stackID); + return 0; + } + if (!a_targetList) { + a_vm->TraceStack("a_targetList in AddArtifactsToList is None", a_stackID); + return 0; + } + + if (a_refOrList->Is(RE::FormType::FormList)) { + a_refOrList->As()->ForEachForm([&](RE::TESForm& a_exform) { + const auto refrItem = a_exform.As(); + if (refrItem) { + AddArtifactsToList(a_vm, a_stackID, nullptr, refrItem, a_targetList, a_excludeList); + } + return true; + }); + return a_targetList->forms.size(); + } + + const auto containerRef = a_refOrList->As(); + + if (!containerRef) { + a_vm->TraceStack("containerRef in AddArtifactsToList is not a reference", a_stackID); + return 0; + } + + const auto inv = containerRef->GetInventory([&](RE::TESBoundObject& a_exform) { + return ArtifactTracker::IsArtifact(&a_exform) && (!a_excludeList || !a_excludeList->HasForm(&a_exform)); + }); + + for (const auto& item : inv) { + if (item.second.first > 0) { + a_targetList->AddForm(item.first); + } + } + + return a_targetList->forms.size(); + } + + inline RE::TESObjectREFR* GetCellStorage(RE::StaticFunctionTag*, + RE::TESObjectREFR* a_ref, + RE::BGSListForm* a_refList, + RE::TESBoundObject* a_objectToCreate, + bool a_autoCreate = true) + { + return ArtifactTracker::GetCellStorage(a_ref, a_refList, a_objectToCreate, a_autoCreate); + } + + inline void SyncCellStorage(RE::StaticFunctionTag*, + RE::TESObjectREFR* a_cellStorage, + RE::BGSListForm* a_excludeContainers) + { + ArtifactTracker::SyncCellStorage(a_cellStorage, a_excludeContainers); + } + + inline std::int32_t AddArtifactsFromFollowersToList(RE::StaticFunctionTag*, + RE::BGSListForm* a_targetList, + RE::BGSListForm* a_excludeList = NULL) + { + if (const auto processLists = RE::ProcessLists::GetSingleton(); processLists) { + for (auto& actorHandle : processLists->highActorHandles) { + if (auto actor = actorHandle.get(); actor && actor->IsPlayerTeammate()) { + + const auto inv = actor->GetInventory([&](RE::TESBoundObject& a_exform) { + return ArtifactTracker::IsArtifact(&a_exform) && (!a_excludeList || !a_excludeList->HasForm(&a_exform)); + }); + + for (const auto& item : inv) { + if (item.second.first > 0) { + a_targetList->AddForm(item.first); + } + } + + } + } + } + + return a_targetList->forms.size(); + } + + inline void OnItemPickup(RE::StaticFunctionTag*, + RE::TESBoundObject* a_item) + { + ArtifactTracker::OnItemPickup(a_item); + } + + inline void Bind(VM& a_vm) + { + BIND(AddAllFormsToList); + logger::info("Registered AddAllFormsToList"sv); + BIND(AddArtifactsToList); + logger::info("Registered AddArtifactsToList"sv); + BIND(AddArtifactsFromFollowersToList); + logger::info("Registered AddArtifactsFromFollowersToList"sv); + BIND(GetCellStorage); + logger::info("Registered GetCellStorage"sv); + BIND(SyncCellStorage); + logger::info("Registered SyncCellStorage"sv); + BIND(OnItemPickup); + logger::info("Registered OnItemPickup"sv); + } +} diff --git a/Source/ArtifactTrackerDLL/src/Util.h b/Source/ArtifactTrackerDLL/src/Util.h new file mode 100644 index 0000000..63adbef --- /dev/null +++ b/Source/ArtifactTrackerDLL/src/Util.h @@ -0,0 +1,78 @@ +#pragma once + +void RemoveListItem(RE::BGSListForm* a_List, RE::TESForm* a_form) +{ + using func_t = decltype(&RemoveListItem); + REL::Relocation func{ REL::RelocationID(20471, 20914) }; + return func(a_List, a_form); +} + +void RevertList(RE::TESForm* a_form) +{ + using func_t = decltype(&RevertList); + REL::Relocation func{ REL::RelocationID(20469, 20912) }; + return func(a_form); +} + +inline bool RefHasItem(RE::TESObjectREFR* a_ref, RE::TESForm* a_item) +{ + if (!a_ref || !a_item) { + SKSE::log::warn("Invalid arguments in RefHasItem"); + return false; + } + + auto invChanges = a_ref->GetInventoryChanges(); + if (invChanges && invChanges->entryList) { + for (auto& entry : *invChanges->entryList) { + if (entry && entry->object && entry->object->formID == a_item->formID) { + return entry->countDelta > 0; + } + } + } + return false; +} + +inline std::uint32_t GetItemCountInList(RE::BGSListForm* a_containerList, RE::TESBoundObject* a_form) +{ + if (!a_containerList || !a_form) { + SKSE::log::warn("Invalid arguments in GetItemCountInList"); + return 0; + } + + std::uint32_t iResult = 0; + + a_containerList->ForEachForm([&](RE::TESForm& a_container) { + const auto refrItem = a_container.As(); + if (refrItem) { + const auto inv = refrItem->GetInventory([&](RE::TESBoundObject& a_object) -> bool { + return a_form->formID == a_object.formID; + }); + const auto it = inv.find(a_form); + iResult += it != inv.end() ? it->second.first : 0; + } + return true; + }); + + return iResult; +} + +inline bool FollowersHaveItem(RE::TESBoundObject* a_form) +{ + if (const auto processLists = RE::ProcessLists::GetSingleton(); processLists) { + for (auto& actorHandle : processLists->highActorHandles) { + if (auto actor = actorHandle.get(); actor && actor->IsPlayerTeammate()) { + const auto inv = actor->GetInventory([&](RE::TESBoundObject& a_object) -> bool { + return a_form->formID == a_object.formID; + }); + const auto it = inv.find(a_form); + const auto iCount = it != inv.end() ? it->second.first : 0; + + if (iCount > 0) { + return true; + } + } + } + } + + return false; +} \ No newline at end of file diff --git a/Source/Scripts/ETR_Functions.psc b/Source/Scripts/ETR_Functions.psc index 895589e..97dc4b8 100644 --- a/Source/Scripts/ETR_Functions.psc +++ b/Source/Scripts/ETR_Functions.psc @@ -4,14 +4,10 @@ int function AddAllFormsToList(FormList targetList, int formType, FormList store int function AddArtifactsToList(Form refOrList, FormList targetList, FormList excludeList = None) native global -int function GetItemCountInList(FormList refList, Form baseForm) native global - ObjectReference function GetCellStorage(ObjectReference ref, FormList refList, Form refToCreate, bool autoCreate = true) native global function SyncCellStorage(ObjectReference cellStorage, FormList excludeContainers) native global -Actor[] function GetPlayerFollowers() native global - -bool function FollowersHaveItem(Form baseForm) native global - int function AddArtifactsFromFollowersToList(FormList targetList, FormList excludeList = None) native global + +function OnItemPickup(Form item) native global diff --git a/Source/Scripts/ETR_TrackFoundItems.psc b/Source/Scripts/ETR_TrackFoundItems.psc deleted file mode 100644 index 1969160..0000000 --- a/Source/Scripts/ETR_TrackFoundItems.psc +++ /dev/null @@ -1,168 +0,0 @@ -Scriptname ETR_TrackFoundItems extends ReferenceAlias - -Actor Property PlayerRef Auto - -FormList Property ETR_ItemsNew Auto -FormList Property ETR_ItemsFound Auto -FormList Property ETR_ItemsStored Auto -FormList Property ETR_PersistentStorageList Auto - -Container Property ETR_CellStorageContainer Auto - -Keyword Property LocTypePlayerHouse Auto - -bool bBusy = false -int iFollowerIndex = 0 -bool bAtHome = false -bool bRescanHome = false -bool bRescanPersistent = false -bool bRescanFollowers = false -ObjectReference lastDestContainer = None -bool lastDestIsPersistent = false -int iUpdateCount - - -event OnInit() - OnPlayerLoadGame() -endevent - - -Event OnPlayerLoadGame() - AddInventoryEventFilter(ETR_ItemsFound) - Location currentLocation = PlayerRef.GetCurrentLocation() - bAtHome = currentLocation && currentLocation.HasKeyword(LocTypePlayerHouse) - lastDestContainer = None - bBusy = false -EndEvent - - -Event OnLocationChange(Location akOldLoc, Location akNewLoc) - - bAtHome = akNewLoc && akNewLoc.HasKeyword(LocTypePlayerHouse) - lastDestContainer = None - - bRescanFollowers = true - RegisterForSingleUpdate(3.0) - -endEvent - - -Event OnMenuClose(String MenuName) - UnregisterForUpdate() - OnUpdate() -EndEvent - - -Event OnUpdate() - - if bRescanFollowers - bRescanFollowers = false - - int iCurrentFollowers = 0; - Actor[] aFollowers = ETR_Functions.GetPlayerFollowers() - int i = aFollowers.length - while i > 0 - i -= 1 - iCurrentFollowers += aFollowers[i].GetFormID() - endwhile - - if iCurrentFollowers == iFollowerIndex - return - endif - - iFollowerIndex = iCurrentFollowers - Debug.Notification("Team changed, rescanning") - endif - - if lastDestContainer && lastDestContainer as Actor && (lastDestContainer as Actor).IsPlayerTeammate() - lastDestContainer = None - bRescanHome = false - return - endif - - if UI.IsMenuOpen("ContainerMenu") - RegisterForMenu("ContainerMenu") - return - endif - - while bBusy - Debug.Notification("Found OnUpdate is busy") - Utility.wait(0.5) - endwhile - - bBusy = true - - iUpdateCount += 1 - Debug.Notification("Running Found OnUpdate " + iUpdateCount) - - if bRescanHome - bRescanHome = false - - if lastDestContainer && lastDestContainer as PlayerBookShelfContainerScript - int iLimit = 10 - while iLimit >= 0 && (lastDestContainer as PlayerBookShelfContainerScript).GetState() == "PlacingBooks" - Debug.Notification("Waiting for shelf update") - iLimit -= 1 - Utility.wait(0.5) - endwhile - endif - - ObjectReference cellStorage = ETR_Functions.GetCellStorage(PlayerRef, ETR_PersistentStorageList, ETR_CellStorageContainer) - ETR_Functions.SyncCellStorage(cellStorage, ETR_PersistentStorageList) - if ! bRescanPersistent - ETR_Functions.AddArtifactsToList(cellStorage, ETR_ItemsStored) - endif - endif - - if bRescanPersistent - bRescanPersistent = false - Form[] aContainers = ETR_PersistentStorageList.ToArray() - int n = aContainers.length - while n > 0 - n -= 1 - ETR_Functions.AddArtifactsToList(aContainers[n], ETR_ItemsStored) - endwhile - endif - - ETR_ItemsFound.Revert() - ETR_Functions.AddArtifactsToList(PlayerRef, ETR_ItemsFound, ETR_ItemsStored) - ETR_Functions.AddArtifactsFromFollowersToList(ETR_ItemsFound, ETR_ItemsStored) - - bBusy = false - -EndEvent - - -event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer) - - if akDestContainer - if lastDestContainer != akDestContainer - lastDestContainer = akDestContainer - lastDestIsPersistent = ETR_PersistentStorageList.HasForm(akDestContainer) - endif - - ; Moving items without latent functions should help with avoiding stack dumps - if lastDestIsPersistent - bRescanHome = false - bRescanPersistent = true - RegisterForSingleUpdate(0.5) - elseif bAtHome - bRescanHome = true - bRescanPersistent = false - RegisterForSingleUpdate(0.5) - elseif PlayerRef.GetItemCount(akBaseItem) == 0 && ! ETR_Functions.FollowersHaveItem(akBaseItem) - ETR_ItemsFound.RemoveAddedForm(akBaseItem) - ETR_ItemsNew.AddForm(akBaseItem) - endif - - elseif bAtHome && akItemReference - ETR_ItemsFound.RemoveAddedForm(akBaseItem) - ETR_ItemsStored.AddForm(akBaseItem) - ETR_Functions.GetCellStorage(PlayerRef, ETR_PersistentStorageList, ETR_CellStorageContainer).AddItem(akBaseItem, 1, true) - - elseif PlayerRef.GetItemCount(akBaseItem) == 0 && ! ETR_Functions.FollowersHaveItem(akBaseItem) - ETR_ItemsFound.RemoveAddedForm(akBaseItem) - ETR_ItemsNew.AddForm(akBaseItem) - endif - -endevent diff --git a/Source/Scripts/ETR_TrackNewItems.psc b/Source/Scripts/ETR_TrackNewItems.psc deleted file mode 100644 index e08e6f1..0000000 --- a/Source/Scripts/ETR_TrackNewItems.psc +++ /dev/null @@ -1,68 +0,0 @@ -Scriptname ETR_TrackNewItems extends ReferenceAlias - -Actor Property PlayerRef Auto - -FormList Property ETR_ItemsNew Auto -FormList Property ETR_ItemsFound Auto -FormList Property ETR_ItemsStored Auto -FormList Property ETR_PersistentStorageList Auto - -GlobalVariable Property ETR_NotifyNewArtifact Auto - - -event OnInit() - OnPlayerLoadGame() -endevent - - -event OnPlayerLoadGame() - - AddInventoryEventFilter(ETR_ItemsNew) - - if skse.GetPluginVersion("Ahzaab's moreHUD Plugin") >= 30800 - ahzmorehud.RegisterIconFormList("dbmNew", ETR_ItemsNew) - ahzmorehud.RegisterIconFormList("dbmFound", ETR_ItemsFound) - ahzmorehud.RegisterIconFormList("dbmDisp", ETR_ItemsStored) - endif - - if skse.GetPluginVersion("Ahzaab's moreHUD Inventory Plugin") >= 10017 - ahzmorehudie.RegisterIconFormList("dbmNew", ETR_ItemsNew) - ahzmorehudie.RegisterIconFormList("dbmFound", ETR_ItemsFound) - ahzmorehudie.RegisterIconFormList("dbmDisp", ETR_ItemsStored) - endif - - if SKSE.GetPluginVersion("QuickLootRE") >= 292 - QuickLootRE.RegisterNewItemsList(ETR_ItemsNew) - QuickLootRE.RegisterDisplayedItemsList(ETR_ItemsStored) - QuickLootRE.RegisterFoundItemsList(ETR_ItemsFound) - endif - - ; Rebuild all lists to avoid discrepancies, stale data, and broken records - - ETR_ItemsStored.Revert() - ETR_Functions.AddArtifactsToList(ETR_PersistentStorageList, ETR_ItemsStored) - - ETR_ItemsFound.Revert() - ETR_Functions.AddArtifactsToList(PlayerRef, ETR_ItemsFound, ETR_ItemsStored) - ETR_Functions.AddArtifactsFromFollowersToList(ETR_ItemsFound, ETR_ItemsStored) - - ETR_ItemsNew.Revert() - ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 41, ETR_ItemsStored, ETR_ItemsFound) - ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 32, ETR_ItemsStored, ETR_ItemsFound) - ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 27, ETR_ItemsStored, ETR_ItemsFound) - ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 26, ETR_ItemsStored, ETR_ItemsFound) - -endevent - - -event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer) - - ETR_ItemsNew.RemoveAddedForm(akBaseItem) - ETR_ItemsFound.AddForm(akBaseItem) - ETR_ItemsStored.RemoveAddedForm(akBaseItem) - - if ETR_NotifyNewArtifact.Value - Debug.Notification("New artifact acquired: " + akBaseItem.GetName()) - endif - -endevent diff --git a/Source/Scripts/ETR_TrackStoredItems.psc b/Source/Scripts/ETR_TrackStoredItems.psc index 261ffc7..ae1be62 100644 --- a/Source/Scripts/ETR_TrackStoredItems.psc +++ b/Source/Scripts/ETR_TrackStoredItems.psc @@ -29,6 +29,40 @@ endevent Event OnPlayerLoadGame() AddInventoryEventFilter(ETR_ItemsStored) + + if skse.GetPluginVersion("Ahzaab's moreHUD Plugin") >= 30800 + ahzmorehud.RegisterIconFormList("dbmNew", ETR_ItemsNew) + ahzmorehud.RegisterIconFormList("dbmFound", ETR_ItemsFound) + ahzmorehud.RegisterIconFormList("dbmDisp", ETR_ItemsStored) + endif + + if skse.GetPluginVersion("Ahzaab's moreHUD Inventory Plugin") >= 10017 + ahzmorehudie.RegisterIconFormList("dbmNew", ETR_ItemsNew) + ahzmorehudie.RegisterIconFormList("dbmFound", ETR_ItemsFound) + ahzmorehudie.RegisterIconFormList("dbmDisp", ETR_ItemsStored) + endif + + if SKSE.GetPluginVersion("QuickLootRE") >= 292 + QuickLootRE.RegisterNewItemsList(ETR_ItemsNew) + QuickLootRE.RegisterDisplayedItemsList(ETR_ItemsStored) + QuickLootRE.RegisterFoundItemsList(ETR_ItemsFound) + endif + + ; Rebuild all lists to avoid discrepancies, stale data, and broken records + + ETR_ItemsStored.Revert() + ETR_Functions.AddArtifactsToList(ETR_PersistentStorageList, ETR_ItemsStored) + + ETR_ItemsFound.Revert() + ETR_Functions.AddArtifactsToList(PlayerRef, ETR_ItemsFound, ETR_ItemsStored) + ETR_Functions.AddArtifactsFromFollowersToList(ETR_ItemsFound, ETR_ItemsStored) + + ETR_ItemsNew.Revert() + ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 41, ETR_ItemsStored, ETR_ItemsFound) + ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 32, ETR_ItemsStored, ETR_ItemsFound) + ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 27, ETR_ItemsStored, ETR_ItemsFound) + ETR_Functions.AddAllFormsToList(ETR_ItemsNew, 26, ETR_ItemsStored, ETR_ItemsFound) + Location currentLocation = PlayerRef.GetCurrentLocation() bAtHome = currentLocation && currentLocation.HasKeyword(LocTypePlayerHouse) if bAtHome @@ -108,6 +142,7 @@ Event OnUpdate() ETR_ItemsStored.Revert() Form[] aContainers = ETR_PersistentStorageList.ToArray() + int n = aContainers.length while n > 0 n -= 1 @@ -117,7 +152,6 @@ Event OnUpdate() ETR_ItemsFound.Revert() ETR_Functions.AddArtifactsToList(PlayerRef, ETR_ItemsFound, ETR_ItemsStored) ETR_Functions.AddArtifactsFromFollowersToList(ETR_ItemsFound, ETR_ItemsStored) - endif bBusy = false @@ -139,41 +173,7 @@ event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemRefere RegisterForSingleUpdate(0.5) endif elseif bAtHome - ObjectReference cellStorage = ETR_Functions.GetCellStorage(PlayerRef, ETR_PersistentStorageList, ETR_CellStorageContainer) - if cellStorage.GetItemCount(akBaseItem) - ETR_Functions.SyncCellStorage(cellStorage, ETR_PersistentStorageList) - if ETR_Functions.GetItemCountInList(ETR_PersistentStorageList, akBaseItem) == 0 - ETR_ItemsStored.RemoveAddedForm(akBaseItem) - ETR_ItemsFound.AddForm(akBaseItem) - endif - endif + ETR_Functions.OnItemPickup(akBaseItem) endif endevent - - -state AtHome - - ; The item is already registered as stored, and we just stored more - event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer) - - if akDestContainer - if lastDestContainer != akDestContainer - lastDestContainer = akDestContainer - lastDestIsPersistent = ETR_PersistentStorageList.HasForm(akDestContainer) - endif - if ! lastDestIsPersistent - bRescanHome = true - bRescanPersistent = false - RegisterForSingleUpdate(0.5) - endif - elseif akItemReference - ObjectReference cellStorage = ETR_Functions.GetCellStorage(PlayerRef, ETR_PersistentStorageList, ETR_CellStorageContainer) - if cellStorage.GetItemCount(akBaseItem) == 0 - cellStorage.AddItem(akBaseItem, 1, true) - endif - endif - - endevent - -endstate