From f856565de9a3a85d0302ac37b606738f081603e3 Mon Sep 17 00:00:00 2001 From: Atrik Date: Sat, 13 Dec 2025 11:52:03 +0100 Subject: [PATCH] Add formations paths to UnitMotionDebugOverlay Formation controllers now display their movement paths when enabling UnitMotion debug overlay. Key changes: - Formation controllers show long paths in blue and short paths in green - Formation controllers path are rendered if selecting some of the formation's members - Prevent the gui from making redundant calls to SetMotionDebugOverlay Existing limitations (not regressions): - Paths are often cleared before entities complete them - Formation members have short paths that rarely get rendered --- .../data/mods/public/gui/session/selection.js | 38 +++++++++++++++---- .../simulation/components/GuiInterface.js | 9 ++--- source/graphics/Color.h | 4 +- .../simulation2/components/CCmpUnitMotion.h | 30 +++++++++------ 4 files changed, 54 insertions(+), 27 deletions(-) diff --git a/binaries/data/mods/public/gui/session/selection.js b/binaries/data/mods/public/gui/session/selection.js index 2b6a2aa7fa..db04df594a 100644 --- a/binaries/data/mods/public/gui/session/selection.js +++ b/binaries/data/mods/public/gui/session/selection.js @@ -24,10 +24,28 @@ function _setStatusBars(ents, enabled) }); } -function _setMotionOverlay(ents, enabled) +function _setMotionOverlay(ents, enabled, motionDebugOverlay, force = false) { - if (ents.length) - Engine.GuiInterfaceCall("SetMotionDebugOverlay", { "entities": ents, "enabled": enabled }); + if (!force && !motionDebugOverlay) + return; + + // Get entities plus their formation controllers (if any) + const resultSet = new Set(); + for (const ent of ents) + { + resultSet.add(ent); + const entState = GetEntityState(ent); + if (entState?.unitAI?.formation) + { + resultSet.add(entState.unitAI.formation); + } + } + + if (resultSet.size) + Engine.GuiInterfaceCall("SetMotionDebugOverlay", { + "entities": resultSet, + "enabled": enabled + }); } function _playSound(ent) @@ -255,7 +273,7 @@ EntitySelection.prototype.update = function() // Disable any highlighting of the disappeared unit _setHighlight([ent], 0, false); _setStatusBars([ent], false); - _setMotionOverlay([ent], false); + _setMotionOverlay([ent], false, this.motionDebugOverlay); this.selected.delete(ent); this.groups.removeEnt(ent); @@ -263,6 +281,10 @@ EntitySelection.prototype.update = function() continue; } } + // Refresh the motion overlay + if (this.motionDebugOverlay) + _setMotionOverlay([...this.selected], true, this.motionDebugOverlay); + if (changed) this.onChange(); }; @@ -327,7 +349,7 @@ EntitySelection.prototype.addList = function(ents, quiet, force = false, addForm _setHighlight(added, 1, true); _setStatusBars(added, true); - _setMotionOverlay(added, this.motionDebugOverlay); + _setMotionOverlay(added, this.motionDebugOverlay, this.motionDebugOverlay); if (added.length) { // Play the sound if the entity is controllable by us or Gaia-owned. @@ -358,7 +380,7 @@ EntitySelection.prototype.removeList = function(ents, addFormationMembers = true _setHighlight(removed, 0, false); _setStatusBars(removed, false); - _setMotionOverlay(removed, false); + _setMotionOverlay(removed, false, this.motionDebugOverlay); this.onChange(); }; @@ -367,7 +389,7 @@ EntitySelection.prototype.reset = function() { _setHighlight(this.toList(), 0, false); _setStatusBars(this.toList(), false); - _setMotionOverlay(this.toList(), false); + _setMotionOverlay(this.toList(), false, this.motionDebugOverlay); this.selected.clear(); this.groups.reset(); this.onChange(); @@ -460,7 +482,7 @@ EntitySelection.prototype.setHighlightList = function(entities) EntitySelection.prototype.SetMotionDebugOverlay = function(enabled) { this.motionDebugOverlay = enabled; - _setMotionOverlay(this.toList(), enabled); + _setMotionOverlay(this.toList(), enabled, this.motionDebugOverlay, true); }; EntitySelection.prototype.onChange = function() diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js index d846151302..1098ce8abb 100644 --- a/binaries/data/mods/public/simulation/components/GuiInterface.js +++ b/binaries/data/mods/public/simulation/components/GuiInterface.js @@ -1986,12 +1986,9 @@ GuiInterface.prototype.SetObstructionDebugOverlay = function(player, enabled) GuiInterface.prototype.SetMotionDebugOverlay = function(player, data) { - for (const ent of data.entities) - { - const cmpUnitMotion = Engine.QueryInterface(ent, IID_UnitMotion); - if (cmpUnitMotion) - cmpUnitMotion.SetDebugOverlay(data.enabled); - } + data.entities.forEach(ent => { + Engine.QueryInterface(ent, IID_UnitMotion)?.SetDebugOverlay(data.enabled); + }); }; GuiInterface.prototype.SetRangeDebugOverlay = function(player, enabled) diff --git a/source/graphics/Color.h b/source/graphics/Color.h index f4a78be66a..430039d24c 100644 --- a/source/graphics/Color.h +++ b/source/graphics/Color.h @@ -45,8 +45,8 @@ extern void ColorActivateFastImpl(); struct CColor { - CColor() : r(-1.f), g(-1.f), b(-1.f), a(1.f) {} - CColor(float cr, float cg, float cb, float ca) : r(cr), g(cg), b(cb), a(ca) {} + constexpr CColor() : r(-1.f), g(-1.f), b(-1.f), a(1.f) {} + constexpr CColor(float cr, float cg, float cb, float ca) : r(cr), g(cg), b(cb), a(ca) {} /** * Returns whether this has been set to a valid color. diff --git a/source/simulation2/components/CCmpUnitMotion.h b/source/simulation2/components/CCmpUnitMotion.h index 06f982042a..62bd9f9dee 100644 --- a/source/simulation2/components/CCmpUnitMotion.h +++ b/source/simulation2/components/CCmpUnitMotion.h @@ -138,8 +138,14 @@ constexpr u8 BACKUP_HACK_DELAY = 10; */ constexpr u8 VERY_OBSTRUCTED_THRESHOLD = 10; -const CColor OVERLAY_COLOR_LONG_PATH(1, 1, 1, 1); -const CColor OVERLAY_COLOR_SHORT_PATH(1, 0, 0, 1); +struct PathColorPalette +{ + CColor longPath; + CColor shortPath; +}; + +constexpr PathColorPalette REGULAR_UNIT_PALETTE{{1, 1, 1, 1}, {1, 0, 0, 1}}; +constexpr PathColorPalette FORMATION_CONTROLLER_PALETTE{{0, 0, 1, 1}, {0, 1, 0, 1}}; } // anonymous namespace class CCmpUnitMotion final : public ICmpUnitMotion @@ -1902,7 +1908,6 @@ bool CCmpUnitMotion::IsTargetRangeReachable(entity_id_t target, entity_pos_t min return cmpPathfinder->IsGoalReachable(pos.X, pos.Y, goal, m_PassClass); } - void CCmpUnitMotion::RenderPath(const WaypointPath& path, std::vector& lines, CColor color) { bool floating = false; @@ -1934,17 +1939,20 @@ void CCmpUnitMotion::RenderPath(const WaypointPath& path, std::vector