diff --git a/mm/2s2h/BenGui/BenGui.cpp b/mm/2s2h/BenGui/BenGui.cpp index ffd40b6d6..92600f33d 100644 --- a/mm/2s2h/BenGui/BenGui.cpp +++ b/mm/2s2h/BenGui/BenGui.cpp @@ -36,6 +36,7 @@ namespace BenGui { std::shared_ptr mHudEditorWindow; std::shared_ptr mActorViewerWindow; std::shared_ptr mCollisionViewerWindow; + std::shared_ptr mEventLogWindow; void SetupGuiElements() { auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui(); @@ -87,6 +88,9 @@ namespace BenGui { mCollisionViewerWindow = std::make_shared("gWindows.CollisionViewer", "Collision Viewer"); gui->AddGuiWindow(mCollisionViewerWindow); + + mEventLogWindow = std::make_shared("gWindows.EventLog", "Event Log"); + gui->AddGuiWindow(mEventLogWindow); } void Destroy() { @@ -96,6 +100,7 @@ namespace BenGui { mInputEditorWindow = nullptr; mGfxDebuggerWindow = nullptr; mCollisionViewerWindow = nullptr; + mEventLogWindow = nullptr; mSaveEditorWindow = nullptr; mHudEditorWindow = nullptr; diff --git a/mm/2s2h/BenGui/BenGui.hpp b/mm/2s2h/BenGui/BenGui.hpp index c75fca8be..e5017c217 100644 --- a/mm/2s2h/BenGui/BenGui.hpp +++ b/mm/2s2h/BenGui/BenGui.hpp @@ -6,6 +6,7 @@ #include "DeveloperTools/SaveEditor.h" #include "DeveloperTools/ActorViewer.h" #include "DeveloperTools/CollisionViewer.h" +#include "DeveloperTools/EventLog.h" namespace BenGui { void SetupHooks(); diff --git a/mm/2s2h/BenGui/BenMenuBar.cpp b/mm/2s2h/BenGui/BenMenuBar.cpp index 6480ed54b..1337b7380 100644 --- a/mm/2s2h/BenGui/BenMenuBar.cpp +++ b/mm/2s2h/BenGui/BenMenuBar.cpp @@ -341,6 +341,7 @@ extern std::shared_ptr mGfxDebuggerWindow; extern std::shared_ptr mSaveEditorWindow; extern std::shared_ptr mActorViewerWindow; extern std::shared_ptr mCollisionViewerWindow; +extern std::shared_ptr mEventLogWindow; void DrawDeveloperToolsMenu() { if (UIWidgets::BeginMenu("Developer Tools", UIWidgets::Colors::Yellow)) { @@ -407,6 +408,9 @@ void DrawDeveloperToolsMenu() { .tooltip = "Enables the Actor Viewer window, allowing you to view actors in the world." }); } + if (mEventLogWindow) { + UIWidgets::WindowButton("Event Log", "gWindows.EventLog", mEventLogWindow); + } ImGui::EndMenu(); } } diff --git a/mm/2s2h/BenGui/BenMenuBar.h b/mm/2s2h/BenGui/BenMenuBar.h index fd4f702e3..7aa3475b6 100644 --- a/mm/2s2h/BenGui/BenMenuBar.h +++ b/mm/2s2h/BenGui/BenMenuBar.h @@ -6,6 +6,7 @@ #include "DeveloperTools/SaveEditor.h" #include "DeveloperTools/ActorViewer.h" #include "DeveloperTools/CollisionViewer.h" +#include "DeveloperTools/EventLog.h" namespace BenGui { class BenMenuBar : public LUS::GuiMenuBar { diff --git a/mm/2s2h/DeveloperTools/EventLog.cpp b/mm/2s2h/DeveloperTools/EventLog.cpp new file mode 100644 index 000000000..a1871307e --- /dev/null +++ b/mm/2s2h/DeveloperTools/EventLog.cpp @@ -0,0 +1,400 @@ +#include "EventLog.h" + +#include +#include +#include +#include + +#include "2s2h/BenGui/UIWidgets.hpp" +#include "2s2h/Enhancements/GameInteractor/GameInteractor.h" + +extern "C" { +#include +} + +typedef enum { + EVENT_LOG_ENTRY_TYPE_FLAG_SET, + EVENT_LOG_ENTRY_TYPE_FLAG_UNSET, + EVENT_LOG_ENTRY_TYPE_SCENE_FLAG_SET, + EVENT_LOG_ENTRY_TYPE_SCENE_FLAG_UNSET, + EVENT_LOG_ENTRY_TYPE_ACTOR_INIT, + EVENT_LOG_ENTRY_TYPE_ACTOR_KILL, + EVENT_LOG_ENTRY_TYPE_SCENE_INIT, + EVENT_LOG_ENTRY_TYPE_ROOM_INIT, + EVENT_LOG_ENTRY_TYPE_OPEN_TEXT, + EVENT_LOG_ENTRY_TYPE_ITEM_GIVE, +} EventLogEntryType; + +const char* eventLogEntryTypeNames[] = { + "FlagSet", + "FlagUnset", + "SceneFlagSet", + "SceneFlagUnset", + "ActorInit", + "ActorKill", + "SceneInit", + "RoomInit", + "OpenText", + "ItemGive", +}; + +struct EventLogEntry { + std::string timestamp; + EventLogEntryType type; + std::string meta; +}; + +std::vector eventLogEntries; + +const char* flagTypeNames[] = { + "", + "weekEventReg", + "eventInf", + "scenesVisible", + "owlActivation", + "chest", + "switch", + "clearedRoom", + "collectible", + "unk_14", + "rooms", + "chest *", + "switch *", + "clearedRoom *", + "collectible *", +}; + +#define DEFINE_ACTOR(name, _enumValue, _allocType, _debugName, _humanName) {_enumValue, _debugName}, +#define DEFINE_ACTOR_INTERNAL(_name, _enumValue, _allocType, _debugName, _humanName) {_enumValue, _debugName}, +#define DEFINE_ACTOR_UNSET(_enumValue) {_enumValue, "Unset"}, + +std::unordered_map actorNames = { +#include "tables/actor_table.h" +}; + +#undef DEFINE_ACTOR +#undef DEFINE_ACTOR_INTERNAL +#undef DEFINE_ACTOR_UNSET + +// 2S2H Added columns to scene table: entranceSceneId, betterMapSelectIndex, humanName +#define DEFINE_SCENE(_name, enumValue, _textId, _drawConfig, _restrictionFlags, _persistentCycleFlags, _entranceSceneId, _betterMapSelectIndex, humanName) \ + { enumValue, humanName }, +#define DEFINE_SCENE_UNSET(_enumValue) + +std::unordered_map sceneNames = { +#include "tables/scene_table.h" +}; + +#undef DEFINE_SCENE +#undef DEFINE_SCENE_UNSET + +static HOOK_ID onFlagSetHookId = 0; +static HOOK_ID onFlagUnsetHookId = 0; +static HOOK_ID onSceneFlagSetHookId = 0; +static HOOK_ID onSceneFlagUnsetHookId = 0; +static HOOK_ID onActorInitHookId = 0; +static HOOK_ID onActorKillHookId = 0; +static HOOK_ID onSceneInitHookId = 0; +static HOOK_ID onRoomInitHookId = 0; +static HOOK_ID onOpenTextHookId = 0; +static HOOK_ID onItemGiveHookId = 0; + +std::string CurrentTime() { + auto t = std::time(nullptr); + auto tm = *std::localtime(&t); + + std::ostringstream oss; + oss << std::put_time(&tm, "%I:%M:%S"); + return oss.str(); +} + +void TrimEventLog() { + while (eventLogEntries.size() > CVarGetInteger("gEventLog.MaxEntries", 1000)) { + eventLogEntries.pop_back(); + } +} + +void RegisterEventLogHooks() { + GameInteractor::Instance->UnregisterGameHook(onFlagSetHookId); + GameInteractor::Instance->UnregisterGameHook(onFlagUnsetHookId); + GameInteractor::Instance->UnregisterGameHook(onSceneFlagSetHookId); + GameInteractor::Instance->UnregisterGameHook(onSceneFlagUnsetHookId); + GameInteractor::Instance->UnregisterGameHook(onActorInitHookId); + GameInteractor::Instance->UnregisterGameHook(onActorKillHookId); + GameInteractor::Instance->UnregisterGameHook(onSceneInitHookId); + GameInteractor::Instance->UnregisterGameHook(onRoomInitHookId); + GameInteractor::Instance->UnregisterGameHook(onOpenTextHookId); + GameInteractor::Instance->UnregisterGameHook(onItemGiveHookId); + onFlagSetHookId = 0; + onFlagUnsetHookId = 0; + onSceneFlagSetHookId = 0; + onSceneFlagUnsetHookId = 0; + onActorInitHookId = 0; + onActorKillHookId = 0; + onSceneInitHookId = 0; + onRoomInitHookId = 0; + onOpenTextHookId = 0; + onItemGiveHookId = 0; + + if (!CVarGetInteger("gEventLog.Enabled", 0)) { + return; + } + + onFlagSetHookId = GameInteractor::Instance->RegisterGameHook([](FlagType type, u32 flag) { + if (type == FlagType::FLAG_WEEK_EVENT_REG) { + eventLogEntries.insert(eventLogEntries.begin(), { + .timestamp = CurrentTime(), + .type = EVENT_LOG_ENTRY_TYPE_FLAG_SET, + .meta = fmt::format("WEEKEVENTREG_{:02d}_{:02x}", flag >> 8, flag & 0xFF), + }); + } else if (type == FlagType::FLAG_EVENT_INF) { + eventLogEntries.insert(eventLogEntries.begin(), { + .timestamp = CurrentTime(), + .type = EVENT_LOG_ENTRY_TYPE_FLAG_SET, + .meta = fmt::format("EVENTINF_{:02x}", flag), + }); + } else { + eventLogEntries.insert(eventLogEntries.begin(), { + .timestamp = CurrentTime(), + .type = EVENT_LOG_ENTRY_TYPE_FLAG_SET, + .meta = fmt::format("{}({:02d}) {:02x}", flagTypeNames[type], flag >> 8, flag & 0xFF), + }); + } + TrimEventLog(); + }); + + onFlagUnsetHookId = GameInteractor::Instance->RegisterGameHook([](FlagType type, u32 flag) { + if (type == FlagType::FLAG_WEEK_EVENT_REG) { + eventLogEntries.insert(eventLogEntries.begin(), { + .timestamp = CurrentTime(), + .type = EVENT_LOG_ENTRY_TYPE_FLAG_UNSET, + .meta = fmt::format("WEEKEVENTREG_{:02d}_{:02x}", flag >> 8, flag & 0xFF), + }); + } else if (type == FlagType::FLAG_EVENT_INF) { + eventLogEntries.insert(eventLogEntries.begin(), { + .timestamp = CurrentTime(), + .type = EVENT_LOG_ENTRY_TYPE_FLAG_UNSET, + .meta = fmt::format("EVENTINF_{:02x}", flag), + }); + } else { + eventLogEntries.insert(eventLogEntries.begin(), { + .timestamp = CurrentTime(), + .type = EVENT_LOG_ENTRY_TYPE_FLAG_UNSET, + .meta = fmt::format("{}({:02d}) {:02x}", flagTypeNames[type], flag >> 8, flag & 0xFF), + }); + } + TrimEventLog(); + }); + + onSceneFlagSetHookId = GameInteractor::Instance->RegisterGameHook([](s16 sceneId, FlagType type, u32 flag) { + eventLogEntries.insert(eventLogEntries.begin(), { + .timestamp = CurrentTime(), + .type = EVENT_LOG_ENTRY_TYPE_SCENE_FLAG_SET, + .meta = fmt::format("{}({:02d}) {:02x}", flagTypeNames[type], flag >> 8, flag & 0xFF), + }); + TrimEventLog(); + }); + + onSceneFlagUnsetHookId = GameInteractor::Instance->RegisterGameHook([](s16 sceneId, FlagType type, u32 flag) { + eventLogEntries.insert(eventLogEntries.begin(), { + .timestamp = CurrentTime(), + .type = EVENT_LOG_ENTRY_TYPE_SCENE_FLAG_UNSET, + .meta = fmt::format("{}[{:02d}] {:02x}", flagTypeNames[type], flag >> 8, flag & 0xFF), + }); + TrimEventLog(); + }); + + onActorInitHookId = GameInteractor::Instance->RegisterGameHook([](Actor* actor) { + eventLogEntries.insert(eventLogEntries.begin(), { + .timestamp = CurrentTime(), + .type = EVENT_LOG_ENTRY_TYPE_ACTOR_INIT, + .meta = fmt::format("{} {}", actorNames[actor->id], actor->params), + }); + TrimEventLog(); + }); + + onActorKillHookId = GameInteractor::Instance->RegisterGameHook([](Actor* actor) { + eventLogEntries.insert(eventLogEntries.begin(), { + .timestamp = CurrentTime(), + .type = EVENT_LOG_ENTRY_TYPE_ACTOR_KILL, + .meta = fmt::format("{} {}", actorNames[actor->id], actor->params), + }); + TrimEventLog(); + }); + + onSceneInitHookId = GameInteractor::Instance->RegisterGameHook([](s16 sceneId, s8 spawnNum) { + eventLogEntries.insert(eventLogEntries.begin(), { + .timestamp = CurrentTime(), + .type = EVENT_LOG_ENTRY_TYPE_SCENE_INIT, + .meta = fmt::format("{} {}", sceneNames[sceneId], spawnNum), + }); + TrimEventLog(); + }); + + onRoomInitHookId = GameInteractor::Instance->RegisterGameHook([](s16 sceneId, s8 roomId) { + eventLogEntries.insert(eventLogEntries.begin(), { + .timestamp = CurrentTime(), + .type = EVENT_LOG_ENTRY_TYPE_ROOM_INIT, + .meta = fmt::format("{} {}", sceneNames[sceneId], roomId), + }); + TrimEventLog(); + }); + + onOpenTextHookId = GameInteractor::Instance->RegisterGameHook([](s16 textId) { + eventLogEntries.insert(eventLogEntries.begin(), { + .timestamp = CurrentTime(), + .type = EVENT_LOG_ENTRY_TYPE_OPEN_TEXT, + .meta = fmt::format("0x{:02x}", textId), + }); + TrimEventLog(); + }); + + onItemGiveHookId = GameInteractor::Instance->RegisterGameHook([](u8 item) { + eventLogEntries.insert(eventLogEntries.begin(), { + .timestamp = CurrentTime(), + .type = EVENT_LOG_ENTRY_TYPE_ITEM_GIVE, + .meta = fmt::format("0x{:02x}", item), + }); + TrimEventLog(); + }); +} + +void EventLogWindow::DrawElement() { + ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver); + if (!ImGui::Begin("Event Log", &mIsVisible, ImGuiWindowFlags_NoFocusOnAppearing)) { + ImGui::End(); + return; + } + + if (UIWidgets::CVarCheckbox("Enable", "gEventLog.Enabled")) { + RegisterEventLogHooks(); + } + + ImGui::SameLine(); + + if (UIWidgets::Button("Filters", { .size = UIWidgets::Sizes::Inline })) { + ImGui::OpenPopup("eventLogFiltersPopup"); + } + + if (ImGui::BeginPopup("eventLogFiltersPopup")) { + bool allChecked = + CVarGetInteger("gEventLog.Filter.FlagSet", 1) && + CVarGetInteger("gEventLog.Filter.FlagUnset", 1) && + CVarGetInteger("gEventLog.Filter.SceneFlagSet", 1) && + CVarGetInteger("gEventLog.Filter.SceneFlagUnset", 1) && + CVarGetInteger("gEventLog.Filter.ActorInit", 1) && + CVarGetInteger("gEventLog.Filter.ActorKill", 1) && + CVarGetInteger("gEventLog.Filter.SceneInit", 1) && + CVarGetInteger("gEventLog.Filter.RoomInit", 1) && + CVarGetInteger("gEventLog.Filter.OpenText", 1) && + CVarGetInteger("gEventLog.Filter.ItemGive", 1); + bool someChecked = + CVarGetInteger("gEventLog.Filter.FlagSet", 1) || + CVarGetInteger("gEventLog.Filter.FlagUnset", 1) || + CVarGetInteger("gEventLog.Filter.SceneFlagSet", 1) || + CVarGetInteger("gEventLog.Filter.SceneFlagUnset", 1) || + CVarGetInteger("gEventLog.Filter.ActorInit", 1) || + CVarGetInteger("gEventLog.Filter.ActorKill", 1) || + CVarGetInteger("gEventLog.Filter.SceneInit", 1) || + CVarGetInteger("gEventLog.Filter.RoomInit", 1) || + CVarGetInteger("gEventLog.Filter.OpenText", 1) || + CVarGetInteger("gEventLog.Filter.ItemGive", 1); + + ImGuiContext* g = ImGui::GetCurrentContext(); + ImGuiItemFlags backup_item_flags = g->CurrentItemFlags; + if (!allChecked && someChecked) g->CurrentItemFlags |= ImGuiItemFlags_MixedValue; + if (UIWidgets::Checkbox("All", &allChecked)) { + if (allChecked) { + CVarSetInteger("gEventLog.Filter.FlagSet", 1); + CVarSetInteger("gEventLog.Filter.FlagUnset", 1); + CVarSetInteger("gEventLog.Filter.SceneFlagSet", 1); + CVarSetInteger("gEventLog.Filter.SceneFlagUnset", 1); + CVarSetInteger("gEventLog.Filter.ActorInit", 1); + CVarSetInteger("gEventLog.Filter.ActorKill", 1); + CVarSetInteger("gEventLog.Filter.SceneInit", 1); + CVarSetInteger("gEventLog.Filter.RoomInit", 1); + CVarSetInteger("gEventLog.Filter.OpenText", 1); + CVarSetInteger("gEventLog.Filter.ItemGive", 1); + } else { + CVarSetInteger("gEventLog.Filter.FlagSet", 0); + CVarSetInteger("gEventLog.Filter.FlagUnset", 0); + CVarSetInteger("gEventLog.Filter.SceneFlagSet", 0); + CVarSetInteger("gEventLog.Filter.SceneFlagUnset", 0); + CVarSetInteger("gEventLog.Filter.ActorInit", 0); + CVarSetInteger("gEventLog.Filter.ActorKill", 0); + CVarSetInteger("gEventLog.Filter.SceneInit", 0); + CVarSetInteger("gEventLog.Filter.RoomInit", 0); + CVarSetInteger("gEventLog.Filter.OpenText", 0); + CVarSetInteger("gEventLog.Filter.ItemGive", 0); + } + LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); + } + g->CurrentItemFlags = backup_item_flags; + + UIWidgets::CVarCheckbox("FlagSet", "gEventLog.Filter.FlagSet", { .defaultValue = 1 }); + UIWidgets::CVarCheckbox("FlagUnset", "gEventLog.Filter.FlagUnset", { .defaultValue = 1 }); + UIWidgets::CVarCheckbox("SceneFlagSet", "gEventLog.Filter.SceneFlagSet", { .defaultValue = 1 }); + UIWidgets::CVarCheckbox("SceneFlagUnset", "gEventLog.Filter.SceneFlagUnset", { .defaultValue = 1 }); + UIWidgets::CVarCheckbox("ActorInit", "gEventLog.Filter.ActorInit", { .defaultValue = 1 }); + UIWidgets::CVarCheckbox("ActorKill", "gEventLog.Filter.ActorKill", { .defaultValue = 1 }); + UIWidgets::CVarCheckbox("SceneInit", "gEventLog.Filter.SceneInit", { .defaultValue = 1 }); + UIWidgets::CVarCheckbox("RoomInit", "gEventLog.Filter.RoomInit", { .defaultValue = 1 }); + UIWidgets::CVarCheckbox("OpenText", "gEventLog.Filter.OpenText", { .defaultValue = 1 }); + UIWidgets::CVarCheckbox("ItemGive", "gEventLog.Filter.ItemGive", { .defaultValue = 1 }); + ImGui::EndPopup(); + } + + ImGui::SameLine(0); + + std::string maxEntriesStr = std::string("Max Entries: ") + std::to_string(eventLogEntries.size()) + "/"; + ImGui::Text("%s", maxEntriesStr.c_str()); + + ImGui::SameLine(0); + + UIWidgets::PushStyleCombobox(); + ImGui::PushItemWidth(50); + s32 maxEntries = CVarGetInteger("gEventLog.MaxEntries", 1000); + if (ImGui::InputScalar("##maxEntriesInput", ImGuiDataType_S32, &maxEntries)) { + CVarSetInteger("gEventLog.MaxEntries", MAX(0, maxEntries)); + LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick(); + TrimEventLog(); + } + ImGui::PopItemWidth(); + UIWidgets::PopStyleCombobox(); + + ImGui::SameLine(0); + + if (UIWidgets::Button("Clear", { .color = UIWidgets::Colors::Red, .size = UIWidgets::Sizes::Inline })) { + eventLogEntries.clear(); + } + + if (ImGui::BeginTable("events", 3, ImGuiTableFlags_ScrollY, ImVec2(0, 0))) { + ImGui::TableSetupColumn("Timestamp", ImGuiTableColumnFlags_WidthFixed, 70); + ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, 100); + ImGui::TableSetupColumn("Meta", ImGuiTableColumnFlags_WidthStretch, 0); + ImGui::TableSetupScrollFreeze(0, 1); + ImGui::TableHeadersRow(); + + for (int i = 0; i < eventLogEntries.size(); i++) { + if (!CVarGetInteger((std::string("gEventLog.Filter.") + eventLogEntryTypeNames[eventLogEntries[i].type]).c_str(), 1)) { + continue; + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::Text("%s", eventLogEntries[i].timestamp.c_str()); + ImGui::TableSetColumnIndex(1); + ImGui::Text("%s", eventLogEntryTypeNames[eventLogEntries[i].type]); + ImGui::TableSetColumnIndex(2); + ImGui::Text("%s", eventLogEntries[i].meta.c_str()); + } + + ImGui::EndTable(); + } + + ImGui::End(); +} + +void EventLogWindow::InitElement() { + RegisterEventLogHooks(); +} diff --git a/mm/2s2h/DeveloperTools/EventLog.h b/mm/2s2h/DeveloperTools/EventLog.h new file mode 100644 index 000000000..85ffecff4 --- /dev/null +++ b/mm/2s2h/DeveloperTools/EventLog.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +class EventLogWindow : public LUS::GuiWindow { + public: + using GuiWindow::GuiWindow; + + void InitElement() override; + void DrawElement() override; + void UpdateElement() override {}; +}; \ No newline at end of file diff --git a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.cpp b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.cpp index 44e718262..e79eafce8 100644 --- a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.cpp +++ b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.cpp @@ -18,6 +18,18 @@ void GameInteractor_ExecuteOnGameStateUpdate() { GameInteractor::Instance->ExecuteHooks(); } +void GameInteractor_ExecuteOnSceneInit(s16 sceneId, s8 spawnNum) { + GameInteractor::Instance->ExecuteHooks(sceneId, spawnNum); + GameInteractor::Instance->ExecuteHooksForID(sceneId, sceneId, spawnNum); + GameInteractor::Instance->ExecuteHooksForFilter(sceneId, spawnNum); +} + +void GameInteractor_ExecuteOnRoomInit(s16 sceneId, s8 roomNum) { + GameInteractor::Instance->ExecuteHooks(sceneId, roomNum); + GameInteractor::Instance->ExecuteHooksForID(sceneId, sceneId, roomNum); + GameInteractor::Instance->ExecuteHooksForFilter(sceneId, roomNum); +} + bool GameInteractor_ShouldActorInit(Actor* actor) { bool result = true; GameInteractor::Instance->ExecuteHooks(actor, &result); @@ -66,6 +78,53 @@ void GameInteractor_ExecuteOnActorDraw(Actor* actor) { GameInteractor::Instance->ExecuteHooksForFilter(actor); } +void GameInteractor_ExecuteOnActorKill(Actor* actor) { + GameInteractor::Instance->ExecuteHooks(actor); + GameInteractor::Instance->ExecuteHooksForID(actor->id, actor); + GameInteractor::Instance->ExecuteHooksForPtr((uintptr_t)actor, actor); + GameInteractor::Instance->ExecuteHooksForFilter(actor); +} + +void GameInteractor_ExecuteOnSceneFlagSet(s16 sceneId, FlagType flagType, u32 flag) { + GameInteractor::Instance->ExecuteHooks(sceneId, flagType, flag); + GameInteractor::Instance->ExecuteHooksForFilter(sceneId, flagType, flag); +} + +void GameInteractor_ExecuteOnSceneFlagUnset(s16 sceneId, FlagType flagType, u32 flag) { + GameInteractor::Instance->ExecuteHooks(sceneId, flagType, flag); + GameInteractor::Instance->ExecuteHooksForFilter(sceneId, flagType, flag); +} + +void GameInteractor_ExecuteOnFlagSet(FlagType flagType, u32 flag) { + GameInteractor::Instance->ExecuteHooks(flagType, flag); + GameInteractor::Instance->ExecuteHooksForFilter(flagType, flag); +} + +void GameInteractor_ExecuteOnFlagUnset(FlagType flagType, u32 flag) { + GameInteractor::Instance->ExecuteHooks(flagType, flag); + GameInteractor::Instance->ExecuteHooksForFilter(flagType, flag); +} + +void GameInteractor_ExecuteOnOpenText(u16 textId) { + GameInteractor::Instance->ExecuteHooks(textId); + GameInteractor::Instance->ExecuteHooksForID(textId, textId); + GameInteractor::Instance->ExecuteHooksForFilter(textId); +} + +bool GameInteractor_ShouldItemGive(u8 item) { + bool result = true; + GameInteractor::Instance->ExecuteHooks(item, &result); + GameInteractor::Instance->ExecuteHooksForID(item, item, &result); + GameInteractor::Instance->ExecuteHooksForFilter(item, &result); + return result; +} + +void GameInteractor_ExecuteOnItemGive(u8 item) { + GameInteractor::Instance->ExecuteHooks(item); + GameInteractor::Instance->ExecuteHooksForID(item, item); + GameInteractor::Instance->ExecuteHooksForFilter(item); +} + bool GameInteractor_Should(GIVanillaBehavior flag, bool result, void* opt) { GameInteractor::Instance->ExecuteHooks(flag, &result, opt); GameInteractor::Instance->ExecuteHooksForID(flag, flag, &result, opt); diff --git a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h index 250d75d03..ae9efd8b1 100644 --- a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h +++ b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h @@ -9,6 +9,24 @@ extern "C" { } #endif +typedef enum { + FLAG_NONE, + FLAG_WEEK_EVENT_REG, + FLAG_EVENT_INF, + FLAG_SCENES_VISIBLE, + FLAG_OWL_ACTIVATION, + FLAG_PERM_SCENE_CHEST, + FLAG_PERM_SCENE_SWITCH, + FLAG_PERM_SCENE_CLEARED_ROOM, + FLAG_PERM_SCENE_COLLECTIBLE, + FLAG_PERM_SCENE_UNK_14, + FLAG_PERM_SCENE_ROOMS, + FLAG_CYCL_SCENE_CHEST, + FLAG_CYCL_SCENE_SWITCH, + FLAG_CYCL_SCENE_CLEARED_ROOM, + FLAG_CYCL_SCENE_COLLECTIBLE, +} FlagType; + typedef enum { // Vanilla condition: gSaveContext.showTitleCard GI_VB_SHOW_TITLE_CARD, @@ -192,12 +210,26 @@ public: DEFINE_HOOK(OnGameStateDrawFinish, ()); DEFINE_HOOK(OnGameStateUpdate, ()); + DEFINE_HOOK(OnSceneInit, (s8 sceneId, s8 spawnNum)); + DEFINE_HOOK(OnRoomInit, (s8 sceneId, s8 roomNum)); + DEFINE_HOOK(ShouldActorInit, (Actor* actor, bool* should)); DEFINE_HOOK(OnActorInit, (Actor* actor)); DEFINE_HOOK(ShouldActorUpdate, (Actor* actor, bool* should)); DEFINE_HOOK(OnActorUpdate, (Actor* actor)); DEFINE_HOOK(ShouldActorDraw, (Actor* actor, bool* should)); DEFINE_HOOK(OnActorDraw, (Actor* actor)); + DEFINE_HOOK(OnActorKill, (Actor* actor)); + + DEFINE_HOOK(OnSceneFlagSet, (s16 sceneId, FlagType flagType, u32 flag)); + DEFINE_HOOK(OnSceneFlagUnset, (s16 sceneId, FlagType flagType, u32 flag)); + DEFINE_HOOK(OnFlagSet, (FlagType flagType, u32 flag)); + DEFINE_HOOK(OnFlagUnset, (FlagType flagType, u32 flag)); + + DEFINE_HOOK(OnOpenText, (u16 textId)); + + DEFINE_HOOK(ShouldItemGive, (u8 item, bool* should)); + DEFINE_HOOK(OnItemGive, (u8 item)); DEFINE_HOOK(ShouldVanillaBehavior, (GIVanillaBehavior flag, bool* should, void* optionalArg)); }; @@ -209,12 +241,26 @@ void GameInteractor_ExecuteOnGameStateMainFinish(); void GameInteractor_ExecuteOnGameStateDrawFinish(); void GameInteractor_ExecuteOnGameStateUpdate(); +void GameInteractor_ExecuteOnSceneInit(s16 sceneId, s8 spawnNum); +void GameInteractor_ExecuteOnRoomInit(s16 sceneId, s8 roomNum); + bool GameInteractor_ShouldActorInit(Actor* actor); void GameInteractor_ExecuteOnActorInit(Actor* actor); bool GameInteractor_ShouldActorUpdate(Actor* actor); void GameInteractor_ExecuteOnActorUpdate(Actor* actor); bool GameInteractor_ShouldActorDraw(Actor* actor); void GameInteractor_ExecuteOnActorDraw(Actor* actor); +void GameInteractor_ExecuteOnActorKill(Actor* actor); + +void GameInteractor_ExecuteOnSceneFlagSet(s16 sceneId, FlagType flagType, u32 flag); +void GameInteractor_ExecuteOnSceneFlagUnset(s16 sceneId, FlagType flagType, u32 flag); +void GameInteractor_ExecuteOnFlagSet(FlagType flagType, u32 flag); +void GameInteractor_ExecuteOnFlagUnset(FlagType flagType, u32 flag); + +void GameInteractor_ExecuteOnOpenText(u16 textId); + +bool GameInteractor_ShouldItemGive(u8 item); +void GameInteractor_ExecuteOnItemGive(u8 item); bool GameInteractor_Should(GIVanillaBehavior flag, bool result, void* optionalArg); #define REGISTER_VB_SHOULD(flag, body) \ diff --git a/mm/2s2h/z_scene_2SH.cpp b/mm/2s2h/z_scene_2SH.cpp index 17d7ed43e..5685098be 100644 --- a/mm/2s2h/z_scene_2SH.cpp +++ b/mm/2s2h/z_scene_2SH.cpp @@ -6,6 +6,7 @@ #include #include #include +#include "2s2h/Enhancements/GameInteractor/GameInteractor.h" #include "2s2h/resource/type/Scene.h" #include "2s2h/resource/type/CollisionHeader.h" #include "2s2h/resource/type/Cutscene.h" @@ -518,7 +519,7 @@ extern "C" s32 OTRfunc_8009728C(PlayState* play, RoomContext* roomCtx, s32 roomN roomCtx->activeMemPage ^= 1; - //SPDLOG_INFO("Room Init - curRoom.num: {0:#x}", roomCtx->curRoom.num); + GameInteractor_ExecuteOnRoomInit(play->sceneId, roomCtx->curRoom.num); return 1; } diff --git a/mm/include/functions.h b/mm/include/functions.h index b4a37ae9e..b03d6273e 100644 --- a/mm/include/functions.h +++ b/mm/include/functions.h @@ -1333,6 +1333,10 @@ float AudioSeq_GetPortVolumeScale(u8 seqPlayerIndex); void Graph_OpenDisps(Gfx** dispRefs, Gfx* dispVals, GraphicsContext* gfxCtx, const char* file, s32 line); void Graph_CloseDisps(Gfx** dispRefs, Gfx* dispVals, GraphicsContext* gfxCtx, const char* file, s32 line); void Lights_GlowCheckPrepare(PlayState* play); +void Flags_SetWeekEventReg(s32 flag); +void Flags_ClearWeekEventReg(s32 flag); +void Flags_SetEventInf(s32 flag); +void Flags_ClearEventInf(s32 flag); // #endregion // #region 2S2H [Port] Stubbed methods void osSetThreadPri(OSThread* thread, OSPri p); diff --git a/mm/include/z64save.h b/mm/include/z64save.h index aed5487a8..4d3a5a764 100644 --- a/mm/include/z64save.h +++ b/mm/include/z64save.h @@ -1502,8 +1502,10 @@ typedef enum { #define CHECK_WEEKEVENTREG(flag) (WEEKEVENTREG((flag) >> 8) & ((flag) & 0xFF)) #define CHECK_WEEKEVENTREG_ALT(flag) (GET_WEEKEVENTREG((flag) >> 8) & ((flag) & 0xFF)) -#define SET_WEEKEVENTREG(flag) (WEEKEVENTREG((flag) >> 8) = GET_WEEKEVENTREG((flag) >> 8) | ((flag) & 0xFF)) -#define CLEAR_WEEKEVENTREG(flag) (WEEKEVENTREG((flag) >> 8) = GET_WEEKEVENTREG((flag) >> 8) & (u8)~((flag) & 0xFF)) +// #region 2S2H Originally these flags were all set with macros, for the port we want them to be in functions so we can hook into them +#define SET_WEEKEVENTREG(flag) (Flags_SetWeekEventReg(flag)) +#define CLEAR_WEEKEVENTREG(flag) (Flags_ClearWeekEventReg(flag)) +// #endregion typedef enum { /* 0 */ WEEKEVENTREG_HORSE_RACE_STATE_END, @@ -1617,9 +1619,11 @@ typedef enum { // EVENTINF_75 to EVENTINF_77 #define CHECK_EVENTINF(flag) (gSaveContext.eventInf[(flag) >> 4] & (1 << ((flag) & 0xF))) -#define SET_EVENTINF(flag) (gSaveContext.eventInf[(flag) >> 4] |= (1 << ((flag) & 0xF))) -#define CLEAR_EVENTINF(flag) (gSaveContext.eventInf[(flag) >> 4] &= (u8)~(1 << ((flag) & 0xF))) -#define CLEAR_EVENTINF_ALT(flag) (gSaveContext.eventInf[(flag) >> 4] &= ~(1 << ((flag) & 0xF))) +// #region 2S2H Originally these flags were all set with macros, for the port we want them to be in functions so we can hook into them +#define SET_EVENTINF(flag) (Flags_SetEventInf(flag)) +#define CLEAR_EVENTINF(flag) (Flags_ClearEventInf(flag)) +#define CLEAR_EVENTINF_ALT(flag) (Flags_ClearEventInf(flag)) +// #endregion typedef enum { /* 0 */ EVENTINF_DOG_RACE_STATE_NOT_STARTED, diff --git a/mm/src/code/z_actor.c b/mm/src/code/z_actor.c index a53d04432..6a0eda8e4 100644 --- a/mm/src/code/z_actor.c +++ b/mm/src/code/z_actor.c @@ -754,7 +754,11 @@ s32 Flags_GetSwitch(PlayState* play, s32 flag) { */ void Flags_SetSwitch(PlayState* play, s32 flag) { if ((flag > SWITCH_FLAG_NONE) && (flag < 0x80)) { + u8 previouslyOff = !Flags_GetSwitch(play, flag); play->actorCtx.sceneFlags.switches[(flag & ~0x1F) >> 5] |= 1 << (flag & 0x1F); + if (previouslyOff) { + GameInteractor_ExecuteOnSceneFlagSet(play->sceneId, FLAG_CYCL_SCENE_SWITCH, flag); + } } } @@ -763,7 +767,11 @@ void Flags_SetSwitch(PlayState* play, s32 flag) { */ void Flags_UnsetSwitch(PlayState* play, s32 flag) { if ((flag > SWITCH_FLAG_NONE) && (flag < 0x80)) { + u8 previouslyOn = Flags_GetSwitch(play, flag); play->actorCtx.sceneFlags.switches[(flag & ~0x1F) >> 5] &= ~(1 << (flag & 0x1F)); + if (previouslyOn) { + GameInteractor_ExecuteOnSceneFlagUnset(play->sceneId, FLAG_CYCL_SCENE_SWITCH, flag); + } } } @@ -778,7 +786,11 @@ s32 Flags_GetTreasure(PlayState* play, s32 flag) { * Sets a current scene chest flag. */ void Flags_SetTreasure(PlayState* play, s32 flag) { + u8 previouslyOff = !Flags_GetTreasure(play, flag); play->actorCtx.sceneFlags.chest |= (1 << flag); + if (previouslyOff) { + GameInteractor_ExecuteOnSceneFlagSet(play->sceneId, FLAG_CYCL_SCENE_CHEST, flag); + } } /** @@ -806,14 +818,22 @@ s32 Flags_GetClear(PlayState* play, s32 roomNumber) { * Sets a current scene clear flag. */ void Flags_SetClear(PlayState* play, s32 roomNumber) { + u8 previouslyOff = !Flags_GetClear(play, roomNumber); play->actorCtx.sceneFlags.clearedRoom |= (1 << roomNumber); + if (previouslyOff) { + GameInteractor_ExecuteOnSceneFlagSet(play->sceneId, FLAG_CYCL_SCENE_CLEARED_ROOM, roomNumber); + } } /** * Unsets a current scene clear flag. */ void Flags_UnsetClear(PlayState* play, s32 roomNumber) { + u8 previouslyOn = Flags_GetClear(play, roomNumber); play->actorCtx.sceneFlags.clearedRoom &= ~(1 << roomNumber); + if (previouslyOn) { + GameInteractor_ExecuteOnSceneFlagUnset(play->sceneId, FLAG_CYCL_SCENE_CLEARED_ROOM, roomNumber); + } } /** @@ -852,10 +872,48 @@ s32 Flags_GetCollectible(PlayState* play, s32 flag) { */ void Flags_SetCollectible(PlayState* play, s32 flag) { if ((flag > 0) && (flag < 0x80)) { + u8 previouslyOff = !Flags_GetCollectible(play, flag); play->actorCtx.sceneFlags.collectible[(flag & ~0x1F) >> 5] |= 1 << (flag & 0x1F); + if (previouslyOff) { + GameInteractor_ExecuteOnSceneFlagSet(play->sceneId, FLAG_CYCL_SCENE_COLLECTIBLE, flag); + } } } +// #region 2S2H Originally these flags were all set with macros, for the port we want them to be in functions so we can hook into them +void Flags_SetWeekEventReg(s32 flag) { + u8 previouslyOff = !CHECK_WEEKEVENTREG(flag); + WEEKEVENTREG((flag) >> 8) = GET_WEEKEVENTREG((flag) >> 8) | ((flag) & 0xFF); + if (previouslyOff) { + GameInteractor_ExecuteOnFlagSet(FLAG_WEEK_EVENT_REG, flag); + } +} + +void Flags_ClearWeekEventReg(s32 flag) { + u8 previouslyOn = CHECK_WEEKEVENTREG(flag); + WEEKEVENTREG((flag) >> 8) = GET_WEEKEVENTREG((flag) >> 8) & (u8)~((flag) & 0xFF); + if (previouslyOn) { + GameInteractor_ExecuteOnFlagUnset(FLAG_WEEK_EVENT_REG, flag); + } +} + +void Flags_SetEventInf(s32 flag) { + u8 previouslyOff = !CHECK_EVENTINF(flag); + gSaveContext.eventInf[(flag) >> 4] |= (1 << ((flag) & 0xF)); + if (previouslyOff) { + GameInteractor_ExecuteOnFlagSet(FLAG_EVENT_INF, flag); + } +} + +void Flags_ClearEventInf(s32 flag) { + u8 previouslyOn = CHECK_EVENTINF(flag); + gSaveContext.eventInf[(flag) >> 4] &= (u8)~(1 << ((flag) & 0xF)); + if (previouslyOn) { + GameInteractor_ExecuteOnFlagUnset(FLAG_EVENT_INF, flag); + } +} +// #endregion + /* End of Flags section */ /* Start of TitleCard section */ @@ -1077,6 +1135,7 @@ void* Actor_FindSharedMemoryEntry(PlayState* play, s16 id) { } void Actor_Kill(Actor* actor) { + GameInteractor_ExecuteOnActorKill(actor); actor->draw = NULL; actor->update = NULL; actor->flags &= ~ACTOR_FLAG_TARGETABLE; diff --git a/mm/src/code/z_message.c b/mm/src/code/z_message.c index 8d5d3e4c8..26e09f390 100644 --- a/mm/src/code/z_message.c +++ b/mm/src/code/z_message.c @@ -6,6 +6,7 @@ #include "interface/parameter_static/parameter_static.h" #include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h" #include "BenPort.h" +#include "2s2h/Enhancements/GameInteractor/GameInteractor.h" #include "assets/archives/schedule_dma_static/schedule_dma_static_yar.h" #include "assets/archives/icon_item_static/icon_item_static_yar.h" #include "assets/archives/icon_item_24_static/icon_item_24_static_yar.h" @@ -3283,6 +3284,8 @@ void Message_OpenText(PlayState* play, u16 textId) { Player* player = GET_PLAYER(play); f32 var_fv0; + GameInteractor_ExecuteOnOpenText(textId); + // BENTODO do this somewhere else gSaveContext.options.language = LANGUAGE_ENG; diff --git a/mm/src/code/z_parameter.c b/mm/src/code/z_parameter.c index 0120e2d7d..27ff0cd67 100644 --- a/mm/src/code/z_parameter.c +++ b/mm/src/code/z_parameter.c @@ -2658,7 +2658,8 @@ s16 sArrowRefillCounts[] = { 10, 30, 40, 50 }; s16 sBombchuRefillCounts[] = { 20, 10, 1, 5 }; s16 sRupeeRefillCounts[] = { 1, 5, 10, 20, 50, 100, 200 }; -u8 Item_Give(PlayState* play, u8 item) { +// 2S2H [Enhancements] This was originally Item_Give, we wrapped it for hooking purposes +u8 Item_GiveImpl(PlayState* play, u8 item) { Player* player = GET_PLAYER(play); u8 i; u8 temp; @@ -3083,6 +3084,19 @@ u8 Item_Give(PlayState* play, u8 item) { return temp; } +// #region 2S2H [Enhancements] This is our wrapper around the original Item_Give function for hooking purposes +u8 Item_Give(PlayState* play, u8 item) { + if (!GameInteractor_ShouldItemGive(item)) { + return ITEM_NONE; + } + + u8 result = Item_GiveImpl(play, item); + GameInteractor_ExecuteOnItemGive(item); + + return result; +} +// #endregion + u8 Item_CheckObtainabilityImpl(u8 item) { s16 i; u8 slot; diff --git a/mm/src/code/z_play.c b/mm/src/code/z_play.c index 7f218a2fa..b627a5b33 100644 --- a/mm/src/code/z_play.c +++ b/mm/src/code/z_play.c @@ -34,6 +34,7 @@ u8 sMotionBlurStatus; #include "overlays/gamestates/ovl_file_choose/z_file_select.h" #include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h" #include "debug.h" +#include "2s2h/Enhancements/GameInteractor/GameInteractor.h" #include "2s2h/Enhancements/FrameInterpolation/FrameInterpolation.h" #include "2s2h/DeveloperTools/CollisionViewer.h" #include "2s2h/framebuffer_effects.h" @@ -2361,9 +2362,10 @@ void Play_Init(GameState* thisx) { sceneLayer = gSaveContext.sceneLayer; - Play_SpawnScene( - this, Entrance_GetSceneIdAbsolute(((void)0, gSaveContext.save.entrance) + ((void)0, gSaveContext.sceneLayer)), - Entrance_GetSpawnNum(((void)0, gSaveContext.save.entrance) + ((void)0, gSaveContext.sceneLayer))); + s32 sceneIdAbsolute = Entrance_GetSceneIdAbsolute(((void)0, gSaveContext.save.entrance) + ((void)0, gSaveContext.sceneLayer)); + s32 spawnNum = Entrance_GetSpawnNum(((void)0, gSaveContext.save.entrance) + ((void)0, gSaveContext.sceneLayer)); + Play_SpawnScene(this, sceneIdAbsolute, spawnNum); + KaleidoScopeCall_Init(this); Interface_Init(this); @@ -2479,4 +2481,6 @@ void Play_Init(GameState* thisx) { BombersNotebook_Init(&sBombersNotebook); sJustClosedBomberNotebook = false; + + GameInteractor_ExecuteOnSceneInit(sceneIdAbsolute, spawnNum); } diff --git a/mm/src/overlays/actors/ovl_En_Pm/z_en_pm.c b/mm/src/overlays/actors/ovl_En_Pm/z_en_pm.c index 3f2202046..7c6007702 100644 --- a/mm/src/overlays/actors/ovl_En_Pm/z_en_pm.c +++ b/mm/src/overlays/actors/ovl_En_Pm/z_en_pm.c @@ -1965,7 +1965,8 @@ s32 func_80AF9E7C(EnPm* this, PlayState* play) { if ((this->unk_356 & 0x10) && (this->unk_258 == 90)) { //! @bug Uses SET_WEEKEVENTREG instead of CHECK_WEEKEVENTREG - if (!SET_WEEKEVENTREG(WEEKEVENTREG_89_40)) { + // 2S2H [Port] We opted to fix this, doesn't have any effect on the outcome + if (!CHECK_WEEKEVENTREG(WEEKEVENTREG_89_40)) { SET_WEEKEVENTREG(WEEKEVENTREG_89_40); } }