Merge branch 'FF/Ops' into FF/OpsDev

This commit is contained in:
Frank
2021-08-02 11:59:32 +02:00
86 changed files with 25224 additions and 5635 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -27,14 +27,19 @@
-- **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 Harry S. Truman](https://en.wikipedia.org/wiki/USS_Harry_S._Truman) (CVN-75) [Super Carrier Module]
-- * [USS Tarawa](https://en.wikipedia.org/wiki/USS_Tarawa_(LHA-1)) (LHA-1) [**WIP**]
--
-- **Supported Aircraft:**
--
-- * [F/A-18C Hornet Lot 20](https://forums.eagle.ru/forumdisplay.php?f=557) (Player & AI)
-- * [F-14B Tomcat](https://forums.eagle.ru/forumdisplay.php?f=395) (Player & AI)
-- * [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) [**WIP**]
-- * [T-45C Goshawk](https://www.vnao-cvw-7.com/t-45-goshawk) (VNAO)(Player & AI) [**WIP**]
-- * F/A-18C Hornet (AI)
-- * F-14A Tomcat (AI)
-- * E-2D Hawkeye (AI)
@@ -47,7 +52,9 @@
-- the no other fixed wing aircraft (human or AI controlled) are supposed to land on the Tarawa. Currently only Case I is supported. Case II/III take slightly steps from the CVN carrier.
-- However, the two Case II/III pattern are very similar so this is not a big drawback.
--
-- Heatblur's mighty F-14B Tomcat has been added (March 13th 2019) as well.
-- 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.
--
-- ## Discussion
--
@@ -294,7 +301,7 @@
--
-- ![Banner Image](..\Presentations\AIRBOSS\Airboss_Case3.png)
--
-- A Case III recovery is conducted during nighttime. The holding position and the landing pattern are rather different from a Case I recovery as can be seen in the image above.
-- A Case III recovery is conducted during nighttime or when the visibility is below CASE II minima during the day. The holding position and the landing pattern are rather different from a Case I recovery as can be seen in the image above.
--
-- The first holding zone starts 21 NM astern the carrier at angels 6. The separation between the stacks is 1000 ft just like in Case I. However, the distance to the boat
-- increases by 1 NM with each stack. The general form can be written as D=15+6+(N-1), where D is the distance to the boat in NM and N the number of the stack starting at N=1.
@@ -566,7 +573,10 @@
-- * **L**ined **U**p **L**eft or **R**ight: LUL, LUR
-- * Too **H**igh or too **LO**w: H, LO
-- * Too **F**ast or too **SLO**w: F, SLO
-- * **Fly through** glideslope **down** or **up**: \\ , /
-- * **O**ver**S**hoot: OS, only referenced during **X**
-- * **Fly through** glideslope **down** or **up**: \\ , /, advisory only
-- * **D**rift **L**eft or **R**ight:DL, DR, advisory only
-- * **A**ngled **A**pproach: Angled approach (wings level and LUL): AA, advisory only
--
-- Each grading, x, is subdivided by
--
@@ -627,7 +637,7 @@
--
-- ## Foul Deck Waveoff
--
-- A foul deck waveoff is called by the LSO if an aircraft is detected within the landing area when an approaching aircraft is crossing the ship's wake during Case I/II operations,
-- A foul deck waveoff is called by the LSO if an aircraft is detected within the landing area when an approaching aircraft is at position IM-IC during Case I/II operations,
-- or with an aircraft approaching the 3/4 NM during Case III operations.
--
-- The approaching aircraft will be notified via LSO radio comms and is supposed to overfly the landing area to enter the Bolter pattern. **This pass is not graded**.
@@ -1257,6 +1267,7 @@ AIRBOSS = {
-- @field #string S3BTANKER Lockheed S-3B Viking tanker.
-- @field #string E2D Grumman E-2D Hawkeye AWACS.
-- @field #string C2A Grumman C-2A Greyhound from Military Aircraft Mod.
-- @field #string T45C T-45C by VNAO
AIRBOSS.AircraftCarrier={
AV8B="AV8BNA",
HORNET="FA-18C_hornet",
@@ -1265,6 +1276,7 @@ AIRBOSS.AircraftCarrier={
F14B="F-14B",
F14A_AI="F-14A",
FA18C="F/A-18C",
T45C="T-45",
S3B="S-3B",
S3BTANKER="S-3B Tanker",
E2D="E-2C",
@@ -1332,6 +1344,8 @@ AIRBOSS.CarrierType={
-- @field #number _min Min _OK_ value. Default -0.5 deg.
-- @field #number Left (LUR) threshold. Default -1.0 deg.
-- @field #number Right (LUL) threshold. Default 1.0 deg.
-- @field #number LeftMed threshold for AA/OS measuring. Default -2.0 deg.
-- @field #number RightMed threshold for AA/OS measuring. Default 2.0 deg.
-- @field #number LEFT LUR threshold. Default -3.0 deg.
-- @field #number RIGHT LUL threshold. Default 3.0 deg.
@@ -1699,7 +1713,7 @@ AIRBOSS.MenuF10Root=nil
--- Airboss class version.
-- @field #string version
AIRBOSS.version="1.1.5"
AIRBOSS.version="1.1.6"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -1933,7 +1947,7 @@ function AIRBOSS:New(carriername, alias)
-- Welcome players.
self:SetWelcomePlayers(true)
-- Coordinates
self.landingcoord=COORDINATE:New(0,0,0) --Core.Point#COORDINATE
self.sterncoord=COORDINATE:New(0, 0, 0) --Core.Point#COORDINATE
@@ -1945,11 +1959,11 @@ function AIRBOSS:New(carriername, alias)
elseif self.carriertype==AIRBOSS.CarrierType.ROOSEVELT then
self:_InitNimitz()
elseif self.carriertype==AIRBOSS.CarrierType.LINCOLN then
self:_InitNimitz()
elseif self.carriertype==AIRBOSS.CarrierType.WASHINGTON then
self:_InitNimitz()
elseif self.carriertype==AIRBOSS.CarrierType.TRUMAN then
self:_InitNimitz()
elseif self.carriertype==AIRBOSS.CarrierType.WASHINGTON then
self:_InitNimitz()
elseif self.carriertype==AIRBOSS.CarrierType.TRUMAN then
self:_InitNimitz()
elseif self.carriertype==AIRBOSS.CarrierType.VINSON then
-- TODO: Carl Vinson parameters.
self:_InitStennis()
@@ -1987,7 +2001,7 @@ function AIRBOSS:New(carriername, alias)
self:_GetZoneGroove():SmokeZone(SMOKECOLOR.Red, 5)
self:_GetZoneLineup():SmokeZone(SMOKECOLOR.Green, 5)
self:_GetZoneBullseye(case):SmokeZone(SMOKECOLOR.White, 45)
self:_GetZoneDirtyUp(case):SmokeZone(SMOKECOLOR.Orange, 45)
self:_GetZoneDirtyUp(case):SmokeZone(SMOKECOLOR.Orange, 45)
self:_GetZoneArcIn(case):SmokeZone(SMOKECOLOR.Blue, 45)
self:_GetZoneArcOut(case):SmokeZone(SMOKECOLOR.Blue, 45)
self:_GetZonePlatform(case):SmokeZone(SMOKECOLOR.Blue, 45)
@@ -2027,7 +2041,7 @@ function AIRBOSS:New(carriername, alias)
-- Bow
bow:FlareYellow()
-- Runway half width = 10 m.
local r1=stern:Translate(self.carrierparam.rwywidth*0.5, FB+90)
local r2=stern:Translate(self.carrierparam.rwywidth*0.5, FB-90)
@@ -2640,6 +2654,15 @@ function AIRBOSS:SetRecoveryTurnTime(interval)
return self
end
--- Set multiplayer environment wire correction.
-- @param #AIRBOSS self
-- @param #number Dcorr Correction distance in meters. Default 12 m.
-- @return #AIRBOSS self
function AIRBOSS:SetMPWireCorrection(Dcorr)
self.mpWireCorrection=Dcorr or 12
return self
end
--- Set time interval for updating queues and other stuff.
-- @param #AIRBOSS self
-- @param #number interval Time interval in seconds. Default 30 sec.
@@ -2798,16 +2821,20 @@ end
-- @param #number _max
-- @param #number _min
-- @param #number Left
-- @param #number LeftMed
-- @param #number LEFT
-- @param #number Right
-- @param #number RightMed
-- @param #number RIGHT
-- @return #AIRBOSS self
function AIRBOSS:SetLineupErrorThresholds(_max,_min, Left, LEFT, Right, RIGHT)
function AIRBOSS:SetLineupErrorThresholds(_max,_min, Left, LeftMed, LEFT, Right, RightMed, RIGHT)
self.lue._max=_max or 0.5
self.lue._min=_min or -0.5
self.lue.Left=Left or -1.0
self.lue.LeftMed=LeftMed or -2.0
self.lue.LEFT=LEFT or -3.0
self.lue.Right=Right or 1.0
self.lue.RightMed=RightMed or 2.0
self.lue.RIGHT=RIGHT or 3.0
return self
end
@@ -3278,7 +3305,7 @@ function AIRBOSS:GetNextRecoveryTime(InSeconds)
if InSeconds then
return self.recoverywindow.START, self.recoverywindow.STOP
else
return UTILS.SecondsToClock(self.recoverywindow.START), UTILS.SecondsToClock(self.recoverywindow.STOP)
return UTILS.SecondsToClock(self.recoverywindow.START), UTILS.SecondsToClock(self.recoverywindow.STOP)
end
else
if InSeconds then
@@ -3392,7 +3419,7 @@ function AIRBOSS:onafterStart(From, Event, To)
--self.StatusScheduler=SCHEDULER:New(self)
--self.StatusScheduler:Schedule(self, self._Status, {}, 1, 0.5)
self.StatusTimer=TIMER:New(self._Status, self):Start(2, 0.5)
-- Start status check in 1 second.
@@ -3478,9 +3505,9 @@ function AIRBOSS:onafterStatus(From, Event, To)
-- Disable turn into the wind for this window so that we do not do this all over again.
self.recoverywindow.WIND=false
end
end
end
@@ -3574,36 +3601,39 @@ function AIRBOSS:_CheckAIStatus()
-- Unit
local unit=element.unit
-- Get lineup and distance to carrier.
local lineup=self:_Lineup(unit, true)
local unitcoord=unit:GetCoord()
local dist=unitcoord:Get2DDistance(self:GetCoord())
if unit and unit:IsAlive() then
-- Distance in NM.
local distance=UTILS.MetersToNM(dist)
-- Get lineup and distance to carrier.
local lineup=self:_Lineup(unit, true)
-- Altitude in ft.
local alt=UTILS.MetersToFeet(unitcoord.y)
local unitcoord=unit:GetCoord()
-- Check if parameters are right and flight is in the groove.
if lineup<2 and distance<=0.75 and alt<500 and not element.ballcall then
local dist=unitcoord:Get2DDistance(self:GetCoord())
-- Paddles: Call the ball!
self:RadioTransmission(self.LSORadio, self.LSOCall.CALLTHEBALL, nil, nil, nil, true)
-- Distance in NM.
local distance=UTILS.MetersToNM(dist)
-- Pilot: "405, Hornet Ball, 3.2"
self:_LSOCallAircraftBall(element.onboard,self:_GetACNickname(unit:GetTypeName()), self:_GetFuelState(unit)/1000)
-- Altitude in ft.
local alt=UTILS.MetersToFeet(unitcoord.y)
-- Paddles: Roger ball after 0.5 seconds.
self:RadioTransmission(self.LSORadio, self.LSOCall.ROGERBALL, nil, nil, 0.5, true)
-- Check if parameters are right and flight is in the groove.
if lineup<2 and distance<=0.75 and alt<500 and not element.ballcall then
-- Flight element called the ball.
element.ballcall=true
-- Paddles: Call the ball!
self:RadioTransmission(self.LSORadio, self.LSOCall.CALLTHEBALL, nil, nil, nil, true)
-- This is for the whole flight. Maybe we need it.
flight.ballcall=true
-- Pilot: "405, Hornet Ball, 3.2"
self:_LSOCallAircraftBall(element.onboard,self:_GetACNickname(unit:GetTypeName()), self:_GetFuelState(unit)/1000)
-- Paddles: Roger ball after 0.5 seconds.
self:RadioTransmission(self.LSORadio, self.LSOCall.ROGERBALL, nil, nil, 0.5, true)
-- Flight element called the ball.
element.ballcall=true
-- This is for the whole flight. Maybe we need it.
flight.ballcall=true
end
end
end
@@ -4139,7 +4169,7 @@ end
-- @param #string To To state.
function AIRBOSS:onafterStop(From, Event, To)
self:I(self.lid..string.format("Stopping airboss script."))
-- Unhandle events.
self:UnHandleEvent(EVENTS.Birth)
self:UnHandleEvent(EVENTS.Land)
@@ -4163,7 +4193,7 @@ function AIRBOSS:_InitStennis()
-- Carrier Parameters.
self.carrierparam.sterndist =-153
self.carrierparam.deckheight = 19
self.carrierparam.deckheight = 19.06
-- Total size of the carrier (approx as rectangle).
self.carrierparam.totlength=310 -- Wiki says 332.8 meters overall length.
@@ -4171,7 +4201,7 @@ function AIRBOSS:_InitStennis()
self.carrierparam.totwidthstarboard=30
-- Landing runway.
self.carrierparam.rwyangle = -9
self.carrierparam.rwyangle = -9.1359
self.carrierparam.rwylength = 225
self.carrierparam.rwywidth = 20
@@ -4314,7 +4344,7 @@ function AIRBOSS:_InitNimitz()
-- Carrier Parameters.
self.carrierparam.sterndist =-164
self.carrierparam.deckheight = 20
self.carrierparam.deckheight = 20.1494 --DCS World OpenBeta\CoreMods\tech\USS_Nimitz\Database\USS_CVN_7X.lua
-- Total size of the carrier (approx as rectangle).
self.carrierparam.totlength=332.8 -- Wiki says 332.8 meters overall length.
@@ -4322,7 +4352,7 @@ function AIRBOSS:_InitNimitz()
self.carrierparam.totwidthstarboard=35
-- Landing runway.
self.carrierparam.rwyangle = -9
self.carrierparam.rwyangle = -9.1359 --DCS World OpenBeta\CoreMods\tech\USS_Nimitz\scripts\USS_Nimitz_RunwaysAndRoutes.lua
self.carrierparam.rwylength = 250
self.carrierparam.rwywidth = 25
@@ -5465,6 +5495,7 @@ function AIRBOSS:_GetAircraftAoA(playerData)
-- Get AC type.
local hornet=playerData.actype==AIRBOSS.AircraftCarrier.HORNET
local goshawk=playerData.actype==AIRBOSS.AircraftCarrier.T45C
local skyhawk=playerData.actype==AIRBOSS.AircraftCarrier.A4EC
local harrier=playerData.actype==AIRBOSS.AircraftCarrier.AV8B
local tomcat=playerData.actype==AIRBOSS.AircraftCarrier.F14A or playerData.actype==AIRBOSS.AircraftCarrier.F14B
@@ -5491,6 +5522,15 @@ function AIRBOSS:_GetAircraftAoA(playerData)
aoa.OnSpeedMin = self:_AoAUnit2Deg(playerData, 14.5) --14.17 --14.5 units
aoa.Fast = self:_AoAUnit2Deg(playerData, 14.0) --13.33 --14.0 units
aoa.FAST = self:_AoAUnit2Deg(playerData, 13.0) --11.67 --13.0 units
elseif goshawk then
-- T-45C Goshawk parameters.
aoa.SLOW = 8.00 --19
aoa.Slow = 7.75 --18
aoa.OnSpeedMax = 7.25 --17.5
aoa.OnSpeed = 7.00 --17
aoa.OnSpeedMin = 6.75 --16.5
aoa.Fast = 6.25 --16
aoa.FAST = 6.00 --15
elseif skyhawk then
-- A-4E-C Skyhawk parameters from https://forums.eagle.ru/showpost.php?p=3703467&postcount=390
-- Note that these are arbitrary UNITS and not degrees. We need a conversion formula!
@@ -5675,6 +5715,9 @@ function AIRBOSS:_GetAircraftParameters(playerData, step)
elseif skyhawk then
alt=UTILS.FeetToMeters(600)
speed=UTILS.KnotsToMps(250)
elseif goshawk then
alt=UTILS.FeetToMeters(800)
speed=UTILS.KnotsToMps(300)
end
elseif step==AIRBOSS.PatternStep.BREAKENTRY then
@@ -5685,11 +5728,14 @@ function AIRBOSS:_GetAircraftParameters(playerData, step)
elseif skyhawk then
alt=UTILS.FeetToMeters(600)
speed=UTILS.KnotsToMps(250)
elseif goshawk then
alt=UTILS.FeetToMeters(800)
speed=UTILS.KnotsToMps(300)
end
elseif step==AIRBOSS.PatternStep.EARLYBREAK then
if hornet or tomcat or harrier then
if hornet or tomcat or harrier or goshawk then
alt=UTILS.FeetToMeters(800)
elseif skyhawk then
alt=UTILS.FeetToMeters(600)
@@ -5697,7 +5743,7 @@ function AIRBOSS:_GetAircraftParameters(playerData, step)
elseif step==AIRBOSS.PatternStep.LATEBREAK then
if hornet or tomcat or harrier then
if hornet or tomcat or harrier or goshawk then
alt=UTILS.FeetToMeters(800)
elseif skyhawk then
alt=UTILS.FeetToMeters(600)
@@ -5705,7 +5751,7 @@ function AIRBOSS:_GetAircraftParameters(playerData, step)
elseif step==AIRBOSS.PatternStep.ABEAM then
if hornet or tomcat or harrier then
if hornet or tomcat or harrier or goshawk then
alt=UTILS.FeetToMeters(600)
elseif skyhawk then
alt=UTILS.FeetToMeters(500)
@@ -5720,10 +5766,19 @@ function AIRBOSS:_GetAircraftParameters(playerData, step)
dist=UTILS.NMToMeters(1.2)
end
if goshawk then
-- 0.9 to 1.1 NM per natops ch.4 page 48
dist=UTILS.NMToMeters(0.9)
else
dist=UTILS.NMToMeters(1.1)
end
elseif step==AIRBOSS.PatternStep.NINETY then
if hornet or tomcat then
alt=UTILS.FeetToMeters(500)
elseif goshawk then
alt=UTILS.FeetToMeters(450)
elseif skyhawk then
alt=UTILS.FeetToMeters(500)
elseif harrier then
@@ -5734,7 +5789,7 @@ function AIRBOSS:_GetAircraftParameters(playerData, step)
elseif step==AIRBOSS.PatternStep.WAKE then
if hornet then
if hornet or goshawk then
alt=UTILS.FeetToMeters(370)
elseif tomcat then
alt=UTILS.FeetToMeters(430) -- Tomcat should be a bit higher as it intercepts the GS a bit higher.
@@ -5747,7 +5802,7 @@ function AIRBOSS:_GetAircraftParameters(playerData, step)
elseif step==AIRBOSS.PatternStep.FINAL then
if hornet then
if hornet or goshawk then
alt=UTILS.FeetToMeters(300)
elseif tomcat then
alt=UTILS.FeetToMeters(360)
@@ -6066,6 +6121,11 @@ function AIRBOSS:_ScanCarrierZone()
-- Get flight group if possible.
local knownflight=self:_GetFlightFromGroupInQueue(group, self.flights)
-- Unknown new AI flight. Create a new flight group.
if not knownflight and not self:_IsHuman(group) then
knownflight=self:_CreateFlightGroup(group)
end
-- Get aircraft type name.
local actype=group:GetTypeName()
@@ -6079,10 +6139,10 @@ function AIRBOSS:_ScanCarrierZone()
local putintomarshal=false
-- Get flight group.
local flight=_DATABASE:GetFlightGroup(groupname)
local flight=_DATABASE:GetOpsGroup(groupname)
if flight and flight:IsInbound() and flight.destbase:GetName()==self.carrier:GetName() then
if flight.ishelo then
if flight.isHelo then
else
putintomarshal=true
end
@@ -6117,16 +6177,13 @@ function AIRBOSS:_ScanCarrierZone()
-- Break the loop to not have all flights at once! Spams the message screen.
break
end -- Closed in or tanker/AWACS
end -- Closed in or tanker/AWACS
end
else
-- Unknown new AI flight. Create a new flight group.
if not self:_IsHuman(group) then
self:_CreateFlightGroup(group)
end
end
@@ -6223,7 +6280,7 @@ function AIRBOSS:_MarshalPlayer(playerData, stack)
-- Set stack flag.
flight.flag=stack
-- Trigger Marshal event.
self:Marshal(flight)
end
@@ -6482,7 +6539,7 @@ function AIRBOSS:_MarshalAI(flight, nstack, respawn)
-- Route group.
flight.group:Route(wp, 1)
-- Trigger Marshal event.
self:Marshal(flight)
@@ -7018,7 +7075,7 @@ function AIRBOSS:_GetFreeStack(ai, case, empty)
-- Recovery case.
case=case or self.case
if case==1 then
return self:_GetFreeStack_Old(ai, case, empty)
end
@@ -7034,7 +7091,7 @@ function AIRBOSS:_GetFreeStack(ai, case, empty)
for i=1,nmaxstacks do
stack[i]=self.NmaxStack -- Number of human flights per stack.
end
local nmax=1
-- Loop over all flights in marshal stack.
@@ -7046,7 +7103,7 @@ function AIRBOSS:_GetFreeStack(ai, case, empty)
-- Get stack of flight.
local n=flight.flag
if n>nmax then
nmax=n
end
@@ -7063,7 +7120,7 @@ function AIRBOSS:_GetFreeStack(ai, case, empty)
end
end
local nfree=nil
if stack[nmax]==0 then
-- Max occupied stack is completely full!
@@ -7079,7 +7136,7 @@ function AIRBOSS:_GetFreeStack(ai, case, empty)
-- Case II/III return next stack
nfree=nmax+1
end
elseif stack[nmax]==self.NmaxStack then
-- Max occupied stack is completely empty! This should happen only when there is no other flight in the marshal queue.
self:E(self.lid..string.format("ERROR: Max occupied stack is empty. Should not happen! Nmax=%d, stack[nmax]=%d", nmax, stack[nmax]))
@@ -7091,7 +7148,7 @@ function AIRBOSS:_GetFreeStack(ai, case, empty)
else
nfree=nmax
end
end
self:I(self.lid..string.format("Returning free stack %s", tostring(nfree)))
@@ -10054,6 +10111,27 @@ function AIRBOSS:_Groove(playerData)
-- Distance in NM.
local d=UTILS.MetersToNM(rho)
-- Drift on lineup.
if rho>=RAR and rho<=RIM then
if gd.LUE>0.22 and lineupError<-0.22 then
env.info" Drift Right across centre ==> DR-"
gd.Drift=" DR"
self:T(self.lid..string.format("Got Drift Right across centre step %s, d=%.3f: Max LUE=%.3f, lower LUE=%.3f", gs, d, gd.LUE, lineupError))
elseif gd.LUE<-0.22 and lineupError>0.22 then
env.info" Drift Left ==> DL-"
gd.Drift=" DL"
self:T(self.lid..string.format("Got Drift Left across centre at step %s, d=%.3f: Min LUE=%.3f, lower LUE=%.3f", gs, d, gd.LUE, lineupError))
elseif gd.LUE>0.13 and lineupError<-0.14 then
env.info" Little Drift Right across centre ==> (DR-)"
gd.Drift=" (DR)"
self:T(self.lid..string.format("Got Little Drift Right across centre at step %s, d=%.3f: Max LUE=%.3f, lower LUE=%.3f", gs, d, gd.LUE, lineupError))
elseif gd.LUE<-0.13 and lineupError>0.14 then
env.info" Little Drift Left across centre ==> (DL-)"
gd.Drift=" (DL)"
self:E(self.lid..string.format("Got Little Drift Left across centre at step %s, d=%.3f: Min LUE=%.3f, lower LUE=%.3f", gs, d, gd.LUE, lineupError))
end
end
-- Update max deviation of line up error.
if math.abs(lineupError)>math.abs(gd.LUE) then
self:T(self.lid..string.format("Got bigger LUE at step %s, d=%.3f: LUE %.3f>%.3f", gs, d, lineupError, gd.LUE))
@@ -10364,7 +10442,7 @@ function AIRBOSS:_GetSternCoord()
self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(7, FB+90, true, true)
else
-- Nimitz SC: translate 8 meters starboard wrt Final bearing.
self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(8.5, FB+90, true, true)
self.sterncoord:Translate(self.carrierparam.sterndist, hdg, true, true):Translate(9.5, FB+90, true, true)
end
-- Set altitude.
@@ -10394,6 +10472,11 @@ function AIRBOSS:_GetWire(Lcoord, dc)
-- Corrected landing distance wrt to stern. Landing distance needs to be reduced due to delayed landing event for human players.
local d=Ldist-dc
-- Multiplayer wire correction.
if self.mpWireCorrection then
d=d-self.mpWireCorrection
end
-- Shift wires from stern to their correct position.
local w1=self.carrierparam.wire1
@@ -10485,6 +10568,9 @@ function AIRBOSS:_Trapped(playerData)
elseif playerData.actype==AIRBOSS.AircraftCarrier.A4EC then
-- A-4E gets slowed down much faster the the F/A-18C!
dcorr=56
elseif playerData.actype==AIRBOSS.AircraftCarrier.T45C then
-- T-45 also gets slowed down much faster the the F/A-18C.
dcorr=56
end
-- Get wire.
@@ -10611,7 +10697,7 @@ function AIRBOSS:_GetZoneInitial(case)
-- Polygon zone.
--local zone=ZONE_POLYGON_BASE:New("Zone CASE I/II Initial", vec2)
self.zoneInitial:UpdateFromVec2(vec2)
--return zone
@@ -10640,13 +10726,13 @@ function AIRBOSS:_GetZoneLineup()
-- Vec2 array.
local vec2={c1:GetVec2(), c2:GetVec2(), c3:GetVec2(), c4:GetVec2(), c5:GetVec2()}
self.zoneLineup:UpdateFromVec2(vec2)
-- Polygon zone.
--local zone=ZONE_POLYGON_BASE:New("Zone Lineup", vec2)
--return zone
return self.zoneLineup
end
@@ -10681,13 +10767,13 @@ function AIRBOSS:_GetZoneGroove(l, w, b)
-- Vec2 array.
local vec2={c1:GetVec2(), c2:GetVec2(), c3:GetVec2(), c4:GetVec2(), c5:GetVec2(), c6:GetVec2()}
self.zoneGroove:UpdateFromVec2(vec2)
-- Polygon zone.
--local zone=ZONE_POLYGON_BASE:New("Zone Groove", vec2)
--return zone
return self.zoneGroove
end
@@ -10713,7 +10799,7 @@ function AIRBOSS:_GetZoneBullseye(case)
-- Create zone.
local zone=ZONE_RADIUS:New("Zone Bullseye", vec2, radius)
return zone
--self.zoneBullseye=self.zoneBullseye or ZONE_RADIUS:New("Zone Bullseye", vec2, radius)
end
@@ -10942,9 +11028,9 @@ function AIRBOSS:_GetZoneCarrierBox()
-- Create polygon zone.
--local zone=ZONE_POLYGON_BASE:New("Carrier Box Zone", vec2)
--return zone
self.zoneCarrierbox:UpdateFromVec2(vec2)
return self.zoneCarrierbox
end
@@ -10979,9 +11065,9 @@ function AIRBOSS:_GetZoneRunwayBox()
-- Create polygon zone.
--local zone=ZONE_POLYGON_BASE:New("Landing Runway Zone", vec2)
--return zone
self.zoneRunwaybox:UpdateFromVec2(vec2)
return self.zoneRunwaybox
end
@@ -11112,7 +11198,7 @@ function AIRBOSS:_GetZoneHolding(case, stack)
-- Square zone length=7NM width=6 NM behind the carrier starting at angels+15 NM behind the carrier.
-- So stay 0-5 NM (+1 NM error margin) port of carrier.
self.zoneHolding=self.zoneHolding or ZONE_POLYGON_BASE:New("CASE II/III Holding Zone")
self.zoneHolding:UpdateFromVec2(p)
end
@@ -11158,12 +11244,12 @@ function AIRBOSS:_GetZoneCommence(case, stack)
-- Create holding zone.
self.zoneCommence=self.zoneCommence or ZONE_RADIUS:New("CASE I Commence Zone")
self.zoneCommence:UpdateFromVec2(Three:GetVec2(), R)
else
-- Case II/III
stack=stack or 1
-- Start point at 21 NM for stack=1.
@@ -11191,7 +11277,7 @@ function AIRBOSS:_GetZoneCommence(case, stack)
-- Zone polygon.
self.zoneCommence=self.zoneCommence or ZONE_POLYGON_BASE:New("CASE II/III Commence Zone")
self.zoneCommence:UpdateFromVec2(p)
end
@@ -11439,7 +11525,7 @@ end
-- @param #AIRBOSS self
-- @return Core.Point#COORDINATE Optimal landing coordinate.
function AIRBOSS:_GetOptLandingCoordinate()
-- Start with stern coordiante.
self.landingcoord:UpdateFromCoordinate(self:_GetSternCoord())
@@ -11558,7 +11644,7 @@ end
--- Get wind speed on carrier deck parallel and perpendicular to runway.
-- @param #AIRBOSS self
-- @param #number alt Altitude in meters. Default 50 m.
-- @param #number alt Altitude in meters. Default 15 m. (change made from 50m from Discord discussion from Sickdog)
-- @return #number Wind component parallel to runway im m/s.
-- @return #number Wind component perpendicular to runway in m/s.
-- @return #number Total wind strength in m/s.
@@ -11581,7 +11667,7 @@ function AIRBOSS:GetWindOnDeck(alt)
zc=UTILS.Rotate2D(zc, -self.carrierparam.rwyangle)
-- Wind (from) vector
local vw=cv:GetWindWithTurbulenceVec3(alt or 50)
local vw=cv:GetWindWithTurbulenceVec3(alt or 15)
-- Total wind velocity vector.
-- Carrier velocity has to be negative. If carrier drives in the direction the wind is blowing from, we have less wind in total.
@@ -11995,15 +12081,15 @@ function AIRBOSS:_EvalGrooveTime(playerData)
local grade=""
if t<9 then
grade="--"
elseif t<12 then
grade="(OK)"
elseif t<22 then
grade="OK"
grade="_NESA_"
elseif t<15 then
grade="NESA"
elseif t<19 then
grade="OK Groove"
elseif t<=24 then
grade="(OK)"
grade="(LIG)"
else
grade="--"
grade="LIG"
end
-- The unicorn!
@@ -12042,9 +12128,9 @@ function AIRBOSS:_LSOgrade(playerData)
local nS=count(G, '%(')
local nN=N-nS-nL
-- Groove time 16-18 sec for a unicorn.
-- Groove time 15-18.99 sec for a unicorn.
local Tgroove=playerData.Tgroove
local TgrooveUnicorn=Tgroove and (Tgroove>=16.0 and Tgroove<=18.0) or false
local TgrooveUnicorn=Tgroove and (Tgroove>=15.0 and Tgroove<=18.99) or false
local grade
local points
@@ -12177,7 +12263,35 @@ function AIRBOSS:_Flightdata2Text(playerData, groovestep)
-- Aircraft specific AoA values.
local acaoa=self:_GetAircraftAoA(playerData)
--Angled Approach.
local P=nil
if step==AIRBOSS.PatternStep.GROOVE_XX and ROL<=4.0 and playerData.case<3 then
if LUE>self.lue.RIGHT then
P=underline("AA")
elseif
LUE>self.lue.RightMed then
P="AA "
elseif
LUE>self.lue.Right then
P=little("AA")
end
end
--Overshoot Start.
local O=nil
if step==AIRBOSS.PatternStep.GROOVE_XX then
if LUE<self.lue.LEFT then
O=underline("OS")
elseif
LUE<self.lue.Left then
O="OS"
elseif
LUE<self.lue._min then
O=little("OS")
end
end
-- Speed via AoA. Depends on aircraft type.
local S=nil
if AOA>acaoa.SLOW then
@@ -12210,7 +12324,7 @@ function AIRBOSS:_Flightdata2Text(playerData, groovestep)
A=little("LO")
end
-- Line up. Good [-0.5, 0.5]
-- Line up. XX Step replaced by Overshoot start (OS). Good [-0.5, 0.5]
local D=nil
if LUE>self.lue.RIGHT then
D=underline("LUL")
@@ -12218,12 +12332,22 @@ function AIRBOSS:_Flightdata2Text(playerData, groovestep)
D="LUL"
elseif LUE>self.lue._max then
D=little("LUL")
elseif LUE<self.lue.LEFT then
D=underline("LUR")
elseif LUE<self.lue.Left then
D="LUR"
elseif LUE<self.lue._min then
D=little("LUR")
elseif playerData.case<3 then
if LUE<self.lue.LEFT and step~=AIRBOSS.PatternStep.GROOVE_XX then
D=underline("LUR")
elseif LUE<self.lue.Left and step~=AIRBOSS.PatternStep.GROOVE_XX then
D="LUR"
elseif LUE<self.lue._min and step~=AIRBOSS.PatternStep.GROOVE_XX then
D=little("LUR")
end
elseif playerData.case==3 then
if LUE<self.lue.LEFT then
D=underline("LUR")
elseif LUE<self.lue.Left then
D="LUR"
elseif LUE<self.lue._min then
D=little("LUR")
end
end
-- Compile.
@@ -12233,6 +12357,11 @@ function AIRBOSS:_Flightdata2Text(playerData, groovestep)
if fdata.FlyThrough then
G=G..fdata.FlyThrough
end
-- Angled Approach - doesn't affect score, advisory only.
if P then
G=G..P
n=n
end
-- Speed.
if S then
G=G..S
@@ -12248,7 +12377,17 @@ function AIRBOSS:_Flightdata2Text(playerData, groovestep)
G=G..D
n=n+1
end
--Drift in Lineup
if fdata.Drift then
G=G..fdata.Drift
n=n -- Drift doesn't affect score, advisory only.
end
-- Overshoot.
if O then
G=G..O
n=n+1
end
-- Add current step.
local step=self:_GS(step)
step=step:gsub("XX","X")
@@ -14074,6 +14213,8 @@ function AIRBOSS:_GetACNickname(actype)
local nickname="unknown"
if actype==AIRBOSS.AircraftCarrier.A4EC then
nickname="Skyhawk"
elseif actype==AIRBOSS.AircraftCarrier.T45C then
nickname="Goshawk"
elseif actype==AIRBOSS.AircraftCarrier.AV8B then
nickname="Harrier"
elseif actype==AIRBOSS.AircraftCarrier.E2D then
@@ -15581,8 +15722,12 @@ function AIRBOSS:_MarshalCallRecoveryStart(case)
-- Debug output.
local text=string.format("Starting aircraft recovery Case %d ops.", case)
if case>1 then
text=text..string.format(" Marshal radial %03d°.", radial)
if case==1 then
text=text..string.format(" BRC %03d°.", self:GetBRC())
elseif case==2 then
text=text..string.format(" Marshal radial %03d°. BRC %03d°.", radial, self:GetBRC())
elseif case==3 then
text=text..string.format(" Marshal radial %03d°. Final heading %03d°.", radial, self:GetFinalBearing(false))
end
self:T(self.lid..text)
@@ -17794,7 +17939,7 @@ function AIRBOSS:onbeforeSave(From, Event, To, path, filename)
-- Check default path.
if path==nil and not lfs then
self:E(self.lid.."WARNING: lfs not desanitized. Results will be saved in DCS installation root directory rather than your \"Saved Games\DCS\" folder.")
self:E(self.lid.."WARNING: lfs not desanitized. Results will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
end
return true
@@ -17901,7 +18046,7 @@ function AIRBOSS:onbeforeLoad(From, Event, To, path, filename)
-- Check default path.
if path==nil and not lfs then
self:E(self.lid.."WARNING: lfs not desanitized. Results will be saved in DCS installation root directory rather than your \"Saved Games\DCS\" folder.")
self:E(self.lid.."WARNING: lfs not desanitized. Results will be saved in DCS installation root directory rather than your \"Saved Games\\DCS\" folder.")
end
-- Set path or default.

View File

@@ -38,7 +38,7 @@
-- @field Core.Set#SET_ZONE retreatZones Set of retreat zones.
-- @extends Ops.OpsGroup#OPSGROUP
--- *Your soul may belong to Jesus, but your ass belongs to the marines.* -- Eugene B. Sledge
--- *Your soul may belong to Jesus, but your ass belongs to the marines.* -- Eugene B Sledge
--
-- ===
--
@@ -55,16 +55,6 @@ ARMYGROUP = {
engage = {},
}
--- Army group element.
-- @type ARMYGROUP.Element
-- @field #string name Name of the element, i.e. the unit.
-- @field Wrapper.Unit#UNIT unit The UNIT object.
-- @field #string status The element status.
-- @field #string typename Type name.
-- @field #number length Length of element in meters.
-- @field #number width Width of element in meters.
-- @field #number height Height of element in meters.
--- Target
-- @type ARMYGROUP.Target
-- @field Ops.Target#TARGET Target The target.
@@ -72,16 +62,16 @@ ARMYGROUP = {
--- Army Group version.
-- @field #string version
ARMYGROUP.version="0.4.0"
ARMYGROUP.version="0.7.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Retreat.
-- TODO: Suppression of fire.
-- TODO: Check if group is mobile.
-- TODO: F10 menu.
-- DONE: Retreat.
-- DONE: Rearm. Specify a point where to go and wait until ammo is full.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -90,17 +80,25 @@ ARMYGROUP.version="0.4.0"
--- Create a new ARMYGROUP class object.
-- @param #ARMYGROUP self
-- @param Wrapper.Group#GROUP Group The group object. Can also be given by its group name as `#string`.
-- @param Wrapper.Group#GROUP group The GROUP object. Can also be given by its group name as `#string`.
-- @return #ARMYGROUP self
function ARMYGROUP:New(Group)
function ARMYGROUP:New(group)
-- First check if we already have an OPS group for this group.
local og=_DATABASE:GetOpsGroup(group)
if og then
og:I(og.lid..string.format("WARNING: OPS group already exists in data base!"))
return og
end
-- Inherit everything from FSM class.
local self=BASE:Inherit(self, OPSGROUP:New(Group)) -- #ARMYGROUP
local self=BASE:Inherit(self, OPSGROUP:New(group)) -- #ARMYGROUP
-- Set some string id for output to DCS.log file.
self.lid=string.format("ARMYGROUP %s | ", self.groupname)
-- Defaults
self.isArmygroup=true
self:SetDefaultROE()
self:SetDefaultAlarmstate()
self:SetDetection()
@@ -115,13 +113,13 @@ function ARMYGROUP:New(Group)
self:AddTransition("*", "Detour", "OnDetour") -- Make a detour to a coordinate and resume route afterwards.
self:AddTransition("OnDetour", "DetourReached", "Cruising") -- Group reached the detour coordinate.
self:AddTransition("*", "Retreat", "Retreating") --
self:AddTransition("Retreating", "Retreated", "Retreated") --
self:AddTransition("*", "Retreat", "Retreating") -- Order a retreat.
self:AddTransition("Retreating", "Retreated", "Retreated") -- Group retreated.
self:AddTransition("Cruising", "EngageTarget", "Engaging") -- Engage a target
self:AddTransition("Holding", "EngageTarget", "Engaging") -- Engage a target
self:AddTransition("OnDetour", "EngageTarget", "Engaging") -- Engage a target
self:AddTransition("Engaging", "Disengage", "Cruising") -- Engage a target
self:AddTransition("Cruising", "EngageTarget", "Engaging") -- Engage a target from Cruising state
self:AddTransition("Holding", "EngageTarget", "Engaging") -- Engage a target from Holding state
self:AddTransition("OnDetour", "EngageTarget", "Engaging") -- Engage a target from OnDetour state
self:AddTransition("Engaging", "Disengage", "Cruising") -- Disengage and back to cruising.
self:AddTransition("*", "Rearm", "Rearm") -- Group is send to a coordinate and waits until ammo is refilled.
self:AddTransition("Rearm", "Rearming", "Rearming") -- Group has arrived at the rearming coodinate and is waiting to be fully rearmed.
@@ -143,7 +141,7 @@ function ARMYGROUP:New(Group)
-- Init waypoints.
self:InitWaypoints()
self:_InitWaypoints()
-- Initialize the group.
self:_InitGroup()
@@ -151,8 +149,7 @@ function ARMYGROUP:New(Group)
-- Handle events:
self:HandleEvent(EVENTS.Birth, self.OnEventBirth)
self:HandleEvent(EVENTS.Dead, self.OnEventDead)
self:HandleEvent(EVENTS.RemoveUnit, self.OnEventRemoveUnit)
self:HandleEvent(EVENTS.RemoveUnit, self.OnEventRemoveUnit)
--self:HandleEvent(EVENTS.Hit, self.OnEventHit)
-- Start the status monitoring.
@@ -163,6 +160,9 @@ function ARMYGROUP:New(Group)
-- Start check zone timer.
self.timerCheckZone=TIMER:New(self._CheckInZones, self):Start(2, 30)
-- Add OPSGROUP to _DATABASE.
_DATABASE:AddOpsGroup(self)
return self
end
@@ -263,6 +263,26 @@ function ARMYGROUP:AddTaskAttackGroup(TargetGroup, WeaponExpend, WeaponType, Clo
return task
end
--- Add a *scheduled* task to transport group(s).
-- @param #ARMYGROUP self
-- @param Core.Set#SET_GROUP GroupSet Set of cargo groups. Can also be a singe @{Wrapper.Group#GROUP} object.
-- @param Core.Zone#ZONE PickupZone Zone where the cargo is picked up.
-- @param Core.Zone#ZONE DeployZone Zone where the cargo is delivered to.
-- @param #string Clock Time when to start the attack.
-- @param #number Prio Priority of the task.
-- @return Ops.OpsGroup#OPSGROUP.Task The task table.
function ARMYGROUP:AddTaskCargoGroup(GroupSet, PickupZone, DeployZone, Clock, Prio)
local DCStask={}
DCStask.id="CargoTransport"
DCStask.params={}
DCStask.params.cargoqueu=1
local task=self:AddTask(DCStask, Clock, nil, Prio)
return task
end
--- Define a set of possible retreat zones.
-- @param #ARMYGROUP self
-- @param Core.Set#SET_ZONE RetreatZoneSet The retreat zone set. Default is an empty set.
@@ -342,7 +362,9 @@ function ARMYGROUP:onafterStatus(From, Event, To)
-- FSM state.
local fsmstate=self:GetState()
if self:IsAlive() then
local alive=self:IsAlive()
if alive then
---
-- Detection
@@ -370,6 +392,21 @@ function ARMYGROUP:onafterStatus(From, Event, To)
self:_UpdateEngageTarget()
end
-- Check if group is waiting.
if self:IsWaiting() then
if self.Twaiting and self.dTwait then
if timer.getAbsTime()>self.Twaiting+self.dTwait then
self.Twaiting=nil
self.dTwait=nil
self:Cruise()
end
end
end
end
if alive~=nil then
if self.verbose>=1 then
-- Get number of tasks and missions.
@@ -378,14 +415,20 @@ function ARMYGROUP:onafterStatus(From, Event, To)
local roe=self:GetROE()
local alarm=self:GetAlarmstate()
local speed=UTILS.MpsToKnots(self.velocity)
local speed=UTILS.MpsToKnots(self.velocity or 0)
local speedEx=UTILS.MpsToKnots(self:GetExpectedSpeed())
local formation=self.option.Formation or "unknown"
local ammo=self:GetAmmoTot()
local cargo=0
for _,_element in pairs(self.elements) do
local element=_element --Ops.OpsGroup#OPSGROUP.Element
cargo=cargo+element.weightCargo
end
-- Info text.
local text=string.format("%s [ROE-AS=%d-%d T/M=%d/%d]: Wp=%d/%d-->%d (final %s), Life=%.1f, Speed=%.1f (%d), Heading=%03d, Ammo=%d",
fsmstate, roe, alarm, nTaskTot, nMissions, self.currentwp, #self.waypoints, self:GetWaypointIndexNext(), tostring(self.passedfinalwp), self.life or 0, speed, speedEx, self.heading, ammo.Total)
local text=string.format("%s [ROE-AS=%d-%d T/M=%d/%d]: Wp=%d/%d-->%d (final %s), Life=%.1f, Speed=%.1f (%d), Heading=%03d, Ammo=%d, Cargo=%.1f",
fsmstate, roe, alarm, nTaskTot, nMissions, self.currentwp, #self.waypoints, self:GetWaypointIndexNext(), tostring(self.passedfinalwp), self.life or 0, speed, speedEx, self.heading or 0, ammo.Total, cargo)
self:I(self.lid..text)
end
@@ -393,11 +436,50 @@ function ARMYGROUP:onafterStatus(From, Event, To)
else
-- Info text.
local text=string.format("State %s: Alive=%s", fsmstate, tostring(self:IsAlive()))
self:T2(self.lid..text)
if self.verbose>=1 then
local text=string.format("State %s: Alive=%s", fsmstate, tostring(self:IsAlive()))
self:I(self.lid..text)
end
end
---
-- Elements
---
if self.verbose>=2 then
local text="Elements:"
for i,_element in pairs(self.elements) do
local element=_element --Ops.OpsGroup#OPSGROUP.Element
local name=element.name
local status=element.status
local unit=element.unit
--local life=unit:GetLifeRelative() or 0
local life,life0=self:GetLifePoints(element)
local life0=element.life0
-- Get ammo.
local ammo=self:GetAmmoElement(element)
-- Output text for element.
text=text..string.format("\n[%d] %s: status=%s, life=%.1f/%.1f, guns=%d, rockets=%d, bombs=%d, missiles=%d, cargo=%d/%d kg",
i, name, status, life, life0, ammo.Guns, ammo.Rockets, ammo.Bombs, ammo.Missiles, element.weightCargo, element.weightMaxCargo)
end
if #self.elements==0 then
text=text.." none!"
end
self:I(self.lid..text)
end
---
-- Cargo
---
self:_CheckCargoTransport()
---
-- Tasks & Missions
@@ -410,6 +492,26 @@ function ARMYGROUP:onafterStatus(From, Event, To)
self:__Status(-30)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- DCS Events ==> See OPSGROUP
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Event function handling when a unit is hit.
-- @param #ARMYGROUP self
-- @param Core.Event#EVENTDATA EventData Event data.
function ARMYGROUP:OnEventHit(EventData)
-- Check that this is the right group.
if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then
local unit=EventData.IniUnit
local group=EventData.IniGroup
local unitname=EventData.IniUnitName
-- TODO: suppression
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Events
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -419,7 +521,7 @@ end
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #ARMYGROUP.Element Element The group element.
-- @param Ops.OpsGroup#OPSGROUP.Element Element The group element.
function ARMYGROUP:onafterElementSpawned(From, Event, To, Element)
self:T(self.lid..string.format("Element spawned %s", Element.name))
@@ -436,8 +538,30 @@ end
function ARMYGROUP:onafterSpawned(From, Event, To)
self:T(self.lid..string.format("Group spawned!"))
-- Debug info.
if self.verbose>=1 then
local text=string.format("Initialized Army Group %s:\n", self.groupname)
text=text..string.format("Unit type = %s\n", self.actype)
text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax))
text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise))
text=text..string.format("Weight = %.1f kg\n", self:GetWeightTotal())
text=text..string.format("Cargo bay = %.1f kg\n", self:GetFreeCargobay())
text=text..string.format("Elements = %d\n", #self.elements)
text=text..string.format("Waypoints = %d\n", #self.waypoints)
text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On))
text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles)
text=text..string.format("FSM state = %s\n", self:GetState())
text=text..string.format("Is alive = %s\n", tostring(self:IsAlive()))
text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated()))
self:I(self.lid..text)
end
-- Update position.
self:_UpdatePosition()
-- Not dead or destroyed yet.
self.isDead=false
self.isDestroyed=false
if self.isAI then
@@ -461,16 +585,34 @@ function ARMYGROUP:onafterSpawned(From, Event, To)
if not self.option.Formation then
self.option.Formation=self.optionDefault.Formation
end
-- Update route.
if #self.waypoints>1 then
self:Cruise(nil, self.option.Formation or self.optionDefault.Formation)
else
self:FullStop()
end
-- Update status.
self:__Status(-0.1)
end
-- Update route.
if #self.waypoints>1 then
self:Cruise(nil, self.option.Formation or self.optionDefault.Formation)
else
self:FullStop()
end
--- On before "UpdateRoute" event.
-- @param #ARMYGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #number n Waypoint number. Default is next waypoint.
-- @param #number Speed Speed in knots. Default cruise speed.
-- @param #number Formation Formation of the group.
function ARMYGROUP:onbeforeUpdateRoute(From, Event, To, n, Speed, Formation)
if self:IsWaiting() then
return false
end
return true
end
--- On after "UpdateRoute" event.
@@ -830,7 +972,7 @@ function ARMYGROUP:onafterEngageTarget(From, Event, To, Target)
end
--- On after "EngageTarget" event.
--- Update engage target.
-- @param #ARMYGROUP self
function ARMYGROUP:_UpdateEngageTarget()
@@ -926,131 +1068,14 @@ end
-- @param #number Formation Formation.
function ARMYGROUP:onafterCruise(From, Event, To, Speed, Formation)
-- Not waiting anymore.
self.Twaiting=nil
self.dTwait=nil
self:__UpdateRoute(-1, nil, Speed, Formation)
end
--- On after "Stop" event.
-- @param #ARMYGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function ARMYGROUP:onafterStop(From, Event, To)
-- Handle events:
self:UnHandleEvent(EVENTS.Birth)
self:UnHandleEvent(EVENTS.Dead)
self:UnHandleEvent(EVENTS.RemoveUnit)
-- Call OPSGROUP function.
self:GetParent(self).onafterStop(self, From, Event, To)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Events DCS
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Event function handling the birth of a unit.
-- @param #ARMYGROUP self
-- @param Core.Event#EVENTDATA EventData Event data.
function ARMYGROUP:OnEventBirth(EventData)
-- Check that this is the right group.
if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then
local unit=EventData.IniUnit
local group=EventData.IniGroup
local unitname=EventData.IniUnitName
if self.respawning then
local function reset()
self.respawning=nil
end
-- Reset switch in 1 sec. This should allow all birth events of n>1 groups to have passed.
-- TODO: Can I do this more rigorously?
self:ScheduleOnce(1, reset)
else
-- Get element.
local element=self:GetElementByName(unitname)
-- Set element to spawned state.
self:T3(self.lid..string.format("EVENT: Element %s born ==> spawned", element.name))
self:ElementSpawned(element)
end
end
end
--- Event function handling the crash of a unit.
-- @param #ARMYGROUP self
-- @param Core.Event#EVENTDATA EventData Event data.
function ARMYGROUP:OnEventDead(EventData)
-- Check that this is the right group.
if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then
self:T(self.lid..string.format("EVENT: Unit %s dead!", EventData.IniUnitName))
local unit=EventData.IniUnit
local group=EventData.IniGroup
local unitname=EventData.IniUnitName
-- Get element.
local element=self:GetElementByName(unitname)
if element then
self:T(self.lid..string.format("EVENT: Element %s dead ==> destroyed", element.name))
self:ElementDestroyed(element)
end
end
end
--- Event function handling when a unit is removed from the game.
-- @param #ARMYGROUP self
-- @param Core.Event#EVENTDATA EventData Event data.
function ARMYGROUP:OnEventRemoveUnit(EventData)
-- Check that this is the right group.
if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then
local unit=EventData.IniUnit
local group=EventData.IniGroup
local unitname=EventData.IniUnitName
-- Get element.
local element=self:GetElementByName(unitname)
if element then
self:T(self.lid..string.format("EVENT: Element %s removed ==> dead", element.name))
self:ElementDead(element)
end
end
end
--- Event function handling when a unit is hit.
-- @param #ARMYGROUP self
-- @param Core.Event#EVENTDATA EventData Event data.
function ARMYGROUP:OnEventHit(EventData)
-- Check that this is the right group.
if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then
local unit=EventData.IniUnit
local group=EventData.IniGroup
local unitname=EventData.IniUnitName
-- TODO: suppression
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Routing
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -1108,17 +1133,18 @@ end
--- Initialize group parameters. Also initializes waypoints if self.waypoints is nil.
-- @param #ARMYGROUP self
-- @param #table Template Template used to init the group. Default is `self.template`.
-- @return #ARMYGROUP self
function ARMYGROUP:_InitGroup()
function ARMYGROUP:_InitGroup(Template)
-- First check if group was already initialized.
if self.groupinitialized then
self:E(self.lid.."WARNING: Group was already initialized!")
self:T(self.lid.."WARNING: Group was already initialized! Will NOT do it again!")
return
end
-- Get template of group.
self.template=self.group:GetTemplate()
local template=Template or self:_GetTemplate()
-- Define category.
self.isAircraft=false
@@ -1129,7 +1155,7 @@ function ARMYGROUP:_InitGroup()
self.isAI=true
-- Is (template) group late activated.
self.isLateActivated=self.template.lateActivation
self.isLateActivated=template.lateActivation
-- Ground groups cannot be uncontrolled.
self.isUncontrolled=false
@@ -1161,64 +1187,16 @@ function ARMYGROUP:_InitGroup()
-- Units of the group.
local units=self.group:GetUnits()
for _,_unit in pairs(units) do
local unit=_unit --Wrapper.Unit#UNIT
-- TODO: this is wrong when grouping is used!
local unittemplate=unit:GetTemplate()
local element={} --#ARMYGROUP.Element
element.name=unit:GetName()
element.unit=unit
element.status=OPSGROUP.ElementStatus.INUTERO
element.typename=unit:GetTypeName()
element.skill=unittemplate.skill or "Unknown"
element.ai=true
element.category=element.unit:GetUnitCategory()
element.categoryname=element.unit:GetCategoryName()
element.size, element.length, element.height, element.width=unit:GetObjectSize()
element.ammo0=self:GetAmmoUnit(unit, false)
element.life0=unit:GetLife0()
element.life=element.life0
-- Debug text.
if self.verbose>=2 then
local text=string.format("Adding element %s: status=%s, skill=%s, life=%.3f category=%s (%d), size: %.1f (L=%.1f H=%.1f W=%.1f)",
element.name, element.status, element.skill, element.life, element.categoryname, element.category, element.size, element.length, element.height, element.width)
self:I(self.lid..text)
end
-- Add elemets.
for _,unit in pairs(units) do
self:_AddElementByName(unit:GetName())
end
-- Get Descriptors.
self.descriptors=units[1]:GetDesc()
-- Add element to table.
table.insert(self.elements, element)
-- Get Descriptors.
self.descriptors=self.descriptors or unit:GetDesc()
-- Set type name.
self.actype=self.actype or unit:GetTypeName()
if unit:IsAlive() then
-- Trigger spawned event.
self:ElementSpawned(element)
end
end
-- Debug info.
if self.verbose>=1 then
local text=string.format("Initialized Army Group %s:\n", self.groupname)
text=text..string.format("Unit type = %s\n", self.actype)
text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax))
text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise))
text=text..string.format("Elements = %d\n", #self.elements)
text=text..string.format("Waypoints = %d\n", #self.waypoints)
text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On))
text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles)
text=text..string.format("FSM state = %s\n", self:GetState())
text=text..string.format("Is alive = %s\n", tostring(self:IsAlive()))
text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated()))
self:I(self.lid..text)
end
-- Set type name.
self.actype=units[1]:GetTypeName()
-- Init done.
self.groupinitialized=true

View File

@@ -7,7 +7,7 @@
-- * Set mission start/stop times
-- * Set mission priority and urgency (can cancel running missions)
-- * Specific mission options for ROE, ROT, formation, etc.
-- * Compatible with FLIGHTGROUP, NAVYGROUP, ARMYGROUP, AIRWING, WINGCOMMANDER and CHIEF classes
-- * Compatible with OPS classes like FLIGHTGROUP, NAVYGROUP, ARMYGROUP, AIRWING, etc.
-- * FSM events when a mission is done, successful or failed
--
-- ===
@@ -39,9 +39,10 @@
-- @field #number prio Mission priority.
-- @field #boolean urgent Mission is urgent. Running missions with lower prio might be cancelled.
-- @field #number importance Importance.
-- @field #number Tstart Mission start time in seconds.
-- @field #number Tstop Mission stop time in seconds.
-- @field #number Tstart Mission start time in abs. seconds.
-- @field #number Tstop Mission stop time in abs. seconds.
-- @field #number duration Mission duration in seconds.
-- @field #number Tpush Mission push/execute time in abs. seconds.
-- @field Wrapper.Marker#MARKER marker F10 map marker.
-- @field #boolean markerOn If true, display marker on F10 map with the AUFTRAG status.
-- @field #number markerCoaliton Coalition to which the marker is dispayed.
@@ -52,8 +53,9 @@
-- @field #number dTevaluate Time interval in seconds before the mission result is evaluated after mission is over.
-- @field #number Tover Mission abs. time stamp, when mission was over.
-- @field #table conditionStart Condition(s) that have to be true, before the mission will be started.
-- @field #table conditionSuccess If all stop conditions are true, the mission is cancelled.
-- @field #table conditionFailure If all stop conditions are true, the mission is cancelled.
-- @field #table conditionSuccess If all conditions are true, the mission is cancelled.
-- @field #table conditionFailure If all conditions are true, the mission is cancelled.
-- @field #table conditionPush If all conditions are true, the mission is executed. Before, the group(s) wait at the mission execution waypoint.
--
-- @field #number orbitSpeed Orbit speed in m/s.
-- @field #number orbitAltitude Orbit altitude in meters.
@@ -101,9 +103,11 @@
--
-- @field #string missionTask Mission task. See `ENUMS.MissionTask`.
-- @field #number missionAltitude Mission altitude in meters.
-- @field #number missionSpeed Mission speed in km/h.
-- @field #number missionFraction Mission coordiante fraction. Default is 0.5.
-- @field #number missionRange Mission range in meters. Used in AIRWING class.
-- @field Core.Point#COORDINATE missionWaypointCoord Mission waypoint coordinate.
-- @field Core.Point#COORDINATE missionEgressCoord Mission egress waypoint coordinate.
--
-- @field #table enrouteTasks Mission enroute tasks.
--
@@ -285,6 +289,7 @@ AUFTRAG = {
conditionStart = {},
conditionSuccess = {},
conditionFailure = {},
conditionPush = {},
}
--- Global mission counter.
@@ -315,6 +320,7 @@ _AUFTRAGSNR=0
-- @field #string TANKER Tanker mission.
-- @field #string TROOPTRANSPORT Troop transport mission.
-- @field #string ARTY Fire at point.
-- @field #string PATROLZONE Patrol a zone.
AUFTRAG.Type={
ANTISHIP="Anti Ship",
AWACS="AWACS",
@@ -338,6 +344,7 @@ AUFTRAG.Type={
TANKER="Tanker",
TROOPTRANSPORT="Troop Transport",
ARTY="Fire At Point",
PATROLZONE="Patrol Zone",
}
--- Mission status.
@@ -430,8 +437,9 @@ AUFTRAG.TargetType={
--- Group specific data. Each ops group subscribed to this mission has different data for this.
-- @type AUFTRAG.GroupData
-- @field Ops.OpsGroup#OPSGROUP opsgroup The OPS group.
-- @field Core.Point#COORDINATE waypointcoordinate Waypoint coordinate.
-- @field Core.Point#COORDINATE waypointcoordinate Ingress waypoint coordinate.
-- @field #number waypointindex Waypoint index.
-- @field Core.Point#COORDINATE wpegresscoordinate Egress waypoint coordinate.
-- @field Ops.OpsGroup#OPSGROUP.Task waypointtask Waypoint task.
-- @field #string status Group mission status.
-- @field Ops.AirWing#AIRWING.SquadronAsset asset The squadron asset.
@@ -439,7 +447,7 @@ AUFTRAG.TargetType={
--- AUFTRAG class version.
-- @field #string version
AUFTRAG.version="0.5.0"
AUFTRAG.version="0.7.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -934,7 +942,7 @@ end
--- Create a STRIKE mission. Flight will attack the closest map object to the specified coordinate.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Target The target coordinate. Can also be given as a GROUP, UNIT or STATIC object.
-- @param Core.Point#COORDINATE Target The target coordinate. Can also be given as a GROUP, UNIT, STATIC or TARGET object.
-- @param #number Altitude Engage altitude in feet. Default 2000 ft.
-- @return #AUFTRAG self
function AUFTRAG:NewSTRIKE(Target, Altitude)
@@ -962,7 +970,7 @@ end
--- Create a BOMBING mission. Flight will drop bombs a specified coordinate.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Target Target coordinate. Can also be specified as a GROUP, UNIT or STATIC object.
-- @param Core.Point#COORDINATE Target Target coordinate. Can also be specified as a GROUP, UNIT, STATIC or TARGET object.
-- @param #number Altitude Engage altitude in feet. Default 25000 ft.
-- @return #AUFTRAG self
function AUFTRAG:NewBOMBING(Target, Altitude)
@@ -1002,10 +1010,6 @@ function AUFTRAG:NewBOMBRUNWAY(Airdrome, Altitude)
if type(Airdrome)=="string" then
Airdrome=AIRBASE:FindByName(Airdrome)
end
if Airdrome:IsInstanceOf("AIRBASE") then
end
local mission=AUFTRAG:New(AUFTRAG.Type.BOMBRUNWAY)
@@ -1106,9 +1110,7 @@ end
function AUFTRAG:NewRESCUEHELO(Carrier)
local mission=AUFTRAG:New(AUFTRAG.Type.RESCUEHELO)
--mission.carrier=Carrier
mission:_TargetFromObject(Carrier)
-- Mission options:
@@ -1191,6 +1193,32 @@ function AUFTRAG:NewARTY(Target, Nshots, Radius)
return mission
end
--- Create a PATROLZONE mission. Group(s) will go to the zone and patrol it randomly.
-- @param #AUFTRAG self
-- @param Core.Zone#ZONE Zone The patrol zone.
-- @param #number Speed Speed in knots.
-- @param #number Altitude Altitude in feet. Only for airborne units. Default 2000 feet ASL.
-- @return #AUFTRAG self
function AUFTRAG:NewPATROLZONE(Zone, Speed, Altitude)
local mission=AUFTRAG:New(AUFTRAG.Type.PATROLZONE)
mission:_TargetFromObject(Zone)
mission.optionROE=ENUMS.ROE.OpenFire
mission.optionROT=ENUMS.ROT.PassiveDefense
mission.optionAlarm=ENUMS.AlarmState.Auto
mission.missionFraction=1.0
mission.missionSpeed=Speed and UTILS.KnotsToKmph(Speed) or nil
mission.missionAltitude=Altitude and UTILS.FeetToMeters(Altitude) or nil
mission.DCStask=mission:GetDCSMissionTask()
return mission
end
--- Create a mission to attack a group. Mission type is automatically chosen from the group category.
-- @param #AUFTRAG self
-- @param Ops.Target#TARGET Target The target.
@@ -1407,6 +1435,24 @@ function AUFTRAG:SetTime(ClockStart, ClockStop)
return self
end
--- Set mission push time. This is the time the mission is executed. If the push time is not passed, the group will wait at the mission execution waypoint.
-- @param #AUFTRAG self
-- @param #string ClockPush Time the mission is executed, e.g. "05:00" for 5 am. Can also be given as a `#number`, where it is interpreted as relative push time in seconds.
-- @return #AUFTRAG self
function AUFTRAG:SetPushTime(ClockPush)
if ClockPush then
if type(ClockPush)=="string" then
self.Tpush=UTILS.ClockToSeconds(ClockPush)
elseif type(ClockPush)=="number" then
self.Tpush=timer.getAbsTime()+ClockPush
end
end
return self
end
--- Set mission priority and (optional) urgency. Urgent missions can cancel other running missions.
-- @param #AUFTRAG self
-- @param #number Prio Priority 1=high, 100=low. Default 50.
@@ -1781,6 +1827,26 @@ function AUFTRAG:AddConditionFailure(ConditionFunction, ...)
return self
end
--- Add push condition.
-- @param #AUFTRAG self
-- @param #function ConditionFunction If this function returns `true`, the mission is executed.
-- @param ... Condition function arguments if any.
-- @return #AUFTRAG self
function AUFTRAG:AddConditionPush(ConditionFunction, ...)
local condition={} --#AUFTRAG.Condition
condition.func=ConditionFunction
condition.arg={}
if arg then
condition.arg=arg
end
table.insert(self.conditionPush, condition)
return self
end
--- Assign airwing squadron(s) to the mission. Only these squads will be considered for the job.
-- @param #AUFTRAG self
@@ -1794,6 +1860,8 @@ function AUFTRAG:AssignSquadrons(Squadrons)
end
self.squadrons=Squadrons
return self
end
--- Add a required payload for this mission. Only these payloads will be used for this mission. If they are not available, the mission cannot start. Only available for use with an AIRWING.
@@ -1806,12 +1874,14 @@ function AUFTRAG:AddRequiredPayload(Payload)
table.insert(self.payloads, Payload)
return self
end
--- Add a Ops group to the mission.
-- @param #AUFTRAG self
-- @param Ops.OpsGroup#OPSGROUP OpsGroup The OPSGROUP object.
-- @return #AUFTRAG self
function AUFTRAG:AddOpsGroup(OpsGroup)
self:T(self.lid..string.format("Adding Ops group %s", OpsGroup.groupname))
@@ -1824,11 +1894,13 @@ function AUFTRAG:AddOpsGroup(OpsGroup)
self.groupdata[OpsGroup.groupname]=groupdata
return self
end
--- Remove an Ops group from the mission.
-- @param #AUFTRAG self
-- @param Ops.OpsGroup#OPSGROUP OpsGroup The OPSGROUP object.
-- @return #AUFTRAG self
function AUFTRAG:DelOpsGroup(OpsGroup)
self:T(self.lid..string.format("Removing OPS group %s", OpsGroup and OpsGroup.groupname or "nil (ERROR)!"))
@@ -1841,6 +1913,7 @@ function AUFTRAG:DelOpsGroup(OpsGroup)
end
return self
end
--- Check if mission is PLANNED.
@@ -1932,12 +2005,12 @@ function AUFTRAG:IsReadyToGo()
local Tnow=timer.getAbsTime()
-- Start time did not pass yet.
if self.Tstart and Tnow<self.Tstart or false then
if self.Tstart and Tnow<self.Tstart then
return false
end
-- Stop time already passed.
if self.Tstop and Tnow>self.Tstop or false then
if self.Tstop and Tnow>self.Tstop then
return false
end
@@ -1963,7 +2036,7 @@ function AUFTRAG:IsReadyToCancel()
local Tnow=timer.getAbsTime()
-- Stop time already passed.
if self.Tstop and Tnow>self.Tstop then
if self.Tstop and Tnow>=self.Tstop then
return true
end
@@ -1987,6 +2060,26 @@ function AUFTRAG:IsReadyToCancel()
return false
end
--- Check if mission is ready to be pushed.
-- * Mission push time already passed.
-- * All push conditions are true.
-- @param #AUFTRAG self
-- @return #boolean If true, mission groups can push.
function AUFTRAG:IsReadyToPush()
local Tnow=timer.getAbsTime()
-- Push time passed?
if self.Tpush and Tnow<self.Tpush then
return false
end
-- Evaluate push condition(s) if any. All need to be true.
local push=self:EvalConditionsAll(self.conditionPush)
return push
end
--- Check if all given condition are true.
-- @param #AUFTRAG self
-- @param #table Conditions Table of conditions.
@@ -2926,7 +3019,7 @@ end
-- @param #AUFTRAG self
-- @return Wrapper.Positionable#POSITIONABLE The target object. Could be many things.
function AUFTRAG:GetObjective()
return self:GetTargetData().Target
return self:GetTargetData():GetObject()
end
--- Get type of target.
@@ -3083,19 +3176,53 @@ function AUFTRAG:GetMissionTypesText(MissionTypes)
return text
end
--- Set the mission waypoint coordinate where the mission is executed.
--- Set the mission waypoint coordinate where the mission is executed. Note that altitude is set via `:SetMissionAltitude`.
-- @param #AUFTRAG self
-- @return Core.Point#COORDINATE Coordinate where the mission is executed.
-- @param Core.Point#COORDINATE Coordinate Coordinate where the mission is executed.
-- @return #AUFTRAG self
function AUFTRAG:SetMissionWaypointCoord(Coordinate)
-- Obviously a zone was passed. We get the coordinate.
if Coordinate:IsInstanceOf("ZONE_BASE") then
Coordinate=Coordinate:GetCoordinate()
end
self.missionWaypointCoord=Coordinate
return self
end
--- Set the mission egress coordinate. This is the coordinate where the assigned group will go once the mission is finished.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Coordinate Egrees coordinate.
-- @param #number Altitude (Optional) Altitude in feet. Default is y component of coordinate.
-- @return #AUFTRAG self
function AUFTRAG:SetMissionEgressCoord(Coordinate, Altitude)
-- Obviously a zone was passed. We get the coordinate.
if Coordinate:IsInstanceOf("ZONE_BASE") then
Coordinate=Coordinate:GetCoordinate()
end
self.missionEgressCoord=Coordinate
if Altitude then
self.missionEgressCoord.y=UTILS.FeetToMeters(Altitude)
end
end
--- Get the mission egress coordinate if this was defined.
-- @param #AUFTRAG self
-- @return Core.Point#COORDINATE Coordinate Coordinate or nil.
function AUFTRAG:GetMissionEgressCoord()
return self.missionEgressCoord
end
--- Get coordinate of target. First unit/group of the set is used.
-- @param #AUFTRAG self
-- @param Wrapper.Group#GROUP group Group.
-- @param #number randomradius Random radius in meters.
-- @return Core.Point#COORDINATE Coordinate where the mission is executed.
function AUFTRAG:GetMissionWaypointCoord(group)
function AUFTRAG:GetMissionWaypointCoord(group, randomradius)
-- Check if a coord has been explicitly set.
if self.missionWaypointCoord then
@@ -3111,7 +3238,9 @@ function AUFTRAG:GetMissionWaypointCoord(group)
local alt=waypointcoord.y
-- Add some randomization.
waypointcoord=ZONE_RADIUS:New("Temp", waypointcoord:GetVec2(), 1000):GetRandomCoordinate():SetAltitude(alt, false)
if randomradius then
waypointcoord=ZONE_RADIUS:New("Temp", waypointcoord:GetVec2(), randomradius):GetRandomCoordinate():SetAltitude(alt, false)
end
-- Set altitude of mission waypoint.
if self.missionAltitude then
@@ -3388,6 +3517,26 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable)
local DCStask=CONTROLLABLE.TaskFireAtPoint(nil, self:GetTargetVec2(), self.artyRadius, self.artyShots, self.engageWeaponType)
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.PATROLZONE then
-------------------------
-- PATROL ZONE Mission --
-------------------------
local DCStask={}
DCStask.id="PatrolZone"
-- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP.
local param={}
param.zone=self:GetObjective()
param.altitude=self.missionAltitude
param.speed=self.missionSpeed
DCStask.params=param
table.insert(DCStasks, DCStask)
else
self:E(self.lid..string.format("ERROR: Unknown mission task!"))

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -65,12 +65,7 @@ NAVYGROUP = {
pathCorridor = 400,
}
--- Navy group element.
-- @type NAVYGROUP.Element
-- @field #string name Name of the element, i.e. the unit.
-- @field #string typename Type name.
--- Navy group element.
--- Turn into wind parameters.
-- @type NAVYGROUP.IntoWind
-- @field #number Tstart Time to start.
-- @field #number Tstop Time to stop.
@@ -87,7 +82,7 @@ NAVYGROUP = {
--- NavyGroup version.
-- @field #string version
NAVYGROUP.version="0.5.0"
NAVYGROUP.version="0.7.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -107,12 +102,19 @@ NAVYGROUP.version="0.5.0"
--- Create a new NAVYGROUP class object.
-- @param #NAVYGROUP self
-- @param #string GroupName Name of the group.
-- @param Wrapper.Group#GROUP group The group object. Can also be given by its group name as `#string`.
-- @return #NAVYGROUP self
function NAVYGROUP:New(GroupName)
function NAVYGROUP:New(group)
-- First check if we already have an OPS group for this group.
local og=_DATABASE:GetOpsGroup(group)
if og then
og:I(og.lid..string.format("WARNING: OPS group already exists in data base!"))
return og
end
-- Inherit everything from FSM class.
local self=BASE:Inherit(self, OPSGROUP:New(GroupName)) -- #NAVYGROUP
local self=BASE:Inherit(self, OPSGROUP:New(group)) -- #NAVYGROUP
-- Set some string id for output to DCS.log file.
self.lid=string.format("NAVYGROUP %s | ", self.groupname)
@@ -123,6 +125,7 @@ function NAVYGROUP:New(GroupName)
self:SetDefaultAlarmstate()
self:SetPatrolAdInfinitum(true)
self:SetPathfinding(false)
self.isNavygroup=true
-- Add FSM transitions.
-- From State --> Event --> To State
@@ -162,7 +165,7 @@ function NAVYGROUP:New(GroupName)
-- Init waypoints.
self:InitWaypoints()
self:_InitWaypoints()
-- Initialize the group.
self:_InitGroup()
@@ -180,6 +183,9 @@ function NAVYGROUP:New(GroupName)
-- Start check zone timer.
self.timerCheckZone=TIMER:New(self._CheckInZones, self):Start(2, 60)
-- Add OPSGROUP to _DATABASE.
_DATABASE:AddOpsGroup(self)
return self
end
@@ -487,7 +493,8 @@ function NAVYGROUP:onafterStatus(From, Event, To)
-- Check if group started or stopped turning.
self:_CheckTurning()
local freepath=UTILS.NMToMeters(10)
local disttoWP=math.min(self:GetDistanceToWaypoint(), UTILS.NMToMeters(10))
local freepath=disttoWP
-- Only check if not currently turning.
if not self:IsTurning() then
@@ -495,7 +502,7 @@ function NAVYGROUP:onafterStatus(From, Event, To)
-- Check free path ahead.
freepath=self:_CheckFreePath(freepath, 100)
if freepath<5000 then
if disttoWP>1 and freepath<disttoWP then
if not self.collisionwarning then
-- Issue a collision warning event.
@@ -515,6 +522,17 @@ function NAVYGROUP:onafterStatus(From, Event, To)
-- Check if group got stuck.
self:_CheckStuck()
-- Check if group is waiting.
if self:IsWaiting() then
if self.Twaiting and self.dTwait then
if timer.getAbsTime()>self.Twaiting+self.dTwait then
self.Twaiting=nil
self.dTwait=nil
self:Cruise()
end
end
end
if self.verbose>=1 then
@@ -571,7 +589,7 @@ function NAVYGROUP:onafterStatus(From, Event, To)
-- Recovery Windows
---
if self.verbose>=2 then
if self.verbose>=2 and #self.Qintowind>0 then
-- Debug output:
local text=string.format(self.lid.."Turn into wind time windows:")
@@ -598,6 +616,11 @@ function NAVYGROUP:onafterStatus(From, Event, To)
end
---
-- Cargo
---
self:_CheckCargoTransport()
---
-- Tasks & Missions
@@ -610,6 +633,12 @@ function NAVYGROUP:onafterStatus(From, Event, To)
self:__Status(-30)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- DCS Events ==> See OPSGROUP
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- See OPSGROUP!
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Events
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -619,7 +648,7 @@ end
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #NAVYGROUP.Element Element The group element.
-- @param Ops.OpsGroup#OPSGROUP.Element Element The group element.
function NAVYGROUP:onafterElementSpawned(From, Event, To, Element)
self:T(self.lid..string.format("Element spawned %s", Element.name))
@@ -636,8 +665,30 @@ end
function NAVYGROUP:onafterSpawned(From, Event, To)
self:T(self.lid..string.format("Group spawned!"))
-- Debug info.
if self.verbose>=1 then
local text=string.format("Initialized Navy Group %s:\n", self.groupname)
text=text..string.format("Unit type = %s\n", self.actype)
text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax))
text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise))
text=text..string.format("Weight = %.1f kg\n", self:GetWeightTotal())
text=text..string.format("Cargo bay = %.1f kg\n", self:GetFreeCargobay())
text=text..string.format("Elements = %d\n", #self.elements)
text=text..string.format("Waypoints = %d\n", #self.waypoints)
text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On))
text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d/T=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles, self.ammo.Torpedos)
text=text..string.format("FSM state = %s\n", self:GetState())
text=text..string.format("Is alive = %s\n", tostring(self:IsAlive()))
text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated()))
self:I(self.lid..text)
end
-- Update position.
self:_UpdatePosition()
-- Not dead or destroyed yet.
self.isDead=false
self.isDestroyed=false
if self.isAI then
@@ -655,20 +706,39 @@ function NAVYGROUP:onafterSpawned(From, Event, To)
-- Set radio.
if self.radioDefault then
self:SwitchRadio()
-- CAREFUL: This makes DCS crash for some ships like speed boats or Higgins boats! (On a respawn for example). Looks like the command SetFrequency is causing this.
--self:SwitchRadio()
else
self:SetDefaultRadio(self.radio.Freq, self.radio.Modu, false)
end
-- Update route.
if #self.waypoints>1 then
self:Cruise()
else
self:FullStop()
end
-- Update status.
self:__Status(-0.1)
end
-- Update route.
if #self.waypoints>1 then
self:Cruise()
else
self:FullStop()
end
--- On before "UpdateRoute" event.
-- @param #NAVYGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #number n Waypoint number. Default is next waypoint.
-- @param #number Speed Speed in knots to the next waypoint.
-- @param #number Depth Depth in meters to the next waypoint.
function NAVYGROUP:onbeforeUpdateRoute(From, Event, To, n, Speed, Depth)
if self:IsWaiting() then
return false
end
return true
end
--- On after "UpdateRoute" event.
@@ -710,6 +780,7 @@ function NAVYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Depth)
wp.alt=-self.depth
else
-- Take default waypoint alt.
wp.alt=wp.alt or 0
end
-- Current set speed in m/s.
@@ -939,6 +1010,10 @@ end
-- @param #number Speed Speed in knots until next waypoint is reached. Default is speed set for waypoint.
function NAVYGROUP:onafterCruise(From, Event, To, Speed)
-- Not waiting anymore.
self.Twaiting=nil
self.dTwait=nil
-- No set depth.
self.depth=nil
@@ -957,7 +1032,7 @@ function NAVYGROUP:onafterDive(From, Event, To, Depth, Speed)
Depth=Depth or 50
self:T(self.lid..string.format("Diving to %d meters", Depth))
self:I(self.lid..string.format("Diving to %d meters", Depth))
self.depth=Depth
@@ -1014,110 +1089,6 @@ function NAVYGROUP:onafterCollisionWarning(From, Event, To, Distance)
self.collisionwarning=true
end
--- On after Start event. Starts the NAVYGROUP FSM and event handlers.
-- @param #NAVYGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function NAVYGROUP:onafterStop(From, Event, To)
-- Handle events:
self:UnHandleEvent(EVENTS.Birth)
self:UnHandleEvent(EVENTS.Dead)
self:UnHandleEvent(EVENTS.RemoveUnit)
-- Call OPSGROUP function.
self:GetParent(self).onafterStop(self, From, Event, To)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Events DCS
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Event function handling the birth of a unit.
-- @param #NAVYGROUP self
-- @param Core.Event#EVENTDATA EventData Event data.
function NAVYGROUP:OnEventBirth(EventData)
-- Check that this is the right group.
if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then
local unit=EventData.IniUnit
local group=EventData.IniGroup
local unitname=EventData.IniUnitName
if self.respawning then
local function reset()
self.respawning=nil
end
-- Reset switch in 1 sec. This should allow all birth events of n>1 groups to have passed.
-- TODO: Can I do this more rigorously?
self:ScheduleOnce(1, reset)
else
-- Get element.
local element=self:GetElementByName(unitname)
-- Set element to spawned state.
self:T3(self.lid..string.format("EVENT: Element %s born ==> spawned", element.name))
self:ElementSpawned(element)
end
end
end
--- Flightgroup event function handling the crash of a unit.
-- @param #NAVYGROUP self
-- @param Core.Event#EVENTDATA EventData Event data.
function NAVYGROUP:OnEventDead(EventData)
-- Check that this is the right group.
if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then
self:T(self.lid..string.format("EVENT: Unit %s dead!", EventData.IniUnitName))
local unit=EventData.IniUnit
local group=EventData.IniGroup
local unitname=EventData.IniUnitName
-- Get element.
local element=self:GetElementByName(unitname)
if element then
self:T(self.lid..string.format("EVENT: Element %s dead ==> destroyed", element.name))
self:ElementDestroyed(element)
end
end
end
--- Flightgroup event function handling the crash of a unit.
-- @param #NAVYGROUP self
-- @param Core.Event#EVENTDATA EventData Event data.
function NAVYGROUP:OnEventRemoveUnit(EventData)
-- Check that this is the right group.
if EventData and EventData.IniGroup and EventData.IniUnit and EventData.IniGroupName and EventData.IniGroupName==self.groupname then
local unit=EventData.IniUnit
local group=EventData.IniGroup
local unitname=EventData.IniUnitName
-- Get element.
local element=self:GetElementByName(unitname)
if element then
self:T(self.lid..string.format("EVENT: Element %s removed ==> dead", element.name))
self:ElementDead(element)
end
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Routing
@@ -1177,17 +1148,18 @@ end
--- Initialize group parameters. Also initializes waypoints if self.waypoints is nil.
-- @param #NAVYGROUP self
-- @param #table Template Template used to init the group. Default is `self.template`.
-- @return #NAVYGROUP self
function NAVYGROUP:_InitGroup()
function NAVYGROUP:_InitGroup(Template)
-- First check if group was already initialized.
if self.groupinitialized then
self:E(self.lid.."WARNING: Group was already initialized!")
self:T(self.lid.."WARNING: Group was already initialized! Will NOT do it again!")
return
end
-- Get template of group.
self.template=self.group:GetTemplate()
local template=Template or self:_GetTemplate()
-- Define category.
self.isAircraft=false
@@ -1201,7 +1173,7 @@ function NAVYGROUP:_InitGroup()
self.isAI=true
-- Is (template) group late activated.
self.isLateActivated=self.template.lateActivation
self.isLateActivated=template.lateActivation
-- Naval groups cannot be uncontrolled.
self.isUncontrolled=false
@@ -1217,8 +1189,8 @@ function NAVYGROUP:_InitGroup()
-- Radio parameters from template. Default is set on spawn if not modified by the user.
self.radio.On=true -- Radio is always on for ships.
self.radio.Freq=tonumber(self.template.units[1].frequency)/1000000
self.radio.Modu=tonumber(self.template.units[1].modulation)
self.radio.Freq=tonumber(template.units[1].frequency)/1000000
self.radio.Modu=tonumber(template.units[1].modulation)
-- Set default formation. No really applicable for ships.
self.optionDefault.Formation="Off Road"
@@ -1235,64 +1207,17 @@ function NAVYGROUP:_InitGroup()
-- Get all units of the group.
local units=self.group:GetUnits()
for _,_unit in pairs(units) do
local unit=_unit --Wrapper.Unit#UNIT
-- Get unit template.
local unittemplate=unit:GetTemplate()
local element={} --#NAVYGROUP.Element
element.name=unit:GetName()
element.unit=unit
element.status=OPSGROUP.ElementStatus.INUTERO
element.typename=unit:GetTypeName()
element.skill=unittemplate.skill or "Unknown"
element.ai=true
element.category=element.unit:GetUnitCategory()
element.categoryname=element.unit:GetCategoryName()
element.size, element.length, element.height, element.width=unit:GetObjectSize()
element.ammo0=self:GetAmmoUnit(unit, false)
-- Debug text.
if self.verbose>=2 then
local text=string.format("Adding element %s: status=%s, skill=%s, category=%s (%d), size: %.1f (L=%.1f H=%.1f W=%.1f)",
element.name, element.status, element.skill, element.categoryname, element.category, element.size, element.length, element.height, element.width)
self:I(self.lid..text)
end
-- Add element to table.
table.insert(self.elements, element)
-- Get Descriptors.
self.descriptors=self.descriptors or unit:GetDesc()
-- Set type name.
self.actype=self.actype or unit:GetTypeName()
if unit:IsAlive() then
-- Trigger spawned event.
self:ElementSpawned(element)
end
end
-- Debug info.
if self.verbose>=1 then
local text=string.format("Initialized Navy Group %s:\n", self.groupname)
text=text..string.format("Unit type = %s\n", self.actype)
text=text..string.format("Speed max = %.1f Knots\n", UTILS.KmphToKnots(self.speedMax))
text=text..string.format("Speed cruise = %.1f Knots\n", UTILS.KmphToKnots(self.speedCruise))
text=text..string.format("Elements = %d\n", #self.elements)
text=text..string.format("Waypoints = %d\n", #self.waypoints)
text=text..string.format("Radio = %.1f MHz %s %s\n", self.radio.Freq, UTILS.GetModulationName(self.radio.Modu), tostring(self.radio.On))
text=text..string.format("Ammo = %d (G=%d/R=%d/M=%d/T=%d)\n", self.ammo.Total, self.ammo.Guns, self.ammo.Rockets, self.ammo.Missiles, self.ammo.Torpedos)
text=text..string.format("FSM state = %s\n", self:GetState())
text=text..string.format("Is alive = %s\n", tostring(self:IsAlive()))
text=text..string.format("LateActivate = %s\n", tostring(self:IsLateActivated()))
self:I(self.lid..text)
-- Add elemets.
for _,unit in pairs(units) do
self:_AddElementByName(unit:GetName())
end
-- Get Descriptors.
self.descriptors=units[1]:GetDesc()
-- Set type name.
self.actype=units[1]:GetTypeName()
-- Init done.
self.groupinitialized=true
@@ -1345,8 +1270,6 @@ function NAVYGROUP:_CheckFreePath(DistanceMax, dx)
--coordinate=coordinate:Translate(500, heading, true)
local function LoS(dist)
--local checkcoord=coordinate:Translate(dist, heading, true)
--return coordinate:IsLOS(checkcoord, offsetY)
local checkvec3=UTILS.VecTranslate(vec3, dist, heading)
local los=land.isVisible(vec3, checkvec3)
return los
@@ -1374,7 +1297,7 @@ function NAVYGROUP:_CheckFreePath(DistanceMax, dx)
local los=LoS(x)
-- Debug message.
self:T2(self.lid..string.format("N=%d: xmin=%.1f xmax=%.1f x=%.1f d=%.3f los=%s", N, xmin, xmax, x, d, tostring(los)))
self:T(self.lid..string.format("N=%d: xmin=%.1f xmax=%.1f x=%.1f d=%.3f los=%s", N, xmin, xmax, x, d, tostring(los)))
if los and d<=eps then
return x
@@ -1548,6 +1471,7 @@ end
-- @param #NAVYGROUP self
-- @return #boolean If true, a path was found.
function NAVYGROUP:_FindPathToNextWaypoint()
env.info("FF Path finding")
-- Pathfinding A*
local astar=ASTAR:New()
@@ -1558,6 +1482,11 @@ function NAVYGROUP:_FindPathToNextWaypoint()
-- Next waypoint.
local wpnext=self:GetWaypointNext()
-- No next waypoint.
if wpnext==nil then
return
end
-- Next waypoint coordinate.
local nextwp=wpnext.coordinate
@@ -1574,16 +1503,21 @@ function NAVYGROUP:_FindPathToNextWaypoint()
-- Set end coordinate.
astar:SetEndCoordinate(nextwp)
-- Distance to next waypoint.
local dist=position:Get2DDistance(nextwp)
-- Check distance >= 5 meters.
if dist<5 then
return
end
local boxwidth=dist*2
local spacex=dist*0.1
local delta=dist/10
-- Create a grid of nodes. We only want nodes of surface type water.
astar:CreateGrid({land.SurfaceType.WATER}, boxwidth, spacex, delta, delta*2, self.Debug)
astar:CreateGrid({land.SurfaceType.WATER}, boxwidth, spacex, delta, delta, self.verbose>10)
-- Valid neighbour nodes need to have line of sight.
astar:SetValidNeighbourLoS(self.pathCorridor)
@@ -1610,7 +1544,9 @@ function NAVYGROUP:_FindPathToNextWaypoint()
uid=wp.uid
-- Debug: smoke and mark path.
--node.coordinate:MarkToAll(string.format("Path node #%d", i))
if self.verbose>=10 then
node.coordinate:MarkToAll(string.format("Path node #%d", i))
end
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -45,6 +45,8 @@
-- @field #table tacanChannel List of TACAN channels available to the squadron.
-- @field #number radioFreq Radio frequency in MHz the squad uses.
-- @field #number radioModu Radio modulation the squad uses.
-- @field #number takeoffType Take of type.
-- @field #table parkingIDs Parking IDs for this squadron.
-- @extends Core.Fsm#FSM
--- *It is unbelievable what a squadron of twelve aircraft did to tip the balance.* -- Adolf Galland
@@ -87,12 +89,13 @@ SQUADRON = {
--- SQUADRON class version.
-- @field #string version
SQUADRON.version="0.5.0"
SQUADRON.version="0.7.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Parking spots for squadrons?
-- DONE: Engage radius.
-- DONE: Modex.
-- DONE: Call signs.
@@ -134,7 +137,6 @@ function SQUADRON:New(TemplateGroupName, Ngroups, SquadronName)
self.Ngroups=Ngroups or 3
self:SetMissionRange()
self:SetSkill(AI.Skill.GOOD)
--self:SetVerbosity(0)
-- Everyone can ORBIT.
self:AddMissionCapability(AUFTRAG.Type.ORBIT)
@@ -282,6 +284,53 @@ function SQUADRON:SetGrouping(nunits)
return self
end
--- Set valid parking spot IDs. Assets of this squad are only allowed to be spawned at these parking spots. **Note** that the IDs are different from the ones displayed in the mission editor!
-- @param #SQUADRON self
-- @param #table ParkingIDs Table of parking ID numbers or a single `#number`.
-- @return #SQUADRON self
function SQUADRON:SetParkingIDs(ParkingIDs)
if type(ParkingIDs)~="table" then
ParkingIDs={ParkingIDs}
end
self.parkingIDs=ParkingIDs
return self
end
--- Set takeoff type. All assets of this squadron will be spawned with cold (default) or hot engines.
-- Spawning on runways is not supported.
-- @param #SQUADRON self
-- @param #string TakeoffType Take off type: "Cold" (default) or "Hot" with engines on.
-- @return #SQUADRON self
function SQUADRON:SetTakeoffType(TakeoffType)
TakeoffType=TakeoffType or "Cold"
if TakeoffType:lower()=="hot" then
self.takeoffType=COORDINATE.WaypointType.TakeOffParkingHot
elseif TakeoffType:lower()=="cold" then
self.takeoffType=COORDINATE.WaypointType.TakeOffParking
else
self.takeoffType=COORDINATE.WaypointType.TakeOffParking
end
return self
end
--- Set takeoff type cold (default). All assets of this squadron will be spawned with engines off (cold).
-- @param #SQUADRON self
-- @return #SQUADRON self
function SQUADRON:SetTakeoffCold()
self:SetTakeoffType("Cold")
return self
end
--- Set takeoff type hot. All assets of this squadron will be spawned with engines on (hot).
-- @param #SQUADRON self
-- @return #SQUADRON self
function SQUADRON:SetTakeoffHot()
self:SetTakeoffType("Hot")
return self
end
--- Set mission types this squadron is able to perform.
-- @param #SQUADRON self
-- @param #table MissionTypes Table of mission types. Can also be passed as a #string if only one type.
@@ -571,15 +620,22 @@ end
-- @return #number TACAN channel or *nil* if no channel is free.
function SQUADRON:FetchTacan()
-- Get the smallest free channel if there is one.
local freechannel=nil
for channel,free in pairs(self.tacanChannel) do
if free then
self:T(self.lid..string.format("Checking out Tacan channel %d", channel))
self.tacanChannel[channel]=false
return channel
if free then
if freechannel==nil or channel<freechannel then
freechannel=channel
end
end
end
if freechannel then
self:T(self.lid..string.format("Checking out Tacan channel %d", freechannel))
self.tacanChannel[freechannel]=false
end
return nil
return freechannel
end
--- "Return" a used TACAN channel.
@@ -764,7 +820,8 @@ end
-- @param #string To To state.
function SQUADRON:onafterStop(From, Event, To)
self:I(self.lid.."STOPPING Squadron!")
-- Debug info.
self:I(self.lid.."STOPPING Squadron and removing all assets!")
-- Remove all assets.
for i=#self.assets,1,-1 do
@@ -772,6 +829,7 @@ function SQUADRON:onafterStop(From, Event, To)
self:DelAsset(asset)
end
-- Clear call scheduler.
self.CallScheduler:Clear()
end
@@ -830,8 +888,9 @@ end
--- Count assets in airwing (warehous) stock.
-- @param #SQUADRON self
-- @param #table MissionTypes (Optional) Count only assest that can perform certain mission type(s). Default is all types.
-- @return #number Assets not spawned.
function SQUADRON:CountAssetsInStock()
function SQUADRON:CountAssetsInStock(MissionTypes)
local N=0
for _,_asset in pairs(self.assets) do
@@ -839,7 +898,9 @@ function SQUADRON:CountAssetsInStock()
if asset.spawned then
else
N=N+1
if MissionTypes==nil or self:CheckMissionCapability(MissionTypes, self.missiontypes) then
N=N+1
end
end
end
@@ -1017,16 +1078,22 @@ end
--- Check if a mission type is contained in a list of possible capabilities.
-- @param #SQUADRON self
-- @param #string MissionType The requested mission type.
-- @param #table MissionTypes The requested mission type. Can also be passed as a single mission type `#string`.
-- @param #table Capabilities A table with possible capabilities.
-- @return #boolean If true, the requested mission type is part of the possible mission types.
function SQUADRON:CheckMissionCapability(MissionType, Capabilities)
function SQUADRON:CheckMissionCapability(MissionTypes, Capabilities)
if type(MissionTypes)~="table" then
MissionTypes={MissionTypes}
end
for _,cap in pairs(Capabilities) do
local capability=cap --Ops.Auftrag#AUFTRAG.Capability
if capability.MissionType==MissionType then
return true
end
for _,MissionType in pairs(MissionTypes) do
if capability.MissionType==MissionType then
return true
end
end
end
return false

View File

@@ -72,6 +72,7 @@ TARGET = {
-- @field #string SCENERY Target is a SCENERY object.
-- @field #string COORDINATE Target is a COORDINATE.
-- @field #string AIRBASE Target is an AIRBASE.
-- @field #string ZONE Target is a ZONE object.
TARGET.ObjectType={
GROUP="Group",
UNIT="Unit",
@@ -79,6 +80,7 @@ TARGET.ObjectType={
SCENERY="Scenery",
COORDINATE="Coordinate",
AIRBASE="Airbase",
ZONE="Zone",
}
@@ -89,12 +91,14 @@ TARGET.ObjectType={
-- @field #string NAVAL
-- @field #string AIRBASE
-- @field #string COORDINATE
-- @field #string ZONE
TARGET.Category={
AIRCRAFT="Aircraft",
GROUND="Ground",
NAVAL="Naval",
AIRBASE="Airbase",
COORDINATE="Coordinate",
ZONE="Zone",
}
--- Object status.
@@ -124,7 +128,7 @@ _TARGETID=0
--- TARGET class version.
-- @field #string version
TARGET.version="0.3.0"
TARGET.version="0.5.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -227,12 +231,24 @@ end
-- User functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create target data from a given object.
--- Create target data from a given object. Valid objects are:
--
-- * GROUP
-- * UNIT
-- * STATIC
-- * AIRBASE
-- * COORDINATE
-- * ZONE
-- * SET_GROUP
-- * SET_UNIT
-- * SET_STATIC
-- * SET_OPSGROUP
--
-- @param #TARGET self
-- @param Wrapper.Positionable#POSITIONABLE Object The target GROUP, UNIT, STATIC, AIRBASE or COORDINATE.
function TARGET:AddObject(Object)
if Object:IsInstanceOf("SET_GROUP") or Object:IsInstanceOf("SET_UNIT") then
if Object:IsInstanceOf("SET_GROUP") or Object:IsInstanceOf("SET_UNIT") or Object:IsInstanceOf("SET_STATIC") or Object:IsInstanceOf("SET_OPSGROUP") then
---
-- Sets
@@ -243,14 +259,18 @@ function TARGET:AddObject(Object)
for _,object in pairs(set.Set) do
self:AddObject(object)
end
else
---
-- Groups, Units, Statics, Airbases, Coordinates
---
self:_AddObject(Object)
if Object:IsInstanceOf("OPSGROUP") then
self:_AddObject(Object:GetGroup()) -- We add the MOOSE GROUP object not the OPSGROUP object.
else
self:_AddObject(Object)
end
end
@@ -658,12 +678,12 @@ function TARGET:_AddObject(Object)
elseif Object:IsInstanceOf("ZONE_BASE") then
local zone=Object --Core.Zone#ZONE_BASE
Object=zone:GetCoordinate()
Object=zone --:GetCoordinate()
target.Type=TARGET.ObjectType.COORDINATE
target.Type=TARGET.ObjectType.ZONE
target.Name=zone:GetName()
target.Coordinate=Object
target.Coordinate=zone:GetCoordinate()
target.Life0=1
target.Life=1
@@ -773,7 +793,13 @@ function TARGET:GetTargetLife(Target)
elseif Target.Type==TARGET.ObjectType.COORDINATE then
return 1
elseif Target.Type==TARGET.ObjectType.ZONE then
return 1
else
self:E("ERROR: unknown target object type in GetTargetLife!")
end
end
@@ -865,6 +891,13 @@ function TARGET:GetTargetVec3(Target)
local vec3={x=object.x, y=object.y, z=object.z}
return vec3
elseif Target.Type==TARGET.ObjectType.ZONE then
local object=Target.Object --Core.Zone#ZONE
local vec3=object:GetVec3()
return vec3
end
self:E(self.lid.."ERROR: Unknown TARGET type! Cannot get Vec3")
@@ -1032,7 +1065,13 @@ function TARGET:GetTargetCategory(Target)
elseif Target.Type==TARGET.ObjectType.COORDINATE then
return TARGET.Category.COORDINATE
elseif Target.Type==TARGET.ObjectType.ZONE then
return TARGET.Category.ZONE
else
self:E("ERROR: unknown target category!")
end
return category
@@ -1141,6 +1180,10 @@ function TARGET:CountObjectives(Target)
-- No target we can check!
elseif Target.Type==TARGET.ObjectType.ZONE then
-- No target we can check!
else
self:E(self.lid.."ERROR: Unknown target type! Cannot count targets")
end