[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