--[[ 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", --"TF-51", --"Hawk", } fac.artyDirectorTypes = { "Soldier M249", } -- ***************** FAC CONFIGURATION ***************** fac.FAC_maxDistance = 18520 -- 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 = { "Chevy 6-1", "Chevy 6-2", "Chevy 6-3", "Chevy 6-4", "Chevy 5-1", "Chevy 5-2", "Chevy 5-3", "Chevy 5-4", "Chevy 4-1", "Chevy 4-2", "Chevy 4-3", "Chevy 4-4", "Chevy 2-1", "Chevy 2-2", "Chevy 2-3", "Chevy 2-4", "Chevy 1-1", "Chevy 1-2", "Chevy 1-3", "Chevy 1-4", "837", "838", "839", "840", "841", "842", "843", "844", -- "Red AFAC #001", -- "Red AFAC #002", -- "Red AFAC #003", -- "Red AFAC #004", -- "Red AFAC #005", -- "Red AFAC #006", -- "Red AFAC #007", -- "Red AFAC #008", -- "Red AFAC #009", -- "Red AFAC #010", -- "Blue AFAC #001", -- "Blue AFAC #002", -- "Blue AFAC #003", -- "Blue AFAC #004", -- "Blue AFAC #005", -- "Blue AFAC #006", -- "Blue AFAC #007", -- "Blue AFAC #008", -- "Blue AFAC #009", -- "Blue AFAC #010", -- "helicargo4", -- "helicargo8", -- "FAC #001", -- "FAC #002", -- "FAC #003", -- "FAC #004", -- "FAC #005", -- "FAC #006", -- "FAC #007", -- "FAC #008", -- "RED Mobile FOB TRANSPORT #001", -- "RED Mobile FOB TRANSPORT #002", -- "BLUE Mobile FOB TRANSPORT #001", -- "BLUE Mobile FOB TRANSPORT #002", } --Define RECCE pilots fac.reccePilotNames = { --Enter RECCE pilot names here, } 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 (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 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 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)