1

Compare commits

...

2 Commits

7 changed files with 65 additions and 13 deletions

Binary file not shown.

View File

@ -6,7 +6,7 @@ message("Using toolchain file ${CMAKE_TOOLCHAIN_FILE}.")
######################################################################################################################## ########################################################################################################################
project( project(
ArtifactTracker ArtifactTracker
VERSION 1.0.3 VERSION 1.0.4
DESCRIPTION "Artifact Tracker" DESCRIPTION "Artifact Tracker"
LANGUAGES CXX) LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 23)

View File

@ -9,6 +9,7 @@ using namespace SKSE::log;
namespace ArtifactTracker namespace ArtifactTracker
{ {
bool g_bLoaded = false; bool g_bLoaded = false;
bool g_bSaveLoaded = true;
bool g_bHomeContainer = false; bool g_bHomeContainer = false;
bool g_bBookShelf = false; bool g_bBookShelf = false;
bool g_bTakeAll = false; bool g_bTakeAll = false;
@ -316,7 +317,7 @@ namespace ArtifactTracker
bool IsValidContainer(RE::TESObjectREFR* a_ref) bool IsValidContainer(RE::TESObjectREFR* a_ref)
{ {
if (!a_ref) { if (!a_ref || a_ref->IsMarkedForDeletion()) {
return false; return false;
} }
@ -327,6 +328,11 @@ namespace ArtifactTracker
void OnCellEnter(const RE::FormID a_formID) void OnCellEnter(const RE::FormID a_formID)
{ {
if (!g_bSaveLoaded) {
// Cell load events fire before formlists are loaded from savegame
return;
}
RE::TESObjectCELL* cell = RE::TESForm::LookupByID<RE::TESObjectCELL>(a_formID); RE::TESObjectCELL* cell = RE::TESForm::LookupByID<RE::TESObjectCELL>(a_formID);
RE::BGSLocation* location = cell ? cell->GetLocation() : nullptr; RE::BGSLocation* location = cell ? cell->GetLocation() : nullptr;
@ -339,40 +345,75 @@ namespace ArtifactTracker
} }
RE::TESObjectREFR* cellStorage = nullptr; RE::TESObjectREFR* cellStorage = nullptr;
bool bHasDupes = false;
g_persistentStorage->ForEachForm([&](RE::TESForm& a_form) { g_persistentStorage->ForEachForm([&](RE::TESForm& a_form) {
const auto refr = a_form.As<RE::TESObjectREFR>(); const auto refr = a_form.As<RE::TESObjectREFR>();
if (refr && refr->GetBaseObject() == g_cellContainer && refr->GetParentCell()->formID == a_formID) { if (refr && refr->GetParentCell()->formID == a_formID && refr->GetBaseObject() == g_cellContainer) {
if (cellStorage) {
log::warn("Multiple cell storages detected in {}", cell->GetName());
bHasDupes = true;
} else {
cellStorage = refr; cellStorage = refr;
return false; }
} }
return true; return true;
}); });
#ifdef _DEBUG
if (cellStorage) {
log::info("Found cell storage in {} (first pass)", cell->GetName());
}
#endif
ToggleHomeMode(cellStorage); ToggleHomeMode(cellStorage);
if (!cellStorage) { if (!cellStorage || bHasDupes) {
RE::ScriptEventSourceHolder::GetSingleton()->AddEventSink<RE::TESCellFullyLoadedEvent>(EventListener::GetSingleton()); RE::ScriptEventSourceHolder::GetSingleton()->AddEventSink<RE::TESCellFullyLoadedEvent>(EventListener::GetSingleton());
} }
} }
void OnCellEnter(const RE::BGSLocation* location, const RE::TESObjectCELL* cell) void OnCellEnter(const RE::BGSLocation* location, const RE::TESObjectCELL* cell)
{ {
RE::ScriptEventSourceHolder::GetSingleton()->RemoveEventSink<RE::TESCellFullyLoadedEvent>(EventListener::GetSingleton());
if (!g_bSaveLoaded) {
// Cell load events fire before formlists are loaded from savegame, duh!
return;
}
if (!location || !cell->IsInteriorCell() || cell != RE::PlayerCharacter::GetSingleton()->GetParentCell() || !location->HasKeyword(g_homeKeyword)) { if (!location || !cell->IsInteriorCell() || cell != RE::PlayerCharacter::GetSingleton()->GetParentCell() || !location->HasKeyword(g_homeKeyword)) {
ToggleHomeMode(nullptr); ToggleHomeMode(nullptr);
return; return;
} }
RE::TESObjectREFR* cellStorage = nullptr; RE::TESObjectREFR* cellStorage = nullptr;
std::vector<RE::TESObjectREFR*> dupes;
for (const auto& a_ref : cell->references) { cell->ForEachReference([&cellStorage, &dupes](RE::TESObjectREFR& a_ref) {
if (a_ref.get()->GetBaseObject() == g_cellContainer) { if (a_ref.GetBaseObject() == g_cellContainer && !a_ref.IsMarkedForDeletion()) {
cellStorage = a_ref.get(); if (cellStorage) {
break; dupes.push_back(&a_ref);
} else {
cellStorage = &a_ref;
} }
} }
return true;
});
for (int i = 0; i < dupes.size(); i++) {
log::warn("Removing duplicate storage {:08X}", dupes[i]->formID);
g_persistentMap.erase(dupes[i]->formID);
ListRemoveItem(g_persistentStorage, dupes[i]);
dupes[i]->Disable();
dupes[i]->SetDelete(true);
}
dupes.clear();
if (cellStorage) { if (cellStorage) {
#ifdef _DEBUG
log::info("Found cell storage in {} (second pass)", cell->GetName());
#endif
if (!g_persistentMap.contains(cellStorage->formID)) { if (!g_persistentMap.contains(cellStorage->formID)) {
g_persistentStorage->AddForm(cellStorage); g_persistentStorage->AddForm(cellStorage);
g_persistentMap[cellStorage->formID] = cellStorage; g_persistentMap[cellStorage->formID] = cellStorage;
@ -390,6 +431,9 @@ namespace ArtifactTracker
const auto cellStorage = RE::PlayerCharacter::GetSingleton()->PlaceObjectAtMe(g_cellContainer, true).get(); const auto cellStorage = RE::PlayerCharacter::GetSingleton()->PlaceObjectAtMe(g_cellContainer, true).get();
if (cellStorage) { if (cellStorage) {
#ifdef _DEBUG
log::info("Created storage {:08X}", cellStorage->formID);
#endif
cellStorage->Disable(); cellStorage->Disable();
g_persistentStorage->AddForm(cellStorage); g_persistentStorage->AddForm(cellStorage);
g_persistentMap[cellStorage->formID] = cellStorage; g_persistentMap[cellStorage->formID] = cellStorage;

View File

@ -3,6 +3,7 @@
namespace ArtifactTracker namespace ArtifactTracker
{ {
extern bool g_bLoaded; extern bool g_bLoaded;
extern bool g_bSaveLoaded;
extern bool g_bHomeContainer; extern bool g_bHomeContainer;
extern bool g_bBookShelf; extern bool g_bBookShelf;
extern bool g_bTakeAll; extern bool g_bTakeAll;

View File

@ -117,7 +117,7 @@ auto EventListener::ProcessEvent(
// This listener is expected to be unregistered outside of home. // This listener is expected to be unregistered outside of home.
if (ArtifactTracker::IsHome() && a_event->actionRef->IsPlayerRef() && a_event->objectActivated) { if (ArtifactTracker::IsHome() && a_event->actionRef->IsPlayerRef() && a_event->objectActivated) {
const auto ref = a_event->objectActivated.get(); const auto ref = a_event->objectActivated.get();
if (ref && ArtifactTracker::IsArtifact(ref->GetBaseObject())) { if (ref && ArtifactTracker::g_artifactAllFormTypes.contains(ref->GetBaseObject()->GetFormType())) {
std::thread([]() { std::thread([]() {
std::this_thread::sleep_for(std::chrono::milliseconds(200)); std::this_thread::sleep_for(std::chrono::milliseconds(200));
ArtifactTracker::SyncCellStorage(); ArtifactTracker::SyncCellStorage();

View File

@ -34,8 +34,15 @@ namespace {
if (const auto pluginInfo = ArtifactTracker::g_loadInterface->GetPluginInfo("po3_KeywordItemDistributor"); !pluginInfo) { if (const auto pluginInfo = ArtifactTracker::g_loadInterface->GetPluginInfo("po3_KeywordItemDistributor"); !pluginInfo) {
ArtifactTracker::Init(); // if KID is not installed ArtifactTracker::Init(); // if KID is not installed
} }
} else if (message->type == MessagingInterface::kPreLoadGame) {
ArtifactTracker::g_bSaveLoaded = false; // block cell load events
} else if (message->type == MessagingInterface::kPostLoadGame) { } else if (message->type == MessagingInterface::kPostLoadGame) {
SKSE::GetTaskInterface()->AddTask([]() {
ArtifactTracker::OnGameLoad(); // save-specific updates ArtifactTracker::OnGameLoad(); // save-specific updates
ArtifactTracker::g_bSaveLoaded = true;
const auto cell = RE::PlayerCharacter::GetSingleton()->GetParentCell();
ArtifactTracker::OnCellEnter(cell->GetLocation(), cell);
});
} }
}); });
} }

View File

@ -1,7 +1,7 @@
{ {
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json", "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
"name": "artifact-tracker", "name": "artifact-tracker",
"version-string": "1.0.3", "version-string": "1.0.4",
"port-version": 0, "port-version": 0,
"description": "Artifact Tracker", "description": "Artifact Tracker",
"homepage": "https://eddoursul.win/mods/artifact-tracker/", "homepage": "https://eddoursul.win/mods/artifact-tracker/",