mirror of
https://github.com/izzy2lost/2ship2harkinian-Android.git
synced 2026-06-19 01:20:08 -07:00
Actor Viewer (#123)
This commit is contained in:
committed by
Garrett Cox
parent
fdbd495ca6
commit
a2ae88987a
@@ -35,6 +35,7 @@ namespace BenGui {
|
||||
|
||||
std::shared_ptr<SaveEditorWindow> mSaveEditorWindow;
|
||||
std::shared_ptr<HudEditorWindow> mHudEditorWindow;
|
||||
std::shared_ptr<ActorViewerWindow> mActorViewerWindow;
|
||||
|
||||
void SetupGuiElements() {
|
||||
auto gui = LUS::Context::GetInstance()->GetWindow()->GetGui();
|
||||
@@ -80,11 +81,15 @@ namespace BenGui {
|
||||
|
||||
mHudEditorWindow = std::make_shared<HudEditorWindow>("gWindows.HudEditor", "Hud Editor");
|
||||
gui->AddGuiWindow(mHudEditorWindow);
|
||||
|
||||
mActorViewerWindow = std::make_shared<ActorViewerWindow>("gWindows.ActorViewer", "Actor Viewer");
|
||||
gui->AddGuiWindow(mActorViewerWindow);
|
||||
}
|
||||
|
||||
void Destroy() {
|
||||
mSaveEditorWindow = nullptr;
|
||||
mHudEditorWindow = nullptr;
|
||||
mActorViewerWindow = nullptr;
|
||||
mStatsWindow = nullptr;
|
||||
mConsoleWindow = nullptr;
|
||||
mBenMenuBar = nullptr;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <stdio.h>
|
||||
#include "BenMenuBar.h"
|
||||
#include "DeveloperTools/SaveEditor.h"
|
||||
#include "DeveloperTools/ActorViewer.h"
|
||||
|
||||
namespace BenGui {
|
||||
void SetupHooks();
|
||||
|
||||
@@ -297,6 +297,7 @@ extern std::shared_ptr<LUS::GuiWindow> mStatsWindow;
|
||||
extern std::shared_ptr<LUS::GuiWindow> mConsoleWindow;
|
||||
extern std::shared_ptr<LUS::GuiWindow> mGfxDebuggerWindow;
|
||||
extern std::shared_ptr<SaveEditorWindow> mSaveEditorWindow;
|
||||
extern std::shared_ptr<ActorViewerWindow> mActorViewerWindow;
|
||||
|
||||
void DrawDeveloperToolsMenu() {
|
||||
if (UIWidgets::BeginMenu("Developer Tools", UIWidgets::Colors::Yellow)) {
|
||||
@@ -345,6 +346,11 @@ void DrawDeveloperToolsMenu() {
|
||||
.tooltip = "Enables the Save Editor window, allowing you to edit your save file"
|
||||
});
|
||||
}
|
||||
if (mActorViewerWindow) {
|
||||
UIWidgets::WindowButton("Actor Viewer", "gWindows.ActorViewer", mActorViewerWindow, {
|
||||
.tooltip = "Enables the Actor Viewer window, allowing you to view actors in the world."
|
||||
});
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "window/gui/GuiMenuBar.h"
|
||||
#include "window/gui/GuiElement.h"
|
||||
#include "DeveloperTools/SaveEditor.h"
|
||||
#include "DeveloperTools/ActorViewer.h"
|
||||
|
||||
namespace BenGui {
|
||||
class BenMenuBar : public LUS::GuiMenuBar {
|
||||
|
||||
@@ -393,4 +393,73 @@ namespace UIWidgets {
|
||||
}
|
||||
return dirty;
|
||||
}
|
||||
|
||||
void DrawFlagArray32(const std::string& name, uint32_t& flags) {
|
||||
ImGui::PushID(name.c_str());
|
||||
for (int32_t flagIndex = 0; flagIndex < 32; flagIndex++) {
|
||||
if ((flagIndex % 8) != 0) {
|
||||
ImGui::SameLine();
|
||||
}
|
||||
ImGui::PushID(flagIndex);
|
||||
uint32_t bitMask = 1 << flagIndex;
|
||||
bool flag = (flags & bitMask) != 0;
|
||||
std::string label = std::to_string(flagIndex);
|
||||
if (UIWidgets::Checkbox(label.c_str(), &flag, {.tooltip = label.c_str(),
|
||||
.labelPosition = LabelPosition::None })) {
|
||||
if (flag) {
|
||||
flags |= bitMask;
|
||||
} else {
|
||||
flags &= ~bitMask;
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void DrawFlagArray16(const std::string& name, uint16_t& flags) {
|
||||
ImGui::PushID(name.c_str());
|
||||
for (int16_t flagIndex = 0; flagIndex < 16; flagIndex++) {
|
||||
if ((flagIndex % 8) != 0) {
|
||||
ImGui::SameLine();
|
||||
}
|
||||
ImGui::PushID(flagIndex);
|
||||
uint16_t bitMask = 1 << flagIndex;
|
||||
bool flag = (flags & bitMask) != 0;
|
||||
std::string label = std::to_string(flagIndex);
|
||||
if (UIWidgets::Checkbox(label.c_str(), &flag, {.tooltip = label.c_str(),
|
||||
.labelPosition = LabelPosition::None })) {
|
||||
if (flag) {
|
||||
flags |= bitMask;
|
||||
} else {
|
||||
flags &= ~bitMask;
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void DrawFlagArray8(const std::string& name, uint8_t& flags) {
|
||||
ImGui::PushID(name.c_str());
|
||||
for (int8_t flagIndex = 0; flagIndex < 8; flagIndex++) {
|
||||
if ((flagIndex % 8) != 0) {
|
||||
ImGui::SameLine();
|
||||
}
|
||||
ImGui::PushID(flagIndex);
|
||||
uint8_t bitMask = 1 << flagIndex;
|
||||
bool flag = (flags & bitMask) != 0;
|
||||
std::string label = std::to_string(flagIndex);
|
||||
if (UIWidgets::Checkbox(label.c_str(), &flag, {.tooltip = label.c_str(),
|
||||
.labelPosition = LabelPosition::None })) {
|
||||
if (flag) {
|
||||
flags |= bitMask;
|
||||
} else {
|
||||
flags &= ~bitMask;
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,6 +409,9 @@ namespace UIWidgets {
|
||||
bool CVarSliderInt(const char* label, const char* cvarName, int32_t min, int32_t max, const int32_t defaultValue, const IntSliderOptions& options = {});
|
||||
bool SliderFloat(const char* label, float* value, float min, float max, const FloatSliderOptions& options = {});
|
||||
bool CVarSliderFloat(const char* label, const char* cvarName, float min, float max, const float defaultValue, const FloatSliderOptions& options = {});
|
||||
void DrawFlagArray32(const std::string& name, uint32_t& flags);
|
||||
void DrawFlagArray16(const std::string& name, uint16_t& flags);
|
||||
void DrawFlagArray8(const std::string& name, uint8_t& flags);
|
||||
}
|
||||
|
||||
#endif /* UIWidgets_hpp */
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
#include "ActorViewer.h"
|
||||
#include "2s2h/BenGui/UIWidgets.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "global.h"
|
||||
extern PlayState* gPlayState;
|
||||
|
||||
}
|
||||
|
||||
typedef struct ActorInfo {
|
||||
u16 id;
|
||||
u16 params;
|
||||
Vec3f pos;
|
||||
Vec3s rot;
|
||||
} ActorInfo;
|
||||
|
||||
typedef enum Method {
|
||||
LIST,
|
||||
TARGET,
|
||||
HOLD,
|
||||
} Method;
|
||||
|
||||
std::array<const char*, 12> 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<s16, const char*> 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<Actor*> GetCurrentSceneActors() {
|
||||
if (!gPlayState) return {};
|
||||
|
||||
std::vector<Actor*> 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<Actor*> list;
|
||||
|
||||
static s16 lastSceneId = 0;
|
||||
static s16 newActorId = 0;
|
||||
static u8 category = 0;
|
||||
static s8 method = -1;
|
||||
static std::string filler = "Please select";
|
||||
|
||||
void ResetVariables() {
|
||||
display = fetch = {};
|
||||
newActor = {};
|
||||
filler = "Please select";
|
||||
}
|
||||
|
||||
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;
|
||||
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::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() {
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <libultraship/libultraship.h>
|
||||
|
||||
class ActorViewerWindow : public LUS::GuiWindow {
|
||||
public:
|
||||
using GuiWindow::GuiWindow;
|
||||
|
||||
void InitElement() override;
|
||||
void DrawElement() override;
|
||||
void UpdateElement() override {};
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "global.h"
|
||||
#include <libultraship/bridge.h>
|
||||
|
||||
/**
|
||||
* Spawn an object file of a specified ID that will persist through room changes.
|
||||
@@ -115,7 +116,7 @@ s32 Object_GetSlot(ObjectContext* objectCtx, s16 objectId) {
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
return CVarGetInteger("gObjDep", 0) ? 0 : -1;
|
||||
}
|
||||
|
||||
s32 Object_IsLoaded(ObjectContext* objectCtx, s32 slot) {
|
||||
|
||||
Reference in New Issue
Block a user