function widget:GetInfo() return { name = 'AI Button example', desc = '', author = 'Silver', version = '1.0', date = '16.05.2026', license = 'GNU GPL, v2 or later', layer = 0, handler = true, enabled = true } end local updateRate = 15 local CMD_FIGHT = CMD.FIGHT local CMD_STOP = CMD.STOP local CMD_MOVE = CMD.MOVE local CMD_INTERNAL = CMD.OPT_INTERNAL local isUnitIdle = {} local AIUnits = {} local Spring_GetUnitDefID = Spring.GetUnitDefID local Spring_GetUnitPosition = Spring.GetUnitPosition local Spring_GetSelectedUnits = Spring.GetSelectedUnits local Spring_GiveOrderToUnit = Spring.GiveOrderToUnit local math_hypot = math.hypot local math_mix = math.mix local math_cos = math.cos local math_sin = math.sin local math_max = math.max local math_min = math.min local math_rad = math.rad local CMD_AI_MODE = 37777 local selectionRankCmdDesc = { id = CMD_AI_MODE, type = CMDTYPE.ICON_MODE, tooltip = 'Enables automatic micro management', name = 'AI MODE', actions = {0, ' AI \nOFF', 'AI \nON'} } local nbActions = #selectionRankCmdDesc.actions - 1 function widget:CommandsChanged() local customCommands = widgetHandler.customCommands local selectedUnits = Spring_GetSelectedUnits() local commandAdded = false for _, unitID in ipairs(selectedUnits) do selectionRankCmdDesc.actions[1] = AIUnits[unitID] or 0 local unitDefID = Spring_GetUnitDefID(unitID) local unitDef = UnitDefs[unitDefID] if not commandAdded and unitDef and unitDef.canMove and not unitDef.isBuilding and not unitDef.canFly then table.insert(customCommands, selectionRankCmdDesc) commandAdded = true end end end function widget:UnitCommand(unitID, unitDefID, unitTeam, cmdID, cmdParams, cmdOpts, cmdTag) if cmdOpts["internal"] == false then -- player commands if cmdID == CMD_MOVE then end if cmdID == CMD_FIGHT then end if cmdID == CMD_STOP then end end end function widget:CommandNotify(cmdID, cmdParams, options) if cmdID == CMD_AI_MODE then local selectedUnits = Spring.GetSelectedUnits() local stateCounts = {0, 0} -- Counts for OFF, ON for _, unitID in ipairs(selectedUnits) do local currentState = AIUnits[unitID] or 0 stateCounts[currentState + 1] = stateCounts[currentState + 1] + 1 end local maxState, maxCount = 0, 0 for state, count in ipairs(stateCounts) do if count > maxCount then maxState, maxCount = state - 1, count end end local nextState = (maxState + 1) % nbActions for _, unitID in ipairs(selectedUnits) do if nextState == 0 then AIUnits[unitID] = nil else AIUnits[unitID] = nextState end end selectionRankCmdDesc.actions[1] = nextState end return false end local function rotateVector(x, z, angle) local cosTheta = math_cos(angle) local sinTheta = math_sin(angle) return x * cosTheta - z * sinTheta, x * sinTheta + z * cosTheta end local function stepTowardsTarget(unitID, currentX, currentZ, targetX, targetZ, stepSize) local dx, dz = targetX - currentX, targetZ - currentZ local distance = math_hypot(dx, dz) if distance < stepSize then return targetX, targetZ else local stepRatio = stepSize / distance local stepX = currentX + dx * stepRatio local stepZ = currentZ + dz * stepRatio return stepX, stepZ end end function widget:UnitIdle(unitID, unitDefID, unitTeam) isUnitIdle[unitID] = true end function widget:UnitDestroyed(unitID, unitDefID, unitTeam) AIUnits[unitID] = nil end local function unitIsReady(unitID) -- Spring.Echo("Unit is ready to fight ! ", unitID) -- Spring_GiveOrderToUnit(unitID, CMD.MOVE, {0,0,0}, CMD_INTERNAL) -- AI commands should use CMD_INTERNAL option end function widget:GameFrame(n) if n % updateRate == 0 then for unitID in pairs(AIUnits) do unitIsReady(unitID) end end end local function drawUnitCircle(unitID) local x, y, z = Spring_GetUnitPosition(unitID) if x and y and z then if AIUnits[unitID] == 1 then gl.Color(0, 1, 0, 0.15) end gl.PushMatrix() gl.Translate(x, y, z) gl.BeginEnd(GL.TRIANGLE_FAN, function() local radius = 30 for i = 0, 360, 18 do local rad = math_rad(i) gl.Vertex(math_cos(rad) * radius, 0, math_sin(rad) * radius) end end) gl.PopMatrix() end end function widget:DrawWorldPreUnit() if Spring.IsGUIHidden() then return end for unitID in pairs(AIUnits) do drawUnitCircle(unitID) end end