From 1c89562e2a0cbc5e196410737386e6804f1866d2 Mon Sep 17 00:00:00 2001 From: Eddoursul Date: Sun, 7 Jul 2024 12:34:40 +0200 Subject: [PATCH] Highlight dropped books --- src/EventListener.cpp | 70 +++++++++++++++++++++++++++---------------- src/EventListener.h | 6 ++++ src/Util.h | 52 ++++++++++++++++++++------------ 3 files changed, 83 insertions(+), 45 deletions(-) diff --git a/src/EventListener.cpp b/src/EventListener.cpp index 234644f..290140a 100644 --- a/src/EventListener.cpp +++ b/src/EventListener.cpp @@ -6,6 +6,8 @@ RE::TESEffectShader* EventListener::regularBookShader; RE::TESEffectShader* EventListener::spellBookShader; RE::TESEffectShader* EventListener::skillBookShader; +inline bool bScanDroppedBooks = false; + auto EventListener::GetSingleton() -> EventListener* { static EventListener singleton{}; @@ -29,9 +31,24 @@ void EventListener::Install() LoadINI(&settings, std::format("Data/SKSE/Plugins/{}.ini", SKSE::PluginDeclaration::GetSingleton()->GetName()).c_str()); RE::TESEffectShader* defaultShader = RE::TESForm::LookupByID(0xC5EF7); - EventListener::regularBookShader = retrieveFormByString(std::any_cast(settings["sRegularBookShader"]), defaultShader)->As(); - EventListener::spellBookShader = retrieveFormByString(std::any_cast(settings["sSpellBookShader"]), defaultShader)->As(); - EventListener::skillBookShader = retrieveFormByString(std::any_cast(settings["sSkillBookShader"]), defaultShader)->As(); + + EventListener::regularBookShader = retrieveFormByString(std::any_cast(settings["sRegularBookShader"]), defaultShader)->As(); + EventListener::spellBookShader = retrieveFormByString(std::any_cast(settings["sSpellBookShader"]), defaultShader)->As(); + EventListener::skillBookShader = retrieveFormByString(std::any_cast(settings["sSkillBookShader"]), defaultShader)->As(); +} + +auto EventListener::ProcessEvent( + const RE::TESContainerChangedEvent* a_event, + RE::BSTEventSource* a_eventSource) + -> RE::BSEventNotifyControl +{ + if (!a_event->newContainer && a_event->oldContainer == 0x14) { + if (const auto baseObj = RE::TESForm::LookupByID(a_event->baseObj); baseObj->Is(RE::FormType::Book)) { + bScanDroppedBooks = true; + } + } + + return RE::BSEventNotifyControl::kContinue; } auto EventListener::ProcessEvent( @@ -52,27 +69,7 @@ auto EventListener::ProcessEvent( -> RE::BSEventNotifyControl { if (a_event && a_event->attached && a_event->reference) { - if (const auto baseObj = a_event->reference->GetBaseObject(); baseObj->IsBook()) { - if (const auto bookObj = baseObj->As()) { - if (!bookObj->IsRead()) { - RE::TESObjectREFR* ref = a_event->reference.get(); - - auto shader = EventListener::regularBookShader; - - if (bookObj->TeachesSpell()) { - shader = EventListener::spellBookShader; - } else if (bookObj->TeachesSkill()) { - shader = EventListener::skillBookShader; - } - - SKSE::GetTaskInterface()->AddTask([ref, shader]() { - if (ref) { - ref->ApplyEffectShader(shader); - } - }); - } - } - } + TryAddBookRefShader(a_event->reference.get()); } return RE::BSEventNotifyControl::kContinue; @@ -83,8 +80,29 @@ auto EventListener::ProcessEvent( RE::BSTEventSource* a_eventSource) -> RE::BSEventNotifyControl { - if (a_event && !a_event->opening && a_event->menuName == RE::BookMenu::MENU_NAME) { - SKSE::GetTaskInterface()->AddTask(ClearOldShaders); + if (a_event) { + if (a_event->opening) { + if (a_event->menuName == RE::InventoryMenu::MENU_NAME) { + RE::ScriptEventSourceHolder::GetSingleton()->GetEventSource()->AddEventSink(EventListener::GetSingleton()); + } + } else { + if (a_event->menuName == RE::InventoryMenu::MENU_NAME) { + RE::ScriptEventSourceHolder::GetSingleton()->GetEventSource()->RemoveEventSink(EventListener::GetSingleton()); + + if (bScanDroppedBooks) { + bScanDroppedBooks = false; + if (const auto TES = RE::TES::GetSingleton()) { + TES->ForEachReferenceInRange(RE::PlayerCharacter::GetSingleton(), 300.0f, [](RE::TESObjectREFR* a_ref) { + TryAddBookRefShader(a_ref); + return RE::BSContainer::ForEachResult::kContinue; + }); + } + } + + } else if (a_event->menuName == RE::BookMenu::MENU_NAME) { + SKSE::GetTaskInterface()->AddTask(ClearOldShaders); + } + } } return RE::BSEventNotifyControl::kContinue; diff --git a/src/EventListener.h b/src/EventListener.h index 02004b5..beaed34 100644 --- a/src/EventListener.h +++ b/src/EventListener.h @@ -1,6 +1,7 @@ #pragma once class EventListener : + public RE::BSTEventSink, public RE::BSTEventSink, public RE::BSTEventSink, public RE::BSTEventSink @@ -15,6 +16,11 @@ public: static void Install(); + auto ProcessEvent( + const RE::TESContainerChangedEvent* a_event, + RE::BSTEventSource* a_eventSource) + -> RE::BSEventNotifyControl override; + auto ProcessEvent( const RE::BGSActorCellEvent* a_event, RE::BSTEventSource* a_eventSource) diff --git a/src/Util.h b/src/Util.h index b0e765c..628ccbd 100644 --- a/src/Util.h +++ b/src/Util.h @@ -2,6 +2,33 @@ #include "EventListener.h" +inline void TryAddBookRefShader(RE::TESObjectREFR* a_ref) +{ + if (!a_ref) { + return; + } + + if (const auto baseObj = a_ref->GetBaseObject(); baseObj->IsBook()) { + if (const auto bookObj = baseObj->As()) { + if (!bookObj->IsRead()) { + auto shader = EventListener::regularBookShader; + + if (bookObj->TeachesSpell()) { + shader = EventListener::spellBookShader; + } else if (bookObj->TeachesSkill()) { + shader = EventListener::skillBookShader; + } + + SKSE::GetTaskInterface()->AddTask([a_ref, shader]() { + if (a_ref) { + a_ref->ApplyEffectShader(shader); + } + }); + } + } + } +} + inline void ClearOldShaders() { if (const auto processLists = RE::ProcessLists::GetSingleton()) { @@ -21,31 +48,18 @@ inline void ClearOldShaders() } } -// std::string str = "adsf+qwer+poui+fdgh"; -// std::vector v = split (str, '+'); -inline std::vector split(const std::string& s, char delim) +inline RE::TESForm* retrieveFormByString(std::string a_str, RE::TESForm* a_defaultForm) { - std::vector result; - std::stringstream ss(s); - std::string item; - - while (getline(ss, item, delim)) { - result.push_back(item); - } - - return result; -} - -inline RE::TESForm* retrieveFormByString(const char* a_str, RE::TESForm* a_defaultForm) -{ - std::vector segments = split(a_str, '@'); + int pos = a_str.find_first_of('@'); + std::string sHex = a_str.substr(pos + 1), + sFilename = a_str.substr(0, pos); RE::FormID id; std::stringstream ss; - ss << std::hex << segments[1]; + ss << std::hex << sHex; ss >> id; - RE::TESForm* result = RE::TESDataHandler::GetSingleton()->LookupForm(id, segments[0]); + RE::TESForm* result = RE::TESDataHandler::GetSingleton()->LookupForm(id, sFilename); if (!result) { logger::error("Unable to retrieve {}, using default value", a_str);