Use a Load3D hook

This commit is contained in:
Eddoursul 2024-07-07 17:43:00 +02:00
parent 1c89562e2a
commit 2b38e77101
4 changed files with 47 additions and 79 deletions

View File

@ -1,13 +1,12 @@
#include "EventListener.h" #include "EventListener.h"
#include "IniLoader.h" #include "IniLoader.h"
#include "Util.h" #include "Util.h"
#include "Hooks.h"
RE::TESEffectShader* EventListener::regularBookShader; RE::TESEffectShader* EventListener::regularBookShader;
RE::TESEffectShader* EventListener::spellBookShader; RE::TESEffectShader* EventListener::spellBookShader;
RE::TESEffectShader* EventListener::skillBookShader; RE::TESEffectShader* EventListener::skillBookShader;
inline bool bScanDroppedBooks = false;
auto EventListener::GetSingleton() -> EventListener* auto EventListener::GetSingleton() -> EventListener*
{ {
static EventListener singleton{}; static EventListener singleton{};
@ -18,14 +17,15 @@ void EventListener::Install()
{ {
logger::info("{} is initializing...", SKSE::PluginDeclaration::GetSingleton()->GetName()); logger::info("{} is initializing...", SKSE::PluginDeclaration::GetSingleton()->GetName());
RE::ScriptEventSourceHolder::GetSingleton()->GetEventSource<RE::TESCellAttachDetachEvent>()->AddEventSink(EventListener::GetSingleton()); SetBookShaderHook::Install();
RE::PlayerCharacter::GetSingleton()->AsBGSActorCellEventSource()->AddEventSink(EventListener::GetSingleton()); RE::PlayerCharacter::GetSingleton()->AsBGSActorCellEventSource()->AddEventSink(EventListener::GetSingleton());
RE::UI::GetSingleton()->AddEventSink<RE::MenuOpenCloseEvent>(EventListener::GetSingleton()); RE::UI::GetSingleton()->AddEventSink<RE::MenuOpenCloseEvent>(EventListener::GetSingleton());
std::map<std::string, std::any> settings{ std::map<std::string, std::any> settings{
{ "sRegularBookShader", "Skyrim.esm@D2057" }, { "sRegularBookShader", "Skyrim.esm@C723A" },
{ "sSpellBookShader", "Skyrim.esm@C5EF7" }, { "sSpellBookShader", "Skyrim.esm@B6BF9" },
{ "sSkillBookShader", "Skyrim.esm@C34B1" }, { "sSkillBookShader", "Skyrim.esm@B6BFA" },
}; };
LoadINI(&settings, std::format("Data/SKSE/Plugins/{}.ini", SKSE::PluginDeclaration::GetSingleton()->GetName()).c_str()); LoadINI(&settings, std::format("Data/SKSE/Plugins/{}.ini", SKSE::PluginDeclaration::GetSingleton()->GetName()).c_str());
@ -37,39 +37,13 @@ void EventListener::Install()
EventListener::skillBookShader = retrieveFormByString(std::any_cast<std::string>(settings["sSkillBookShader"]), defaultShader)->As<RE::TESEffectShader>(); EventListener::skillBookShader = retrieveFormByString(std::any_cast<std::string>(settings["sSkillBookShader"]), defaultShader)->As<RE::TESEffectShader>();
} }
auto EventListener::ProcessEvent(
const RE::TESContainerChangedEvent* a_event,
RE::BSTEventSource<RE::TESContainerChangedEvent>* 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( auto EventListener::ProcessEvent(
const RE::BGSActorCellEvent* a_event, const RE::BGSActorCellEvent* a_event,
RE::BSTEventSource<RE::BGSActorCellEvent>* a_eventSource) RE::BSTEventSource<RE::BGSActorCellEvent>* a_eventSource)
-> RE::BSEventNotifyControl -> RE::BSEventNotifyControl
{ {
if (a_event->flags == RE::BGSActorCellEvent::CellFlag::kEnter) { if (a_event->flags == RE::BGSActorCellEvent::CellFlag::kEnter) {
SKSE::GetTaskInterface()->AddTask(ClearOldShaders); ClearOldShaders();
}
return RE::BSEventNotifyControl::kContinue;
}
auto EventListener::ProcessEvent(
const RE::TESCellAttachDetachEvent* a_event,
RE::BSTEventSource<RE::TESCellAttachDetachEvent>* a_eventSource)
-> RE::BSEventNotifyControl
{
if (a_event && a_event->attached && a_event->reference) {
TryAddBookRefShader(a_event->reference.get());
} }
return RE::BSEventNotifyControl::kContinue; return RE::BSEventNotifyControl::kContinue;
@ -80,28 +54,9 @@ auto EventListener::ProcessEvent(
RE::BSTEventSource<RE::MenuOpenCloseEvent>* a_eventSource) RE::BSTEventSource<RE::MenuOpenCloseEvent>* a_eventSource)
-> RE::BSEventNotifyControl -> RE::BSEventNotifyControl
{ {
if (a_event) { if (a_event && !a_event->opening) {
if (a_event->opening) { if (a_event->menuName == RE::BookMenu::MENU_NAME) {
if (a_event->menuName == RE::InventoryMenu::MENU_NAME) { ClearOldShaders();
RE::ScriptEventSourceHolder::GetSingleton()->GetEventSource<RE::TESContainerChangedEvent>()->AddEventSink(EventListener::GetSingleton());
}
} else {
if (a_event->menuName == RE::InventoryMenu::MENU_NAME) {
RE::ScriptEventSourceHolder::GetSingleton()->GetEventSource<RE::TESContainerChangedEvent>()->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);
}
} }
} }

View File

@ -1,9 +1,7 @@
#pragma once #pragma once
class EventListener : class EventListener :
public RE::BSTEventSink<RE::TESContainerChangedEvent>,
public RE::BSTEventSink<RE::BGSActorCellEvent>, public RE::BSTEventSink<RE::BGSActorCellEvent>,
public RE::BSTEventSink<RE::TESCellAttachDetachEvent>,
public RE::BSTEventSink<RE::MenuOpenCloseEvent> public RE::BSTEventSink<RE::MenuOpenCloseEvent>
{ {
public: public:
@ -16,21 +14,11 @@ public:
static void Install(); static void Install();
auto ProcessEvent(
const RE::TESContainerChangedEvent* a_event,
RE::BSTEventSource<RE::TESContainerChangedEvent>* a_eventSource)
-> RE::BSEventNotifyControl override;
auto ProcessEvent( auto ProcessEvent(
const RE::BGSActorCellEvent* a_event, const RE::BGSActorCellEvent* a_event,
RE::BSTEventSource<RE::BGSActorCellEvent>* a_eventSource) RE::BSTEventSource<RE::BGSActorCellEvent>* a_eventSource)
-> RE::BSEventNotifyControl override; -> RE::BSEventNotifyControl override;
auto ProcessEvent(
const RE::TESCellAttachDetachEvent* a_event,
RE::BSTEventSource<RE::TESCellAttachDetachEvent>* a_eventSource)
-> RE::BSEventNotifyControl override;
auto ProcessEvent( auto ProcessEvent(
const RE::MenuOpenCloseEvent* a_event, const RE::MenuOpenCloseEvent* a_event,
RE::BSTEventSource<RE::MenuOpenCloseEvent>* a_eventSource) RE::BSTEventSource<RE::MenuOpenCloseEvent>* a_eventSource)

24
src/Hooks.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include "Util.h"
struct SetBookShaderHook
{
static RE::NiAVObject* thunk(RE::TESObjectREFR& a_this, bool a_loadInBackground)
{
const auto ret = func(a_this, a_loadInBackground);
TryAddBookRefShader(&a_this);
return ret;
}
static inline REL::Relocation<decltype(thunk)> func;
static void Install()
{
REL::Relocation<uintptr_t> vTable(RE::TESObjectREFR::VTABLE[0]);
func = vTable.write_vfunc(0x6A, thunk); // NiAVObject* Load3D(bool a_backgroundLoading)
logger::info("Added book shader hook");
}
};

View File

@ -31,21 +31,22 @@ inline void TryAddBookRefShader(RE::TESObjectREFR* a_ref)
inline void ClearOldShaders() inline void ClearOldShaders()
{ {
if (const auto processLists = RE::ProcessLists::GetSingleton()) { SKSE::GetTaskInterface()->AddTask([]() {
processLists->ForEachShaderEffect([&](RE::ShaderReferenceEffect* a_shaderEffect) -> RE::BSContainer::ForEachResult { if (const auto processLists = RE::ProcessLists::GetSingleton()) {
if (a_shaderEffect && (a_shaderEffect->effectData == EventListener::regularBookShader || a_shaderEffect->effectData == EventListener::spellBookShader || a_shaderEffect->effectData == EventListener::skillBookShader) && a_shaderEffect->target) { processLists->ForEachShaderEffect([&](RE::ShaderReferenceEffect* a_shaderEffect) -> RE::BSContainer::ForEachResult {
if (const auto targetRef = a_shaderEffect->target.get()) { if (a_shaderEffect && (a_shaderEffect->effectData == EventListener::regularBookShader || a_shaderEffect->effectData == EventListener::spellBookShader || a_shaderEffect->effectData == EventListener::skillBookShader) && a_shaderEffect->target) {
if (const auto baseObj = targetRef->GetBaseObject(); baseObj->IsBook()) { if (const auto targetRef = a_shaderEffect->target.get()) {
if (baseObj->As<RE::TESObjectBOOK>()->IsRead()) { if (const auto baseObj = targetRef->GetBaseObject(); baseObj->IsBook()) {
a_shaderEffect->finished = true; if (baseObj->As<RE::TESObjectBOOK>()->IsRead()) {
a_shaderEffect->finished = true;
}
} }
} }
} }
} return RE::BSContainer::ForEachResult::kContinue;
});
return RE::BSContainer::ForEachResult::kContinue; }
}); });
}
} }
inline RE::TESForm* retrieveFormByString(std::string a_str, RE::TESForm* a_defaultForm) inline RE::TESForm* retrieveFormByString(std::string a_str, RE::TESForm* a_defaultForm)