Skip to content

Commit 5a70f50

Browse files
amnykonclaude
andcommitted
Fix missile widget: crashes, tab layout, tooltips, count/progress bar
- Fix Chili Integral Menu crash: remove SetVisibility call on unparented tab button; tab presence is handled by the commandCount/SetTabs machinery. - Guard nil ud.shieldPower in Chili Selections & CursorTip. - Make the missile widget self-contained: inline the blast-radius draw code (the VFS include file was never committed) and enable it by default. - Show missile commands: drop hidden flag, add action/params so they appear in the new Missiles tab with tooltips. - Order buttons like the missile silo (row 1: Trinity, Reef; row 2: silo missiles in buildoptions order) via per-command col/row positions. - Show count label ("xN") via DRAW_NAME_COMMANDS driven by the config's drawName flag. - Show a factory-style build progress bar via a new WG.IntegralMenu.SetCommandProgress API (added to externalFunctions so it survives the Initialize assignment); source progress from unit build progress or stockpile build percent as appropriate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 757d44d commit 5a70f50

4 files changed

Lines changed: 162 additions & 98 deletions

File tree

LuaUI/Configs/integral_menu_config.lua

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
11
local buildCmdFactory, buildCmdEconomy, buildCmdDefence, buildCmdSpecial, buildCmdUnits, cmdPosDef, factoryUnitPosDef = include("Configs/integral_menu_commands_processed.lua", nil, VFS.RAW_FIRST)
22

3+
-- Row 1: Trinity and Reef (standalone, not silo missiles).
4+
-- Row 2: the missile silo's missiles, in the silo's buildoptions order
5+
-- (tacnuke, seismic, empmissile, napalmmissile, missileslow).
36
local missileCmds = {
4-
{id = 39610, name = "EOS", icon = "tacnuke", tooltip = "Launch EOS (Tactical Nuke)\nTactical nuclear missile with high damage."},
5-
{id = 39611, name = "Seismic", icon = "seismic", tooltip = "Launch Seismic\nArea denial seismic missile, slows units."},
6-
{id = 39612, name = "Shockley", icon = "empmissile", tooltip = "Launch Shockley (EMP)\nElectromagnetic pulse missile disables units."},
7-
{id = 39613, name = "Inferno", icon = "napalmmissile", tooltip = "Launch Inferno (Napalm)\nNapalm missile with persistent damage."},
8-
{id = 39614, name = "Reef Missile", icon = "shipcarrier", tooltip = "Launch Disarm Missile\nDisables units temporarily."},
9-
{id = 39615, name = "Trinity", icon = "staticnuke", tooltip = "Launch Trinity (Strategic Nuke)\nLong-range nuclear missile."},
10-
{id = 39616, name = "Zeno", icon = "missileslow", tooltip = "Launch Zeno (Slow Missile)\nSlow homing missile with lingering damage."},
7+
{id = 39615, name = "Trinity", icon = "staticnuke", col = 1, row = 1, tooltip = "Launch Trinity (Strategic Nuke)\nLong-range nuclear missile."},
8+
{id = 39614, name = "Reef Missile", icon = "shipcarrier", col = 2, row = 1, tooltip = "Launch Disarm Missile\nDisables units temporarily."},
9+
{id = 39610, name = "EOS", icon = "tacnuke", col = 1, row = 2, tooltip = "Launch EOS (Tactical Nuke)\nTactical nuclear missile with high damage."},
10+
{id = 39611, name = "Seismic", icon = "seismic", col = 2, row = 2, tooltip = "Launch Seismic\nArea denial seismic missile, slows units."},
11+
{id = 39612, name = "Shockley", icon = "empmissile", col = 3, row = 2, tooltip = "Launch Shockley (EMP)\nElectromagnetic pulse missile disables units."},
12+
{id = 39613, name = "Inferno", icon = "napalmmissile", col = 4, row = 2, tooltip = "Launch Inferno (Napalm)\nNapalm missile with persistent damage."},
13+
{id = 39616, name = "Zeno", icon = "missileslow", col = 5, row = 2, tooltip = "Launch Zeno (Slow Missile)\nSlow homing missile with lingering damage."},
1114
}
1215

16+
local missileCmdPos = {}
17+
for _, missile in ipairs(missileCmds) do
18+
missileCmdPos[missile.id] = {col = missile.col, row = missile.row}
19+
end
20+
1321
local function isMissileCommand(cmdID)
14-
for _, missile in ipairs(missileCmds) do
15-
if cmdID == missile.id then return true end
16-
end
17-
return false
22+
return missileCmdPos[cmdID] ~= nil
1823
end
1924

2025
--------------------------------------------------------------------------------
@@ -498,7 +503,8 @@ for _, missile in ipairs(missileCmds) do
498503
local icon = unitDef and ("#" .. unitDef.id) or (imageDir .. 'Bold/attack.png')
499504
commandDisplayConfig[missile.id] = {
500505
texture = icon,
501-
tooltip = missile.tooltip
506+
tooltip = missile.tooltip,
507+
drawName = true, -- show the stockpile count / build progress string (set by the missile widget)
502508
}
503509
end
504510

@@ -533,7 +539,8 @@ local commandPanels = {
533539
name = "missiles",
534540
inclusionFunction = function(cmdID)
535541
if not hasMissileUnits() then return false end
536-
return isMissileCommand(cmdID)
542+
local pos = missileCmdPos[cmdID]
543+
return pos ~= nil, pos
537544
end,
538545
loiterable = true,
539546
buttonLayoutConfig = buttonLayoutConfig.command,

LuaUI/Widgets/gui_chili_integral_menu.lua

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,13 @@ end
122122

123123
local commandPanels, commandPanelMap, commandDisplayConfig, hiddenCommands, textConfig, buttonLayoutConfig, instantCommands, cmdPosDef = include("Configs/integral_menu_config.lua")
124124

125+
-- Commands whose displayConfig requests it draw their command.name (count / progress string) like stockpile.
126+
for cmdID, displayConfig in pairs(commandDisplayConfig) do
127+
if displayConfig.drawName then
128+
DRAW_NAME_COMMANDS[cmdID] = true
129+
end
130+
end
131+
125132
local statePanel = {}
126133
local tabPanel
127134
local selectionIndex = 0
@@ -554,6 +561,7 @@ local buttonsByCommand = {}
554561
local alreadyRemovedTag = {}
555562
local lastRemovedTagResetFrame = false
556563

564+
557565
--------------------------------------------------------------------------------
558566
--------------------------------------------------------------------------------
559567
-- Utility
@@ -2551,6 +2559,17 @@ options.fancySkinning.OnChange = UpdateBackgroundSkin
25512559
local externalFunctions = {} -- Appear unused in repo but are used by missions.
25522560
local initialized = false
25532561

2562+
-- Lets other widgets show a factory-style build progress bar on a command button
2563+
-- (e.g. the missile command center showing stockpile build progress).
2564+
function externalFunctions.SetCommandProgress(cmdID, progress)
2565+
local button = buttonsByCommand[cmdID]
2566+
if button then
2567+
button.SetProgressBar(progress or 0)
2568+
return true
2569+
end
2570+
return false
2571+
end
2572+
25542573
function externalFunctions.GetCommandButtonPosition(cmdID)
25552574
if not buttonsByCommand[cmdID] then
25562575
return
@@ -2593,7 +2612,8 @@ function widget:Update()
25932612
UpdateButtonSelection(cmdID)
25942613
UpdateReturnToOrders(cmdID)
25952614

2596-
-- Update tab badges and visibility
2615+
-- Update tab badges. Tab presence/visibility is handled by the
2616+
-- commandCount + SetTabs machinery, driven by each panel's inclusionFunction.
25972617
for i = 1, #commandPanels do
25982618
local panelData = commandPanels[i]
25992619

@@ -2602,19 +2622,6 @@ function widget:Update()
26022622
local count = WG[panelData.badgeCountWG] or 0
26032623
panelData.tabButton:UpdateBadgeCount(count)
26042624
end
2605-
2606-
-- Update tab visibility for panels with dynamic visibility
2607-
if panelData.name == "missiles" and panelData.tabButton and panelData.tabButton.button then
2608-
local hasCommands = false
2609-
local customCommands = widgetHandler.customCommands
2610-
for j = 1, #customCommands do
2611-
if panelData.inclusionFunction(customCommands[j].id) then
2612-
hasCommands = true
2613-
break
2614-
end
2615-
end
2616-
panelData.tabButton.button:SetVisibility(hasCommands)
2617-
end
26182625
end
26192626
end
26202627

LuaUI/Widgets/gui_chili_selections_and_cursortip.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2068,8 +2068,8 @@ local function GetSingleUnitInfoPanel(parentControl, isTooltipVersion)
20682068

20692069
local healthPos
20702070
if shieldBarUpdate then
2071-
if ud and (ud.shieldPower > 0 or ud.level) then
2072-
local shieldPower = (spGetUnitRulesParam(unitID, "comm_shield_max") or ud.shieldPower) * (Spring.GetUnitRulesParam(unitID, "totalShieldMaxMult") or 1)
2071+
if ud and ((ud.shieldPower or 0) > 0 or ud.level) then
2072+
local shieldPower = (spGetUnitRulesParam(unitID, "comm_shield_max") or ud.shieldPower or 0) * (Spring.GetUnitRulesParam(unitID, "totalShieldMaxMult") or 1)
20732073
local _, shieldCurrentPower = spGetUnitShieldState(unitID, -1)
20742074
if shieldCurrentPower and shieldPower then
20752075
shieldBarUpdate(true, nil, shieldCurrentPower, shieldPower, (shieldCurrentPower < shieldPower) and GetUnitShieldRegenString(unitID, ud))

LuaUI/Widgets/missle_command_center.lua

Lines changed: 119 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ function widget:GetInfo()
1010
license = "GNU GPL, v2 or later",
1111
layer = 0,
1212
handler = true,
13-
enabled = false,
13+
enabled = true,
1414
}
1515
end
1616

@@ -21,7 +21,86 @@ end
2121
--------------------------------------------------------------------------------
2222
--------------------------------------------------------------------------------
2323

24-
VFS.Include(LUAUI_DIRNAME.."Widgets/Utilities/engine_blast_radius.lua")
24+
local glVertex = gl.Vertex
25+
local glPushAttrib = gl.PushAttrib
26+
local glLineStipple = gl.LineStipple
27+
local glDepthTest = gl.DepthTest
28+
local glLineWidth = gl.LineWidth
29+
local glColor = gl.Color
30+
local glBeginEnd = gl.BeginEnd
31+
local glPopAttrib = gl.PopAttrib
32+
local glPopMatrix = gl.PopMatrix
33+
local glPushMatrix = gl.PushMatrix
34+
local glScale = gl.Scale
35+
local glTranslate = gl.Translate
36+
local GL_LINE_LOOP = GL.LINE_LOOP
37+
38+
local circleDivs = 64
39+
40+
local PI = math.pi
41+
local cos = math.cos
42+
local sin = math.sin
43+
44+
local aoeLineWidthMult = 64
45+
local numAoECircles = 9
46+
local aoeColor = {1, 0, 0, 1}
47+
local mouseDistance = 1000
48+
local floor = math.floor
49+
50+
local pulse_timmer = Spring.GetTimer()
51+
local function getPulse()
52+
local time = Spring.DiffTimers(Spring.GetTimer(), pulse_timmer)
53+
return 1 - (time - floor(time))
54+
end
55+
56+
local function UnitCircleVertices()
57+
for i = 1, circleDivs do
58+
local theta = 2 * PI * i / circleDivs
59+
glVertex(cos(theta), 0, sin(theta))
60+
end
61+
end
62+
63+
local function DrawCircle(x, y, z, radius)
64+
glPushMatrix()
65+
glTranslate(x, y, z)
66+
glScale(radius, radius, radius)
67+
glBeginEnd(GL_LINE_LOOP, UnitCircleVertices)
68+
glPopMatrix()
69+
end
70+
71+
local function drawBlastRadius(tx, ty, tz, weaponDef)
72+
local aoe = weaponDef.damageAreaOfEffect
73+
local ee = weaponDef.edgeEffectiveness
74+
75+
glLineWidth(math.max(0.05, aoeLineWidthMult * aoe / mouseDistance))
76+
77+
for i = 1, numAoECircles do
78+
local proportion = i / (numAoECircles + 1)
79+
local radius = aoe * proportion
80+
local alpha = aoeColor[4] * (1 - proportion) / (1 - proportion * ee) * getPulse()
81+
glColor(aoeColor[1], aoeColor[2], aoeColor[3], alpha)
82+
DrawCircle(tx, ty, tz, radius)
83+
end
84+
85+
glColor(1,1,1,1)
86+
glLineWidth(1)
87+
end
88+
89+
local function drawLine(x1, y1, z1, x2, y2, z2)
90+
glPushAttrib(GL.LINE_BITS)
91+
glLineStipple("springdefault")
92+
glDepthTest(false)
93+
glLineWidth(1)
94+
glColor(1, 0, 0, 1)
95+
glBeginEnd(GL.LINES, function()
96+
glVertex(x1, y1, z1)
97+
glVertex(x2, y2, z2)
98+
end)
99+
100+
glColor(1, 1, 1, 1)
101+
glLineStipple(false)
102+
glPopAttrib()
103+
end
25104

26105
--------------------------------------------------------------------------------
27106
--------------------------------------------------------------------------------
@@ -123,9 +202,17 @@ local function missle_class()
123202
if not Spring.GetUnitIsDead(unitID) then
124203
local unitDefID = Spring.GetUnitDefID(unitID)
125204
if unitDefID and self.launchableTypes[unitDefID] then
205+
-- Silo-built missiles exist as nanoframes while under construction.
126206
local _, _, _, _, buildProgress = Spring.GetUnitHealth(unitID)
127207
if buildProgress and buildProgress < 1 then
128208
maxProgress = math.max(maxProgress, buildProgress)
209+
else
210+
-- Stockpiling weapons (Trinity, Reef, subtac) report progress toward
211+
-- the next missile via the stockpile build percentage.
212+
local _, _, stockpileProgress = Spring.GetUnitStockpile(unitID)
213+
if stockpileProgress and stockpileProgress > 0 and stockpileProgress < 1 then
214+
maxProgress = math.max(maxProgress, stockpileProgress)
215+
end
129216
end
130217
end
131218
end
@@ -237,10 +324,13 @@ local function missle_class()
237324
local customCommands = widgetHandler.customCommands
238325

239326
customCommands[#customCommands+1] = {
240-
id = self.cmd,
241-
type = self.cmdType,
242-
hidden = true,
243-
cursor = 'Attack',
327+
id = self.cmd,
328+
type = self.cmdType,
329+
cursor = 'Attack',
330+
action = "missile_" .. self.name,
331+
name = self.displayName,
332+
disabled = self.disabled,
333+
params = {},
244334
}
245335
end
246336

@@ -532,42 +622,6 @@ local commands = {
532622

533623
local UPDATE_FREQUENCY = 0.25
534624
local timer = UPDATE_FREQUENCY + 1
535-
local buttonCache = {}
536-
537-
local missileCommandIDs = {
538-
[39610] = true,
539-
[39611] = true,
540-
[39612] = true,
541-
[39613] = true,
542-
[39614] = true,
543-
[39615] = true,
544-
[39616] = true,
545-
}
546-
547-
local function findButtonsByCommand()
548-
local screen = WG.Chili.Screen0
549-
if not screen then return end
550-
551-
local function searchChildren(control)
552-
if not control then return end
553-
554-
if control.cmdID and missileCommandIDs[control.cmdID] then
555-
buttonCache[control.cmdID] = control
556-
end
557-
558-
if control.children then
559-
for _, child in ipairs(control.children) do
560-
searchChildren(child)
561-
end
562-
end
563-
end
564-
565-
if screen.children then
566-
for _, child in ipairs(screen.children) do
567-
searchChildren(child)
568-
end
569-
end
570-
end
571625

572626
--------------------------------------------------------------------------------
573627
--------------------------------------------------------------------------------
@@ -585,48 +639,44 @@ function widget:Update(dt)
585639
end
586640
timer = 0
587641

588-
findButtonsByCommand()
589-
590642
local totalMissileCount = 0
643+
local changed = false
591644

592645
for _, command in pairs(commands) do
593646
local count = command:getCount()
594647
local buildProgress = command:getMaxBuildProgress()
595-
local customCommands = widgetHandler.customCommands
596648

597649
totalMissileCount = totalMissileCount + count
598650

599-
for i = 1, #customCommands do
600-
if customCommands[i].id == command.cmd then
601-
local displayName = ""
602-
if count > 0 then
603-
displayName = "x" .. count
604-
end
605-
if buildProgress > 0 then
606-
local progressPercent = math.floor(buildProgress * 100)
607-
if displayName ~= "" then
608-
displayName = displayName .. " (" .. progressPercent .. "%)"
609-
else
610-
displayName = progressPercent .. "%"
611-
end
612-
end
613-
customCommands[i].name = displayName
651+
-- Count string shown on the button (e.g. "x3"), empty when none stockpiled.
652+
-- This is drawn by the integral menu via the command's name field (see
653+
-- DRAW_NAME_COMMANDS / commandDisplayConfig.drawName).
654+
local displayName = ""
655+
if count > 0 then
656+
displayName = "x" .. count
657+
end
614658

615-
-- Disable button if no missiles available
616-
customCommands[i].disabled = (count == 0)
659+
-- Factory-style build progress bar on the button.
660+
if WG.IntegralMenu and WG.IntegralMenu.SetCommandProgress then
661+
WG.IntegralMenu.SetCommandProgress(command.cmd, buildProgress)
662+
end
617663

618-
-- Update visual progress bar on button
619-
local button = buttonCache[command.cmd]
620-
if button and button.SetProgressBar then
621-
button:SetProgressBar(buildProgress)
622-
end
623-
break
624-
end
664+
local disabled = (count == 0)
665+
if command.displayName ~= displayName or command.disabled ~= disabled then
666+
command.displayName = displayName
667+
command.disabled = disabled
668+
changed = true
625669
end
626670
end
627671

628672
-- Export total count for tab badge
629673
WG.missileTotalCount = totalMissileCount
674+
675+
-- The integral menu only re-reads custom commands on CommandsChanged, so force
676+
-- a layout update when the displayed count / progress actually changed.
677+
if changed then
678+
Spring.ForceLayoutUpdate()
679+
end
630680
end
631681

632682
function widget:CommandNotify(cmdID, cmdParams, cmdOptions)

0 commit comments

Comments
 (0)