630 lines
20 KiB
C++
630 lines
20 KiB
C++
#include "ArtifactTracker.h"
|
|
#include "BookCheck.h"
|
|
#include "EventListener.h"
|
|
#include "Util.h"
|
|
|
|
namespace ArtifactTracker
|
|
{
|
|
bool g_bLoaded;
|
|
bool g_bHomeContainer;
|
|
RE::TESBoundObject* g_cellContainer;
|
|
RE::BGSListForm* g_listNew;
|
|
RE::BGSListForm* g_listStored;
|
|
RE::BGSListForm* g_listFound;
|
|
RE::BGSListForm* g_persistentStorage;
|
|
RE::BGSKeyword* g_homeKeyword;
|
|
std::unordered_map<RE::FormID, RE::TESForm*> g_artifactMap;
|
|
std::unordered_set<RE::FormType> g_artifactFormTypes;
|
|
std::unordered_map<RE::FormID, RE::TESObjectREFR*> g_persistentMap;
|
|
RE::TESObjectREFR* g_cellStorage;
|
|
|
|
bool Init()
|
|
{
|
|
if (g_bLoaded) {
|
|
return true;
|
|
}
|
|
|
|
SKSE::GetModCallbackEventSource()->RemoveEventSink(EventListener::GetSingleton());
|
|
|
|
const auto dataHandler = RE::TESDataHandler::GetSingleton();
|
|
|
|
if (!dataHandler) {
|
|
SKSE::log::error("Unable to call RE::TESDataHandler::GetSingleton()");
|
|
RE::DebugMessageBox("Unable to initialize Artifact Tracker.");
|
|
return false;
|
|
}
|
|
|
|
g_cellContainer = dataHandler->LookupForm(0x800, "Artifact Tracker.esp")->As<RE::TESBoundObject>(); // ETR_CellStorageContainer
|
|
|
|
g_listNew = dataHandler->LookupForm<RE::BGSListForm>(0x803, "Artifact Tracker.esp"); // ETR_ItemsNew
|
|
g_listStored = dataHandler->LookupForm<RE::BGSListForm>(0x805, "Artifact Tracker.esp"); // ETR_ItemsStored
|
|
g_listFound = dataHandler->LookupForm<RE::BGSListForm>(0x806, "Artifact Tracker.esp"); // ETR_ItemsFound
|
|
g_persistentStorage = dataHandler->LookupForm<RE::BGSListForm>(0x807, "Artifact Tracker.esp"); // ETR_PersistentStorageList
|
|
|
|
g_homeKeyword = dataHandler->LookupForm<RE::BGSKeyword>(0xFC1A3, "Skyrim.esm"); // LocTypePlayerHouse
|
|
|
|
const auto recipeKeyword = dataHandler->LookupForm<RE::BGSKeyword>(0xF5CB0, "Skyrim.esm"); // VendorItemRecipe
|
|
const auto excludeKeywords = dataHandler->LookupForm<RE::BGSListForm>(0x801, "Artifact Tracker.esp"); // ETR_ExcludeMiscKeywords
|
|
const auto extraArtifactKeyword = dataHandler->LookupForm<RE::BGSKeyword>(0xDE3FD3, "Update.esm"); // ETR_ExtraArtifact
|
|
const auto notArtifactKeyword = dataHandler->LookupForm<RE::BGSKeyword>(0xDE3FD4, "Update.esm"); // ETR_NotArtifact
|
|
|
|
if (!g_cellContainer || !g_listNew || !g_listStored || !g_listFound || !g_persistentStorage || !g_homeKeyword || !recipeKeyword || !excludeKeywords || !extraArtifactKeyword || !notArtifactKeyword) {
|
|
SKSE::log::warn("Unable to load data from Artifact Tracker.esp");
|
|
RE::DebugMessageBox("Unable to load data from Artifact Tracker.esp, the mod is disabled.");
|
|
return false;
|
|
}
|
|
|
|
// Preloading item lists
|
|
|
|
std::map<std::string, bool> settings{
|
|
{ "DumpItemList", false },
|
|
};
|
|
LoadINI(&settings, "Data/SKSE/Plugins/ArtifactTracker.ini");
|
|
|
|
g_artifactFormTypes.insert(RE::FormType::Weapon);
|
|
for (const auto& form : dataHandler->GetFormArray<RE::TESObjectWEAP>()) {
|
|
if (form->GetPlayable() && !form->IsBound() && !form->weaponData.flags.all(RE::TESObjectWEAP::Data::Flag::kCantDrop) && !form->IsDeleted()) {
|
|
if ((!form->HasKeyword(notArtifactKeyword) || form->HasKeyword(extraArtifactKeyword)) && strlen(form->GetName()) > 0) {
|
|
g_artifactMap[form->formID] = form;
|
|
}
|
|
}
|
|
}
|
|
g_artifactMap.erase(0x1F4); // Unarmed
|
|
|
|
g_artifactFormTypes.insert(RE::FormType::Armor);
|
|
for (const auto& form : dataHandler->GetFormArray<RE::TESObjectARMO>()) {
|
|
if (form->GetPlayable() && !form->IsDeleted()) {
|
|
if ((!form->HasKeyword(notArtifactKeyword) || form->HasKeyword(extraArtifactKeyword)) && strlen(form->GetName()) > 0) {
|
|
g_artifactMap[form->formID] = form;
|
|
}
|
|
}
|
|
}
|
|
g_artifactMap.erase(0xD64); // SkinNaked
|
|
g_artifactMap.erase(0x69CE3); // SkinNakedBeast
|
|
g_artifactMap.erase(0xCDD86); // SkinNakedWerewolfBeast
|
|
|
|
g_artifactFormTypes.insert(RE::FormType::Book);
|
|
for (const auto& form : dataHandler->GetFormArray<RE::TESObjectBOOK>()) {
|
|
if (!form->TeachesSpell() && !form->IsDeleted() && (form->HasKeyword(recipeKeyword) || form->HasKeyword(extraArtifactKeyword) || BookCheck::IsBook(form)) && !form->HasKeyword(notArtifactKeyword)) {
|
|
g_artifactMap[form->formID] = form;
|
|
}
|
|
}
|
|
|
|
g_artifactFormTypes.insert(RE::FormType::Misc);
|
|
for (const auto& form : dataHandler->GetFormArray<RE::TESObjectMISC>()) {
|
|
if (form->GetPlayable() && !form->IsDeleted() && (form->HasKeyword(extraArtifactKeyword) || !form->HasKeywordInList(excludeKeywords, false)) && !form->HasKeyword(notArtifactKeyword) && strlen(form->GetName()) > 0) {
|
|
g_artifactMap[form->formID] = form;
|
|
}
|
|
}
|
|
g_artifactMap.erase(0xA); // Lockpick
|
|
g_artifactMap.erase(0xF); // Gold
|
|
|
|
for (const auto& form : dataHandler->GetFormArray<RE::AlchemyItem>()) {
|
|
if (!form->IsDeleted() && form->HasKeyword(extraArtifactKeyword) && !form->HasKeyword(notArtifactKeyword)) {
|
|
g_artifactMap[form->formID] = form;
|
|
g_artifactFormTypes.insert(RE::FormType::AlchemyItem);
|
|
}
|
|
}
|
|
|
|
for (const auto& form : dataHandler->GetFormArray<RE::IngredientItem>()) {
|
|
if (!form->IsDeleted() && form->HasKeyword(extraArtifactKeyword) && !form->HasKeyword(notArtifactKeyword)) {
|
|
g_artifactMap[form->formID] = form;
|
|
g_artifactFormTypes.insert(RE::FormType::Ingredient);
|
|
}
|
|
}
|
|
|
|
for (const auto& form : dataHandler->GetFormArray<RE::TESSoulGem>()) {
|
|
if (!form->IsDeleted() && form->HasKeyword(extraArtifactKeyword) && !form->HasKeyword(notArtifactKeyword)) {
|
|
g_artifactMap[form->formID] = form;
|
|
g_artifactFormTypes.insert(RE::FormType::SoulGem);
|
|
}
|
|
}
|
|
|
|
// Fishing CC (remove, when KID adds formlist support)
|
|
const auto plaqueFish = dataHandler->LookupForm<RE::BGSListForm>(0xF4B, "ccBGSSSE001-Fish.esm"); // ccBGSSSE001_FishPlaqueGiftFilterList
|
|
if (plaqueFish) {
|
|
plaqueFish->ForEachForm([&](RE::TESForm& a_form) {
|
|
g_artifactMap[a_form.formID] = &a_form;
|
|
return true;
|
|
});
|
|
}
|
|
|
|
OnGameLoad();
|
|
EventListener::Install();
|
|
g_bLoaded = true;
|
|
|
|
SKSE::log::info("Total artifacts: {}", g_artifactMap.size());
|
|
|
|
if (settings.at("DumpItemList")) {
|
|
for (const auto& item : g_artifactMap) {
|
|
SKSE::log::info("[{:08X}] {}", item.second->formID, item.second->GetName());
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool IsArtifact(const RE::TESForm* a_form)
|
|
{
|
|
return a_form && g_artifactFormTypes.contains(a_form->GetFormType()) && g_artifactMap.contains(a_form->formID);
|
|
}
|
|
|
|
RE::TESForm* GetArtifactByID(const RE::FormID a_formID)
|
|
{
|
|
if (!a_formID) {
|
|
return nullptr;
|
|
}
|
|
|
|
const auto it = g_artifactMap.find(a_formID);
|
|
return it != g_artifactMap.end() ? it->second : nullptr;
|
|
}
|
|
|
|
void OnGameLoad()
|
|
{
|
|
#ifdef _DEBUG
|
|
SKSE::log::info("OnGameLoad");
|
|
#endif
|
|
|
|
g_persistentMap.clear();
|
|
g_persistentStorage->ForEachForm([&](RE::TESForm& a_exform) {
|
|
if (&a_exform) {
|
|
g_persistentMap[a_exform.formID] = a_exform.As<RE::TESObjectREFR>();
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
|
|
void SetContainerMode(const bool bOpening)
|
|
{
|
|
if (bOpening) {
|
|
|
|
const auto refr = RE::TESObjectREFR::LookupByHandle(RE::ContainerMenu::GetTargetRefHandle());
|
|
|
|
g_bHomeContainer = IsHome()
|
|
&& refr
|
|
&& IsInSameCell(refr.get())
|
|
&& !g_persistentMap.contains(refr.get()->formID);
|
|
|
|
#ifdef _DEBUG
|
|
if (g_bHomeContainer) {
|
|
RE::DebugNotification("Delayed processing enabled");
|
|
}
|
|
#endif
|
|
|
|
} else if (g_bHomeContainer) {
|
|
g_bHomeContainer = false;
|
|
SyncCellStorage();
|
|
}
|
|
}
|
|
|
|
bool IsHome()
|
|
{
|
|
return (bool)g_cellStorage;
|
|
}
|
|
|
|
bool ToggleHomeMode(RE::TESObjectREFR* cellStorage)
|
|
{
|
|
if (cellStorage) {
|
|
g_bHomeContainer = false;
|
|
g_cellStorage = cellStorage;
|
|
RE::UI::GetSingleton()->AddEventSink<RE::MenuOpenCloseEvent>(EventListener::GetSingleton());
|
|
#ifdef _DEBUG
|
|
SKSE::log::info("Home mode ON");
|
|
#endif
|
|
return true;
|
|
} else if (g_cellStorage) {
|
|
g_bHomeContainer = false;
|
|
g_cellStorage = nullptr;
|
|
RE::UI::GetSingleton()->RemoveEventSink<RE::MenuOpenCloseEvent>(EventListener::GetSingleton());
|
|
#ifdef _DEBUG
|
|
SKSE::log::info("Home mode OFF");
|
|
#endif
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IsValidContainer(RE::TESObjectREFR* a_ref)
|
|
{
|
|
if (!a_ref) {
|
|
return false;
|
|
}
|
|
|
|
const auto baseObj = a_ref->GetBaseObject();
|
|
|
|
return baseObj->formType == RE::FormType::Container || (baseObj->formType == RE::FormType::NPC && !a_ref->IsDisabled() && baseObj->As<RE::TESNPC>()->GetRace()->formID == 0x0010760A);
|
|
}
|
|
|
|
void OnCellEnter(const RE::FormID a_formID)
|
|
{
|
|
RE::TESObjectCELL* cell = RE::TESForm::LookupByID<RE::TESObjectCELL>(a_formID);
|
|
RE::BGSLocation* location = cell ? cell->GetLocation() : nullptr;
|
|
|
|
if (!cell || !location || !cell->IsInteriorCell() || !location->HasKeyword(g_homeKeyword)) {
|
|
if (IsHome()) {
|
|
RE::ScriptEventSourceHolder::GetSingleton()->RemoveEventSink<RE::TESCellFullyLoadedEvent>(EventListener::GetSingleton());
|
|
ToggleHomeMode(nullptr);
|
|
}
|
|
return;
|
|
}
|
|
|
|
RE::TESObjectREFR* cellStorage = nullptr;
|
|
|
|
g_persistentStorage->ForEachForm([&](RE::TESForm& a_form) {
|
|
const auto refr = a_form.As<RE::TESObjectREFR>();
|
|
if (refr && refr->GetParentCell()->formID == a_formID) {
|
|
cellStorage = refr;
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
ToggleHomeMode(cellStorage);
|
|
|
|
if (!cellStorage) {
|
|
RE::ScriptEventSourceHolder::GetSingleton()->AddEventSink<RE::TESCellFullyLoadedEvent>(EventListener::GetSingleton());
|
|
}
|
|
}
|
|
|
|
void OnCellEnter(const RE::BGSLocation* location, const RE::TESObjectCELL* cell)
|
|
{
|
|
if (!location || !cell->IsInteriorCell() || cell != RE::PlayerCharacter::GetSingleton()->GetParentCell() || !location->HasKeyword(g_homeKeyword)) {
|
|
ToggleHomeMode(nullptr);
|
|
return;
|
|
}
|
|
|
|
RE::TESObjectREFR* cellStorage = nullptr;
|
|
|
|
for (const auto& a_ref : cell->references) {
|
|
if (a_ref.get()->GetBaseObject() == g_cellContainer) {
|
|
cellStorage = a_ref.get();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (cellStorage) {
|
|
if (!g_persistentStorage->HasForm(cellStorage)) {
|
|
g_persistentStorage->AddForm(cellStorage);
|
|
g_persistentMap[cellStorage->formID] = cellStorage;
|
|
}
|
|
ToggleHomeMode(cellStorage);
|
|
return;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
SKSE::log::info("Adding new storage in {}", cell->GetName());
|
|
#endif
|
|
|
|
cellStorage = RE::PlayerCharacter::GetSingleton()->PlaceObjectAtMe(g_cellContainer, true).get();
|
|
|
|
if (cellStorage) {
|
|
cellStorage->Disable();
|
|
g_persistentStorage->AddForm(cellStorage);
|
|
g_persistentMap[cellStorage->formID] = cellStorage;
|
|
ToggleHomeMode(cellStorage);
|
|
SyncCellStorage();
|
|
} else {
|
|
SKSE::log::error("Failed to create cell storage in OnCellEnter");
|
|
ToggleHomeMode(nullptr);
|
|
}
|
|
}
|
|
|
|
void SyncCellStorage(const RE::TESObjectREFR* a_ignoreRef)
|
|
{
|
|
if (!IsHome()) {
|
|
#ifdef _DEBUG
|
|
SKSE::log::info("SyncCellStorage called while not at home");
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
SKSE::log::info("Running SyncCellStorage");
|
|
#endif
|
|
|
|
std::unordered_set<RE::FormID> cellItems;
|
|
|
|
const auto cell = g_cellStorage->GetParentCell();
|
|
const auto inv = g_cellStorage->GetInventory();
|
|
|
|
for (const auto& a_ref : cell->references) {
|
|
if (a_ignoreRef && a_ignoreRef->formID == a_ref->formID) {
|
|
continue;
|
|
}
|
|
|
|
const auto baseObj = a_ref->GetBaseObject();
|
|
|
|
if (IsValidContainer(a_ref.get())) {
|
|
if (g_cellContainer->formID == baseObj->formID || baseObj->formID == 0xDC9E7 || g_persistentMap.contains(a_ref->formID)) { // 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.insert(item->formID);
|
|
if (IsArtifact(item)) {
|
|
if (inv.find(item) == inv.end()) {
|
|
g_cellStorage->AddObjectToContainer(item, nullptr, 1, nullptr);
|
|
}
|
|
if (!g_listStored->HasForm(item)) {
|
|
ListRemoveItem(g_listNew, item);
|
|
ListRemoveItem(g_listFound, item);
|
|
g_listStored->AddForm(item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (a_ref->IsDisabled() || a_ref->IsMarkedForDeletion()) {
|
|
continue;
|
|
}
|
|
|
|
if (cellItems.contains(baseObj->formID)) {
|
|
continue;
|
|
}
|
|
|
|
cellItems.insert(baseObj->formID);
|
|
|
|
if (!IsArtifact(baseObj)) {
|
|
continue;
|
|
}
|
|
|
|
if (inv.find(baseObj) == inv.end()) {
|
|
g_cellStorage->AddObjectToContainer(baseObj, nullptr, 1, nullptr);
|
|
}
|
|
|
|
if (!g_listStored->HasForm(baseObj)) {
|
|
ListRemoveItem(g_listNew, baseObj);
|
|
ListRemoveItem(g_listFound, baseObj);
|
|
g_listStored->AddForm(baseObj);
|
|
}
|
|
}
|
|
|
|
for (const auto& [item, data] : inv) {
|
|
const auto& [count, entry] = data;
|
|
if (count > 0 && !cellItems.contains(item->formID)) {
|
|
g_cellStorage->RemoveItem(item, count, RE::ITEM_REMOVE_REASON::kRemove, nullptr, nullptr);
|
|
if (!RefListHasItem(g_persistentStorage, item->formID)) {
|
|
ListRemoveItem(g_listNew, item);
|
|
ListRemoveItem(g_listStored, item);
|
|
g_listFound->AddForm(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
cellItems.clear();
|
|
}
|
|
|
|
void OnContainerChanged(const RE::TESContainerChangedEvent* a_event, RE::TESForm* form)
|
|
{
|
|
if (a_event->newContainer) { // added to a container or actor
|
|
|
|
if (a_event->newContainer == 0x14) { // acquired by player
|
|
|
|
if (a_event->oldContainer) {
|
|
|
|
if (const auto it = g_persistentMap.find(a_event->oldContainer); it != g_persistentMap.end()) { // moved from a persistent container
|
|
|
|
const auto ref = it->second;
|
|
|
|
if (ref && GetItemCount(ref, form) <= 0) { // no items left in the container
|
|
for (const auto& persref : g_persistentMap) {
|
|
if (persref.second != ref) {
|
|
if (GetItemCount(persref.second, form)) {
|
|
// if other containers have it, do nothing
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
ListRemoveItem(g_listStored, form);
|
|
g_listFound->AddForm(form);
|
|
}
|
|
|
|
return;
|
|
|
|
} else if (g_cellStorage) { // taken from a container at home
|
|
|
|
if (!g_bHomeContainer) {
|
|
const auto container = RE::TESForm::LookupByID<RE::TESObjectREFR>(a_event->oldContainer);
|
|
if (container && !GetItemCount(container, form)) {
|
|
SyncCellStorage(container);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!g_listStored->HasForm(form) && !g_listFound->HasForm(form)) { // it's a new item, move it to found
|
|
ListRemoveItem(g_listNew, form);
|
|
g_listFound->AddForm(form);
|
|
}
|
|
|
|
} else if (g_cellStorage && g_cellStorage->formID == a_event->newContainer) {
|
|
return; // ignore cell storage
|
|
|
|
} else if (g_persistentMap.contains(a_event->newContainer)) { // stored in a registered persistent container
|
|
|
|
if (!g_listStored->HasForm(form)) {
|
|
ListRemoveItem(g_listFound, form);
|
|
g_listStored->AddForm(form);
|
|
}
|
|
|
|
} else {
|
|
const auto ref = RE::TESForm::LookupByID(a_event->newContainer);
|
|
if (ref->Is(RE::FormType::ActorCharacter)) {
|
|
if (ref->As<RE::Actor>()->IsPlayerTeammate()) { // acquired by companion
|
|
if (!g_listFound->HasForm(form) && !g_listStored->HasForm(form)) {
|
|
ListRemoveItem(g_listNew, form);
|
|
g_listFound->AddForm(form);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
const auto container = ref->As<RE::TESObjectREFR>();
|
|
if (container) {
|
|
if (g_cellStorage && IsInSameCell(container)) { // stored at home
|
|
|
|
if (!g_bHomeContainer && !g_listStored->HasForm(form)) {
|
|
ListRemoveItem(g_listFound, form);
|
|
ListRemoveItem(g_listNew, form);
|
|
g_listStored->AddForm(form);
|
|
}
|
|
|
|
} else if (a_event->oldContainer == 0x14 && !g_listStored->HasForm(form) && !GetItemCount(RE::PlayerCharacter::GetSingleton(), form) && !FollowersHaveItem(form)) {
|
|
// disposed by player
|
|
ListRemoveItem(g_listFound, form);
|
|
g_listNew->AddForm(form);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} else if (a_event->oldContainer) { // removed from container
|
|
|
|
if (g_cellStorage && a_event->reference) { // dropped or placed on rack at home by any actor
|
|
|
|
if (a_event->oldContainer != 0x14) {
|
|
const auto ref = RE::TESForm::LookupByID<RE::TESObjectREFR>(a_event->oldContainer);
|
|
if (!ref || !IsInSameCell(ref)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!GetItemCount(g_cellStorage, form)) {
|
|
#ifdef _DEBUG
|
|
SKSE::log::info("Added dropped {} to cell storage", form->GetName());
|
|
RE::DebugNotification("Adding to cell storage");
|
|
#endif
|
|
g_cellStorage->AddObjectToContainer(form->As<RE::TESBoundObject>(), nullptr, 1, nullptr);
|
|
}
|
|
|
|
if (!g_listStored->HasForm(form)) {
|
|
ListRemoveItem(g_listFound, form);
|
|
ListRemoveItem(g_listNew, form);
|
|
g_listStored->AddForm(form);
|
|
}
|
|
|
|
} else if (a_event->oldContainer == 0x14) { // dropped, consumed, dismantle, removed by script
|
|
|
|
if (!g_listStored->HasForm(form)) {
|
|
if (!GetItemCount(RE::PlayerCharacter::GetSingleton(), form) && !FollowersHaveItem(form)) {
|
|
ListRemoveItem(g_listFound, form);
|
|
g_listNew->AddForm(form);
|
|
|
|
} else if (!g_listFound->HasForm(form)) {
|
|
ListRemoveItem(g_listNew, form);
|
|
g_listFound->AddForm(form);
|
|
}
|
|
}
|
|
|
|
} else if (g_cellStorage && g_cellStorage->formID == a_event->oldContainer) {
|
|
return; // ignore cell storage
|
|
|
|
} else if (const auto it = g_persistentMap.find(a_event->oldContainer); it != g_persistentMap.end()) { // deleted from a persistent container
|
|
|
|
const auto ref = it->second;
|
|
|
|
if (ref && !GetItemCount(ref, form)) { // no items left in the container
|
|
for (const auto& persref : g_persistentMap) {
|
|
if (persref.second != ref) {
|
|
if (GetItemCount(persref.second, form)) {
|
|
// if other containers have it, do nothing
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
ListRemoveItem(g_listStored, form);
|
|
if (GetItemCount(RE::PlayerCharacter::GetSingleton(), form) || FollowersHaveItem(form)) {
|
|
g_listFound->AddForm(form);
|
|
} else {
|
|
g_listNew->AddForm(form);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
const auto ref = RE::TESForm::LookupByID(a_event->oldContainer);
|
|
|
|
if (ref->Is(RE::FormType::ActorCharacter)) {
|
|
if (ref->As<RE::Actor>()->IsPlayerTeammate() && GetItemCount(ref->As<RE::TESObjectREFR>(), form) <= 0) { // removed from companion (probably, disarmed)
|
|
if (!g_listStored->HasForm(form) && g_listFound->HasForm(form)) {
|
|
|
|
if (!GetItemCount(RE::PlayerCharacter::GetSingleton(), form)) {
|
|
// player does not have it, check companions
|
|
if (const auto processLists = RE::ProcessLists::GetSingleton(); processLists) {
|
|
for (auto& actorHandle : processLists->highActorHandles) {
|
|
if (auto actor = actorHandle.get(); actor && actor->IsPlayerTeammate() && actor->formID != ref->formID) {
|
|
if (GetItemCount(actor.get(), form)) {
|
|
// other companion has it, do nothing
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ListRemoveItem(g_listFound, form);
|
|
g_listNew->AddForm(form);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
} else {
|
|
const auto container = ref->As<RE::TESObjectREFR>();
|
|
if (container) {
|
|
if (g_cellStorage && IsInSameCell(container)) { // deleted from container at home
|
|
|
|
if (!GetItemCount(container, form)) {
|
|
SyncCellStorage(container);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AddRefArtifactsToList(RE::TESForm* a_refOrList, RE::BGSListForm* a_targetList, RE::BGSListForm* a_excludeList)
|
|
{
|
|
if (!a_refOrList || !a_targetList) {
|
|
SKSE::log::warn("Invalid arguments in AddRefArtifactsToList");
|
|
return;
|
|
}
|
|
|
|
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) {
|
|
AddRefArtifactsToList(refrItem, a_targetList, a_excludeList);
|
|
}
|
|
return true;
|
|
});
|
|
return;
|
|
}
|
|
|
|
const auto containerRef = a_refOrList->As<RE::TESObjectREFR>();
|
|
|
|
if (!containerRef) {
|
|
SKSE::log::warn("containerRef in AddRefArtifactsToList is not a reference");
|
|
return;
|
|
}
|
|
|
|
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);
|
|
ListRemoveItem(g_listNew, item.first);
|
|
}
|
|
}
|
|
}
|
|
}
|