Moved most synchronous operations to DLL; decoded Formlist.Revert and FormList.RemoveAddedForm
This commit is contained in:
parent
bb427cc419
commit
a7229e0bea
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -23,8 +23,9 @@ configure_file(
|
|||||||
set(sources
|
set(sources
|
||||||
src/Main.cpp
|
src/Main.cpp
|
||||||
src/Papyrus.cpp
|
src/Papyrus.cpp
|
||||||
|
src/ArtifactTracker.cpp
|
||||||
|
src/EventListener.cpp
|
||||||
src/BookCheck.cpp
|
src/BookCheck.cpp
|
||||||
src/MiscCheck.cpp
|
|
||||||
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
||||||
|
|
||||||
|
321
Source/ArtifactTrackerDLL/src/ArtifactTracker.cpp
Normal file
321
Source/ArtifactTrackerDLL/src/ArtifactTracker.cpp
Normal file
@ -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<RE::FormID, RE::TESObjectMISC*> validMisc;
|
||||||
|
std::unordered_map<RE::FormID, RE::TESObjectBOOK*> validBooks;
|
||||||
|
|
||||||
|
void Init()
|
||||||
|
{
|
||||||
|
EventListener::Install();
|
||||||
|
|
||||||
|
const auto dataHandler = RE::TESDataHandler::GetSingleton();
|
||||||
|
|
||||||
|
notifyNewArtifact = dataHandler->LookupForm<RE::TESGlobal>(0x809, "Artifact Tracker.esp");
|
||||||
|
cellContainer = dataHandler->LookupForm(0x800, "Artifact Tracker.esp")->As<RE::TESBoundObject>();
|
||||||
|
|
||||||
|
listNew = dataHandler->LookupForm<RE::BGSListForm>(0x803, "Artifact Tracker.esp");
|
||||||
|
listStored = dataHandler->LookupForm<RE::BGSListForm>(0x805, "Artifact Tracker.esp");
|
||||||
|
listFound = dataHandler->LookupForm<RE::BGSListForm>(0x806, "Artifact Tracker.esp");
|
||||||
|
persistentStorage = dataHandler->LookupForm<RE::BGSListForm>(0x807, "Artifact Tracker.esp");
|
||||||
|
|
||||||
|
homeKeyword = dataHandler->LookupForm<RE::BGSKeyword>(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<RE::BGSKeyword>(0xF5CB0, "Skyrim.esm"); // VendorItemRecipe
|
||||||
|
|
||||||
|
for (const auto& form : dataHandler->GetFormArray<RE::TESObjectBOOK>()) {
|
||||||
|
if (form && !form->TeachesSpell() && (!form->HasKeyword(recipeKeyword) || BookCheck::IsBook(form))) {
|
||||||
|
validBooks[form->formID] = form;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RE::BGSListForm* excludeKeywords = dataHandler->LookupForm<RE::BGSListForm>(0x801, "Artifact Tracker.esp"); // ETR_ExcludeMiscKeywords
|
||||||
|
|
||||||
|
for (const auto& form : dataHandler->GetFormArray<RE::TESObjectMISC>()) {
|
||||||
|
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<RE::TESObjectREFR>();
|
||||||
|
|
||||||
|
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<RE::FormID, bool> cellItems;
|
||||||
|
|
||||||
|
const auto cell = a_cellStorage->GetParentCell();
|
||||||
|
const auto inv = a_cellStorage->GetInventory();
|
||||||
|
|
||||||
|
for (const RE::NiPointer<RE::TESObjectREFR> 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<RE::TESNPC>()->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<RE::TESBoundObject>(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<RE::TESBoundObject>(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<RE::TESBoundObject>())) {
|
||||||
|
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<RE::TESObjectREFR>(a_event->newContainer);
|
||||||
|
|
||||||
|
if (!targetContainer || targetContainer->GetParentCell() != RE::PlayerCharacter::GetSingleton()->GetParentCell()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetContainer->GetBaseObject()->Is(RE::FormType::NPC) && targetContainer->GetBaseObject()->As<RE::TESNPC>()->GetRace()->formID != 0x0010760A) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RE::TESObjectREFR* cellStorage = GetCellStorage(RE::PlayerCharacter::GetSingleton(), persistentStorage, cellContainer, true);
|
||||||
|
cellStorage->AddObjectToContainer(form->As<RE::TESBoundObject>(), nullptr, 1, nullptr);
|
||||||
|
RE::DebugNotification("updated cellStorage");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listFound->HasForm(form)) {
|
||||||
|
RemoveListItem(listFound, form);
|
||||||
|
}
|
||||||
|
|
||||||
|
listStored->AddForm(form);
|
||||||
|
RE::DebugNotification("added to stored");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
Source/ArtifactTrackerDLL/src/ArtifactTracker.h
Normal file
29
Source/ArtifactTrackerDLL/src/ArtifactTracker.h
Normal file
@ -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<RE::FormID, RE::TESObjectMISC*> validMisc;
|
||||||
|
extern std::unordered_map<RE::FormID, RE::TESObjectBOOK*> 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);
|
||||||
|
}
|
@ -3,33 +3,6 @@
|
|||||||
// Mostly borrowed from Fix Note icon for SkyUI by 0xC0000005
|
// Mostly borrowed from Fix Note icon for SkyUI by 0xC0000005
|
||||||
namespace BookCheck
|
namespace BookCheck
|
||||||
{
|
{
|
||||||
std::unordered_map<RE::FormID, RE::TESObjectBOOK*> validBooks;
|
|
||||||
|
|
||||||
void PreloadBookList()
|
|
||||||
{
|
|
||||||
const auto dataHandler = RE::TESDataHandler::GetSingleton();
|
|
||||||
|
|
||||||
if (!dataHandler) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const RE::BGSKeyword* recipeKeyword = dataHandler->LookupForm<RE::BGSKeyword>(0xF5CB0, "Skyrim.esm"); // VendorItemRecipe
|
|
||||||
|
|
||||||
for (const auto& form : dataHandler->GetFormArray<RE::TESObjectBOOK>()) {
|
|
||||||
if (!form || form->TeachesSpell()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (form->HasKeyword(recipeKeyword) || IsBook(form)) {
|
|
||||||
validBooks[form->formID] = form;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_map<RE::FormID, RE::TESObjectBOOK*> GetBookList()
|
|
||||||
{
|
|
||||||
return validBooks;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* ExtractFileName(const char* const path, size_t& fileNameLength)
|
const char* ExtractFileName(const char* const path, size_t& fileNameLength)
|
||||||
{
|
{
|
||||||
if (!path) {
|
if (!path) {
|
||||||
|
@ -3,10 +3,6 @@
|
|||||||
// Mostly borrowed from Fix Note icon for SkyUI by 0xC0000005
|
// Mostly borrowed from Fix Note icon for SkyUI by 0xC0000005
|
||||||
namespace BookCheck
|
namespace BookCheck
|
||||||
{
|
{
|
||||||
void PreloadBookList();
|
|
||||||
|
|
||||||
std::unordered_map<RE::FormID, RE::TESObjectBOOK*> GetBookList();
|
|
||||||
|
|
||||||
const char* ExtractFileName(const char* const path, size_t& fileNameLength);
|
const char* ExtractFileName(const char* const path, size_t& fileNameLength);
|
||||||
|
|
||||||
// Performs a case-sensitive search for 'needle' in the specified string,
|
// Performs a case-sensitive search for 'needle' in the specified string,
|
||||||
|
38
Source/ArtifactTrackerDLL/src/EventListener.cpp
Normal file
38
Source/ArtifactTrackerDLL/src/EventListener.cpp
Normal file
@ -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<RE::TESContainerChangedEvent>(EventListener::GetSingleton());
|
||||||
|
eventSource->AddEventSink<RE::TESActorLocationChangeEvent>(EventListener::GetSingleton());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto EventListener::ProcessEvent(
|
||||||
|
const RE::TESActorLocationChangeEvent* a_event,
|
||||||
|
RE::BSTEventSource<RE::TESActorLocationChangeEvent>* 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<RE::TESContainerChangedEvent>* a_eventSource)
|
||||||
|
-> RE::BSEventNotifyControl
|
||||||
|
{
|
||||||
|
ArtifactTracker::OnContainerChanged(a_event);
|
||||||
|
|
||||||
|
return RE::BSEventNotifyControl::kContinue;
|
||||||
|
}
|
29
Source/ArtifactTrackerDLL/src/EventListener.h
Normal file
29
Source/ArtifactTrackerDLL/src/EventListener.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
class EventListener :
|
||||||
|
public RE::BSTEventSink<RE::TESActorLocationChangeEvent>,
|
||||||
|
public RE::BSTEventSink<RE::TESContainerChangedEvent>
|
||||||
|
{
|
||||||
|
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<RE::TESActorLocationChangeEvent>* a_eventSource)
|
||||||
|
-> RE::BSEventNotifyControl override;
|
||||||
|
|
||||||
|
auto ProcessEvent(
|
||||||
|
const RE::TESContainerChangedEvent* a_event,
|
||||||
|
RE::BSTEventSource<RE::TESContainerChangedEvent>* a_eventSource)
|
||||||
|
-> RE::BSEventNotifyControl override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
EventListener() = default;
|
||||||
|
};
|
@ -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<RE::FormType>(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<RE::BGSListForm>()->ForEachForm([&](RE::TESForm& a_exform) {
|
|
||||||
const auto refrItem = a_exform.As<RE::TESObjectREFR>();
|
|
||||||
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<RE::TESObjectREFR>();
|
|
||||||
|
|
||||||
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<RE::TESObjectREFR>();
|
|
||||||
|
|
||||||
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<RE::FormID, bool> cellItems;
|
|
||||||
|
|
||||||
const auto cell = a_cellStorage->GetParentCell();
|
|
||||||
const auto inv = a_cellStorage->GetInventory();
|
|
||||||
|
|
||||||
for (const RE::NiPointer<RE::TESObjectREFR> 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<RE::TESNPC>()->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<RE::Actor*> GetPlayerFollowers(RE::StaticFunctionTag*)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<RE::TESObjectREFR>();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
#include "Papyrus.h"
|
#include "Papyrus.h"
|
||||||
#include "BookCheck.h"
|
#include "ArtifactTracker.h"
|
||||||
#include "MiscCheck.h"
|
|
||||||
|
|
||||||
|
using namespace ArtifactTracker;
|
||||||
using namespace RE::BSScript;
|
using namespace RE::BSScript;
|
||||||
using namespace SKSE;
|
using namespace SKSE;
|
||||||
using namespace SKSE::log;
|
using namespace SKSE::log;
|
||||||
@ -27,11 +27,11 @@ namespace {
|
|||||||
spdlog::set_pattern("[%l] %v"s);
|
spdlog::set_pattern("[%l] %v"s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeMessaging() {
|
void InitializeMessaging()
|
||||||
|
{
|
||||||
GetMessagingInterface()->RegisterListener([](MessagingInterface::Message* message) {
|
GetMessagingInterface()->RegisterListener([](MessagingInterface::Message* message) {
|
||||||
if (message->type == MessagingInterface::kDataLoaded) {
|
if (message->type == MessagingInterface::kDataLoaded) {
|
||||||
BookCheck::PreloadBookList();
|
ArtifactTracker::Init();
|
||||||
MiscCheck::PreloadMiscList();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -46,6 +46,7 @@ SKSEPluginLoad(const LoadInterface* skse) {
|
|||||||
|
|
||||||
Init(skse);
|
Init(skse);
|
||||||
InitializeMessaging();
|
InitializeMessaging();
|
||||||
|
|
||||||
SKSE::GetPapyrusInterface()->Register(Papyrus::Bind);
|
SKSE::GetPapyrusInterface()->Register(Papyrus::Bind);
|
||||||
|
|
||||||
log::info("{} has finished loading.", plugin->GetName());
|
log::info("{} has finished loading.", plugin->GetName());
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
#include "MiscCheck.h"
|
|
||||||
|
|
||||||
namespace MiscCheck
|
|
||||||
{
|
|
||||||
std::unordered_map<RE::FormID, RE::TESObjectMISC*> validMisc;
|
|
||||||
|
|
||||||
void PreloadMiscList()
|
|
||||||
{
|
|
||||||
const auto dataHandler = RE::TESDataHandler::GetSingleton();
|
|
||||||
|
|
||||||
if (!dataHandler) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RE::BGSListForm* excludeKeywords = dataHandler->LookupForm<RE::BGSListForm>(0x801, "Artifact Tracker.esp"); // ETR_ExcludeMiscKeywords
|
|
||||||
|
|
||||||
for (const auto& form : dataHandler->GetFormArray<RE::TESObjectMISC>()) {
|
|
||||||
if (form->GetPlayable() && !form->IsGold() && !form->IsLockpick() && !form->HasKeywordInList(excludeKeywords, false)) {
|
|
||||||
validMisc[form->formID] = form;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_map<RE::FormID, RE::TESObjectMISC*> GetMiscList()
|
|
||||||
{
|
|
||||||
return validMisc;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace MiscCheck
|
|
||||||
{
|
|
||||||
void PreloadMiscList();
|
|
||||||
|
|
||||||
std::unordered_map<RE::FormID, RE::TESObjectMISC*> GetMiscList();
|
|
||||||
}
|
|
@ -1,9 +1,5 @@
|
|||||||
#include "Papyrus.h"
|
#include "Papyrus.h"
|
||||||
#include "BookCheck.h"
|
#include "PapyrusFunctions.h"
|
||||||
#include "MiscCheck.h"
|
|
||||||
|
|
||||||
#include "Functions/ObjectReference.h"
|
|
||||||
#include "Functions/ArtifactTracker.h"
|
|
||||||
|
|
||||||
namespace Papyrus
|
namespace Papyrus
|
||||||
{
|
{
|
||||||
@ -16,8 +12,7 @@ namespace Papyrus
|
|||||||
|
|
||||||
logger::info("{:*^30}", "FUNCTIONS"sv);
|
logger::info("{:*^30}", "FUNCTIONS"sv);
|
||||||
|
|
||||||
ObjectReference::Bind(*a_vm);
|
PapyrusFunctions::Bind(*a_vm);
|
||||||
ArtifactTracker::Bind(*a_vm);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
164
Source/ArtifactTrackerDLL/src/PapyrusFunctions.h
Normal file
164
Source/ArtifactTrackerDLL/src/PapyrusFunctions.h
Normal file
@ -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<RE::FormType>(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<RE::BGSListForm>()->ForEachForm([&](RE::TESForm& a_exform) {
|
||||||
|
const auto refrItem = a_exform.As<RE::TESObjectREFR>();
|
||||||
|
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<RE::TESObjectREFR>();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
78
Source/ArtifactTrackerDLL/src/Util.h
Normal file
78
Source/ArtifactTrackerDLL/src/Util.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
void RemoveListItem(RE::BGSListForm* a_List, RE::TESForm* a_form)
|
||||||
|
{
|
||||||
|
using func_t = decltype(&RemoveListItem);
|
||||||
|
REL::Relocation<func_t> 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_t> 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<RE::TESObjectREFR>();
|
||||||
|
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;
|
||||||
|
}
|
@ -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 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
|
ObjectReference function GetCellStorage(ObjectReference ref, FormList refList, Form refToCreate, bool autoCreate = true) native global
|
||||||
|
|
||||||
function SyncCellStorage(ObjectReference cellStorage, FormList excludeContainers) 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
|
int function AddArtifactsFromFollowersToList(FormList targetList, FormList excludeList = None) native global
|
||||||
|
|
||||||
|
function OnItemPickup(Form item) native global
|
||||||
|
@ -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
|
|
@ -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
|
|
@ -29,6 +29,40 @@ endevent
|
|||||||
|
|
||||||
Event OnPlayerLoadGame()
|
Event OnPlayerLoadGame()
|
||||||
AddInventoryEventFilter(ETR_ItemsStored)
|
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()
|
Location currentLocation = PlayerRef.GetCurrentLocation()
|
||||||
bAtHome = currentLocation && currentLocation.HasKeyword(LocTypePlayerHouse)
|
bAtHome = currentLocation && currentLocation.HasKeyword(LocTypePlayerHouse)
|
||||||
if bAtHome
|
if bAtHome
|
||||||
@ -108,6 +142,7 @@ Event OnUpdate()
|
|||||||
|
|
||||||
ETR_ItemsStored.Revert()
|
ETR_ItemsStored.Revert()
|
||||||
Form[] aContainers = ETR_PersistentStorageList.ToArray()
|
Form[] aContainers = ETR_PersistentStorageList.ToArray()
|
||||||
|
|
||||||
int n = aContainers.length
|
int n = aContainers.length
|
||||||
while n > 0
|
while n > 0
|
||||||
n -= 1
|
n -= 1
|
||||||
@ -117,7 +152,6 @@ Event OnUpdate()
|
|||||||
ETR_ItemsFound.Revert()
|
ETR_ItemsFound.Revert()
|
||||||
ETR_Functions.AddArtifactsToList(PlayerRef, ETR_ItemsFound, ETR_ItemsStored)
|
ETR_Functions.AddArtifactsToList(PlayerRef, ETR_ItemsFound, ETR_ItemsStored)
|
||||||
ETR_Functions.AddArtifactsFromFollowersToList(ETR_ItemsFound, ETR_ItemsStored)
|
ETR_Functions.AddArtifactsFromFollowersToList(ETR_ItemsFound, ETR_ItemsStored)
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
bBusy = false
|
bBusy = false
|
||||||
@ -139,41 +173,7 @@ event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemRefere
|
|||||||
RegisterForSingleUpdate(0.5)
|
RegisterForSingleUpdate(0.5)
|
||||||
endif
|
endif
|
||||||
elseif bAtHome
|
elseif bAtHome
|
||||||
ObjectReference cellStorage = ETR_Functions.GetCellStorage(PlayerRef, ETR_PersistentStorageList, ETR_CellStorageContainer)
|
ETR_Functions.OnItemPickup(akBaseItem)
|
||||||
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
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
endevent
|
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
|
|
||||||
|
Loading…
Reference in New Issue
Block a user