-- ***************************************************************************** -- ** AirBoss ** -- ********************************************************* -- region AirBossFunctions ----------------------------------------------------- function SwitchCarrierDefCon2(params) local carrierName = params[1] local timeMinutes = params[2] local cvUnit = UNIT:FindByName(carrierName) local cvGroup = cvUnit:GetGroup() cvGroup:OptionROE(ENUMS.ROE.WeaponFree):OptionAlarmStateRed() Jtff_log.info(string.format("CSG : %s DEFCON 2 -> ROE = %d", carrierName, ENUMS.ROE.WeaponFree),"AIRBOSS") SCHEDULER:New( nil, function(carrierName) Jtff_log.info(carrierName .. " switchback to DEFCON 4","AIRBOSS") Jtff_log.info(string.format("CSG : %s DEFCON 4 -> ROE = %d", carrierName, ENUMS.ROE.ReturnFire),"AIRBOSS") UNIT:FindByName(carrierName):GetGroup():OptionROE(ENUMS.ROE.ReturnFire):OptionAlarmStateRed() end, { carrierName }, timeMinutes*60 ) end function ForceCarrierDefCon4(params) local carrierName = params[1] local cvUnit = UNIT:FindByName(carrierName) local cvGroup = cvUnit:GetGroup() cvGroup:OptionROE(ENUMS.ROE.ReturnFire):OptionAlarmStateRed() end --- Get Weather and visibility parameters from a #COORDINATE object and return the case Type based on NATOPS -- @param Core.Point#COORDINATE CVNCoordinates Object to get the weather and visibility parameters from -- @param #number recovery_start Recovery start time in seconds -- @param #number recovery_stop Recovery stop time in seconds -- @return #number caseType Case Type based on NATOPS (1 = Good visibility, 2 = Bad visibility in altitude, 3 = Bad visibility) function GetCaseTypeFromWeather(CVNCoordinates, recovery_start, recovery_stop) --navyNightCondition : Day by default local navyNightCondition = false if (CVNCoordinates) then if (type(tonumber(CVNCoordinates:GetSunset(true))) ~= "nil" and type(tonumber(CVNCoordinates:GetSunrise(true))) ~= "nil" ) then if ((timer.getAbsTime() >= (tonumber(CVNCoordinates:GetSunset(true)) - 30*60)) or (timer.getAbsTime() <= (tonumber(CVNCoordinates:GetSunrise(true)) + 30*60))) then --Navy Night conditions Jtff_log.info("CASE III weather : Navy Night", "AIRBOSS") navyNightCondition = true end if (recovery_stop) then if (recovery_stop > (tonumber(CVNCoordinates:GetSunset(true)) - 30*60)) then --recovery_stop after Navy SunSet Jtff_log.info("CASE III weather : Recovery ending after Navy SunSet", "AIRBOSS") navyNightCondition = true end end if (recovery_start) then if (recovery_start < (tonumber(CVNCoordinates:GetSunrise(true)) + 30*60)) then --recover_start before Navy SunRise Jtff_log.info("CASE III weather : Recovery starting before Navy SunRise","AIRBOSS") navyNightCondition = true end end end if (navyNightCondition) then return 3 end end --récupération de la meteo globale de la mission local weather = env.mission.weather local clouds = weather.clouds local static = weather.atmosphere_type == 0 local visibility = weather.visibility.distance local turbulence = weather.groundTurbulence local dust if weather.enable_dust == true then dust = weather.dust_density else dust = 0 end local fog if weather.enable_fog == true then fog = weather.fog else fog = { thickness = 0, visibility = 0, } end Jtff_log.info(string.format("visibility : %i | cloud : base %i density %i | fog %i,%i | dust %i | precpt: %i", visibility, clouds.base, clouds.density, fog.thickness, fog.visibility, dust, clouds.iprecptns),"AIRBOSS") --détermination de minVisibility en fonction des precipitations local minVisibility = (clouds.iprecptns >= 2) and math.floor(visibility/2) or visibility if (weather.enable_fog and fog.thickness > 0) then minVisibility = math.min(minVisibility,fog.visibility) end if (weather.enable_dust) then minVisibility = math.min(minVisibility,dust) end if (minVisibility > UTILS.NMToMeters(5)) then --minVisibility > 5nm we need to check the cloudbase if (clouds.base > UTILS.FeetToMeters(3000)) then --cloud base > 3000ft -> case I Jtff_log.info("CASE I weather","AIRBOSS") return 1 elseif (clouds.base > UTILS.FeetToMeters(1200)) then --cloud base > 1200ft -> case II Jtff_log.info("CASE II weather","AIRBOSS") return 2 else --cloud base <= 1200ft -> case III Jtff_log.info("CASE III weather","AIRBOSS") return 3 end else --minVisibility <= 5 nm -> Case III Jtff_log.info("CASE III weather","AIRBOSS") return 3 end end function StartRecoveryOnDemand(objAirboss, forced_eventcase, turn_into_wind, durationMinutes) local effectiveeventcase local effective_turn_into_wind if type(turn_into_wind) == 'boolean' then effective_turn_into_wind = turn_into_wind else effective_turn_into_wind = true end local turnBeforeRecoveryDuration = 5 local delayMinutesBeforeRecovery = ( effective_turn_into_wind and 10 ) or 5 local recoveryStart = UTILS.Round(timer.getAbsTime() + delayMinutesBeforeRecovery *60,0) local recoveryStop if type(durationMinutes) == 'number' then recoveryStop = UTILS.Round(recoveryStart + (durationMinutes * 60),0) else recoveryStop = UTILS.Round(recoveryStart + ((objAirboss.customconfig.recoveryops.ondemand.recovery_duration_minutes or 30) * 60),0) end if (forced_eventcase) then --Forcage du case si demandé effectiveeventcase = forced_eventcase else -- Si pas de forcage de case demandé determination par les conditions météo effectiveeventcase = GetCaseTypeFromWeather( objAirboss:GetCoordinate(), recoveryStart - (turnBeforeRecoveryDuration * 60), recoveryStop ) end if effectiveeventcase == 1 then objAirboss:MessageToMarshal('Good visibility : Case 1', objAirboss.customconfig.alias, "", 45, false, 0) objAirboss:SetMaxSectionSize(4) elseif effectiveeventcase == 2 then objAirboss:MessageToMarshal('Bad visibility in altitude : Case 2', objAirboss.customconfig.alias, "", 45, false, 0) objAirboss:SetMaxSectionSize(2) elseif effectiveeventcase == 3 then objAirboss:MessageToMarshal('Bad visibility : Case 3', objAirboss.customconfig.alias, "", 45, false, 0) objAirboss:SetMaxSectionSize(1) else objAirboss:SetMaxSectionSize(1) end Jtff_log.info( string.format( "StartRecoveryOnDemand: turn into wind=%s -- event_case=%d -- recoveryStart=%s, recoveryStop=%s", tostring(effective_turn_into_wind), effectiveeventcase, UTILS.SecondsToClock(recoveryStart, false), UTILS.SecondsToClock(recoveryStop, false) ), "AIRBOSS" ) objAirboss:AddRecoveryWindow( UTILS.SecondsToClock(recoveryStart, false), UTILS.SecondsToClock(recoveryStop, false), effectiveeventcase, objAirboss.customconfig.menurecovery.offset, effective_turn_into_wind, objAirboss.customconfig.menurecovery.windondeck, objAirboss.customconfig.menurecovery.uturn ) end function InitCSGAirbossMenus(airbossObject) local menuCoalitionCSGCommands = {} local rootMenuObject local deckLayoutmenuObject menuCoalitionCSGCommands = MenuCoalitionCSGCommands[UTILS.GetCoalitionName(airbossObject:GetCoalition())] rootMenuObject = MENU_COALITION:New( airbossObject:GetCoalition(), airbossObject.customconfig.alias, menuCoalitionCSGCommands ) MENU_COALITION_COMMAND:New( airbossObject:GetCoalition(), "DEFCON 2 - duration 5 minutes", rootMenuObject, SwitchCarrierDefCon2, { airbossObject.customconfig.carriername, 5 } ) MENU_COALITION_COMMAND:New( airbossObject:GetCoalition(), "DEFCON 2 - duration 10 minutes", rootMenuObject, SwitchCarrierDefCon2, { airbossObject.customconfig.carriername, 10 } ) MENU_COALITION_COMMAND:New( airbossObject:GetCoalition(), "Release : DEFCON 4", rootMenuObject, ForceCarrierDefCon4, { airbossObject.customconfig.carriername } ) if ( (airbossObject.carriertype == AIRBOSS.CarrierType.ROOSEVELT) or (airbossObject.carriertype == AIRBOSS.CarrierType.LINCOLN) or (airbossObject.carriertype == AIRBOSS.CarrierType.WASHINGTON) or (airbossObject.carriertype == AIRBOSS.CarrierType.TRUMAN) ) then deckLayoutmenuObject = MENU_COALITION:New( airbossObject:GetCoalition(), "Deck Layout", rootMenuObject ) MENU_COALITION_COMMAND:New( airbossObject:GetCoalition(), "Clean Layout", deckLayoutmenuObject, CleanDeckLayout, airbossObject ) MENU_COALITION_COMMAND:New( airbossObject:GetCoalition(), "Flex 3 Spawns", deckLayoutmenuObject, FlexDeck3SpawnLayout, airbossObject ) MENU_COALITION_COMMAND:New( airbossObject:GetCoalition(), "Flex 7 Spawns", deckLayoutmenuObject, FlexDeck7SpawnLayout, airbossObject ) MENU_COALITION_COMMAND:New( airbossObject:GetCoalition(), "Launch 7 Spawns", deckLayoutmenuObject, LaunchDeck7SpawnLayout, airbossObject ) MENU_COALITION_COMMAND:New( airbossObject:GetCoalition(), "Launch 1 Spawn", deckLayoutmenuObject, LaunchDeck1SpawnLayout, airbossObject ) MENU_COALITION_COMMAND:New( airbossObject:GetCoalition(), "Wipe All", deckLayoutmenuObject, WipeDeckLayout, airbossObject ) end if (airbossObject.customconfig.recoveryops.mode == 'ondemand') then MENU_COALITION_COMMAND:New( airbossObject:GetCoalition(), "OnDemand Recovery : will start turning in 5 minutes", rootMenuObject, StartRecoveryOnDemand, airbossObject ) MENU_COALITION_COMMAND:New( airbossObject:GetCoalition(), "OnDemand Recovery Forced Case I: will start turning in 5 minutes", rootMenuObject, StartRecoveryOnDemand, airbossObject, 1 ) MENU_COALITION_COMMAND:New( airbossObject:GetCoalition(), "OnDemand Recovery Forced Case II: will start turning in 5 minutes", rootMenuObject, StartRecoveryOnDemand, airbossObject, 2 ) MENU_COALITION_COMMAND:New( airbossObject:GetCoalition(), "OnDemand Recovery Forced Case III: will start turning in 5 minutes", rootMenuObject, StartRecoveryOnDemand, airbossObject, 3 ) end MENU_COALITION_COMMAND:New( airbossObject:GetCoalition(), "Recovery : close the deck and reset Airboss", rootMenuObject, ResetAirbossDeck, airbossObject ) return rootMenuObject, menuCoalitionCSGCommands, deckLayoutmenuObject end function ActivateDL4(customconfig) if (customconfig.dl4) then local cvUnit = UNIT:FindByName(customconfig.carriername) cvUnit:SetCommand( { id = "ActivateLink4", params = { ["unitId"] = cvUnit:GetID(), ["frequency"] = customconfig.dl4.freq * 1000000 }, } ) cvUnit:SetCommand( { id = "ActivateACLS", params = { ["unitId"] = cvUnit:GetID(), }, } ) end end --- Generate a RescueHelo mission object. -- @param #AirBossConfig airbossconfig configuration object. -- @param #boolean manageEscort Manage escort mission creation. -- @return #AUFTRAG rescueheloMission RescueHelo mission object, #AIRWING airwing Airwing object. function GenerateRescueHeloMission(airbossconfig, manageEscort) local generateEscortMission = false if manageEscort == true then generateEscortMission = true end local objAirboss = FindAirbossByUnitName(airbossconfig.carriername) or {} local airwing = objAirboss.airwing local rescueheloMission = AUFTRAG:NewRESCUEHELO(objAirboss.carrier) rescueheloMission:SetRepeat(10) rescueheloMission:SetReturnToLegion(true) rescueheloMission:SetRequiredAssets(1) rescueheloMission:SetName( string.format( "Pedro-%s", airbossconfig.alias ) ) rescueheloMission:SetTeleport(false) rescueheloMission:SetRadio( airbossconfig.integratedpatrols.rescuehelo.freq, radio.modulation.AM ) rescueheloMission:SetEPLRS(true) rescueheloMission:SetVerbosity(JTFF_verbosity_levels[JTFF_LOGLEVEL]) rescueheloMission.additionalData = { fuelLowThreshold = 25, } function rescueheloMission:OnAfterScheduled(From, Event, To) local rescueheloOpsGroup = self:GetOpsGroups()[1] local airwing = rescueheloOpsGroup:GetAirwing() for _, _opsGroup in ipairs(self:GetOpsGroups()) do _opsGroup:SetFuelLowThreshold(self.additionalData.fuelLowThreshold) _opsGroup:SetFuelCriticalThreshold(self.additionalData.fuelLowThreshold-10) _opsGroup:SetFuelLowRefuel(false) _opsGroup:SetFuelLowRTB(false) _opsGroup:SetFuelCriticalRTB(false) _opsGroup:SetDespawnAfterLanding() _opsGroup:SetHomebase(_opsGroup:GetAirwing().airboss.carrier:GetName()) _opsGroup:SetDestinationbase(_opsGroup:GetAirwing().airboss.carrier:GetName()) function _opsGroup:OnAfterFuelLow(From, Event, To) Jtff_log.debug( string.format( "RescueHelo LowFuel" ), "AIRBOSS" ) local currenttask=self:GetTaskCurrent() currenttask.formation.airbase=self.homebase currenttask.formation:RTB() end function _opsGroup:OnAfterFuelCritical(From, Event, To) Jtff_log.debug( string.format( "RescueHelo CriticalFuel" ), "AIRBOSS" ) local currenttask=self:GetTaskCurrent() currenttask.formation.airbase=self.homebase currenttask.formation:RTB() end end end return rescueheloMission, airwing end --- Generate a Naval AWACS mission object. -- @param #AirBossConfig airbossconfig configuration object. -- @param #boolean manageEscort Manage escort mission creation. -- @return #AUFTRAG navalAwacsMission Naval AWACS mission object, #AIRWING airwing Airwing object. function GenerateNavalAwacsMission(airbossconfig, manageEscort) local generateEscortMission = false if manageEscort == true then generateEscortMission = true end local objAirboss = FindAirbossByUnitName(airbossconfig.carriername) or {} local airwing = objAirboss.airwing local navalAwacsMission = nil if airwing == nil then Jtff_log.error( string.format( "Airwing not found for AirBoss %s, skipping navalAwacs mission generation", airbossconfig.alias ), "AIRBOSS" ) return nil,nil else navalAwacsMission = AUFTRAG:NewAWACS( objAirboss.carrier, airbossconfig.integratedpatrols.awacs.racetrack.fl * 100, airbossconfig.integratedpatrols.awacs.racetrack.speed, nil, math.abs(airbossconfig.integratedpatrols.awacs.racetrack.front + airbossconfig.integratedpatrols.awacs.racetrack.back) ) or {} local offsetAngle local offsetDist=math.abs(airbossconfig.integratedpatrols.awacs.racetrack.back) if (airbossconfig.integratedpatrols.awacs.racetrack.back >= 0) then offsetAngle = 180 else offsetAngle = 0 end local orbitDeltaR = UTILS.NMToMeters(5) local offsetVec2={r=UTILS.NMToMeters(offsetDist), phi=offsetAngle} navalAwacsMission.orbitOffsetVec2=offsetVec2 navalAwacsMission.orbitDeltaR=orbitDeltaR navalAwacsMission:SetDuration(airbossconfig.integratedpatrols.awacs.missionmaxduration * 60) navalAwacsMission:SetRepeat(10) navalAwacsMission:SetReturnToLegion(true) navalAwacsMission:SetRequiredAssets(1) navalAwacsMission:SetName( string.format( "NavalAwacs-%s", airbossconfig.alias ) ) navalAwacsMission:SetRadio( airbossconfig.integratedpatrols.awacs.radio.freq, airbossconfig.integratedpatrols.awacs.radio.modulation ) navalAwacsMission:SetTACAN( airbossconfig.integratedpatrols.awacs.tacan.channel, airbossconfig.integratedpatrols.awacs.tacan.morse, nil, airbossconfig.integratedpatrols.awacs.tacan.band ) navalAwacsMission:SetEPLRS(true) navalAwacsMission:SetEmission(true) navalAwacsMission:SetVerbosity(JTFF_verbosity_levels[JTFF_LOGLEVEL]) -- navalAwacsMission:SetEnableMarkers(objAirboss.carrier:GetCoalition()) navalAwacsMission.additionalData = { minEscort = airbossconfig.integratedpatrols.awacs.nbEscort, maxEscort = airbossconfig.integratedpatrols.awacs.nbEscort, awacsDuration = airbossconfig.integratedpatrols.awacs.missionmaxduration, callsign = airbossconfig.integratedpatrols.awacs.callsign, awacsTacan = airbossconfig.integratedpatrols.awacs.tacan, awacsFuelLowThreshold = airbossconfig.integratedpatrols.awacs.fuelLowThreshold, awacsSpeed = airbossconfig.integratedpatrols.awacs.racetrack.speed, } function navalAwacsMission:OnAfterScheduled(From, Event, To) local navalAwacsOpsGroup = self:GetOpsGroups()[1] local airwing = navalAwacsOpsGroup:GetAirwing() local airboss_name = airwing.airboss.carrier:GetName() navalAwacsOpsGroup:SwitchCallsign( self.additionalData.callsign.name, self.additionalData.callsign.number ) for _, _opsGroup in ipairs(self:GetOpsGroups()) do _opsGroup:SetFuelLowThreshold(self.additionalData.awacsFuelLowThreshold) _opsGroup:SetFuelCriticalThreshold(self.additionalData.awacsFuelLowThreshold-10) _opsGroup:SetFuelLowRefuel(true) _opsGroup:SetFuelLowRTB(true) _opsGroup:SetFuelCriticalRTB(true) _opsGroup:SetDespawnAfterLanding() _opsGroup:SetHomebase( AIRBASE:FindByName(airboss_name) ) _opsGroup:SetAirboss( FindAirbossByUnitName(airboss_name) ) end if self.additionalData.maxEscort > 0 and generateEscortMission == true then local escortMission = AUFTRAG:NewESCORT( navalAwacsOpsGroup, {x=250, y=100, z=400}, 40, {'Air'} ) escortMission:SetMissionSpeed(self.additionalData.awacsSpeed) escortMission:SetReturnToLegion(true) escortMission:SetRequiredAssets(self.additionalData.minEscort, self.additionalData.maxEscort) escortMission:SetName( string.format("Escort-NavalAwacs-%s", navalAwacsOpsGroup:GetName()) ) escortMission:SetEPLRS(true) escortMission:SetEmission(true) escortMission:SetVerbosity(JTFF_verbosity_levels[JTFF_LOGLEVEL]) escortMission:SetFormation(ENUMS.Formation.FixedWing.EchelonRight.Close) escortMission:SetMissionAltitude(math.floor(math.random(10000, 25000))) escortMission:SetMissionWaypointRandomization(UTILS.NMToMeters(math.random(30, 50))) escortMission:SetTime( 180, (self.additionalData.awacsDuration*60)+960 ) function escortMission:OnAfterScheduled(From, Event, To) for _, opsGroup in ipairs(self:GetOpsGroups()) do local airwing = opsGroup:GetAirwing() local airboss_name = airwing.airboss.carrier:GetName() opsGroup:SetHomebase( AIRBASE:FindByName(airboss_name) ) opsGroup:SetAirboss( FindAirbossByUnitName(airboss_name) ) opsGroup:SetFuelLowThreshold(30) opsGroup:SetFuelCriticalThreshold(25) opsGroup:SetJettisonEmptyTanks(false) opsGroup:SetFuelLowRefuel(false) opsGroup:SetFuelLowRTB(true) opsGroup:SetFuelCriticalRTB(true) opsGroup:SetDespawnAfterLanding() function opsGroup:OnBeforeRTB(From, Event, To, airBase, speedTo, speedHold, speedLand) local airwing = self:GetAirwing() local airboss = FindAirbossByUnitName( airwing.airboss.carrier:GetName() ) or {} local mission = self:GetMissionCurrent() if mission then mission:SetRepeat(mission.Nrepeat+1) Jtff_log.debug( string.format( "%s mission is done (RTB requested). Repeating it", mission:GetName() ), "AIRBOSS" ) end local nextRecoveryStart, nextRecoveryStop = airboss:GetNextRecoveryTime( true ) if nextRecoveryStart == -1 then nextRecoveryStart = timer.getAbsTime() + 24 * 3600 end if ( not(airboss:IsRecovering()) and nextRecoveryStart > timer.getAbsTime() + 15 * 60) or (airboss:IsRecovering() and nextRecoveryStop < timer.getAbsTime() + 20 * 60) then Jtff_log.debug( string.format( "Airboss %s not recovering any time soon (next is %s) : requesting Ondemand recovery", airwing.airboss.alias, UTILS.SecondsToClock((nextRecoveryStart), false) ), "AIRBOSS" ) StartRecoveryOnDemand(airboss, nil, false, UTILS.Round(airboss.customconfig.menurecovery.duration,0)) else Jtff_log.debug( string.format( "Airboss %s is or will be recovering at %s : %s is going to Marshall in the mean time", airwing.airboss.alias, UTILS.SecondsToClock((nextRecoveryStart), false), self:GetName() ), "AIRBOSS" ) end end end end airwing:AddMission(escortMission) self.JTFFEscortmission = escortMission end end end return navalAwacsMission, airwing end --- Generate a Tanker mission object. -- @param #AirBossConfig airbossconfig configuration object. -- @param #boolean manageEscort Manage escort mission creation. -- @return #AUFTRAG tankermission Recovery Tanker mission object, #AIRWING airwing Airwing object. function GenerateRecoveryTankerMission(airbossconfig, manageEscort) local generateEscortMission = false if manageEscort == true then generateEscortMission = true end local objAirboss = FindAirbossByUnitName(airbossconfig.carriername) or {} local airwing = objAirboss.airwing local tankermission = nil if airwing == nil then Jtff_log.error( string.format( "Airwing not found for AirBoss %s, skipping tanker mission generation", airbossconfig.alias ), "AIRBOSS" ) return nil,nil else local offsetAngle local offsetDist=math.abs( airbossconfig.integratedpatrols.tanker.racetrack.back ) if (airbossconfig.integratedpatrols.tanker.racetrack.back >= 0) then offsetAngle = 180 else offsetAngle = 0 end tankermission = AUFTRAG:NewRECOVERYTANKER( objAirboss.carrier, airbossconfig.integratedpatrols.tanker.racetrack.fl * 100, airbossconfig.integratedpatrols.tanker.racetrack.speed, math.abs(airbossconfig.integratedpatrols.tanker.racetrack.front + airbossconfig.integratedpatrols.tanker.racetrack.back), nil, offsetDist, offsetAngle, 5 ) tankermission:SetDuration(airbossconfig.integratedpatrols.tanker.missionmaxduration * 60) tankermission:SetRepeat(10) tankermission:SetReturnToLegion(true) tankermission:SetRequiredAssets(1) tankermission:SetName( string.format( "RecoveryTanker-%s", airbossconfig.alias ) ) tankermission:SetRadio( airbossconfig.integratedpatrols.tanker.radio.freq, airbossconfig.integratedpatrols.tanker.radio.modulation ) tankermission:SetTACAN( airbossconfig.integratedpatrols.tanker.tacan.channel, airbossconfig.integratedpatrols.tanker.tacan.morse, nil, airbossconfig.integratedpatrols.tanker.tacan.band ) tankermission:SetEPLRS(true) tankermission:SetEmission(false) tankermission:SetVerbosity(JTFF_verbosity_levels[JTFF_LOGLEVEL]) -- tankermission:SetEnableMarkers(objAirboss.carrier:GetCoalition()) -- if airbossconfig.integratedpatrols.tanker.escorts > 5000 and generateEscortMission == true then -- tankermission:SetRequiredEscorts( -- airbossconfig.integratedpatrols.tanker.nbEscort, -- airbossconfig.integratedpatrols.tanker.nbEscort, -- AUFTRAG.Type.ESCORT, -- {"Air"}, -- 35 -- ) -- end tankermission.additionalData = { minEscort = airbossconfig.integratedpatrols.tanker.nbEscort, maxEscort = airbossconfig.integratedpatrols.tanker.nbEscort, tankerDuration = airbossconfig.integratedpatrols.tanker.missionmaxduration, callsign = airbossconfig.integratedpatrols.tanker.callsign, tankerTacan = airbossconfig.integratedpatrols.tanker.tacan, tankerFuelLowThreshold = airbossconfig.integratedpatrols.tanker.fuelLowThreshold, tankerSpeed = airbossconfig.integratedpatrols.tanker.racetrack.speed, } function tankermission:OnAfterScheduled(From, Event, To) local tankerOpsGroup = self:GetOpsGroups()[1] local airwing = tankerOpsGroup:GetAirwing() tankerOpsGroup:SwitchCallsign( self.additionalData.callsign.name, self.additionalData.callsign.number ) for _, _opsGroup in ipairs(self:GetOpsGroups()) do _opsGroup:SetFuelLowThreshold(self.additionalData.tankerFuelLowThreshold) _opsGroup:SetFuelCriticalThreshold(self.additionalData.tankerFuelLowThreshold-10) _opsGroup:SetFuelLowRefuel(false) _opsGroup:SetFuelLowRTB(true) _opsGroup:SetFuelCriticalRTB(true) _opsGroup:SetDespawnAfterLanding() end if self.additionalData.maxEscort > 0 and generateEscortMission == true then local escortMission = AUFTRAG:NewESCORT( tankerOpsGroup, {x=250, y=100, z=400}, 35, {'Air'} ) escortMission:SetReturnToLegion(true) escortMission:SetMissionSpeed(self.additionalData.tankerSpeed) escortMission:SetRequiredAssets(self.additionalData.minEscort, self.additionalData.maxEscort) escortMission:SetName("Escort-RecoveryTanker-%s" .. tankerOpsGroup:GetName()) escortMission:SetEPLRS(true) escortMission:SetEmission(true) escortMission:SetVerbosity(JTFF_verbosity_levels[JTFF_LOGLEVEL]) escortMission:SetFormation(ENUMS.Formation.FixedWing.EchelonRight.Close) escortMission:SetMissionAltitude(math.floor(math.random(10000, 25000))) escortMission:SetMissionWaypointRandomization(UTILS.NMToMeters(math.random(30, 50))) escortMission:SetProhibitAfterburnerExecutePhase() escortMission:SetTime( 180, (self.additionalData.tankerDuration*60)+960 ) function escortMission:OnAfterScheduled(From, Event, To) for _, opsGroup in ipairs(self:GetOpsGroups()) do opsGroup:SetHomebase( AIRBASE:FindByName( opsGroup:GetAirwing().airboss.carrier:GetName() ) ) opsGroup:SetAirboss( FindAirbossByUnitName( opsGroup:GetAirwing().airboss.carrier:GetName() ) ) opsGroup:SetFuelLowThreshold(30) opsGroup:SetFuelCriticalThreshold(20) opsGroup:SetFuelLowRefuel(false) opsGroup:SetFuelLowRTB(true) opsGroup:SetFuelCriticalRTB(true) opsGroup:SetJettisonEmptyTanks(false) opsGroup:SetJettisonWeapons(false) opsGroup:SetDespawnAfterLanding() function opsGroup:OnBeforeRTB(From, Event, To, airBase, speedTo, speedHold, speedLand) local airwing = self:GetAirwing() local airboss = FindAirbossByUnitName( airwing.airboss.carrier:GetName() ) or {} local mission = self:GetMissionCurrent() if mission then mission:SetRepeat(mission.Nrepeat+1) Jtff_log.debug( string.format( "%s mission is done. Repeating it", mission:GetName() ), "AIRBOSS" ) end local nextRecoveryStart, nextRecoveryStop = airboss:GetNextRecoveryTime( true ) if nextRecoveryStart == -1 then nextRecoveryStart = timer.getAbsTime() + 24 * 3600 end if ( not(airboss:IsRecovering()) and nextRecoveryStart > timer.getAbsTime() + 15 * 60) or (airboss:IsRecovering() and nextRecoveryStop < timer.getAbsTime() + 20 * 60) then Jtff_log.debug( string.format( "Airboss %s not recovering any time soon (next is %s) : requesting Ondemand recovery", airwing.airboss.alias, UTILS.SecondsToClock((nextRecoveryStart), false) ), "AIRBOSS" ) StartRecoveryOnDemand(airboss, nil, false, UTILS.Round(airboss.customconfig.menurecovery.duration,0)) else Jtff_log.debug( string.format( "Airboss %s is or will be recovering at %s : %s is going to Marshall in the mean time", airwing.airboss.alias, UTILS.SecondsToClock((nextRecoveryStart), false), self:GetName() ), "AIRBOSS" ) end end end end airwing:AddMission(escortMission) self.JTFFEscortmission = escortMission end end function tankermission:OnAfterCancel(From, Event, To) Jtff_log.debug( string.format( "Recovery Tanker %s is done", self:GetName() ), "AIRBOSS" ) local tankerOpsGroup = self:GetOpsGroups()[1] local airwing = tankerOpsGroup:GetAirwing() local airboss = FindAirbossByUnitName(airwing.airboss.carrier:GetName()) or {} local nextRecoveryStart, nextRecoveryStop = airboss:GetNextRecoveryTime( true ) if nextRecoveryStart == -1 then nextRecoveryStart = timer.getAbsTime() + 24 * 3600 end if ( not(airboss:IsRecovering()) and nextRecoveryStart > timer.getAbsTime() + 15 * 60) or (airboss:IsRecovering() and nextRecoveryStop < timer.getAbsTime() + 20 * 60) then Jtff_log.debug( string.format( "Airboss %s not recovering any time soon (next is %s) : requesting Ondemand recovery", airwing.airboss.alias, UTILS.SecondsToClock((nextRecoveryStart), false) ), "AIRBOSS" ) StartRecoveryOnDemand(airboss, nil, false, UTILS.Round(airboss.customconfig.menurecovery.duration,0)) else Jtff_log.debug( string.format( "Airboss %s is or will be recovering at %s : %s is going to Marshall in the mean time", airwing.airboss.alias, UTILS.SecondsToClock((nextRecoveryStart), false), self:GetName() ), "AIRBOSS" ) end airboss.airwing:RemoveMission(self.JTFFEscortmission) end return tankermission, objAirboss.airwing end end -- endregion AirBossFunctions -- region AirBossDeckFunctions ------------------------------------------------- function WipeDeckLayout(objAirboss) local statObj = coalition.getStaticObjects(objAirboss:GetCoalition()) Jtff_log.info(string.format("CSG : %s Wiping Deck Layout...", objAirboss.customconfig.alias),"AIRBOSS") for i, static in pairs(statObj) do local staticName = static:getName() if string.match(staticName, objAirboss.customconfig.alias .. "_Deck_Layout_.*") then Jtff_log.debug(string.format("CSG : %s Deleting Static %s.", objAirboss.customconfig.alias, staticName),"AIRBOSS") static:destroy() end end Jtff_log.info(string.format("CSG : %s Deck Layout is shining clean", objAirboss.customconfig.alias),"AIRBOSS") end function SpawnDeckLayout_Zone(objAirboss, zoneName, layoutData) Jtff_log.info(string.format("CSG : %s Deck Layout " .. zoneName .. " Spawning Layout...", objAirboss.customconfig.alias),"AIRBOSS") SpawnStaticListWithUnitLink( layoutData, objAirboss.customconfig.alias .. "_Deck_Layout_" .. zoneName, objAirboss.carrier:GetID(), objAirboss.carrier:GetCountry() ) Jtff_log.info(string.format("CSG : %s Deck Layout " .. zoneName .. " is up and ready.", objAirboss.customconfig.alias),"AIRBOSS") end function CleanDeckLayout(objAirboss) WipeDeckLayout(objAirboss) local lsoLayout = { { ["category"] = "Personnel", ["offsets"] = { ["y"] = -22.642253013093, ["angle"] = 9.4630780121514, ["x"] = -129.54834584263, }, -- end of ["offsets"] ["shape_name"] = "carrier_lso_usa", ["type"] = "Carrier LSO Personell", }, { ["category"] = "Personnel", ["offsets"] = { ["y"] = -21.045047881828, ["angle"] = 46.778217419792, ["x"] = -130.21820141007, }, -- end of ["offsets"] ["shape_name"] = "carrier_lso1_usa", ["type"] = "Carrier LSO Personell 1", }, { ["category"] = "Personnel", ["offsets"] = { ["y"] = -21.839557639788, ["angle"] = 9.3409049645118, ["x"] = -129.49332696254, }, -- end of ["offsets"] ["shape_name"] = "carrier_lso2_usa", ["type"] = "Carrier LSO Personell 2", }, { ["category"] = "Personnel", ["offsets"] = { ["y"] = -22.255108049524, ["angle"] = 46.673497664673, ["x"] = -130.45775733336, }, -- end of ["offsets"] ["shape_name"] = "carrier_lso3_usa", ["type"] = "Carrier LSO Personell 3", }, { ["category"] = "Personnel", ["offsets"] = { ["y"] = -20.754178989515, ["angle"] = 45.957912671355, ["x"] = -129.34298284226, }, -- end of ["offsets"] ["shape_name"] = "carrier_lso4_usa", ["type"] = "Carrier LSO Personell 4", }, { ["category"] = "Personnel", ["offsets"] = { ["y"] = -21.663772743992, ["angle"] = 46.778217419792, ["x"] = -130.12909556276, }, -- end of ["offsets"] ["shape_name"] = "carrier_lso5_usa", ["type"] = "Carrier LSO Personell 5", }, } SpawnDeckLayout_Zone(objAirboss, "LSO", lsoLayout) local islandLayout = { { ["livery_id"] = "usn hsm-70", ["category"] = "Helicopters", ["offsets"] = { ["y"] = 19.24072690114, ["angle"] = 114.52850551587, ["x"] = -62.06548607626, }, -- end of ["offsets"] ["type"] = "S_70B_Seahawk", }, { ["livery_id"] = "usn hsc-9", ["category"] = "Helicopters", ["offsets"] = { ["y"] = 18.832155465242, ["angle"] = 114.54595880839, ["x"] = -58.356914646729, }, -- end of ["offsets"] ["type"] = "S_70B_Seahawk", }, { ["livery_id"] = "usn hsm-70", ["category"] = "Helicopters", ["offsets"] = { ["y"] = 18.463584036166, ["angle"] = 114.52850551587, ["x"] = -54.716914652369, }, -- end of ["offsets"] ["type"] = "S_70B_Seahawk", }, { ["livery_id"] = "usn hsc-9", ["category"] = "Helicopters", ["offsets"] = { ["y"] = 18.063584040064, ["angle"] = 114.54595880839, ["x"] = -51.002628938575, }, -- end of ["offsets"] ["type"] = "S_70B_Seahawk", }, { ["livery_id"] = "vaw-124_161781 - used", ["category"] = "Planes", ["offsets"] = { ["y"] = 14.988698327991, ["angle"] = 117.80972450962, ["x"] = -35.311371789741, }, -- end of ["offsets"] ["type"] = "E-2C", }, } SpawnDeckLayout_Zone(objAirboss, "Island", islandLayout) local fingerLayout = { { ["category"] = "ADEquipment", ["offsets"] = { ["y"] = -33.184467034521, ["angle"] = 57.655506497452, ["x"] = -118.52125502295, }, -- end of ["offsets"] ["type"] = "CV_59_MD3", }, { ["category"] = "ADEquipment", ["offsets"] = { ["y"] = -28.268248410584, ["angle"] = 57.655506497452, ["x"] = -119.29494811179, }, -- end of ["offsets"] ["type"] = "CV_59_MD3", }, { ["category"] = "Personnel", ["offsets"] = { ["y"] = -26.53677632134, ["angle"] = 52.398177611214, ["x"] = -121.48120753936, }, -- end of ["offsets"] ["shape_name"] = "carrier_airboss_USA", ["type"] = "Carrier Airboss", }, { ["category"] = "Personnel", ["offsets"] = { ["y"] = -25.971342876553, ["angle"] = 53.515188332491, ["x"] = -120.90243392733, }, -- end of ["offsets"] ["shape_name"] = "carrier_tech_USA", ["type"] = "us carrier tech", }, { ["category"] = "Personnel", ["offsets"] = { ["y"] = -28.356878435401, ["angle"] = 55.993555870323, ["x"] = -117.49430504236, }, -- end of ["offsets"] ["shape_name"] = "carrier_tech_USA", ["type"] = "us carrier tech", }, } SpawnDeckLayout_Zone(objAirboss, "Finger", fingerLayout) local junkyardLayout = { { ["livery_id"] = "jtff_vf-84_91_206", ["category"] = "Planes", ["offsets"] = { ["y"] = 23.868126250806, ["angle"] = 118.38568316278, ["x"] = -76.849637038031, }, -- end of ["offsets"] ["type"] = "F-14B", }, { ["category"] = "ADEquipment", ["offsets"] = { ["y"] = 17.139018329887, ["angle"] = 58.035591095156, ["x"] = -65.813777735393, }, -- end of ["offsets"] ["type"] = "AS32-31A", }, { ["category"] = "ADEquipment", ["offsets"] = { ["y"] = 24.120499629569, ["angle"] = 61.561156184185, ["x"] = -69.203061035979, }, -- end of ["offsets"] ["type"] = "CV_59_NS60", }, { ["category"] = "Personnel", ["offsets"] = { ["y"] = 17.879791797899, ["angle"] = 50.6353950667, ["x"] = -74.475258571736, }, -- end of ["offsets"] ["shape_name"] = "carrier_tech_USA", ["type"] = "us carrier tech", }, { ["category"] = "Personnel", ["offsets"] = { ["y"] = 25.619087114483, ["angle"] = 54.614745761248, ["x"] = -66.508616271609, }, -- end of ["offsets"] ["shape_name"] = "carrier_seaman_USA", ["type"] = "Carrier Seaman", }, { ["category"] = "Personnel", ["offsets"] = { ["y"] = 34.051254809191, ["angle"] = 53.968973938009, ["x"] = -81.499626394137, }, -- end of ["offsets"] ["shape_name"] = "carrier_seaman_USA", ["type"] = "Carrier Seaman", }, { ["category"] = "Personnel", ["offsets"] = { ["y"] = 17.570464910906, ["angle"] = 54.614745761248, ["x"] = -69.116627300857, }, -- end of ["offsets"] ["shape_name"] = "carrier_tech_USA", ["type"] = "us carrier tech", }, { ["category"] = "Personnel", ["offsets"] = { ["y"] = 17.633141928651, ["angle"] = 54.527479298648, ["x"] = -69.719469345642, }, -- end of ["offsets"] ["shape_name"] = "carrier_tech_USA", ["type"] = "us carrier tech", }, } SpawnDeckLayout_Zone(objAirboss, "Junkyard", junkyardLayout) end function FlexDeck3SpawnLayout(objAirboss) FlexDeck7SpawnLayout(objAirboss) local elev2Layout = { { ["livery_id"] = "vaq-137 co 158805", ["category"] = "Planes", ["offsets"] = { ["y"] = 33.621846629802, ["angle"] = 4.7080880359424, ["x"] = -13.423541233483, }, -- end of ["offsets"] ["type"] = "EA_6B", }, { ["livery_id"] = "JTFF VFA-83 Line", ["category"] = "Planes", ["offsets"] = { ["y"] = 32.375666919525, ["angle"] = 4.7080880359424, ["x"] = -25.647526329967, }, -- end of ["offsets"] ["type"] = "FA-18C_hornet", }, { ["livery_id"] = "vfa-87", ["category"] = "Planes", ["offsets"] = { ["y"] = 30.99658128452, ["angle"] = 1.5490420898326, ["x"] = -19.557373659788, }, -- end of ["offsets"] ["type"] = "FA-18C_hornet", }, } SpawnDeckLayout_Zone(objAirboss, "Elev2", elev2Layout) local elev4Layout = { { ["livery_id"] = "jtff_vf-84_91_200", ["category"] = "Planes", ["offsets"] = { ["y"] = -34.152985378431, ["angle"] = 20.423745714676, ["x"] = -110.45527895631, }, -- end of ["offsets"] ["type"] = "F-14B", }, { ["livery_id"] = "jtff_vf-84_91_201", ["category"] = "Planes", ["offsets"] = { ["y"] = -33.777172440145, ["angle"] = 20.423745714676, ["x"] = -96.916883461044, }, -- end of ["offsets"] ["type"] = "F-14B", }, { ["livery_id"] = "jtff_vf-84_91_202", ["category"] = "Planes", ["offsets"] = { ["y"] = -29.525479966254, ["angle"] = 23.565338368266, ["x"] = -103.85620127214, }, -- end of ["offsets"] ["type"] = "F-14B", }, { ["category"] = "ADEquipment", ["offsets"] = { ["y"] = -25.86192061308, ["angle"] = 13.686774801978, ["x"] = -113.12563229675, }, -- end of ["offsets"] ["type"] = "AS32-p25", }, } SpawnDeckLayout_Zone(objAirboss, "Elev4", elev4Layout) end function FlexDeck7SpawnLayout(objAirboss) CleanDeckLayout(objAirboss) local pointLayout = { { ["category"] = "ADEquipment", ["offsets"] = { ["y"] = 28.099036062225, ["angle"] = 4.5335551107429, ["x"] = 73.682386210606, }, -- end of ["offsets"] ["type"] = "AS32-32A", }, { ["livery_id"] = "marines 06 cb 161352", ["category"] = "Planes", ["offsets"] = { ["y"] = 33.012193853945, ["angle"] = 4.7080880359424, ["x"] = 48.207851738378, }, -- end of ["offsets"] ["type"] = "EA_6B", }, { ["livery_id"] = "vaq-132 ae 605", ["category"] = "Planes", ["offsets"] = { ["y"] = 32.037151622558, ["angle"] = 4.2368491379039, ["x"] = 65.674634840866, }, -- end of ["offsets"] ["type"] = "EA_6B", }, { ["livery_id"] = "vaq-132 ae 604", ["category"] = "Planes", ["offsets"] = { ["y"] = 33.063487980478, ["angle"] = 4.7080880359424, ["x"] = 56.912418720699, }, -- end of ["offsets"] ["type"] = "EA_6B", }, { ["livery_id"] = "vaq-132 ae 605", ["category"] = "Planes", ["offsets"] = { ["y"] = 24.590838184328, ["angle"] = 4.0623162127045, ["x"] = 78.22761285087, }, -- end of ["offsets"] ["type"] = "EA_6B", }, } SpawnDeckLayout_Zone(objAirboss, "Point", pointLayout) local cat1Layout = { { ["livery_id"] = "JTFF VFA-83 Line", ["category"] = "Planes", ["offsets"] = { ["y"] = 10.075737706355, ["angle"] = 116.53563415567, ["x"] = 145.80306508157, }, -- end of ["offsets"] ["type"] = "FA-18C_hornet", }, { ["livery_id"] = "JTFF VFA-83 Line", ["category"] = "Planes", ["offsets"] = { ["y"] = 10.541399489708, ["angle"] = 116.53563415567, ["x"] = 132.19996926151, }, -- end of ["offsets"] ["type"] = "FA-18C_hornet", }, { ["livery_id"] = "JTFF VFA-83 Line", ["category"] = "Planes", ["offsets"] = { ["y"] = 10.98313938269, ["angle"] = 116.53563415567, ["x"] = 118.83733772334, }, -- end of ["offsets"] ["type"] = "FA-18C_hornet", }, { ["livery_id"] = "VFA-87", ["category"] = "Planes", ["offsets"] = { ["y"] = 12.59068314775, ["angle"] = 116.53563415567, ["x"] = 106.45353714522, }, -- end of ["offsets"] ["type"] = "FA-18C_hornet", }, { ["livery_id"] = "VFA-87", ["category"] = "Planes", ["offsets"] = { ["y"] = 14.419254583666, ["angle"] = 116.53563415567, ["x"] = 94.424965715345, }, -- end of ["offsets"] ["type"] = "FA-18C_hornet", }, { ["livery_id"] = "VFA-87", ["category"] = "Planes", ["offsets"] = { ["y"] = 18.643328055733, ["angle"] = 116.53563415567, ["x"] = 83.777230709954, }, -- end of ["offsets"] ["type"] = "FA-18C_hornet", }, } SpawnDeckLayout_Zone(objAirboss, "Cat1", cat1Layout) local patioLayout = { { ["livery_id"] = "VF-41 AJ102 1981 base Reflected", ["category"] = "Planes", ["offsets"] = { ["y"] = 28.093555953897, ["angle"] = 118.10643048246, ["x"] = -116.21846377714, }, -- end of ["offsets"] ["type"] = "F-14B", }, { ["livery_id"] = "jtff_vf-84_91_205", ["category"] = "Planes", ["offsets"] = { ["y"] = 26.33823811991, ["angle"] = 118.50785621042, ["x"] = -125.6398759805, }, -- end of ["offsets"] ["type"] = "F-14B", }, { ["livery_id"] = "VF-41 AJ107 1981 base Reflected", ["category"] = "Planes", ["offsets"] = { ["y"] = 24.866259756495, ["angle"] = 118.90928193838, ["x"] = -137.27914171635, }, -- end of ["offsets"] ["type"] = "F-14B", }, } SpawnDeckLayout_Zone(objAirboss, "Patio", patioLayout) local elev1Layout = { { ["livery_id"] = "VFA-87", ["category"] = "Planes", ["offsets"] = { ["y"] = 31.562717287402, ["angle"] = 117.80972450962, ["x"] = 35.545373177252, }, -- end of ["offsets"] ["type"] = "FA-18C_hornet", }, { ["livery_id"] = "VFA-87", ["category"] = "Planes", ["offsets"] = { ["y"] = 31.631930277644, ["angle"] = 117.80972450962, ["x"] = 26.732753925417, }, -- end of ["offsets"] ["type"] = "FA-18C_hornet", }, { ["livery_id"] = "JTFF VFA-83 Line", ["category"] = "Planes", ["offsets"] = { ["y"] = 31.755826137288, ["angle"] = 117.80972450962, ["x"] = 17.845507060166, }, -- end of ["offsets"] ["type"] = "FA-18C_hornet", }, } SpawnDeckLayout_Zone(objAirboss, "Elev1", elev1Layout) local coralLayout = { { ["livery_id"] = "usn vrc-40", ["category"] = "Planes", ["offsets"] = { ["y"] = 32.194100140318, ["angle"] = 4.6906347434224, ["x"] = 8.026960843163, }, -- end of ["offsets"] ["type"] = "C2A_Greyhound", }, { ["livery_id"] = "usaf - vs-24 - cag", ["category"] = "Planes", ["offsets"] = { ["y"] = 29.785585834077, ["angle"] = 4.6906347434224, ["x"] = -1.16631697175602, }, -- end of ["offsets"] ["type"] = "S-3B Tanker", }, } SpawnDeckLayout_Zone(objAirboss, "Coral", coralLayout) end function LaunchDeck7SpawnLayout(objAirboss) FlexDeck7SpawnLayout(objAirboss) local sternLayout = { { ["livery_id"] = "VF-41 AJ101 1981 base Reflected", ["category"] = "Planes", ["offsets"] = { ["y"] = 17.098132258261, ["angle"] = 18.957669143001, ["x"] = -152.05692527267, }, -- end of ["offsets"] ["type"] = "F-14B", }, { ["livery_id"] = "VF-41 AJ105 1989 by Reflected", ["category"] = "Planes", ["offsets"] = { ["y"] = 6.1548658818238, ["angle"] = 25.101228110021, ["x"] = -154.09653869538, }, -- end of ["offsets"] ["type"] = "F-14B", }, { ["livery_id"] = "VF-41 AJ103 1981 base Reflected", ["category"] = "Planes", ["offsets"] = { ["y"] = -4.3075244605538, ["angle"] = 25.066321524981, ["x"] = -155.52554595995, }, -- end of ["offsets"] ["type"] = "F-14B", }, { ["livery_id"] = "VF-41 AJ106 1981 base Reflected", ["category"] = "Planes", ["offsets"] = { ["y"] = -12.208173326793, ["angle"] = 19.25437511584, ["x"] = -150.18756952213, }, -- end of ["offsets"] ["type"] = "F-14B", }, { ["livery_id"] = "VF-41 AJ110 1981 base Reflected", ["category"] = "Planes", ["offsets"] = { ["y"] = -13.74494937795, ["angle"] = 19.044935605601, ["x"] = -133.97486609459, }, -- end of ["offsets"] ["type"] = "F-14B", }, { ["livery_id"] = "jtff_vf-84_91_203", ["category"] = "Planes", ["offsets"] = { ["y"] = -17.274425547057, ["angle"] = 19.27182840836, ["x"] = -119.46237855354, }, -- end of ["offsets"] ["type"] = "F-14B", }, } SpawnDeckLayout_Zone(objAirboss, "Stern", sternLayout) end function LaunchDeck1SpawnLayout(objAirboss) FlexDeck3SpawnLayout(objAirboss) local elev3Layout = { { ["livery_id"] = "VF-41 AJ100 1989 by Reflected", ["category"] = "Planes", ["offsets"] = { ["y"] = 33.021151110532, ["angle"] = 117.80972450962, ["x"] = -91.343728458666, }, -- end of ["offsets"] ["type"] = "F-14B", }, { ["livery_id"] = "JTFF_VF-84_commemorative_92_207", ["category"] = "Planes", ["offsets"] = { ["y"] = 32.929781304891, ["angle"] = 117.80972450962, ["x"] = -102.40483618017, }, -- end of ["offsets"] ["type"] = "F-14B", }, { ["category"] = "ADEquipment", ["offsets"] = { ["y"] = 29.92471616058, ["angle"] = 58.035591095156, ["x"] = -97.143110662258, }, -- end of ["offsets"] ["type"] = "AS32-31A", }, { ["category"] = "ADEquipment", ["offsets"] = { ["y"] = 24.313863322494, ["angle"] = 13.547148461818, ["x"] = -94.953329922951, }, -- end of ["offsets"] ["type"] = "AS32-p25", }, { ["category"] = "Personnel", ["offsets"] = { ["y"] = 30.933519971295, ["angle"] = 13.582055046858, ["x"] = -95.295382139387, }, -- end of ["offsets"] ["shape_name"] = "carrier_tech_USA", ["type"] = "us carrier tech", }, { ["category"] = "Personnel", ["offsets"] = { ["y"] = 24.943708100079, ["angle"] = 54.614745761248, ["x"] = -97.153104784033, }, -- end of ["offsets"] ["shape_name"] = "carrier_tech_USA", ["type"] = "us carrier tech", }, { ["category"] = "Personnel", ["offsets"] = { ["y"] = 24.411201211127, ["angle"] = 13.582055046858, ["x"] = -97.618770508055, }, -- end of ["offsets"] ["shape_name"] = "carrier_seaman_USA", ["type"] = "Carrier Seaman", }, { ["category"] = "Personnel", ["offsets"] = { ["y"] = 25.438348163322, ["angle"] = 16.11278246225, ["x"] = -100.66033122272, }, -- end of ["offsets"] ["shape_name"] = "carrier_tech_USA", ["type"] = "us carrier tech", }, } SpawnDeckLayout_Zone(objAirboss, "Elev3", elev3Layout) local sternLayout = { { ["livery_id"] = "VF-41 AJ101 1981 base Reflected", ["category"] = "Planes", ["offsets"] = { ["y"] = 17.098132258261, ["angle"] = 18.957669143001, ["x"] = -152.05692527267, }, -- end of ["offsets"] ["type"] = "F-14B", }, { ["livery_id"] = "jtff_vf-84_91_205", ["category"] = "Planes", ["offsets"] = { ["y"] = 6.1548658818238, ["angle"] = 25.101228110021, ["x"] = -154.09653869538, }, -- end of ["offsets"] ["type"] = "F-14B", }, { ["livery_id"] = "VF-41 AJ103 1981 base Reflected", ["category"] = "Planes", ["offsets"] = { ["y"] = -4.3075244605538, ["angle"] = 25.066321524981, ["x"] = -155.52554595995, }, -- end of ["offsets"] ["type"] = "F-14B", }, { ["livery_id"] = "VF-41 AJ106 1981 base Reflected", ["category"] = "Planes", ["offsets"] = { ["y"] = -12.208173326793, ["angle"] = 19.25437511584, ["x"] = -150.18756952213, }, -- end of ["offsets"] ["type"] = "F-14B", }, { ["livery_id"] = "VF-41 AJ110 1981 base Reflected", ["category"] = "Planes", ["offsets"] = { ["y"] = -13.74494937795, ["angle"] = 19.044935605601, ["x"] = -133.97486609459, }, -- end of ["offsets"] ["type"] = "F-14B", }, { ["livery_id"] = "jtff_vf-84_91_203", ["category"] = "Planes", ["offsets"] = { ["y"] = -17.274425547057, ["angle"] = 19.27182840836, ["x"] = -119.46237855354, }, -- end of ["offsets"] ["type"] = "F-14B", }, } SpawnDeckLayout_Zone(objAirboss, "Stern", sternLayout) end function ResetAirbossDeck(objAirboss, delay) local delay_second = delay or 5 Jtff_log.info(string.format("reset all Deck Operations, closing the Deck on %s in %i seconds",objAirboss.customconfig.alias, delay_second),"AIRBOSS") objAirboss:CloseCurrentRecoveryWindow(delay_second) objAirboss:DeleteAllRecoveryWindows(delay_second+1) end -- endregion AirBossDeckFunctions -- region AirBossConfig -------------------------------------------------------- -- @type RescueHeloCallsignConfig -- @field #string alias Alias for the rescuehelo. -- @field #number name CallSign name for the rescuehelo. -- @field #number number CallSign number (1-7) for the rescuehelo. --- Parse an RescueHelo Callsign config Object. -- @param #JsonObject config Config object to parse -- @param #string parser_name Parser name ("AIRBASE", "AIRBOSS", etc...). -- @return #RescueHeloCallsignConfig rescueHeloCallsignConfigJson Parsed CallSign object function ParseRescueHeloCallsignConfigJson(config, parser_name) local rescueHeloCallsignConfigJson = {} local default_number = 1 -- ************************************************************************** -- name -- ************************************************************************** if type(config.name) == "number" then if table.count_value(CALLSIGN.Aircraft, config.name) > 0 then rescueHeloCallsignConfigJson.name = config.name else Jtff_log.warn("RescueHelo Callsign name is not a valid CallSign name, skipping RescueHelo configuration", parser_name) rescueHeloCallsignConfigJson.name = CALLSIGN.Aircraft.Ford end else Jtff_log.error("RescueHelo Callsign name is not valid, skipping RescueHelo configuration", parser_name) return {} end -- ************************************************************************** -- alias -- ************************************************************************** if type(config.alias) == "string" then rescueHeloCallsignConfigJson.alias = config.alias else Jtff_log.warn("RescueHelo Callsign alias is not a string, defaulting to RescueHelo name", parser_name) rescueHeloCallsignConfigJson.alias = "Ford" end -- ************************************************************************** -- number -- ************************************************************************** if type(config.number) == "number" then if config.number >= 1 and config.number <= 7 then rescueHeloCallsignConfigJson.number = config.number else Jtff_log.warn( string.format("RescueHelo Callsign number is not a valid number (1-7), defaulting number to %d", default_number), parser_name) rescueHeloCallsignConfigJson.number = default_number end else Jtff_log.warn(string.format("RescueHelo Callsign number is not a number, defaulting number to %d", default_number), parser_name) rescueHeloCallsignConfigJson.number = default_number end return rescueHeloCallsignConfigJson end -- @type AirbossIntegratedNavalAwacsConfig -- @field #boolean enable Enable AWACS integrated patrol. -- @field #boolean nbEscort Number of escort assets. -- @field #boolean autorespawn Enable auto respawn of the awacs. -- @field #number missionmaxduration Maximum duration of the mission in minutes. -- @field #number fuelLowThreshold Fuel warning level in %. -- @field #TacanConfig tacan TACAN configuration. -- @field #RadioConfig radio Radio configuration. -- @field #RelativeRacetrackConfig racetrack Racetrack configuration for the tanker. -- @field #AwacsCallsignConfig callsign Callsign configuration. --- Parse a Naval AWACS config Object. -- @param #JsonObject config Config object to parse -- @param #string parser_name Parser name ("AIRBASE", "AIRBOSS", etc...). -- @return #AirbossIntegratedNavalAwacsConfig awacsConfigJson Parsed AWACS configuration object function ParseNavalAwacsConfigJson(config, parser_name) local awacsConfigJson = {} local default_enable = false local default_escorts = 1 local default_autorespawn = true local default_missionmaxduration = 180 local default_fuelLowThreshold = 30 local default_callsign = { name = CALLSIGN.AWACS.Darkstar, alias = "Darkstar", number = 4, } -- ************************************************************************** -- enable -- ************************************************************************** if type(config.enable) == "boolean" then awacsConfigJson.enable = config.enable else Jtff_log.warn( "Tanker enable is not a boolean, defaulting to false", parser_name ) awacsConfigJson.enable = default_enable return awacsConfigJson end -- ************************************************************************** -- racetrack -- ************************************************************************** if type(config.racetrack) == "table" then awacsConfigJson.racetrack = ParseRelativeRacetrackConfigJson(config.racetrack, parser_name) if next(awacsConfigJson.racetrack) == nil then Jtff_log.error("AWACS racetrack is not a valid Racetrack object, skipping AWACS configuration", parser_name) config.enable = false return config end else Jtff_log.error("AWACS racetrack is not a table, skipping AWACS configuration", parser_name) config.enable = false return config end -- ************************************************************************** -- nbEscort -- ************************************************************************** if type(config.nbEscort) == "number" then awacsConfigJson.nbEscort = config.nbEscort or default_escorts else Jtff_log.warn( string.format( "AWACS escorts is not a number, defaulting to %d", default_escorts ), parser_name ) awacsConfigJson.nbEscort = default_escorts end -- ************************************************************************** -- autorespawn -- ************************************************************************** if type(config.autorespawn) == "boolean" then awacsConfigJson.autorespawn = config.autorespawn or default_autorespawn else Jtff_log.warn( "AWACS autorespawn is not a boolean, defaulting to false", parser_name ) awacsConfigJson.autorespawn = default_autorespawn end -- ************************************************************************** -- missionmaxduration -- ************************************************************************** if type(config.missionmaxduration) == "number" then awacsConfigJson.missionmaxduration = config.missionmaxduration else Jtff_log.warn( string.format("AWACS missionmaxduration is not a number, defaulting to %d", default_missionmaxduration), parser_name) awacsConfigJson.missionmaxduration = default_missionmaxduration end -- ************************************************************************** -- tacan -- ************************************************************************** if type(config.tacan) == "table" then awacsConfigJson.tacan = ParseTacanConfigJson(config.tacan, parser_name) else Jtff_log.error("AWACS tacan is not a table, skipping AWACS Tacan configuration", parser_name) awacsConfigJson.tacan = {} end -- ************************************************************************** -- radio -- ************************************************************************** if type(config.radio) == "table" then awacsConfigJson.radio = ParseRadioConfigJson(config.radio, parser_name) if next(awacsConfigJson.radio) == nil then Jtff_log.error("AWACS radio is not a valid Radio object, skipping AWACS configuration", parser_name) config.enable = false return config end else Jtff_log.error("AWACS radio is not a table, skipping AWACS configuration", parser_name) config.enable = false return config end -- ************************************************************************** -- fuelLowThreshold -- ************************************************************************** if type(config.fuelLowThreshold) == "number" then awacsConfigJson.fuelLowThreshold = config.fuelLowThreshold else Jtff_log.warn( string.format("AWACS fuelLowThreshold is not a number, defaulting to %d %%", default_fuelLowThreshold), parser_name) awacsConfigJson.fuelLowThreshold = default_fuelLowThreshold end -- ************************************************************************** -- callsign -- ************************************************************************** if type(config.callsign) == "table" then awacsConfigJson.callsign = ParseAwacsCallsignConfigJson(config.callsign, parser_name) if next(awacsConfigJson.callsign) == nil then Jtff_log.warn("AWACS callsign is not a valid Callsign object, defaulting AWACS Callsign configuration", parser_name) awacsConfigJson.callsign = default_callsign end else Jtff_log.warn("AWACS callsign is not a table, skipping AWACS configuration", parser_name) awacsConfigJson.callsign = default_callsign end return awacsConfigJson end -- @type AirbossIntegratedTankerConfig -- @field #boolean enable Enable tanker integrated patrol. -- @field #number nbEscort Number of escort assets. -- @field #boolean autorespawn Enable auto respawn of the tanker. -- @field #number missionmaxduration Maximum duration of the mission in minutes. -- @field #number refuelSystem Refueling system (0=boom, 1=probe). -- @field #TacanConfig tacan TACAN configuration. -- @field #RadioConfig radio Radio configuration. -- @field #number fuelLowThreshold Fuel warning level in %. -- @field #RelativeRacetrackConfig racetrack Racetrack configuration for the tanker. -- @field #TankerCallsignConfig callsign Callsign configuration. --- Parse a Naval tanker config Object. -- @param #JsonObject config Config object to parse -- @param #string parser_name Parser name ("AIRBASE", "AIRBOSS", etc...). -- @return #AirbossIntegratedTankerConfig tankerConfigJson Parsed tanker configuration object function ParseNavalTankerConfigJson(config, parser_name) local tankerConfigJson = {} local default_enable = false local default_escorts = 0 local default_autorespawn = false local default_missionmaxduration = 180 local default_fuelLowThreshold = 30 local default_refuelSystem = Unit.RefuelingSystem.BOOM_AND_RECEPTACLE -- ************************************************************************** -- enable -- ************************************************************************** if type(config.enable) == "boolean" then tankerConfigJson.enable = config.enable else Jtff_log.warn( "Tanker enable is not a boolean, defaulting to false", parser_name ) tankerConfigJson.enable = default_enable return tankerConfigJson end -- ************************************************************************** -- racetrack -- ************************************************************************** if type(config.racetrack) == "table" then tankerConfigJson.racetrack = ParseRelativeRacetrackConfigJson(config.racetrack, parser_name) if next(tankerConfigJson.racetrack) == nil then Jtff_log.error("Tanker racetrack is not a valid Racetrack object, skipping tanker configuration", parser_name) config.enable = false return config end else Jtff_log.error("Tanker racetrack is not a table, skipping tanker configuration", parser_name) config.enable = false return config end -- ************************************************************************** -- nbEscort -- ************************************************************************** if type(config.nbEscort) == "number" then tankerConfigJson.nbEscort = config.nbEscort or default_escorts else Jtff_log.warn( string.format( "Tanker escorts is not a number, defaulting to %d", default_escorts ), parser_name ) tankerConfigJson.nbEscort = default_escorts end -- ************************************************************************** -- autorespawn -- ************************************************************************** if type(config.autorespawn) == "boolean" then tankerConfigJson.autorespawn = config.autorespawn or default_autorespawn else Jtff_log.warn( "Tanker autorespawn is not a boolean, defaulting to false", parser_name ) tankerConfigJson.autorespawn = default_autorespawn end -- ************************************************************************** -- missionmaxduration -- ************************************************************************** if type(config.missionmaxduration) == "number" then tankerConfigJson.missionmaxduration = config.missionmaxduration else Jtff_log.warn( string.format("Tanker missionmaxduration is not a number, defaulting to %d", default_missionmaxduration), parser_name) tankerConfigJson.missionmaxduration = default_missionmaxduration end -- ************************************************************************** -- refuelSystem -- ************************************************************************** if type(config.refuelSystem) == "number" then tankerConfigJson.refuelSystem = config.refuelSystem or default_refuelSystem else tankerConfigJson.refuelSystem = default_refuelSystem end -- ************************************************************************** -- tacan -- ************************************************************************** if type(config.tacan) == "table" then tankerConfigJson.tacan = ParseTacanConfigJson(config.tacan, parser_name) if next(tankerConfigJson.tacan) == nil then Jtff_log.error("Tanker tacan is not a valid TACAN object, skipping tanker configuration", parser_name) config.enable = false return config end else Jtff_log.error("Tanker tacan is not a table, skipping tanker configuration", parser_name) config.enable = false return config end -- ************************************************************************** -- radio -- ************************************************************************** if type(config.radio) == "table" then tankerConfigJson.radio = ParseRadioConfigJson(config.radio, parser_name) if next(tankerConfigJson.radio) == nil then Jtff_log.error("Tanker radio is not a valid Radio object, skipping tanker configuration", parser_name) config.enable = false return config end else Jtff_log.error("Tanker radio is not a table, skipping tanker configuration", parser_name) config.enable = false return config end -- ************************************************************************** -- fuelLowThreshold -- ************************************************************************** if type(config.fuelLowThreshold) == "number" then tankerConfigJson.fuelLowThreshold = config.fuelLowThreshold else Jtff_log.warn( string.format("Tanker fuelLowThreshold is not a number, defaulting to %d %%", default_fuelLowThreshold), parser_name) tankerConfigJson.fuelLowThreshold = default_fuelLowThreshold end -- ************************************************************************** -- callsign -- ************************************************************************** if type(config.callsign) == "table" then tankerConfigJson.callsign = ParseTankerCallsignConfigJson(config.callsign, parser_name) if next(tankerConfigJson.callsign) == nil then Jtff_log.error("Tanker callsign is not a valid Callsign object, skipping tanker configuration", parser_name) config.enable = false return config end else Jtff_log.error("Tanker callsign is not a table, skipping tanker configuration", parser_name) config.enable = false return config end return tankerConfigJson end -- @type AirbossIntegratedRescueHeloConfig -- @field #boolean enable Enable rescuehelo integrated patrol. -- @field #RadioConfig radio Radio configuration. -- @field #RescueHeloCallsignConfig callsign Callsign configuration. --- Parse a Naval rescue Helo config Object. -- @param #JsonObject config Config object to parse -- @param #string parser_name Parser name ("AIRBASE", "AIRBOSS", etc...). -- @return #AirbossIntegratedRescueHeloConfig rescueHeloConfigJson Parsed rescuehelo configuration object function ParseNavalRescueHeloConfigJson(config, parser_name) local rescueHeloConfigJson = {} local default_enable = false local default_radio = { freq = 35.100, modulation = radio.modulation.FM, power = 100, } local default_callsign = { name = CALLSIGN.Aircraft.Ford, alias = "Ford", number = 7, } -- ************************************************************************** -- enable -- ************************************************************************** if type(config.enable) == "boolean" then rescueHeloConfigJson.enable = config.enable else Jtff_log.warn( "RescueHelo enable is not a boolean, defaulting to false", parser_name ) rescueHeloConfigJson.enable = default_enable return rescueHeloConfigJson end -- ************************************************************************** -- radio -- ************************************************************************** if type(config.radio) == "table" then rescueHeloConfigJson.radio = ParseRadioConfigJson(config.radio, parser_name) if next(rescueHeloConfigJson.radio) == nil then Jtff_log.error("RescueHelo radio is not a valid Radio object, skipping tanker configuration", parser_name) config.enable = false return config end else Jtff_log.warn( string.format( "RescueHelo radio is not a table, defaulting RescueHelo configuration to %.3f MHz %s power %d", default_radio.freq, UTILS.GetModulationName(default_radio.modulation), default_radio.power ), parser_name ) rescueHeloConfigJson.radio = default_radio return rescueHeloConfigJson end -- ************************************************************************** -- callsign -- ************************************************************************** if type(config.callsign) == "table" then rescueHeloConfigJson.callsign = ParseRescueHeloCallsignConfigJson(config.callsign, parser_name) if next(rescueHeloConfigJson.callsign) == nil then Jtff_log.warn( string.format( "RescueHelo callsign is not a table, defaulting RescueHelo callsign configuration to %s-%d-1", CALLSIGN.Aircraft.Ford, "Ford", 7 ), parser_name ) rescueHeloConfigJson.callsign = default_callsign end else Jtff_log.warn( string.format( "RescueHelo callsign is not a table, defaulting RescueHelo callsign configuration to %s-%d-1", default_callsign.name, default_callsign.number ), parser_name ) rescueHeloConfigJson.callsign = default_callsign end return rescueHeloConfigJson end -- @type AirbossIntegratedPatrolsConfig -- @field #AirbossTankerConfig tanker tanker integrated patrol configuration. -- @field #AirbossRescueheloConfig rescuehelo rescuehelo integrated patrol configuration. -- @field #AirbossAwacsConfig awacs awacs integrated patrol configuration. --- Parse an naval integrated patrols config Object. -- @param #JsonObject config Config object to parse -- @param #string parser_name Parser name ("AIRBASE", "AIRBOSS", etc...). -- @return #AirbossIntegratedPatrolsConfig integratedPatrolConfigJson Parsed integrated patrols configuration object function ParseIntegratedPatrolConfigJson(config, parser_name) local integratedPatrolConfigJson = {} local default_integratedpatrols = { tanker = { enable = false, }, rescuehelo = { enable = false, }, awacs = { enable = false, }, } -- ************************************************************************** -- tanker -- ************************************************************************** if type(config.tanker) == "table" then integratedPatrolConfigJson.tanker = ParseNavalTankerConfigJson(config.tanker, parser_name) else Jtff_log.warn( "Tanker integrated patrol configuration is not a table, skipping tanker integrated patrol configuration", parser_name ) integratedPatrolConfigJson.tanker = default_integratedpatrols.tanker end -- ************************************************************************** -- rescuehelo -- ************************************************************************** if type(config.rescuehelo) == "table" then integratedPatrolConfigJson.rescuehelo = ParseNavalRescueHeloConfigJson(config.rescuehelo, parser_name) else Jtff_log.warn( "Rescuehelo integrated patrol configuration is not a table, skipping rescuehelo integrated patrol configuration", parser_name ) integratedPatrolConfigJson.rescuehelo = default_integratedpatrols.rescuehelo end -- ************************************************************************** -- awacs -- ************************************************************************** if type(config.awacs) == "table" then integratedPatrolConfigJson.awacs = ParseNavalAwacsConfigJson(config.awacs, parser_name) else Jtff_log.warn( "AWACS integrated patrol configuration is not a table, skipping AWACS integrated patrol configuration", parser_name ) integratedPatrolConfigJson.awacs = default_integratedpatrols.awacs end return integratedPatrolConfigJson end -- @type DL4Config -- @field #number freq DL4 frequency. --- Parse an DL4 config Object. -- @param #JsonObject config Config object to parse -- @param #string parser_name Parser name ("AIRBASE", "AIRBOSS", etc...). -- @return #DL4Config DL4ConfigJson Parsed DL4Config object function ParseDL4ConfigJson(config, parser_name) local DL4ConfigJson = {} local default_freq = 336.000 -- ************************************************************************** -- freq -- ************************************************************************** if type(config.freq) == "number" then DL4ConfigJson.freq = config.freq or default_freq else Jtff_log.error( string.format( "DL4 frequency is not a number, defaulting to %.3f MHz", default_freq ), parser_name ) DL4ConfigJson.freq = default_freq end return DL4ConfigJson end -- @type ICLSConfig -- @field #number channel ICLS channel. -- @field #string morse ICLS morse code. --- Parse an ICLS config Object. -- @param #JsonObject config Config object to parse -- @param #string parser_name Parser name ("AIRBASE", "AIRBOSS", etc...). -- @return #ICLSConfig ICLSConfigJson Parsed ICLSConfig object function ParseICLSConfigJson(config, parser_name) local ICLSConfigJson = {} local default_channel = 11 local default_morse = 'RSVLSO' -- ************************************************************************** -- channel -- ************************************************************************** ICLSConfigJson.channel = config.channel or default_channel -- ************************************************************************** -- morse -- ************************************************************************** ICLSConfigJson.morse = config.morse or default_morse return ICLSConfigJson end -- @type OnDemandRecoveryConfig -- @field #number recovery_duration_minutes Recovery duration in minutes. -- @type CyclicRecoveryConfig -- @field #number event_duration_minutes Event duration in minutes. -- @field #number event_ia_reserved_minutes minutes reserved for IA unit at the begining of the Event. -- type AlphaRecoveryEventConfig -- @field #number recovery_start_minutes Recovery start time in minutes. -- @field #number recovery_duration_minutes Recovery duration in minutes. -- @type AlphaRecoveryConfig -- @field #AlphaRecoveryEventConfig[] recoveries Alpha Strike recovery operations configuration. -- @type RecoveryopsConfig -- @field #string mode Recovery operations mode. -- @field #OnDemandRecoveryConfig ondemand On demand recovery operations configuration. -- @field #AlphaRecoveryConfig alpha Alpha Strike recovery operations configuration. -- @field #CyclicRecoveryConfig cyclic Cyclic recovery operations configuration. --- Parse an RecoveryOps config Object. -- @param #JsonObject config Config object to parse -- @param #string parser_name Parser name ("AIRBASE", "AIRBOSS", etc...). -- @return #RecoveryopsConfig recoveryOpsConfigJson Parsed RecoveryopsConfig object function ParseRecoveryOpsConfigJson(config, parser_name) local recoveryOpsConfigJson = {} local default_mode = 'ondemand' local default_ondemand = { recovery_duration_minutes = 45, } local default_alpha_duration = 60 local default_cyclic = { event_duration_minutes = 60, event_ia_reserved_minutes = 15, } -- ************************************************************************** -- mode -- ************************************************************************** if type(config.mode) == "string" then recoveryOpsConfigJson.mode = config.mode else Jtff_log.error( string.format( "RecoveryOps mode is not a string, defaulting to %s mode", default_mode ), parser_name ) recoveryOpsConfigJson.mode = default_mode end if recoveryOpsConfigJson.mode == 'ondemand' then -- ********************************************************************* -- ondemand -- ********************************************************************* if type(config.ondemand) == "table" then if type(config.ondemand.recovery_duration_minutes) == "number" then recoveryOpsConfigJson.ondemand = { recovery_duration_minutes = config.ondemand.recovery_duration_minutes, } else Jtff_log.error( string.format( "RecoveryOps ondemand recovery_duration_minutes is not a number, defaulting to %d minutes long events", default_ondemand.recovery_duration_minutes ), parser_name ) recoveryOpsConfigJson.ondemand = default_ondemand end else Jtff_log.error( string.format( "RecoveryOps ondemand is not a table, defaulting to %d minutes long events", default_ondemand.recovery_duration_minutes ), parser_name ) recoveryOpsConfigJson.ondemand = default_ondemand end elseif recoveryOpsConfigJson.mode == 'alpha' then -- ********************************************************************* -- alpha -- ********************************************************************* if type(config.alpha) == 'table' then if type(config.recoveryops.alpha.recoveries) ~= 'table' or next(config.recoveryops.alpha.recoveries) == nil then Jtff_log.error( string.format( "no recoveries defined in alpha mode, switching to ondemand mode", config.alias or "" ), parser_name ) recoveryOpsConfigJson.mode = 'ondemand' recoveryOpsConfigJson.ondemand = default_ondemand return recoveryOpsConfigJson end local alpha_recoveries = {} for alphaindex, alphaevent in ipairs(config.recoveryops.alpha.recoveries) do if type(alphaevent.recovery_start_minutes) ~= 'number' then Jtff_log.warn( string.format( "No start time for this alpha event, skipping event %i", alphaindex ), parser_name ) else table.insert( alpha_recoveries, { recovery_start_minutes = alphaevent.recovery_start_minutes, recovery_duration_minutes = alphaevent.recovery_duration_minutes or default_alpha_duration, } ) end end recoveryOpsConfigJson.alpha = { recoveries = alpha_recoveries, } else Jtff_log.error( string.format( "RecoveryOps alpha is not a table, ondemand config" ), parser_name ) recoveryOpsConfigJson.mode = 'ondemand' recoveryOpsConfigJson.ondemand = default_ondemand end elseif recoveryOpsConfigJson.mode == 'cyclic' then -- ************************************************************************** -- cyclic -- ************************************************************************** if type(config.cyclic) == "table" then if type(config.cyclic.event_duration_minutes) == "number" then recoveryOpsConfigJson.cyclic = { event_duration_minutes = config.cyclic.event_duration_minutes or default_cyclic.event_duration_minutes, } else Jtff_log.error( string.format( "RecoveryOps cyclic event_duration_minutes is not a number, defaulting to %d minutes long events", default_cyclic.event_duration_minutes ), parser_name ) recoveryOpsConfigJson.cyclic = default_cyclic end if type(config.cyclic.event_ia_reserved_minutes) == "number" then recoveryOpsConfigJson.cyclic.event_ia_reserved_minutes = config.cyclic.event_ia_reserved_minutes or default_cyclic.event_ia_reserved_minutes else Jtff_log.warn( string.format( "RecoveryOps cyclic event_ia_reserved_minutes is not a number, defaulting to %d minutes reserved for IA at the begining of each Event", default_cyclic.event_ia_reserved_minutes ), parser_name ) recoveryOpsConfigJson.cyclic.event_ia_reserved_minutes = default_cyclic.event_ia_reserved_minutes end else Jtff_log.warn( string.format( "RecoveryOps cyclic is not a table, defaulting to %d min events with %d min reserved for IA at the begining of each Event", default_cyclic.event_duration_minutes, default_cyclic.event_ia_reserved_minutes ), parser_name ) recoveryOpsConfigJson.cyclic = default_cyclic end else Jtff_log.error( string.format( "RecoveryOps mode is not a valid mode, defaulting to %s mode", default_mode ), parser_name ) recoveryOpsConfigJson.mode = default_mode recoveryOpsConfigJson.ondemand = default_ondemand end return recoveryOpsConfigJson end -- @type AirBossConfig -- @field #boolean enable Enable AirBoss creation. -- @field #string carriername Carrier name. -- @field #string alias AirBoss alias. -- @field #RecoveryopsConfig recoveryops Recovery operations configuration. -- @field #tacanConfig tacan TACAN configuration. -- @field #ICLSConfig icls ICLS configuration. -- @field #dl4Config dl4 Datalink 4 configuration. -- @field #boolean infinitepatrol Infinite patrol configuration. -- @field #boolean menumarkzones Menu mark zones configuration. -- @field #boolean menusmokezones Menu smoke zones configuration. -- @field #boolean niceguy Nice guy configuration. -- @field #boolean singlecarrier Single carrier configuration. -- @field #boolean wirecorrection Wire correction configuration. -- @field #boolean handleAI Handle AI aircrafts configuration. -- @field #boolean respawnAI Respawn AI aircrafts configuration. -- @field #number controlarea Control area radius in nautical miles. -- @field #number maxpatterns Maximum number of patterns. -- @field #number maxstacks Maximum number of stacks. -- @field #string difficulty Difficulty level. -- @field #string operationsstatspath Operations status path. -- @field #string operationstrapsheetpath Operationstrapsheet path. -- @field #table menurecovery Menu recovery configuration. -- @field #table srs SRS configuration. -- @field #table freq frequency configuration. -- @field #table releayunit reley unit configuration. -- @field #AirwingConfig airwing Airwing configuration. -- @field #table integratedpatrols Integrated patrols configuration. -- TODO: specify types and Parsefunction for : -- menurecovery -- srs -- freq -- releayunit --- Parse AirBoss config Object. -- @param #JsonObject config Config object to parse -- @return #AirBossConfig airbossConfigJson Parsed AirBossConfig object function ParseAirbossConfigJson(config) local json = require('Scripts/json') local parser_name = "AIRBOSS" -- ************************************************************************ -- Default Values -- ************************************************************************ local default_enable = false local default_radio_freqs = { base = 127.300, marshall = 127.500, lso = 127.400 } local default_infinite_patrol = true local default_cca_radius_nm = 60 local default_max_patterns = 5 local default_max_stacks = 10 local default_difficulty = AIRBOSS.Difficulty.NORMAL local default_handleAI = true local default_respawnAI = false local default_menumarkzones = false local default_menusmokezones = false local default_menurecovery = { enable = true, duration = 45, windondeck = 30, offset = 0, uturn = true } local default_srs = { useSRS = false, voices = { marshall = MSRS.Voices.Microsoft.Zira, lso = MSRS.Voices.Microsoft.David, airboss = MSRS.Voices.Microsoft.Hazel, }, voiceovers = { marshall = "Gabriella", lso = "FF" } } local default_niceguy = false local default_wirecorrection = true local default_operationsstatspath = "C:/airboss-stats" local default_operationstrapsheetpath = "C:/airboss-trapsheets" local default_singlecarrier = false local default_integratedpatrols = { tanker = { enable = false, }, awacs = { enable = false, }, pedro = { enable = false, } } local airbossConfigJson = { enable = default_enable, } -- ************************************************************************ -- enable -- ************************************************************************ if type(config.enable) ~= 'boolean' then Jtff_log.warn(string.format("no proper switch enable config defined, skipping config for %s Airboss Object",tostring(default_enable),config.alias or ""),parser_name) airbossConfigJson.enable = false else airbossConfigJson.enable = config.enable end -- ************************************************************************ -- carriername -- ************************************************************************ if type(config.carriername ) == 'nil' then Jtff_log.error( string.format( "no carriername field in config Object, skipping %s Airboss Object", config.alias or "" ), parser_name ) config.enable = false return config end if type(UNIT:FindByName(config.carriername)) == 'nil' then Jtff_log.error(string.format("Unit %s not found, skipping %s Airboss Object",config.carriername, config.alias or ""),parser_name) config.enable = false return config end airbossConfigJson.carriername = config.carriername -- ************************************************************************ -- Alias -- ************************************************************************ airbossConfigJson.alias = config.alias or config.carriername -- ************************************************************************ -- RecoveryOps -- ************************************************************************ if type(config.recoveryops) == "table" then airbossConfigJson.recoveryops = ParseRecoveryOpsConfigJson(config.recoveryops, parser_name) if next(airbossConfigJson.recoveryops) == nil then Jtff_log.error("AirBoss RecoveryOps is not a valid RecoveryopsConfig object, skipping AirBoss configuration", parser_name) config.enable = false return config end else Jtff_log.error("AirBoss RecoveryOps config is not a table, skipping AirBoss configuration", parser_name) config.enable = false return config end -- ************************************************************************ -- Tacan -- ************************************************************************ if type(config.tacan) == "table" then airbossConfigJson.tacan = ParseTacanConfigJson(config.tacan, parser_name) if next(airbossConfigJson.tacan) == nil then Jtff_log.error("AirBoss TACAN is not a valid TACAN object, skipping AirBoss configuration", parser_name) config.enable = false return config end else Jtff_log.error("AirBoss TACAN config is not a table, skipping AirBoss configuration", parser_name) config.enable = false return config end -- ************************************************************************ -- ICLS -- ************************************************************************ if type(config.icls) == "table" then airbossConfigJson.icls = ParseICLSConfigJson(config.icls, parser_name) if next(airbossConfigJson.icls) == nil then Jtff_log.error("AirBoss ICLS is not a valid ICLS object, skipping AirBoss configuration", parser_name) config.enable = false return config end else Jtff_log.error("AirBoss ICLS config is not a table, skipping AirBoss configuration", parser_name) config.enable = false return config end -- ************************************************************************ -- Datalink 4 -- ************************************************************************ if type(config.dl4) == "table" then airbossConfigJson.dl4 = ParseDL4ConfigJson(config.dl4, parser_name) if next(airbossConfigJson.dl4) == nil then Jtff_log.error("AirBoss DL4 is not a valid DL4Config object, skipping AirBoss configuration", parser_name) config.enable = false return config end else Jtff_log.error("AirBoss DL4 config is not a table, skipping AirBoss configuration", parser_name) config.enable = false return config end -- ************************************************************************ -- InfinitePatrol -- ************************************************************************ if type(config.infinitepatrol) ~= 'boolean' then Jtff_log.warn(string.format("no proper switch infinitepatrol config defined, defaulting to %s for %s Airboss Object",tostring(default_infinite_patrol),config.alias or ""),parser_name) airbossConfigJson.infinitepatrol = default_infinite_patrol else airbossConfigJson.infinitepatrol = config.infinitepatrol or default_infinite_patrol end -- ************************************************************************ -- MenuMarkZones -- ************************************************************************ if type(config.enable_menumarkzones) ~= 'boolean' then Jtff_log.warn(string.format("no proper switch enable_menumarkzones config defined, defaulting to %s for %s Airboss Object",tostring(default_menumarkzones),config.alias or ""),parser_name) airbossConfigJson.enable_menumarkzones = default_menumarkzones else airbossConfigJson.enable_menumarkzones = config.enable_menumarkzones or default_menumarkzones end -- ************************************************************************ -- MenuSmokeZones -- ************************************************************************ if type(config.enable_menusmokezones) ~= 'boolean' then Jtff_log.warn(string.format("no proper switch enable_menusmokezones config defined, defaulting to %s for %s Airboss Object",tostring(default_menusmokezones),config.alias or ""),parser_name) airbossConfigJson.enable_menusmokezones = default_menusmokezones else airbossConfigJson.enable_menusmokezones = config.enable_menusmokezones or default_menusmokezones end -- ************************************************************************ -- Airboss Nice Guy -- ************************************************************************ if type(config.enable_niceguy) ~= 'boolean' then Jtff_log.warn(string.format("no proper switch enable_niceguy config defined, defaulting to %s for %s Airboss Object",tostring(default_niceguy),config.alias or ""),parser_name) airbossConfigJson.enable_niceguy = default_niceguy else airbossConfigJson.enable_niceguy = config.enable_niceguy or default_niceguy end -- ************************************************************************ -- Single Carrier -- ************************************************************************ if type(config.singlecarrier) ~= 'boolean' then Jtff_log.warn(string.format("no proper switch singlecarrier config defined, defaulting to %s for %s Airboss Object",tostring(default_singlecarrier),config.alias or ""),parser_name) airbossConfigJson.singlecarrier = default_singlecarrier else airbossConfigJson.singlecarrier = config.singlecarrier or default_singlecarrier end -- ************************************************************************ -- Multi Player Wire Correction -- ************************************************************************ if type(config.wirecorrection) ~= 'boolean' then Jtff_log.warn(string.format("no proper switch wirecorrection config defined, defaulting to %s for %s Airboss Object",tostring(default_wirecorrection),config.alias or ""),parser_name) airbossConfigJson.wirecorrection = default_wirecorrection else airbossConfigJson.wirecorrection = config.wirecorrection or default_wirecorrection end -- ************************************************************************ -- Airboss Handle AI aircrafts -- ************************************************************************ if type(config.handleAI) ~= 'boolean' then Jtff_log.warn(string.format("no proper switch handleAI config defined, defaulting to %s for %s Airboss Object",tostring(default_handleAI),config.alias or ""),parser_name) airbossConfigJson.handleAI = default_handleAI else airbossConfigJson.handleAI = config.handleAI or default_handleAI end -- ************************************************************************ -- Airboss Respawn AI aircrafts -- ************************************************************************ if type(config.respawnAI) ~= 'boolean' then Jtff_log.warn(string.format("no proper switch respawnAI config defined, defaulting to %s for %s Airboss Object",tostring(default_respawnAI),config.alias or ""),parser_name) airbossConfigJson.respawnAI = default_respawnAI else airbossConfigJson.respawnAI = config.respawnAI or default_respawnAI end -- ************************************************************************ -- Airboss CCA -- ************************************************************************ if type(config.controlarea) ~= 'number' then Jtff_log.warn(string.format("no proper controlarea defined, defaulting to %d for %s Airboss Object",default_cca_radius_nm,config.alias or ""),parser_name) airbossConfigJson.controlarea = default_cca_radius_nm else airbossConfigJson.controlarea = config.controlarea or default_cca_radius_nm end -- ************************************************************************ -- Airboss Max Patterns -- ************************************************************************ if type(config.maxpatterns) ~= 'number' then Jtff_log.warn(string.format("no proper maxpatterns defined, defaulting to %d for %s Airboss Object",default_max_patterns,config.alias or ""),parser_name) airbossConfigJson.maxpatterns = default_max_patterns else airbossConfigJson.maxpatterns = config.maxpatterns or default_max_patterns end -- ************************************************************************ -- Airboss Max Stacks -- ************************************************************************ if type(config.maxstacks) ~= 'number' then Jtff_log.warn(string.format("no proper maxstacks defined, defaulting to %d for %s Airboss Object",default_max_stacks,config.alias or ""),parser_name) airbossConfigJson.maxstacks = default_max_stacks else airbossConfigJson.maxstacks = config.maxstacks or default_max_stacks end -- ************************************************************************ -- Airboss Difficulty -- ************************************************************************ if type(config.difficulty) ~= 'string' then Jtff_log.warn(string.format("no proper difficulty defined, defaulting to %s for %s Airboss Object",default_difficulty,config.alias or ""),parser_name) airbossConfigJson.difficulty = default_difficulty else if ((config.difficulty ~= AIRBOSS.Difficulty.EASY) and (config.difficulty ~= AIRBOSS.Difficulty.NORMAL) and (config.difficulty ~= AIRBOSS.Difficulty.HARD)) then Jtff_log.warn(string.format("invalid difficulty string defined, defaulting to %s for %s Airboss Object",default_difficulty,config.alias or ""),parser_name) airbossConfigJson.difficulty = default_difficulty else airbossConfigJson.difficulty = config.difficulty or default_difficulty end end -- ************************************************************************ -- Airboss Operations Status Path -- ************************************************************************ if type(config.operationsstatspath) ~= 'string' then Jtff_log.warn(string.format("no proper operationsstatspath defined, defaulting to %s for %s Airboss Object",default_operationsstatspath,config.alias or ""),parser_name) airbossConfigJson.operationsstatspath = default_operationsstatspath else airbossConfigJson.operationsstatspath = config.operationsstatspath or default_operationsstatspath end -- ************************************************************************ -- Airboss Operationstrapsheet Path -- ************************************************************************ if type(config.operationstrapsheetpath) ~= 'string' then Jtff_log.warn(string.format("no proper operationstrapsheetpath defined, defaulting to %s for %s Airboss Object",default_operationstrapsheetpath,config.alias or ""),parser_name) airbossConfigJson.operationstrapsheetpath = default_operationstrapsheetpath else airbossConfigJson.operationstrapsheetpath = config.operationstrapsheetpath or default_operationstrapsheetpath end -- ************************************************************************ -- Airboss Menu Recovery -- ************************************************************************ if type(config.menurecovery) ~= 'table' then Jtff_log.warn(string.format("no proper menurecovery defined, defaulting menurecovery config for %s Airboss Object",config.alias or ""),parser_name) airbossConfigJson.menurecovery = default_menurecovery else airbossConfigJson.menurecovery = { enable = config.menurecovery.enable or default_menurecovery.enable, duration = config.menurecovery.duration or default_menurecovery.duration, windondeck = config.menurecovery.windondeck or default_menurecovery.windondeck, offset = config.menurecovery.offset or default_menurecovery.offset, uturn = config.menurecovery.uturn or default_menurecovery.uturn, } end -- ************************************************************************ -- Airboss SRS -- ************************************************************************ if type(config.srs) ~= 'table' then Jtff_log.warn(string.format("no proper SRS config defined, defaulting SRS config for %s Airboss Object",config.alias or ""),parser_name) airbossConfigJson.srs = default_srs else airbossConfigJson.srs = { useSRS = config.srs.useSRS or default_srs.useSRS } if airbossConfigJson.srs.useSRS then if type(config.srs.voices) ~= 'table' then Jtff_log.warn(string.format("no proper voices defined, defaulting voices config for %s Airboss Object",config.alias or ""),parser_name) airbossConfigJson.srs.voices = default_srs.voices else airbossConfigJson.srs.voices = { marshall = config.srs.voices.marshall or default_srs.voices.marshall, lso = config.srs.voices.lso or default_srs.voices.lso, airboss = config.srs.voices.airboss or default_srs.voices.airboss, } end airbossConfigJson.srs.voiceovers = default_srs.voiceovers else if type(config.srs.voiceovers) ~= 'table' then Jtff_log.warn(string.format("no proper voiceovers defined, defaulting voiceovers config for %s Airboss Object",config.alias or ""),parser_name) airbossConfigJson.srs.voiceovers = default_srs.voiceovers else airbossConfigJson.srs.voiceovers = { marshall = config.srs.voiceovers.marshall or default_srs.voiceovers.marshall, lso = config.srs.voiceovers.lso or default_srs.voiceovers.lso, } end airbossConfigJson.srs.voices = default_srs.voices end end -- ************************************************************************ -- Airboss Radio Frequencies -- ************************************************************************ if type(config.freq) ~= 'table' then Jtff_log.warn(string.format("no proper radio freqs defined, defaulting config for %s Airboss Object",config.alias or ""),parser_name) airbossConfigJson.freq = default_radio_freqs else airbossConfigJson.freq = { base = config.freq.base or default_radio_freqs.base, marshall = config.freq.marshall or default_radio_freqs.marshall, lso = config.freq.lso or default_radio_freqs.lso, } end -- ************************************************************************ -- Airboss Radio Relay Units -- ************************************************************************ if type(config.releayunit) ~= 'table' then Jtff_log.warn(string.format("no proper radio releayunit defined, skipping freqs config for %s Airboss Object",config.alias or ""),parser_name) else local airboss_found = config.releayunit.airboss or nil local marshall_found = config.releayunit.marshall or nil local lso_found = config.releayunit.lso or nil if ((type(UNIT:FindByName(config.releayunit.airboss or '')) == 'nil') and (type(STATIC:FindByName(config.releayunit.airboss or '', false)) == 'nil')) then airboss_found = nil end if ((type(UNIT:FindByName(config.releayunit.marshall or '')) == 'nil') and (type(STATIC:FindByName(config.releayunit.marshall or '',false)) == 'nil')) then marshall_found = nil end if ((type(UNIT:FindByName(config.releayunit.lso or '')) == 'nil') and (type(STATIC:FindByName(config.releayunit.lso or '',false)) == 'nil')) then lso_found = nil end if not(airboss_found or marshall_found or lso_found) then Jtff_log.warn(string.format("relayunit config empty, skipping freqs config for %s Airboss Object",config.alias or ""),parser_name) else airbossConfigJson.releayunit = { airboss = airboss_found or nil, marshall = marshall_found or nil, lso = lso_found or nil, } end end -- ************************************************************************ -- Airboss AirWing -- ************************************************************************ if type(config.airwing) == "table" then airbossConfigJson.airwing = ParseAirwingConfigJson(config.airwing, parser_name) else Jtff_log.error("Airboss airwing is not a table, aborting Airboss configuration", parser_name) config.enable = false return config end -- ************************************************************************ -- Airboss Integrated Patrols -- ************************************************************************ if type(config.integratedpatrols) == "table" then airbossConfigJson.integratedpatrols = ParseIntegratedPatrolConfigJson(config.integratedpatrols, parser_name) else Jtff_log.warn(string.format("no proper integratedpatrols defined, defaulting integratedpatrols config for %s Airboss Object",config.alias or ""),parser_name) airbossConfigJson.integratedpatrols = default_integratedpatrols end if type(config.integratedpatrols) ~= 'table' then Jtff_log.warn(string.format("no proper integratedpatrols defined, defaulting integratedpatrols config for %s Airboss Object",config.alias or ""),parser_name) airbossConfigJson.integratedpatrols = default_integratedpatrols else airbossConfigJson.integratedpatrols = config.integratedpatrols or default_integratedpatrols -- ******************************************************************** -- Airboss Integrated Patrols - Tanker -- ******************************************************************** if type(config.integratedpatrols.tanker) ~= 'table' then Jtff_log.warn(string.format("no proper integratedpatrols.tanker defined, defaulting integratedpatrols.tanker config for %s Airboss Object",config.alias or ""),parser_name) airbossConfigJson.integratedpatrols.tanker = default_integratedpatrols.tanker else airbossConfigJson.integratedpatrols.tanker = config.integratedpatrols.tanker or default_integratedpatrols.tanker end -- ******************************************************************** -- Airboss Integrated Patrols - AWACS -- ******************************************************************** if type(config.integratedpatrols.awacs) ~= 'table' then Jtff_log.warn(string.format("no proper integratedpatrols.awacs defined, defaulting integratedpatrols.awacs config for %s Airboss Object",config.alias or ""),parser_name) airbossConfigJson.integratedpatrols.awacs = default_integratedpatrols.awacs else airbossConfigJson.integratedpatrols.awacs = config.integratedpatrols.awacs or default_integratedpatrols.awacs end -- ******************************************************************** -- Airboss Integrated Patrols - Pedro -- ******************************************************************** if type(config.integratedpatrols.pedro) ~= 'table' then Jtff_log.warn(string.format("no proper integratedpatrols.pedro defined, defaulting integratedpatrols.pedro config for %s Airboss Object",config.alias or ""),parser_name) airbossConfigJson.integratedpatrols.pedro = default_integratedpatrols.pedro else airbossConfigJson.integratedpatrols.pedro = config.integratedpatrols.pedro or default_integratedpatrols.pedro end end Jtff_log.info( string.format( "parsed Airboss config for %s Airboss, resulting config :\n%s", config.alias or "", json:encode( airbossConfigJson, { indent = true } ) ), parser_name ) return airbossConfigJson end -- endregion AirBossConfig AIRBOSSArray = {} local compteur = #AIRBOSSArray MenuCoalitionCSGCommands = {} for _k,_coalition in pairs(coalition.side) do MenuCoalitionCSGCommands[UTILS.GetCoalitionName(_coalition)] = MENU_COALITION:New( _coalition, "CSG Commands", MenuCoalition[UTILS.GetCoalitionName(_coalition)] ) end for index, currentAirbossConfigObject in ipairs(AirBossConfig) do local airbossconfig = ParseAirbossConfigJson(currentAirbossConfigObject) if airbossconfig.enable == true then compteur = compteur +1 -- region AirBossSetUp Jtff_log.info( string.format( 'configuration AirBoss %s: ', airbossconfig.alias ), "AIRBOSS" ) local menuCoalitionCSGCommands local objAirboss = AIRBOSS:New(airbossconfig.carriername, airbossconfig.alias) or {} objAirboss.autosave = false objAirboss.customconfig = airbossconfig objAirboss.menuObject, menuCoalitionCSGCommands, objAirboss.deckLayoutmenuObject = InitCSGAirbossMenus(objAirboss) --TACAN objAirboss:SetTACANoff() objAirboss:SetTACAN(airbossconfig.tacan.channel, airbossconfig.tacan.mode, airbossconfig.tacan.morse) --ICLS objAirboss:SetICLSoff() objAirboss:SetICLS(airbossconfig.icls.channel, airbossconfig.icls.morse) --ACLS ActivateDL4(airbossconfig) --Control parameters --CCA objAirboss:SetCarrierControlledArea(airbossconfig.controlarea) objAirboss:SetCarrierControlledZone(7) --Static weather objAirboss:SetStaticWeather(true) --Set Recovery case objAirboss:SetRecoveryCase(GetCaseTypeFromWeather( objAirboss:GetCoordinate(), timer.getAbsTime(), timer.getAbsTime() + airbossconfig.menurecovery.duration * 60 )) --Activate Emergency landings objAirboss:SetEmergencyLandings(true) --Max authorized patterns objAirboss:SetMaxLandingPattern(airbossconfig.maxpatterns) --Max section size objAirboss:SetMaxSectionSize(4) --Max stacks authorized objAirboss:SetMaxMarshalStacks(airbossconfig.maxstacks) --Default playerSkills objAirboss:SetDefaultPlayerSkill(airbossconfig.difficulty) --MultiPlayer Wire correction if airbossconfig.wirecorrection then objAirboss:SetMPWireCorrection(12) end --Funkman --objAirboss:SetFunkManOn(FunkmanConfig.port, FunkmanConfig.ip) objAirboss.funkmanSocket = nil if airbossconfig.operationsstatspath then objAirboss:Load(airbossconfig.operationsstatspath) if airbossconfig.operationsstatspath then objAirboss:SetTrapSheet(airbossconfig.operationstrapsheetpath) end --objAirboss:SetAutoSave(airbossconfig.operationsstatspath) end --Handle AI aircrafts objAirboss:SetRespawnAI(airbossconfig.respawnAI) if airbossconfig.handleAI == true then objAirboss:SetHandleAION() objAirboss:SetDespawnOnEngineShutdown(true) else objAirboss:SetHandleAIOFF() objAirboss:SetRespawnAI(false) end --Patrol control utility objAirboss:SetPatrolAdInfinitum(airbossconfig.infinitepatrol) --Menus Management if airbossconfig.menurecovery.enable == true then objAirboss:SetMenuRecovery(airbossconfig.menurecovery.duration, airbossconfig.menurecovery.windondeck, airbossconfig.menurecovery.uturn, airbossconfig.menurecovery.offset) end objAirboss:SetMenuMarkZones(airbossconfig.enable_menumarkzones) objAirboss:SetMenuSmokeZones(airbossconfig.enable_menusmokezones) objAirboss:SetAirbossNiceGuy(airbossconfig.enable_niceguy) objAirboss:SetMarshalRadius() objAirboss:SetRecoveryTurnTime(60) objAirboss:SetRefuelAI(20) objAirboss:SetWelcomePlayers(false) if airbossconfig.singlecarrier == true then objAirboss:SetMenuSingleCarrier() end --Communications Management if ( type(airbossconfig.srs) ~= 'nil' ) then if ( airbossconfig.srs.useSRS == true) then --we use SRS as communication method objAirboss:EnableSRS(SRSConfig.path, SRSConfig.port, 'en-US') objAirboss:SetLSORadio(airbossconfig.freq.lso, radio.modulation.AM, airbossconfig.srs.voices.lso) objAirboss:SetMarshalRadio(airbossconfig.freq.marshall, radio.modulation.AM, airbossconfig.srs.voices.marshall) objAirboss:SetAirbossRadio(airbossconfig.freq.base, radio.modulation.AM, airbossconfig.srs.voices.airboss) else --we use voice overs from airboss : need to include soundfiles in the mission objAirboss:SetSoundfilesFolder(SoundFilesPrefix .. "AIRBOSS/Airboss Soundfiles/") if airbossconfig.srs.voices.marshall then if airbossconfig.srs.voices.marshall == "Raynor" then objAirboss:SetVoiceOversMarshalByRaynor(SoundFilesPrefix .. 'AIRBOSS/Airboss Soundpack Marshal Raynor/') elseif airbossconfig.srs.voices.marshall == "Gabriella" then objAirboss:SetVoiceOversMarshalByGabriella(SoundFilesPrefix .. 'AIRBOSS/Airboss Soundpack Marshal Gabriella/') elseif airbossconfig.srs.voices.marshall == "FF" then objAirboss:SetVoiceOversMarshalByFF(SoundFilesPrefix .. 'AIRBOSS/Airboss Soundpack Marshal FF/') end else objAirboss:SetVoiceOversMarshalByRaynor(SoundFilesPrefix .. 'AIRBOSS/Airboss Soundpack Marshal Raynor/') end if airbossconfig.srs.voices.lso then if airbossconfig.srs.voices.lso == "Raynor" then objAirboss:SetVoiceOversLSOByRaynor(SoundFilesPrefix .. 'AIRBOSS/Airboss Soundpack LSO Raynor/') elseif airbossconfig.srs.voices.lso == "FF" then objAirboss:SetVoiceOversLSOByFF(SoundFilesPrefix .. 'AIRBOSS/Airboss Soundpack LSO FF/') end else objAirboss:SetVoiceOversLSOByRaynor(SoundFilesPrefix .. 'AIRBOSS/Airboss Soundpack LSO Raynor/') end objAirboss:SetLSORadio(airbossconfig.freq.lso, radio.modulation.AM) objAirboss:SetMarshalRadio(airbossconfig.freq.marshall, radio.modulation.AM) objAirboss:SetAirbossRadio(airbossconfig.freq.base, radio.modulation.AM) end else --airbossconfig.srs does not exist --Should never happen as we parse the config objAirboss:SetSoundfilesFolder(SoundFilesPrefix .. "AIRBOSS/Airboss Soundfiles/") objAirboss:SetVoiceOversMarshalByRaynor(SoundFilesPrefix .. 'AIRBOSS/Airboss Soundpack Marshal Raynor/') objAirboss:SetVoiceOversLSOByRaynor(SoundFilesPrefix .. 'AIRBOSS/Airboss Soundpack LSO Raynor/') objAirboss:SetLSORadio(airbossconfig.freq.lso, radio.modulation.AM) objAirboss:SetMarshalRadio(airbossconfig.freq.marshall, radio.modulation.AM) objAirboss:SetAirbossRadio(airbossconfig.freq.base, radio.modulation.AM) end objAirboss:SetRadioRelayMarshal(airbossconfig.releayunit.marshall) objAirboss:SetRadioRelayLSO(airbossconfig.releayunit.lso) objAirboss:SetDebugModeOFF() --objAirboss.trapsheet = false --Recovery operation management if (airbossconfig.recoveryops.mode == 'cyclic') then if not(airbossconfig.recoveryops.cyclic.event_duration_minutes) then airbossconfig.recoveryops.cyclic.event_duration_minutes = 60 end end -- endregion AirBossSetUp -- region AirBossSetUpAirwing --Airwing generation objAirboss.airwing = AIRWING:New( objAirboss.carrier:GetName(), airbossconfig.airwing.name ) objAirboss.airwing:SetAirboss(objAirboss) Commander[objAirboss.airwing:GetCoalitionName()]:AddAirwing( objAirboss.airwing ) for _, _squadronconfig in ipairs(airbossconfig.airwing.squadrons) do local squadronToCreate = SQUADRON:New( _squadronconfig.template, _squadronconfig.nb_aircrafts, _squadronconfig.name ) squadronToCreate:SetFuelLowThreshold(_squadronconfig.fuellowthreshold) squadronToCreate:SetFuelLowRefuel(_squadronconfig.lowfuelrtb) squadronToCreate:SetGrouping(_squadronconfig.grouping) squadronToCreate:SetModex(_squadronconfig.modex) squadronToCreate:SetRadio(_squadronconfig.radio.freq, _squadronconfig.radio.modulation) squadronToCreate:SetSkill(_squadronconfig.skill) squadronToCreate:SetVerbosity( JTFF_verbosity_levels[JTFF_LOGLEVEL] ) squadronToCreate:SetTurnoverTime( _squadronconfig.turnovertime, _squadronconfig.repairtime ) -- TODO: improve this if type(_squadronconfig.livery) == 'string' then squadronToCreate:SetLivery(_squadronconfig.livery) end for _, _payload in ipairs(_squadronconfig.payloads) do local payloadTemplateGroup = GROUP:FindByName(_payload.name) if #(_payload.payloadconfigs) > 0 then for _, _payloadconfig in ipairs(_payload.payloadconfigs) do squadronToCreate:AddMissionCapability(_payloadconfig.roles) Jtff_log.debug( string.format( "Added mission capability %s perf=%d to squadron %s", _payload.name, _payloadconfig.perf, squadronToCreate:GetName() ), "AIRBOSS" ) objAirboss.airwing:NewPayload( payloadTemplateGroup:GetUnit(1), _payloadconfig.qty, _payloadconfig.roles, _payloadconfig.perf ) Jtff_log.debug( string.format( "Payload added qty=%d perf=%d template=%s to squadron %s", _payloadconfig.qty, _payloadconfig.perf, _payload.name, squadronToCreate:GetName() ), "AIRBOSS" ) end end end objAirboss.airwing:AddSquadron( squadronToCreate ) end -- endregion AirBossSetUpAirwing --registering customconfig objAirboss.customconfig = airbossconfig --Apply DeckLayout if ( (objAirboss.carriertype == AIRBOSS.CarrierType.LINCOLN) or (objAirboss.carriertype == AIRBOSS.CarrierType.ROOSEVELT) or (objAirboss.carriertype == AIRBOSS.CarrierType.TRUMAN) or (objAirboss.carriertype == AIRBOSS.CarrierType.WASHINGTON) ) then Jtff_log.info( string.format( "CSG : %s is Supercarrier apply Deck Layout...", objAirboss.customconfig.alias ), "AIRBOSS" ) FlexDeck7SpawnLayout(objAirboss) else Jtff_log.info( string.format( "CSG : %s is not Supercarrier (%s) skipping Deck Layout...", objAirboss.customconfig.alias, objAirboss.carriertype ), "AIRBOSS" ) end -- region AirBossCustomFunctions function objAirboss:detectShitHotBreak() local clientData={} local player_name="" local clientsCount = objAirboss.CVNClients:Count() if clientsCount > 0 then Jtff_log.debug( string.format( "detectShitHotBreak : %s has %d clients in Set ...", objAirboss.customconfig.alias, clientsCount ), "AIRBOSS" ) end objAirboss.CVNClients:ForEachClientInZone( objAirboss.CVN_UNITZone, function( MooseClient ) local function resetFlag(client) --trigger.action.outText('RESET SH Pass FLAG)', 5 ) USERFLAG:New(client:GetUCID()):Set(0) end local player_velocity = MooseClient:GetVelocityKNOTS() local player_name = MooseClient:GetPlayerName() local player_type = MooseClient:GetTypeName() local player_fuel = MooseClient:GetFuel() local player_alt_feet = UTILS.MetersToFeet(MooseClient:GetAltitude()) --trigger.action.outText('ForEachClientInZone: MooseClient name is '..player_name , 5) local Play_SH_Sound = USERSOUND:New( SoundFilesPrefix .. "AIRBOSS/Airboss Soundfiles/GreatBallsOfFire.ogg" ) --trigger.action.outText(player_name..' altitude is '..player_alt_feet..' feet', 5) --trigger.action.outText(player_name..' speed is '..player_velocity, 5) local client_in_zone_flag = USERFLAG:New(MooseClient:GetUCID()) local client_performing_sh = USERFLAG:New(MooseClient:GetUCID()..'_sh') if client_in_zone_flag:Get() == 0 and player_velocity > 475 and player_alt_feet < 700 then -- Requirements for Shit Hot break are velocity >475 knots and less than 700 feet trigger.action.outText(player_name..' performing a Sierra Hotel Break around ' .. objAirboss.customconfig.alias .. ' !', 10) if (type(Jtff_dcsbot) ~= 'nil') then local title = string.format('Shit Hot Break !!!') local description = string.format("[%s] is performing a Sierra Hotel Break at %.0f knots and %.0f feet over %s.",player_name , UTILS.Round(player_velocity, 0), player_alt_feet, objAirboss.customconfig.alias) local img = 'https://media1.tenor.com/m/s5P2w4A4H2MAAAAd/003.gif' local fields = { ['Pilot'] = player_name, ['CVN'] = objAirboss.customconfig.alias } local footer = 'Great balls of fire...' Jtff_dcsbot.sendEmbed(title, description, img, fields, footer) end Play_SH_Sound:ToClient(MooseClient) client_performing_sh:Set(1) client_in_zone_flag:Set(1) timer.scheduleFunction(resetFlag, MooseClient, timer.getTime() + 5 * 60) Jtff_log.info('Sierra Hotel Bravo detected : '.. player_name .. ' is breaking over ' .. objAirboss.customconfig.alias ..'...',"AIRBOSS") end end ) end function objAirboss:OnAfterLSOGrade(From, Event, To, playerData, myGrade) local string_grade = myGrade.grade local unit_name = playerData.unitname local player_name = playerData.name local player_wire = playerData.wire local isSH = false local bonusPoint = 0 local malusPoint = 0 local trapSheetBaseName = string.format("AIRBOSS-trapsheet-%s", player_name) local boolSendFunkman = true local PerfectSound = USERSOUND:New( SoundFilesPrefix .. "AIRBOSS/Airboss Soundfiles/ffyrtp.ogg" ) local FirstWireSound = USERSOUND:New( SoundFilesPrefix .. "AIRBOSS/Airboss Soundfiles/GetYourButtsUptoVipersOffice.ogg" ) player_name = player_name:gsub('[%p]', '') local client_performing_sh = USERFLAG:New(UNIT:FindByName(unit_name):GetClient():GetUCID()..'_sh') if client_performing_sh:Get() == 1 then myGrade.grade = string.format("%s",myGrade.grade) client_performing_sh:Set(0) isSH = true bonusPoint = 1 Jtff_log.debug('Adding bonus point to '.. player_name .. ' for his shit hot break over ' .. objAirboss.customconfig.alias ..'...',"AIRBOSS") end if player_wire == 1 then malusPoint = 1 Jtff_log.debug('Substracting malus point to '.. player_name .. ' for his 1-wire trap on ' .. objAirboss.customconfig.alias ..'...',"AIRBOSS") timer.scheduleFunction( function() FirstWireSound:ToClient(UNIT:FindByName(unit_name):GetClient()) end, {}, timer.getTime() + 5 ) if (type(Jtff_dcsbot) ~= 'nil') then local title = string.format('Poor recovery !!!') local description = string.format("%s almost had a rampstrike with that 1-wire !",player_name) local img = 'https://theaviationgeekclub.com/wp-content/uploads/2019/05/VF-51-F-14A.jpg' local fields = { ['Pilot'] = player_name, ['CVN'] = objAirboss.customconfig.alias } local footer = 'Almost went direct to the parking...' Jtff_dcsbot.sendEmbed(title, description, img, fields, footer) end end if string_grade == "_OK_" then --if string_grade == "_OK_" and player_wire == "3" and player_Tgroove >=15 and player_Tgroove <19 then trapSheetBaseName = string.format("unicorn_%s",trapSheetBaseName) timer.scheduleFunction( function() PerfectSound:ToClient(UNIT:FindByName(unit_name):GetClient()) end, {}, timer.getTime() + 5 ) elseif string_grade == "WO" then boolSendFunkman = false elseif string_grade == "NC" then boolSendFunkman = false end myGrade.messageType = 2 myGrade.callsign = playerData.callsign myGrade.name = playerData.name myGrade.airbossconfig = objAirboss.customconfig myGrade.points = math.min(math.max(myGrade.points - malusPoint, 0.50) + bonusPoint, 5) if isSH then trapSheetBaseName = string.format("SH_%s",trapSheetBaseName) end objAirboss:SetTrapSheet(objAirboss.trappath, trapSheetBaseName) if boolSendFunkman then --objAirboss:_SaveTrapSheet(playerData, myGrade) objAirboss:SetFunkManOn(FunkmanConfig.port, FunkmanConfig.ip) local trapsheet={} ; trapsheet.X={} ; trapsheet.Z={} ; trapsheet.AoA={} ; trapsheet.Alt={} -- Loop over trapsheet and extract used values. for i = 1, #playerData.trapsheet do local ts=playerData.trapsheet[i] --#AIRBOSS.GrooveData table.insert(trapsheet.X, UTILS.Round(ts.X, 1)) table.insert(trapsheet.Z, UTILS.Round(ts.Z, 1)) table.insert(trapsheet.AoA, UTILS.Round(ts.AoA, 2)) table.insert(trapsheet.Alt, UTILS.Round(ts.Alt, 1)) end local result={} result.command=SOCKET.DataType.LSOGRADE result.name=playerData.name result.trapsheet=trapsheet result.airframe=myGrade.airframe result.mitime=myGrade.mitime result.midate=myGrade.midate result.wind=myGrade.wind result.carriertype=myGrade.carriertype result.carriername=myGrade.carriername result.carrierrwy=myGrade.carrierrwy result.landingdist=objAirboss.carrierparam.landingdist result.theatre=myGrade.theatre result.case=playerData.case result.Tgroove=myGrade.Tgroove result.wire=player_wire result.grade=myGrade.grade result.points=myGrade.points result.details=myGrade.details -- Send result. objAirboss.funkmanSocket:SendTable(result) objAirboss.funkmanSocket = nil end Jtff_log.info(string.format("%s trapped on %s in case %.0f with Grade %s and %.1f points.",player_name,objAirboss.customconfig.alias,playerData.case,myGrade.grade,myGrade.points),"AIRBOSS") end function objAirboss:OnAfterRecoveryStart(From, Event, To, Case, Offset) objAirboss:MessageToMarshal('Recovery started Case '..Case..' operations...', objAirboss.customconfig.alias, "", 30, false, 10) end function objAirboss:OnAfterRecoveryStop(From, Event, To) local nextRecoveryStart local nextRecoveryStop objAirboss:MessageToMarshal('Recovery operations closed.', objAirboss.customconfig.alias, "", 30, false, 10) if (airbossconfig.recoveryops.mode == 'cyclic') then nextRecoveryStart = timer.getAbsTime() + UTILS.Round(objAirboss.customconfig.recoveryops.cyclic.event_duration_minutes*60*1/3, 0) nextRecoveryStop = timer.getAbsTime() + UTILS.Round(objAirboss.customconfig.recoveryops.cyclic.event_duration_minutes*60*3/3, 0) objAirboss:SetRecoveryCase(GetCaseTypeFromWeather( objAirboss:GetCoordinate(), nextRecoveryStart, nextRecoveryStop )) if objAirboss.defaultcase == 1 then objAirboss:SetMaxSectionSize(4) elseif objAirboss.defaultcase == 2 then objAirboss:SetMaxSectionSize(2) elseif objAirboss.defaultcase == 3 then objAirboss:SetMaxSectionSize(1) else objAirboss:SetMaxSectionSize(1) end objAirboss:AddRecoveryWindow( UTILS.SecondsToClock(nextRecoveryStart,false), UTILS.SecondsToClock(nextRecoveryStop,false), objAirboss.defaultcase, objAirboss.customconfig.menurecovery.offset, true, objAirboss.customconfig.menurecovery.windondeck, objAirboss.customconfig.menurecovery.uturn ) objAirboss:MessageToMarshal('Next Recovery in : '..UTILS.Round(objAirboss.customconfig.recoveryops.cyclic.event_duration_minutes/3, 0)..' minutes', objAirboss.customconfig.alias, "", 30, false, 0) --LeaveRecovery(objAirboss) end end -- endregion AirBossCustomFunctions --registering airboss object to AIRBOSSArray and starting it AIRBOSSArray[compteur] = objAirboss AIRBOSSArray[compteur]:Start() -- region AirBossSetUpIntegratedPatrols -- tanker integrated patrols if airbossconfig.integratedpatrols.tanker.enable == true then local recoveryTankerMission, recoveryAirwing = GenerateRecoveryTankerMission(airbossconfig, true) if type(recoveryTankerMission) ~= 'nil' then AIRBOSSArray[compteur].airwing:AddMission(recoveryTankerMission) end end -- rescue helo integrated patrols if airbossconfig.integratedpatrols.rescuehelo.enable == true then local RescueHeloMission, recoveryAirwing = GenerateRescueHeloMission(airbossconfig, true) if type(RescueHeloMission) ~= 'nil' then AIRBOSSArray[compteur].airwing:AddMission(RescueHeloMission) end end -- AWACS integrated patrols if airbossconfig.integratedpatrols.awacs.enable == true then local navalAwacsMission, recoveryAirwing = GenerateNavalAwacsMission(airbossconfig, true) if type(navalAwacsMission) ~= 'nil' then AIRBOSSArray[compteur].airwing:AddMission(navalAwacsMission) end end -- endregion AirBossSetUpIntegratedPatrols -- region AirBossRecovery AIRBOSSArray[compteur].CVN_UNITZone = ZONE_UNIT:New( 'cvnUnitZone-'..AIRBOSSArray[compteur].customconfig.alias, AIRBOSSArray[compteur].carrier, 1111 ) AIRBOSSArray[compteur].CVNClients = SET_CLIENT:New() :FilterActive() :FilterCoalitions(string.lower(UTILS.GetCoalitionName(objAirboss:GetCoalition()))) :FilterStart() local myscheduler local myschedulerID myscheduler, myschedulerID = SCHEDULER:New( nil, AIRBOSSArray[compteur].detectShitHotBreak, nil, 2, 1 ) AIRBOSSArray[compteur].scheduler = myscheduler AIRBOSSArray[compteur].schedulerID = myschedulerID trigger.action.outText('INFO '..airbossconfig.alias..' : Naval sunset at '..UTILS.SecondsToClock((AIRBOSSArray[compteur]:GetCoordinate():GetSunset(true) - 30*60)), 75) local nextRecoveryStart local nextRecoveryStop if (airbossconfig.recoveryops.mode == 'cyclic') then if airbossconfig.recoveryops.cyclic.event_ia_reserved_minutes then nextRecoveryStart = timer.getAbsTime() + (airbossconfig.recoveryops.cyclic.event_ia_reserved_minutes)*60+UTILS.Round(airbossconfig.recoveryops.cyclic.event_duration_minutes*60*0/3, 0) nextRecoveryStop = timer.getAbsTime() + (airbossconfig.recoveryops.cyclic.event_ia_reserved_minutes)*60+UTILS.Round(airbossconfig.recoveryops.cyclic.event_duration_minutes*60*2/3, 0) else nextRecoveryStart = timer.getAbsTime() + 15*60 + UTILS.Round(airbossconfig.recoveryops.cyclic.event_duration_minutes*60*0/3, 0) nextRecoveryStop = timer.getAbsTime() + 15*60 + UTILS.Round(airbossconfig.recoveryops.cyclic.event_duration_minutes*60*2/3, 0) end AIRBOSSArray[compteur]:SetRecoveryCase(GetCaseTypeFromWeather( AIRBOSSArray[compteur]:GetCoordinate(), nextRecoveryStart, nextRecoveryStop )) if AIRBOSSArray[compteur].defaultcase == 1 then AIRBOSSArray[compteur]:SetMaxSectionSize(4) elseif AIRBOSSArray[compteur].defaultcase == 2 then AIRBOSSArray[compteur]:SetMaxSectionSize(2) elseif AIRBOSSArray[compteur].defaultcase == 3 then AIRBOSSArray[compteur]:SetMaxSectionSize(1) else AIRBOSSArray[compteur]:SetMaxSectionSize(1) end AIRBOSSArray[compteur]:AddRecoveryWindow( UTILS.SecondsToClock(nextRecoveryStart,false), UTILS.SecondsToClock(nextRecoveryStop,false), AIRBOSSArray[compteur].defaultcase, airbossconfig.menurecovery.offset, true, airbossconfig.menurecovery.windondeck, airbossconfig.menurecovery.uturn ) elseif (airbossconfig.recoveryops.mode == 'alpha') then if (airbossconfig.recoveryops.alpha) then if (airbossconfig.recoveryops.alpha.recoveries) then for alphaindex, alphaevent in ipairs(airbossconfig.recoveryops.alpha.recoveries) do nextRecoveryStart = UTILS.Round(env.mission.start_time + (alphaevent.recovery_start_minutes * 60),0) nextRecoveryStop = UTILS.Round(env.mission.start_time + (( alphaevent.recovery_start_minutes + alphaevent.recovery_duration_minutes ) * 60),0) AIRBOSSArray[compteur]:SetRecoveryCase(GetCaseTypeFromWeather( AIRBOSSArray[compteur]:GetCoordinate(), nextRecoveryStart, nextRecoveryStop )) if AIRBOSSArray[compteur].defaultcase == 1 then AIRBOSSArray[compteur]:SetMaxSectionSize(4) elseif AIRBOSSArray[compteur].defaultcase == 2 then AIRBOSSArray[compteur]:SetMaxSectionSize(2) elseif AIRBOSSArray[compteur].defaultcase == 3 then AIRBOSSArray[compteur]:SetMaxSectionSize(1) else AIRBOSSArray[compteur]:SetMaxSectionSize(1) end AIRBOSSArray[compteur]:AddRecoveryWindow( UTILS.SecondsToClock(nextRecoveryStart), UTILS.SecondsToClock(nextRecoveryStop) ) end end end else AIRBOSSArray[compteur]:SetRecoveryCase(GetCaseTypeFromWeather( AIRBOSSArray[compteur]:GetCoordinate(), UTILS.Round(env.mission.start_time + (5 * 60),0), UTILS.Round(env.mission.start_time + (35 * 60),0) )) if AIRBOSSArray[compteur].defaultcase == 1 then AIRBOSSArray[compteur]:SetMaxSectionSize(4) elseif AIRBOSSArray[compteur].defaultcase == 2 then AIRBOSSArray[compteur]:SetMaxSectionSize(2) elseif AIRBOSSArray[compteur].defaultcase == 3 then AIRBOSSArray[compteur]:SetMaxSectionSize(1) else AIRBOSSArray[compteur]:SetMaxSectionSize(1) end end -- endregion AirBossRecovery AIRBOSSArray[compteur].airwing:Start() trigger.action.outText('AIRBOSS scripts Loaded for unit '..airbossconfig.carriername, 10) timer.scheduleFunction( function() trigger.action.outText( "<< If the AIRBOSS option does not appear in your F10 - Other Menu, try switching slots a few times and you will get the AIRBOSS message popups! Check the AIRBOSS documentation (link in briefing for more info) >>", 30) end, nil, timer.getTime() + 30 ) else timer.scheduleFunction( function() trigger.action.outText('AIRBOSS script disabled for unit '..airbossconfig.carriername, 10) end, nil, timer.getTime() + 8 ) end end