function playQRASoundToClient(client, time) soundQRA:ToClient(client) return nil end function QRALaunchBogey(params) local objQRA = params[1] local trigerringUnitName = params[2] local QRAIndex = params[3] local playerClient = CLIENT:FindByName(trigerringUnitName) local playerCoord = playerClient:GetCoordinate() local bogeyGroup = nil local bogeyRouteLengthNM = 200 local bogeyRangeMin = 100 local bogeyRangeMax = 300 local bogeyFLMin = 5 local bogeyFLMax = 250 local bogeyAzimuthMin = 0 local bogeyAzimuthMax = 359 local bogeyGroundSpeedKnots = 600 local bogeyAzimuth = math.floor( math.random( UTILS.FeetToMeters(bogeyAzimuthMin), UTILS.FeetToMeters(bogeyAzimuthMax) ) ) local bogeyFL = math.floor( math.random( UTILS.FeetToMeters(bogeyFLMin * 100), UTILS.FeetToMeters(bogeyFLMax * 100) ) ) local bogeyRangeMeters = math.floor( math.random( UTILS.NMToMeters(bogeyRangeMin), UTILS.NMToMeters(bogeyRangeMax) ) ) local bogeySpawnObj = QRAArray[QRAIndex].objSpawn local bogeyCoord = POINT_VEC3:NewFromVec3( { x = playerCoord.x + math.floor(bogeyRangeMeters * math.cos(math.rad(bogeyAzimuth))), y = math.max( math.min( bogeyFL, UTILS.FeetToMeters(32000) ), UTILS.FeetToMeters(500) ), z = playerCoord.z + math.floor(bogeyRangeMeters * math.sin(math.rad(bogeyAzimuth))), } ) local bogeyDestination = playerCoord:GetRandomCoordinateInRadius( UTILS.NMToMeters(10), UTILS.NMToMeters(60) ) local bogeyHeading = bogeyCoord:HeadingTo(bogeyDestination) jtff_log.info( string.format( "Spawning bogey based on %s with Heading of %i degrees", bogeySpawnObj.SpawnTemplatePrefix, bogeyHeading ), "QRA" ) bogeyGroup = bogeySpawnObj:InitHeading(bogeyHeading):SpawnFromVec3(bogeyCoord) bogeyGroup:OptionROE(ENUMS.ROE.WeaponHold) bogeyGroup:OptionROT(ENUMS.ROT.NoReaction) bogeyGroup:OptionRTBBingoFuel(true) bogeyGroup:OptionRestrictBurner(false) bogeyGroup:EnableEmission(false) bogeyGroup:OptionAlarmStateGreen() bogeyGroup:OptionECM_Never() bogeyGroup:CommandEPLRS(true) local bogeyDestinationCoord = bogeyGroup:GetCoordinate():Translate( UTILS.NMToMeters(bogeyRouteLengthNM), bogeyHeading, true, false ) bogeyGroup:Route( { bogeyDestinationCoord:WaypointAirTurningPoint( COORDINATE.WaypointAltType.BARO, UTILS.KnotsToKmph(bogeyGroundSpeedKnots), nil, "End QRA" ), bogeyDestinationCoord:GetClosestAirbase( --bogeyGroup:GetCategory(), --bogeyGroup:GetCoalition() ):GetCoordinate():WaypointAirLanding( UTILS.KnotsToKmph(bogeyGroundSpeedKnots), bogeyDestinationCoord:GetClosestAirbase( --bogeyGroup:GetCategory(), --bogeyGroup:GetCoalition() ) ) } ) return bogeyGroup end function QRATrigger(params) local objQRA = params[1] local trigerringUnitName = params[2] local QRAIndex = params[3] local msgTriggeredGround = "" local msgTriggeredOthers = "" local triggerPlayerName = "" if (objQRA.customconfig.type == JTFF_QRA.Type.TangoAuto) then local playerClient = CLIENT:FindByName(trigerringUnitName) local playerName = playerClient:GetPlayer() triggerPlayerName = playerName local playerCoord = playerClient:GetCoordinate() QRAArray[QRAIndex].objBogeyGroup = QRALaunchBogey(params) local bullsCoord = COORDINATE.GetBullseyeCoordinate(objQRA.customconfig.benefit_coalition) local bogeyCoord = QRAArray[QRAIndex].objBogeyGroup:GetCoordinate() local bullsBearing = math.floor(bullsCoord:HeadingTo(bogeyCoord)) local bullsRange = math.floor(UTILS.MetersToNM(bullsCoord:Get2DDistance(bogeyCoord))) local bogeyAngel = math.floor(UTILS.MetersToFeet(QRAArray[QRAIndex].objBogeyGroup:GetAltitude(false))/1000) local bogeyApproxDestination = playerCoord:GetClosestAirbase():GetName() jtff_log.info( string.format( "Bogey Spawned based on %s", QRAArray[QRAIndex].objSpawn.SpawnTemplatePrefix ), "QRA" ) jtff_log.info( string.format("Bogey is Bulls %i for %i Angel %i, tracking to %s", bullsBearing, bullsRange, bogeyAngel, bogeyApproxDestination ), "QRA" ) local formatString = "QRA %s has been triggered by %s!\n" .. "Type is : %s\n" .. "Bogey is Bulls %i for %i Angel %i, tracking %s\n" .. "If you're on Alert," .. " you have 10 minutes to take-off and intercept the bogey.\n" .. "If you're not, please give %s priority on the taxiway.\n" msgTriggeredGround = string.format( formatString, objQRA.customconfig.name, playerName, objQRA.customconfig.type, bullsBearing, bullsRange, bogeyAngel, bogeyApproxDestination, playerName ) formatString = " : QRA Triggered by %s!\n" .. "A Bogey is approaching %s, " .. "stay away from the interception area\n" .. "...\n" .. "Bogey last position : Bulls %i for %i Angel %i, tracking to %s" msgTriggeredOthers = string.format(formatString, playerName, playerCoord:GetClosestAirbase():GetName(), bullsBearing, bullsRange, bogeyAngel, bogeyApproxDestination ) elseif (objQRA.customconfig.type == JTFF_QRA.Type.TangoManual) then local playerClient local playerName local playerCoord if type(trigerringUnitName) == 'nil' then playerName = "GameMaster" playerClient = nil playerCoord = COORDINATE.GetBullseyeCoordinate(objQRA.customconfig.benefit_coalition) else playerClient = CLIENT:FindByName(trigerringUnitName) playerName = playerClient:GetPlayer() playerCoord = playerClient:GetCoordinate() end triggerPlayerName = playerName local formatString = "QRA %s has been triggered by %s !\n" .. "Type is : %s\n" .. "If you're on Alert," .. " you have 10 minutes to take-off and start intercepting the the bogey.\n" .. "You are cleared to start and wait your AIC for scramble information\n\n\n" .. "If you're not on alert, please monitor the ground frequency" .. " and let the scramble flight taxi in highest priority..." msgTriggeredGround = string.format( formatString, objQRA.customconfig.name, playerName, objQRA.customconfig.type ) formatString = " : QRA Training triggered by %s!\n" .. "Stay away from the interception area (%s)\n" .. "...\n" .. "Keep monitoring your tactical frequency" .. " for information about the Scramble procedure" msgTriggeredOthers = string.format(formatString, playerName, type(playerClient) ~= 'nil' and playerCoord:ToStringMGRS() or "" ) elseif (objQRA.customconfig.type == JTFF_QRA.Type.Alpha) then local playerClient local playerName local playerCoord if type(trigerringUnitName) == 'nil' then playerName = "GameMaster" playerClient = nil playerCoord = COORDINATE.GetBullseyeCoordinate(objQRA.customconfig.benefit_coalition) else playerClient = CLIENT:FindByName(trigerringUnitName) playerName = playerClient:GetPlayer() playerCoord = playerClient:GetCoordinate() end triggerPlayerName = playerName local formatString = "QRA %s has been triggered by %s !\n" .. "Type is : %s\n" .. "If you're on Alert," .. " you have 10 minutes to take-off and start intercepting the the bogey.\n" .. "You are cleared to start and wait your AIC for scramble information\n\n\n" .. "If you're not on alert, please monitor the ground frequency" .. " and let the scramble flight taxi in highest priority..." msgTriggeredGround = string.format( formatString, objQRA.customconfig.name, playerName, objQRA.customconfig.type ) formatString = " : QRA Triggered by %s!\n" .. "Stay away from the interception area (%s)\n" .. "...\n" .. "Keep monitoring your tactical frequency" .. " for information about the Scramble procedure" msgTriggeredOthers = string.format(formatString, playerName, type(playerClient) ~= 'nil' and playerCoord:ToStringMGRS() or "" ) else end objQRA.clientSet:ForEachClient( function(clientObject) if ClientIsOnGround(clientObject) then playQRASoundToClient(clientObject) timer.scheduleFunction( playQRASoundToClient, clientObject, timer.getTime() + 17 ) MESSAGE:New(msgTriggeredGround,60):ToClient(clientObject) end end ) MESSAGE:New(msgTriggeredOthers,30):ToCoalition(objQRA.customconfig.benefit_coalition) QRAArray[QRAIndex].menu_finex[QRAArray[QRAIndex].customconfig.benefit_coalition] = MENU_COALITION_COMMAND:New( QRAArray[QRAIndex].customconfig.benefit_coalition, "FinEx - " .. QRAArray[QRAIndex].customconfig.name .. " !!", MenuCoalitionQRA[QRAArray[QRAIndex].customconfig.benefit_coalition], QRAFinex, QRAIndex ) if (type(dcsbot) ~= 'nil') then local title = string.format('QRA triggered !') local description = string.format("[%s] triggered a QRA scenario : All QRA pilots, startup and scramble following the procedure",triggerPlayerName) local img = 'https://www.defense.gouv.fr/sites/default/files/styles/16_9_sm/public/air/22_09_2023_13.jpeg?itok=n_MPmvZc' local fields = { ['Trigger'] = triggerPlayerName, ['Type'] = QRAArray[QRAIndex].customconfig.type, ['Coalition'] = UTILS.GetCoalitionName(QRAArray[QRAIndex].customconfig.benefit_coalition), } local footer = 'Scramble scramble scramble...' dcsbot.sendEmbed(title, description, img, fields, footer) end end function QRAFinex(objQRAIndex) local objQRA = QRAArray[objQRAIndex] if objQRA.customconfig.type == JTFF_QRA.Type.TangoAuto then jtff_log.trace(string.format("objQRA.objSpawn is %s", net.lua2json(objQRA.objSpawn)),"QRA") local GroupPlane, Index = objQRA.objSpawn:GetFirstAliveGroup() while GroupPlane ~= nil do GroupPlane:ScheduleStop() GroupPlane:Destroy(true,0) GroupPlane, Index = objQRA.objSpawn:GetNextAliveGroup( Index ) end collectgarbage() end QRAArray[objQRAIndex].menu_finex[QRAArray[objQRAIndex].customconfig.benefit_coalition]:Remove() QRAArray[objQRAIndex].menu_finex[QRAArray[objQRAIndex].customconfig.benefit_coalition] = nil end -- ***************************************************************************** -- ** QRA ** -- ********************************************************* QRAArray = {} compteur = 0 MenuCoalitionQRA = {} MenuCoalitionQRA[coalition.side.BLUE] = MENU_COALITION:New(coalition.side.BLUE, "QRA", MenuCoalitionBlue) MenuCoalitionQRA[coalition.side.RED] = MENU_COALITION:New(coalition.side.RED, "QRA", MenuCoalitionRed) for index, qraconfig in ipairs(QRAConfig) do if qraconfig.enable == true then compteur = compteur + 1 jtff_log.info('creation QRA : '.. qraconfig.name..'...',"QRA") local objQRA = { customconfig = {}, clientSet = nil, objSpawn = nil, objBogeyGroup = nil, menu = {}, menu_finex = {}, } if ( qraconfig.type == JTFF_QRA.Type.TangoAuto) then objQRA.objSpawn = SPAWN:New(qraconfig.templates[1]) :InitSkill(qraconfig.skill) :InitRandomizeTemplate(qraconfig.templates or {}) end objQRA.clientSet = SET_CLIENT:New() :FilterCoalitions(string.lower(UTILS.GetCoalitionName(qraconfig.benefit_coalition))) :FilterCategories("plane") :FilterActive(true) :FilterFunction(ClientIsOnGround) :FilterStart() objQRA.clientSet:HandleEvent(EVENTS.PlayerEnterAircraft) jtff_log.debug(string.format("Nbr Clients getting QRA Calls : %i", objQRA.clientSet:Count()),"QRA") function objQRA.clientSet:OnEventPlayerEnterAircraft(EventData) local clientObject = CLIENT:Find(EventData.IniUnit:GetDCSObject()) if ClientIsOnGround(clientObject) then --self:Add(clientObject:GetPlayer(), clientObject) jtff_log.debug(string.format("Added %s to QRA receivers", clientObject:GetPlayer() or ""),"QRA") jtff_log.debug(string.format("Nbr Clients getting QRA Calls : %i", objQRA.clientSet:Count()),"QRA") end end if ( qraconfig.type ~= JTFF_QRA.Type.TangoAuto) then objQRA.menu[qraconfig.benefit_coalition] = MENU_COALITION_COMMAND:New( qraconfig.benefit_coalition, "Trigger " .. qraconfig.name, MenuCoalitionQRA[qraconfig.benefit_coalition], QRATrigger, { objQRA, nil, index, } ) end objQRA.customconfig = qraconfig QRAArray[compteur] = objQRA end end if compteur == 0 then MenuCoalitionQRA[coalition.side.BLUE]:Remove() MenuCoalitionQRA[coalition.side.RED]:Remove() MenuCoalitionQRA = {} end