1
Fork 0

Moved most synchronous operations to DLL; decoded Formlist.Revert and FormList.RemoveAddedForm

ae-1.6.629
Eddoursul 2 years ago
parent bb427cc419
commit a7229e0bea
  1. BIN
      Artifact Tracker.esp
  2. BIN
      SKSE/Plugins/ArtifactTrackerFunctions.dll
  3. BIN
      Scripts/ETR_Functions.pex
  4. BIN
      Scripts/ETR_TrackFoundItems.pex
  5. BIN
      Scripts/ETR_TrackNewItems.pex
  6. BIN
      Scripts/ETR_TrackStoredItems.pex
  7. 3
      Source/ArtifactTrackerDLL/CMakeLists.txt
  8. 321
      Source/ArtifactTrackerDLL/src/ArtifactTracker.cpp
  9. 29
      Source/ArtifactTrackerDLL/src/ArtifactTracker.h
  10. 27
      Source/ArtifactTrackerDLL/src/BookCheck.cpp
  11. 4
      Source/ArtifactTrackerDLL/src/BookCheck.h
  12. 38
      Source/ArtifactTrackerDLL/src/EventListener.cpp
  13. 29
      Source/ArtifactTrackerDLL/src/EventListener.h
  14. 288
      Source/ArtifactTrackerDLL/src/Functions/ArtifactTracker.h
  15. 35
      Source/ArtifactTrackerDLL/src/Functions/ObjectReference.h
  16. 17
      Source/ArtifactTrackerDLL/src/Main.cpp
  17. 28
      Source/ArtifactTrackerDLL/src/MiscCheck.cpp
  18. 8
      Source/ArtifactTrackerDLL/src/MiscCheck.h
  19. 9
      Source/ArtifactTrackerDLL/src/Papyrus.cpp
  20. 164
      Source/ArtifactTrackerDLL/src/PapyrusFunctions.h
  21. 78
      Source/ArtifactTrackerDLL/src/Util.h
  22. 8
      Source/Scripts/ETR_Functions.psc
  23. 168
      Source/Scripts/ETR_TrackFoundItems.psc
  24. 68
      Source/Scripts/ETR_TrackNewItems.psc
  25. 72
      Source/Scripts/ETR_TrackStoredItems.psc

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
src/Main.cpp
src/Papyrus.cpp
src/ArtifactTracker.cpp
src/EventListener.cpp
src/BookCheck.cpp
src/MiscCheck.cpp
${CMAKE_CURRENT_BINARY_DIR}/version.rc)

@ -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");
}
}
}

@ -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
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)
{
if (!path) {

@ -3,10 +3,6 @@
// Mostly borrowed from Fix Note icon for SkyUI by 0xC0000005
namespace BookCheck
{
void PreloadBookList();
std::unordered_map<RE::FormID, RE::TESObjectBOOK*> GetBookList();
const char* ExtractFileName(const char* const path, size_t& fileNameLength);
// Performs a case-sensitive search for 'needle' in the specified string,

@ -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;
}

@ -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 "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());

@ -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 "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;
}

@ -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);
}
}

@ -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 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

@ -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()
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

Loading…
Cancel
Save