diff --git a/SKSE/Plugins/ArtifactTracker.dll b/SKSE/Plugins/ArtifactTracker.dll index 8b28012..ae8e490 100644 Binary files a/SKSE/Plugins/ArtifactTracker.dll and b/SKSE/Plugins/ArtifactTracker.dll differ diff --git a/Source/ArtifactTrackerDLL/src/ArtifactTracker.cpp b/Source/ArtifactTrackerDLL/src/ArtifactTracker.cpp index 111d1f5..1c598d5 100644 --- a/Source/ArtifactTrackerDLL/src/ArtifactTracker.cpp +++ b/Source/ArtifactTrackerDLL/src/ArtifactTracker.cpp @@ -303,7 +303,7 @@ namespace ArtifactTracker } } - void SyncCellStorage() + void SyncCellStorage(RE::FormID a_ignoreRef) { if (!IsHome()) { #ifdef _DEBUG @@ -322,6 +322,10 @@ namespace ArtifactTracker const auto inv = g_cellStorage->GetInventory(); for (const auto& a_ref : cell->references) { + if (a_ignoreRef == a_ref->formID) { + continue; + } + const auto baseObj = a_ref->GetBaseObject(); if (IsValidContainer(a_ref.get())) { @@ -382,6 +386,7 @@ namespace ArtifactTracker 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); } @@ -393,124 +398,182 @@ namespace ArtifactTracker void OnContainerChanged(const RE::TESContainerChangedEvent* a_event, RE::TESForm* form) { - if (a_event->newContainer == 0x14) { - - if (a_event->oldContainer) { + if (a_event->newContainer) { // added to a container or actor - if (g_persistentMap.contains(a_event->oldContainer)) { + if (a_event->newContainer == 0x14) { // acquired by player - // Items in persistent containers are marked as stored by definition, no need to check the list - if (!RefListHasItem(g_persistentStorage, a_event->baseObj)) { - ListRemoveItem(g_listStored, form); - g_listFound->AddForm(form); - } + if (a_event->oldContainer) { - return; + if (const auto it = g_persistentMap.find(a_event->oldContainer); it != g_persistentMap.end()) { // moved from a persistent container - } else if (g_cellStorage) { // non-persistent container at home - - // g_bContainerMode is expected to be true, enabling processing after closing container. - // Reachable by Loot Menu or a mod, retrieving items via a spell of after scanning containers in cell. - // In extreme cases of mass retrieval, this can result in a few frames lag. + const auto ref = it->second; - #ifdef _DEBUG - SKSE::log::info("Synchronous processing of a non-persistent container (moved to player)"); - #endif + if (ref && GetItemCount(ref, form) - a_event->itemCount <= 0) { // no items left in the container + for (const auto& persref : g_persistentMap) { + if (persref.second != ref) { + if (GetItemCount(persref.second, form) > 0) { + // if other containers have it, do nothing + return; + } + } + } + ListRemoveItem(g_listStored, form); + g_listFound->AddForm(form); + } + + } else if (g_cellStorage && !g_bHomeContainer) { // taken from a container at home - SyncCellStorage(); + const auto container = RE::TESForm::LookupByID(a_event->oldContainer); + if (container && GetItemCount(container, form) - a_event->itemCount <= 0) { + SyncCellStorage(container->formID); + } + } return; } - } else if (g_cellStorage && !g_listNew->HasForm(form)) { - // Items picked up at home are handled in the perk - 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); + } - // Instead of looking up in huge g_listNew, we check smaller g_listStored and g_listFound. - // Mass retrieval of found items is rare, so we check the stored list first. - if (g_listStored->HasForm(form) || g_listFound->HasForm(form)) { - return; - } + } else if (g_cellStorage && g_cellStorage->formID == a_event->newContainer) { + return; // ignore cell storage - // It's a new item, move it to found - ListRemoveItem(g_listNew, form); - g_listFound->AddForm(form); + } else if (g_persistentMap.contains(a_event->newContainer)) { // stored in a registered persistent container - return; + if (!g_listStored->HasForm(form)) { + ListRemoveItem(g_listFound, form); + g_listStored->AddForm(form); + } - } else if (a_event->oldContainer == 0x14) { - - // Items moved from player's inventory + } else { + const auto ref = RE::TESForm::LookupByID(a_event->newContainer); + if (ref->Is(RE::FormType::ActorCharacter)) { + if (ref->As()->IsPlayerTeammate()) { // acquired by companion + if (!g_listFound->HasForm(form) && !g_listStored->HasForm(form)) { + ListRemoveItem(g_listNew, form); + g_listFound->AddForm(form); + } + } - if (!a_event->newContainer) { // no destination container + } else { + const auto container = ref->As(); + if (container) { + if (g_cellStorage && container->GetParentCell() == RE::PlayerCharacter::GetSingleton()->GetParentCell()) { // stored at home - if (g_cellStorage && a_event->reference) { // dropped or placed on rack at home - if (GetItemCount(g_cellStorage, form) <= 0) { - #ifdef _DEBUG - SKSE::log::info("Added dropped {} to cell storage", form->GetName()); - RE::DebugNotification("Adding to cell storage"); - #endif - g_cellStorage->AddObjectToContainer(form->As(), nullptr, 1, nullptr); + if (!g_bHomeContainer && !g_listStored->HasForm(form)) { + ListRemoveItem(g_listFound, form); + ListRemoveItem(g_listNew, form); + g_listStored->AddForm(form); + } + + // During OnContainerChanged, InventoryChanges do not have the current change included yet + } else if (a_event->oldContainer == 0x14 && !g_listStored->HasForm(form) && (GetItemCount(RE::PlayerCharacter::GetSingleton(), form) - a_event->itemCount <= 0) && !FollowersHaveItem(form)) { + // disposed by player + ListRemoveItem(g_listFound, form); + g_listNew->AddForm(form); + } } - ListRemoveItem(g_listFound, form); - ListRemoveItem(g_listNew, form); - g_listStored->AddForm(form); - return; } + } - if (g_listStored->HasForm(form)) { - return; + } 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 (GetItemCount(g_cellStorage, form) <= 0) { + #ifdef _DEBUG + SKSE::log::info("Added dropped {} to cell storage", form->GetName()); + RE::DebugNotification("Adding to cell storage"); + #endif + g_cellStorage->AddObjectToContainer(form->As(), nullptr, 1, nullptr); } - // NB: During OnContainerChanged, InventoryChanges do not have the current change included yet - if ((GetItemCount(RE::PlayerCharacter::GetSingleton(), form) - a_event->itemCount <= 0) && !FollowersHaveItem(form)) { + if (!g_listStored->HasForm(form)) { ListRemoveItem(g_listFound, form); ListRemoveItem(g_listNew, form); - g_listNew->AddForm(form); + g_listStored->AddForm(form); } - return; - } - - if (g_persistentMap.contains(a_event->newContainer)) { // moved to a persistent container + } else if (a_event->oldContainer == 0x14) { // dropped, dismantled, consumed, removed by a script if (!g_listStored->HasForm(form)) { - ListRemoveItem(g_listFound, form); - g_listStored->AddForm(form); + if ((GetItemCount(RE::PlayerCharacter::GetSingleton(), form) - a_event->itemCount <= 0) && !FollowersHaveItem(form)) { + if (g_listFound->HasForm(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) { // stored at home in a non-persistent/non-registered container - - // g_bContainerMode is expected to be true, enabling processing after closing container. - // Can be hit by autosorting mods. Most of them work with persistent containers, which should be added to the list of persistent containers. + } else if (g_cellStorage && g_cellStorage->formID == a_event->oldContainer) { + return; // ignore cell storage - #ifdef _DEBUG - SKSE::log::info("Synchronous processing of a non-persistent container (moved from player)"); - #endif + } else if (const auto it = g_persistentMap.find(a_event->oldContainer); it != g_persistentMap.end()) { // deleted from a persistent container - const auto targetContainer = RE::TESForm::LookupByID(a_event->newContainer); + const auto ref = it->second; - if (IsValidContainer(targetContainer)) { - if (GetItemCount(g_cellStorage, form) <= 0) { - g_cellStorage->AddObjectToContainer(form->As(), nullptr, 1, nullptr); + if (ref && GetItemCount(ref, form) - a_event->itemCount <= 0) { // no items left in the container + for (const auto& persref : g_persistentMap) { + if (persref.second != ref) { + if (GetItemCount(persref.second, form) > 0) { + // 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()->IsPlayerTeammate() && GetItemCount(ref->As(), form) - a_event->itemCount <= 0) { // removed from companion (probably, disarmed) + if (!g_listStored->HasForm(form) && g_listFound->HasForm(form)) { + + if (GetItemCount(RE::PlayerCharacter::GetSingleton(), form) <= 0) { + // 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) > 0) { + // other companion has it, do nothing + return; + } + } + } + } + ListRemoveItem(g_listFound, form); + g_listNew->AddForm(form); + } + + } } - - if (!g_listStored->HasForm(form)) { - ListRemoveItem(g_listFound, form); - g_listStored->AddForm(form); + + } else { + const auto container = ref->As(); + if (container) { + if (g_cellStorage && container->GetParentCell() == RE::PlayerCharacter::GetSingleton()->GetParentCell()) { // deleted from container at home + + if (GetItemCount(container, form) - a_event->itemCount <= 0) { + SyncCellStorage(container->formID); + } + + } } } - - // NB: During OnContainerChanged, InventoryChanges do not have the current change included yet - } else if (g_listFound->HasForm(form) && (GetItemCount(RE::PlayerCharacter::GetSingleton(), form) - a_event->itemCount <= 0) && !FollowersHaveItem(form)) { - ListRemoveItem(g_listFound, form); - g_listNew->AddForm(form); } - } else if (g_cellStorage && a_event->reference) { - // Items dropped by someone else at home. - // Mainly for compatibility with Immersive Display Overhaul - SyncCellStorage(); } } diff --git a/Source/ArtifactTrackerDLL/src/ArtifactTracker.h b/Source/ArtifactTrackerDLL/src/ArtifactTracker.h index 2a88d68..8fdcba3 100644 --- a/Source/ArtifactTrackerDLL/src/ArtifactTracker.h +++ b/Source/ArtifactTrackerDLL/src/ArtifactTracker.h @@ -33,7 +33,7 @@ namespace ArtifactTracker void OnCellEnter(RE::BGSLocation* location, RE::TESObjectCELL* cell); - void SyncCellStorage(); + void SyncCellStorage(RE::FormID a_ignoreRef = NULL); void OnContainerChanged(const RE::TESContainerChangedEvent* a_event, RE::TESForm* form); diff --git a/Source/ArtifactTrackerDLL/src/EventListener.cpp b/Source/ArtifactTrackerDLL/src/EventListener.cpp index 2ddf0f9..ba97498 100644 --- a/Source/ArtifactTrackerDLL/src/EventListener.cpp +++ b/Source/ArtifactTrackerDLL/src/EventListener.cpp @@ -31,11 +31,9 @@ auto EventListener::ProcessEvent( RE::BSTEventSource* a_eventSource) -> RE::BSEventNotifyControl { - if (!ArtifactTracker::g_bHomeContainer && (a_event->newContainer == 0x14 || a_event->oldContainer == 0x14 || (ArtifactTracker::IsHome() && a_event->reference))) { - const auto form = ArtifactTracker::GetArtifactByID(a_event->baseObj); - if (form) { - ArtifactTracker::OnContainerChanged(a_event, form); - } + const auto form = ArtifactTracker::GetArtifactByID(a_event->baseObj); + if (form) { + ArtifactTracker::OnContainerChanged(a_event, form); } return RE::BSEventNotifyControl::kContinue;