Merge branch 'FF/Ops' into FF/OpsDev

This commit is contained in:
Frank
2023-10-31 20:17:10 +01:00
147 changed files with 4277 additions and 2418 deletions

View File

@@ -1524,7 +1524,7 @@ end
-- @param #string GoogleKey Path to Google JSON-Key.
-- @return #ATIS self
function ATIS:SetSRS(PathToSRS, Gender, Culture, Voice, Port, GoogleKey)
if PathToSRS then
if PathToSRS or MSRS.path then
self.useSRS=true
self.msrs=MSRS:New(PathToSRS, self.frequency, self.modulation)
self.msrs:SetGender(Gender)
@@ -1555,7 +1555,7 @@ end
--- Get the coalition of the associated airbase.
-- @param #ATIS self
-- @return #number Coalition of the associcated airbase.
-- @return #number Coalition of the associated airbase.
function ATIS:GetCoalition()
local coal = self.airbase and self.airbase:GetCoalition() or nil
return coal

View File

@@ -54,7 +54,9 @@
-- @field #string takeoffType Take of type.
-- @field #boolean despawnAfterLanding Aircraft are despawned after landing.
-- @field #boolean despawnAfterHolding Aircraft are despawned after holding.
--
-- @field #boolean capOptionPatrolRaceTrack Use closer patrol race track or standard orbit auftrag.
-- @field #number capFormation If capOptionPatrolRaceTrack is true, set the formation, also.
--
-- @extends Ops.Legion#LEGION
--- *I fly because it releases my mind from the tyranny of petty things.* -- Antoine de Saint-Exupery
@@ -128,6 +130,8 @@ AIRWING = {
pointsAWACS = {},
pointsRecon = {},
markpoints = false,
capOptionPatrolRaceTrack = false,
capFormation = nil,
}
--- Payload data.
@@ -179,7 +183,7 @@ AIRWING = {
--- AIRWING class version.
-- @field #string version
AIRWING.version="0.9.3"
AIRWING.version="0.9.4"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
@@ -699,6 +703,24 @@ function AIRWING:SetNumberCAP(n)
return self
end
--- Set CAP flight formation.
-- @param #AIRWING self
-- @param #number Formation Formation to take, e.g. ENUMS.Formation.FixedWing.Trail.Close, also see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_option_formation).
-- @return #AIRWING self
function AIRWING:SetCAPFormation(Formation)
self.capFormation = Formation
return self
end
--- Set CAP close race track.We'll utilize the AUFTRAG PatrolRaceTrack instead of a standard race track orbit task.
-- @param #AIRWING self
-- @param #boolean OnOff If true, switch this on, else switch off. Off by default.
-- @return #AIRWING self
function AIRWING:SetCapCloseRaceTrack(OnOff)
self.capOptionPatrolRaceTrack = OnOff
return self
end
--- Set number of TANKER flights with Boom constantly in the air.
-- @param #AIRWING self
-- @param #number Nboom Number of flights. Default 1.
@@ -1112,13 +1134,14 @@ end
-- @return #AIRWING self
function AIRWING:CheckCAP()
local Ncap=0 --self:CountMissionsInQueue({AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT})
local Ncap=0
-- Count CAP missions.
for _,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
if mission:IsNotOver() and mission.type==AUFTRAG.Type.GCICAP and mission.patroldata then
if mission:IsNotOver() and (mission.type==AUFTRAG.Type.GCICAP or mission.type == AUFTRAG.Type.PATROLRACETRACK) and mission.patroldata then
Ncap=Ncap+1
end
@@ -1130,8 +1153,18 @@ function AIRWING:CheckCAP()
local altitude=patrol.altitude+1000*patrol.noccupied
local missionCAP=AUFTRAG:NewGCICAP(patrol.coord, altitude, patrol.speed, patrol.heading, patrol.leg)
local missionCAP = nil -- Ops.Auftrag#AUFTRAG
if self.capOptionPatrolRaceTrack then
missionCAP=AUFTRAG:NewPATROL_RACETRACK(patrol.coord,altitude,patrol.speed,patrol.heading,patrol.leg, self.capFormation)
else
missionCAP=AUFTRAG:NewGCICAP(patrol.coord, altitude, patrol.speed, patrol.heading, patrol.leg)
end
missionCAP.patroldata=patrol
patrol.noccupied=patrol.noccupied+1
@@ -1150,7 +1183,7 @@ end
-- @return #AIRWING self
function AIRWING:CheckRECON()
local Ncap=0 --self:CountMissionsInQueue({AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT})
local Ncap=0
-- Count CAP missions.
for _,_mission in pairs(self.missionqueue) do
@@ -1278,7 +1311,6 @@ function AIRWING:CheckAWACS()
end
for i=1,self.nflightsAWACS-N do
local patrol=self:_GetPatrolData(self.pointsAWACS)

View File

@@ -27,17 +27,17 @@
-- **Supported Carriers:**
--
-- * [USS John C. Stennis](https://en.wikipedia.org/wiki/USS_John_C._Stennis) (CVN-74)
-- * [USS Theodore Roosevelt](https://en.wikipedia.org/wiki/USS_Theodore_Roosevelt_(CVN-71\)) (CVN-71) [Super Carrier Module]
-- * [USS Abraham Lincoln](https://en.wikipedia.org/wiki/USS_Abraham_Lincoln_(CVN-72\)) (CVN-72) [Super Carrier Module]
-- * [USS George Washington](https://en.wikipedia.org/wiki/USS_George_Washington_(CVN-73\)) (CVN-73) [Super Carrier Module]
-- * [USS Theodore Roosevelt](https://en.wikipedia.org/wiki/USS_Theodore_Roosevelt_\(CVN-71\)) (CVN-71) [Super Carrier Module]
-- * [USS Abraham Lincoln](https://en.wikipedia.org/wiki/USS_Abraham_Lincoln_\(CVN-72\)) (CVN-72) [Super Carrier Module]
-- * [USS George Washington](https://en.wikipedia.org/wiki/USS_George_Washington_\(CVN-73\)) (CVN-73) [Super Carrier Module]
-- * [USS Harry S. Truman](https://en.wikipedia.org/wiki/USS_Harry_S._Truman) (CVN-75) [Super Carrier Module]
-- * [USS Forrestal](https://en.wikipedia.org/wiki/USS_Forrestal_(CV-59\)) (CV-59) [Heatblur Carrier Module]
-- * [HMS Hermes](https://en.wikipedia.org/wiki/HMS_Hermes_(R12\)) (R12)
-- * [HMS Invincible](https://en.wikipedia.org/wiki/HMS_Invincible_(R05\)) (R05)
-- * [USS Tarawa](https://en.wikipedia.org/wiki/USS_Tarawa_(LHA-1\)) (LHA-1)
-- * [USS America](https://en.wikipedia.org/wiki/USS_America_(LHA-6\)) (LHA-6)
-- * [USS Forrestal](https://en.wikipedia.org/wiki/USS_Forrestal_\(CV-59\)) (CV-59) [Heatblur Carrier Module]
-- * [HMS Hermes](https://en.wikipedia.org/wiki/HMS_Hermes_\(R12\)) (R12)
-- * [HMS Invincible](https://en.wikipedia.org/wiki/HMS_Invincible_\(R05\)) (R05)
-- * [USS Tarawa](https://en.wikipedia.org/wiki/USS_Tarawa_\(LHA-1\)) (LHA-1)
-- * [USS America](https://en.wikipedia.org/wiki/USS_America_\(LHA-6\)) (LHA-6)
-- * [Juan Carlos I](https://en.wikipedia.org/wiki/Spanish_amphibious_assault_ship_Juan_Carlos_I) (L61)
-- * [HMAS Canberra](https://en.wikipedia.org/wiki/HMAS_Canberra_(L02\)) (L02)
-- * [HMAS Canberra](https://en.wikipedia.org/wiki/HMAS_Canberra_\(L02\)) (L02)
--
-- **Supported Aircraft:**
--
@@ -45,7 +45,7 @@
-- * [F-14A/B Tomcat](https://forums.eagle.ru/forumdisplay.php?f=395) (Player & AI)
-- * [A-4E Skyhawk Community Mod](https://forums.eagle.ru/showthread.php?t=224989) (Player & AI)
-- * [AV-8B N/A Harrier](https://forums.eagle.ru/forumdisplay.php?f=555) (Player & AI)
-- * [T-45C Goshawk](https://www.vnao-cvw-7.com/t-45-goshawk) (VNAO mod) (Player & AI)
-- * [T-45C Goshawk](https://forum.dcs.world/topic/203816-vnao-t-45-goshawk/) (VNAO mod) (Player & AI)
-- * [FE/A-18E/F/G Superhornet](https://forum.dcs.world/topic/316971-cjs-super-hornet-community-mod-v20-official-thread/) (CJS mod) (Player & AI)
-- * F/A-18C Hornet (AI)
-- * F-14A Tomcat (AI)
@@ -61,7 +61,7 @@
--
-- Heatblur's mighty F-14B Tomcat has been added (March 13th 2019) as well. Same goes for the A version.
--
-- The [DCS Supercarriers](https://forums.eagle.ru/forum/151-dcs-supercarrier/) are also supported.
-- The [DCS Supercarriers](https://www.digitalcombatsimulator.com/de/shop/modules/supercarrier/) are also supported.
--
-- ## Discussion
--
@@ -95,11 +95,6 @@
-- * [[MOOSE] Airboss - CASE I, "Until We Go Down" featuring the F-14B by Pikes](https://www.youtube.com/watch?v=ojgHDSw3Doc)
-- * [[MOOSE] Airboss - Skipper Menu](https://youtu.be/awnecCxRoNQ)
--
-- ### Lex explaining Boat Ops:
--
-- * [( DCS HORNET ) Some boat ops basics VID 1](https://www.youtube.com/watch?v=LvGQS-3AzMc)
-- * [( DCS HORNET ) Some boat ops basics VID 2](https://www.youtube.com/watch?v=bN44wvtRsw0)
--
-- ### Jabbers Case I and III Recovery Tutorials:
--
-- * [DCS World - F/A-18 - Case I Carrier Recovery Tutorial](https://www.youtube.com/watch?v=lm-M3VUy-_I)

View File

@@ -2043,82 +2043,90 @@ end
-- @param #ARMYGROUP self
-- @param #table Template Template used to init the group. Default is `self.template`.
-- @return #ARMYGROUP self
function ARMYGROUP:_InitGroup(Template)
function ARMYGROUP:_InitGroup(Template, Delay)
-- First check if group was already initialized.
if self.groupinitialized then
self:T(self.lid.."WARNING: Group was already initialized! Will NOT do it again!")
return
end
-- Get template of group.
local template=Template or self:_GetTemplate()
-- Ground are always AI.
self.isAI=true
-- Is (template) group late activated.
self.isLateActivated=template.lateActivation
-- Ground groups cannot be uncontrolled.
self.isUncontrolled=false
-- Max speed in km/h.
self.speedMax=self.group:GetSpeedMax()
-- Is group mobile?
if self.speedMax>3.6 then
self.isMobile=true
if Delay and Delay>0 then
self:ScheduleOnce(Delay, ARMYGROUP._InitGroup, self, Template, 0)
else
self.isMobile=false
end
-- Cruise speed in km/h
self.speedCruise=self.speedMax*0.7
-- Group ammo.
self.ammo=self:GetAmmoTot()
-- Radio parameters from template.
self.radio.On=false -- Radio is always OFF for ground.
self.radio.Freq=133
self.radio.Modu=radio.modulation.AM
-- Set default radio.
self:SetDefaultRadio(self.radio.Freq, self.radio.Modu, self.radio.On)
-- Get current formation from first waypoint.
self.option.Formation=template.route.points[1].action
-- Set default formation to "on road".
self.optionDefault.Formation=ENUMS.Formation.Vehicle.OnRoad
-- Default TACAN off.
self:SetDefaultTACAN(nil, nil, nil, nil, true)
self.tacan=UTILS.DeepCopy(self.tacanDefault)
-- First check if group was already initialized.
if self.groupinitialized then
self:T(self.lid.."WARNING: Group was already initialized! Will NOT do it again!")
return
end
self:I(self.lid.."FF Initializing Group")
-- Units of the group.
local units=self.group:GetUnits()
-- Get template of group.
local template=Template or self:_GetTemplate()
-- Ground are always AI.
self.isAI=true
-- Is (template) group late activated.
self.isLateActivated=template.lateActivation
-- Ground groups cannot be uncontrolled.
self.isUncontrolled=false
-- Max speed in km/h.
self.speedMax=self.group:GetSpeedMax()
-- Is group mobile?
if self.speedMax>3.6 then
self.isMobile=true
else
self.isMobile=false
end
-- Cruise speed in km/h
self.speedCruise=self.speedMax*0.7
-- Group ammo.
self.ammo=self:GetAmmoTot()
-- Radio parameters from template.
self.radio.On=false -- Radio is always OFF for ground.
self.radio.Freq=133
self.radio.Modu=radio.modulation.AM
-- Set default radio.
self:SetDefaultRadio(self.radio.Freq, self.radio.Modu, self.radio.On)
-- Get current formation from first waypoint.
self.option.Formation=template.route.points[1].action
-- Set default formation to "on road".
self.optionDefault.Formation=ENUMS.Formation.Vehicle.OnRoad
-- DCS group.
local dcsgroup=Group.getByName(self.groupname)
local size0=dcsgroup:getInitialSize()
-- Default TACAN off.
self:SetDefaultTACAN(nil, nil, nil, nil, true)
self.tacan=UTILS.DeepCopy(self.tacanDefault)
-- Units of the group.
local units=self.group:GetUnits()
-- DCS group.
local dcsgroup=Group.getByName(self.groupname)
local size0=dcsgroup:getInitialSize()
local u=dcsgroup:getUnits()
-- Quick check.
if #units~=size0 then
self:T(self.lid..string.format("ERROR: Got #units=%d but group consists of %d units! u=%d", #units, size0, #u))
end
-- Add elemets.
for _,unit in pairs(units) do
local unitname=unit:GetName()
self:_AddElementByName(unitname)
end
-- Quick check.
if #units~=size0 then
self:T(self.lid..string.format("ERROR: Got #units=%d but group consists of %d units!", #units, size0))
-- Init done.
self.groupinitialized=true
end
-- Add elemets.
for _,unit in pairs(units) do
local unitname=unit:GetName()
self:_AddElementByName(unitname)
end
-- Init done.
self.groupinitialized=true
return self
end

View File

@@ -443,6 +443,7 @@ _AUFTRAGSNR=0
-- @field #string REARMING Rearming mission.
-- @field #string CAPTUREZONE Capture zone mission.
-- @field #string NOTHING Nothing.
-- @field #string PATROLRACETRACK Patrol Racetrack.
AUFTRAG.Type={
ANTISHIP="Anti Ship",
AWACS="AWACS",
@@ -484,10 +485,10 @@ AUFTRAG.Type={
RELOCATECOHORT="Relocate Cohort",
AIRDEFENSE="Air Defence",
EWR="Early Warning Radar",
--RECOVERYTANKER="Recovery Tanker",
REARMING="Rearming",
CAPTUREZONE="Capture Zone",
NOTHING="Nothing",
PATROLRACETRACK="Patrol Racetrack",
}
--- Special task description.
@@ -511,6 +512,7 @@ AUFTRAG.Type={
-- @field #string REARMING Rearming.
-- @field #string CAPTUREZONE Capture OPS zone.
-- @field #string NOTHING Nothing.
-- @field #string PATROLRACETRACK Patrol Racetrack.
AUFTRAG.SpecialTask={
FORMATION="Formation",
PATROLZONE="PatrolZone",
@@ -532,6 +534,7 @@ AUFTRAG.SpecialTask={
REARMING="Rearming",
CAPTUREZONE="Capture Zone",
NOTHING="Nothing",
PATROLRACETRACK="Patrol Racetrack",
}
--- Mission status.
@@ -652,7 +655,7 @@ AUFTRAG.Category={
--- AUFTRAG class version.
-- @field #string version
AUFTRAG.version="1.2.0"
AUFTRAG.version="1.2.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -1017,7 +1020,7 @@ end
-- @param #number Altitude Hover altitude in feet AGL. Default is 50 feet above ground.
-- @param #number Time Time in seconds to hold the hover. Default 300 seconds.
-- @param #number Speed Speed in knots to fly to the target coordinate. Default 150kn.
-- @param #number MissionAlt Altitide to fly towards the mission in feet AGL. Default 1000ft.
-- @param #number MissionAlt Altitude to fly towards the mission in feet AGL. Default 1000ft.
-- @return #AUFTRAG self
function AUFTRAG:NewHOVER(Coordinate, Altitude, Time, Speed, MissionAlt)
@@ -1049,6 +1052,58 @@ function AUFTRAG:NewHOVER(Coordinate, Altitude, Time, Speed, MissionAlt)
return mission
end
--- **[AIR]** Create an enhanced orbit race track mission. Planes will keep closer to the track.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Coordinate Where to start the race track.
-- @param #number Altitude (Optional) Altitude in feet. Defaults to 20,000ft ASL.
-- @param #number Speed (Optional) Speed in knots. Defaults to 300kn TAS.
-- @param #number Heading (Optional) Heading in degrees, 0 to 360. Defaults to 90 degree (East).
-- @param #number Leg (Optional) Leg of the race track in NM. Defaults to 10nm.
-- @param #number Formation (Optional) Formation to take, e.g. ENUMS.Formation.FixedWing.Trail.Close, also see [Hoggit Wiki](https://wiki.hoggitworld.com/view/DCS_option_formation).
-- @return #AUFTRAG self
function AUFTRAG:NewPATROL_RACETRACK(Coordinate,Altitude,Speed,Heading,Leg,Formation)
local mission = AUFTRAG:New(AUFTRAG.Type.PATROLRACETRACK)
-- Target.
mission:_TargetFromObject(Coordinate)
-- Set Altitude.
if Altitude then
mission.TrackAltitude=UTILS.FeetToMeters(Altitude)
else
mission.TrackAltitude=UTILS.FeetToMeters(20000)
end
-- Points
mission.TrackPoint1 = Coordinate
local leg = UTILS.NMToMeters(Leg) or UTILS.NMToMeters(10)
local heading = Heading or 90
if heading < 0 or heading > 360 then heading = 90 end
mission.TrackPoint2 = Coordinate:Translate(leg,heading,true)
-- Orbit speed in m/s TAS.
mission.TrackSpeed = UTILS.IasToTas(UTILS.KnotsToKmph(Speed or 300), mission.TrackAltitude)
-- Mission speed in km/h and altitude
mission.missionSpeed = UTILS.KnotsToKmph(Speed or 300)
mission.missionAltitude = mission.TrackAltitude * 0.9
mission.missionTask=ENUMS.MissionTask.CAP
mission.optionROE=ENUMS.ROE.ReturnFire
mission.optionROT=ENUMS.ROT.PassiveDefense
mission.categories={AUFTRAG.Category.AIRCRAFT}
mission.DCStask=mission:GetDCSMissionTask()
return mission
end
--- **[AIR]** Create an ORBIT mission, which can be either a circular orbit or a race-track pattern.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Coordinate Where to orbit.
@@ -1181,7 +1236,7 @@ function AUFTRAG:NewORBIT_GROUP(Group, Altitude, Speed, Leg, Heading, OffsetVec2
end
--- **[AIR]** Create a Ground Controlled CAP (GCICAP) mission. Flights with this task are considered for A2A INTERCEPT missions by the CHIEF class. They will perform a compat air patrol but not engage by
--- **[AIR]** Create a Ground Controlled CAP (GCICAP) mission. Flights with this task are considered for A2A INTERCEPT missions by the CHIEF class. They will perform a combat air patrol but not engage by
-- themselfs. They wait for the CHIEF to tell them whom to engage.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Coordinate Where to orbit.
@@ -2688,6 +2743,8 @@ function AUFTRAG:NewAUTO(EngageGroup)
mission=AUFTRAG:NewTANKER(Coordinate,Altitude,Speed,Heading,Leg,RefuelSystem)
elseif auftrag==AUFTRAG.Type.TROOPTRANSPORT then
mission=AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet,DropoffCoordinate,PickupCoordinate)
elseif auftrag==AUFTRAG.Type.PATROLRACETRACK then
mission=AUFTRAG:NewPATROL_RACETRACK(Coordinate,Altitude,Speed,Heading,Leg,Formation)
else
end
@@ -6343,8 +6400,32 @@ function AUFTRAG:GetDCSMissionTask()
DCStask.params=param
table.insert(DCStasks, DCStask)
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.PATROLRACETRACK then
---------------------
-- Enhanced Orbit Racetrack --
---------------------
local DCStask={}
DCStask.id=AUFTRAG.SpecialTask.PATROLRACETRACK
local param={}
-- ONTROLLABLE:PatrolRaceTrack(Point1, Point2, Altitude, Speed, Formation, Delay)
param.TrackAltitude = self.TrackAltitude
param.TrackSpeed = self.TrackSpeed
param.TrackPoint1 = self.TrackPoint1
param.TrackPoint2 = self.TrackPoint2
param.missionSpeed = self.missionSpeed
param.missionAltitude = self.missionAltitude
param.TrackFormation = self.TrackFormation
DCStask.params=param
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.HOVER then
---------------------
@@ -6619,6 +6700,8 @@ function AUFTRAG:GetMissionTaskforMissionType(MissionType)
mtask=ENUMS.MissionTask.NOTHING
elseif MissionType==AUFTRAG.Type.HOVER then
mtask=ENUMS.MissionTask.NOTHING
elseif MissionType==AUFTRAG.Type.PATROLRACETRACK then
mtask=ENUMS.MissionTask.CAP
end
return mtask

View File

@@ -46,8 +46,6 @@
--
-- ===
--
-- ![Banner Image](OPS_CSAR.jpg)
--
-- # CSAR Concept
--
-- * MOOSE-based Helicopter CSAR Operations for Players.

View File

@@ -19,10 +19,12 @@
-- ===
--
-- ### Author: **Applevangelist** (Moose Version), ***Ciribob*** (original), Thanks to: Shadowze, Cammel (testing), bbirchnz (additional code!!)
-- ### Repack addition for crates: **Raiden**
--
-- @module Ops.CTLD
-- @image OPS_CTLD.jpg
-- Last Update June 2023
-- Last Update October 2023
do
@@ -599,7 +601,7 @@ do
--
-- ===
--
-- ![Banner Image](OPS_CTLD.jpg)
-- ![Banner Image](../Images/OPS_CTLD.jpg)
--
-- # CTLD Concept
--
@@ -700,6 +702,7 @@ do
--
-- my_ctld.useprefix = true -- (DO NOT SWITCH THIS OFF UNLESS YOU KNOW WHAT YOU ARE DOING!) Adjust **before** starting CTLD. If set to false, *all* choppers of the coalition side will be enabled for CTLD.
-- my_ctld.CrateDistance = 35 -- List and Load crates in this radius only.
-- my_ctld.PackDistance = 35 -- Pack crates in this radius only
-- my_ctld.dropcratesanywhere = false -- Option to allow crates to be dropped anywhere.
-- my_ctld.dropAsCargoCrate = false -- Parachuted herc cargo is not unpacked automatically but placed as crate to be unpacked. Needs a cargo with the same name defined like the cargo that was dropped.
-- my_ctld.maximumHoverHeight = 15 -- Hover max this high to load.
@@ -1121,6 +1124,7 @@ CTLD = {
Spawned_Crates = {}, -- Holds objects for crates spawned generally
Spawned_Cargo = {}, -- Binds together spawned_crates and their CTLD_CARGO objects
CrateDistance = 35, -- list crates in this radius
PackDistance = 35, -- pack crates in this radius
debug = false,
wpZones = {},
dropOffZones = {},
@@ -1144,6 +1148,7 @@ CTLD = {
-- DONE: List cargo in stock
-- DONE: Limit of troops, crates buildable?
-- DONE: Allow saving of Troops & Vehicles
-- DONE: Adding re-packing dropped units
------------------------------
--- Radio Beacons
@@ -1223,7 +1228,7 @@ CTLD.UnitTypes = {
--- CTLD class version.
-- @field #string version
CTLD.version="1.0.40"
CTLD.version="1.0.41"
--- Instantiate a new CTLD.
-- @param #CTLD self
@@ -1341,6 +1346,7 @@ function CTLD:New(Coalition, Prefixes, Alias)
-- setup
self.CrateDistance = 35 -- list/load crates in this radius
self.PackDistance = 35 -- pack objects in this radius
self.ExtractFactor = 3.33 -- factor for troops extraction, i.e. CrateDistance * Extractfactor
self.prefixes = Prefixes or {"Cargoheli"}
self.useprefix = true
@@ -2260,9 +2266,10 @@ end
-- @param #CTLD_CARGO Cargo
-- @param #number number Number of crates to generate (for dropping)
-- @param #boolean drop If true we\'re dropping from heli rather than loading.
function CTLD:_GetCrates(Group, Unit, Cargo, number, drop)
-- @param #boolean pack If true we\'re packing crates from a template rather than loading or dropping
function CTLD:_GetCrates(Group, Unit, Cargo, number, drop, pack)
self:T(self.lid .. " _GetCrates")
if not drop then
if not drop and not pack then
local cgoname = Cargo:GetName()
-- check if we have stock
local instock = Cargo:GetStock()
@@ -2279,18 +2286,20 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop)
local width = 20
local distance = nil
local zone = nil
if not drop then
if not drop and not pack then
inzone = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD)
if not inzone then
---@diagnostic disable-next-line: cast-local-type
inzone, ship, zone, distance, width = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
end
else
elseif drop and not pack then
if self.dropcratesanywhere then -- #1570
inzone = true
else
inzone = self:IsUnitInZone(Unit,CTLD.CargoZoneType.DROP)
end
elseif pack and not drop then
inzone = true
end
if not inzone then
@@ -3229,6 +3238,42 @@ function CTLD:_BuildCrates(Group, Unit,Engineering)
return self
end
--- (Internal) Function to repair nearby vehicles / FOBs
-- @param #CTLD self
-- @param Wrapper.Group#GROUP Group
-- @param Wrapper.Unit#UNIT Unit
function CTLD:_PackCratesNearby(Group, Unit)
self:T(self.lid .. " _PackCratesNearby")
-----------------------------------------
-- search for nearest group to player
-- determine if group is packable
-- generate crates and destroy group
-----------------------------------------
-- get nearby vehicles
local location = Group:GetCoordinate() -- get coordinate of group using function
local nearestGroups = SET_GROUP:New():FilterCoalitions("blue"):FilterZones({ZONE_RADIUS:New("TempZone", location:GetVec2(), self.PackDistance, false)}):FilterOnce() -- get all groups withing PackDistance from group using function
-- get template name of all vehicles in zone
-- determine if group is packable
for _, _Group in pairs(nearestGroups.Set) do -- convert #SET_GROUP to a list of Wrapper.Group#GROUP
for _, _Template in pairs(_DATABASE.Templates.Groups) do -- iterate through the database of templates
if (string.match(_Group:GetName(), _Template.GroupName)) then -- check if the Wrapper.Group#GROUP near the player is in the list of templates by name
-- generate crates and destroy group
for _, _entry in pairs(self.Cargo_Crates) do -- iterate through #CTLD_CARGO
if (_entry.Templates[1] == _Template.GroupName) then -- check if the #CTLD_CARGO matches the template name
_Group:Destroy() -- if a match is found destroy the Wrapper.Group#GROUP near the player
self:_GetCrates(Group, Unit, _entry, nil, false, true) -- spawn the appropriate crates near the player
return self
end
end
end
end
end
return self
end
--- (Internal) Function to repair nearby vehicles / FOBs
-- @param #CTLD self
-- @param Wrapper.Group#GROUP Group
@@ -3541,6 +3586,7 @@ function CTLD:_RefreshF10Menus()
if cancrates then
local loadmenu = MENU_GROUP_COMMAND:New(_group,"Load crates",topcrates, self._LoadCratesNearby, self, _group, _unit)
local cratesmenu = MENU_GROUP:New(_group,"Get Crates",topcrates)
local packmenu = MENU_GROUP_COMMAND:New(_group, "Pack crates", topcrates, self._PackCratesNearby, self, _group, _unit)
if self.usesubcats then
local subcatmenus = {}

View File

@@ -75,7 +75,7 @@ COHORT = {
livery = nil,
skill = nil,
legion = nil,
Ngroups = nil,
--Ngroups = nil,
Ngroups = 0,
engageRange = nil,
tacanChannel = {},
@@ -1098,7 +1098,7 @@ function COHORT:RecruitAssets(MissionType, Npayloads)
-- Assets on mission NOTHING are considered.
table.insert(assets, asset)
elseif self.legion:IsAssetOnMission(asset, AUFTRAG.Type.GCICAP) and MissionType==AUFTRAG.Type.INTERCEPT then
elseif self.legion:IsAssetOnMission(asset, {AUFTRAG.Type.GCICAP, AUFTRAG.Type.PATROLRACETRACK}) and MissionType==AUFTRAG.Type.INTERCEPT then
-- Check if the payload of this asset is compatible with the mission.
-- Note: we do not check the payload as an asset that is on a GCICAP mission should be able to do an INTERCEPT as well!
@@ -1595,4 +1595,3 @@ end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@@ -64,6 +64,7 @@
-- @field Core.Set#SET_ZONE NoGoZoneSet
-- @field #boolean Monitor
-- @field #boolean TankerInvisible
-- @field #number CapFormation
-- @extends Core.Fsm#FSM
--- *“Airspeed, altitude, and brains. Two are always needed to successfully complete the flight.”* -- Unknown.
@@ -207,6 +208,7 @@ EASYGCICAP = {
NoGoZoneSet = nil,
Monitor = false,
TankerInvisible = true,
CapFormation = nil,
}
--- Internal Squadron data type
@@ -242,7 +244,7 @@ EASYGCICAP = {
--- EASYGCICAP class version.
-- @field #string version
EASYGCICAP.version="0.0.8"
EASYGCICAP.version="0.0.9"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -289,6 +291,7 @@ function EASYGCICAP:New(Alias, AirbaseName, Coalition, EWRName)
self.repeatsonfailure = 3
self.Monitor = false
self.TankerInvisible = true
self.CapFormation = ENUMS.Formation.FixedWing.FingerFour.Group
-- Set some string id for output to DCS.log file.
self.lid=string.format("EASYGCICAP %s | ", self.alias)
@@ -313,6 +316,14 @@ end
-- Functions
-------------------------------------------------------------------------
--- Set CAP formation.
-- @param #EASYGCICAP self
-- @param #number Formation Formation to fly, defaults to ENUMS.Formation.FixedWing.FingerFour.Group
-- @return #EASYGCICAP self
function EASYGCICAP:SetCAPFormation(Formation)
self.CapFormation = Formation
return self
end
--- Set Tanker and AWACS to be invisible to enemy AI eyes
-- @param #EASYGCICAP self
@@ -476,6 +487,8 @@ end
function EASYGCICAP:_AddAirwing(Airbasename, Alias)
self:T(self.lid.."_AddAirwing "..Airbasename)
local CapFormation = self.CapFormation
-- Create Airwing
local CAP_Wing = AIRWING:New(Airbasename,Alias)
CAP_Wing:SetVerbosityLevel(3)
@@ -484,11 +497,15 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
CAP_Wing:SetAirbase(AIRBASE:FindByName(Airbasename))
CAP_Wing:SetRespawnAfterDestroyed()
CAP_Wing:SetNumberCAP(self.capgrouping)
CAP_Wing:SetCapCloseRaceTrack(true)
if CapFormation then
CAP_Wing:SetCAPFormation(CapFormation)
end
if #self.ManagedTK > 0 then
CAP_Wing:SetNumberTankerBoom(1)
CAP_Wing:SetNumberTankerProbe(1)
end
if #self.ManagedAW > 0 then
if #self.ManagedEWR > 0 then
CAP_Wing:SetNumberAWACS(1)
end
if #self.ManagedREC > 0 then
@@ -515,6 +532,9 @@ function EASYGCICAP:_AddAirwing(Airbasename, Alias)
flightgroup:SetDetection(true)
flightgroup:SetEngageDetectedOn(self.engagerange,{"Air"},self.GoZoneSet,self.NoGoZoneSet)
flightgroup:SetOutOfAAMRTB()
if CapFormation then
flightgroup:GetGroup():SetOption(AI.Option.Air.id.FORMATION,CapFormation)
end
end
if Mission.type == AUFTRAG.Type.TANKER or Mission.type == AUFTRAG.Type.AWACS or Mission.type == AUFTRAG.Type.RECON then
if TankerInvisible then
@@ -549,8 +569,8 @@ end
-- @param #EASYGCICAP self
-- @param #string AirbaseName Name of the Wing's airbase
-- @param Core.Point#COORDINATE Coordinate.
-- @param #number Altitude Defaults to 25000 feet.
-- @param #number Speed Defaults to 300 knots.
-- @param #number Altitude Defaults to 25000 feet ASL.
-- @param #number Speed Defaults to 300 knots TAS.
-- @param #number Heading Defaults to 90 degrees (East).
-- @param #number LegLength Defaults to 15 NM.
-- @return #EASYGCICAP self
@@ -888,7 +908,7 @@ function EASYGCICAP:_AddSquadron(TemplateName, SquadName, AirbaseName, AirFrames
self:T(self.lid.."_AddSquadron "..SquadName)
-- Add Squadrons
local Squadron_One = SQUADRON:New(TemplateName,AirFrames,SquadName)
Squadron_One:AddMissionCapability({AUFTRAG.Type.CAP, AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.ALERT5})
Squadron_One:AddMissionCapability({AUFTRAG.Type.CAP, AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.PATROLRACETRACK, AUFTRAG.Type.ALERT5})
--Squadron_One:SetFuelLowRefuel(true)
Squadron_One:SetFuelLowThreshold(0.3)
Squadron_One:SetTurnoverTime(10,20)
@@ -900,7 +920,7 @@ function EASYGCICAP:_AddSquadron(TemplateName, SquadName, AirbaseName, AirFrames
local wing = self.wings[AirbaseName][1] -- Ops.AirWing#AIRWING
wing:AddSquadron(Squadron_One)
wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.CAP, AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.ALERT5},75)
wing:NewPayload(TemplateName,-1,{AUFTRAG.Type.CAP, AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.PATROLRACETRACK, AUFTRAG.Type.ALERT5},75)
return self
end
@@ -1234,12 +1254,16 @@ function EASYGCICAP:onafterStatus(From,Event,To)
local capmission = 0
local interceptmission = 0
local reconmission = 0
local awacsmission = 0
local tankermission = 0
for _,_wing in pairs(self.wings) do
local count = _wing[1]:CountAssetsOnMission(MissionTypes,Cohort)
local count2 = _wing[1]:CountAssets(true,MissionTypes,Attributes)
capmission = capmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.GCICAP})
capmission = capmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.GCICAP,AUFTRAG.Type.PATROLRACETRACK})
interceptmission = interceptmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.INTERCEPT})
reconmission = reconmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.RECON})
awacsmission = awacsmission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.AWACS})
tankermission = tankermission + _wing[1]:CountMissionsInQueue({AUFTRAG.Type.TANKER})
assets = assets + count
instock = instock + count2
end
@@ -1251,6 +1275,8 @@ function EASYGCICAP:onafterStatus(From,Event,To)
text = text.."\nMissions: "..capmission+interceptmission
text = text.."\n - CAP: "..capmission
text = text.."\n - Intercept: "..interceptmission
text = text.."\n - AWACS: "..awacsmission
text = text.."\n - TANKER: "..tankermission
text = text.."\n - Recon: "..reconmission
MESSAGE:New(text,15,"GCICAP"):ToAll():ToLogIf(self.debug)
end

File diff suppressed because it is too large Load Diff

View File

@@ -2630,6 +2630,9 @@ function FLIGHTGROUP:onbeforeUpdateRoute(From, Event, To, n, N)
elseif task.dcstask.id==AUFTRAG.SpecialTask.RECON then
-- For recon missions, we need to allow the update as we insert new waypoints.
self:T2(self.lid.."Allowing update route for Task: ReconMission")
elseif task.dcstask.id==AUFTRAG.SpecialTask.PATROLRACETRACK then
-- For recon missions, we need to allow the update as we insert new waypoints.
self:T2(self.lid.."Allowing update route for Task: Patrol Race Track")
elseif task.dcstask.id==AUFTRAG.SpecialTask.HOVER then
-- For recon missions, we need to allow the update as we insert new waypoints.
self:T2(self.lid.."Allowing update route for Task: Hover")

View File

@@ -965,7 +965,7 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission, Assets)
local pause=false
-- Check if mission is INTERCEPT and asset is currently on GCI mission. If so, GCI is paused.
if currM.type==AUFTRAG.Type.GCICAP and Mission.type==AUFTRAG.Type.INTERCEPT then
if (currM.type==AUFTRAG.Type.GCICAP or currM.type==AUFTRAG.Type.PATROLRACETRACK) and Mission.type==AUFTRAG.Type.INTERCEPT then
pause=true
elseif (currM.type==AUFTRAG.Type.ONGUARD or currM.type==AUFTRAG.Type.PATROLZONE) and (Mission.type==AUFTRAG.Type.ARTY or Mission.type==AUFTRAG.Type.GROUNDATTACK) then
pause=true
@@ -1445,7 +1445,7 @@ end
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset that returned.
function LEGION:onafterLegionAssetReturned(From, Event, To, Cohort, Asset)
-- Debug message.
self:I(self.lid..string.format("Asset %s from Cohort %s returned! asset.assignment=\"%s\"", Asset.spawngroupname, Cohort.name, tostring(Asset.assignment)))
self:T(self.lid..string.format("Asset %s from Cohort %s returned! asset.assignment=\"%s\"", Asset.spawngroupname, Cohort.name, tostring(Asset.assignment)))
-- Stop flightgroup.
if Asset.flightgroup and not Asset.flightgroup:IsStopped() then
@@ -2557,9 +2557,10 @@ function LEGION._CohortCan(Cohort, MissionType, Categories, Attributes, Properti
-- Distance to target.
local TargetDistance=TargetVec2 and UTILS.VecDist2D(TargetVec2, cohort.legion:GetVec2()) or 0
-- Is in range?
local Rmax=cohort:GetMissionRange(WeaponTypes)
local RangeMax = RangeMax or 0
local InRange=(RangeMax and math.max(RangeMax, Rmax) or Rmax) >= TargetDistance
return InRange
@@ -2610,6 +2611,7 @@ function LEGION._CohortCan(Cohort, MissionType, Categories, Attributes, Properti
-- Is capable of the mission type?
local can=AUFTRAG.CheckMissionCapability(MissionType, Cohort.missiontypes)
if can then
can=CheckCategory(Cohort)
else
@@ -2687,7 +2689,7 @@ function LEGION._CohortCan(Cohort, MissionType, Categories, Attributes, Properti
return nil
end
--- Recruit assets from Cohorts for the given parameters. **NOTE** that we set the `asset.isReserved=true` flag so it cant be recruited by anyone else.
--- Recruit assets from Cohorts for the given parameters. **NOTE** that we set the `asset.isReserved=true` flag so it cannot be recruited by anyone else.
-- @param #table Cohorts Cohorts included.
-- @param #string MissionTypeRecruit Mission type for recruiting the cohort assets.
-- @param #string MissionTypeOpt Mission type for which the assets are optimized. Default is the same as `MissionTypeRecruit`.
@@ -3181,7 +3183,7 @@ function LEGION.CalculateAssetMissionScore(asset, MissionType, TargetVec2, Inclu
if currmission.type==AUFTRAG.Type.ALERT5 and currmission.alert5MissionType==MissionType then
-- Prefer assets that are on ALERT5 for this mission type.
score=score+25
elseif currmission.type==AUFTRAG.Type.GCICAP and MissionType==AUFTRAG.Type.INTERCEPT then
elseif (currmission.type==AUFTRAG.Type.GCICAP or currmission.type==AUFTRAG.Type.PATROLRACETRACK) and MissionType==AUFTRAG.Type.INTERCEPT then
-- Prefer assets that are on GCICAP to perform INTERCEPTS. We set this even higher than alert5 because they are already in the air.
score=score+35
elseif (currmission.type==AUFTRAG.Type.ONGUARD or currmission.type==AUFTRAG.Type.PATROLZONE) and (MissionType==AUFTRAG.Type.ARTY or MissionType==AUFTRAG.Type.GROUNDATTACK) then

View File

@@ -1774,6 +1774,8 @@ function OPSGROUP:GetDCSUnit(UnitNumber)
if DCSGroup then
local unit=DCSGroup:getUnit(UnitNumber or 1)
return unit
else
self:E(self.lid..string.format("ERROR: DCS group does not exist! Cannot get unit"))
end
return nil
@@ -3523,9 +3525,11 @@ function OPSGROUP:OnEventBirth(EventData)
local element=self:GetElementByName(unitname)
if element and element.status~=OPSGROUP.ElementStatus.SPAWNED then
-- Debug info.
self:T(self.lid..string.format("EVENT: Element %s born ==> spawned", unitname))
self:T2(self.lid..string.format("DCS unit=%s isExist=%s", tostring(EventData.IniDCSUnit:getName()), tostring(EventData.IniDCSUnit:isExist()) ))
-- Set element to spawned state.
self:ElementSpawned(element)
@@ -4469,6 +4473,26 @@ function OPSGROUP:_UpdateTask(Task, Mission)
if target then
self:EngageTarget(target, speed, Task.dcstask.params.formation)
end
elseif Task.dcstask.id==AUFTRAG.SpecialTask.PATROLRACETRACK then
---
-- Task "Patrol Race Track" Mission.
---
if self.isFlightgroup then
self:T("We are Special Auftrag Patrol Race Track, starting now ...")
--self:I({Task.dcstask.params})
--[[
Task.dcstask.params.TrackAltitude = self.TrackAltitude
Task.dcstask.params.TrackSpeed = self.TrackSpeed
Task.dcstask.params.TrackPoint1 = self.TrackPoint1
Task.dcstask.params.TrackPoint2 = self.TrackPoint2
Task.dcstask.params.TrackFormation = self.TrackFormation
--]]
local aircraft = self:GetGroup()
aircraft:PatrolRaceTrack(Task.dcstask.params.TrackPoint1,Task.dcstask.params.TrackPoint2,Task.dcstask.params.TrackAltitude,Task.dcstask.params.TrackSpeed,Task.dcstask.params.TrackFormation,false,1)
end
elseif Task.dcstask.id==AUFTRAG.SpecialTask.HOVER then

View File

@@ -21,7 +21,7 @@
-- ===
-- @module Ops.PlayerTask
-- @image OPS_PlayerTask.jpg
-- @date Last Update Sept 2023
-- @date Last Update Oct 2023
do
@@ -98,7 +98,7 @@ PLAYERTASK = {
--- PLAYERTASK class version.
-- @field #string version
PLAYERTASK.version="0.1.20"
PLAYERTASK.version="0.1.21"
--- Generic task condition.
-- @type PLAYERTASK.Condition
@@ -470,10 +470,11 @@ end
--- [User] Remove a client from this task
-- @param #PLAYERTASK self
-- @param Wrapper.Client#CLIENT Client
-- @param #string Name Name of the client
-- @return #PLAYERTASK self
function PLAYERTASK:RemoveClient(Client)
function PLAYERTASK:RemoveClient(Client,Name)
self:T(self.lid.."RemoveClient")
local name = Client:GetPlayerName()
local name = Name or Client:GetPlayerName()
if self.Clients:HasUniqueID(name) then
self.Clients:PullByID(name)
if self.verbose then
@@ -1551,7 +1552,7 @@ PLAYERTASKCONTROLLER.Messages = {
--- PLAYERTASK class version.
-- @field #string version
PLAYERTASKCONTROLLER.version="0.1.61"
PLAYERTASKCONTROLLER.version="0.1.62"
--- Create and run a new TASKCONTROLLER instance.
-- @param #PLAYERTASKCONTROLLER self
@@ -2186,6 +2187,10 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData)
task:RemoveClient(Client)
--text = "Task aborted!"
text = self.gettext:GetEntry("TASKABORT",self.locale)
else
task:RemoveClient(nil,EventData.IniPlayerName)
--text = "Task aborted!"
text = self.gettext:GetEntry("TASKABORT",self.locale)
end
else
--text = "No active task!"

View File

@@ -63,6 +63,7 @@
-- @field #boolean eplrs If true, enable data link, e.g. if used as AWACS.
-- @field #boolean recovery If true, tanker will recover using the AIRBOSS marshal pattern.
-- @field #number terminaltype Terminal type of used parking spots on airbases.
-- @field #boolean unlimitedfuel If true, the tanker will have unlimited fuel.
-- @extends Core.Fsm#FSM
--- Recovery Tanker.
@@ -300,6 +301,7 @@ RECOVERYTANKER = {
eplrs = nil,
recovery = nil,
terminaltype = nil,
unlimitedfuel = false,
}
--- Unique ID (global).
@@ -308,7 +310,7 @@ _RECOVERYTANKERID=0
--- Class version.
-- @field #string version
RECOVERYTANKER.version="1.0.9"
RECOVERYTANKER.version="1.0.10"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -326,6 +328,7 @@ RECOVERYTANKER.version="1.0.9"
-- DONE: Set AA TACAN.
-- DONE: Add refueling event/state.
-- DONE: Possibility to add already present/spawned aircraft, e.g. for warehouse.
-- DONE: Add unlimited fuel
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
@@ -550,6 +553,15 @@ end
-- User functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Set the tanker to have unlimited fuel.
-- @param #RECOVERYTANKER self
-- @param #boolean OnOff If true, the tanker will have unlimited fuel.
-- @return #RECOVERYTANKER self
function RECOVERYTANKER:SetUnlimitedFuel(OnOff)
self.unlimitedfuel = OnOff
return self
end
--- Set the speed the tanker flys in its orbit pattern.
-- @param #RECOVERYTANKER self
-- @param #number speed True air speed (TAS) in knots. Default 274 knots, which results in ~250 KIAS.
@@ -899,6 +911,14 @@ function RECOVERYTANKER:onafterStart(From, Event, To)
-- Spawn tanker. We need to introduce an alias in case this class is used twice. This would confuse the spawn routine.
local Spawn=SPAWN:NewWithAlias(self.tankergroupname, self.alias)
if self.unlimitedfuel then
Spawn:OnSpawnGroup(
function (grp)
grp:CommandSetUnlimitedFuel(self.unlimitedfuel)
end
)
end
-- Set radio frequency and modulation.
Spawn:InitRadioCommsOnOff(true)
Spawn:InitRadioFrequency(self.RadioFreq)

View File

@@ -64,8 +64,6 @@
--
-- ===
--
-- ![Banner Image](..\Presentations\RESCUEHELO\RescueHelo_Main.png)
--
-- # Recue Helo
--
-- The rescue helo will fly in close formation with another unit, which is typically an aircraft carrier.