Untitled

 avatar
unknown
lua
2 years ago
24 kB
6
Indexable
function widget:GetInfo()
    return {
        name = "AA Controller3",
        desc = "target bombers",
        author = "ABC",
        version = "v3",
        date = "July, 2021",
        layer = 4588,
        enabled = true,
    }
end

local spEcho = Spring.Echo
local spGetUnitPosition = Spring.GetUnitPosition

local spGetUnitIsCloaked = Spring.GetUnitIsCloaked
local spGetUnitWeaponCanFire = Spring.GetUnitWeaponCanFire
local spGetUnitWeaponState = Spring.GetUnitWeaponState

local spGetUnitStates = Spring.GetUnitStates
local spGetUnitHeading = Spring.GetUnitHeading
local spGetUnitHealth = Spring.GetUnitHealth
local spGetUnitIsStunned = Spring.GetUnitIsStunned

local spGetUnitWeaponTestTarget = Spring.GetUnitWeaponTestTarget
local spGetUnitWeaponTryTarget = Spring.GetUnitWeaponTryTarget

local spSetUnitRotation = Spring.SetUnitRotation

local spGetUnitDefID = Spring.GetUnitDefID
local spGetMyPlayerID = Spring.GetMyPlayerID
local spGetPlayerInfo = Spring.GetPlayerInfo
local spGetUnitSeparation = Spring.GetUnitSeparation
local spGiveOrderToUnit = Spring.GiveOrderToUnit
local spGetUnitWeaponDamages = Spring.GetUnitWeaponDamages
local spGetUnitRulesParam = Spring.GetUnitRulesParam
local spGetUnitMaxRange = Spring.GetUnitMaxRange
local spGetUnitCurrentCommand = Spring.GetUnitCurrentCommand
local spGetUnitIsDead = Spring.GetUnitIsDead

local spGetUnitWeaponTestTarget = Spring.GetUnitWeaponTestTarget
local spGetUnitWeaponTestRange = Spring.GetUnitWeaponTestRange
local spGetUnitWeaponTarget = Spring.GetUnitWeaponTarget
local spGetUnitWeaponVectors = Spring.GetUnitWeaponVectors

local spGetProjectileOwnerID = Spring.GetProjectileOwnerID
local spGetProjectileTarget =  Spring.GetProjectileTarget

local ENEMY_UNITS = Spring.ENEMY_UNITS
local spGetTeamUnits = Spring.GetTeamUnits
local CMD_UNIT_SET_TARGET = 34923
local myPlayerID = spGetMyPlayerID()
local myTeamID = Spring.GetMyTeamID()
local myAllyTeamID = Spring.GetMyAllyTeamID()
-- tables for anti air
local antiAirUnitList = {}
local antiAirLastReloadFrame = {}

-- tables for target monitoring
local scoutList = {}
local fighterList = {}
local bomberList = {}

local targetFutureDMG = {}
local targetInitialHP = {}

local projectileList = {}
local projectileTargetsList = {} -- projectileID : {targetID, dmg}

-- For Debugging
function dump(o)
    if type(o) == 'table' then
       local s = '{ '
       for k,v in pairs(o) do
          if type(k) ~= 'number' then k = '"'..k..'"' end
          s = s .. '['..k..'] = ' .. dump(v) .. ','
       end
       return s .. '} '
    else
       return tostring(o)
    end
 end

 
local PI     = math.pi;
local TWOPI  = PI * 2;
local PIDIV2 = PI * 0.5;

local GL_TRIANGLE_STRIP = GL.TRIANGLE_STRIP
local GL_QUADS   = GL.QUADS
local glBeginEnd = gl.BeginEnd
local glNormal   = gl.Normal
local glTexCoord = gl.TexCoord
local glVertex   = gl.Vertex
local glColor    = gl.Color
local sin = math.sin
local cos = math.cos

function DrawSphere( cx, cy, cz, r, p, color)
    local theta1,theta2,theta3 = 0,0,0;
    local ex,ey,ez = 0,0,0;
    local px,py,pz = 0,0,0;

    --// Disallow a negative number for radius.
    if ( r < 0 ) then r = -r; end

    --// Disallow a negative number for precision.
    if ( p < 0 ) then p = -p; end

    for i = 0,p*0.5-1 do
        theta1 = i * TWOPI / p - PIDIV2;
        theta2 = (i + 1) * TWOPI / p - PIDIV2;
        glColor(color[1], color[2], color[3], 0.2)
        glBeginEnd( GL_TRIANGLE_STRIP , function()
            for j = 0,p do
                theta3 = j * TWOPI / p;
                ex = cos(theta2) * cos(theta3);
                ey = sin(theta2);
                ez = cos(theta2) * sin(theta3);
                px = cx + r * ex;
                py = cy + r * ey;
                pz = cz + r * ez;
                glNormal( ex, ey, ez );
                glTexCoord( -(j/p) , 2*(i+1)/p );
                glVertex( px, py, pz );
                ex = cos(theta1) * cos(theta3);
                ey = sin(theta1);
                ez = cos(theta1) * sin(theta3);
                px = cx + r * ex;
                py = cy + r * ey;
                pz = cz + r * ez;
                glNormal( ex, ey, ez );
                glTexCoord( -(j/p), 2*i/p );
                glVertex( px, py, pz );
            end
        end)
    end
end


function addAntiAir(unitID)
    antiAirLastReloadFrame[unitID] = spGetUnitWeaponState(unitID, 1, "reloadFrame");
    AddUnitsToList({unitID}, antiAirUnitList)
end

function removeBomber(unitID)
    bomberList = removeUnitFromList(unitID, bomberList)
    -- targetFutureDMG[unitID] = nil;
    -- targetInitialHP[unitID] = nil;
end


function AddUnitsToList(UnitsIds, unitlistName)
    for i = 1, #UnitsIds do
        local unitID = UnitsIds[i]
        local unitExist = false
        for o = 1, #unitlistName do
            local uid = unitlistName[o]
            if unitID == uid then
                unitExist = true
            end
        end
        if not unitExist then
            unitlistName[#unitlistName + 1] = unitID
        end
    end
end

function removeUnitsFromList(UnitsIds, unitlistName)
    if #UnitsIds > 0 then
        local newUnitList = {}
        for p = 1, #unitlistName do
            local unitID = unitlistName[p]
            local unitExist = false
            for q = 1, #UnitsIds do
                local uid = UnitsIds[q]
                if unitID == uid then
                    unitExist = true
                end
            end
            if not unitExist then
                newUnitList[#newUnitList + 1] = unitID
            end
        end
        return newUnitList
    else
        return unitlistName
    end
end

function valueInList(unitID, unitList)
    for p = 1, #unitList do
        local storedUnitID = unitList[p];
        if storedUnitID == unitID then
            return true
        end
    end
    return false
end

function removeUnitFromList(unitID, unitList)
    if valueInList(unitID, unitList) then
        local newUnitList = {}
        for p = 1, #unitList do
            local storedUnitID = unitList[p]
            if storedUnitID ~= unitID then
                newUnitList[#newUnitList] = storedUnitID;
            end
        end
        return newUnitList
    end
    return unitList
end



function checkIsAntiAir(unitID)
    local ud = getUnitDef(unitID)
    if #ud.weapons >= 1 then
        if not ud.canMove and not ud.isFactory then
            if  ud.weapons[1]["onlyTargets"]["vtol"] == true then
                return true
            end
        else
            exclusiveAA = true;
            for i = 1, #ud.weapons do
                if ud.weapons[i]["onlyTargets"]["vtol"] == nil or ud.weapons[i]["onlyTargets"]["vtol"] == false then
                    exclusiveAA = false;
                end
            end
            if exclusiveAA then
                return true
            end
        end
    end
    return false
end

function getUnitDef(unitID)
    if unitID == nil then
        return nil
    end
    local unitDefID = spGetUnitDefID(unitID)
    if unitDefID == nil then return nil end
    return UnitDefs[ unitDefID ]
end



function checkForTargetsInList(unitID, list)
    local maxRange = spGetUnitMaxRange(unitID);
    local minDist = maxRange;
    local longestTime = nil;
    local target = nil;
    local removeList = {}
    for j = 1, #list do
        local targetUnitID = list[j];
        Spring.Echo(targetUnitID, Spring.ValidUnitID(targetUnitID), 'a', spGetUnitIsDead(targetUnitID))
        local overDamaged = targetFutureDMG[targetUnitID] > targetInitialHP[targetUnitID]
        local dist = spGetUnitSeparation(unitID, targetUnitID, true)
        if dist == nil then
            removeList[#removeList+1] = targetUnitID
        else
            local canHit = spGetUnitWeaponTestTarget(unitID, 1, targetUnitID);
            local canHit = canHit and spGetUnitWeaponTestRange(unitID, 1, targetUnitID);    
            if overDamaged then
                if target == nil and dist < maxRange and canHit then
                    target = targetUnitID;
                end
            else         
                if  minDist ~= nil and  dist < minDist and canHit then
                    target = targetUnitID
                    minDist = dist
                end
            end
        end
    end
    return target, removeList
end

function checkTargets(f)

    if #antiAirUnitList > 0 then
        deadAA = {}
        local split = 3
        local iteration = (f%split)
        for i = 1, #antiAirUnitList do
            if i%split == iteration or true then
                local unitID = antiAirUnitList[i];
                if spGetUnitIsDead(unitID) then
                    deadAA[#deadAA+1] = unitID
                else

                    local unitDef = getUnitDef(unitID)
                    local _, isUser, currentTargetID = spGetUnitWeaponTarget(unitID, 1)


                    if antiAirLastReloadFrame[unitID] ~= spGetUnitWeaponState(unitID, 1, "reloadFrame") then
                        antiAirLastReloadFrame[unitID] = spGetUnitWeaponState(unitID, 1, "reloadFrame")
                        if currentTargetID == nil then
                            --Spring.Echo("WTF HOW DOES THIS HAPPEN" )
                        else
                            local estimatedDmg = 0
                            ud = getUnitDef(currentTargetID)
                            if targetFutureDMG[currentTargetID] ~= nil and ud then
                                estimatedDmg = 0.9 * spGetUnitWeaponDamages(unitID, 1,  ud.armorType)
                                targetFutureDMG[currentTargetID] = targetFutureDMG[currentTargetID] + estimatedDmg
                            end
                            local x, y, z, _, _, _ = spGetUnitWeaponVectors( unitID, 1 ) --spGetUnitPosition(unitID, true, false)
                            if x ~= nil then
                                --local x, y, z= spGetUnitPosition(unitID, true, false)
                                
                                local searchSize = 200;
                                projectiles = Spring.GetProjectilesInRectangle(x-searchSize, z-searchSize, x+searchSize, z+searchSize)
                                for j = 1, #projectiles do
                                    local projectileID = projectiles[j]
                                    local projectileOwner = spGetProjectileOwnerID(projectileID)
                                    if not valueInList(projectileID, projectileList) and projectileOwner == unitID then
                                        local pTargetType, pTarget = spGetProjectileTarget(projectileID)
                                        projectileList[#projectileList+1] = projectileID
                                        projectileTargetsList[projectileID] = {currentTargetID, estimatedDmg}
                                        --Spring.Echo("fired", projectileOwner, projectileID, pTarget, currentTargetID)
                                
                                    end
                                end
                            end
                        end
                    end
                    
                    --
                    --
                    
                    -- Don't set priority target if manual attack command is already issued
                    local ccmdID, _, ctag, targetID = spGetUnitCurrentCommand(unitID)
                    if  ccmdID ~= CMD.ATTACK then
                        currentTargetPriority = 0;
                        
                        -- if we are already targeting a bomber then no need set new target
                        if valueInList(currentTargetID, bomberList) and isUser and targetFutureDMG[currentTargetID] <  targetInitialHP[currentTargetID] then
                            currentTargetPriority = 3;
                        else --Lets check for bomber targets in range
                            local target, removeList = checkForTargetsInList(unitID, bomberList);
                            bomberList = removeUnitsFromList(removeList, bomberList);
                            if target ~= nil then -- and target ~= spGetUnitRulesParam(unitID, "targetID") then
                                spGiveOrderToUnit(unitID, 34923,  { target  }, {"alt"})
                                currentTargetPriority = 3;
                            end
                        end


                        if currentTargetPriority < 3 then
                            if valueInList(currentTargetID, scoutList) and targetFutureDMG[currentTargetID] <  targetInitialHP[currentTargetID] then
                                currentTargetPriority = 2;
                            else --Lets check for bomber targets in range
                                local target, removeList = checkForTargetsInList(unitID, scoutList);
                                scoutList = removeUnitsFromList(removeList, scoutList);
                                if target ~= nil then -- and target ~= spGetUnitRulesParam(unitID, "targetID") then
                                    spGiveOrderToUnit(unitID, 34923,  { target  }, {"alt"})
                                    currentTargetPriority = 2;
                                end
                            end
                        end
                        if currentTargetPriority < 2 then
                            if valueInList(currentTargetID, fighterList) and targetFutureDMG[currentTargetID] <  targetInitialHP[currentTargetID] then
                                currentTargetPriority = 1;
                            else --Lets check for bomber targets in range
                                local target, removeList = checkForTargetsInList(unitID, fighterList);
                                fighterList = removeUnitsFromList(removeList, fighterList);
                                if target ~= nil then -- and target ~= spGetUnitRulesParam(unitID, "targetID") then
                                    spGiveOrderToUnit(unitID, 34923,  { target  }, {"alt"})
                                    currentTargetPriority = 1;
                                end
                            end
                        end
                    end
                end
            end
        end
        antiAirUnitList = removeUnitsFromList(deadAA, antiAirUnitList);
        
        -- loop projectiles
        projToRemove = {}
        needRecalculate = {}
        for j = 1, #projectileList do
            local projectileID = projectileList[j]
            local initTarget, dmg = projectileTargetsList[projectileID][1], projectileTargetsList[projectileID][2];

            if spGetProjectileOwnerID(projectileID) ~= nil and spGetProjectileTarget(projectileID) ~= nil then
                local pTargetType, pTarget = spGetProjectileTarget(projectileID)

                if initTarget ~= pTarget then
                    if targetInitialHP[initTarget] ~= nil then
                        local health, _, _, _ = spGetUnitHealth(initTarget);
                        if health ~= nil then
                            targetInitialHP[initTarget] = health;
                            if not valueInList(initTarget, needRecalculate) then
                                needRecalculate[#needRecalculate+1] = initTarget
                            end
                            targetFutureDMG[initTarget] = 0;
                        end
                        projToRemove[#projToRemove+1] = projectileID
                    end
                else
                    if dmg == 0 and initTarget ~= nil and  getUnitDef(initTarget) then
                        dmg = 0.9 * spGetUnitWeaponDamages(spGetProjectileOwnerID(projectileID), 1,  getUnitDef(initTarget).armorType)
                        projectileTargetsList[projectileID][2] = dmg
                    end
                end
                --Spring.Echo("proj", projectileID, initTarget, pTarget, dmg)
                --Spring.Echo("proj",projectileID, Spring.GetProjectileOwnerID(projectileID), Spring.GetProjectileTarget(projectileID))
            else
                --Spring.Echo("proj died", projectileID, initTarget, targetInitialHP[initTarget], targetFutureDMG[initTarget])
                if targetInitialHP[initTarget] ~= nil then
                    local health, _, _, _ = spGetUnitHealth(initTarget);
                    if health ~= nil then
                        targetInitialHP[initTarget] = health;
                        if not valueInList(initTarget, needRecalculate) then
                            needRecalculate[#needRecalculate+1] = initTarget
                        end
                        targetFutureDMG[initTarget] = 0;
                    end
                    projToRemove[#projToRemove+1] = projectileID
                end
            end
        end
        projectileList = removeUnitsFromList(projToRemove, projectileList)
        for j = 1, #projToRemove do
            projectileTargetsList[projToRemove[j]] = nil;
        end
        if #needRecalculate > 0 then
            for j = 1, #projectileList do
                local projectileID = projectileList[j]
                local initTarget, dmg = projectileTargetsList[projectileID][1], projectileTargetsList[projectileID][2];
                if valueInList(initTarget, needRecalculate) then
                    targetFutureDMG[initTarget] = targetFutureDMG[initTarget] + dmg;
                end
            end
        end
        
    end
end



function widget:UnitEnteredLos(unitID, unitTeam, allyTeam, unitDefID)
    if not valueInList(unitID, bomberList) then
        local ud = getUnitDef(unitID)
        if ud.modCategories.scout then
            scoutList[#scoutList + 1] = unitID;
            targetFutureDMG[unitID] = 0;
            local health, _, _, _ = spGetUnitHealth(unitID);
            targetInitialHP[unitID] = health;
        elseif ud.modCategories.fighter then
            fighterList[#fighterList + 1] = unitID;
            targetFutureDMG[unitID] = 0;
            local health, _, _, _ = spGetUnitHealth(unitID);
            targetInitialHP[unitID] = health;
        elseif #ud.weapons >= 1 then
            if unitTeam ~= myTeamID and WeaponDefs[ud.weapons[1].weaponDef].type == "AircraftBomb" then
                bomberList[#bomberList + 1] = unitID;
                targetFutureDMG[unitID] = 0;
                local health, _, _, _ = spGetUnitHealth(unitID);
                targetInitialHP[unitID] = health;
            end
        end
    end
end







function widget:DrawWorldPreUnit()
    for j = 1, #bomberList do
        local targetUnitID = bomberList[j];
        local overDamaged = targetFutureDMG[targetUnitID] > targetInitialHP[targetUnitID]
        if overDamaged or true then
            local x, y, z = spGetUnitPosition(targetUnitID)
            if x ~= nil then
                local color = {1, 0, 0}
                if not overDamaged then
                    sHP = targetInitialHP[targetUnitID]
                    fHP = targetFutureDMG[targetUnitID]
                    color = {0, 1-(fHP/sHP), (fHP/sHP) }
                end
                DrawSphere(x, y, z, 40, 20, color)
            end
        end
        
    end

    --drawCircle(uID, pos.coverageRange, pos.x, pos.y, pos.z, pos.circleColor)
    --DrawSphere(3600, 20, 7300, 1000, 100)
end

bomberWeaponDefs = {}
bomberUnitDefs = {}

function widget:Initialize()
    local _, _, spectator = spGetPlayerInfo(myPlayerID)

    if spectator then
        widgetHandler:RemoveWidget()
    end

    for i = 1,#WeaponDefs do
        if WeaponDefs[i].type == "AircraftBomb" then
            --spEcho(i,dump(WeaponDefs[i].type))
            bomberWeaponDefs[#bomberWeaponDefs+1] = i
        end
    end
    for i = 1,#UnitDefs do
        if #UnitDefs[i].weapons >= 1 and valueInList(UnitDefs[i].weapons[1].weaponDef, bomberWeaponDefs) then
        --if true then
            --spEcho(i, dump(UnitDefs[i].name))
            bomberUnitDefs[#bomberUnitDefs+1] = i
        end
    end
    local allBombers = Spring.GetTeamUnitsByDefs( 0, bomberUnitDefs )
    spEcho("mybombers", dump(allBombers))
   -- WeaponDefs[ud.weapons[1].weaponDef].type == "AircraftBomb"

    if false then
        for k, unitID in pairs(Spring.GetAllUnits()) do
            teamID = Spring.GetUnitTeam(unitID);
            if teamID == myTeamID then
                if checkIsAntiAir(unitID) then
                    addAntiAir(unitID)
                end
            elseif myAllyTeamID == Spring.GetUnitAllyTeam(unitID) then

            else
                if not valueInList(unitID, bomberList) then
                    local ud = getUnitDef(unitID)
                    if ud then
                        if ud.modCategories.scout then
                            scoutList[#scoutList + 1] = unitID;
                            targetFutureDMG[unitID] = 0;
                            local health, _, _, _ = spGetUnitHealth(unitID);
                            targetInitialHP[unitID] = health;
                        elseif ud.modCategories.fighter then
                            fighterList[#fighterList + 1] = unitID;
                            targetFutureDMG[unitID] = 0;
                            local health, _, _, _ = spGetUnitHealth(unitID);
                            targetInitialHP[unitID] = health;
                        elseif #ud.weapons >= 1 then
                            if unitTeam ~= myTeamID and WeaponDefs[ud.weapons[1].weaponDef].type == "AircraftBomb" then
                                bomberList[#bomberList + 1] = unitID;
                                targetFutureDMG[unitID] = 0;
                                local health, _, _, _ = spGetUnitHealth(unitID);
                                targetInitialHP[unitID] = health;
                            end
                        end
                    end
                end
            end

        end
    end

end

function widget:GameFrame(f)
    checkTargets(f)
end


function widget:UnitLeftRadar(unitID, unitTeam, allyTeam, unitDefID)
    spEcho("left radar")
    if unitTeam ~= myTeamID then
        --removeBomber(unitID)
    end
end
function widget:UnitLeftLos(unitID, unitTeam, allyTeam, unitDefID)
    Spring.Echo("left los")
end

function widget:UnitCreated(unitID, unitDefID, unitTeam)
    if unitTeam == myTeamID then
        if checkIsAntiAir(unitID) then
            addAntiAir(unitID)
        end
    end
end

function widget:UnitGiven(unitID, unitDefID, oldTeam, newTeam)
    if unitTeam == myTeamID then
        if checkIsAntiAir(unitID) then
            addAntiAir(unitID)
        end
    end
end

function widget:RenderUnitDestroyed(unitID, unitDefID, unitTeam)
    spEcho("renderdestroyed")
end

function widget:UnitDestroyed(unitID, unitDefID, unitTeam, attackerID, attackerDefID, attackerTeam)
    spEcho("blabla")
    if unitTeam ~= myTeamID then
        bomberList = removeUnitFromList(unitID, bomberList)
        targetFutureDMG[unitID] = nil;
        targetInitialHP[unitID] = nil;
    end

    local destrUnit = { unitID }
    if unitTeam == myTeamID then
        antiAirUnitList = removeUnitFromList(destrUnit, antiAirUnitList)
    end
end
Editor is loading...