mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
Merge branch 'FF/Ops' into FF/OpsDev
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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 @@
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- 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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!"))
|
||||
|
||||
2080
Moose Development/Moose/Ops/CSAR.lua
Normal file
2080
Moose Development/Moose/Ops/CSAR.lua
Normal file
File diff suppressed because it is too large
Load Diff
2806
Moose Development/Moose/Ops/CTLD.lua
Normal file
2806
Moose Development/Moose/Ops/CTLD.lua
Normal file
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
@@ -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
1180
Moose Development/Moose/Ops/OpsTransport.lua
Normal file
1180
Moose Development/Moose/Ops/OpsTransport.lua
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user