[Scummvm-git-logs] scummvm master -> 31b9ff4af88a1d2b748f9fe3ac68f8bf5fad4065
bluegr
noreply at scummvm.org
Fri Jun 13 08:13:41 UTC 2025
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
31b9ff4af8 ULTIMA8: Add imgui debug tools with item stats window
Commit: 31b9ff4af88a1d2b748f9fe3ac68f8bf5fad4065
https://github.com/scummvm/scummvm/commit/31b9ff4af88a1d2b748f9fe3ac68f8bf5fad4065
Author: Matthew Jimenez (matthew.jimenez at outlook.com)
Date: 2025-06-13T10:13:38+02:00
Commit Message:
ULTIMA8: Add imgui debug tools with item stats window
Changed paths:
A engines/ultima/ultima8/debugtools.cpp
A engines/ultima/ultima8/debugtools.h
engines/ultima/configure.engine
engines/ultima/detection.cpp
engines/ultima/module.mk
engines/ultima/ultima.h
engines/ultima/ultima8/ultima8.cpp
diff --git a/engines/ultima/configure.engine b/engines/ultima/configure.engine
index 4476f77d2ae..48b47d54563 100644
--- a/engines/ultima/configure.engine
+++ b/engines/ultima/configure.engine
@@ -4,4 +4,4 @@ add_engine ultima "Ultima" yes "ultima1 ultima4 ultima6 ultima8" "" "midi"
add_engine ultima1 "Ultima I - The First Age of Darkness" no "" "" ""
add_engine ultima4 "Ultima IV - Quest of the Avatar" yes "" "" "16bit"
add_engine ultima6 "Ultima VI = The False Prophet" yes "" "" "highres 16bit lua"
-add_engine ultima8 "Ultima VIII - Pagan" yes "" "" "highres 16bit"
+add_engine ultima8 "Ultima VIII - Pagan" yes "" "" "highres 16bit" "imgui"
diff --git a/engines/ultima/detection.cpp b/engines/ultima/detection.cpp
index 046de392228..c3df3b1cc77 100644
--- a/engines/ultima/detection.cpp
+++ b/engines/ultima/detection.cpp
@@ -57,6 +57,7 @@ const DebugChannelDef UltimaMetaEngineDetection::debugFlagList[] = {
{Ultima::kDebugActor, "Actor", "Actor debug level"},
{Ultima::kDebugObject, "Object", "Object debug level"},
{Ultima::kDebugCollision, "Collision", "Collision debug level"},
+ {Ultima::kDebugImGui, "imgui", "Imgui debug output"},
DEBUG_CHANNEL_END
};
diff --git a/engines/ultima/module.mk b/engines/ultima/module.mk
index 0b89e1de875..10b90868531 100644
--- a/engines/ultima/module.mk
+++ b/engines/ultima/module.mk
@@ -594,6 +594,12 @@ MODULE_OBJS += \
ultima8/world/actors/targeted_anim_process.o \
ultima8/world/actors/teleport_to_egg_process.o \
ultima8/world/actors/u8_avatar_mover_process.o
+
+ifdef USE_IMGUI
+MODULE_OBJS += \
+ ultima8/debugtools.o
+endif
+
endif
# This module can be built as a plugin
diff --git a/engines/ultima/ultima.h b/engines/ultima/ultima.h
index 1341184ccda..ef8e11b795d 100644
--- a/engines/ultima/ultima.h
+++ b/engines/ultima/ultima.h
@@ -31,6 +31,7 @@ enum UltimaDebugChannels {
kDebugActor,
kDebugObject,
kDebugCollision,
+ kDebugImGui,
};
} // End of namespace Ultima
diff --git a/engines/ultima/ultima8/debugtools.cpp b/engines/ultima/ultima8/debugtools.cpp
new file mode 100644
index 00000000000..0970e78f250
--- /dev/null
+++ b/engines/ultima/ultima8/debugtools.cpp
@@ -0,0 +1,348 @@
+/* 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "ultima/ultima8/debugtools.h"
+#include "backends/imgui/imgui.h"
+#include "ultima/ultima.h"
+#include "ultima/ultima8/ultima8.h"
+#include "ultima/ultima8/games/game_data.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/get_object.h"
+#include "ultima/ultima8/world/item.h"
+#include "ultima/ultima8/misc/debugger.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+typedef struct ImGuiState {
+ bool _itemStats = false;
+ uint32 _targetItemId = kMainActorId;
+ ObjId _targetGumpId = 0;
+} ImGuiState;
+
+ImGuiState *_state = nullptr;
+
+void showItemStats() {
+ if (!_state->_itemStats)
+ return;
+
+ ImGui::SetNextWindowPos(ImVec2(20, 20), ImGuiCond_FirstUseEver);
+ ImGui::SetNextWindowSize(ImVec2(300, 550), ImGuiCond_FirstUseEver);
+
+ if (ImGui::Begin("Item Stats", &_state->_itemStats)) {
+ 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<ItemRelativeGump *>(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();
+}
+
+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);
+
+ if (ImGui::BeginMainMenuBar()) {
+ if (ImGui::BeginMenu("View")) {
+ ImGui::MenuItem("Item Stats", NULL, &_state->_itemStats);
+ ImGui::EndMenu();
+ }
+ ImGui::EndMainMenuBar();
+ }
+
+ showItemStats();
+}
+
+void onImGuiCleanup() {
+ delete _state;
+ _state = nullptr;
+}
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
+
diff --git a/engines/ultima/ultima8/debugtools.h b/engines/ultima/ultima8/debugtools.h
new file mode 100644
index 00000000000..cc9c3918d31
--- /dev/null
+++ b/engines/ultima/ultima8/debugtools.h
@@ -0,0 +1,33 @@
+/* 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef ULTIMA_ULTIMA8_DEBUGTOOLS_H
+#define ULTIMA_ULTIMA8_DEBUGTOOLS_H
+
+namespace Ultima {
+namespace Ultima8 {
+void onImGuiInit();
+void onImGuiRender();
+void onImGuiCleanup();
+} // End of namespace Ultima8
+} // End of namespace Ultima
+
+#endif // ULTIMA_ULTIMA8_DEBUGTOOLS_H
diff --git a/engines/ultima/ultima8/ultima8.cpp b/engines/ultima/ultima8/ultima8.cpp
index 480e6caffe1..91132b5bf6c 100644
--- a/engines/ultima/ultima8/ultima8.cpp
+++ b/engines/ultima/ultima8/ultima8.cpp
@@ -27,6 +27,7 @@
#include "image/png.h"
#include "engines/dialogs.h"
#include "engines/util.h"
+#include "ultima/ultima.h"
// TODO: !! a lot of these includes are just for some hacks... clean up sometime
#include "ultima/ultima8/conf/config_file_manager.h"
@@ -96,6 +97,9 @@
#include "ultima/ultima8/audio/midi_player.h"
#include "ultima/ultima8/gumps/shape_viewer_gump.h"
#include "ultima/ultima8/metaengine.h"
+#ifdef USE_IMGUI
+#include "ultima/ultima8/debugtools.h"
+#endif
//#define PAINT_TIMING 1
@@ -321,6 +325,16 @@ Common::Error Ultima8Engine::initialize() {
warning("game failed to initialize");
}
paint();
+
+#ifdef USE_IMGUI
+ ImGuiCallbacks callbacks;
+ bool drawImGui = debugChannelSet(-1, kDebugImGui);
+ callbacks.init = Ultima8::onImGuiInit;
+ callbacks.render = drawImGui ? Ultima8::onImGuiRender : nullptr;
+ callbacks.cleanup = Ultima8::onImGuiCleanup;
+ _system->setImGuiCallbacks(callbacks);
+#endif
+
return Common::kNoError;
}
@@ -377,6 +391,10 @@ void Ultima8Engine::deinitialize() {
_configFileMan->clearRoot("game");
_gameInfo = nullptr;
+#ifdef USE_IMGUI
+ _system->setImGuiCallbacks(ImGuiCallbacks());
+#endif
+
debug(1, "-- Game Shutdown -- ");
}
More information about the Scummvm-git-logs
mailing list