Snippet #97786
on 2022/10/05 14:08:38 (UTC) by Anonymous as Lua
-
local widgetName = "Smarter Nanos" -- used also in Echo() definition
-
-
function widget:GetInfo()
-
return {
-
name = widgetName,
-
desc = "Highly responsive automatic management for static builders (and Funnelweb).\nReplaces 'Auto Patrol Nanos', 'Auto Patrol Nanos v2', and 'Smart Nanos' widgets -- disable them to use this one.\nFirst priority: to avoid energy stalling; second priority: to avoid metal overflow; third priority: repair own units.\nDoes not assist allied buildings, but repairs allied units if there is nothing better to do.\nTakes into account building priority.\nIf a player issues manual command(s) to a builder, it returns to automatic management upon finishing them.\nAdds two new command buttons to the command panel: 'Enable Automatic Management' (green 'AI' letters) and 'Disable Automatic Management' (red 'AI' letters).\nBy default all the builders are automatically managed until the auto-management is disabled by the latter command.",
-
author = "rollmops, using some ideas from 'Smart Nanos' by TheFatController and 'Auto Patrol Nanos v2' by trepan",
-
date = "Oct 2022",
-
license = "GNU GPL, v2 or later",
-
layer = 0,
-
handler = true, -- for adding custom commands into UI
-
enabled = true,
-
}
-
end
-
-
-
-- TL;DR: all the core logic is in widget:GameFrame()
-
-
--------------------------------------------------------------------------------
-
-- Speedups
-
--------------------------------------------------------------------------------
-
-
local spGetTeamResources = Spring.GetTeamResources
-
local spEcho = Spring.Echo
-
local spGetUnitPosition = Spring.GetUnitPosition
-
local spGetTeamUnits = Spring.GetTeamUnits
-
local spGetUnitDefID = Spring.GetUnitDefID
-
local spGetUnitsInCylinder = Spring.GetUnitsInCylinder
-
local spGetFeaturesInCylinder = Spring.GetFeaturesInCylinder
-
local spGetFeatureResources = Spring.GetFeatureResources
-
local spGiveOrderToUnit = Spring.GiveOrderToUnit
-
local spGetUnitCurrentCommand = Spring.GetUnitCurrentCommand
-
local spGetSelectedUnits = Spring.GetSelectedUnits
-
local spGetUnitHealth = Spring.GetUnitHealth
-
local spGetUnitAllyTeam = Spring.GetUnitAllyTeam
-
local spGetUnitRulesParam = Spring.GetUnitRulesParam
-
local spGetUnitFeatureSeparation = Spring.GetUnitFeatureSeparation
-
local spGetSpectatingState = Spring.GetSpectatingState
-
local spIsReplay = Spring.IsReplay
-
-
local CMD_RECLAIM = CMD.RECLAIM
-
local CMD_REPAIR = CMD.REPAIR
-
local CMD_STOP = CMD.STOP
-
-
local customCmds = VFS.Include("LuaRules/Configs/customcmds.lua")
-
local CMD_PRIORITY = customCmds.PRIORITY
-
-
local Game_maxUnits = Game.maxUnits
-
-
-
--------------------------------------------------------------------------------
-
-- Config
-
--------------------------------------------------------------------------------
-
-
local gameFramesInterval = 10 -- defines the frequency ( = 30 / gameFramesInterval Hz) of the management actions
-
-
local debug = false -- change to true to enable console/log messages (see Echo() command definition in Globals section)
-
-
local buildersNames = { "staticcon", "striderhub", "striderfunnelweb" } -- these types of builders will be managed
-
-
local energyDefs = { -- structures whose building will be prioritized in case of low energy
-
[ UnitDefNames.energywind.id ] = { cost = UnitDefNames.energywind.cost },
-
[ UnitDefNames.energysolar.id ] = { cost = UnitDefNames.energysolar.cost },
-
[ UnitDefNames.energygeo.id ] = { cost = UnitDefNames.energygeo.cost },
-
[ UnitDefNames.energyheavygeo.id ] = { cost = UnitDefNames.energyheavygeo.cost },
-
[ UnitDefNames.energyfusion.id ] = { cost = UnitDefNames.energyfusion.cost },
-
[ UnitDefNames.energysingu.id ] = { cost = UnitDefNames.energysingu.cost },
-
}
-
-
local metalDefs = { -- structures whose building will be prioritized in case of high metal
-
[ UnitDefNames.staticcon.id ] = { cost = UnitDefNames.staticcon.cost },
-
[ UnitDefNames.staticstorage.id ] = { cost = UnitDefNames.staticstorage.cost },
-
[ UnitDefNames.striderhub.id ] = { cost = UnitDefNames.striderhub.cost },
-
[ UnitDefNames.factoryamph.id ] = { cost = UnitDefNames.factoryamph.cost },
-
[ UnitDefNames.factorycloak.id ] = { cost = UnitDefNames.factorycloak.cost },
-
[ UnitDefNames.factorygunship.id ] = { cost = UnitDefNames.factorygunship.cost },
-
[ UnitDefNames.factoryhover.id ] = { cost = UnitDefNames.factoryhover.cost },
-
[ UnitDefNames.factoryjump.id ] = { cost = UnitDefNames.factoryjump.cost },
-
[ UnitDefNames.factoryplane.id ] = { cost = UnitDefNames.factoryplane.cost },
-
[ UnitDefNames.factoryshield.id ] = { cost = UnitDefNames.factoryshield.cost },
-
[ UnitDefNames.factoryship.id ] = { cost = UnitDefNames.factoryship.cost },
-
[ UnitDefNames.factoryspider.id ] = { cost = UnitDefNames.factoryspider.cost },
-
[ UnitDefNames.factorytank.id ] = { cost = UnitDefNames.factorytank.cost },
-
[ UnitDefNames.factoryveh.id ] = { cost = UnitDefNames.factoryveh.cost },
-
[ UnitDefNames.plateamph.id ] = { cost = UnitDefNames.plateamph.cost },
-
[ UnitDefNames.platecloak.id ] = { cost = UnitDefNames.platecloak.cost },
-
[ UnitDefNames.plategunship.id ] = { cost = UnitDefNames.plategunship.cost },
-
[ UnitDefNames.platehover.id ] = { cost = UnitDefNames.platehover.cost },
-
[ UnitDefNames.platejump.id ] = { cost = UnitDefNames.platejump.cost },
-
[ UnitDefNames.plateplane.id ] = { cost = UnitDefNames.plateplane.cost },
-
[ UnitDefNames.plateshield.id ] = { cost = UnitDefNames.plateshield.cost },
-
[ UnitDefNames.plateship.id ] = { cost = UnitDefNames.plateship.cost },
-
[ UnitDefNames.platespider.id ] = { cost = UnitDefNames.platespider.cost },
-
[ UnitDefNames.platetank.id ] = { cost = UnitDefNames.platetank.cost },
-
[ UnitDefNames.plateveh.id ] = { cost = UnitDefNames.plateveh.cost },
-
} -- This list was generated with the help of the following one-liner:
-
-- Zero-K-master/units$ for f in $(ls | grep -P "(factory|plate)" | grep -oP '\w+(?=\.lua)'); do echo -e "\t[ UnitDefNames.$f.id\t] = { cost = UnitDefNames.$f.cost\t},"; done
-
-
-- define two custom commands ("enable/disable auto-management") to be added to each builder's command list
-
-
-- each command should have its number. for custom commands as these, put some random numbers, but not already taken in https://springrts.com/wiki/Lua_CMDs or in LuaRules/Configs/customcmds.lua
-
local CMD_ENABLE_MANAGE_BUILDER = 18247
-
local CMD_DISABLE_MANAGE_BUILDER = 18248
-
-
-- define the two commands ( will be applied to selected builders by widget:CommandsChanged )
-
local cmdEnableManageBuilder = {
-
id = CMD_ENABLE_MANAGE_BUILDER,
-
type = CMDTYPE.ICON, -- expect 0 parameters in return
-
tooltip = 'Enable Automatic Management (Smarter Nanos widget)',
-
action = 'smarter_nanos_on', -- "it can be binded to a key (eg: /bind f fight, will activate FIGHT when f is pressed" <-- LuaRules/Configs/customCmdTypes.lua
-
params = {},
-
texture = 'LuaUI/Images/commands/states/ai_on.png', -- green 'AI' letters
-
}
-
-
local cmdDisableManageBuilder = {
-
id = CMD_DISABLE_MANAGE_BUILDER,
-
type = CMDTYPE.ICON,
-
tooltip = 'Disable Automatic Management (Smarter Nanos widget)',
-
action = 'smarter_nanos_off',
-
params = {},
-
texture = 'LuaUI/Images/commands/states/ai_off.png', -- red 'AI' letters
-
}
-
-
--------------------------------------------------------------------------------
-
-- Constants
-
--------------------------------------------------------------------------------
-
-
local myTeamID = Spring.GetMyTeamID()
-
local myAllyTeamID = Spring.GetMyAllyTeamID()
-
-
VFS.Include("LuaRules/Configs/constants.lua") -- for HIDDEN_STORAGE which is 10,000 and should be subtracted from metalStorage and energyStorage provided by spGetTeamResources() to acquire the actual storage value.
-
-
--------------------------------------------------------------------------------
-
-- Globals
-
--------------------------------------------------------------------------------
-
-
local buildersDefID = {} -- populated by the 'for' loop below, using buildersNames defined in Config section
-
-
for _,n in pairs( buildersNames ) do
-
local defID = UnitDefNames[n]
-
buildersDefID[ defID.id ] = { name = defID.humanName, range = defID.buildDistance, mobile = not defID.isImmobile }
-
end
-
-
-- following are set and used in widget:GameFrame
-
local energyState -- current energy state, can be "low" or "fine"
-
local metalState -- current metal state, can be "low", "medium", or "high"
-
local lastEnergyState = "fine" -- what was the previous energy state
-
local lastMetalState = "high" -- what was the previous metal state
-
-
local function Echo(...)
-
-- if 'debug' (defined in Config section) is true,
-
-- accepts any number of arguments, concatenates them to a space-delimited string, then spEcho() it.
-
-
if not debug then return end
-
-
local msg = widgetName..":"
-
for _, s in pairs{...} do
-
msg = msg .. " " .. tostring(s)
-
end
-
spEcho( msg )
-
end
-
-
-
--------------------------------------------------------------------------------
-
-- Builder Class and its Methods
-
--------------------------------------------------------------------------------
-
-
local builders = {} -- holds builders' objects
-
-
local builderClass = { -- each builder gets its own object derived from this class
-
id,
-
defid,
-
name,
-
range,
-
mobile, -- Boolean
-
pos = {},
-
managed, -- Boolean, whether the builder is under automated management;
-
-- True by default, can be changed by the custom commands which this widget adds to builders' command panel.
-
manualCommand, -- Boolean, whether the builder is currently doing manual-issued command(s),
-
-- upon finishing which, will return to automated management.
-
myUnitsInRange,
-
currentCmdID,
-
currentCmdParam1,
-
}
-
-
function builderClass:New( unitID, unitDefID ) -- the second argument is optional
-
-
local o = {}
-
-
setmetatable(o, self)
-
self.__index = self
-
-
o.id = unitID
-
o.defid = unitDefID or spGetUnitDefID( unitID )
-
o.name = buildersDefID[ o.defid ].name
-
o.range = buildersDefID[ o.defid ].range
-
o.mobile = buildersDefID[ o.defid ].mobile
-
-
local x,y,z = spGetUnitPosition( unitID )
-
o.pos = { x = x, y = y, z = z }
-
-
spGiveOrderToUnit( unitID, CMD_PRIORITY, 1, 0 ) -- set building priority to "normal"
-
o.managed = true
-
o.manualCommand = false
-
-
Echo( "added:", o.id, o.name, "mobile:", o.mobile, "range =", o.range, "located at", o.pos.x, o.pos.y, o.pos.z )
-
-
return o
-
end
-
-
function builderClass:Stop()
-
spGiveOrderToUnit( self.id, CMD_STOP, 0, 0 )
-
end
-
-
-- each following builderClass function returns "true" if is resulted in an order given to a builder, "false" otherwise.
-
-- in the latter case, another function (with lower priority) will be called (see widget:GameFrame).
-
-
-- there are three general functions: ReclaimFeatures(condition), AssistBuilding(arg),
-
-- and RepairUnits(units); then there are quite a few specific functions which provide a specific task by supplying
-
-- a relevant argument to one of the general functions, for example: RepairOwnUnits() calls RepairUnits(myUnitsInRange)
-
-
function builderClass:ReclaimFeatures( condition )
-
-- "condition" is a boolean function of feature's reclaimable energy and metal, defining what to reclaim (e.g., "only metal", "prefer energy" etc.)
-
-
local featuresInRange = spGetFeaturesInCylinder( self.pos.x, self.pos.z, self.range )
-
-- this functions returns also features that only their edge is in range, but their center is out of range, so they cannot be reclaimed. Hence additional checkup is needed (see the "if" statement below).
-
-- seems that the similar GetUnitsInCylinder() doesn't suffer from this issue.
-
-
if not featuresInRange then return false end
-
-
for _, featureID in pairs( featuresInRange ) do
-
-
local RemainingMetal, _, RemainingEnergy = spGetFeatureResources( featureID )
-
-
local target = featureID + Game_maxUnits -- convert featureID to absoluteID for GiveOrderToUnit()
-
-
-- check that:
-
-- (1) the feature meets the condition requirement
-
-- (2) the feature is actually in builder's range (see comment about spGetFeaturesInCylinder above)
-
-- features also have "reclaimable" boolean value, but seems that there is no need to check it
-
if condition( RemainingEnergy, RemainingMetal ) and
-
spGetUnitFeatureSeparation( self.id, featureID) < self.range then
-
-
-- give order to reclaim unless the builder is already reclaiming this feature
-
if not ( self.currentCmdID and self.currentCmdID == CMD_RECLAIM and self.currentCmdParam1 and self.currentCmdParam1 == target ) then
-
spGiveOrderToUnit( self.id, CMD_RECLAIM, target, 0 )
-
end
-
-
return true
-
end
-
end
-
return false
-
end
-
-
function builderClass:ReclaimEnergyOnly()
-
Echo( self.id, ": ReclaimEnergyOnly" )
-
return self:ReclaimFeatures( function( E, M ) return E > 1 and M < 1 end )
-
-- some features have some weird very low values of E or M (e.g. on Mescaline, San Pedro mushrooms have 0.001M),
-
-- so always compare to 1, not to 0.
-
end
-
-
function builderClass:ReclaimEnergyMainly()
-
Echo( self.id, ": ReclaimEnergyMainly" )
-
return self:ReclaimFeatures( function( E, M ) return E > M end )
-
end
-
-
function builderClass:ReclaimMetal()
-
Echo( self.id, ": ReclaimMetal" )
-
return self:ReclaimFeatures( function( E, M ) return M > 1 and E < 1 end )
-
end
-
-
function builderClass:ReclaimAny()
-
Echo( self.id, ": ReclaimAny" )
-
return self:ReclaimFeatures( function( E, M ) return M > 1 or E > 1 end )
-
end
-
-
function builderClass:AssistBuilding( arg )
-
-- assist is always for own units only.
-
-- if no list of DefIDs is provided, assist any own unit, otherwise only the specified kinds (DefIDs).
-
-- assist only units with building priority no less than minPriority ("normal" if the argument isn't provided).
-
-- choose a unit to assist by its building priority, then by how much metal remains to spend (see table.sort() below).
-
-- since both arguments are optional, the function receives a table of named arguments ( DefIds and minPriority ).
-
-
if not self.myUnitsInRange then return false end
-
-
minPriority = arg.minPriority or 1 -- (0-low, 1-normal, 2-high)
-
-
local unitsToAssist = {} -- this table will be populated with all relevant units, then sorted to find the best.
-
-
for _, unitID in pairs( self.myUnitsInRange ) do
-
-
local unitDefID = spGetUnitDefID( unitID )
-
-
local _,_,_,_,buildProgress = spGetUnitHealth( unitID )
-
local buildPriority = spGetUnitRulesParam(unitID, "buildpriority") or 1 -- (0-low, 1-normal, 2-high)
-
-
-- check that:
-
-- (1) unit is not finished (needs building assist)
-
-- (2) list of DefIds wasn't provided (assist any), or unit's DefId is in the list
-
-- (3) buildPriority is equal or higher to minPriority
-
if buildProgress and buildProgress < 1 and
-
( not arg.DefIDs or arg.DefIDs[ unitDefID ] ) and
-
buildPriority >= minPriority then
-
-
local unitCost = arg.DefIDs and arg.DefIDs[unitDefID].cost or UnitDefs[unitDefID].cost
-
local metalToInvest = unitCost * ( 1 - buildProgress )
-
unitsToAssist[ #unitsToAssist + 1 ] = { unitID = unitID, metalToInvest = metalToInvest, buildPriority = buildPriority }
-
-
end
-
end
-
-
if #unitsToAssist == 0 then return false end
-
-
table.sort( unitsToAssist, function(a,b)
-
-- sort first by building priority (the higher, the better), then by metalToinvest (the less, the better)
-
return a.buildPriority ~= b.buildPriority and a.buildPriority > b.buildPriority or a.metalToInvest < b.metalToInvest end )
-
-
local bestUnitToAssist = unitsToAssist[1].unitID
-
if not ( self.currentCmdID and self.currentCmdID == CMD_REPAIR and self.currentCmdParam1 and self.currentCmdParam1 == bestUnitToAssist ) then
-
spGiveOrderToUnit( self.id, CMD_REPAIR, bestUnitToAssist, 0 )
-
end
-
return true
-
end
-
-
function builderClass:AssistEnergyStructures()
-
Echo( self.id, ": AssistEnergyStructures" )
-
return self:AssistBuilding{ DefIDs = energyDefs }
-
end
-
-
function builderClass:AssistMetalSpending()
-
Echo( self.id, ": AssistMetalSpending" )
-
return self:AssistBuilding{ DefIDs = metalDefs }
-
end
-
-
function builderClass:AssistAnyNormal()
-
Echo( self.id, ": AssistAnyNormal" )
-
return self:AssistBuilding{}
-
end
-
-
function builderClass:AssistAnyLow()
-
Echo( self.id, ": AssistAnyLow" )
-
return self:AssistBuilding{ minPriority = 0 }
-
end
-
-
function builderClass:RepairUnits( units )
-
-- repair either own or allied units, depending on the argument (units list).
-
-- choose the unit with the least HP deficiency (see table.sort() below)
-
-
if not units then return false end
-
-
local unitsToRepair = {} -- this table will be populated with all relevant units, then sorted to find the best.
-
-
for _, unitID in pairs( units ) do
-
local currentHP, maxHP, _, _, buildProgress = spGetUnitHealth( unitID )
-
-
-- check that:
-
-- (1) unit is finished, because we want to repair, not to build
-
-- (2) unit needs repair
-
-- (3) not trying to repair itself
-
if buildProgress == 1 and currentHP < maxHP and unitID ~= self.id then
-
unitsToRepair[ #unitsToRepair + 1 ] = { unitID = unitID, HP_deficiency = maxHP - currentHP }
-
end
-
end
-
if #unitsToRepair == 0 then return false end
-
-
table.sort( unitsToRepair, function(a,b) return a.HP_deficiency < b.HP_deficiency end )
-
local fastestRepairUnitID = unitsToRepair[1].unitID
-
-
if not ( self.currentCmdID and self.currentCmdID == CMD_REPAIR and self.currentCmdParam1 and self.currentCmdParam1 == fastestRepairUnitID ) then
-
spGiveOrderToUnit( self.id, CMD_REPAIR, fastestRepairUnitID, 0 )
-
end
-
return true
-
end
-
-
function builderClass:RepairOwnUnits()
-
Echo( self.id, ": RepairOwnUnits" )
-
return self:RepairUnits( self.myUnitsInRange )
-
end
-
-
function builderClass:RepairAllyUnits()
-
Echo( self.id, ": RepairAllyUnits" )
-
local unitsInRange = spGetUnitsInCylinder( self.pos.x, self.pos.z, self.range )
-
if not unitsInRange then return false end
-
local allyUnitsInRange = {}
-
for _, unitID in pairs( unitsInRange ) do
-
if spGetUnitAllyTeam( unitID ) == myAllyTeamID then
-
allyUnitsInRange[ #allyUnitsInRange + 1 ] = unitID
-
end
-
end
-
return self:RepairUnits( allyUnitsInRange )
-
end
-
-
-
--------------------------------------------------------------------------------
-
-- Callins
-
--------------------------------------------------------------------------------
-
-
-- all the core logic is in widget:GameFrame
-
-
function widget:GameFrame(n)
-
-
if n % gameFramesInterval ~= 1 then return end
-
-
local metal, metalStorage = spGetTeamResources(myTeamID, "metal")
-
-
metalStorage = metalStorage - HIDDEN_STORAGE -- see explanation in Constants section
-
-
local energy, energyStorage = spGetTeamResources(myTeamID, "energy")
-
-
energyStorage = energyStorage - HIDDEN_STORAGE -- see explanation in Constants section
-
-
-- lastEnergyState adds some hysteresis to avoid fast switching back and forth around Energy States boundary;
-
-- in other words, moving from "low" to "fine" energy state requires more energy than moving from "fine" to "low",
-
energyState =
-
( energy < 0.2 * energyStorage or lastEnergyState == "low" and energy < 0.3 * energyStorage ) and "low"
-
or "fine"
-
-
-- lastMetalState plays a similar role to that of lastEnergyState above
-
metalState =
-
( metal < 0.3 * metalStorage or lastMetalState == "low" and metal < 0.4 * metalStorage ) and "low"
-
or
-
( metal > 0.75 * metalStorage or lastMetalState == "high" and metal > 0.65 * metalStorage ) and "high"
-
or "medium"
-
-
lastEnergyState = energyState
-
lastMetalState = metalState
-
-
Echo( "Metal State:", metalState, "Energy State:", energyState )
-
-
for _, builder in pairs( builders ) do
-
-
if builder.managed then -- if automatic management was disabled, there is nothing to do
-
-
local cmdID, _, _, cmdParam1 = spGetUnitCurrentCommand( builder.id )
-
-
Echo( builder.id, "cmd:", CMD[cmdID] or cmdID or "none", "managed:", builder.managed, "manualCommand:", builder.manualCommand )
-
-
if not builder.manualCommand then
-
-- if the builder is performing manual-issued command(s), leave it alone
-
-- until it becomes idle (see 'elseif' clause of this level below)
-
-
builder.currentCmdID = cmdID
-
builder.currentCmdParam1 = cmdParam1
-
-
if builder.mobile then
-
local x,y,z = spGetUnitPosition( builder.id )
-
builder.pos = { x = x, y = y, z = z }
-
end
-
-
builder.myUnitsInRange = spGetUnitsInCylinder( builder.pos.x, builder.pos.z, builder.range, myTeamID )
-
-
if energyState == "low" then dummy =
-
-- 'dummy = ...' is used to try each task (they are organized in priority-descending order) in turn;
-
-- if a task was successful (an order was given to the builder), it returns 'true',
-
-- so the attempts will stop here;
-
-- if a task was unsuccessful (e.g., builder:ReclaimEnergyOnly() was called, but there are no
-
-- pure-energy features in the builder's range), the task will return 'false', then
-
-- the next function will be called.
-
-- if all tasks fail, the Stop() command will be issued (always last in the chain).
-
-
builder:ReclaimEnergyOnly() or
-
metalState == "low" and builder:ReclaimEnergyMainly() or
-
builder:AssistEnergyStructures() or
-
builder:ReclaimEnergyMainly() or
-
builder:Stop()
-
-
elseif metalState == "high" then dummy =
-
-
builder:AssistMetalSpending() or
-
builder:AssistAnyNormal() or
-
builder:AssistAnyLow() or
-
builder:RepairOwnUnits() or
-
builder:RepairAllyUnits() or
-
builder:Stop()
-
else dummy =
-
builder:RepairOwnUnits() or
-
builder:ReclaimMetal() or
-
builder:ReclaimAny() or
-
builder:RepairAllyUnits() or
-
builder:AssistAnyNormal() or
-
builder:Stop()
-
end
-
-
elseif not cmdID then -- builder was on manual-issued command(s) but idle now, therefore
-
-- returns to automated management.
-
-- in this gameFrame set its priority to "normal";
-
-- a building order will be issued in the next checked frame
-
-- to avoid interference.
-
-
builder.manualCommand = false
-
spGiveOrderToUnit( builder.id, CMD_PRIORITY, 1, 0 )
-
end
-
end
-
end
-
end
-
-
function widget:CommandNotify(cmdID, params, options)
-
-- used to check if one of the two custom commands ("enable/disable auto-management") was issued,
-
-- or, in case of any other command, switch the builder to "manual" state, since
-
-- orders issued by the widget itself will not be caught by this callin
-
-
Echo("CommandNotify:", CMD[cmdID] or cmdID )
-
-
selectedUnits = spGetSelectedUnits()
-
if not selectedUnits then return end
-
-
local returnValue -- return after "for" loop; "return true" discards the issued command
-
-
for _, unitID in pairs( selectedUnits ) do
-
if builders[ unitID ] then
-
if cmdID == CMD_DISABLE_MANAGE_BUILDER then
-
Echo( "management disabled for", unitID )
-
builders[ unitID ].managed = false
-
returnValue = true
-
elseif cmdID == CMD_ENABLE_MANAGE_BUILDER then
-
Echo( "management enabled for", unitID )
-
spGiveOrderToUnit( unitID, CMD_PRIORITY, 1, 0 )
-
builders[ unitID ].managed = true
-
returnValue = true
-
else
-
builders[ unitID ].manualCommand = true
-
returnValue = false
-
end
-
end
-
end
-
-
return returnValue
-
end
-
-
function widget:CommandsChanged()
-
-- Called when the command descriptions (the "commands" panel) changed, i.e. when selecting or deselecting units of different types, to add the two custom commands, if any builder was selected.
-
-
selectedUnits = spGetSelectedUnits()
-
if not selectedUnits then return end
-
-
for _, unitID in pairs( selectedUnits ) do
-
if builders[ unitID ] then
-
local customCommands = widgetHandler.customCommands
-
customCommands[#customCommands+1] = cmdEnableManageBuilder
-
customCommands[#customCommands+1] = cmdDisableManageBuilder
-
return
-
end
-
end
-
end
-
-
-- remaining is the usual boilerplate
-
-
function widget:UnitFinished( unitID, unitDefID, unitTeam )
-
if unitTeam == myTeamID and buildersDefID[ unitDefID ] and not builders[ unitID ] then
-
builders[ unitID ] = builderClass:New( unitID, unitDefID )
-
end
-
end
-
-
function widget:UnitGiven(unitID, unitDefID, unitTeam)
-
local _,_,_,_,buildProgress = spGetUnitHealth ( unitID )
-
if buildProgress and buildProgress == 1 then
-
widget:UnitFinished(unitID, unitDefID, unitTeam)
-
end
-
end
-
-
function widget:UnitDestroyed(unitID)
-
builders[ unitID ] = nil
-
end
-
-
function widget:UnitTaken(unitID)
-
widget:UnitDestroyed(unitID)
-
end
-
-
function widget:Initialize()
-
if spGetSpectatingState() or spIsReplay() then
-
widgetHandler:RemoveWidget(widget)
-
end
-
local myUnits = spGetTeamUnits( myTeamID )
-
if myUnits then
-
for _, unitID in pairs( myUnits ) do
-
widget:UnitGiven(unitID, spGetUnitDefID( unitID ), myTeamID)
-
end
-
end
-
end
-
-
function widget:PlayerChanged (playerID)
-
if spGetSpectatingState() then
-
widgetHandler:RemoveWidget(widget)
-
end
-
end
Recent Snippets
- #157071 by Anonymous (706 days ago)
- #156876 by Anonymous (706 days ago)
- #154030 by Anonymous (710 days ago)
- #140524 by Anonymous (730 days ago)
- #137907 by rotense (733 days ago)
- #131892 by Anonymous (740 days ago)