DCS_MissionDev/DCS_Caucasus/F99th-Iron Hand/fac2_ArtyMarkRecceZone_v2_1_21.lua
2024-11-12 14:18:28 -06:00

2724 lines
105 KiB
Lua

--[[
DCS FAC v2.1.0
-------
Authors: Pb_Magnet (github.com/jweisner), modified by Shadow
NOTE: Almost all of the hard stuff here is copied/adapted from CTLD: https://github.com/ciribob/DCS-CTLD
This script was originally submitted as a PR to include in CTLD, but there didn't seem to be any interest from CTLD vOv
New features:
Required Scripts for this scrip to function:
Mist
markManager
Reworked Artillery control via F10
Auto Map Marking
RECCE Units --> Cant Lase but will mark all units in an area
Manual Targeting mode
Offset Mortar aimpoint to compensate for rounds falling short
Added arty fire type choice
]]
local facPlayerSpawn ={}
fac = {} -- do not modify this line
function contains(t, e)
for i = 1,#t do
if t[i] == e then return true end
end
return false
end
fac.facACTypes ={
"SA342L",
"SA342M",
"SA342Mistral",
"SA342Minigun"
--"TF-51",
--"Hawk",
}
fac.artyDirectorTypes = {
"Soldier M249",
}
-- ***************** FAC CONFIGURATION *****************
fac.FAC_maxDistance = 15000 -- How far a FAC can "see" in meters (with Line of Sight)
fac.AutoOn = true -- when enabled turns auto on station for AFAC
fac.FAC_smokeOn_RED = true -- enables marking of target with smoke for RED forces
fac.FAC_smokeOn_BLUE = true -- enables marking of target with smoke for BLUE forces
fac.FAC_smokeColour_RED = 0 -- RED side smoke colour -- Green = 0 , Red = 1, White = 2, Orange = 3, Blue = 4 (if using flares limit to green and orange)
fac.FAC_smokeColour_BLUE = 3 -- BLUE side smoke colour -- Green = 0 , Red = 1, White = 2, Orange = 3, Blue = 4 (if using flares limit to green and orange)
fac.FAC_FACStatusF10 = true -- enables F10 FAC Status menu
fac.FAC_location = true -- shows location of target in FAC message
fac.FAC_lock = "all" -- "vehicle" OR "troop" OR "all" forces FAC to only lock vehicles or troops or all ground units
fac.FAC_laser_codes = { '1688', '1677', '1666', '1113','1115' ,'1111'}
fac.fireMissionRounds = 24 -- number of shells per fire mission request
fac.illumHeight = 500 --height for illumination bomb
fac.facOffsetDist = 5000
fac.markID = 1000 --intial MarkerID
fac.recceID = 50000 --Initial recce ID number
-- ******************** FAC names **********************
function fac.isAFAC(acUnit)
local playerGroup = Unit.getGroup(acUnit)
local playerGroupName = playerGroup:getName()
local AFACmatch = 0
if (string.find(playerGroupName, "AFAC") == nil) and (string.find(playerGroupName, "RECON") == nil) then
AFACmatch = 0
else
AFACmatch = 1
end
if contains(fac.facACTypes,acUnit:getTypeName()) then
AFACmatch = 1
end
return AFACmatch
end
function fac.isRECCE(acUnit)
local playerGroup = Unit.getGroup(acUnit)
local playerGroupName = playerGroup:getName()
local RECCEmatch = 0
--trigger.action.outText(acUnit:getTypeName(),5)
if (string.find(playerGroupName, "RECCE") == nil) and (string.find(playerGroupName, "RECON") == nil) then
RECCEmatch = 0
else
RECCEmatch = 1
end
if contains(fac.facACTypes,acUnit:getTypeName()) then
RECCEmatch = 1
end
return RECCEmatch
end
function fac.isArtyD(acUnit)
local adMatch = 0
local playerGroup = Unit.getGroup(acUnit)
if playerGroup then
local playerGroupName = playerGroup:getName()
if playerGroupName then
if contains(fac.artyDirectorTypes,acUnit:getTypeName()) then
adMatch = 1
end
end
end
return adMatch
end
-- Use any of the predefined names or set your own ones
fac.facPilotNames = {
"AFAC-1",
"AFAC-2",
"AFAC-3",
"AFAC-4",
"AFAC-5",
"AFAC-6",
"AFAC-7",
"AFAC-8",
}
--Define RECCE pilots
fac.reccePilotNames = {
"AFAC-1",
"AFAC-2",
"AFAC-3",
"AFAC-4",
"AFAC-5",
"AFAC-6",
"AFAC-7",
"AFAC-8",
}
fac.artDirectNames = {
"testSpotter"
}
--init FAC
-- Auto Detects Group names and adds unit to FAC and RECCE list
function facPlayerSpawn:onEvent(e)
if e.id == world.event.S_EVENT_BIRTH then
local objType = e.initiator:getDesc()
if Object.getCategory(e.initiator) == 1 then
if objType.category < 2 then
local AFAC = fac.isAFAC(e.initiator)
local RECCE = fac.isRECCE(e.initiator)
local test2 = Unit.getGroup(e.initiator)
local gid = test2:getID()
local afacUnitName = e.initiator:getName()
local checkAFACName, afacIdx = contains(fac.facPilotNames,afacUnitName)
local checkRECCEName, afacIdx = contains(fac.reccePilotNames,afacUnitName)
--trigger.action.outText(RECCE .. afacUnitName,5)
if AFAC == 1 then
if contains(fac.facPilotNames,afacUnitName) == false then
table.insert(fac.facPilotNames, afacUnitName)
trigger.action.outTextForGroup(gid,"Your A/C is an AFAC and added to AFAC List \nUse your F10 menu to spot targets for your team and call in artillery strikes", 15)
else
trigger.action.outTextForGroup(gid,"Your A/C is an AFAC \nUse your F10 menu to spot targets for your team and call in artillery strikes", 15)
end
if fac.AutoOn == true then
fac.setFacOnStation({afacUnitName,true})
end
--trigger.action.outTextForGroup(gid,"Your A/C is an AFAC "..afacUnitName.." "..#fac.facPilotNames, 15)
--trigger.action.outText(rampcheck,5)
elseif checkAFACName == true then
trigger.action.outTextForGroup(gid,"Your A/C is an AFAC \nUse your F10 menu to spot targets for your team and call in artillery strikes", 15)
end
if RECCE == 1 then
if contains(fac.reccePilotNames,afacUnitName) == false then
table.insert(fac.reccePilotNames, afacUnitName)
trigger.action.outTextForGroup(gid,"Your A/C is an RECCE and added to RECCE List \nUse your F10 menu to spot all targets in an area for your team and mark them on the F10 map", 15)
else
trigger.action.outTextForGroup(gid,"Your A/C is an Recon A/C \nUse your F10 menu to spot all targets in an area for your team and mark them on the F10 map", 15)
end
elseif checkRECCEName == true then
trigger.action.outTextForGroup(gid,"Your A/C is an RECCE \nUse your F10 menu to spot all targets in an area for your team and mark them on the F10 map", 15)
end
elseif objType.category == 2 then
local artDirect = fac.isArtyD(e.initiator)
local afacUnitName = e.initiator:getName()
local test2 = Unit.getGroup(e.initiator)
local gid = test2:getCoalition()
if artDirect == 1 then
if contains(fac.artDirectNames,afacUnitName) == false then
table.insert(fac.artDirectNames, afacUnitName)
--trigger.action.outTextForGroup(gid,"Artillery Spotter has been deployed", 15)
end
end
end
end
end
end
world.addEventHandler(facPlayerSpawn)
--Arty properties from Mbot's AES script
fac.ArtilleryProperties = {
["2B11 mortar"] = {
minrange = 500, --Minimal firing range
maxrange = 7000, --Maximal firing range
FM_rounds = 24, --The total amount of shots of a fire mission for a battery of this unit type
minAmmo = 0, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire
displacementTime = 0 --Time the battery waits between firing and moving
},
["SAU 2-C9"] = {
minrange = 500, --Minimal firing range
maxrange = 7000, --Maximal firing range
FM_rounds = 24, --The total amount of shots of a fire mission for a battery of this unit type
minAmmo = 0, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire
displacementTime = 10 --Time the battery waits between firing and moving
},
["M-109"] = {
minrange = 300, --Minimal firing range
maxrange = 22000, --Maximal firing range
FM_rounds = 24, --The total amount of shots of a fire mission for a battery of this unit type
minAmmo = 160, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire
displacementTime = 10 --Time the battery waits between firing and moving
},
["SAU Gvozdika"] = {
minrange = 300, --Minimal firing range
maxrange = 15000, --Maximal firing range
FM_rounds = 24, --The total amount of shots of a fire mission for a battery of this unit type
minAmmo = 0, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire
displacementTime = 10 --Time the battery waits between firing and moving
},
["SAU Akatsia"] = {
minrange = 300, --Minimal firing range
maxrange = 17000, --Maximal firing range
FM_rounds = 24, --The total amount of shots of a fire mission for a battery of this unit type
minAmmo = 0, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire
displacementTime = 10 --Time the battery waits between firing and moving
},
["SAU Msta"] = {
minrange = 300, --Minimal firing range
maxrange = 23500, --Maximal firing range
FM_rounds = 24, --The total amount of shots of a fire mission for a battery of this unit type
minAmmo = 300, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire
displacementTime = 10 --Time the battery waits between firing and moving
},
["MLRS"] = {
minrange = 10000, --Minimal firing range
maxrange = 32000, --Maximal firing range
FM_rounds = 12, --The total amount of shots of a fire mission for a battery of this unit type
minAmmo = 12, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire
displacementTime = 10 --Time the battery waits between firing and moving
},
["Grad-URAL"] = {
minrange = 5000, --Minimal firing range
maxrange = 19000, --Maximal firing range
FM_rounds = 120, --The total amount of shots of a fire mission for a battery of this unit type
minAmmo = 40, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire
displacementTime = 120 --Time the battery waits between firing and moving
},
["Smerch"] = {
minrange = 20000, --Minimal firing range
maxrange = 70000, --Maximal firing range
FM_rounds = 24, --The total amount of shots of a fire mission for a battery of this unit type
minAmmo = 12, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire
displacementTime = 120 --Time the battery waits between firing and moving
},
["Uragan_BM-27"] = {
minrange = 11500, --Minimal firing range
maxrange = 35800, --Maximal firing range
FM_rounds = 16, --The total amount of shots of a fire mission for a battery of this unit type
minAmmo = 12, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire
displacementTime = 120 --Time the battery waits between firing and moving
},
["hy_launcher"] = { -- doesn't work do not use
minrange = 3000, --Minimal firing range
maxrange = 40000, --Maximal firing range
FM_rounds = 16, --The total amount of shots of a fire mission for a battery of this unit type
minAmmo = 12, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire
displacementTime = 120 --Time the battery waits between firing and moving
},
["TomahawkLauncher"] = {
minrange = 18520, --Minimal firing range
maxrange = 460000, --Maximal firing range
FM_rounds = 16, --The total amount of shots of a fire mission for a battery of this unit type
minAmmo = 12, --The amount of rounds left in a individual unit when switching from rapid fire to sustained fire
displacementTime = 120 --Time the battery waits between firing and moving
}
}
------------ FAC -----------
fac.facManTGT = {}
fac.facLaserPoints = {}
fac.facIRPoints = {}
fac.facSmokeMarks = {}
fac.facUnits = {} -- list of FAC units for f10 command
fac.facCurrentTargets = {}
fac.facAddedTo = {} -- keeps track of who's had the fac command menu added
fac.RECCEAddedTo = {} -- keeps track of who's had the RECCE command menu added
fac.facRadioAdded = {} -- keeps track of who's had the radio command added
fac.facLaserPointCodes = {} -- keeps track of what laser code is used by each fac
fac.facOnStation = {} -- keeps track of which facs are on station
fac.markerType = {} -- keeps track of marker type per FAC
fac.markerTypeColor = {} -- keeps track of marker color per FAC
fac.redArty = {} -- keeps track of available arty for Red
fac.bluArty = {} --keeps track of available arty for Blue
fac.ArtyTasked = {} --keeps track of Arty with current fire missions
fac.AITgted = {}
-- search for activated FAC units and schedule facAutoLase
function fac.checkFacStatus()
--env.info("FAC checkFacStatus")
timer.scheduleFunction(fac.checkFacStatus, nil, timer.getTime() + 1.0)
local _status, _result = pcall(function()
for _, _facUnitName in ipairs(fac.facPilotNames) do
local _facUnit = fac.getFacUnit(_facUnitName)
if _facUnit ~= nil then
--[[
if fac.facOnStation[_facUnitName] == true then
env.info("FAC DEBUG: fac.checkFacStatus() " .. _facUnitName .. " on-station")
end
if fac.facOnStation[_facUnitName] == nil then
env.info("FAC DEBUG: fac.checkFacStatus() " .. _facUnitName .. " off-station")
end
]]
-- if fac is off-station and is AI, set onStation
if fac.facUnits[_facUnitName] == nil and _facUnit:getPlayerName() == nil then
--env.info("FAC: setting onStation for AI fac unit " .. _facUnitName)
fac.setFacOnStation({_facUnitName, true})
end
-- start facAutoLase if the FAC is on station and not already scheduled
if fac.facUnits[_facUnitName] == nil and fac.facOnStation[_facUnitName] == true then
env.info("FAC: found new FAC unit. Starting facAutoLase for " .. _facUnitName)
fac.facAutoLase(_facUnitName) --(_facUnitName, _laserCode, _smoke, _lock, _colour)
end
end
end
end)
if (not _status) then
env.error(string.format("FAC ERROR: %s", _result))
end
end
-- gets the FAC status and displays to coalition units
function fac.getFacStatus(_args)
--returns the status of all FAC units
local _playerUnit = fac.getFacUnit(_args[1])
local colorString = {["0"] = "GREEN" ,["1"] = "RED" ,["2"] = "WHITE", ["3"] = "ORANGE" , ["4"] = "BLUE"}
if _playerUnit == nil then
return
end
local _side = _playerUnit:getCoalition()
local _mkrColor
if _side == 1 then
_mkrColor = fac.FAC_smokeColour_BLUE
elseif _side == 2 then
_mkrColor = fac.FAC_smokeColour_RED
else
_mkrColor = 2
end
local _facUnit = nil
local _message = "FAC STATUS: \n\n"
for _facUnitName, _facDetails in pairs(fac.facUnits) do
--look up units
_facUnit = Unit.getByName(_facDetails.name)
if _facUnit ~= nil and _facUnit:getLife() > 0 and _facUnit:isActive() == true and _facUnit:getCoalition() == _side and fac.facOnStation[_facUnitName] == true then
local _enemyUnit = fac.getCurrentFacUnit(_facUnit, _facUnitName)
local _laserCode = fac.facLaserPointCodes[_facUnitName]
-- get player name if available
local _facName = _facUnitName
if _facUnit:getPlayerName() ~= nil then
_facName = _facUnit:getPlayerName()
end
if fac.markerTypeColor[_facName] ~= nil then
_mkrColor = fac.markerTypeColor[_facName]
end
if _laserCode == nil then
_laserCode = "UNKNOWN"
end
if fac.markerType[_facUnitName] == nil then
fac.markerType[_facUnitName] = "FLARES"
end
if _enemyUnit ~= nil and _enemyUnit:getLife() > 0 and _enemyUnit:isActive() == true then
_message = _message .. "" .. _facName .. " targeting " .. _enemyUnit:getTypeName() .. " CODE: " .. _laserCode .. fac.getFacPositionString(_enemyUnit) .. "\nMarked with " .. colorString[tostring(_mkrColor)] .." ".. fac.markerType[_facUnitName].. "\n"
else
_message = _message .. "" .. _facName .. " on-station and searching for targets" .. " CODE: " .. _laserCode .. "\n"
end
end
end
if _message == "FAC STATUS: \n\n" then
_message = "No Active FACs, Join a slot labeled RECON or AFAC to play as a flying JTAC and Artillery Spotter"
end
fac.notifyCoalition(_message, 60, _side)
end
function fac.getFacPositionString(_unit)
if fac.FAC_location == false then
return ""
end
local _lat, _lon = coord.LOtoLL(_unit:getPosition().p)
local unitPos = _unit:getPoint()
-- local _latDeg,_latMinf = math.modf(_lat)
-- local _longDeg,_longMinf = math.modf(_lat)
-- local _latMin, _latSecf = math.modf(_latMinf*60)
-- local _longMin, _longSecf = math.modf(_longMinf*60)
-- local _latSec = _latSecf*605
-- local _longSec = _longSecf*60
local _latLngStr = mist.tostringLL(_lat, _lon, 3, false)
local _latLngSecStr = mist.tostringLL(_lat, _lon, 3, true)
local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_unit:getPosition().p)), 5)
return " @\n- DD " .. _latLngStr .." \n- DMS: " .. _latLngSecStr .. " \n- MGRS: " .. _mgrsString .."\nAltitude: ".. math.floor(unitPos.y) .."m/" .. math.floor(unitPos.y*3.28084) .."ft"
end
-- get currently selected unit and check if the FAC is still in range
function fac.getCurrentFacUnit(_facUnit, _facUnitName)
local _unit = nil
if fac.facCurrentTargets[_facUnitName] ~= nil then
_unit = Unit.getByName(fac.facCurrentTargets[_facUnitName].name)
end
local _tempPoint = nil
local _tempDist = nil
local _tempPosition = nil
local _facPosition = _facUnit:getPosition()
local _facPoint = _facUnit:getPoint()
if _unit ~= nil and _unit:getLife() > 0 and _unit:isActive() == true then
-- calc distance
_tempPoint = _unit:getPoint()
-- tempPosition = unit:getPosition()
_tempDist = fac.getDistance(_unit:getPoint(), _facUnit:getPoint())
if _tempDist < fac.FAC_maxDistance then
-- calc visible
-- check slightly above the target as rounding errors can cause issues, plus the unit has some height anyways
local _offsetEnemyPos = { x = _tempPoint.x, y = _tempPoint.y + 2.0, z = _tempPoint.z }
local _offsetFacPos = { x = _facPoint.x, y = _facPoint.y + 2.0, z = _facPoint.z }
if land.isVisible(_offsetEnemyPos, _offsetFacPos) then
return _unit
end
end
end
return nil
end
function fac.getFacUnit(_facUnitName)
if _facUnitName == nil then
return nil
end
local _fac = Unit.getByName(_facUnitName)
if _fac ~= nil and _fac:isActive() and _fac:getLife() > 0 then
return _fac
end
return nil
end
-- gets the FAC player name if available
function fac.getFacName(_facUnitName)
local _facUnit = Unit.getByName(_facUnitName)
local _facName = _facUnitName
if _facUnit == nil then
--env.info('FAC: fac.getFacName: unit not found: '.._facUnitName)
return _facUnitName
end
if _facUnit:getPlayerName() ~= nil then
_facName = _facUnit:getPlayerName()
end
return _facName
end
function fac.facAutoLase(_facUnitName, _laserCode, _smoke, _lock, _colour,_knwnTarget)
local colorString = { ["0"] = "GREEN" ,["1"] = "RED" ,["2"] = "WHITE", ["3"] = "ORANGE" ,["4"] = "BLUE"}
--env.info('FAC DEBUG: ' .. _facUnitName .. ' autolase')
if _lock == nil then
_lock = fac.FAC_lock
end
local _mkrColor
local _facUnit = Unit.getByName(_facUnitName)
if _facUnit == nil then
--env.info('FAC: ' .. _facUnitName .. ' dead.')
-- FAC was in the list, now the unit is missing: probably dead
if fac.facUnits[_facUnitName] ~= nil then
fac.notifyCoalition("[Forward Air Controller \"" ..fac.getFacName(_facUnitName).. "\" MIA.]", 10, fac.facUnits[_facUnitName].side)
end
--remove fac
fac.cleanupFac(_facUnitName)
return
end
local side = _facUnit:getCoalition()
if side == 1 then
_mkrColor = fac.FAC_smokeColour_BLUE
elseif side == 2 then
_mkrColor = fac.FAC_smokeColour_RED
else
_mkrColor = 2
end
-- stop fac activity if fac is marked off-station CANCELS AUTO-LASE
if fac.facOnStation[_facUnitName] == nil then
env.info('FAC: ' .. _facUnitName .. ' is marked off-station, stopping autolase')
fac.cancelFacLase(_facUnitName)
fac.facCurrentTargets[_facUnitName] = nil
return
end
if fac.facLaserPointCodes[_facUnitName] == nil then
--env.info('FAC: fac.facAutoLase() ' .. _facUnitName .. ' has no laserCode, setting default')
fac.facLaserPointCodes[_facUnitName] = fac.FAC_laser_codes[1]
end
_laserCode = fac.facLaserPointCodes[_facUnitName]
--env.info('FAC: ' .. _facUnitName .. ' laser code: ' .. _laserCode)
if fac.facUnits[_facUnitName] == nil then
--env.info('FAC: ' .. _facUnitName .. ' not in fac.facUnits list, adding')
--add to list
fac.facUnits[_facUnitName] = { name = _facUnit:getName(), side = _facUnit:getCoalition() }
-- work out smoke colour
if _colour == nil then
if _facUnit:getCoalition() == 1 then
_colour = fac.FAC_smokeColour_RED
else
_colour = fac.FAC_smokeColour_BLUE
end
end
if fac.markerTypeColor[_facUnitName] ~= nil then
_colour = fac.markerTypeColor[_facUnitName]
end
if _smoke == nil then
if _facUnit:getCoalition() == 1 then
_smoke = fac.FAC_smokeOn_RED
else
_smoke = fac.FAC_smokeOn_BLUE
end
end
end
if fac.markerType[_facUnitName] == nil then
fac.markerType[_facUnitName] = "FLARES"
end
-- search for current unit
if _facUnit:isActive() == false then
fac.cleanupFac(_facUnitName)
env.info('FAC: ' .. _facUnitName .. ' Not Active - Waiting 30 seconds')
timer.scheduleFunction(fac.timerFacAutoLase, { _facUnitName, _laserCode, _smoke, _lock, _colour }, timer.getTime() + 30)
return
end
local _enemyUnit = fac.getCurrentFacUnit(_facUnit, _facUnitName)
if _enemyUnit == nil and fac.facCurrentTargets[_facUnitName] ~= nil then
local _tempUnitInfo = fac.facCurrentTargets[_facUnitName]
local _tempUnit = Unit.getByName(_tempUnitInfo.name)
if _tempUnit ~= nil and _tempUnit:getLife() > 0 and _tempUnit:isActive() == true then
fac.notifyCoalition("["..fac.getFacName(_facUnitName) .. " target " .. _tempUnitInfo.unitType .. " lost. Scanning for Targets.]", 10, _facUnit:getCoalition())
else
fac.notifyCoalition("["..fac.getFacName(_facUnitName) .. " target " .. _tempUnitInfo.unitType .. " KIA. Good Job! Scanning for Targets.]", 10, _facUnit:getCoalition())
--trigger.action.removeMark(fac.markID+_tempUnit:getID())
end
--remove from smoke list
fac.facSmokeMarks[_tempUnitInfo.name] = nil
-- remove from target list
fac.facCurrentTargets[_facUnitName] = nil
--stop lasing
fac.cancelFacLase(_facUnitName)
end
if _enemyUnit == nil then
if _knwnTarget == nil then
_enemyUnit = fac.findFacNearestVisibleEnemy(_facUnit, _lock)
else
_enemyUnit = _knwnTarget
end
if _enemyUnit ~= nil then
local tgtMarkID = timer.getTime() * 1000
-- store current target for easy lookup
fac.facCurrentTargets[_facUnitName] = { name = _enemyUnit:getName(), unitType = _enemyUnit:getTypeName(), unitId = _enemyUnit:getID() }
local _vel = _enemyUnit:getVelocity()
local _spd = 0
if _vel ~=nil then
_spd = math.sqrt(_vel.x^2+_vel.z^2)
end
local unitPos = _enemyUnit:getPoint()
local _lat, _lon = coord.LOtoLL(_enemyUnit:getPosition().p)
local _latLngStr = mist.tostringLL(_lat, _lon, 3, false)
local _latLngSecStr = mist.tostringLL(_lat, _lon, 3, true)
local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_enemyUnit:getPosition().p)), 5)
trigger.action.markToCoalition(tgtMarkID,_enemyUnit:getTypeName().. " - DMS: " .. _latLngSecStr .." Altitude: ".. math.floor(unitPos.y) .."m/" .. math.floor(unitPos.y*3.28084) .."ft".. "\nHeading: ".. math.floor(getHeading(_enemyUnit) * 180/math.pi) .. "\nSpeed: " .. math.floor(_spd*2) .. " MPH" .."\nSpotted by: " .. fac.getFacName(_facUnitName), _enemyUnit:getPoint(), _facUnit:getCoalition(),false,fac.getFacName(_facUnitName).." marked a target")
timer.scheduleFunction(removeSetMark,{tgtMarkID},timer.getTime() + 120)
fac.notifyCoalition("["..fac.getFacName(_facUnitName) .. " lasing new target " .. _enemyUnit:getTypeName() .. '. CODE: ' .. _laserCode .. fac.getFacPositionString(_enemyUnit).."\nMarked with "..colorString[tostring(_colour)] .." "..fac.markerType[_facUnitName].."]" , 10, _facUnit:getCoalition())
local tgtMark = fac.markID+1
--local tgtMark = curMark
fac.markID = tgtMark
-- create smoke
if _smoke == true then
--trigger.action.removeMark(tgtMark)
--timer.scheduleFunction(trigger.action.removeMark,{tgtMark},timer.getTime() + 30)
--create first smoke
fac.createSmokeMarker(_enemyUnit, _colour,_facUnitName)
end
end
end
if _enemyUnit ~= nil then
fac.facLaseUnit(_enemyUnit, _facUnit, _facUnitName, _laserCode)
-- DEBUG
--env.info('FAC: Timer timerSparkleLase '.._facUnitName.." ".._laserCode.." ".._enemyUnit:getName())
--
local _vel = _enemyUnit:getVelocity()
local _spd = 0
if _vel ~=nil then
_spd = math.sqrt(_vel.x^2+_vel.z^2)
end
local timeNext
if _spd < 1 then
timeNext = 1
else
timeNext = 1/(_spd)
end
--trigger.action.outText(timeNext,10)
timer.scheduleFunction(fac.timerFacAutoLase, { _facUnitName, _laserCode, _smoke, _lock, _colour }, timer.getTime() + timeNext)
if _smoke == true then
local _nextSmokeTime = fac.facSmokeMarks[_enemyUnit:getName()]
--recreate smoke marker after 5 mins
if _nextSmokeTime ~= nil and _nextSmokeTime < timer.getTime() then
fac.createSmokeMarker(_enemyUnit, _colour,_facUnitName)
end
end
else
--env.info('FAC: LASE: No Enemies Nearby')
-- stop lazing the old spot
fac.cancelFacLase(_facUnitName)
timer.scheduleFunction(fac.timerFacAutoLase, { _facUnitName, _laserCode, _smoke, _lock, _colour }, timer.getTime() + 5)
end
end
-- used by the timer function
function fac.timerFacAutoLase(_args)
fac.facAutoLase(_args[1], _args[2], _args[3], _args[4], _args[5])
end
function fac.cleanupFac(_facUnitName)
-- clear laser - just in case
fac.cancelFacLase(_facUnitName)
-- Cleanup
fac.facLaserPoints[_facUnitName] = nil
fac.facIRPoints[_facUnitName] = nil
fac.facSmokeMarks[_facUnitName] = nil
fac.facUnits[_facUnitName] = nil
fac.facCurrentTargets[_facUnitName] = nil
fac.facAddedTo[_facUnitName] = nil
fac.facRadioAdded[_facUnitName] = nil
fac.facLaserPointCodes[_facUnitName] = nil
fac.facOnStation[_facUnitName] = nil
fac.markerType[_facUnitName] = nil
end
function fac.createFacSmokeMarker(_enemyUnit, _colour,_facUnitName)
--recreate in 5 mins
if fac.markerType[_facUnitName] == "SMOKE" then
fac.facSmokeMarks[_enemyUnit:getName()] = timer.getTime() + 300.0
else
fac.facSmokeMarks[_enemyUnit:getName()] = timer.getTime() + 5
end
-- move smoke 2 meters above target for ease
local _enemyPoint = _enemyUnit:getPoint()
if fac.markerType[_facUnitName] =="SMOKE" then
trigger.action.smoke({ x = _enemyPoint.x, y = _enemyPoint.y + 2.0, z = _enemyPoint.z }, _colour)
else
trigger.action.signalFlare({ x = _enemyPoint.x, y = _enemyPoint.y + 2.0, z = _enemyPoint.z }, _colour,0)
end
end
function fac.cancelFacLase(_facUnitName)
local _tempLase = fac.facLaserPoints[_facUnitName]
if _tempLase ~= nil then
Spot.destroy(_tempLase)
fac.facLaserPoints[_facUnitName] = nil
_tempLase = nil
end
local _tempIR = fac.facIRPoints[_facUnitName]
if _tempIR ~= nil then
Spot.destroy(_tempIR)
fac.facIRPoints[_facUnitName] = nil
_tempIR = nil
end
end
function fac.facLasePoint(_Point, _facUnit, _facUnitName, _laserCode)
--cancelLase(_facUnitName)
local _spots = {}
local _enemyVector = _Point
local _enemyVectorUpdated = { x = _enemyVector.x, y = _enemyVector.y + 2.0, z = _enemyVector.z }
local _oldLase = fac.facLaserPoints[_facUnitName]
local _oldIR = fac.facIRPoints[_facUnitName]
if _oldLase == nil or _oldIR == nil then
-- create lase
local _status, _result = pcall(function()
_spots['irPoint'] = Spot.createInfraRed(_facUnit, { x = 0, y = 2.0, z = 0 }, _enemyVectorUpdated)
_spots['laserPoint'] = Spot.createLaser(_facUnit, { x = 0, y = 2.0, z = 0 }, _enemyVectorUpdated, _laserCode)
return _spots
end)
if not _status then
env.error('FAC: ERROR: ' .. _result, false)
else
if _result.irPoint then
-- DEBUG
--env.info('FAC:' .. _facUnitName .. ' placed IR Pointer on '.._enemyUnit:getName())
fac.facIRPoints[_facUnitName] = _result.irPoint --store so we can remove after
end
if _result.laserPoint then
-- DEBUG
--env.info('FAC:' .. _facUnitName .. ' is Lasing '.._enemyUnit:getName()..'. CODE:'.._laserCode)
fac.facLaserPoints[_facUnitName] = _result.laserPoint
end
end
else
-- update lase
if _oldLase ~= nil then
_oldLase:setPoint(_enemyVectorUpdated)
end
if _oldIR ~= nil then
_oldIR:setPoint(_enemyVectorUpdated)
end
end
end
function fac.facLaseUnit(_enemyUnit, _facUnit, _facUnitName, _laserCode)
--cancelLase(_facUnitName)
local _spots = {}
local _enemyVector = _enemyUnit:getPoint()
local _enemyVectorUpdated = { x = _enemyVector.x, y = _enemyVector.y + 2.0, z = _enemyVector.z }
local _oldLase = fac.facLaserPoints[_facUnitName]
local _oldIR = fac.facIRPoints[_facUnitName]
if _oldLase == nil or _oldIR == nil then
-- create lase
local _status, _result = pcall(function()
_spots['irPoint'] = Spot.createInfraRed(_facUnit, { x = 0, y = 2.0, z = 0 }, _enemyVectorUpdated)
_spots['laserPoint'] = Spot.createLaser(_facUnit, { x = 0, y = 2.0, z = 0 }, _enemyVectorUpdated, _laserCode)
return _spots
end)
if not _status then
env.error('FAC: ERROR: ' .. _result, false)
else
if _result.irPoint then
-- DEBUG
--env.info('FAC:' .. _facUnitName .. ' placed IR Pointer on '.._enemyUnit:getName())
fac.facIRPoints[_facUnitName] = _result.irPoint --store so we can remove after
end
if _result.laserPoint then
-- DEBUG
--env.info('FAC:' .. _facUnitName .. ' is Lasing '.._enemyUnit:getName()..'. CODE:'.._laserCode)
fac.facLaserPoints[_facUnitName] = _result.laserPoint
end
end
else
-- update lase
if _oldLase ~= nil then
_oldLase:setPoint(_enemyVectorUpdated)
end
if _oldIR ~= nil then
_oldIR:setPoint(_enemyVectorUpdated)
end
end
end
-- Find nearest enemy to FAC that isn't blocked by terrain
function fac.findFacNearestVisibleEnemy(_facUnit, _targetType,_distance)
-- DEBUG
--local _facUnitName = _facUnit:getName()
--env.info('FAC:' .. _facUnitName .. ' fac.findFacNearestVisibleEnemy() ')
local _maxDistance = _distance or fac.FAC_maxDistance
local _x = 1
local _i = 1
local _units = nil
local _groupName = nil
local _nearestUnit = nil
local _nearestDistance = _maxDistance
local _enemyGroups
local _enemyStatic
local _facSide = _facUnit:getCoalition()
if _facUnit:getCoalition() == 1 then
_enemyGroups = coalition.getGroups(2, Group.Category.GROUND)
_enemyShips = coalition.getGroups(2, Group.Category.SHIP)
_enemyStatic = coalition.getStaticObjects(2)
else
_enemyGroups = coalition.getGroups(1, Group.Category.GROUND)
_enemyShips = coalition.getGroups(1, Group.Category.SHIP)
_enemyStatic = coalition.getStaticObjects(1)
end
local _facPoint = _facUnit:getPoint()
local _facPosition = _facUnit:getPosition()
local _tempPoint = nil
local _tempPosition = nil
local _tempDist = nil
local dist = {
id = world.VolumeType.SPHERE,
params = {
point = _facPoint,
radius = fac.FAC_maxDistance --max range ARTY
}
}
local findClosest = function(foundItem, val) -- generic search for all scenery
local _unit = foundItem
local foundOutput = nil
local foundObjectPos = nil
local sideCheck = foundItem:getCoalition()
if foundItem:getLife() > 1 and foundItem:inAir() == false then
if sideCheck ~= _facSide then
local samFactor = 1
local _unitPos = _unit:getPoint()
if _unit:hasAttribute("SAM TR") then
samFactor = 0.1
elseif _unit:hasAttribute("IR Guided SAM") then
samFactor = 0.5
elseif _unit:hasAttribute("AA_flak") then
samFactor = 0.7
end
local _tempADist = fac.getDistance(_unitPos,_facPoint)
_tempDist = _tempADist*samFactor
--trigger.action.outText("Found ".._tempDist.." " ..samFactor,10)
local _offsetEnemyPos = { x = _unitPos.x, y = _unitPos.y + 2.0, z = _unitPos.z }
local _offsetFacPos = { x = _facPoint.x, y = _facPoint.y + 2.0, z = _facPoint.z }
if land.isVisible(_offsetEnemyPos, _offsetFacPos) and _unit:isActive() then
local _type = fac.tgtCatType(foundItem)
if _tempADist < fac.FAC_maxDistance then
if _nearestDistance > _tempDist then
_nearestDistance = _tempDist
_nearestUnit = _unit
end
end
end
end
end
end
world.searchObjects(Object.Category.UNIT,dist,findClosest)
-- -- finish this function
-- local _vhpriority = false
-- local _vpriority = false
-- local _thpriority = false
-- local _tpriority = false
-- for _i = 1, #_enemyGroups do
-- if _enemyGroups[_i] ~= nil then
-- _groupName = _enemyGroups[_i]:getName()
-- _units = fac.getGroup(_groupName)
-- if #_units > 0 then
-- for _y = 1, #_units do
-- local _targeted = false
-- local _targetedJTAC = false
-- if not _distance then
-- _targeted = fac.alreadyFacTarget(_facUnit, _units[_x])
-- end
-- -- calc distance
-- _tempPoint = _units[_y]:getPoint()
-- _tempDist = fac.getDistance(_tempPoint, _facPoint)
-- if _tempDist < _maxDistance and _tempDist < _nearestDistance then
-- local _offsetEnemyPos = { x = _tempPoint.x, y = _tempPoint.y + 2.0, z = _tempPoint.z }
-- local _offsetFacPos = { x = _facPoint.x, y = _facPoint.y + 2.0, z = _facPoint.z }
-- -- calc visible
-- if land.isVisible(_offsetEnemyPos, _offsetFacPos) and _targeted == false and _targetedJTAC == false then
-- if (string.match(_units[_y]:getName(), "hpriority") ~= nil) and fac.isVehicle(_units[_y]) then
-- _vhpriority = true
-- elseif (string.match(_units[_y]:getName(), "priority") ~= nil) and fac.isVehicle(_units[_y]) then
-- _vpriority = true
-- elseif (string.match(_units[_y]:getName(), "hpriority") ~= nil) and fac.isInfantry(_units[_y]) then
-- _thpriority = true
-- elseif (string.match(_units[_y]:getName(), "priority") ~= nil) and fac.isInfantry(_units[_y]) then
-- _tpriority = true
-- end
-- end
-- end
-- end
-- end
-- end
-- end
-- for _i = 1, #_enemyGroups do
-- if _enemyGroups[_i] ~= nil then
-- _groupName = _enemyGroups[_i]:getName()
-- _units = fac.getGroup(_groupName)
-- if #_units > 0 then
-- for _x = 1, #_units do
-- --check to see if a FAC has already targeted this unit only if a distance
-- --wasnt passed in
-- local _targeted = false
-- if not _distance then
-- _targeted = fac.alreadyFacTarget(_facUnit, _units[_x])
-- end
-- local _allowedTarget = true
-- if _targetType == "vehicle" and _vhpriority == true then
-- _allowedTarget = (string.match(_units[_x]:getName(), "hpriority") ~= nil) and fac.isVehicle(_units[_x])
-- elseif _targetType == "vehicle" and _vpriority == true then
-- _allowedTarget = (string.match(_units[_x]:getName(), "priority") ~= nil) and fac.isVehicle(_units[_x])
-- elseif _targetType == "vehicle" then
-- _allowedTarget = fac.isVehicle(_units[_x])
-- elseif _targetType == "troop" and _hpriority == true then
-- _allowedTarget = (string.match(_units[_x]:getName(), "hpriority") ~= nil) and fac.isInfantry(_units[_x])
-- elseif _targetType == "troop" and _priority == true then
-- _allowedTarget = (string.match(_units[_x]:getName(), "priority") ~= nil) and fac.isInfantry(_units[_x])
-- elseif _targetType == "troop" then
-- _allowedTarget = fac.isInfantry(_units[_x])
-- elseif _vhpriority == true or _thpriority == true then
-- _allowedTarget = (string.match(_units[_x]:getName(), "hpriority") ~= nil)
-- elseif _vpriority == true or _tpriority == true then
-- _allowedTarget = (string.match(_units[_x]:getName(), "priority") ~= nil)
-- else
-- _allowedTarget = true
-- end
-- if _units[_x]:isActive() == true and _targeted == false and _allowedTarget == true then
-- -- calc distance
-- _tempPoint = _units[_x]:getPoint()
-- _tempDist = fac.getDistance(_tempPoint, _facPoint)
-- if _tempDist < _maxDistance and _tempDist < _nearestDistance then
-- local _offsetEnemyPos = { x = _tempPoint.x, y = _tempPoint.y + 2.0, z = _tempPoint.z }
-- local _offsetFacPos = { x = _facPoint.x, y = _facPoint.y + 2.0, z = _facPoint.z }
-- -- calc visible
-- if land.isVisible(_offsetEnemyPos, _offsetFacPos) then
-- _nearestDistance = _tempDist
-- _nearestUnit = _units[_x]
-- end
-- end
-- end
-- end
-- end
-- end
-- end
if _nearestUnit == nil then
return nil
end
return _nearestUnit
end
-- tests whether the unit is targeted by another FAC
function fac.alreadyFacTarget(_facUnit, _enemyUnit)
for _, _facTarget in pairs(fac.facCurrentTargets) do
if _facTarget.unitId == _enemyUnit:getID() then
-- env.info("FAC: ALREADY TARGET")
return true
end
end
return false
end
function fac.scanForTGToff(_args)
--fac.facManTGT[_args[1]] = {}
local _fac = Unit.getByName(_args[1])
local _side = _fac:getCoalition()
fac.setFacOnStation({_args[1], nil})
--fac.cancelFacLase(_args[1])
--fac.notifyCoalition("Forward Air Controller \"" .. fac.getFacName(_args[1]) .. "\" off-station.", 10, _fac:getCoalition())
end
function fac.scanForTGT (_args)
--trigger.action.outText("scanning ",10)
local _fac = Unit.getByName(_args[1])
local _side = _fac:getCoalition()
local _facPos = _fac:getPoint()
local _facGID = fac.getGroupId(_fac)
local _i
local _j
local _offsetFacPos = { x = _facPos.x, y = _facPos.y + 2.0, z = _facPos.z }
local dist2 = {
id = world.VolumeType.SPHERE,
params = {
point = _fac:getPoint(),
radius = fac.FAC_maxDistance --max range ARTY
}
}
--fac.cancelFacLase(_args[1])
--fac.facCurrentTargets[_args[1]] = nil
--trigger.action.outText("scanning ",10)
fac.facManTGT[_args[1]] = {}
local tempFound = {}
local aaTempFound = {}
local count = 0
local getTGT = function(foundItemM, val) -- generic search for all scenery
--trigger.action.outText("scanning ",10)
--local _checkArty = fac.isArty(foundItem)
--local _tempTGTGroup = foundItem:getGroup()
if _side ~= foundItemM:getCoalition() then -- check for friendly
--if count <6 then
--trigger.action.outText("scanning ",10)
--trigger.action.outText("scanning "..foundItemM:getTypeName(),10)
if foundItemM:inAir() == false and foundItemM:isActive() then
local _tempPoint= foundItemM:getPoint()
local _offsetEnemyPos = { x = _tempPoint.x, y = _tempPoint.y + 2.0, z = _tempPoint.z }
if land.isVisible(_offsetEnemyPos, _offsetFacPos) then
--trigger.action.outText("scanning ",10)
--trigger.action.outText("FOUND "..foundItemM:getTypeName(),10)
if foundItemM:hasAttribute("SAM TR") or foundItemM:hasAttribute("IR Guided SAM") or foundItemM:hasAttribute("AA_flak") then
table.insert(aaTempFound,foundItemM)
else
table.insert(tempFound,foundItemM)
--count = count + 1
end
end
end
--end
--return
end
end
world.searchObjects(Object.Category.UNIT,dist2,getTGT)
for count = 1,10 do
if #aaTempFound >= count then
table.insert(fac.facManTGT[_args[1]],aaTempFound[count])
else
table.insert(fac.facManTGT[_args[1]],tempFound[count])
end
end
local _tgtList = fac.facManTGT[_args[1]]
local count = 0
for _i=1, #_tgtList do
local tgtp = _tgtList[_i]:getPoint()
local _TGTdist = math.sqrt(((tgtp.z-_facPos.z)^2)+((tgtp.x-_facPos.x)^2))
-- local _TGTheading = math.atan((tp.z-_facPos.z)/(tp.x-_facPos.x))
-- if _TGTheading > 0 then
-- _TGTheading= _TGTheading + math.pi
-- end
-- _TGTheadDeg = _TGTheading*180/math.pi
-- if _TGTheadDeg <0 then
-- _TGTheadDeg = _TGTheadDeg +360
-- end
--trigger.action.outText("FOUND "..foundItem:getTypeName(),10)
local dy = tgtp.z-_facPos.z
local dx = tgtp.x-_facPos.x
count = count + 1
local _TGTheading = math.atan(dy/dx)
--correcting for coordinate space
local recceMarkID = timer.getTime()*1000 + count
if dy < 0 then -- dy = -1 90-270
if dx < 0 then --dy/dx = 1 180-270
--trigger.action.outText(_artyInRange:getName().." Firing SW:\n".. (_fireheading/math.pi)*180 .."\n", 300)
_TGTheading= _TGTheading + math.pi
else
--trigger.action.outText(_artyInRange:getName().." Firing SE:\n".. (_fireheading/math.pi)*180 .."\n", 300)
_TGTheading= _TGTheading + math.pi
end
else --dy = 1 270-90
if dx < 0 then --dy/dx = -1 270-0
--trigger.action.outText(_artyInRange:getName().." Firing NW:\n".. (_fireheading/math.pi)*180 .."\n", 300)
_TGTheading= _TGTheading + 2*math.pi
else --dy/dx = 1 0-90
--trigger.action.outText(_artyInRange:getName().." Firing NE:\n".. (_fireheading/math.pi)*180 .."\n", 300)
_TGTheading= _TGTheading
end
end
local _TGTheadDeg =(_TGTheading)/math.pi*180
--math.floor(_TGTdist) .."m/" .. math.floor(_TGTdist*3.28084) .."ft"
trigger.action.outTextForGroup(_facGID,"Target ".._i .. ":" .. _tgtList[_i]:getTypeName() .. " Bearing:" ..math.floor(_TGTheadDeg) .. " Range:"..math.floor(_TGTdist) .."m/" .. math.floor(_TGTdist*3.28084) .."ft",30)
trigger.action.markToGroup(recceMarkID,"Target ".._i ..":".. _tgtList[_i]:getTypeName(), _tgtList[_i]:getPoint(),_facGID,false,"")
timer.scheduleFunction(removeSetMark,{recceMarkID},timer.getTime() + 60)
end
end
function fac.setManualTgt(_args)
fac.cancelFacLase(_args[1])
local _fac = Unit.getByName(_args[1])
local _facGID = fac.getGroupId(_fac)
fac.notifyCoalition("[Forward Air Controller \"" .. fac.getFacName(_args[1]) .. "\" starting manual laze. Reseting to new target]", 10, _fac:getCoalition())
--fac.facCurrentTargets[_args[1]] = nil
fac.setFacOnStation({_args[1],true})
--fac.cancelFacLase(_args[1])
--fac.facCurrentTargets[_facUnitName]
--fac.facCurrentTargets[_args[1]] = nil
--fac.facUnits[_facUnitName] = nil
local _tgtList = fac.facManTGT[_args[1]]
if _tgtList == nil then
trigger.action.outTextForGroup(_facGID,"Error loading tgts, please reset FAC",10)
return
end
local _enemyUnit = _tgtList[_args[2]]
fac.facCurrentTargets[_args[1]] = { name = _enemyUnit:getName(), unitType = _enemyUnit:getTypeName(), unitId = _enemyUnit:getID() }
local _lock = "all"
if _fac:getCoalition() == 1 then
local _colour = fac.FAC_smokeColour_RED
local _smoke = fac.FAC_smokeOn_RED
else
local _colour = fac.FAC_smokeColour_BLUE
local _smoke = fac.FAC_smokeOn_BLUE
end
if fac.markerTypeColor[_args[1]] ~= nil then
_colour = fac.markerTypeColor[_args[1]]
end
local _laserCode = fac.facLaserPointCodes[_args[1]]
if _laserCode == nil then
fac.setFacLaserCode({_args[1], fac.FAC_laser_codes[1]})
_laserCode = fac.facLaserPointCodes[_args[1]]+1
end
--local _facGroup =
trigger.action.outTextForGroup(_facGID,"Designating Target ".._args[2]..": ".._tgtList[_args[2]]:getTypeName(),10)
if _args[2] > #_tgtList then
trigger.action.outTextForGroup(_facGID,"Invalid Target",10)
elseif _tgtList[_args[2]]:getLife() > 0 then
fac.notifyCoalition("[Forward Air Controller \"" .. fac.getFacName(_args[1]) .. "\" starting manual laze.]", 10, _fac:getCoalition())
fac.facAutoLase(_args[1], _laserCode, _smoke, _lock, _colour)
fac.createSmokeMarker(_tgtList[_args[2]], _colour,_args[1])
--fac.facLaseUnit(_tgtList[_args[2]], _fac, _args[1], _laserCode)
-- --fac.notifyCoalition(fac.getFacName(_args[1]) .. " lasing new target " .. _tgtList[_args[2]]:getTypeName() .. '. CODE: ' .. _laserCode .. fac.getFacPositionString(_tgtList[_args[2]]), 10, _fac:getCoalition())
-- --trigger.action.outTextForGroup(_facGID,"Designated Target ".._args[2].. " " .. _tgtList[_args[2]]:getTypeName().." for attack",10)
-- if _smoke == true then
-- local _nextSmokeTime = fac.facSmokeMarks[_tgtList[_args[2]]:getName()]
-- --recreate smoke marker after 5 mins
-- if _nextSmokeTime ~= nil and _nextSmokeTime < timer.getTime() then
-- fac.createSmokeMarker(_tgtList[_args[2]], _colour,_args[1])
-- end
-- end
trigger.action.outTextForGroup(_facGID,"Designated Target ".._args[2].. " " .. _tgtList[_args[2]]:getTypeName().." for attack",10)
elseif _tgtList[_args[2]]:getLife() < 1 then
trigger.action.outTextForGroup(_facGID,"Designated Target ".._args[2].. " " .. _tgtList[_args[2]]:getTypeName().." is already dead",10)
end
end
-- Adds menuitem to all FAC units that are active
function fac.addFacF10MenuOptions()
-- Loop through all FAC units
timer.scheduleFunction(fac.addFacF10MenuOptions, nil, timer.getTime() + 10)
for _, _facUnitName in pairs(fac.facPilotNames) do
local status, error = pcall(function()
local _unit = fac.getFacUnit(_facUnitName)
if _unit ~= nil then
local _groupId = fac.getGroupId(_unit)
if _groupId then
if fac.facAddedTo[tostring(_groupId)] == nil then
local _rootPath = missionCommands.addSubMenuForGroup(_groupId, "FAC")
local _TGTModePath = missionCommands.addSubMenuForGroup(_groupId, "Targeting Mode",_rootPath)
local _AutoTGTModePath = missionCommands.addSubMenuForGroup(_groupId, "Auto Mode",_TGTModePath)
local _ManTGTModePath = missionCommands.addSubMenuForGroup(_groupId, "Manual Mode",_TGTModePath)
missionCommands.addCommandForGroup(_groupId, "Auto Laze On", _AutoTGTModePath, fac.setFacOnStation, { _facUnitName, true})
missionCommands.addCommandForGroup(_groupId, "Auto Laze Off", _AutoTGTModePath, fac.setFacOnStation, { _facUnitName, nil})
missionCommands.addCommandForGroup(_groupId, "Scan for Close Targets", _ManTGTModePath, fac.scanForTGT, { _facUnitName})
missionCommands.addCommandForGroup(_groupId, "Stop Manual Designating", _ManTGTModePath, fac.scanForTGToff, { _facUnitName})
local _TGTSelectPath = missionCommands.addSubMenuForGroup(_groupId, "Select Found Target",_ManTGTModePath)
missionCommands.addCommandForGroup(_groupId, "Target 1", _TGTSelectPath, fac.setManualTgt, { _facUnitName, 1})
missionCommands.addCommandForGroup(_groupId, "Target 2", _TGTSelectPath, fac.setManualTgt, { _facUnitName, 2})
missionCommands.addCommandForGroup(_groupId, "Target 3", _TGTSelectPath, fac.setManualTgt, { _facUnitName, 3})
missionCommands.addCommandForGroup(_groupId, "Target 4", _TGTSelectPath, fac.setManualTgt, { _facUnitName, 4})
missionCommands.addCommandForGroup(_groupId, "Target 5", _TGTSelectPath, fac.setManualTgt, { _facUnitName, 5})
missionCommands.addCommandForGroup(_groupId, "Target 6", _TGTSelectPath, fac.setManualTgt, { _facUnitName, 6})
missionCommands.addCommandForGroup(_groupId, "Target 7", _TGTSelectPath, fac.setManualTgt, { _facUnitName, 7})
missionCommands.addCommandForGroup(_groupId, "Target 8", _TGTSelectPath, fac.setManualTgt, { _facUnitName, 8})
missionCommands.addCommandForGroup(_groupId, "Target 9", _TGTSelectPath, fac.setManualTgt, { _facUnitName, 9})
missionCommands.addCommandForGroup(_groupId, "Target 10", _TGTSelectPath, fac.setManualTgt, { _facUnitName, 10})
missionCommands.addCommandForGroup(_groupId, "Call artillery strikes on all manual targets", _ManTGTModePath, fac.multiStrike, { _facUnitName})
-- add each possible laser code as a menu option
local _lzrpath = missionCommands.addSubMenuForGroup(_groupId, "Avaliable Laser Codes",_rootPath)
for _, _laserCode in pairs(fac.FAC_laser_codes) do
missionCommands.addCommandForGroup(_groupId, string.format("Laser code: %s", _laserCode), _lzrpath, fac.setFacLaserCode, { _facUnitName, _laserCode})
end
local _lzerCustPath = missionCommands.addSubMenuForGroup(_groupId, "Custom Laser Code", _lzrpath)
local _digCount
local _lzerCode1Path = missionCommands.addSubMenuForGroup(_groupId, "Digit 1", _lzerCustPath)
for _digCount = 1 ,1 do
missionCommands.addCommandForGroup(_groupId, _digCount, _lzerCode1Path, fac.setCustCode, { _facUnitName, 1,_digCount})
end
local _lzerCode2Path = missionCommands.addSubMenuForGroup(_groupId, "Digit 2", _lzerCustPath)
for _digCount = 1 ,6 do
missionCommands.addCommandForGroup(_groupId, _digCount, _lzerCode2Path, fac.setCustCode, { _facUnitName, 2,_digCount})
end
local _lzerCode3Path = missionCommands.addSubMenuForGroup(_groupId, "Digit 3", _lzerCustPath)
for _digCount = 1 ,8 do
missionCommands.addCommandForGroup(_groupId, _digCount, _lzerCode3Path, fac.setCustCode, { _facUnitName, 3,_digCount})
end
local _lzerCode4Path = missionCommands.addSubMenuForGroup(_groupId, "Digit 4", _lzerCustPath)
for _digCount = 1 ,8 do
missionCommands.addCommandForGroup(_groupId, _digCount, _lzerCode4Path, fac.setCustCode, { _facUnitName, 4,_digCount})
end
local _artyPath = missionCommands.addSubMenuForGroup(_groupId, "Artillery Control",_rootPath)
missionCommands.addCommandForGroup(_groupId, "Check Avaliable Arty Groups", _artyPath , fac.checkTask, {_facUnitName})
missionCommands.addCommandForGroup(_groupId, "Call Artillery Fire Mission", _artyPath , fac.callFireMission, { _facUnitName,fac.fireMissionRounds,0})
missionCommands.addCommandForGroup(_groupId, "Call Illummination", _artyPath , fac.callFireMission, { _facUnitName,fac.fireMissionRounds,1})
missionCommands.addCommandForGroup(_groupId, "Call Mortar Strike Only(Anti-infantry)", _artyPath , fac.callFireMission, { _facUnitName,fac.fireMissionRounds,2})
missionCommands.addCommandForGroup(_groupId, "Call Heavy Artillery Strike Only <No Smart Munition> (Anti-Material)", _artyPath , fac.callFireMission, { _facUnitName,10,3})
local _cMissilePath = missionCommands.addSubMenuForGroup(_groupId, "Air Strike Menu",_artyPath)
missionCommands.addCommandForGroup(_groupId, "Single Target", _cMissilePath , fac.callFireMission, { _facUnitName,1,4})
missionCommands.addCommandForGroup(_groupId, "Multi Target (Manual Targeting Required), GPS weapons only", _cMissilePath , fac.callFireMissionMulti, { _facUnitName,1,4})
missionCommands.addCommandForGroup(_groupId, "Carpet Bomb (Turn your aircraft to desired attack azimuth), Dumb bombs only", _cMissilePath , fac.callFireMissionCarpet, { _facUnitName,1,5})
local _mkrpath = missionCommands.addSubMenuForGroup(_groupId, "Marker Type",_rootPath)
local _colorSmokePath = missionCommands.addSubMenuForGroup(_groupId, "Smoke",_mkrpath)
missionCommands.addCommandForGroup(_groupId, "GREEN", _colorSmokePath, fac.setMarkerColor, { _facUnitName, "SMOKE",0})
missionCommands.addCommandForGroup(_groupId, "RED", _colorSmokePath , fac.setMarkerColor, { _facUnitName, "SMOKE",1})
missionCommands.addCommandForGroup(_groupId, "ORANGE", _colorSmokePath , fac.setMarkerColor, { _facUnitName, "SMOKE",3})
missionCommands.addCommandForGroup(_groupId, "BLUE", _colorSmokePath , fac.setMarkerColor, { _facUnitName, "SMOKE",4})
missionCommands.addCommandForGroup(_groupId, "WHITE", _colorSmokePath , fac.setMarkerColor, { _facUnitName, "SMOKE",2})
local _colorFlarePath = missionCommands.addSubMenuForGroup(_groupId, "FLARES",_mkrpath)
missionCommands.addCommandForGroup(_groupId, "GREEN", _colorFlarePath, fac.setMarkerColor, { _facUnitName, "FLARES",0})
missionCommands.addCommandForGroup(_groupId, "WHITE", _colorFlarePath , fac.setMarkerColor, { _facUnitName, "FLARES",2})
missionCommands.addCommandForGroup(_groupId, "ORANGE", _colorFlarePath , fac.setMarkerColor, { _facUnitName, "FLARES",3})
--missionCommands.addCommandForGroup(_groupId, "Smoke", _mkrpath, fac.setMarkerType, { _facUnitName, "SMOKE"})
--missionCommands.addCommandForGroup(_groupId, "FLARES", _mkrpath , fac.setMarkerType, { _facUnitName, "FLARES"})
missionCommands.addCommandForGroup(_groupId, "Map Marker", _mkrpath , fac.setMapMarker, { _facUnitName})
--missionCommands.addCommandForGroup(_groupId, "RECCE", _mkrpath , fac.recceDetect, { _facUnitName})
fac.facAddedTo[tostring(_groupId)] = true
end
end
--[[else
env.info(string.format("FAC DEBUG: unit nil %s",_facUnitName)) ]]
end
end)
end
for _, _facUnitName in pairs(fac.reccePilotNames) do
local status, error = pcall(function()
local _unitR = fac.getFacUnit(_facUnitName)
if _unitR ~= nil then
local _groupIdR = fac.getGroupId(_unitR)
if _groupIdR then
if fac.RECCEAddedTo[tostring(_groupIdR)] == nil then
local _rootPathR = missionCommands.addSubMenuForGroup(_groupIdR, "RECCE")
missionCommands.addCommandForGroup(_groupIdR, "RECCE", _rootPathR, fac.recceDetect, { _facUnitName})
missionCommands.addCommandForGroup(_groupIdR, "Strategic Strike", _rootPathR, fac.stratStrike, { _facUnitName})
--missionCommands.addCommandForGroup(_groupId, "Go Off-Station", _rootPathR, fac.recceDetect, { _facUnitName, nil})
fac.RECCEAddedTo[tostring(_groupIdR)] = true
end
end
--[[else
env.info(string.format("FAC DEBUG: unit nil %s",_facUnitName)) ]]
end
end)
if (not status) then
env.error(string.format("Error adding f10 to RECCE: %s", error), false)
end
end
local status, error = pcall(function()
-- now do any player controlled aircraft that ARENT FAC units
if fac.FAC_FACStatusF10 then
-- get all BLUE players
fac.addFacRadioCommand(2)
-- get all RED players
fac.addFacRadioCommand(1)
end
end)
if (not status) then
env.error(string.format("Error adding f10 to other players: %s", error), false)
end
end
function fac.setMapMarker(_args)
local _facUnitName = _args[1]
local _unit = nil
local _facUnit = fac.getFacUnit(_facUnitName)
local _groupId = fac.getGroupId(_facUnit)
local tgtMarkID = timer.getTime()*1000
local recceMarkID = timer.getTime()*1000
trigger.action.outTextForGroup(_groupId,"Processing Mark",10)
if fac.facCurrentTargets[_facUnitName] ~= nil then
_unit = Unit.getByName(fac.facCurrentTargets[_facUnitName].name)
tempMarkID = tgtMarkID
--fac.markID = tgtMarkID + 1
elseif _args[2] ~= nil then
_unit = _args[2]
tempMarkID = recceMarkID
--fac.recceID = recceMarkID + 1
else
trigger.action.outTextForGroup(_groupId,"No Target to Mark",10)
end
if _unit ~=nil and _unit:isActive() then
local _vel = _unit:getVelocity()
local _spd = 0
if _vel ~=nil then
_spd = math.sqrt(_vel.x^2+_vel.z^2)
end
local unitPos = _unit:getPoint()
local _lat, _lon = coord.LOtoLL(_unit:getPosition().p)
local _latLngStr = mist.tostringLL(_lat, _lon, 3, false)
local _latLngSecStr = mist.tostringLL(_lat, _lon, 3, true)
local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_unit:getPosition().p)), 5)
trigger.action.markToCoalition(tgtMarkID,_unit:getTypeName().. " - DMS: " .. _latLngSecStr .." Altitude: ".. math.floor(unitPos.y) .."m/" .. math.floor(unitPos.y*3.28084) .."ft".. "\nHeading: ".. math.floor(getHeading(_unit) * 180/math.pi) .. "\nSpeed: " .. math.floor(_spd*2) .. " MPH" .."\nSpotted by: " .. fac.getFacName(_facUnitName), _unit:getPoint(), _facUnit:getCoalition(),false,fac.getFacName(_facUnitName).." marked a target")
timer.scheduleFunction(removeSetMark,{tgtMarkID},timer.getTime() + 300)
end
end
function fac.setMarkerColor(_args)
local _facUnitName = _args[1]
local _mkrType = _args[2]
fac.cancelFacLase(_facUnitName)
fac.setFacOnStation({_args[1],nil})
fac.markerType[_facUnitName] = _mkrType
fac.markerTypeColor[_facUnitName] = _args[3]
fac.setMarkerType(_args)
fac.setFacOnStation({_args[1],true})
end
function fac.setMarkerType(_args)
local _facUnitName = _args[1]
local _mkrType = _args[2]
local _facUnit = fac.getFacUnit(_facUnitName)
local _groupId = fac.getGroupId(_facUnit)
local colorString = { ["0"] = "GREEN" ,["1"] = "RED" ,["2"] = "WHITE", ["3"] = "ORANGE" ,["4"] = "BLUE"}
local _mkrColor
local _facUnit = Unit.getByName(_facUnitName)
local side = _facUnit:getCoalition()
if side == 1 then
_mkrColor = fac.FAC_smokeColour_BLUE
elseif side == 2 then
_mkrColor = fac.FAC_smokeColour_RED
else
_mkrColor = 2
end
if _args[3]~=nil then
_mkrColor = _args[3]
end
if _facUnit == nil then
--env.info('FAC DEBUG: fac.setFacLaserCode() _facUnit is null, aborting.')
return
end
fac.markerTypeColor[_facUnitName] = _args[3]
fac.markerType[_facUnitName] = _mkrType
if fac.facOnStation[_facUnitName] == true then
fac.notifyCoalition("[Forward Air Controller \"" .. fac.getFacName(_facUnitName) .. "\" on-station marking with: "..colorString[tostring(_mkrColor)].." "..fac.markerType[_facUnitName]..".]", 10, _facUnit:getCoalition())
else
trigger.action.outTextForGroup(_groupId,"Marker set to ".. colorString[tostring(_mkrColor)].." "..fac.markerType[_facUnitName],10)
end
--fac.setFacOnStation({ _facUnitName, nil})
--fac.setFacOnStation( {_facUnitName, true})
end
function fac.addFacRadioCommand(_side)
local _players = coalition.getPlayers(_side)
if _players ~= nil then
for _, _playerUnit in pairs(_players) do
local _groupId = fac.getGroupId(_playerUnit)
if _groupId then
-- env.info("adding command for "..index)
if fac.facRadioAdded[tostring(_groupId)] == nil then
-- env.info("about command for "..index)
missionCommands.addCommandForGroup(_groupId, "FAC Status", nil, fac.getFacStatus, { _playerUnit:getName() })
fac.facRadioAdded[tostring(_groupId)] = true
-- env.info("Added command for " .. index)
end
end
end
end
end
function fac.setFacLaserCode(_args)
local _facUnitName = _args[1]
local _laserCode = _args[2]
local _facUnit = fac.getFacUnit(_facUnitName)
--fac.setFacOnStation( {_facUnitName, nil})
--fac.setFacOnStation( {_facUnitName, true})
if _facUnit == nil then
--env.info('FAC DEBUG: fac.setFacLaserCode() _facUnit is null, aborting.')
return
end
fac.facLaserPointCodes[_facUnitName] = _laserCode
if fac.facOnStation[_facUnitName] == true then
fac.notifyCoalition("[Forward Air Controller \"" .. fac.getFacName(_facUnitName) .. "\" on-station using CODE: "..fac.facLaserPointCodes[_facUnitName]..".]", 10, _facUnit:getCoalition())
end
end
function fac.setCustCode(_args)
local _facUnitName = _args[1]
local _facUnit = fac.getFacUnit(_facUnitName)
if fac.facLaserPointCodes[_facUnitName] == nil then
fac.facLaserPointCodes[_facUnitName] = "1688"
end
local tempCode = fac.facLaserPointCodes[_facUnitName]
tempCode = fac.replace_char(_args[2],tempCode,_args[3])
fac.facLaserPointCodes[_facUnitName] = tempCode
fac.notifyCoalition("[Forward Air Controller \"" .. fac.getFacName(_facUnitName) .. "\" on-station using CODE: "..fac.facLaserPointCodes[_facUnitName]..".]", 10, _facUnit:getCoalition())
end
function fac.replace_char(pos, str, r)
return str:sub(1, pos-1) .. r .. str:sub(pos+1)
end
function fac.setFacOnStation(_args)
local _facUnitName = _args[1]
local _onStation = _args[2]
local _facUnit = fac.getFacUnit(_facUnitName)
local colorString = { ["0"] = "GREEN" ,["1"] = "RED" ,["2"] = "WHITE", ["3"] = "ORANGE" ,["4"] = "BLUE"}
local _mkrColor = tostring(fac.markerTypeColor[_facUnitName])
-- going on-station
if _facUnit == nil then
--env.info('FAC DEBUG: fac.setFacOnStation() _facUnit is null, aborting.')
return
end
if fac.facLaserPointCodes[_facUnitName] == nil then
-- set default laser code
--env.info('FAC: ' .. _facUnitName .. ' no laser code, assigning default ' .. fac.FAC_laser_codes[1])
fac.setFacLaserCode( {_facUnitName, fac.FAC_laser_codes[1]} )
end
-- going on-station from off-station
if fac.facOnStation[_facUnitName] == nil and _onStation == true then
env.info('FAC: ' .. _facUnitName .. ' going on-station')
fac.cancelFacLase(_facUnitName)
--fac.scanForTGToff({_facUnitName})
fac.notifyCoalition("[Forward Air Controller \"" .. fac.getFacName(_facUnitName) .. "\" on-station using CODE: "..fac.facLaserPointCodes[_facUnitName]..".]", 10, _facUnit:getCoalition())
fac.setFacLaserCode( {_facUnitName, fac.facLaserPointCodes[_facUnitName]} )
end
-- going off-station from on-station
if fac.facOnStation[_facUnitName] == true and _onStation == nil then
env.info('FAC: ' .. _facUnitName .. ' going off-station')
fac.notifyCoalition("[Forward Air Controller \"" .. fac.getFacName(_facUnitName) .. "\" off-station.]", 10, _facUnit:getCoalition())
fac.cancelFacLase(_facUnitName)
fac.facUnits[_facUnitName] = nil
--fac.scanForTGToff({_facUnitName})
end
fac.facOnStation[_facUnitName] = _onStation
end
--get distance in meters assuming a Flat world
function fac.getDistance(_point1, _point2)
local xUnit = _point1.x
local yUnit = _point1.z
local xZone = _point2.x
local yZone = _point2.z
local xDiff = xUnit - xZone
local yDiff = yUnit - yZone
return math.sqrt(xDiff * xDiff + yDiff * yDiff)
end
function fac.notifyCoalition(_message, _displayFor, _side)
trigger.action.outTextForCoalition(_side, _message, _displayFor)
trigger.action.outSoundForCoalition(_side, "radiobeep.ogg")
end
-- Returns only alive units from group but the group / unit may not be active
function fac.getGroup(groupName)
local _groupUnits = Group.getByName(groupName)
local _filteredUnits = {} --contains alive units
local _x = 1
if _groupUnits ~= nil and _groupUnits:isExist() then
_groupUnits = _groupUnits:getUnits()
if _groupUnits ~= nil and #_groupUnits > 0 then
for _x = 1, #_groupUnits do
if _groupUnits[_x]:getLife() > 0 then -- removed and _groupUnits[_x]:isExist() as isExist doesnt work on single units!
table.insert(_filteredUnits, _groupUnits[_x])
end
end
end
end
return _filteredUnits
end
function fac.isInfantry(_unit)
local _typeName = _unit:getTypeName()
--type coerce tostring
_typeName = string.lower(_typeName .. "")
local _soldierType = { "infantry", "paratrooper", "stinger", "manpad", "mortar" }
for _key, _value in pairs(_soldierType) do
if string.match(_typeName, _value) then
return true
end
end
return false
end
function fac.isArty(_Aunit)
local _typeName = _Aunit:getTypeName()
-- --type coerce tostring
-- _typeName = string.lower(_typeName .. "")
--trigger.action.outText( _typeName.." found",10)
-- local _artyType = { "mrls", "sph", "mortar" }
-- for _key, _value in pairs(_artyType) do
-- if string.match(_typeName, _value) then
-- return true
-- end
-- end
if _Aunit:hasAttribute('Artillery') or _Aunit:hasAttribute('MLRS') or _Aunit:hasAttribute('Strategic bombers') or _Aunit:hasAttribute('Bombers') then
return true
else
return false
end
end
-- assume anything that isnt soldier is vehicle
function fac.isVehicle(_unit)
if fac.isInfantry(_unit) then
return false
end
return true
end
-- copied from CTLD
function fac.getGroupId(_unit)
local _unitDB = mist.DBs.unitsById[tonumber(_unit:getID())]
if _unitDB ~= nil and _unitDB.groupId then
return _unitDB.groupId
end
return nil
end
function fac.createSmokeMarker(_enemyUnit, _colour,_facUnitName)
--recreate in 5 mins
if fac.markerType[_facUnitName] == "SMOKE" then
fac.facSmokeMarks[_enemyUnit:getName()] = timer.getTime() + 300.0
else
fac.facSmokeMarks[_enemyUnit:getName()] = timer.getTime() + 2
end
-- move smoke 2 meters above target for ease
local _enemyPoint = _enemyUnit:getPoint()
if fac.markerType[_facUnitName] == "SMOKE" then
trigger.action.smoke({ x = _enemyPoint.x, y = _enemyPoint.y + 2.0, z = _enemyPoint.z }, _colour)
else
trigger.action.signalFlare({ x = _enemyPoint.x, y = _enemyPoint.y + 2.0, z = _enemyPoint.z }, _colour,0)
end
end
--fire mission control
-- function fac.checkArty(_side, _artyTable)
-- --trigger.action.outText( "Found " ..#_artyTable.." arty units".._side,10)
-- local _artyIDX = 1
-- local _artyGroups = coalition.getGroups(_side, Group.Category.GROUND)
-- if #_artyGroups == 0 then
-- trigger.action.outText( "ERROR no Units",10)
-- --_artyGroups = coalition.getGroups(_side, Group.Category.GROUND)
-- elseif _artyGroups[1] == nil then
-- trigger.action.outText( "ERROR ground returned Nil",10)
-- end
-- for _artyIDX = 1, #_artyGroups do
-- local _artyunits = _artyGroups[_artyIDX]:getUnits()
-- if _artyunits[1] ~= nil then
-- if fac.isArty(_artyunits[1]) == true then
-- if contains(_artyTable,_artyGroups[_artyIDX]:getName()) == false then
-- table.insert(_artyTable,_artyGroups[_artyIDX]:getName())
-- --trigger.action.outText( "Found " ..#_artyTable.." arty units out of" ..#_artyGroups ,10)
-- end
-- end
-- end
-- end
-- end
function getHeading(unit)
local unitpos = unit:getPosition()
if unitpos then
local Heading = math.atan2(unitpos.x.z, unitpos.x.x)
Heading = Heading + getNorthCorrection(unitpos.p)
if Heading < 0 then
Heading = Heading + 2*math.pi -- put heading in range of 0 to 2*pi
end
return Heading
end
end
getNorthCorrection = function(point) --gets the correction needed for true north
if not point.z then --Vec2; convert to Vec3
point.z = point.y
point.y = 0
end
local lat, lon = coord.LOtoLL(point)
local north_posit = coord.LLtoLO(lat + 1, lon)
return math.atan2(north_posit.z - point.z, north_posit.x - point.x)
end
function fac.facOffsetMaker(_fac)
local _facOffset = {}
local angle = getHeading(_fac)
local xofs = math.cos(angle) * fac.facOffsetDist
local yofs = math.sin(angle) * fac.facOffsetDist
local _facPoint = _fac:getPoint()
_facOffset.x = _facPoint.x + xofs
_facOffset.y = _facPoint.y
_facOffset.z = _facPoint.z +yofs
return _facOffset
end
function fac.getArty(_enemyUnit,_fac,_artyType)
local _attackPoint = {}
if _enemyUnit == nil then
_attackPoint = fac.facOffsetMaker(_fac)
else
_attackPoint = _enemyUnit:getPoint()
end
local _i = 1
local _j = 1
--local _gndGroups = {}
local _tempPoint = nil
local _tempDist = nil
local _tempPosition = nil
local _lastArty = {ammo = 0, group = {}}
local _chosenArty = nil
local _tempArty = {}
local _tempList = {}
local _artyGroups = {}
local side = _fac:getCoalition()
local lastAmmo = 0
-- if overide == 2 then
-- local retask = true
-- --trigger.action.outText("retask",10)
-- else
-- local retask = false
-- end
-- finds all avaliable arty and chooses any untasked arty for firemission
-- NEW arty check
local dist = {
id = world.VolumeType.SPHERE,
params = {
point = _attackPoint,
radius = 4600000 --max range ARTY
}
}
local tempFound = {}
local _foundArty = {}
local getArty = function(foundItem, val) -- generic search for all scenery
local _checkArty = fac.isArty(foundItem)
local _tempArtyGroup = foundItem:getGroup()
local _tGC = _tempArtyGroup:getController()
if side == foundItem:getCoalition() and foundItem:getPlayerName() == nil then -- check for friendly and not a player
if contains(tempFound,_tempArtyGroup:getName()) == false then --check for redundant groups
if _checkArty == true then
--trigger.action.outText(foundItem:getTypeName(),10)
--if _tGC:hasTask() == false then -- check for tasked arty
--if contains(fac.ArtyTasked,_tempArtyGroup:getName()) == false then -- check for tasked arty
if fac.ArtyTasked[_tempArtyGroup:getName()] == nil then
fac.ArtyTasked[_tempArtyGroup:getName()] = { name = _tempArtyGroup:getName(), tasked = 0, timeTasked = nil,tgt = nil}
end
--trigger.action.outText(_tempArtyGroup:getName() .. " " .. fac.ArtyTasked[_tempArtyGroup:getName()].tasked,10)
if fac.ArtyTasked[_tempArtyGroup:getName()].tasked == 0 then
_tempPoint = foundItem:getPoint()
_tempDist = fac.getDistance(_tempPoint, _attackPoint)
--foundItem
--trigger.action.outText(_tempArtyGroup:getName() .. " " .. fac.ArtyTasked[_tempArtyGroup:getName()].tasked,10)
local _type = Unit.getTypeName(foundItem)
if fac.artyGetAmmo(_tempArtyGroup:getUnits()) > 0 and foundItem:isActive() == true then
--trigger.action.outText(_tempDist.. " "..fac.ArtilleryProperties[_type].minrange .. " " .. fac.ArtilleryProperties[_type].maxrange,10)
if foundItem:hasAttribute('Strategic bombers') or foundItem:hasAttribute('Bombers') then
--trigger.action.outText(foundItem:getTypeName().." "..fac.artyGetAmmo(_tempArtyGroup:getUnits()),10)
table.insert(tempFound,_tempArtyGroup:getName())
elseif (_tempDist > fac.ArtilleryProperties[_type].minrange and _tempDist < fac.ArtilleryProperties[_type].maxrange) then --check if in arty params
--trigger.action.outText(foundItem:getTypeName().." "..fac.artyGetAmmo(_tempArtyGroup:getUnits()),10)
-- check for ammo and if arty is active
--if foundItem:getLife() > 1 then
table.insert(tempFound,_tempArtyGroup:getName())
--end
end
end
end
end
end
end
end
world.searchObjects(Object.Category.UNIT,dist,getArty)
local _artyFilter = "2B11 mortar"
if _artyType == 2 then
--trigger.action.outText(Group.getByName(tempFound[_i]):getUnit(1):getTypeName().. " ".._artyFilter,10)
for _i = 1, #tempFound do
if Group.getByName(tempFound[_i]):getUnit(1):getTypeName() == _artyFilter then
table.insert(_foundArty,tempFound[_i])
end
end
elseif _artyType == 3 then
for _i = 1, #tempFound do
--trigger.action.outText(Group.getByName(tempFound[_i]):getUnit(1):getTypeName() .." ".._artyFilter,10)
if Group.getByName(tempFound[_i]):getUnit(1):getTypeName() ~= _artyFilter and (not Group.getByName(tempFound[_i]):getUnit(1):hasAttribute('MissilesSS')) then
local payloadCheck = Group.getByName(tempFound[_i]):getUnit(1):getAmmo()
if payloadCheck[1].desc.guidance ~= nil then
--trigger.action.outText(Group.getByName(tempFound[_i]):getUnit(1):getTypeName() .. " " .. payloadCheck[1].desc.guidance,10)
else
--trigger.action.outText(Group.getByName(tempFound[_i]):getUnit(1):getTypeName() .. " unguided bombs",10)
table.insert(_foundArty,tempFound[_i])
end
end
end
elseif _artyType == 5 then
for _i = 1, #tempFound do
if Group.getByName(tempFound[_i]):getUnit(1):hasAttribute('Strategic bombers') or Group.getByName(tempFound[_i]):getUnit(1):hasAttribute('Bombers') then
--trigger.action.outText(Group.getByName(tempFound[_i]):getUnit(1):getTypeName() .." ".._artyFilter,10)
local payloadCheck = Group.getByName(tempFound[_i]):getUnit(1):getAmmo()
if payloadCheck[1].desc.guidance == 1 then
--trigger.action.outText(Group.getByName(tempFound[_i]):getUnit(1):getTypeName() .. " " .. payloadCheck[1].desc.guidance,10)
else
--trigger.action.outText(Group.getByName(tempFound[_i]):getUnit(1):getTypeName() .. " unguided bombs",10)
table.insert(_foundArty,tempFound[_i])
end
end
end
elseif _artyType == 4 then
for _i = 1, #tempFound do
--trigger.action.outText(Group.getByName(tempFound[_i]):getUnit(1):getTypeName() .." ".._artyFilter,10)
if Group.getByName(tempFound[_i]):getUnit(1):hasAttribute('Strategic bombers') or Group.getByName(tempFound[_i]):getUnit(1):hasAttribute('Bombers') then
local payloadCheck = Group.getByName(tempFound[_i]):getUnit(1):getAmmo()
if payloadCheck[1].desc.guidance == 1 then
table.insert(_foundArty,tempFound[_i])
--trigger.action.outText(Group.getByName(tempFound[_i]):getUnit(1):getTypeName() .. " " .. payloadCheck[1].desc.guidance,10)
else
--trigger.action.outText(Group.getByName(tempFound[_i]):getUnit(1):getTypeName() .. " unguided bombs",10)
end
end
end
else
_foundArty = tempFound
end
for _j = 1, #_foundArty do -- find arty with most ammo
--trigger.action.outText(_foundArty[_j] .. " unguided bombs",10)
--trigger.action.outText(#Group.getByName(_foundArty[_j]):getUnits().. " unguided bombs",10)
local curAmmo = fac.artyGetAmmo(Group.getByName(_foundArty[_j]):getUnits())
if curAmmo > lastAmmo then
_chosenArty = _foundArty[_j]
end
end
if _chosenArty ~=nil then
return Group.getByName(_chosenArty)
-- if retask == true then
-- trigger.action.outTextForCoalition(side,fac.getFacName(_fac:getName()) .. " is re-tasking ".. _chosenArty .. "(".. Group.getByName(tempFound[_j]):getUnit(1) ..") to a new target",10)
-- end
else
return nil
end
end
function fac.artyGetAmmo(_units)
local BatteryAmmo = 0
local n
for n = 1, #_units do --Iterate through all units of the battery
--if _units[n]:getTypeName() == groupType then --Check if a unit is of the same type as the battery
local ammo = _units[n]:getAmmo() --Get ammo for this unit
if ammo ~= nil then
if ammo[1] then --Check if ammo[1] exists. If the ammo is used up, it returns nil...
local UnitAmmo = ammo[1].count --Get the shell count for this unit
--if UnitAmmo > minAmmo then --Check if there is ready ammo left
local UnitReadyAmmo = UnitAmmo
BatteryAmmo = BatteryAmmo + UnitReadyAmmo --Add unit ready ammo to total of group
--end
end
end
--end
end
return BatteryAmmo
end
function fac.purgeArtList(_args)
-- local element = fac.ArtyTasked[_args[1]]
-- local tempTbl = {}
--local I = 1
-- fac.ArtyTasked[_args[1]] = nil
-- for I = 1, #fac.ArtyTasked do
-- if(fac.ArtyTasked[I] ~= nil) then
-- table.insert(tempTbl, fac.ArtyTasked[I])
-- end
-- end
-- fac.ArtyTasked = {}
-- trigger.action.outText("clear" .. #fac.ArtyTasked,10)
-- fac.ArtyTasked = tempTbl
-- trigger.action.outText(_args[1].."clearing " .. #fac.ArtyTasked,10)
-- local test, outIdx = contains(fac.ArtyTasked,_args[1])
-- trigger.action.outText(_args[1].."clearing " .. fac.ArtyTasked[outIdx],10)
-- if test == true and fac.ArtyTasked[outIdx] == _args[1] then
-- table.remove(fac.ArtyTasked,outIdx)
-- trigger.action.outText(_args[1].." cleared " .. #fac.ArtyTasked,10)
-- elseif test == true and fac.ArtyTasked[outIdx] ~= _args[1] then
-- trigger.action.outText(" error clearing ".._args[1],10)
-- for I = 1, #fac.ArtyTasked do
-- if fac.ArtyTasked[I] ~= nil then
-- trigger.action.outText(I .. " ".. fac.ArtyTasked[I],10)
-- end
-- end
-- fac.purgeArtList(_args[1])
-- end
--table.remove(fac.ArtyTasked)
if _args ~= nil then
if _args[1] ~= nil and _args[2] ~= nil then
if fac.ArtyTasked[_args[1]].timeTasked == _args[2] then
fac.ArtyTasked[_args[1]].tasked = 0
fac.ArtyTasked[_args[1]].tgt = nil
fac.ArtyTasked[_args[1]].timeTasked = nil
trigger.action.outTextForCoalition(Group.getByName(_args[1]):getCoalition(),Group.getByName(_args[1]):getUnit(1):getTypeName() .. " available for re-tasking ",10)
--return element
end
end
end
end
function fac.tgtCatType(target)
--trigger.action.outText("Assessing Type "..target:getName(), 15)
--local _TGT_Type = Group.getByName(target:getName()):getCategory()
local _TGT_Type = target:getCategory()
--trigger.action.outText(_TGT_Type, 15)
if _TGT_Type == 3 then
--trigger.action.outText("TGT is Static Object", 15)
return "Static"
elseif _TGT_Type == 1 then
--local _leadUnit = Group.getByName(target):getUnit(1)
local _unitType = target:getCategory()
--trigger.action.outText("TGT is Unit Object " .. _unitType, 15)
return "Unit"
end
--end
end
function removeSetMark(_args)
trigger.action.removeMark(_args[1])
end
function fac.recceDetect(_args)
local _facUnitName = _args[1]
local _unit = nil
fac.facManTGT[_args[1]] = {}
local recceUnit = fac.getFacUnit(_facUnitName)
local recceSide = recceUnit:getCoalition()
local reccePos = recceUnit:getPoint()
local _groupId = fac.getGroupId(recceUnit)
local tempFound = {}
local dist = {
id = world.VolumeType.SPHERE,
params = {
point = reccePos,
radius = 40000
}
}
--trigger.action.outText("FINDING STUFF", 15)
--remove old Marks
local _i
--local recceMarkID = timer.getTime() * 1000
--for _i = 5000, fac.recceID do
--trigger.action.removeMark(_i)
--end
local count = 0
local recceMarkTagets = function(foundItem, val) -- generic search for all scenery
local foundOutput = nil
local foundObjectPos = nil
local sideCheck = foundItem:getCoalition()
count = count + 1
if foundItem:getLife() >= 1 then
if sideCheck ~= recceSide then
_unit = foundItem
local _unitPos = _unit:getPoint()
local _offsetEnemyPos = { x = _unitPos.x, y = _unitPos.y + 2.0, z = _unitPos.z }
local _offsetFacPos = { x = reccePos.x, y = reccePos.y + 2.0, z = reccePos.z }
--trigger.action.outText("FOUND STUFF ".. foundItem:getLife(), 15)
local _type = fac.tgtCatType(foundItem)
if land.isVisible(_offsetEnemyPos, _offsetFacPos) then
if _type == "Static" or _unit:isActive() then
local recceMarkID = timer.getTime() * 1000 + count
--trigger.action.outText("FOUND STUFF ".. _unit:getTypeName() .. recceMarkID .." "..fID, 15)
local _vel = _unit:getVelocity()
local _spd = 0
if _vel ~=nil then
_spd = math.sqrt(_vel.x^2+_vel.z^2)
end
local unitPos = _unit:getPosition()
local head = math.atan2(unitPos.x.z, unitPos.x.x)
local _unitPos = _unit:getPosition().p
--trigger.action.outText("FOUND STUFF ".. _unitPos.z, 15)
if _unit:getTypeName() == "outpost_road" then
_unitPos.x = _unitPos.x + math.cos(head+math.pi/2)*18
_unitPos.z = _unitPos.z + math.sin(head+math.pi/2)*18
end
local _lat, _lon = coord.LOtoLL(_unitPos)
local _latLngStr = mist.tostringLL(_lat, _lon, 3, false)
local _latLngSecStr = mist.tostringLL(_lat, _lon, 3, true)
local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_unit:getPosition().p)), 5)
--timer.scheduleFunction(fac.msgDraw, {_unit,recceSide,_facUnitName},timer.getTime() + 0.1)
trigger.action.markToCoalition(recceMarkID,_unit:getTypeName() .." - DMS: " .. _latLngSecStr .." Altitude: ".. math.floor(_unitPos.y) .."m/" .. math.floor(_unitPos.y*3.28084) .."ft" .. "\nHeading: ".. math.floor(getHeading(_unit) * 180/math.pi) .. "\nSpeed: " .. math.floor(_spd*2) .. " MPH" .."\nSpotted by: " .. fac.getFacName(_facUnitName), _unitPos, recceSide, false,"")
timer.scheduleFunction(removeSetMark,{recceMarkID},timer.getTime() + 300)
table.insert(tempFound,_unit)
fac.recceID = recceMarkID +1
end
end
end
end
end
world.searchObjects(1,dist,recceMarkTagets)
--world.searchObjects(Group.Category.GROUND,dist,recceMarkTagets)
count = 0
world.searchObjects(3,dist,recceMarkTagets)
--timer.scheduleFunction(fac.recceDetect, {_args,true}, timer.getTime() + 180)
fac.facManTGT[_args[1]] = tempFound
end
function fac.stratStrike(_args)
local i = 0
local _tgtList = fac.facManTGT[_args[1]]
--local roundsExpended = fac.callFireMissionMulti({_args[1],1,4,_tgtList})
local roundsExpended = 1
--trigger.action.outText("FOUND STUFF "..#_tgtList .. " " .. roundsExpended, 30)
while roundsExpended ~= nil do
local tempList = {}
for i = roundsExpended+1, #_tgtList do
table.insert(tempList,_tgtList[i])
end
_tgtList = {}
_tgtList = tempList
--trigger.action.outText(i .. " Targeting "..#_tgtList .. " " .. roundsExpended , 30)
if #_tgtList >0 then
roundsExpended = fac.callFireMissionMulti({_args[1],1,4,_tgtList})
else
roundsExpended = nil
end
end
end
function fac.multiStrike(_args)
local i = 0
local _tgtList = fac.facManTGT[_args[1]]
--local roundsExpended = fac.callFireMissionMulti({_args[1],1,4,_tgtList})
local roundsExpended = 1
--trigger.action.outText("FOUND STUFF "..#_tgtList .. " " .. roundsExpended, 30)
for i = 1, #_tgtList do
if _tgtList[i]:isExist() then
fac.callFireMission({_args[1],10,0,_tgtList[i]})
end
end
end
function fac.msgDraw(_args)
local _unit = _args[1]
local recceSide = _args[2]
local _facUnitName = _args[3]
local recceMarkID = timer.getTime() * 1000
local _unitPos = _unit:getPoint()
local _vel = _unit:getVelocity()
local _spd = 0
if _vel ~=nil then
_spd = math.sqrt(_vel.x^2+_vel.z^2)
end
--local unitPos = _unit:getPoint()
local _lat, _lon = coord.LOtoLL(_unit:getPosition().p)
local _latLngStr = mist.tostringLL(_lat, _lon, 3, false)
local _latLngSecStr = mist.tostringLL(_lat, _lon, 3, true)
local _mgrsString = mist.tostringMGRS(coord.LLtoMGRS(coord.LOtoLL(_unit:getPosition().p)), 5)
trigger.action.outText("Trying to draw mark ".. curMark, 15)
trigger.action.markToCoalition(recceMarkID,_unit:getTypeName() .." - DMS: " .. _latLngSecStr .." Altitude: ".. math.floor(_unitPos.y) .."m/" .. math.floor(_unitPos.y*3.28084) .."ft" .. "\nHeading: ".. math.floor(getHeading(_unit) * 180/math.pi) .. "\nSpeed: " .. math.floor(_spd*2) .. " MPH" .."\nSpotted by: " .. fac.getFacName(_facUnitName), _unitPos, recceSide, false,"")
timer.scheduleFunction(removeSetMark,{recceMarkID},timer.getTime() + 180)
--timer.scheduleFunction(awacs.removeSetMark,{facMarkID,_args[2]:getUnit(1):getName()},timer.getTime() + awacs.setAWACSscanRate [_args[3]]-.25)
--awacs.trackID = AWACSMarkID +1
return
end
function fac.callFireMissionMulti(_args)
local _facUnitName = _args[1]
local _tgtList
--trigger.action.outText("FOUND STUFF "..#_args[4], 15)
local _facUnit = Unit.getByName(_facUnitName)
if _args[4] == nil then
_tgtList = fac.facManTGT[_args[1]]
else
_tgtList = _args[4]
end
local _artyRounds = _args[2]
local _roundType = _args[3]
local _groupId = fac.getGroupId(_facUnit)
local _artyInRange = nil
local _artyPos = {}
local comboShoot = {}
local comboTasker = {}
local tp
local _i
if _tgtList == nil then
trigger.action.outTextForGroup(_groupId,"Unable to process fire mission",10)
trigger.action.outTextForGroup(_groupId,"No Valid Target, please create target list with manual scan",10)
return nil
end
for _i = 1, #_tgtList do
if _tgtList[_i]:isExist() then
_artyInRange = fac.getArty(_tgtList[_i],_facUnit,_roundType)
--if _artyInRange ~=nil then
--_artyPos = _artyInRange:getUnit(1):getPoint()
--else
--return
--end
else
trigger.action.outTextForGroup(_groupId,"Invalid target detected, please rescan for more current target list with manual scan",10)
return
end
if _tgtList[_i] == nil or _artyInRange == nil then
if _tgtList[_i] == nil and _artyInRange ~= nil then
trigger.action.outTextForGroup(_groupId,"Unable to process fire mission",10)
trigger.action.outTextForGroup(_groupId,"No Valid Target",10)
return nil
elseif _artyInRange == nil then
trigger.action.outTextForGroup(_groupId,"Unable to process fire mission",10)
trigger.action.outTextForGroup(_groupId,"No untasked active artillery/bomber in range of target",10)
return nil
else
_artyPos = _artyInRange:getUnit(1):getPoint()
end
else
tp = _tgtList[_i]:getPoint()
local firepoint = {}
firepoint.x = tp.x --+ _fireOffset.x
firepoint.y = tp.z --+ _fireOffset.z
--firepoint.radius = 10
firepoint.expend = "One"
--firepoint.attackQty = 1
firepoint.attackQty = 1
firepoint.altitude = _artyPos.y
firepoint.altitudeEnabled = true
--firepoint.expendQty = _artyRounds
--firepoint.expendQtyEnabled = true
local firemission = {id = 'Bombing', params = firepoint}
--_artyInRange:getController():pushTask(firemission)
if (fac.artyGetAmmo(_artyInRange:getUnits())-_i) >= 0 then
--trigger.action.outTextForGroup(_groupId,"Target processed " .. _tgtList[_i]:getTypeName(),10)
table.insert (comboTasker,firemission)
end
-- timer.scheduleFunction(fac.launchMulti,{_artyInRange:getController(),firemission},timer.getTime()+ 45)
end
--tasks = { _i =
end
comboShoot = {id = 'ComboTask', params = {tasks = comboTasker}}
_artyInRange:getController():setOption(1,1)
_artyInRange:getController():pushTask(comboShoot)
_artyInRange:getController():setOption(10,3221225470)
--if fac.ArtyTasked[_artyInRange:getName()] == nil or fac.ArtyTasked[_artyInRange:getName()].tasked == 0 then
--table.insert (fac.ArtyTasked, _artyInRange:getName())
--fac.ArtyTasked[_artyInRange:getName()]
fac.ArtyTasked[_artyInRange:getName()]= {name = _artyInRange:getName(), tasked = #comboTasker,timeTasked = nil}
--timer.scheduleFunction(fac.purgeArtList,{_artyInRange:getName()},timer.getTime()+ 45*#_tgtList)
--end
--timer.scheduleFunction(fac.clearTask,{_artyInRange:getController(),#_tgtList},timer.getTime()+ 20*#_tgtList)
trigger.action.outTextForCoalition(_facUnit:getCoalition(),"Fire mission order sent, " .._artyInRange:getUnit(1):getTypeName() .. " ("..fac.artyGetAmmo(_artyInRange:getUnits())-#comboTasker .. " rounds remaining"..") firing ".._artyRounds.." rounds at " .. #comboTasker .. " target(s). Requestor: " .. fac.getFacName(_facUnitName) ,10)
return fac.artyGetAmmo(_artyInRange:getUnits())
end
function tablelength(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
function fac.callFireMissionCarpet(_args)
local _facUnitName = _args[1]
local _facUnit = Unit.getByName(_facUnitName)
local _tgtList = fac.facManTGT[_args[1]]
local _artyRounds = _args[2]
local _roundType = _args[3]
local _groupId = fac.getGroupId(_facUnit)
local _enemyUnit = nil
local _artyInRange = nil
local _artyPos = {}
local comboShoot = {}
local previousTask = {}
local comboTasker = {}
local firemission = {}
local firePointTask = {}
local firePointWpt = {}
local firepoint = {}
local tp
local _i
--if _tgtList == nil then
--trigger.action.outTextForGroup(_groupId,"Unable to process fire mission",10)
--trigger.action.outTextForGroup(_groupId,"No Valid Target, please create target list with manual scan",10)
--return
--end
--for _i = 1, #_tgtList do
--if _tgtList[_i]:isExist() then
_enemyUnit = fac.getCurrentFacUnit(_facUnit, _facUnitName)
_artyInRange = fac.getArty(_enemyUnit,_facUnit,_roundType)
--if _artyInRange ~=nil then
--_artyPos = _artyInRange:getUnit(1):getPoint()
--else
--return
--end
--else
--trigger.action.outTextForGroup(_groupId,"Invalid target detected, please rescan for more current target list with manual scan",10)
--return
--end
if _enemyUnit == nil or _artyInRange == nil then
if _enemyUnit == nil and _artyInRange ~= nil then
trigger.action.outTextForGroup(_groupId,"Unable to process fire mission",10)
trigger.action.outTextForGroup(_groupId,"No Valid Target",10)
return
elseif _artyInRange == nil then
trigger.action.outTextForGroup(_groupId,"Unable to process fire mission",10)
trigger.action.outTextForGroup(_groupId,"No untasked active artillery/bomber in range of target",10)
return
--else
end
else
_artyPos = _artyInRange:getUnit(1):getPoint()
tp = _enemyUnit:getPoint()
firePointWpt.x = tp.x + math.cos(getHeading(_facUnit)+math.pi)*5000
firePointWpt.y = tp.z + math.sin(getHeading(_facUnit)+math.pi)*5000
firePointWpt.type = "Turning Point"
firePointWpt.action = "Turning Point"
firePointWpt.alt = _artyPos.y
firepoint.x = tp.x --+ _fireOffset.x
firepoint.y = tp.z --+ _fireOffset.z
firepoint.attackType = 'Carpet'
firepoint.carpetLength = 500
firepoint.expend = "All"
--firepoint.direction = getHeading(_facUnit)+math.pi
firepoint.directionEnabled = true
firepoint.attackQty = 1
firepoint.altitude = _artyPos.y
firepoint.altitudeEnabled = true
--firepoint.expendQty = _artyRounds
--firepoint.expendQtyEnabled = true
firepoint.groupAttack = true
firePointTask = {id = 'CarpetBombing', params = firepoint}
firePointWpt.task = firepointTask
previousTask = mist.getGroupRoute(_artyInRange:getName())
table.insert (comboTasker,firePointWpt)
--trigger.action.outText(mist.utils.tableShow(previousTask), 25)
table.insert (comboTasker,previousTask)
--_artyInRange
firemission = {
id = 'Mission',
params = {
route = {
points = {--firePointWpt--,previousTask
[1] = {
["x"] = tp.x + math.cos(getHeading(_facUnit)+math.pi)*5000,
["y"] = tp.z + math.sin(getHeading(_facUnit)+math.pi)*5000,
["type"] = "Turning Point",
["action"] = "Turning Point",
["alt"] = _artyPos.y,
["task"] = firePointTask
}
-- [2] = {
}
}
}
}
--_artyInRange:getController():pushTask(firemission)
-- timer.scheduleFunction(fac.launchMulti,{_artyInRange:getController(),firemission},timer.getTime()+ 45)
end
--tasks = { _i =
--end
--comboShoot = {id = 'ComboTask', params = {tasks = comboTasker}}
_artyInRange:getController():setOption(1,1)
--_artyInRange:getController():pushTask(firePointTask)
_artyInRange:getController():setTask(firemission)
_artyInRange:getController():setOption(10,3221225470)
--if fac.ArtyTasked[_artyInRange:getName()] == nil or fac.ArtyTasked[_artyInRange:getName()].tasked > 0 then
--table.insert (fac.ArtyTasked, _artyInRange:getName())
--fac.ArtyTasked[_artyInRange:getName()]
fac.ArtyTasked[_artyInRange:getName()]= {name = _artyInRange:getName(), tasked = 1,timeTasked = nil}
--timer.scheduleFunction(fac.purgeArtList,{_artyInRange:getName()},timer.getTime()+ 45*1)
--end
local ammoType = _artyInRange:getUnit(1):getAmmo()
--timer.scheduleFunction(fac.clearTask,{_artyInRange:getController(),#_tgtList},timer.getTime()+ 20*#_tgtList)
trigger.action.outTextForCoalition(_facUnit:getCoalition(),"Fire mission order sent, " .._artyInRange:getUnit(1):getTypeName() .. "(".. ammoType[1].count .." Rounds)".." will carpet bomb using ATK Az of ".. math.floor(getHeading(_facUnit)/math.pi*180).. " degrees on target:" .. _enemyUnit:getTypeName() .. ". Requestor: " .. fac.getFacName(_facUnitName) ,10)
end
function fac.launchMulti(_args)
_args[1]:pushTask(_args[2])
end
function fac.taskedArty(_args)
_args[1]:pushTask(_args[2])
end
local facArtyFireDetect ={}
function facArtyFireDetect:onEvent(e)
if e.id == world.event.S_EVENT_SHOT then
local fireGroup = Unit.getGroup(e.initiator):getName()
if fac.ArtyTasked[fireGroup] ~= nil and fac.ArtyTasked[fireGroup].tasked > 0 then
fac.ArtyTasked[fireGroup].tasked = fac.ArtyTasked[fireGroup].tasked - 1
trigger.action.outTextForCoalition(Group.getByName(fireGroup):getCoalition(),Group.getByName(fireGroup):getUnit(1):getTypeName() .. " has commenced firing at designated target, tasked rounds remaining "..fac.ArtyTasked[fireGroup].tasked,0.5)
if fac.ArtyTasked[fireGroup].tasked == 0 then
--fac.ArtyTasked[fireGroup].tasked = false
trigger.action.outTextForCoalition(Group.getByName(fireGroup):getCoalition(),Group.getByName(fireGroup):getUnit(1):getTypeName() .. " available for re-tasking ",20)
end
if Unit.getGroup(e.initiator):getCategory() > 1 then
if fac.ArtyTasked[fireGroup].tgt:isExist() then
local tgtPnt = fac.ArtyTasked[fireGroup].tgt:getPoint()
local _tempDist = fac.getDistance(e.initiator:getPoint(), tgtPnt)
timer.scheduleFunction(fac.explosions, {tgtPnt}, timer.getTime() + _tempDist/264)
else
Group.getByName(fireGroup):getController():popTask()
fac.ArtyTasked[fireGroup].tasked = 0
trigger.action.outTextForCoalition(Group.getByName(fireGroup):getCoalition(),Group.getByName(fireGroup):getUnit(1):getTypeName() .. " tasked target destroyed, available for re-tasking ",20)
end
end
elseif fac.ArtyTasked[fireGroup] ~= nil then
fac.ArtyTasked[fireGroup].tasked = 0
if Unit.getGroup(e.initiator):getCategory() == 1 then
trigger.action.outTextForCoalition(Group.getByName(fireGroup):getCoalition(),Group.getByName(fireGroup):getUnit(1):getTypeName() .. " BOMBS AWAY!!! ",0.25)
end
end
end
end
function fac.explosions(args)
local tgtPnt = args[1]
local explodePnt = {}
explodePnt.x = tgtPnt.x + math.random(-15,15)
explodePnt.z = tgtPnt.z + math.random(-15,15)
explodePnt.y = tgtPnt.y
trigger.action.explosion(explodePnt, 10)
end
world.addEventHandler(facArtyFireDetect)
function fac.checkTask(_args)
local _i
local _j
local _facUnitName = _args[1]
local _facUnit = Unit.getByName(_facUnitName)
local _facGroup = _facUnit:getGroup()
local _groupId = fac.getGroupId(_facUnit)
trigger.action.outTextForCoalition(_facGroup:getCoalition(), "Checking artillery/bomber units" ,10)
local _enemyUnit = fac.getCurrentFacUnit(_facUnit, _facUnitName)
local _artyInRange = fac.getArty(_enemyUnit,_facUnit,1)
local artyList = fac.ArtyTasked
local tp
local _TGTdist = -1
--trigger.action.outTextForCoalition(_facGroup:getCoalition(), tablelength(fac.ArtyTasked),10)
for _i, _j in pairs(fac.ArtyTasked) do
if tablelength(fac.ArtyTasked) > 0 then
local fireGroup = _i
if _facGroup:getCoalition() == Group.getByName(fireGroup):getCoalition() and Group.getByName(fireGroup):getUnit(1):isActive() and Group.getByName(fireGroup):isExist() then
local _artyPos = Group.getByName(fireGroup):getUnit(1):getPoint()
if _enemyUnit ~= nil then
tp = _enemyUnit:getPoint()
_TGTdist = math.sqrt(((tp.z-_artyPos.z)^2)+((tp.x-_artyPos.x)^2))
end
local ammoCount = fac.artyGetAmmo(Group.getByName(fireGroup):getUnits())
trigger.action.outTextForCoalition(Group.getByName(fireGroup):getCoalition(),Group.getByName(fireGroup):getUnit(1):getTypeName() .. " Tasked rounds remaining: "..fac.ArtyTasked[fireGroup].tasked .. " Current Ammo: " .. ammoCount .." " .. math.floor(_TGTdist) ,10)
end
else
trigger.action.outTextForCoalition(_facGroup:getCoalition(), "No active artillery detected, build some artillery units with CTLD!!" ,10)
end
end
end
function fac.callFireMission(_args)
local _facUnitName = _args[1]
local _artyRounds = _args[2]
local _roundType = _args[3]
local _enemyUnit = nil
local _artyInRange = nil
local _facUnit = Unit.getByName(_facUnitName)
local _groupId = fac.getGroupId(_facUnit)
local _illumPoint = {}
local tp
local spotterName
if fac.getFacName(_facUnitName) == nil then
spotterName = "AI Spotter"
else
spotterName = fac.getFacName(_facUnitName)
end
if _args[4] == nil then
_enemyUnit = fac.getCurrentFacUnit(_facUnit, _facUnitName)
else
_enemyUnit = _args[4]
end
_artyInRange = fac.getArty(_enemyUnit,_facUnit,_roundType)
if _enemyUnit == nil or _artyInRange == nil then
if _enemyUnit == nil and _artyInRange ~= nil then
if _roundType ~= 1 then
trigger.action.outTextForGroup(_groupId,"Unable to process fire mission",10)
trigger.action.outTextForGroup(_groupId,"No Valid Target",10)
else
tp = fac.facOffsetMaker(_facUnit)
_illumPoint.x = tp.x
_illumPoint.y = tp.y + fac.illumHeight
_illumPoint.z = tp.z
trigger.action.illuminationBomb(_illumPoint,500)
trigger.action.outTextForCoalition(_facUnit:getCoalition(),"Fire mission order sent, " .._artyInRange:getUnit(1):getTypeName() .. " firing illumination rounds " .. fac.facOffsetDist .. " m from " ..spotterName,10)
end
elseif _artyInRange == nil then
trigger.action.outTextForGroup(_groupId,"Unable to process fire mission",10)
trigger.action.outTextForGroup(_groupId,"No untasked active artillery/bomber in range of target",10)
end
else
--if _enemyUnit:getCoalition() == 1 then
if _roundType ~= 1 then
if fac.ArtyTasked[_artyInRange:getName()] == nil or fac.ArtyTasked[_artyInRange:getName()].tasked == 0 then
--table.insert (fac.ArtyTasked, _artyInRange:getName())
--fac.ArtyTasked[_artyInRange:getName()]
fac.ArtyTasked[_artyInRange:getName()]= {name = _artyInRange:getName(), tasked = 0}
if _artyInRange:getUnit(1):hasAttribute('Strategic bombers') or _artyInRange:getUnit(1):hasAttribute('Bombers') then
--local firemission = {id = 'Bombing', params = firepoint}
--timer.scheduleFunction(fac.purgeArtList,{_artyInRange:getName()},timer.getTime()+ 20)
else
--timer.scheduleFunction(fac.purgeArtList,{_artyInRange:getName()},timer.getTime()+ 600)
end
--timer.scheduleFunction(fac.clearTask,{_artyInRange:getController(),1},timer.getTime()+ 20*_artyRounds+180)
end
end
--trigger.action.outText(table.getn(fac.redArty) .. "insert blu" ,10)
-- else
-- if _roundType ~= 1 then
-- table.insert (fac.ArtyTasked, _artyInRange:getName())
-- timer.scheduleFunction(fac.purgeArtList,{_artyInRange:getName()},timer.getTime()+ 180)
-- end
-- --trigger.action.outText(table.getn(fac.redArty) .. "insert red" ,10)
-- end
local _artyPos = _artyInRange:getUnit(1):getPoint()
local firepoint = {}
tp = _enemyUnit:getPoint()
local _fireOffset = {x = 0,y = 0,z =0}
--local _fireOffset.y = 0
local dx = tp.x-_artyPos.x
local dy = tp.z-_artyPos.z
local fpNorthOffset = getNorthCorrection(tp)
local _fireheading = math.atan(dy/dx)
--correcting for coordinate space
local _FH = 1
if dy < 0 then -- dy = -1 90-270
if dx < 0 then --dy/dx = 1 180-270
--trigger.action.outText(_artyInRange:getName().." Firing SW:\n".. (_fireheading/math.pi)*180 .."\n", 300)
_fireheading= _fireheading + 2*math.pi
else
--trigger.action.outText(_artyInRange:getName().." Firing SE:\n".. (_fireheading/math.pi)*180 .."\n", 300)
_fireheading= _fireheading + math.pi
end
elseif dy > 0 then --dy = 1 270-90
if dx < 0 then --dy/dx = -1 270-0
--trigger.action.outText(_artyInRange:getName().." Firing NW:\n".. (_fireheading/math.pi)*180 .."\n", 300)
_fireheading= _fireheading + 2*math.pi
else --dy/dx = 1 0-90
--trigger.action.outText(_artyInRange:getName().." Firing NE:\n".. (_fireheading/math.pi)*180 .."\n", 300)
_fireheading= _fireheading
_FH = -1
end
end
local headingDeg =(_fireheading)/math.pi*180
if headingDeg >= 360 then
headingDeg = headingDeg-360
end
--trigger.action.outText(_artyInRange:getName().."\ndY ".. dy .."\ndX ".. dx .."\ndy/dx" .. dy/dx .."\nrad ".. _fireheading/math.pi .."\ndeg " .. headingDeg,120)
--_fireheading = _fireheading
local _TGTdist = math.sqrt(((tp.z-_artyPos.z)^2)+((tp.x-_artyPos.x)^2))
local _TGThghtD = math.abs(tp.y-_artyPos.y)
if _artyInRange:getUnit(1):getTypeName() == "2B11 mortar" then
_fireOffset.x = math.cos(_fireheading)*(-(100+_TGThghtD/10))*_FH
_fireOffset.z = math.sin(_fireheading)*(-(100+_TGThghtD/10))*_FH
elseif _artyInRange:getUnit(1):hasAttribute('Strategic bombers') or _artyInRange:getUnit(1):hasAttribute('Bombers') then
_artyRounds = 1
end
firepoint.x = tp.x + _fireOffset.x
firepoint.y = tp.z + _fireOffset.z
--firepoint.radius = 10
local firemission = {}
if _artyInRange:getUnit(1):hasAttribute('Strategic bombers') or _artyInRange:getUnit(1):hasAttribute('Bombers') then
local bomberTasked = _artyInRange:getUnit(1)
local payloadCheck = {}
payloadCheck = bomberTasked:getAmmo()
if payloadCheck[1].desc.guidance == nil or payloadCheck[1].desc.guidance == 7 then
--trigger.action.outText(bomberTasked:getTypeName(),10)
--trigger.action.outText(#payloadCheck,10)
firepoint.expend = "Quarter"
else
--trigger.action.outText( payloadCheck[1].desc.guidance,120)
firepoint.expend = "One"
end
firepoint.attackQty = 1
firepoint.altitude = _artyPos.y
firepoint.altitudeEnabled = true
firemission = {id = 'Bombing', params = firepoint}
_artyInRange:getController():setOption(1,1)
_artyInRange:getController():setOption(10,3221225470)
else
firepoint.expendQty = _artyRounds
firepoint.expendQtyEnabled = true
firemission = {id = 'FireAtPoint', params = firepoint}
end
-- local firepoint2 ={}
-- firepoint2.groupId = fac.getGroupId(_enemyUnit)
-- firepoint2.expend = "Quarter"
-- local firemission = {id = 'AttackGroup', params = firepoint2}
if _roundType ~= 1 then
_artyInRange:getController():pushTask(firemission)
fac.ArtyTasked[_artyInRange:getName()]= {name = _artyInRange:getName(), tasked = _artyRounds ,timeTasked = timer.getTime() ,tgt = _enemyUnit}
--Controller.knowTarget(_artyInRange:getUnit(3):getController(),_enemyUnit)
trigger.action.outTextForGroup(_artyInRange:getID(),"Az: "..math.floor(headingDeg).." Range: ".._TGTdist,30)
trigger.action.outTextForGroup(_groupId,"Az: "..math.floor(headingDeg).." Range: ".._TGTdist,60)
trigger.action.outTextForCoalition(_facUnit:getCoalition(),"Fire mission order sent, " .._artyInRange:getUnit(1):getTypeName() .. " ("..fac.artyGetAmmo(_artyInRange:getUnits()) .. " rounds remaining"..") firing ".._artyRounds.." rounds at " .. _enemyUnit:getTypeName() .. ". Requestor: " .. spotterName ,10)
timer.scheduleFunction(fac.purgeArtList,{_artyInRange:getName(),timeTasked}, timer.getTime() + 600)
else
_illumPoint.x = tp.x
_illumPoint.y = tp.y + fac.illumHeight
_illumPoint.z = tp.z
trigger.action.illuminationBomb(_illumPoint,500)
trigger.action.outTextForCoalition(_facUnit:getCoalition(),"Fire mission order sent, " .._artyInRange:getUnit(1):getTypeName() .. " firing illumination rounds at " .. _enemyUnit:getTypeName() .. ". Requestor: " .. spotterName,10)
end
end
end
function fac.artyAICall()
--local atyDunitName
--trigger.action.outText("Tasking AI to call ARTY", 10)
for _, atyDunitName in pairs(fac.artDirectNames) do
local status, error = pcall(function()
local _unitR = fac.getFacUnit(atyDunitName)
if _unitR ~= nil then
local _groupIdR = fac.getGroupId(_unitR)
if _groupIdR then
--trigger.action.outText(atyDunitName.. " calling ARTY", 10)
local aiARTYTgt = fac.findFacNearestVisibleEnemy(Unit.getByName(atyDunitName))
if contains(fac.AITgted,aiARTYTgt:getName()) == false then
table.insert(fac.AITgted, aiARTYTgt:getName())
fac.callFireMission({atyDunitName,10,2,aiARTYTgt})
end
end
--[[else
env.info(string.format("FAC DEBUG: unit nil %s",_facUnitName)) ]]
end
end)
if (not status) then
env.error(string.format("Error adding f10 to ARTY Spotter: %s", error), false)
end
end
fac.AITgted = {}
timer.scheduleFunction(fac.artyAICall, nil, timer.getTime() + 300)
end
-- Scheduled functions (run cyclically)
timer.scheduleFunction(fac.addFacF10MenuOptions, nil, timer.getTime() + 5.5)
timer.scheduleFunction(fac.checkFacStatus, nil, timer.getTime() + 5.4)
timer.scheduleFunction(fac.artyAICall, nil, timer.getTime() + 5)