Snippet #124806
on 2022/11/02 13:10:52 (UTC) by Anonymous as Lua
-
local widgetName = "Next Gen Bombers"
-
-
function widget:GetInfo()
-
return {
-
name = widgetName,
-
desc = "Adds several features to Likho, Raven, Phoenix and Thunderbird:\n1. If an attacked unit becomes cloaked, bombers will hit its presumptive location (accounting for last seen position and velocity)\n2. Submerged units can be targeted, the water surface above the target will be hit, accounting for units speed. Raven may hit fast submerged units like Seawolf.\n3. If a targeted unit got destroyed by something else, and there was not a queued command, the bomber will return to air factory or airpad, to avoid dangerous circling at the frontline.\n4. In contrast to 'Smart Bombers' widget (which should be disabled to use this one), this widget not only temporarily turns on the 'Free Fire' state when Attack Move is issued, but also discards the Attack Move command after firing. Thus the Attack Move becomes one-time action rather than a kind of a state.",
-
author = "rollmops",
-
date = "2022",
-
license = "GNU GPL, v2 or later",
-
layer = 0,
-
enabled = true -- loaded by default
-
}
-
end
-
-
--------------------------------------------------------------------------------
-
-- Speedups
-
--------------------------------------------------------------------------------
-
-
local sqrt = math.sqrt
-
-
local spGetUnitPosition = Spring.GetUnitPosition
-
local spGiveOrderToUnit = Spring.GiveOrderToUnit
-
local spGetTeamUnits = Spring.GetTeamUnits
-
local spGetUnitDefID = Spring.GetUnitDefID
-
local spEcho = Spring.Echo
-
local spGetUnitCommands = Spring.GetUnitCommands
-
local spGetUnitVelocity = Spring.GetUnitVelocity
-
local spGetUnitHealth = Spring.GetUnitHealth
-
local spGetSpecState = Spring.GetSpectatingState
-
local spIsUnitInLos = Spring.IsUnitInLos
-
local spGetUnitTeam = Spring.GetUnitTeam
-
local spValidUnitID = Spring.ValidUnitID
-
local spGetGameFrame = Spring.GetGameFrame
-
-
local CMD_FORCE_FIRE = CMD.ATTACK -- same number (20) as LOOPBACKATTACK
-
local CMD_REMOVE = CMD.REMOVE
-
local CMD_INSERT = CMD.INSERT
-
local CMD_OPT_ALT = CMD.OPT_ALT
-
local CMD_STOP = CMD.STOP
-
local CMD_OPT_INTERNAL = CMD.OPT_INTERNAL
-
local CMD_OPT_SHIFT = CMD.OPT_SHIFT
-
local CMD_ATTACK_MOVE = CMD.FIGHT
-
local CMD_FIRE_STATE = CMD.FIRE_STATE
-
local CMD_FIRESTATE_HOLDFIRE = CMD.FIRESTATE_HOLDFIRE
-
local CMD_FIRESTATE_FIREATWILL = CMD.FIRESTATE_FIREATWILL
-
-
local customCmds = VFS.Include("LuaRules/Configs/customcmds.lua")
-
local CMD_REARM = customCmds.REARM -- 33410
-
local CMD_RAW_MOVE = customCmds.RAW_MOVE -- 31109
-
-
-
--------------------------------------------------------------------------------
-
-- Config
-
--------------------------------------------------------------------------------
-
-
local gameFramesInterval = 8
-
-
local debug = false -- see Echo()
-
-
local bombersDefID = { -- The four managed bombers types. Projectile speed adjusted manually.
-
[UnitDefNames.bomberheavy.id ] = {name = UnitDefNames.bomberheavy.humanName, projectileSpeed = 12.5, },
-
[UnitDefNames.bomberdisarm.id] = {name = UnitDefNames.bomberdisarm.humanName, projectileSpeed = 100,},
-
[UnitDefNames.bomberriot.id ] = {name = UnitDefNames.bomberriot.humanName, projectileSpeed = 9, },
-
[UnitDefNames.bomberprec.id ] = {name = UnitDefNames.bomberprec.humanName, projectileSpeed = 5.5,},
-
}
-
-
-
--------------------------------------------------------------------------------
-
-- Constants
-
--------------------------------------------------------------------------------
-
-
local myTeamID = Spring.GetMyTeamID()
-
local myAllyTeamID = Spring.GetMyAllyTeamID()
-
-
local airpadDefID = UnitDefNames.staticrearm.id
-
local airFactoryDefID = UnitDefNames.factoryplane.id
-
local airPalteDefID = UnitDefNames.plateplane.id
-
-
--------------------------------------------------------------------------------
-
-- Globals
-
--------------------------------------------------------------------------------
-
-
local bombers = {} -- Bomber Class Objects, per bomber
-
-
local targets = {}
-
-- Targeted units. Several bombers could have the same target, so targets' data resides in this table:
-
-- [unitID] -> { inLos=bool, inRadar=bool, inWater=bool, lastKnownPos={x,y,z}, vel={x,y,z,speed}, lastSeen=gameFrame }
-
-- inLos updated by UnitEnteredLos/UnitLeftLos
-
-- inRadar updated by UnitEnteredRadar/UnitLeftRadar
-
-- inWater, lastKnownPos, vel(ocity), and lastSeen updated repeatedly in GameFrame() while target is in LoS,
-
-- that's because, while position can be obtained in UnitLeftLos(), velocity cannot, so
-
-- for data consistency, all are obtained in GameFrame().
-
-
-
-- following are own only, not allied; used as a destination to return if the target gone and there are no queued commands.
-
local airpads = {} -- [unitID] -> {x,y,z} (position)
-
local airFacs = {} -- [unitID] -> {x,y,z}
-
local airPlates = {} -- [unitID] -> {x,y,z}
-
-
-
--------------------------------------------------------------------------------
-
-- Functions
-
--------------------------------------------------------------------------------
-
-
-
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 = "NGB:" -- original widget name is too long
-
--local msg = widgetName..":"
-
-
for _, s in pairs{...} do
-
msg = msg .. " " .. tostring(s)
-
end
-
spEcho(msg)
-
end
-
-
local function GetHumanName(unitID)
-
return spGetUnitDefID(unitID) and UnitDefs[spGetUnitDefID(unitID)] and UnitDefs[spGetUnitDefID(unitID)].humanName or "noname"
-
end
-
-
local function GetAimPosition(unitID)
-
local _,_,_,x,y,z = spGetUnitPosition(unitID, false, true) -- last 2 args: bool return midPos , bool return aimPos
-
return {x=x, y=y, z=z}
-
end
-
-
local function DiscardOrphanedTargets()
-
-- remove target objects which no bomber has them anymore as the target (current or queued)
-
-
for targetID in pairs(targets) do
-
local orphaned = true
-
for _, bomber in pairs(bombers) do
-
if bomber.target == targetID then
-
orphaned = false
-
break
-
end
-
for _, cmd in pairs(bomber.queue) do
-
if cmd.target == targetID then
-
orphaned = false
-
break
-
end
-
end
-
end
-
if orphaned then
-
targets[targetID] = nil
-
--Echo("orphaned target discarded")
-
end
-
end
-
end
-
-
local function BombersHitTargetPosition(targetID, targetPos)
-
for _, bomber in pairs(bombers) do
-
if bomber.target == targetID then
-
bomber:HitTargetPosition({x=targetPos.x, y=targetPos.y, z=targetPos.z})
-
end
-
end
-
end
-
-
local function BombersHitTargetID(targetID)
-
for _, bomber in pairs(bombers) do
-
if bomber.target == targetID then
-
spGiveOrderToUnit(bomber.id, CMD_FORCE_FIRE, targetID, CMD_OPT_INTERNAL)
-
end
-
end
-
end
-
-
local function TargetIsGone(targetID)
-
-- called when target is destroyed by something else, or never been in LoS and left radar
-
--Echo("Target is gone", targetID)
-
for _, bomber in pairs(bombers) do
-
if bomber.target == targetID then
-
bomber.target = nil
-
spGiveOrderToUnit(bomber.id, CMD_REMOVE, CMD_FORCE_FIRE, CMD_OPT_ALT)
-
bomber:RestoreQueuedCmds(false) -- arg is needRearm
-
else
-
for k, cmd in pairs(bomber.queue) do
-
if cmd.target == targetID then
-
table.remove(bomber.queue, k)
-
-- shifts the queue up, in contrast to 'bomber.queue[k]=nil' which makes a hole.
-
end
-
end
-
end
-
end
-
targets[targetID] = nil
-
end
-
-
-- to debug "Bad command from..." messages in log; see AllowCommandParams in gadgets.lua which issues these msgs.
-
--local SIZE_LIMIT = 10^8
-
--local function CheckCommandParams(cmdParams)
-
--for i = 1, #cmdParams do
-
--if (not cmdParams[i]) or cmdParams[i] ~= cmdParams[i] or cmdParams[i] < -SIZE_LIMIT or cmdParams[i] > SIZE_LIMIT then
-
--Echo("Bad command: i=",i,"param=",cmdParams[i])
-
--end
-
--end
-
--end
-
--local function DebugGiveOrder(...)
-
--CheckCommandParams(select(3, ...))
-
--spGiveOrderToUnit(...)
-
--end
-
-
-
-
--------------------------------------------------------------------------------
-
-- Bomber Class and its Methods
-
--------------------------------------------------------------------------------
-
-
local bomberClass = {id, defid, name, weaponSpeed, attackMove, pos = {}, queue = {}, target}
-
-
function bomberClass:New(unitID, unitDefID)
-
local o = {}
-
setmetatable(o, self)
-
self.__index = self
-
o.id = unitID
-
o.defid = unitDefID or spGetUnitDefID(unitID)
-
o.name = bombersDefID[o.defid].name
-
o.weaponSpeed = bombersDefID[o.defid].projectileSpeed
-
o.attackMove = false
-
o.pos = {}
-
o.queue = {} -- Keeps commands added with SHIFT. [n] -> {cmdID, params={}, target=unitID/nil}
-
-- Commands added by UnitCommand, removed (restored) by RestoreQueuedCmds
-
return o
-
--Echo("added:", o.id, o.name)
-
end
-
-
function bomberClass:HitTargetPosition(targetPos)
-
-
-- For cloaked and submerged targets, hit their position with "Force Fire Point".
-
-- Approximating ballistic trajectory with constant speed trajectory, see:
-
-- https://playtechs.blogspot.com/2007/04/aiming-at-moving-target.html
-
-
-
if not (targetPos and targets[self.target].vel) then return end
-
-
x, y, z = spGetUnitPosition(self.id)
-
-
-- relative position of the target (in relation to bomber's position)
-
local px = targetPos.x - x
-
local py = targetPos.y - y
-
local pz = targetPos.z - z
-
-
-- original algorithm uses relative velocity,
-
-- but it seems bombers' weapons speed doesn't depend on bomber's speed,
-
-- hence using target's absolute velocity.
-
local vx = targets[self.target].vel.x
-
local vy = targets[self.target].vel.y
-
local vz = targets[self.target].vel.z
-
-
-
local a = self.weaponSpeed * self.weaponSpeed - targets[self.target].vel.speed * targets[self.target].vel.speed
-
if a < 0.01 then return end -- should not happen as weapon speed > target speed
-
local b = px * vx + py * vy + pz * vz
-
local c = px * px + py * py + pz * pz
-
local d = b * b + a * c
-
if d>= 0 then
-
local t = (b + sqrt(d)) / a
-
--local t2 = (b - sqrt(d)) / a
-
--if t2 > 0 then Echo("T2 POSITIVE: t=", t, "t2 = ", t2) end -- should not happen
-
local aimX = targetPos.x + vx * t
-
local aimY = targetPos.y + vy * t
-
local aimZ = targetPos.z + vz * t
-
spGiveOrderToUnit(self.id, CMD_FORCE_FIRE, {aimX, aimY, aimZ }, CMD_OPT_INTERNAL)
-
else
-
--Echo ("d is negative") -- should not happen
-
end
-
end
-
-
function bomberClass:RestoreQueuedCmds(needRearm)
-
-- Since for submerged and cloaked units the widget repeatedly issues Force Fire To Point command,
-
-- the player's commands added with SHIFT are kept in bomber.queue and are restored when the target is hit or destroyed.
-
-- Queue is FIFO (commands are pushed to the end of it in UnitCommand, here they are popped from the beginning).
-
-- Arg needRearm is bool, to know whether rearm is needed, so to restore only move commands or fight commands too.
-
-
--Echo("Restoring queue for",self.id)
-
-
local cmd = table.remove(self.queue, 1)
-
-
if not cmd then
-
--Echo("no cmd in queue")
-
if not needRearm then -- if needs rearm, it will get rearm command in GameFrame
-
--Echo("Fly to Base", self.id)
-
local padPos = airpads[next(airpads)] or airFacs[next(airFacs)] or airPlates[next(airPlates)]
-
if padPos then
-
spGiveOrderToUnit(self.id, CMD_RAW_MOVE, padPos, CMD_OPT_INTERNAL)
-
end
-
end
-
return
-
end
-
-
if cmd.target then
-
-
if needRearm then
-
-- can't fire, need rearm; try to find non-FFU cmd such as Move
-
DiscardOrphanedTargets() -- may be only this bomber has cmd.target
-
self:RestoreQueuedCmds(needRearm) -- try next command as first (recursion!)
-
return
-
end
-
-
self.target = cmd.target
-
-
--Echo("queued target restored:", GetHumanName(cmd.target), cmd.target, "for", self.id)
-
if targets[cmd.target].inRadar then
-
local pos = GetAimPosition(cmd.target)
-
if pos and pos.y and pos.y >= 0 then -- if y<0 (submerged), will get commands from GameFrame
-
spGiveOrderToUnit(self.id, CMD_FORCE_FIRE, cmd.target, CMD_OPT_INTERNAL)
-
end
-
elseif targets[cmd.target].lastKnownPos then
-
self:HitTargetPosition(targets[cmd.target].lastKnownPos) -- will continue to get adjusted commands from GameFrame()
-
else
-
TargetIsGone(unitID) -- not in radar and we have no data about it
-
self:RestoreQueuedCmds(needRearm) -- try next command as first (recursion!)
-
end
-
return -- if queued cmds are MOVE, can continue to give orders, but for Force Fire Unit need to break, keeping the rest of the queue
-
else
-
--Echo("First cmd in queue restored",cmd.cmdID,"for",self.id)
-
spGiveOrderToUnit(self.id, cmd.cmdID, cmd.params, CMD_OPT_INTERNAL)
-
end
-
-
while #self.queue > 0 do -- rest of the commands are added with shift
-
-
local cmd = table.remove(self.queue, 1)
-
if cmd.target then
-
self.queue = nil -- Force Fire Unit should not be queued after Move command(s)
-
return
-
else
-
--Echo("Next cmd in queue restored",cmd.cmdID,"for",self.id)
-
spGiveOrderToUnit(self.id, cmd.cmdID, cmd.params, CMD_OPT_SHIFT+CMD_OPT_INTERNAL)
-
end
-
end
-
end
-
-
-
--------------------------------------------------------------------------------
-
-- Callins
-
--------------------------------------------------------------------------------
-
-
-
function widget:UnitCommand(unitID, unitDefID, unitTeam, cmdID, cmdParams, cmdOpts, cmdTag)
-
-
if not bombers[unitID] then return end
-
--Echo("UC:",CMD[cmdID],cmdID,"p1=",cmdParams[1],"p2=",cmdParams[2],"p3=",cmdParams[3],"p4=",cmdParams[4],"shift=",cmdOpts.shift,"internal=",cmdOpts.internal,"alt=",cmdOpts.alt)
-
-
-- Catching player-issued commands.
-
-- Placed here and not in CommandNotify as to also catch commands issued as a result of ForceFire+drag
-
if not (cmdID==CMD_INSERT or cmdID==CMD_REMOVE or cmdID==CMD_FIRE_STATE or cmdOpts.internal) then
-
-
-- if targetID is not nil, then targetID is unitID targeted by the bomber (may be a radar point as well)
-
local targetID = cmdID==CMD_FORCE_FIRE and cmdParams and #cmdParams==1 and spValidUnitID(cmdParams[1]) and cmdParams[1]
-
-
if targetID and not targets[targetID] then
-
targets[targetID] = {inRadar = true, inLos = spIsUnitInLos(targetID, myAllyTeamID)}
-
--Echo(cmdOpts.shift and "queued" or "", "target added:", GetHumanName(targetID), cmdParams[1], "for", unitID)
-
end
-
-
if not cmdOpts.shift then
-
if cmdID~=CMD_FORCE_FIRE then
-
spGiveOrderToUnit(unitID, CMD_REMOVE, CMD_FORCE_FIRE, CMD_OPT_ALT)
-
--CMD_OPT_ALT means "use the parameters as commandIDs, not as tags"
-
end
-
bombers[unitID].target = targetID
-
bombers[unitID].queue = {}
-
DiscardOrphanedTargets()
-
else
-
--Echo("Queued cmd",cmdID,"added for",unitID)
-
bombers[unitID].queue[#bombers[unitID].queue+1] = {cmdID = cmdID, params = cmdParams, target = targetID}
-
end
-
-
-- similar functionality to "Smart Bombers" widget,
-
-- but also discards the Attack command after firing (see elseif REARM clause below)
-
if cmdID == CMD_ATTACK_MOVE then
-
--Echo("Set Fire At Will")
-
spGiveOrderToUnit(unitID, CMD_FIRE_STATE, CMD_FIRESTATE_FIREATWILL, CMD_OPT_INTERNAL)
-
bombers[unitID].attackMove = true
-
elseif bombers[unitID].attackMove then
-
spGiveOrderToUnit(unitID, CMD_FIRE_STATE, CMD_FIRESTATE_HOLDFIRE, CMD_OPT_INTERNAL)
-
spGiveOrderToUnit(unitID, CMD_REMOVE, CMD_ATTACK_MOVE, CMD_OPT_ALT)
-
bombers[unitID].attackMove = false
-
end
-
-
-- LuaRules/Gadgets/unit_bomber_command.lua inserts REARM command after a bomber fired, where:
-
-- cmdParams[3] is REARM options = CMD_OPT_SHIFT + CMD_OPT_INTERNAL;
-
-- cmdParams[4] is REARM params[1] = auto-chosen airpad (own or allied closest and having free slot AirPad/Factory or Reef).
-
-- REARM options are tested to distinguish gadget-inserted REARM from one that this block inserts, to avoid endless loop.
-
elseif cmdID == CMD_INSERT and cmdParams[4] and
-
cmdParams[2] and cmdParams[2] == CMD_REARM and
-
cmdParams[3] and cmdParams[3] == CMD_OPT_SHIFT + CMD_OPT_INTERNAL then
-
-
--Echo("AUTO-REARM INSERTED")
-
-
spGiveOrderToUnit(unitID, CMD_REMOVE, CMD_FORCE_FIRE, CMD_OPT_ALT)
-
bombers[unitID].target = nil
-
DiscardOrphanedTargets()
-
-
spGiveOrderToUnit(unitID, CMD_FIRE_STATE, CMD_FIRESTATE_HOLDFIRE, CMD_OPT_INTERNAL)
-
spGiveOrderToUnit(unitID, CMD_REMOVE, CMD_ATTACK_MOVE, CMD_OPT_ALT)
-
-
if bombers[unitID].queue[1] then
-
bombers[unitID]:RestoreQueuedCmds(true) -- arg is needRearm
-
-
-- move rearm command after restored commands
-
spGiveOrderToUnit(unitID, CMD_REMOVE, CMD_REARM, CMD_OPT_ALT)
-
spGiveOrderToUnit(unitID, CMD_INSERT, {-1, CMD_REARM, CMD_OPT_SHIFT, cmdParams[4]}, CMD_OPT_ALT)
-
end
-
end
-
end
-
-
function widget:UnitLeftLos(unitID, unitTeam, allyTeam, unitDefID)
-
-- For widgets, this one is called just before the unit leaves los,
-
-- so you can still get the position of a unit that left los,
-
-- but not the velocity. Hence, target's data obtained in GameFrame()
-
-- Btw, seems that it doesn't provide unitDefID, doesn't matter here.
-
-
if targets[unitID] then
-
--Echo("Target Left Los:", GetHumanName(unitID), unitID)
-
targets[unitID].inLos = false
-
end
-
end
-
-
function widget:UnitEnteredLos(unitID, unitTeam, allyTeam, unitDefID)
-
if targets[unitID] then
-
--Echo("Target Entered Los:", GetHumanName(unitID), unitID)
-
targets[unitID].inLos = true
-
end
-
end
-
-
function widget:UnitLeftRadar(unitID)
-
-- Also called when a unit leaves LOS without any radar coverage.
-
-- For widgets, this is called just after a unit leaves radar coverage,
-
-- so widgets cannot get the position of units that left their radar.
-
-
if targets[unitID] then
-
--Echo("Target Left Radar:", GetHumanName(unitID), unitID)
-
if targets[unitID].lastKnownPos then
-
targets[unitID].inRadar = false
-
BombersHitTargetPosition(unitID, targets[unitID].lastKnownPos) -- will continue to get adjusted commands from GameFrame()
-
else
-
TargetIsGone(unitID)
-
end
-
end
-
end
-
-
function widget:UnitEnteredRadar(unitID, unitTeam, allyTeam, unitDefID)
-
-- Also called when a unit enters LOS without any radar coverage.
-
-
if targets[unitID] then
-
--Echo("Target Entered Radar:", GetHumanName(unitID), unitID)
-
targets[unitID].inRadar = true
-
local pos = GetAimPosition(unitID)
-
if pos and pos.y and pos.y >= 0 then -- if y<0 (submerged), will get commands from GameFrame
-
BombersHitTargetID(unitID)
-
end
-
end
-
end
-
-
function widget:GameFrame(gameFrame)
-
if gameFrame % gameFramesInterval ~= gameFramesInterval - 1 then return end
-
-
if next(targets) == nil then return end
-
-
for targetID, target in pairs(targets) do
-
-
if target.inLos then
-
-
local vx,vy,vz,v = spGetUnitVelocity(targetID)
-
local pos = GetAimPosition(targetID)
-
-
if pos and pos.x and pos.y and pos.z and vx and vy and vz and v then
-
target.vel = {x=vx, y=vy, z=vz, speed=v}
-
target.lastKnownPos = pos
-
target.lastSeen = gameFrame
-
--Echo("target data updated")
-
if pos.y < 0 then
-
target.inWater = true
-
BombersHitTargetPosition(targetID, pos)
-
elseif target.inWater then -- was submerged but not now
-
target.inWater = false
-
BombersHitTargetID(targetID)
-
end
-
else
-
--Echo("target data missing")
-
end
-
-
elseif not target.inRadar then -- if its in Radar, continue with the regular Force Fire
-
-
if target.lastSeen then
-
-
local framesPassedSinceSeen = gameFrame - target.lastSeen
-
-
-- predict target's position by last known position and velocity
-
BombersHitTargetPosition(targetID, {x = target.lastKnownPos.x + target.vel.x * framesPassedSinceSeen,
-
y = target.lastKnownPos.y + target.vel.y * framesPassedSinceSeen,
-
z = target.lastKnownPos.z + target.vel.z * framesPassedSinceSeen})
-
else
-
TargetIsGone(targetID)
-
end
-
end
-
end
-
end
-
-
--function widget:UnitCmdDone(unitID, unitDefID, unitTeam, cmdID, cmdParams, cmdOpts, cmdTag)
-
--Echo("UnitCmdDone:", unitID, cmdID, cmdParams[1], cmdOpts)
-
--end
-
-
function widget:UnitFinished(unitID, unitDefID, unitTeam)
-
if unitTeam == myTeamID then
-
if bombersDefID[unitDefID] and not bombers[unitID] then
-
bombers[unitID] = bomberClass:New(unitID, unitDefID)
-
elseif unitDefID == airpadDefID then
-
local x,y,z = spGetUnitPosition(unitID)
-
airpads[unitID] = {x,y,z}
-
elseif unitDefID == airFactoryDefID then
-
local x,y,z = spGetUnitPosition(unitID)
-
airFacs[unitID] = {x,y,z}
-
elseif unitDefID == airPalteDefID then
-
local x,y,z = spGetUnitPosition(unitID)
-
airPlates[unitID] = {x,y,z}
-
end
-
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)
-
--Echo("unit destroyed") -- takes long time to arrive? (after destroying cloaked target)
-
-
if targets[unitID] then
-
--Echo("target destroyed", GetHumanName(unitID))
-
TargetIsGone(unitID)
-
-
elseif bombers[unitID] then
-
--Echo("bomber destroyed")
-
bombers[unitID] = nil
-
DiscardOrphanedTargets()
-
-
elseif airpads[unitID] then
-
airpads[unitID] = nil
-
elseif airFacs[unitID] then
-
airFacs[unitID] = nil
-
elseif airPlates[unitID] then
-
airPlates[unitID] = nil
-
end
-
end
-
-
function widget:UnitTaken(unitID)
-
widget:UnitDestroyed(unitID)
-
end
-
-
function widget:Initialize()
-
if (Spring.GetSpectatingState() or Spring.IsReplay()) 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 spGetSpecState() then widgetHandler:RemoveWidget(widget) end
-
end
Recent Snippets
- #157071 by Anonymous (745 days ago)
- #156876 by Anonymous (745 days ago)
- #154030 by Anonymous (749 days ago)
- #140524 by Anonymous (769 days ago)
- #137907 by rotense (772 days ago)
- #131892 by Anonymous (779 days ago)