/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "ultima/ultima8/debugtools.h"
#include "backends/imgui/imgui.h"
#include "backends/imgui/imgui_utils.h"
#include "ultima/ultima.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/palette.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/gumps/game_map_gump.h"
#include "ultima/ultima8/gumps/item_relative_gump.h"
#include "ultima/ultima8/gumps/target_gump.h"
#include "ultima/ultima8/usecode/usecode.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/world/actors/quick_avatar_mover_process.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/world/item.h"
#include "ultima/ultima8/misc/debugger.h"
namespace Ultima {
namespace Ultima8 {
typedef struct ImGuiState {
bool _itemStatsWindow = false;
bool _paletteWindow = false;
uint32 _targetItemId = kMainActorId;
ObjId _targetGumpId = 0;
} ImGuiState;
ImGuiState *_state = nullptr;
void showItemStats() {
if (!_state->_itemStatsWindow)
return;
ImGui::SetNextWindowPos(ImVec2(20, 20), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(300, 550), ImGuiCond_FirstUseEver);
if (ImGui::Begin("Item Stats", &_state->_itemStatsWindow)) {
if (_state->_targetGumpId) {
// Check if gump still exists and has a result
Gump *gump = getGump(_state->_targetGumpId);
if (gump) {
if (gump->GetResult()) {
_state->_targetItemId = gump->GetResult();
_state->_targetGumpId = 0;
}
} else {
_state->_targetGumpId = 0;
}
}
ImGui::BeginChild("##scrolling", ImVec2(0, -30), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
if (_state->_targetItemId) {
Item *item = getItem(_state->_targetItemId);
if (item) {
const ShapeInfo *si = item->getShapeInfo();
if (ImGui::CollapsingHeader("Properties", ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::BeginTable("Properties", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
// ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed);
// ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthFixed);
// ImGui::TableHeadersRow();
ImGui::TableNextColumn();
ImGui::Text("Id");
ImGui::TableNextColumn();
ImGui::Text("%u", item->getObjId());
ImGui::TableNextColumn();
ImGui::Text("Class");
ImGui::TableNextColumn();
ImGui::Text("%s", item->GetClassType()._className);
ImGui::TableNextColumn();
ImGui::Text("Shape");
ImGui::TableNextColumn();
ImGui::Text("%u", item->getShape());
ImGui::TableNextColumn();
ImGui::Text("Frame");
ImGui::TableNextColumn();
ImGui::Text("%u", item->getFrame());
ImGui::TableNextColumn();
ImGui::Text("Map");
ImGui::TableNextColumn();
ImGui::Text("%u", item->getMapNum());
ImGui::TableNextColumn();
ImGui::Text("Location");
ImGui::TableNextColumn();
if (item->getParent()) {
int32 gx, gy;
item->getGumpLocation(gx, gy);
ImGui::Text("(%d, %d)", gx, gy);
} else {
Point3 p = item->getLocation();
ImGui::Text("(%d, %d, %d)", p.x, p.y, p.z);
}
ImGui::TableNextColumn();
ImGui::Text("Footpad");
ImGui::TableNextColumn();
int32 xd, yd, zd;
item->getFootpadData(xd, yd, zd);
ImGui::Text("%d, %d, %d", xd, yd, zd);
ImGui::TableNextColumn();
// Original weights appear to be different
ImGui::Text("Weight");
ImGui::TableNextColumn();
ImGui::Text("%d", item->getWeight());
ImGui::TableNextColumn();
ImGui::Text("Volume");
ImGui::TableNextColumn();
ImGui::Text("%d", item->getVolume());
ImGui::TableNextColumn();
// Original menu had "Anim" here - None, Unk, Normal, Fast
// Original menu had "Fr" and "Sp" here
ImGui::Text("Family");
ImGui::TableNextColumn();
ImGui::Text("%d", item->getFamily());
ImGui::TableNextColumn();
ImGui::Text("Usecode");
ImGui::TableNextColumn();
const char *ucname = GameData::get_instance()->getMainUsecode()->get_class_name(item->getShape());
ImGui::Text("%s", ucname != nullptr ? ucname : "");
ImGui::TableNextColumn();
ImGui::Text("Quality");
ImGui::TableNextColumn();
ImGui::Text("%u", item->getQuality());
ImGui::TableNextColumn();
ImGui::Text("NPC Number");
ImGui::TableNextColumn();
ImGui::Text("%u", item->getNpcNum());
ImGui::TableNextColumn();
ImGui::Text("Equipment Type");
ImGui::TableNextColumn();
ImGui::Text("0x%x", si ? si->_equipType : 0);
ImGui::EndTable();
}
}
// ShapeInfo "Type Flags"
Common::String shapeFlagsLabel = Common::String::format("Shape Flags: 0x%04x###shapeflags", si->_flags);
if (ImGui::CollapsingHeader(shapeFlagsLabel.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) {
if (si) {
if (ImGui::BeginTable("Shape Flags", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
ImGui::TableNextColumn();
ImGui::Text("Fixed");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_fixed() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Solid");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_solid() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Sea");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_sea() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Land");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_land() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Occlude");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_occl() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Bag");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_bag() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Damaging");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_damaging() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Noisy");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_noisy() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Draw");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_draw() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Ignore");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_ignore() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Roof");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_roof() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Translucent");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_translucent() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Editor");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_editor() ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Explode");
ImGui::TableNextColumn();
ImGui::Text("%s", si->is_u8_explode() ? "true" : "false");
// Original menu had "InDlist" flag here
ImGui::EndTable();
}
}
}
Common::String itemFlagsLabel = Common::String::format("Item Flags: 0x%04x###itemflags", item->getFlags());
if (ImGui::CollapsingHeader(itemFlagsLabel.c_str(), ImGuiTreeNodeFlags_DefaultOpen)) {
if (ImGui::BeginTable("Item Flags", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
ImGui::TableNextColumn();
// Original menu had "InDlist" flag here - 0x0001?
ImGui::Text("Disposible");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_DISPOSABLE) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Owned"); // "Virtl"? Appears on items created by usecode
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_OWNED) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Contained");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_CONTAINED) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Invisible");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_INVISIBLE) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Flipped");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_FLIPPED) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("NPC");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_IN_NPC_LIST) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Fast Only"); // "GlobCr"
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_FAST_ONLY) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Gump Open"); // "GumpUp"
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_GUMP_OPEN) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Equipped");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_EQUIPPED) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Bounce");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_BOUNCING) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Ethereal");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_ETHEREAL) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Hanging");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_HANGING) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("In Fast Area");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_FASTAREA) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Low Friction");
ImGui::TableNextColumn();
ImGui::Text("%s", item->hasFlags(Item::FLG_LOW_FRICTION) ? "true" : "false");
ImGui::TableNextColumn();
ImGui::Text("Item Relative Gump"); // "IRGump" - 0x8000?
ImGui::TableNextColumn();
bool isRelative = false;
if (item->getGump()) {
Gump *g = getGump(item->getGump());
if (g && dynamic_cast(g)) {
isRelative = true;
}
}
ImGui::Text("%s", isRelative ? "true" : "false");
ImGui::EndTable();
}
}
} else {
ImGui::Text("Item not found: %d", _state->_targetItemId);
}
}
ImGui::EndChild();
if (ImGui::Button("Set Target")) {
// If not targetting start a new target gump and wait
if (!_state->_targetGumpId) {
TargetGump *targetGump = new TargetGump(0, 0);
targetGump->InitGump(0);
_state->_targetGumpId = targetGump->getObjId();
}
}
}
ImGui::End();
}
static void showPalette() {
if (!_state->_paletteWindow) {
return;
}
ImGui::SetNextWindowSize(ImVec2(320, 550), ImGuiCond_FirstUseEver);
if (ImGui::Begin("Palettes", &_state->_paletteWindow)) {
PaletteManager *pm = PaletteManager::get_instance();
Palette *p = pm->getPalette(PaletteManager::Pal_Game);
if (p) {
ImGui::SeparatorText("Game palette");
ImGui::PushID("palette_0");
ImGuiEx::Palette(*p);
ImGui::PopID();
ImGui::NewLine();
}
for (uint i = 1; i < pm->getNumPalettes(); i++) {
p = pm->getPalette(static_cast(i));
if (p) {
Common::String text = Common::String::format("Palette %d", i);
Common::String id = Common::String::format("palette_%d", i);
ImGui::SeparatorText(text.c_str());
ImGui::PushID(id.c_str());
ImGuiEx::Palette(*p);
ImGui::PopID();
ImGui::NewLine();
}
}
}
ImGui::End();
}
void onImGuiInit() {
_state = new ImGuiState();
}
void onImGuiRender() {
ImGuiIO& io = ImGui::GetIO();
if (!debugChannelSet(-1, kDebugImGui)) {
io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange | ImGuiConfigFlags_NoMouse;
return;
}
if (!_state)
return;
io.ConfigFlags &= ~(ImGuiConfigFlags_NoMouseCursorChange | ImGuiConfigFlags_NoMouse);
Ultima8Engine *engine = Ultima8Engine::get_instance();
if (ImGui::BeginMainMenuBar()) {
if (ImGui::BeginMenu("Toggles")) {
if (ImGui::MenuItem("Cheats", NULL, engine->areCheatsEnabled())) {
bool flag = engine->areCheatsEnabled();
engine->setCheatMode(!flag);
}
if (ImGui::MenuItem("Editor Items", NULL, engine->isShowEditorItems())) {
bool flag = engine->isShowEditorItems();
engine->setShowEditorItems(!flag);
}
if (ImGui::MenuItem("Footpads", NULL, GameMapGump::getShowFootpads())) {
bool flag = GameMapGump::getShowFootpads();
GameMapGump::setShowFootpads(!flag);
}
if (ImGui::BeginMenu("Gridlines")) {
int gridlines = GameMapGump::getGridlines();
if (ImGui::MenuItem("Auto", NULL, gridlines == -1)) {
GameMapGump::setGridlines(gridlines == -1 ? 0 : -1);
}
if (ImGui::MenuItem("128 x 128", NULL, gridlines == 128)) {
GameMapGump::setGridlines(gridlines == 128 ? 0 : 128);
}
if (ImGui::MenuItem("256 x 256", NULL, gridlines == 256)) {
GameMapGump::setGridlines(gridlines == 256 ? 0 : 256);
}
if (ImGui::MenuItem("512 x 512", NULL, gridlines == 512)) {
GameMapGump::setGridlines(gridlines == 512 ? 0 : 512);
}
if (ImGui::MenuItem("1024 x 1024", NULL, gridlines == 1024)) {
GameMapGump::setGridlines(gridlines == 1024 ? 0 : 1024);
}
ImGui::EndMenu();
}
if (ImGui::MenuItem("Hack Mover", NULL, engine->isHackMoverEnabled())) {
bool flag = engine->isHackMoverEnabled();
engine->setHackMoverEnabled(!flag);
}
if (ImGui::MenuItem("Quick Movement", NULL, QuickAvatarMoverProcess::isEnabled())) {
bool flag = QuickAvatarMoverProcess::isEnabled();
QuickAvatarMoverProcess::setEnabled(!flag);
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Item Stats", NULL, &_state->_itemStatsWindow);
ImGui::MenuItem("Palette", NULL, &_state->_paletteWindow);
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
showItemStats();
showPalette();
}
void onImGuiCleanup() {
delete _state;
_state = nullptr;
}
} // End of namespace Ultima8
} // End of namespace Ultima