ae-1.6.629
parent
bb427cc419
commit
a7229e0bea
25 changed files with 710 additions and 684 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -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); |
||||
} |
@ -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,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(); |
||||
} |
@ -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; |
||||
} |
@ -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 |
Loading…
Reference in new issue