#include "ActorViewer.h" #include "2s2h/BenGui/UIWidgets.hpp" #include "global.h" #include "2s2h/Enhancements/GameInteractor/GameInteractor.h" typedef struct ActorInfo { u16 id; u16 params; Vec3f pos; Vec3s rot; } ActorInfo; typedef enum Method { LIST, TARGET, HOLD, } Method; std::array acMapping = { "Switch", "Background", "Player", "Explosive", "NPC", "Enemy", "Prop", "Item/Action", "Misc.", "Boss", "Door", "Chest" }; #define DEFINE_ACTOR(name, _enumValue, _allocType, _debugName, _humanName) { _enumValue, _humanName }, #define DEFINE_ACTOR_INTERNAL(_name, _enumValue, _allocType, _debugName, _humanName) { _enumValue, _humanName }, #define DEFINE_ACTOR_UNSET(_enumValue) { _enumValue, "Unset" }, std::unordered_map actorDescriptions = { #include "tables/actor_table.h" }; #undef DEFINE_ACTOR #undef DEFINE_ACTOR_INTERNAL #undef DEFINE_ACTOR_UNSET std::string GetActorDescription(u16 actorNum) { return actorDescriptions.contains(actorNum) ? actorDescriptions[actorNum] : "???"; } std::vector GetCurrentSceneActors() { if (!gPlayState) return {}; std::vector sceneActors; for (size_t category = ACTORCAT_SWITCH; category < ACTORCAT_MAX; category++) { Actor* currentActor = gPlayState->actorCtx.actorLists[category].first; if (currentActor != nullptr) { while (currentActor != nullptr) { sceneActors.push_back(currentActor); currentActor = currentActor->next; } } } return sceneActors; } static bool needs_reset = false; static Actor* display{}; static Actor* fetch; static ActorInfo newActor; static std::vector list; static s16 lastSceneId = 0; static s16 newActorId = 0; static u8 category = 0; static s8 method = -1; static std::string filler = "Please select"; static HOOK_ID preventActorDrawHookId = 0; static HOOK_ID preventActorUpdateHookId = 0; void ResetVariables() { display = fetch = {}; newActor = {}; filler = "Please select"; GameInteractor::Instance->UnregisterGameHookForPtr(preventActorDrawHookId); GameInteractor::Instance->UnregisterGameHookForPtr(preventActorUpdateHookId); preventActorDrawHookId = 0; preventActorUpdateHookId = 0; } void ActorViewerWindow::DrawElement() { ImGui::SetNextWindowSize(ImVec2(520, 600), ImGuiCond_FirstUseEver); if (!ImGui::Begin("Actor Viewer", &mIsVisible, ImGuiWindowFlags_NoFocusOnAppearing)) { ImGui::End(); return; } if (gPlayState != nullptr) { needs_reset = lastSceneId != gPlayState->sceneId; if (needs_reset) { ResetVariables(); lastSceneId = gPlayState->sceneId; needs_reset = false; } if (ImGui::BeginCombo("Actor", filler.c_str())) { if (gPlayState != nullptr) { list = GetCurrentSceneActors(); lastSceneId = gPlayState->sceneId; } if (!list.empty()) { u8 count = 0; u8 prev_category = 0xff; for (size_t i = 0; i < list.size(); i++) { std::string label = acMapping[list[i]->category]; if (list[i]->category != prev_category) { prev_category = list[i]->category; count = 1; } else { count++; } label += " " + std::to_string(count) + ": " + GetActorDescription(list[i]->id); if (ImGui::Selectable(label.c_str())) { method = LIST; display = list[i]; newActorId = i; filler = label; GameInteractor::Instance->UnregisterGameHookForPtr( preventActorDrawHookId); GameInteractor::Instance->UnregisterGameHookForPtr( preventActorUpdateHookId); preventActorDrawHookId = 0; preventActorUpdateHookId = 0; break; } } } ImGui::EndCombo(); } if (ImGui::TreeNode("Selected Actor")) { if (display != nullptr) { ImGui::BeginGroup(); ImGui::Text("ID: %hd", display->id); ImGui::Text("Description: %s", GetActorDescription(display->id).c_str()); ImGui::Text("Category: %s", acMapping[display->category]); ImGui::Text("Params: %hd", display->params); ImGui::EndGroup(); ImGui::BeginGroup(); if (preventActorDrawHookId) { if (ImGui::Button("Continue Drawing", ImVec2(ImGui::GetFontSize() * 10, 0))) { GameInteractor::Instance->UnregisterGameHookForPtr( preventActorDrawHookId); preventActorDrawHookId = 0; } } else { if (ImGui::Button("Stop Drawing", ImVec2(ImGui::GetFontSize() * 10, 0))) { preventActorDrawHookId = GameInteractor::Instance->RegisterGameHookForPtr( (uintptr_t)display, [](Actor* _, bool* result) { *result = false; }); } } ImGui::SameLine(); if (preventActorUpdateHookId) { if (ImGui::Button("Continue Updating", ImVec2(ImGui::GetFontSize() * 10, 0))) { GameInteractor::Instance->UnregisterGameHookForPtr( preventActorUpdateHookId); preventActorUpdateHookId = 0; } } else { if (ImGui::Button("Stop Updating", ImVec2(ImGui::GetFontSize() * 10, 0))) { preventActorUpdateHookId = GameInteractor::Instance->RegisterGameHookForPtr( (uintptr_t)display, [](Actor* _, bool* result) { *result = false; }); } } ImGui::EndGroup(); ImGui::PushItemWidth(ImGui::GetFontSize() * 10); ImGui::BeginGroup(); ImGui::Text("Actor Position"); ImGui::InputScalar("x", ImGuiDataType_Float, &display->world.pos.x); ImGui::SameLine(); ImGui::InputScalar("y", ImGuiDataType_Float, &display->world.pos.y); ImGui::SameLine(); ImGui::InputScalar("z", ImGuiDataType_Float, &display->world.pos.z); ImGui::EndGroup(); ImGui::BeginGroup(); ImGui::Text("Actor Rotation"); ImGui::InputScalar("rx", ImGuiDataType_S16, &display->world.rot.x); ImGui::SameLine(); ImGui::InputScalar("ry", ImGuiDataType_S16, &display->world.rot.y); ImGui::SameLine(); ImGui::InputScalar("rz", ImGuiDataType_S16, &display->world.rot.z); ImGui::EndGroup(); ImGui::PopItemWidth(); ImGui::PushItemWidth(ImGui::GetFontSize() * 5); if ((display->category == ACTORCAT_BOSS || display->category == ACTORCAT_ENEMY) && display->colChkInfo.health > 0) { ImGui::InputScalar("Enemy Health", ImGuiDataType_U8, &display->colChkInfo.health); } ImGui::PopItemWidth(); ImGui::BeginGroup(); ImGui::Text("Flags"); UIWidgets::DrawFlagArray32("flags", display->flags); ImGui::EndGroup(); ImGui::BeginGroup(); ImGui::Text("BG Check"); UIWidgets::DrawFlagArray16("bgCheckFlags", display->bgCheckFlags); ImGui::EndGroup(); } if (UIWidgets::Button("Refresh")) { list = GetCurrentSceneActors(); display = list[newActorId]; } if (UIWidgets::Button("Go to Actor")) { Player* player = GET_PLAYER(gPlayState); if (display != nullptr) { Math_Vec3f_Copy(&player->actor.world.pos, &display->world.pos); Math_Vec3f_Copy(&player->actor.home.pos, &display->world.pos); } } if (UIWidgets::Button("Fetch: Target", { .tooltip = "Grabs actor with target arrow above it." })) { Player* player = GET_PLAYER(gPlayState); fetch = player->lockOnActor; if (fetch != nullptr) { display = fetch; category = fetch->category; list = GetCurrentSceneActors(); method = TARGET; } else { display = {}; } } if (UIWidgets::Button("Fetch: Held", { .tooltip = "Grabs actor Link is currently holding." })) { Player* player = GET_PLAYER(gPlayState); fetch = player->heldActor; if (fetch != nullptr) { display = fetch; category = fetch->category; list = GetCurrentSceneActors(); method = HOLD; } else { display = {}; } } if (UIWidgets::Button("Kill", { .color = UIWidgets::Colors::Red }) && display != nullptr && display->id != ACTOR_PLAYER) { Actor_Kill(display); } ImGui::TreePop(); } if (ImGui::TreeNode("New...")) { ImGui::PushItemWidth(ImGui::GetFontSize() * 10); ImU16 one = 1; ImGui::Text("%s", GetActorDescription(newActor.id).c_str()); ImGui::InputScalar("ID", ImGuiDataType_S16, &newActor.id, &one); ImGui::InputScalar("Params", ImGuiDataType_S16, &newActor.params, &one); ImGui::BeginGroup(); ImGui::Text("New Actor Position"); ImGui::InputScalar("x", ImGuiDataType_Float, &newActor.pos.x); ImGui::SameLine(); ImGui::InputScalar("y", ImGuiDataType_Float, &newActor.pos.y); ImGui::SameLine(); ImGui::InputScalar("z", ImGuiDataType_Float, &newActor.pos.z); ImGui::EndGroup(); ImGui::BeginGroup(); ImGui::Text("New Actor Rotation"); ImGui::InputScalar("rX", ImGuiDataType_S16, &newActor.rot.x); ImGui::SameLine(); ImGui::InputScalar("rY", ImGuiDataType_S16, &newActor.rot.y); ImGui::SameLine(); ImGui::InputScalar("rZ", ImGuiDataType_S16, &newActor.rot.z); ImGui::EndGroup(); UIWidgets::CVarCheckbox("Remove Obj Dep?", "gObjDep", { .tooltip = "Allows actors to spawn where/when they normally wouldn't." }); if (UIWidgets::Button("Fetch from Link")) { Player* player = GET_PLAYER(gPlayState); newActor.pos = player->actor.world.pos; newActor.rot = player->actor.world.rot; } if (UIWidgets::Button("Spawn", { .color = UIWidgets::Colors::Green })) { Actor_Spawn(&gPlayState->actorCtx, gPlayState, newActor.id, newActor.pos.x, newActor.pos.y, newActor.pos.z, newActor.rot.x, newActor.rot.y, newActor.rot.z, newActor.params); } if (UIWidgets::Button("Spawn as Child", { .color = UIWidgets::Colors::Green })) { Actor* parent = display; if (parent != nullptr) { Actor_SpawnAsChild(&gPlayState->actorCtx, parent, gPlayState, newActor.id, newActor.pos.x, newActor.pos.y, newActor.pos.z, newActor.rot.x, newActor.rot.y, newActor.rot.z, newActor.params); } else { Audio_PlaySfx(NA_SE_SY_ERROR); } } if (UIWidgets::Button("Reset")) { newActor = {}; } ImGui::TreePop(); } } else { ImGui::Text("Playstate needed for actors!"); } ImGui::End(); } void ActorViewerWindow::InitElement() { }